Passed
Push — main ( 10dbfc...61a674 )
by LCS
05:39 queued 03:09
created

node_modules/seek-bzip/lib/index.js   F

Complexity

Total Complexity 99
Complexity/F 5.21

Size

Lines of Code 568
Function Count 19

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 357
dl 0
loc 568
rs 2
c 0
b 0
f 0
wmc 99
mnd 80
bc 80
fnc 19
bpm 4.2105
cpm 5.2105
noi 50

How to fix   Complexity   

Complexity

Complex classes like node_modules/seek-bzip/lib/index.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*
2
seek-bzip - a pure-javascript module for seeking within bzip2 data
3
4
Copyright (C) 2013 C. Scott Ananian
5
Copyright (C) 2012 Eli Skeggs
6
Copyright (C) 2011 Kevin Kwok
7
8
Permission is hereby granted, free of charge, to any person obtaining
9
a copy of this software and associated documentation files (the
10
"Software"), to deal in the Software without restriction, including
11
without limitation the rights to use, copy, modify, merge, publish,
12
distribute, sublicense, and/or sell copies of the Software, and to
13
permit persons to whom the Software is furnished to do so, subject to
14
the following conditions:
15
16
The above copyright notice and this permission notice shall be
17
included in all copies or substantial portions of the Software.
18
19
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
22
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
23
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
24
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
25
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26
27
Adapted from node-bzip, copyright 2012 Eli Skeggs.
28
Adapted from bzip2.js, copyright 2011 Kevin Kwok ([email protected]).
29
30
Based on micro-bunzip by Rob Landley ([email protected]).
31
32
Based on bzip2 decompression code by Julian R Seward ([email protected]),
33
which also acknowledges contributions by Mike Burrows, David Wheeler,
34
Peter Fenwick, Alistair Moffat, Radford Neal, Ian H. Witten,
35
Robert Sedgewick, and Jon L. Bentley.
36
*/
37
38
var BitReader = require('./bitreader');
39
var Stream = require('./stream');
40
var CRC32 = require('./crc32');
41
var pjson = require('../package.json');
42
43
var MAX_HUFCODE_BITS = 20;
44
var MAX_SYMBOLS = 258;
45
var SYMBOL_RUNA = 0;
46
var SYMBOL_RUNB = 1;
47
var MIN_GROUPS = 2;
48
var MAX_GROUPS = 6;
49
var GROUP_SIZE = 50;
50
51
var WHOLEPI = "314159265359";
52
var SQRTPI = "177245385090";
53
54
var 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
63
var 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
};
74
var ErrorMessages = {};
75
ErrorMessages[Err.LAST_BLOCK] =            "Bad file checksum";
76
ErrorMessages[Err.NOT_BZIP_DATA] =         "Not bzip data";
77
ErrorMessages[Err.UNEXPECTED_INPUT_EOF] =  "Unexpected input EOF";
78
ErrorMessages[Err.UNEXPECTED_OUTPUT_EOF] = "Unexpected output EOF";
79
ErrorMessages[Err.DATA_ERROR] =            "Data error";
80
ErrorMessages[Err.OUT_OF_MEMORY] =         "Out of memory";
81
ErrorMessages[Err.OBSOLETE_INPUT] = "Obsolete (pre 0.9.5) bzip format not supported.";
82
83
var _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
91
var Bunzip = function(inputStream, outputStream) {
92
  this.writePos = this.writeCurrent = this.writeCount = 0;
93
94
  this._start_bunzip(inputStream, outputStream);
95
};
96
Bunzip.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 */
106
Bunzip.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
};
126
Bunzip.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
*/
406
Bunzip.prototype._read_bunzip = function(outputBuffer, len) {
407
    var copies, previous, outbyte;
408
    /* [email protected]: 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
451
var 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
};
460
var 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 */
501
Bunzip.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)
504
Bunzip.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
};
532
Bunzip.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*). */
558
Bunzip.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
600
Bunzip.Stream = Stream;
601
602
Bunzip.version = pjson.version;
603
Bunzip.license = pjson.license;
604
605
module.exports = Bunzip;
606