Projekt

Obecné

Profil

Stáhnout (16.3 KB) Statistiky
| Větev: | Revize:
1
'use strict';
2

    
3
var Buffer = require('buffer').Buffer;
4
var Transform = require('stream').Transform;
5
var binding = require('./binding');
6
var util = require('util');
7
var assert = require('assert').ok;
8
var kMaxLength = require('buffer').kMaxLength;
9
var kRangeErrorMessage = 'Cannot create final Buffer. It would be larger ' + 'than 0x' + kMaxLength.toString(16) + ' bytes';
10

    
11
// zlib doesn't provide these, so kludge them in following the same
12
// const naming scheme zlib uses.
13
binding.Z_MIN_WINDOWBITS = 8;
14
binding.Z_MAX_WINDOWBITS = 15;
15
binding.Z_DEFAULT_WINDOWBITS = 15;
16

    
17
// fewer than 64 bytes per chunk is stupid.
18
// technically it could work with as few as 8, but even 64 bytes
19
// is absurdly low.  Usually a MB or more is best.
20
binding.Z_MIN_CHUNK = 64;
21
binding.Z_MAX_CHUNK = Infinity;
22
binding.Z_DEFAULT_CHUNK = 16 * 1024;
23

    
24
binding.Z_MIN_MEMLEVEL = 1;
25
binding.Z_MAX_MEMLEVEL = 9;
26
binding.Z_DEFAULT_MEMLEVEL = 8;
27

    
28
binding.Z_MIN_LEVEL = -1;
29
binding.Z_MAX_LEVEL = 9;
30
binding.Z_DEFAULT_LEVEL = binding.Z_DEFAULT_COMPRESSION;
31

    
32
// expose all the zlib constants
33
var bkeys = Object.keys(binding);
34
for (var bk = 0; bk < bkeys.length; bk++) {
35
  var bkey = bkeys[bk];
36
  if (bkey.match(/^Z/)) {
37
    Object.defineProperty(exports, bkey, {
38
      enumerable: true, value: binding[bkey], writable: false
39
    });
40
  }
41
}
42

    
43
// translation table for return codes.
44
var codes = {
45
  Z_OK: binding.Z_OK,
46
  Z_STREAM_END: binding.Z_STREAM_END,
47
  Z_NEED_DICT: binding.Z_NEED_DICT,
48
  Z_ERRNO: binding.Z_ERRNO,
49
  Z_STREAM_ERROR: binding.Z_STREAM_ERROR,
50
  Z_DATA_ERROR: binding.Z_DATA_ERROR,
51
  Z_MEM_ERROR: binding.Z_MEM_ERROR,
52
  Z_BUF_ERROR: binding.Z_BUF_ERROR,
53
  Z_VERSION_ERROR: binding.Z_VERSION_ERROR
54
};
55

    
56
var ckeys = Object.keys(codes);
57
for (var ck = 0; ck < ckeys.length; ck++) {
58
  var ckey = ckeys[ck];
59
  codes[codes[ckey]] = ckey;
60
}
61

    
62
Object.defineProperty(exports, 'codes', {
63
  enumerable: true, value: Object.freeze(codes), writable: false
64
});
65

    
66
exports.Deflate = Deflate;
67
exports.Inflate = Inflate;
68
exports.Gzip = Gzip;
69
exports.Gunzip = Gunzip;
70
exports.DeflateRaw = DeflateRaw;
71
exports.InflateRaw = InflateRaw;
72
exports.Unzip = Unzip;
73

    
74
exports.createDeflate = function (o) {
75
  return new Deflate(o);
76
};
77

    
78
exports.createInflate = function (o) {
79
  return new Inflate(o);
80
};
81

    
82
exports.createDeflateRaw = function (o) {
83
  return new DeflateRaw(o);
84
};
85

    
86
exports.createInflateRaw = function (o) {
87
  return new InflateRaw(o);
88
};
89

    
90
exports.createGzip = function (o) {
91
  return new Gzip(o);
92
};
93

    
94
exports.createGunzip = function (o) {
95
  return new Gunzip(o);
96
};
97

    
98
exports.createUnzip = function (o) {
99
  return new Unzip(o);
100
};
101

    
102
// Convenience methods.
103
// compress/decompress a string or buffer in one step.
104
exports.deflate = function (buffer, opts, callback) {
105
  if (typeof opts === 'function') {
106
    callback = opts;
107
    opts = {};
108
  }
109
  return zlibBuffer(new Deflate(opts), buffer, callback);
110
};
111

    
112
exports.deflateSync = function (buffer, opts) {
113
  return zlibBufferSync(new Deflate(opts), buffer);
114
};
115

    
116
exports.gzip = function (buffer, opts, callback) {
117
  if (typeof opts === 'function') {
118
    callback = opts;
119
    opts = {};
120
  }
121
  return zlibBuffer(new Gzip(opts), buffer, callback);
122
};
123

    
124
exports.gzipSync = function (buffer, opts) {
125
  return zlibBufferSync(new Gzip(opts), buffer);
126
};
127

    
128
exports.deflateRaw = function (buffer, opts, callback) {
129
  if (typeof opts === 'function') {
130
    callback = opts;
131
    opts = {};
132
  }
133
  return zlibBuffer(new DeflateRaw(opts), buffer, callback);
134
};
135

    
136
exports.deflateRawSync = function (buffer, opts) {
137
  return zlibBufferSync(new DeflateRaw(opts), buffer);
138
};
139

    
140
exports.unzip = function (buffer, opts, callback) {
141
  if (typeof opts === 'function') {
142
    callback = opts;
143
    opts = {};
144
  }
145
  return zlibBuffer(new Unzip(opts), buffer, callback);
146
};
147

    
148
exports.unzipSync = function (buffer, opts) {
149
  return zlibBufferSync(new Unzip(opts), buffer);
150
};
151

    
152
exports.inflate = function (buffer, opts, callback) {
153
  if (typeof opts === 'function') {
154
    callback = opts;
155
    opts = {};
156
  }
157
  return zlibBuffer(new Inflate(opts), buffer, callback);
158
};
159

    
160
exports.inflateSync = function (buffer, opts) {
161
  return zlibBufferSync(new Inflate(opts), buffer);
162
};
163

    
164
exports.gunzip = function (buffer, opts, callback) {
165
  if (typeof opts === 'function') {
166
    callback = opts;
167
    opts = {};
168
  }
169
  return zlibBuffer(new Gunzip(opts), buffer, callback);
170
};
171

    
172
exports.gunzipSync = function (buffer, opts) {
173
  return zlibBufferSync(new Gunzip(opts), buffer);
174
};
175

    
176
exports.inflateRaw = function (buffer, opts, callback) {
177
  if (typeof opts === 'function') {
178
    callback = opts;
179
    opts = {};
180
  }
181
  return zlibBuffer(new InflateRaw(opts), buffer, callback);
182
};
183

    
184
exports.inflateRawSync = function (buffer, opts) {
185
  return zlibBufferSync(new InflateRaw(opts), buffer);
186
};
187

    
188
function zlibBuffer(engine, buffer, callback) {
189
  var buffers = [];
190
  var nread = 0;
191

    
192
  engine.on('error', onError);
193
  engine.on('end', onEnd);
194

    
195
  engine.end(buffer);
196
  flow();
197

    
198
  function flow() {
199
    var chunk;
200
    while (null !== (chunk = engine.read())) {
201
      buffers.push(chunk);
202
      nread += chunk.length;
203
    }
204
    engine.once('readable', flow);
205
  }
206

    
207
  function onError(err) {
208
    engine.removeListener('end', onEnd);
209
    engine.removeListener('readable', flow);
210
    callback(err);
211
  }
212

    
213
  function onEnd() {
214
    var buf;
215
    var err = null;
216

    
217
    if (nread >= kMaxLength) {
218
      err = new RangeError(kRangeErrorMessage);
219
    } else {
220
      buf = Buffer.concat(buffers, nread);
221
    }
222

    
223
    buffers = [];
224
    engine.close();
225
    callback(err, buf);
226
  }
227
}
228

    
229
function zlibBufferSync(engine, buffer) {
230
  if (typeof buffer === 'string') buffer = Buffer.from(buffer);
231

    
232
  if (!Buffer.isBuffer(buffer)) throw new TypeError('Not a string or buffer');
233

    
234
  var flushFlag = engine._finishFlushFlag;
235

    
236
  return engine._processChunk(buffer, flushFlag);
237
}
238

    
239
// generic zlib
240
// minimal 2-byte header
241
function Deflate(opts) {
242
  if (!(this instanceof Deflate)) return new Deflate(opts);
243
  Zlib.call(this, opts, binding.DEFLATE);
244
}
245

    
246
function Inflate(opts) {
247
  if (!(this instanceof Inflate)) return new Inflate(opts);
248
  Zlib.call(this, opts, binding.INFLATE);
249
}
250

    
251
// gzip - bigger header, same deflate compression
252
function Gzip(opts) {
253
  if (!(this instanceof Gzip)) return new Gzip(opts);
254
  Zlib.call(this, opts, binding.GZIP);
255
}
256

    
257
function Gunzip(opts) {
258
  if (!(this instanceof Gunzip)) return new Gunzip(opts);
259
  Zlib.call(this, opts, binding.GUNZIP);
260
}
261

    
262
// raw - no header
263
function DeflateRaw(opts) {
264
  if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts);
