blob: f3bab490a2ddeb668786f53398108204bc5a522a [file] [log] [blame]
Leo Repp58b9f112021-11-22 11:57:47 +01001/*
2seek-bzip - a pure-javascript module for seeking within bzip2 data
3
4Copyright (C) 2013 C. Scott Ananian
5Copyright (C) 2012 Eli Skeggs
6Copyright (C) 2011 Kevin Kwok
7
8Permission is hereby granted, free of charge, to any person obtaining
9a copy of this software and associated documentation files (the
10"Software"), to deal in the Software without restriction, including
11without limitation the rights to use, copy, modify, merge, publish,
12distribute, sublicense, and/or sell copies of the Software, and to
13permit persons to whom the Software is furnished to do so, subject to
14the following conditions:
15
16The above copyright notice and this permission notice shall be
17included in all copies or substantial portions of the Software.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
27Adapted from node-bzip, copyright 2012 Eli Skeggs.
28Adapted from bzip2.js, copyright 2011 Kevin Kwok (antimatter15@gmail.com).
29
30Based on micro-bunzip by Rob Landley (rob@landley.net).
31
32Based on bzip2 decompression code by Julian R Seward (jseward@acm.org),
33which also acknowledges contributions by Mike Burrows, David Wheeler,
34Peter Fenwick, Alistair Moffat, Radford Neal, Ian H. Witten,
35Robert Sedgewick, and Jon L. Bentley.
36*/
37
38var BitReader = require('./bitreader');
39var Stream = require('./stream');
40var CRC32 = require('./crc32');
41var pjson = require('../package.json');
42
43var MAX_HUFCODE_BITS = 20;
44var MAX_SYMBOLS = 258;
45var SYMBOL_RUNA = 0;
46var SYMBOL_RUNB = 1;
47var MIN_GROUPS = 2;
48var MAX_GROUPS = 6;
49var GROUP_SIZE = 50;
50
51var WHOLEPI = "314159265359";
52var SQRTPI = "177245385090";
53
54var mtf = function(array, index) {
55 var src = array[index], i;
56 for (i = index; i > 0; i--) {
57 array[i] = array[i-1];
58 }
59 array[0] = src;
60 return src;
61};
62
63var Err = {
64 OK: 0,
65 LAST_BLOCK: -1,
66 NOT_BZIP_DATA: -2,
67 UNEXPECTED_INPUT_EOF: -3,
68 UNEXPECTED_OUTPUT_EOF: -4,
69 DATA_ERROR: -5,
70 OUT_OF_MEMORY: -6,
71 OBSOLETE_INPUT: -7,
72 END_OF_BLOCK: -8
73};
74var ErrorMessages = {};
75ErrorMessages[Err.LAST_BLOCK] = "Bad file checksum";
76ErrorMessages[Err.NOT_BZIP_DATA] = "Not bzip data";
77ErrorMessages[Err.UNEXPECTED_INPUT_EOF] = "Unexpected input EOF";
78ErrorMessages[Err.UNEXPECTED_OUTPUT_EOF] = "Unexpected output EOF";
79ErrorMessages[Err.DATA_ERROR] = "Data error";
80ErrorMessages[Err.OUT_OF_MEMORY] = "Out of memory";
81ErrorMessages[Err.OBSOLETE_INPUT] = "Obsolete (pre 0.9.5) bzip format not supported.";
82
83var _throw = function(status, optDetail) {
84 var msg = ErrorMessages[status] || 'unknown error';
85 if (optDetail) { msg += ': '+optDetail; }
86 var e = new TypeError(msg);
87 e.errorCode = status;
88 throw e;
89};
90
91var Bunzip = function(inputStream, outputStream) {
92 this.writePos = this.writeCurrent = this.writeCount = 0;
93
94 this._start_bunzip(inputStream, outputStream);
95};
96Bunzip.prototype._init_block = function() {
97 var moreBlocks = this._get_next_block();
98 if ( !moreBlocks ) {
99 this.writeCount = -1;
100 return false; /* no more blocks */
101 }
102 this.blockCRC = new CRC32();
103 return true;
104};
105/* XXX micro-bunzip uses (inputStream, inputBuffer, len) as arguments */
106Bunzip.prototype._start_bunzip = function(inputStream, outputStream) {
107 /* Ensure that file starts with "BZh['1'-'9']." */
108 var buf = new Buffer(4);
109 if (inputStream.read(buf, 0, 4) !== 4 ||
110 String.fromCharCode(buf[0], buf[1], buf[2]) !== 'BZh')
111 _throw(Err.NOT_BZIP_DATA, 'bad magic');
112
113 var level = buf[3] - 0x30;
114 if (level < 1 || level > 9)
115 _throw(Err.NOT_BZIP_DATA, 'level out of range');
116
117 this.reader = new BitReader(inputStream);
118
119 /* Fourth byte (ascii '1'-'9'), indicates block size in units of 100k of
120 uncompressed data. Allocate intermediate buffer for block. */
121 this.dbufSize = 100000 * level;
122 this.nextoutput = 0;
123 this.outputStream = outputStream;
124 this.streamCRC = 0;
125};
126Bunzip.prototype._get_next_block = function() {
127 var i, j, k;
128 var reader = this.reader;
129 // this is get_next_block() function from micro-bunzip:
130 /* Read in header signature and CRC, then validate signature.
131 (last block signature means CRC is for whole file, return now) */
132 var h = reader.pi();
133 if (h === SQRTPI) { // last block
134 return false; /* no more blocks */
135 }
136 if (h !== WHOLEPI)
137 _throw(Err.NOT_BZIP_DATA);
138 this.targetBlockCRC = reader.read(32) >>> 0; // (convert to unsigned)
139 this.streamCRC = (this.targetBlockCRC ^
140 ((this.streamCRC << 1) | (this.streamCRC>>>31))) >>> 0;
141 /* We can add support for blockRandomised if anybody complains. There was
142 some code for this in busybox 1.0.0-pre3, but nobody ever noticed that
143 it didn't actually work. */
144 if (reader.read(1))
145 _throw(Err.OBSOLETE_INPUT);
146 var origPointer = reader.read(24);
147 if (origPointer > this.dbufSize)
148 _throw(Err.DATA_ERROR, 'initial position out of bounds');
149 /* mapping table: if some byte values are never used (encoding things
150 like ascii text), the compression code removes the gaps to have fewer
151 symbols to deal with, and writes a sparse bitfield indicating which
152 values were present. We make a translation table to convert the symbols
153 back to the corresponding bytes. */
154 var t = reader.read(16);
155 var symToByte = new Buffer(256), symTotal = 0;
156 for (i = 0; i < 16; i++) {
157 if (t & (1 << (0xF - i))) {
158 var o = i * 16;
159 k = reader.read(16);
160 for (j = 0; j < 16; j++)
161 if (k & (1 << (0xF - j)))
162 symToByte[symTotal++] = o + j;
163 }
164 }
165
166 /* How many different huffman coding groups does this block use? */
167 var groupCount = reader.read(3);
168 if (groupCount < MIN_GROUPS || groupCount > MAX_GROUPS)
169 _throw(Err.DATA_ERROR);
170 /* nSelectors: Every GROUP_SIZE many symbols we select a new huffman coding
171 group. Read in the group selector list, which is stored as MTF encoded
172 bit runs. (MTF=Move To Front, as each value is used it's moved to the
173 start of the list.) */
174 var nSelectors = reader.read(15);
175 if (nSelectors === 0)
176 _throw(Err.DATA_ERROR);
177
178 var mtfSymbol = new Buffer(256);
179 for (i = 0; i < groupCount; i++)
180 mtfSymbol[i] = i;
181
182 var selectors = new Buffer(nSelectors); // was 32768...
183
184 for (i = 0; i < nSelectors; i++) {
185 /* Get next value */
186 for (j = 0; reader.read(1); j++)
187 if (j >= groupCount) _throw(Err.DATA_ERROR);
188 /* Decode MTF to get the next selector */
189 selectors[i] = mtf(mtfSymbol, j);
190 }
191
192 /* Read the huffman coding tables for each group, which code for symTotal
193 literal symbols, plus two run symbols (RUNA, RUNB) */
194 var symCount = symTotal + 2;
195 var groups = [], hufGroup;
196 for (j = 0; j < groupCount; j++) {
197 var length = new Buffer(symCount), temp = new Uint16Array(MAX_HUFCODE_BITS + 1);
198 /* Read huffman code lengths for each symbol. They're stored in
199 a way similar to mtf; record a starting value for the first symbol,
200 and an offset from the previous value for everys symbol after that. */
201 t = reader.read(5); // lengths
202 for (i = 0; i < symCount; i++) {
203 for (;;) {
204 if (t < 1 || t > MAX_HUFCODE_BITS) _throw(Err.DATA_ERROR);
205 /* If first bit is 0, stop. Else second bit indicates whether
206 to increment or decrement the value. */
207 if(!reader.read(1))
208 break;
209 if(!reader.read(1))
210 t++;
211 else
212 t--;
213 }
214 length[i] = t;
215 }
216
217 /* Find largest and smallest lengths in this group */
218 var minLen, maxLen;
219 minLen = maxLen = length[0];
220 for (i = 1; i < symCount; i++) {
221 if (length[i] > maxLen)
222 maxLen = length[i];
223 else if (length[i] < minLen)
224 minLen = length[i];
225 }
226
227 /* Calculate permute[], base[], and limit[] tables from length[].
228 *
229 * permute[] is the lookup table for converting huffman coded symbols
230 * into decoded symbols. base[] is the amount to subtract from the
231 * value of a huffman symbol of a given length when using permute[].
232 *
233 * limit[] indicates the largest numerical value a symbol with a given
234 * number of bits can have. This is how the huffman codes can vary in
235 * length: each code with a value>limit[length] needs another bit.
236 */
237 hufGroup = {};
238 groups.push(hufGroup);
239 hufGroup.permute = new Uint16Array(MAX_SYMBOLS);
240 hufGroup.limit = new Uint32Array(MAX_HUFCODE_BITS + 2);
241 hufGroup.base = new Uint32Array(MAX_HUFCODE_BITS + 1);
242 hufGroup.minLen = minLen;
243 hufGroup.maxLen = maxLen;
244 /* Calculate permute[]. Concurently, initialize temp[] and limit[]. */
245 var pp = 0;
246 for (i = minLen; i <= maxLen; i++) {
247 temp[i] = hufGroup.limit[i] = 0;
248 for (t = 0; t < symCount; t++)
249 if (length[t] === i)
250 hufGroup.permute[pp++] = t;
251 }
252 /* Count symbols coded for at each bit length */
253 for (i = 0; i < symCount; i++)
254 temp[length[i]]++;
255 /* Calculate limit[] (the largest symbol-coding value at each bit
256 * length, which is (previous limit<<1)+symbols at this level), and
257 * base[] (number of symbols to ignore at each bit length, which is
258 * limit minus the cumulative count of symbols coded for already). */
259 pp = t = 0;
260 for (i = minLen; i < maxLen; i++) {
261 pp += temp[i];
262 /* We read the largest possible symbol size and then unget bits
263 after determining how many we need, and those extra bits could
264 be set to anything. (They're noise from future symbols.) At
265 each level we're really only interested in the first few bits,
266 so here we set all the trailing to-be-ignored bits to 1 so they
267 don't affect the value>limit[length] comparison. */
268 hufGroup.limit[i] = pp - 1;
269 pp <<= 1;
270 t += temp[i];
271 hufGroup.base[i + 1] = pp - t;
272 }
273 hufGroup.limit[maxLen + 1] = Number.MAX_VALUE; /* Sentinal value for reading next sym. */
274 hufGroup.limit[maxLen] = pp + temp[maxLen] - 1;
275 hufGroup.base[minLen] = 0;
276 }
277 /* We've finished reading and digesting the block header. Now read this
278 block's huffman coded symbols from the file and undo the huffman coding
279 and run length encoding, saving the result into dbuf[dbufCount++]=uc */
280
281 /* Initialize symbol occurrence counters and symbol Move To Front table */
282 var byteCount = new Uint32Array(256);
283 for (i = 0; i < 256; i++)
284 mtfSymbol[i] = i;
285 /* Loop through compressed symbols. */
286 var runPos = 0, dbufCount = 0, selector = 0, uc;
287 var dbuf = this.dbuf = new Uint32Array(this.dbufSize);
288 symCount = 0;
289 for (;;) {
290 /* Determine which huffman coding group to use. */
291 if (!(symCount--)) {
292 symCount = GROUP_SIZE - 1;
293 if (selector >= nSelectors) { _throw(Err.DATA_ERROR); }
294 hufGroup = groups[selectors[selector++]];
295 }
296 /* Read next huffman-coded symbol. */
297 i = hufGroup.minLen;
298 j = reader.read(i);
299 for (;;i++) {
300 if (i > hufGroup.maxLen) { _throw(Err.DATA_ERROR); }
301 if (j <= hufGroup.limit[i])
302 break;
303 j = (j << 1) | reader.read(1);
304 }
305 /* Huffman decode value to get nextSym (with bounds checking) */
306 j -= hufGroup.base[i];
307 if (j < 0 || j >= MAX_SYMBOLS) { _throw(Err.DATA_ERROR); }
308 var nextSym = hufGroup.permute[j];
309 /* We have now decoded the symbol, which indicates either a new literal
310 byte, or a repeated run of the most recent literal byte. First,
311 check if nextSym indicates a repeated run, and if so loop collecting
312 how many times to repeat the last literal. */
313 if (nextSym === SYMBOL_RUNA || nextSym === SYMBOL_RUNB) {
314 /* If this is the start of a new run, zero out counter */
315 if (!runPos){
316 runPos = 1;
317 t = 0;
318 }
319 /* Neat trick that saves 1 symbol: instead of or-ing 0 or 1 at
320 each bit position, add 1 or 2 instead. For example,
321 1011 is 1<<0 + 1<<1 + 2<<2. 1010 is 2<<0 + 2<<1 + 1<<2.
322 You can make any bit pattern that way using 1 less symbol than
323 the basic or 0/1 method (except all bits 0, which would use no
324 symbols, but a run of length 0 doesn't mean anything in this
325 context). Thus space is saved. */
326 if (nextSym === SYMBOL_RUNA)
327 t += runPos;
328 else
329 t += 2 * runPos;
330 runPos <<= 1;
331 continue;
332 }
333 /* When we hit the first non-run symbol after a run, we now know
334 how many times to repeat the last literal, so append that many
335 copies to our buffer of decoded symbols (dbuf) now. (The last
336 literal used is the one at the head of the mtfSymbol array.) */
337 if (runPos){
338 runPos = 0;
339 if (dbufCount + t > this.dbufSize) { _throw(Err.DATA_ERROR); }
340 uc = symToByte[mtfSymbol[0]];
341 byteCount[uc] += t;
342 while (t--)
343 dbuf[dbufCount++] = uc;
344 }
345 /* Is this the terminating symbol? */
346 if (nextSym > symTotal)
347 break;
348 /* At this point, nextSym indicates a new literal character. Subtract
349 one to get the position in the MTF array at which this literal is
350 currently to be found. (Note that the result can't be -1 or 0,
351 because 0 and 1 are RUNA and RUNB. But another instance of the
352 first symbol in the mtf array, position 0, would have been handled
353 as part of a run above. Therefore 1 unused mtf position minus
354 2 non-literal nextSym values equals -1.) */
355 if (dbufCount >= this.dbufSize) { _throw(Err.DATA_ERROR); }
356 i = nextSym - 1;
357 uc = mtf(mtfSymbol, i);
358 uc = symToByte[uc];
359 /* We have our literal byte. Save it into dbuf. */
360 byteCount[uc]++;
361 dbuf[dbufCount++] = uc;
362 }
363 /* At this point, we've read all the huffman-coded symbols (and repeated
364 runs) for this block from the input stream, and decoded them into the
365 intermediate buffer. There are dbufCount many decoded bytes in dbuf[].
366 Now undo the Burrows-Wheeler transform on dbuf.
367 See http://dogma.net/markn/articles/bwt/bwt.htm
368 */
369 if (origPointer < 0 || origPointer >= dbufCount) { _throw(Err.DATA_ERROR); }
370 /* Turn byteCount into cumulative occurrence counts of 0 to n-1. */
371 j = 0;
372 for (i = 0; i < 256; i++) {
373 k = j + byteCount[i];
374 byteCount[i] = j;
375 j = k;
376 }
377 /* Figure out what order dbuf would be in if we sorted it. */
378 for (i = 0; i < dbufCount; i++) {
379 uc = dbuf[i] & 0xff;
380 dbuf[byteCount[uc]] |= (i << 8);
381 byteCount[uc]++;
382 }
383 /* Decode first byte by hand to initialize "previous" byte. Note that it
384 doesn't get output, and if the first three characters are identical
385 it doesn't qualify as a run (hence writeRunCountdown=5). */
386 var pos = 0, current = 0, run = 0;
387 if (dbufCount) {
388 pos = dbuf[origPointer];
389 current = (pos & 0xff);
390 pos >>= 8;
391 run = -1;
392 }
393 this.writePos = pos;
394 this.writeCurrent = current;
395 this.writeCount = dbufCount;
396 this.writeRun = run;
397
398 return true; /* more blocks to come */
399};
400/* Undo burrows-wheeler transform on intermediate buffer to produce output.
401 If start_bunzip was initialized with out_fd=-1, then up to len bytes of
402 data are written to outbuf. Return value is number of bytes written or
403 error (all errors are negative numbers). If out_fd!=-1, outbuf and len
404 are ignored, data is written to out_fd and return is RETVAL_OK or error.
405*/
406Bunzip.prototype._read_bunzip = function(outputBuffer, len) {
407 var copies, previous, outbyte;
408 /* james@jamestaylor.org: writeCount goes to -1 when the buffer is fully
409 decoded, which results in this returning RETVAL_LAST_BLOCK, also
410 equal to -1... Confusing, I'm returning 0 here to indicate no
411 bytes written into the buffer */
412 if (this.writeCount < 0) { return 0; }
413
414 var gotcount = 0;
415 var dbuf = this.dbuf, pos = this.writePos, current = this.writeCurrent;
416 var dbufCount = this.writeCount, outputsize = this.outputsize;
417 var run = this.writeRun;
418
419 while (dbufCount) {
420 dbufCount--;
421 previous = current;
422 pos = dbuf[pos];
423 current = pos & 0xff;
424 pos >>= 8;
425 if (run++ === 3){
426 copies = current;
427 outbyte = previous;
428 current = -1;
429 } else {
430 copies = 1;
431 outbyte = current;
432 }
433 this.blockCRC.updateCRCRun(outbyte, copies);
434 while (copies--) {
435 this.outputStream.writeByte(outbyte);
436 this.nextoutput++;
437 }
438 if (current != previous)
439 run = 0;
440 }
441 this.writeCount = dbufCount;
442 // check CRC
443 if (this.blockCRC.getCRC() !== this.targetBlockCRC) {
444 _throw(Err.DATA_ERROR, "Bad block CRC "+
445 "(got "+this.blockCRC.getCRC().toString(16)+
446 " expected "+this.targetBlockCRC.toString(16)+")");
447 }
448 return this.nextoutput;
449};
450
451var coerceInputStream = function(input) {
452 if ('readByte' in input) { return input; }
453 var inputStream = new Stream();
454 inputStream.pos = 0;
455 inputStream.readByte = function() { return input[this.pos++]; };
456 inputStream.seek = function(pos) { this.pos = pos; };
457 inputStream.eof = function() { return this.pos >= input.length; };
458 return inputStream;
459};
460var coerceOutputStream = function(output) {
461 var outputStream = new Stream();
462 var resizeOk = true;
463 if (output) {
464 if (typeof(output)==='number') {
465 outputStream.buffer = new Buffer(output);
466 resizeOk = false;
467 } else if ('writeByte' in output) {
468 return output;
469 } else {
470 outputStream.buffer = output;
471 resizeOk = false;
472 }
473 } else {
474 outputStream.buffer = new Buffer(16384);
475 }
476 outputStream.pos = 0;
477 outputStream.writeByte = function(_byte) {
478 if (resizeOk && this.pos >= this.buffer.length) {
479 var newBuffer = new Buffer(this.buffer.length*2);
480 this.buffer.copy(newBuffer);
481 this.buffer = newBuffer;
482 }
483 this.buffer[this.pos++] = _byte;
484 };
485 outputStream.getBuffer = function() {
486 // trim buffer
487 if (this.pos !== this.buffer.length) {
488 if (!resizeOk)
489 throw new TypeError('outputsize does not match decoded input');
490 var newBuffer = new Buffer(this.pos);
491 this.buffer.copy(newBuffer, 0, 0, this.pos);
492 this.buffer = newBuffer;
493 }
494 return this.buffer;
495 };
496 outputStream._coerced = true;
497 return outputStream;
498};
499
500/* Static helper functions */
501Bunzip.Err = Err;
502// 'input' can be a stream or a buffer
503// 'output' can be a stream or a buffer or a number (buffer size)
504Bunzip.decode = function(input, output, multistream) {
505 // make a stream from a buffer, if necessary
506 var inputStream = coerceInputStream(input);
507 var outputStream = coerceOutputStream(output);
508
509 var bz = new Bunzip(inputStream, outputStream);
510 while (true) {
511 if ('eof' in inputStream && inputStream.eof()) break;
512 if (bz._init_block()) {
513 bz._read_bunzip();
514 } else {
515 var targetStreamCRC = bz.reader.read(32) >>> 0; // (convert to unsigned)
516 if (targetStreamCRC !== bz.streamCRC) {
517 _throw(Err.DATA_ERROR, "Bad stream CRC "+
518 "(got "+bz.streamCRC.toString(16)+
519 " expected "+targetStreamCRC.toString(16)+")");
520 }
521 if (multistream &&
522 'eof' in inputStream &&
523 !inputStream.eof()) {
524 // note that start_bunzip will also resync the bit reader to next byte
525 bz._start_bunzip(inputStream, outputStream);
526 } else break;
527 }
528 }
529 if ('getBuffer' in outputStream)
530 return outputStream.getBuffer();
531};
532Bunzip.decodeBlock = function(input, pos, output) {
533 // make a stream from a buffer, if necessary
534 var inputStream = coerceInputStream(input);
535 var outputStream = coerceOutputStream(output);
536 var bz = new Bunzip(inputStream, outputStream);
537 bz.reader.seek(pos);
538 /* Fill the decode buffer for the block */
539 var moreBlocks = bz._get_next_block();
540 if (moreBlocks) {
541 /* Init the CRC for writing */
542 bz.blockCRC = new CRC32();
543
544 /* Zero this so the current byte from before the seek is not written */
545 bz.writeCopies = 0;
546
547 /* Decompress the block and write to stdout */
548 bz._read_bunzip();
549 // XXX keep writing?
550 }
551 if ('getBuffer' in outputStream)
552 return outputStream.getBuffer();
553};
554/* Reads bzip2 file from stream or buffer `input`, and invoke
555 * `callback(position, size)` once for each bzip2 block,
556 * where position gives the starting position (in *bits*)
557 * and size gives uncompressed size of the block (in *bytes*). */
558Bunzip.table = function(input, callback, multistream) {
559 // make a stream from a buffer, if necessary
560 var inputStream = new Stream();
561 inputStream.delegate = coerceInputStream(input);
562 inputStream.pos = 0;
563 inputStream.readByte = function() {
564 this.pos++;
565 return this.delegate.readByte();
566 };
567 if (inputStream.delegate.eof) {
568 inputStream.eof = inputStream.delegate.eof.bind(inputStream.delegate);
569 }
570 var outputStream = new Stream();
571 outputStream.pos = 0;
572 outputStream.writeByte = function() { this.pos++; };
573
574 var bz = new Bunzip(inputStream, outputStream);
575 var blockSize = bz.dbufSize;
576 while (true) {
577 if ('eof' in inputStream && inputStream.eof()) break;
578
579 var position = inputStream.pos*8 + bz.reader.bitOffset;
580 if (bz.reader.hasByte) { position -= 8; }
581
582 if (bz._init_block()) {
583 var start = outputStream.pos;
584 bz._read_bunzip();
585 callback(position, outputStream.pos - start);
586 } else {
587 var crc = bz.reader.read(32); // (but we ignore the crc)
588 if (multistream &&
589 'eof' in inputStream &&
590 !inputStream.eof()) {
591 // note that start_bunzip will also resync the bit reader to next byte
592 bz._start_bunzip(inputStream, outputStream);
593 console.assert(bz.dbufSize === blockSize,
594 "shouldn't change block size within multistream file");
595 } else break;
596 }
597 }
598};
599
600Bunzip.Stream = Stream;
601
602Bunzip.version = pjson.version;
603Bunzip.license = pjson.license;
604
605module.exports = Bunzip;