doc: improvements to debugger.markdown copy
[platform/upstream/nodejs.git] / lib / zlib.js
1 'use strict';
2
3 const Buffer = require('buffer').Buffer;
4 const Transform = require('_stream_transform');
5 const binding = process.binding('zlib');
6 const util = require('util');
7 const assert = require('assert').ok;
8 const kMaxLength = require('buffer').kMaxLength;
9 const kRangeErrorMessage = 'Cannot create final Buffer. ' +
10     'It would be larger than 0x' + kMaxLength.toString(16) + ' bytes.';
11
12 // zlib doesn't provide these, so kludge them in following the same
13 // const naming scheme zlib uses.
14 binding.Z_MIN_WINDOWBITS = 8;
15 binding.Z_MAX_WINDOWBITS = 15;
16 binding.Z_DEFAULT_WINDOWBITS = 15;
17
18 // fewer than 64 bytes per chunk is stupid.
19 // technically it could work with as few as 8, but even 64 bytes
20 // is absurdly low.  Usually a MB or more is best.
21 binding.Z_MIN_CHUNK = 64;
22 binding.Z_MAX_CHUNK = Infinity;
23 binding.Z_DEFAULT_CHUNK = (16 * 1024);
24
25 binding.Z_MIN_MEMLEVEL = 1;
26 binding.Z_MAX_MEMLEVEL = 9;
27 binding.Z_DEFAULT_MEMLEVEL = 8;
28
29 binding.Z_MIN_LEVEL = -1;
30 binding.Z_MAX_LEVEL = 9;
31 binding.Z_DEFAULT_LEVEL = binding.Z_DEFAULT_COMPRESSION;
32
33 // expose all the zlib constants
34 const bkeys = Object.keys(binding);
35 for (var bk = 0; bk < bkeys.length; bk++) {
36   var bkey = bkeys[bk];
37   if (bkey.match(/^Z/)) {
38     Object.defineProperty(exports, bkey, {
39       enumerable: true, value: binding[bkey], writable: false
40     });
41   }
42 }
43
44 // translation table for return codes.
45 const codes = {
46   Z_OK: binding.Z_OK,
47   Z_STREAM_END: binding.Z_STREAM_END,
48   Z_NEED_DICT: binding.Z_NEED_DICT,
49   Z_ERRNO: binding.Z_ERRNO,
50   Z_STREAM_ERROR: binding.Z_STREAM_ERROR,
51   Z_DATA_ERROR: binding.Z_DATA_ERROR,
52   Z_MEM_ERROR: binding.Z_MEM_ERROR,
53   Z_BUF_ERROR: binding.Z_BUF_ERROR,
54   Z_VERSION_ERROR: binding.Z_VERSION_ERROR
55 };
56
57 const ckeys = Object.keys(codes);
58 for (var ck = 0; ck < ckeys.length; ck++) {
59   var ckey = ckeys[ck];
60   codes[codes[ckey]] = ckey;
61 }
62
63 Object.defineProperty(exports, 'codes', {
64   enumerable: true, value: Object.freeze(codes), writable: false
65 });
66
67 exports.Deflate = Deflate;
68 exports.Inflate = Inflate;
69 exports.Gzip = Gzip;
70 exports.Gunzip = Gunzip;
71 exports.DeflateRaw = DeflateRaw;
72 exports.InflateRaw = InflateRaw;
73 exports.Unzip = Unzip;
74
75 exports.createDeflate = function(o) {
76   return new Deflate(o);
77 };
78
79 exports.createInflate = function(o) {
80   return new Inflate(o);
81 };
82
83 exports.createDeflateRaw = function(o) {
84   return new DeflateRaw(o);
85 };
86
87 exports.createInflateRaw = function(o) {
88   return new InflateRaw(o);
89 };
90
91 exports.createGzip = function(o) {
92   return new Gzip(o);
93 };
94
95 exports.createGunzip = function(o) {
96   return new Gunzip(o);
97 };
98
99 exports.createUnzip = function(o) {
100   return new Unzip(o);
101 };
102
103
104 // Convenience methods.
105 // compress/decompress a string or buffer in one step.
106 exports.deflate = function(buffer, opts, callback) {
107   if (typeof opts === 'function') {
108     callback = opts;
109     opts = {};
110   }
111   return zlibBuffer(new Deflate(opts), buffer, callback);
112 };
113
114 exports.deflateSync = function(buffer, opts) {
115   return zlibBufferSync(new Deflate(opts), buffer);
116 };
117
118 exports.gzip = function(buffer, opts, callback) {
119   if (typeof opts === 'function') {
120     callback = opts;
121     opts = {};
122   }
123   return zlibBuffer(new Gzip(opts), buffer, callback);
124 };
125
126 exports.gzipSync = function(buffer, opts) {
127   return zlibBufferSync(new Gzip(opts), buffer);
128 };
129
130 exports.deflateRaw = function(buffer, opts, callback) {
131   if (typeof opts === 'function') {
132     callback = opts;
133     opts = {};
134   }
135   return zlibBuffer(new DeflateRaw(opts), buffer, callback);
136 };
137
138 exports.deflateRawSync = function(buffer, opts) {
139   return zlibBufferSync(new DeflateRaw(opts), buffer);
140 };
141
142 exports.unzip = function(buffer, opts, callback) {
143   if (typeof opts === 'function') {
144     callback = opts;
145     opts = {};
146   }
147   return zlibBuffer(new Unzip(opts), buffer, callback);
148 };
149
150 exports.unzipSync = function(buffer, opts) {
151   return zlibBufferSync(new Unzip(opts), buffer);
152 };
153
154 exports.inflate = function(buffer, opts, callback) {
155   if (typeof opts === 'function') {
156     callback = opts;
157     opts = {};
158   }
159   return zlibBuffer(new Inflate(opts), buffer, callback);
160 };
161
162 exports.inflateSync = function(buffer, opts) {
163   return zlibBufferSync(new Inflate(opts), buffer);
164 };
165
166 exports.gunzip = function(buffer, opts, callback) {
167   if (typeof opts === 'function') {
168     callback = opts;
169     opts = {};
170   }
171   return zlibBuffer(new Gunzip(opts), buffer, callback);
172 };
173
174 exports.gunzipSync = function(buffer, opts) {
175   return zlibBufferSync(new Gunzip(opts), buffer);
176 };
177
178 exports.inflateRaw = function(buffer, opts, callback) {
179   if (typeof opts === 'function') {
180     callback = opts;
181     opts = {};
182   }
183   return zlibBuffer(new InflateRaw(opts), buffer, callback);
184 };
185
186 exports.inflateRawSync = function(buffer, opts) {
187   return zlibBufferSync(new InflateRaw(opts), buffer);
188 };
189
190 function zlibBuffer(engine, buffer, callback) {
191   var buffers = [];
192   var nread = 0;
193
194   engine.on('error', onError);
195   engine.on('end', onEnd);
196
197   engine.end(buffer);
198   flow();
199
200   function flow() {
201     var chunk;
202     while (null !== (chunk = engine.read())) {
203       buffers.push(chunk);
204       nread += chunk.length;
205     }
206     engine.once('readable', flow);
207   }
208
209   function onError(err) {
210     engine.removeListener('end', onEnd);
211     engine.removeListener('readable', flow);
212     callback(err);
213   }
214
215   function onEnd() {
216     var buf;
217     var err = null;
218
219     if (nread >= kMaxLength) {
220       err = new RangeError(kRangeErrorMessage);
221     } else {
222       buf = Buffer.concat(buffers, nread);
223     }
224
225     buffers = [];
226     engine.close();
227     callback(err, buf);
228   }
229 }
230
231 function zlibBufferSync(engine, buffer) {
232   if (typeof buffer === 'string')
233     buffer = new Buffer(buffer);
234   if (!(buffer instanceof Buffer))
235     throw new TypeError('Not a string or buffer');
236
237   var flushFlag = binding.Z_FINISH;
238
239   return engine._processChunk(buffer, flushFlag);
240 }
241
242 // generic zlib
243 // minimal 2-byte header
244 function Deflate(opts) {
245   if (!(this instanceof Deflate)) return new Deflate(opts);
246   Zlib.call(this, opts, binding.DEFLATE);
247 }
248
249 function Inflate(opts) {
250   if (!(this instanceof Inflate)) return new Inflate(opts);
251   Zlib.call(this, opts, binding.INFLATE);
252 }
253
254
255 // gzip - bigger header, same deflate compression
256 function Gzip(opts) {
257   if (!(this instanceof Gzip)) return new Gzip(opts);
258   Zlib.call(this, opts, binding.GZIP);
259 }
260
261 function Gunzip(opts) {
262   if (!(this instanceof Gunzip)) return new Gunzip(opts);
263   Zlib.call(this, opts, binding.GUNZIP);
264 }
265
266
267 // raw - no header
268 function DeflateRaw(opts) {
269   if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts);
270   Zlib.call(this, opts, binding.DEFLATERAW);
271 }
272
273 function InflateRaw(opts) {
274   if (!(this instanceof InflateRaw)) return new InflateRaw(opts);
275   Zlib.call(this, opts, binding.INFLATERAW);
276 }
277
278
279 // auto-detect header.
280 function Unzip(opts) {
281   if (!(this instanceof Unzip)) return new Unzip(opts);
282   Zlib.call(this, opts, binding.UNZIP);
283 }
284
285
286 // the Zlib class they all inherit from
287 // This thing manages the queue of requests, and returns
288 // true or false if there is anything in the queue when
289 // you call the .write() method.
290
291 function Zlib(opts, mode) {
292   this._opts = opts = opts || {};
293   this._chunkSize = opts.chunkSize || exports.Z_DEFAULT_CHUNK;
294
295   Transform.call(this, opts);
296
297   if (opts.flush) {
298     if (opts.flush !== binding.Z_NO_FLUSH &&
299         opts.flush !== binding.Z_PARTIAL_FLUSH &&
300         opts.flush !== binding.Z_SYNC_FLUSH &&
301         opts.flush !== binding.Z_FULL_FLUSH &&
302         opts.flush !== binding.Z_FINISH &&
303         opts.flush !== binding.Z_BLOCK) {
304       throw new Error('Invalid flush flag: ' + opts.flush);
305     }
306   }
307   this._flushFlag = opts.flush || binding.Z_NO_FLUSH;
308
309   if (opts.chunkSize) {
310     if (opts.chunkSize < exports.Z_MIN_CHUNK ||
311         opts.chunkSize > exports.Z_MAX_CHUNK) {
312       throw new Error('Invalid chunk size: ' + opts.chunkSize);
313     }
314   }
315
316   if (opts.windowBits) {
317     if (opts.windowBits < exports.Z_MIN_WINDOWBITS ||
318         opts.windowBits > exports.Z_MAX_WINDOWBITS) {
319       throw new Error('Invalid windowBits: ' + opts.windowBits);
320     }
321   }
322
323   if (opts.level) {
324     if (opts.level < exports.Z_MIN_LEVEL ||
325         opts.level > exports.Z_MAX_LEVEL) {
326       throw new Error('Invalid compression level: ' + opts.level);
327     }
328   }
329
330   if (opts.memLevel) {
331     if (opts.memLevel < exports.Z_MIN_MEMLEVEL ||
332         opts.memLevel > exports.Z_MAX_MEMLEVEL) {
333       throw new Error('Invalid memLevel: ' + opts.memLevel);
334     }
335   }
336
337   if (opts.strategy) {
338     if (opts.strategy != exports.Z_FILTERED &&
339         opts.strategy != exports.Z_HUFFMAN_ONLY &&
340         opts.strategy != exports.Z_RLE &&
341         opts.strategy != exports.Z_FIXED &&
342         opts.strategy != exports.Z_DEFAULT_STRATEGY) {
343       throw new Error('Invalid strategy: ' + opts.strategy);
344     }
345   }
346
347   if (opts.dictionary) {
348     if (!(opts.dictionary instanceof Buffer)) {
349       throw new Error('Invalid dictionary: it should be a Buffer instance');
350     }
351   }
352
353   this._handle = new binding.Zlib(mode);
354
355   var self = this;
356   this._hadError = false;
357   this._handle.onerror = function(message, errno) {
358     // there is no way to cleanly recover.
359     // continuing only obscures problems.
360     self._handle = null;
361     self._hadError = true;
362
363     var error = new Error(message);
364     error.errno = errno;
365     error.code = exports.codes[errno];
366     self.emit('error', error);
367   };
368
369   var level = exports.Z_DEFAULT_COMPRESSION;
370   if (typeof opts.level === 'number') level = opts.level;
371
372   var strategy = exports.Z_DEFAULT_STRATEGY;
373   if (typeof opts.strategy === 'number') strategy = opts.strategy;
374
375   this._handle.init(opts.windowBits || exports.Z_DEFAULT_WINDOWBITS,
376                     level,
377                     opts.memLevel || exports.Z_DEFAULT_MEMLEVEL,
378                     strategy,
379                     opts.dictionary);
380
381   this._buffer = new Buffer(this._chunkSize);
382   this._offset = 0;
383   this._closed = false;
384   this._level = level;
385   this._strategy = strategy;
386
387   this.once('end', this.close);
388 }
389
390 util.inherits(Zlib, Transform);
391
392 Zlib.prototype.params = function(level, strategy, callback) {
393   if (level < exports.Z_MIN_LEVEL ||
394       level > exports.Z_MAX_LEVEL) {
395     throw new RangeError('Invalid compression level: ' + level);
396   }
397   if (strategy != exports.Z_FILTERED &&
398       strategy != exports.Z_HUFFMAN_ONLY &&
399       strategy != exports.Z_RLE &&
400       strategy != exports.Z_FIXED &&
401       strategy != exports.Z_DEFAULT_STRATEGY) {
402     throw new TypeError('Invalid strategy: ' + strategy);
403   }
404
405   if (this._level !== level || this._strategy !== strategy) {
406     var self = this;
407     this.flush(binding.Z_SYNC_FLUSH, function() {
408       assert(!self._closed, 'zlib binding closed');
409       self._handle.params(level, strategy);
410       if (!self._hadError) {
411         self._level = level;
412         self._strategy = strategy;
413         if (callback) callback();
414       }
415     });
416   } else {
417     process.nextTick(callback);
418   }
419 };
420
421 Zlib.prototype.reset = function() {
422   assert(!this._closed, 'zlib binding closed');
423   return this._handle.reset();
424 };
425
426 // This is the _flush function called by the transform class,
427 // internally, when the last chunk has been written.
428 Zlib.prototype._flush = function(callback) {
429   this._transform(new Buffer(0), '', callback);
430 };
431
432 Zlib.prototype.flush = function(kind, callback) {
433   var ws = this._writableState;
434
435   if (typeof kind === 'function' || (kind === undefined && !callback)) {
436     callback = kind;
437     kind = binding.Z_FULL_FLUSH;
438   }
439
440   if (ws.ended) {
441     if (callback)
442       process.nextTick(callback);
443   } else if (ws.ending) {
444     if (callback)
445       this.once('end', callback);
446   } else if (ws.needDrain) {
447     if (callback) {
448       this.once('drain', () => this.flush(kind, callback));
449     }
450   } else {
451     this._flushFlag = kind;
452     this.write(new Buffer(0), '', callback);
453   }
454 };
455
456 Zlib.prototype.close = function(callback) {
457   if (callback)
458     process.nextTick(callback);
459
460   if (this._closed)
461     return;
462
463   this._closed = true;
464
465   this._handle.close();
466
467   process.nextTick(emitCloseNT, this);
468 };
469
470 function emitCloseNT(self) {
471   self.emit('close');
472 }
473
474 Zlib.prototype._transform = function(chunk, encoding, cb) {
475   var flushFlag;
476   var ws = this._writableState;
477   var ending = ws.ending || ws.ended;
478   var last = ending && (!chunk || ws.length === chunk.length);
479
480   if (chunk !== null && !(chunk instanceof Buffer))
481     return cb(new Error('invalid input'));
482
483   if (this._closed)
484     return cb(new Error('zlib binding closed'));
485
486   // If it's the last chunk, or a final flush, we use the Z_FINISH flush flag.
487   // If it's explicitly flushing at some other time, then we use
488   // Z_FULL_FLUSH. Otherwise, use Z_NO_FLUSH for maximum compression
489   // goodness.
490   if (last)
491     flushFlag = binding.Z_FINISH;
492   else {
493     flushFlag = this._flushFlag;
494     // once we've flushed the last of the queue, stop flushing and
495     // go back to the normal behavior.
496     if (chunk.length >= ws.length) {
497       this._flushFlag = this._opts.flush || binding.Z_NO_FLUSH;
498     }
499   }
500
501   this._processChunk(chunk, flushFlag, cb);
502 };
503
504 Zlib.prototype._processChunk = function(chunk, flushFlag, cb) {
505   var availInBefore = chunk && chunk.length;
506   var availOutBefore = this._chunkSize - this._offset;
507   var inOff = 0;
508
509   var self = this;
510
511   var async = typeof cb === 'function';
512
513   if (!async) {
514     var buffers = [];
515     var nread = 0;
516
517     var error;
518     this.on('error', function(er) {
519       error = er;
520     });
521
522     assert(!this._closed, 'zlib binding closed');
523     do {
524       var res = this._handle.writeSync(flushFlag,
525                                        chunk, // in
526                                        inOff, // in_off
527                                        availInBefore, // in_len
528                                        this._buffer, // out
529                                        this._offset, //out_off
530                                        availOutBefore); // out_len
531     } while (!this._hadError && callback(res[0], res[1]));
532
533     if (this._hadError) {
534       throw error;
535     }
536
537     if (nread >= kMaxLength) {
538       this.close();
539       throw new RangeError(kRangeErrorMessage);
540     }
541
542     var buf = Buffer.concat(buffers, nread);
543     this.close();
544
545     return buf;
546   }
547
548   assert(!this._closed, 'zlib binding closed');
549   var req = this._handle.write(flushFlag,
550                                chunk, // in
551                                inOff, // in_off
552                                availInBefore, // in_len
553                                this._buffer, // out
554                                this._offset, //out_off
555                                availOutBefore); // out_len
556
557   req.buffer = chunk;
558   req.callback = callback;
559
560   function callback(availInAfter, availOutAfter) {
561     if (self._hadError)
562       return;
563
564     var have = availOutBefore - availOutAfter;
565     assert(have >= 0, 'have should not go down');
566
567     if (have > 0) {
568       var out = self._buffer.slice(self._offset, self._offset + have);
569       self._offset += have;
570       // serve some output to the consumer.
571       if (async) {
572         self.push(out);
573       } else {
574         buffers.push(out);
575         nread += out.length;
576       }
577     }
578
579     // exhausted the output buffer, or used all the input create a new one.
580     if (availOutAfter === 0 || self._offset >= self._chunkSize) {
581       availOutBefore = self._chunkSize;
582       self._offset = 0;
583       self._buffer = new Buffer(self._chunkSize);
584     }
585
586     if (availOutAfter === 0) {
587       // Not actually done.  Need to reprocess.
588       // Also, update the availInBefore to the availInAfter value,
589       // so that if we have to hit it a third (fourth, etc.) time,
590       // it'll have the correct byte counts.
591       inOff += (availInBefore - availInAfter);
592       availInBefore = availInAfter;
593
594       if (!async)
595         return true;
596
597       var newReq = self._handle.write(flushFlag,
598                                       chunk,
599                                       inOff,
600                                       availInBefore,
601                                       self._buffer,
602                                       self._offset,
603                                       self._chunkSize);
604       newReq.callback = callback; // this same function
605       newReq.buffer = chunk;
606       return;
607     }
608
609     if (!async)
610       return false;
611
612     // finished with the chunk.
613     cb();
614   }
615 };
616
617 util.inherits(Deflate, Zlib);
618 util.inherits(Inflate, Zlib);
619 util.inherits(Gzip, Zlib);
620 util.inherits(Gunzip, Zlib);
621 util.inherits(DeflateRaw, Zlib);
622 util.inherits(InflateRaw, Zlib);
623 util.inherits(Unzip, Zlib);