265
  Zlib.call(this, opts, binding.DEFLATERAW);
266
}
267

    
268
function InflateRaw(opts) {
269
  if (!(this instanceof InflateRaw)) return new InflateRaw(opts);
270
  Zlib.call(this, opts, binding.INFLATERAW);
271
}
272

    
273
// auto-detect header.
274
function Unzip(opts) {
275
  if (!(this instanceof Unzip)) return new Unzip(opts);
276
  Zlib.call(this, opts, binding.UNZIP);
277
}
278

    
279
function isValidFlushFlag(flag) {
280
  return flag === binding.Z_NO_FLUSH || flag === binding.Z_PARTIAL_FLUSH || flag === binding.Z_SYNC_FLUSH || flag === binding.Z_FULL_FLUSH || flag === binding.Z_FINISH || flag === binding.Z_BLOCK;
281
}
282

    
283
// the Zlib class they all inherit from
284
// This thing manages the queue of requests, and returns
285
// true or false if there is anything in the queue when
286
// you call the .write() method.
287

    
288
function Zlib(opts, mode) {
289
  var _this = this;
290

    
291
  this._opts = opts = opts || {};
292
  this._chunkSize = opts.chunkSize || exports.Z_DEFAULT_CHUNK;
293

    
294
  Transform.call(this, opts);
295

    
296
  if (opts.flush && !isValidFlushFlag(opts.flush)) {
297
    throw new Error('Invalid flush flag: ' + opts.flush);
298
  }
299
  if (opts.finishFlush && !isValidFlushFlag(opts.finishFlush)) {
300
    throw new Error('Invalid flush flag: ' + opts.finishFlush);
301
  }
302

    
303
  this._flushFlag = opts.flush || binding.Z_NO_FLUSH;
304
  this._finishFlushFlag = typeof opts.finishFlush !== 'undefined' ? opts.finishFlush : binding.Z_FINISH;
305

    
306
  if (opts.chunkSize) {
307
    if (opts.chunkSize < exports.Z_MIN_CHUNK || opts.chunkSize > exports.Z_MAX_CHUNK) {
308
      throw new Error('Invalid chunk size: ' + opts.chunkSize);
309
    }
310
  }
311

    
312
  if (opts.windowBits) {
313
    if (opts.windowBits < exports.Z_MIN_WINDOWBITS || opts.windowBits > exports.Z_MAX_WINDOWBITS) {
314
      throw new Error('Invalid windowBits: ' + opts.windowBits);
315
    }
316
  }
317

    
318
  if (opts.level) {
319
    if (opts.level < exports.Z_MIN_LEVEL || opts.level > exports.Z_MAX_LEVEL) {
320
      throw new Error('Invalid compression level: ' + opts.level);
321
    }
322
  }
323

    
324
  if (opts.memLevel) {
325
    if (opts.memLevel < exports.Z_MIN_MEMLEVEL || opts.memLevel > exports.Z_MAX_MEMLEVEL) {
326
      throw new Error('Invalid memLevel: ' + opts.memLevel);
327
    }
328
  }
329

    
330
  if (opts.strategy) {
331
    if (opts.strategy != exports.Z_FILTERED && opts.strategy != exports.Z_HUFFMAN_ONLY && opts.strategy != exports.Z_RLE && opts.strategy != exports.Z_FIXED && opts.strategy != exports.Z_DEFAULT_STRATEGY) {
332
      throw new Error('Invalid strategy: ' + opts.strategy);
333
    }
334
  }
335

    
336
  if (opts.dictionary) {
337
    if (!Buffer.isBuffer(opts.dictionary)) {
338
      throw new Error('Invalid dictionary: it should be a Buffer instance');
339
    }
340
  }
341

    
342
  this._handle = new binding.Zlib(mode);
343

    
344
  var self = this;
345
  this._hadError = false;
346
  this._handle.onerror = function (message, errno) {
347
    // there is no way to cleanly recover.
348
    // continuing only obscures problems.
349
    _close(self);
350
    self._hadError = true;
351

    
352
    var error = new Error(message);
353
    error.errno = errno;
354
    error.code = exports.codes[errno];
355
    self.emit('error', error);
356
  };
357

    
358
  var level = exports.Z_DEFAULT_COMPRESSION;
359
  if (typeof opts.level === 'number') level = opts.level;
360

    
361
  var strategy = exports.Z_DEFAULT_STRATEGY;
362
  if (typeof opts.strategy === 'number') strategy = opts.strategy;
363

    
364
  this._handle.init(opts.windowBits || exports.Z_DEFAULT_WINDOWBITS, level, opts.memLevel || exports.Z_DEFAULT_MEMLEVEL, strategy, opts.dictionary);
365

    
366
  this._buffer = Buffer.allocUnsafe(this._chunkSize);
367
  this._offset = 0;
368
  this._level = level;
369
  this._strategy = strategy;
370

    
371
  this.once('end', this.close);
372

    
373
  Object.defineProperty(this, '_closed', {
374
    get: function () {
375
      return !_this._handle;
376
    },
377
    configurable: true,
378
    enumerable: true
379
  });
380
}
381

    
382
util.inherits(Zlib, Transform);
383

    
384
Zlib.prototype.params = function (level, strategy, callback) {
385
  if (level < exports.Z_MIN_LEVEL || level > exports.Z_MAX_LEVEL) {
386
    throw new RangeError('Invalid compression level: ' + level);
387
  }
388
  if (strategy != exports.Z_FILTERED && strategy != exports.Z_HUFFMAN_ONLY && strategy != exports.Z_RLE && strategy != exports.Z_FIXED && strategy != exports.Z_DEFAULT_STRATEGY) {
389
    throw new TypeError('Invalid strategy: ' + strategy);
390
  }
391

    
392
  if (this._level !== level || this._strategy !== strategy) {
393
    var self = this;
394
    this.flush(binding.Z_SYNC_FLUSH, function () {
395
      assert(self._handle, 'zlib binding closed');
396
      self._handle.params(level, strategy);
397
      if (!self._hadError) {
398
        self._level = level;
399
        self._strategy = strategy;
400
        if (callback) callback();
401
      }
402
    });
403
  } else {
404
    process.nextTick(callback);
405
  }
406
};
407

    
408
Zlib.prototype.reset = function () {
409
  assert(this._handle, 'zlib binding closed');
410
  return this._handle.reset();
411
};
412

    
413
// This is the _flush function called by the transform class,
414
// internally, when the last chunk has been written.
415
Zlib.prototype._flush = function (callback) {
416
  this._transform(Buffer.alloc(0), '', callback);
417
};
418

    
419
Zlib.prototype.flush = function (kind, callback) {
420
  var _this2 = this;
421

    
422
  var ws = this._writableState;
423

    
424
  if (typeof kind === 'function' || kind === undefined && !callback) {
425
    callback = kind;
426
    kind = binding.Z_FULL_FLUSH;
427
  }
428

    
429
  if (ws.ended) {
430
    if (callback) process.nextTick(callback);
431
  } else if (ws.ending) {
432
    if (callback) this.once('end', callback);
433
  } else if (ws.needDrain) {
434
    if (callback) {
435
      this.once('drain', function () {
436
        return _this2.flush(kind, callback);
437
      });
438
    }
439
  } else {
440
    this._flushFlag = kind;
441
    this.write(Buffer.alloc(0), '', callback);
442
  }
443
};
444

    
445
Zlib.prototype.close = function (callback) {
446
  _close(this, callback);
447
  process.nextTick(emitCloseNT, this);
448
};
449

    
450
function _close(engine, callback) {
451
  if (callback) process.nextTick(callback);
452

    
453
  // Caller may invoke .close after a zlib error (which will null _handle).
454
  if (!engine._handle) return;
455

    
456
  engine._handle.close();
457
  engine._handle = null;
458
}
459

    
460
function emitCloseNT(self) {
461
  self.emit('close');
462
}
463

    
464
Zlib.prototype._transform = function (chunk, encoding, cb) {
465
  var flushFlag;
466
  var ws = this._writableState;
467
  var ending = ws.ending || ws.ended;
468
  var last = ending && (!chunk || ws.length === chunk.length);
469

    
470
  if (chunk !== null && !Buffer.isBuffer(chunk)) return cb(new Error('invalid input'));
471

    
472
  if (!this._handle) return cb(new Error('zlib binding closed'));
473

    
474
  // If it's the last chunk, or a final flush, we use the Z_FINISH flush flag
475
  // (or whatever flag was provided using opts.finishFlush).
476
  // If it's explicitly flushing at some other time, then we use
477
  // Z_FULL_FLUSH. Otherwise, use Z_NO_FLUSH for maximum compression
478
  // goodness.
479
  if (last) flushFlag = this._finishFlushFlag;else {
480
    flushFlag = this._flushFlag;
481
    // once we've flushed the last of the queue, stop flushing and
482
    // go back to the normal behavior.
483
    if (chunk.length >= ws.length) {
484
      this._flushFlag = this._opts.flush || binding.Z_NO_FLUSH;
485
    }
486
  }
487

    
488
  this._processChunk(chunk, flushFlag, cb);
489
};
490

    
491
Zlib.prototype._processChunk = function (chunk, flushFlag, cb) {
492
  var availInBefore = chunk && chunk.length;
493
  var availOutBefore = this._chunkSize - this._offset;
494
  var inOff = 0;
495

    
496
  var self = this;
497

    
498
  var async = typeof cb === 'function';
499

    
500
  if (!async) {
501
    var buffers = [];
502
    var nread = 0;
503

    
504
    var error;
505
    this.on('error', function (er) {
506
      error = er;
507
    });
508

    
509
    assert(this._handle, 'zlib binding closed');
510
    do {
511
      var res = this._handle.writeSync(flushFlag, chunk, // in
512
      inOff, // in_off
513
      availInBefore, // in_len
514
      this._buffer, // out
515
      this._offset, //out_off
516
      availOutBefore); // out_len
517
    } while (!this._hadError && callback(res[0], res[1]));
518

    
519
    if (this._hadError) {
520
      throw error;
521
    }
522

    
523
    if (nread >= kMaxLength) {
524
      _close(this);
525
      throw new RangeError(kRangeErrorMessage);
526
    }
527

    
528
    var buf = Buffer.concat(buffers, nread);
529
    _close(this);
530

    
531
    return buf;
532
  }
533

    
534
  assert(this._handle, 'zlib binding closed');
535
  var req = this._handle.write(flushFlag, chunk, // in
536
  inOff, // in_off
537
  availInBefore, // in_len
538
  this._buffer, // out
539
  this._offset, //out_off
540
  availOutBefore); // out_len
541

    
542
  req.buffer = chunk;
543
  req.callback = callback;
544

    
545
  function callback(availInAfter, availOutAfter) {
546
    // When the callback is used in an async write, the callback's
547
    // context is the `req` object that was created. The req object
548
    // is === this._handle, and that's why it's important to null
549
    // out the values after they are done being used. `this._handle`
550
    // can stay in memory longer than the callback and buffer are needed.
551
    if (this) {
552
      this.buffer = null;
553
      this.callback = null;
554
    }
555

    
556
    if (self._hadError) return;
557

    
558
    var have = availOutBefore - availOutAfter;
559
    assert(have >= 0, 'have should not go down');
560

    
561
    if (have > 0) {
562
      var out = self._buffer.slice(self._offset, self._offset + have);
563
      self._offset += have;
564
      // serve some output to the consumer.
565
      if (async) {
566
        self.push(out);
567
      } else {
568
        buffers.push(out);
569
        nread += out.length;
570
      }
571
    }
572

    
573
    // exhausted the output buffer, or used all the input create a new one.
574
    if (availOutAfter === 0 || self._offset >= self._chunkSize) {
575
      availOutBefore = self._chunkSize;
576
      self._offset = 0;
577
      self._buffer = Buffer.allocUnsafe(self._chunkSize);
578
    }
579

    
580
    if (availOutAfter === 0) {
581
      // Not actually done.  Need to reprocess.
582
      // Also, update the availInBefore to the availInAfter value,
583
      // so that if we have to hit it a third (fourth, etc.) time,
584
      // it'll have the correct byte counts.
585
      inOff += availInBefore - availInAfter;
586
      availInBefore = availInAfter;
587

    
588
      if (!async) return true;
589

    
590
      var newReq = self._handle.write(flushFlag, chunk, inOff, availInBefore, self._buffer, self._offset, self._chunkSize);
591
      newReq.callback = callback; // this same function
592
      newReq.buffer = chunk;
593
      return;
594
    }
595

    
596
    if (!async) return false;
597

    
598
    // finished with the chunk.
599
    cb();
600
  }
601
};
602

    
603
util.inherits(Deflate, Zlib);
604
util.inherits(Inflate, Zlib);
605
util.inherits(Gzip, Zlib);
606
util.inherits(Gunzip, Zlib);
607
util.inherits(DeflateRaw, Zlib);
608
util.inherits(InflateRaw, Zlib);
609
util.inherits(Unzip, Zlib);
(2-2/2)