From 8e69f7e38552a4c65064f2829d9ca973ad9b05ba Mon Sep 17 00:00:00 2001 From: James M Snell Date: Tue, 14 Feb 2017 17:36:12 -0800 Subject: [PATCH] zlib: refactor zlib module MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-URL: https://github.com/nodejs/node/pull/11391 Reviewed-By: Michaƫl Zasso Reviewed-By: Anna Henningsen Reviewed-By: Sam Roberts --- lib/zlib.js | 865 ++++++++++++++++++++++++---------------------------- 1 file changed, 403 insertions(+), 462 deletions(-) diff --git a/lib/zlib.js b/lib/zlib.js index b6817749bae35d..19328d381adb60 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -1,31 +1,16 @@ 'use strict'; const Buffer = require('buffer').Buffer; +const internalUtil = require('internal/util'); const Transform = require('_stream_transform'); const binding = process.binding('zlib'); -const util = require('util'); const assert = require('assert').ok; const kMaxLength = require('buffer').kMaxLength; const kRangeErrorMessage = 'Cannot create final Buffer. It would be larger ' + - 'than 0x' + kMaxLength.toString(16) + ' bytes'; + `than 0x${kMaxLength.toString(16)} bytes`; const constants = process.binding('constants').zlib; - -// These should be considered deprecated -// expose all the zlib constants -const bkeys = Object.keys(constants); -for (var bk = 0; bk < bkeys.length; bk++) { - var bkey = bkeys[bk]; - Object.defineProperty(exports, bkey, { - enumerable: true, value: constants[bkey], writable: false - }); -} - -Object.defineProperty(exports, 'constants', { - configurable: false, - enumerable: true, - value: constants -}); +const createClassWrapper = internalUtil.createClassWrapper; // translation table for return codes. const codes = { @@ -46,132 +31,29 @@ for (var ck = 0; ck < ckeys.length; ck++) { codes[codes[ckey]] = ckey; } -Object.defineProperty(exports, 'codes', { - enumerable: true, value: Object.freeze(codes), writable: false -}); - -exports.Deflate = Deflate; -exports.Inflate = Inflate; -exports.Gzip = Gzip; -exports.Gunzip = Gunzip; -exports.DeflateRaw = DeflateRaw; -exports.InflateRaw = InflateRaw; -exports.Unzip = Unzip; - -exports.createDeflate = function(o) { - return new Deflate(o); -}; - -exports.createInflate = function(o) { - return new Inflate(o); -}; - -exports.createDeflateRaw = function(o) { - return new DeflateRaw(o); -}; - -exports.createInflateRaw = function(o) { - return new InflateRaw(o); -}; - -exports.createGzip = function(o) { - return new Gzip(o); -}; - -exports.createGunzip = function(o) { - return new Gunzip(o); -}; - -exports.createUnzip = function(o) { - return new Unzip(o); -}; - - -// Convenience methods. -// compress/decompress a string or buffer in one step. -exports.deflate = function(buffer, opts, callback) { - if (typeof opts === 'function') { - callback = opts; - opts = {}; - } - return zlibBuffer(new Deflate(opts), buffer, callback); -}; - -exports.deflateSync = function(buffer, opts) { - return zlibBufferSync(new Deflate(opts), buffer); -}; - -exports.gzip = function(buffer, opts, callback) { - if (typeof opts === 'function') { - callback = opts; - opts = {}; - } - return zlibBuffer(new Gzip(opts), buffer, callback); -}; - -exports.gzipSync = function(buffer, opts) { - return zlibBufferSync(new Gzip(opts), buffer); -}; - -exports.deflateRaw = function(buffer, opts, callback) { - if (typeof opts === 'function') { - callback = opts; - opts = {}; - } - return zlibBuffer(new DeflateRaw(opts), buffer, callback); -}; - -exports.deflateRawSync = function(buffer, opts) { - return zlibBufferSync(new DeflateRaw(opts), buffer); -}; - -exports.unzip = function(buffer, opts, callback) { - if (typeof opts === 'function') { - callback = opts; - opts = {}; - } - return zlibBuffer(new Unzip(opts), buffer, callback); -}; - -exports.unzipSync = function(buffer, opts) { - return zlibBufferSync(new Unzip(opts), buffer); -}; - -exports.inflate = function(buffer, opts, callback) { - if (typeof opts === 'function') { - callback = opts; - opts = {}; - } - return zlibBuffer(new Inflate(opts), buffer, callback); -}; - -exports.inflateSync = function(buffer, opts) { - return zlibBufferSync(new Inflate(opts), buffer); -}; - -exports.gunzip = function(buffer, opts, callback) { - if (typeof opts === 'function') { - callback = opts; - opts = {}; - } - return zlibBuffer(new Gunzip(opts), buffer, callback); -}; - -exports.gunzipSync = function(buffer, opts) { - return zlibBufferSync(new Gunzip(opts), buffer); -}; +function isValidFlushFlag(flag) { + return flag >= constants.Z_NO_FLUSH && + flag <= constants.Z_BLOCK; + + // Covers: constants.Z_NO_FLUSH (0), + // constants.Z_PARTIAL_FLUSH (1), + // constants.Z_SYNC_FLUSH (2), + // constants.Z_FULL_FLUSH (3), + // constants.Z_FINISH (4), and + // constants.Z_BLOCK (5) +} -exports.inflateRaw = function(buffer, opts, callback) { - if (typeof opts === 'function') { - callback = opts; - opts = {}; - } - return zlibBuffer(new InflateRaw(opts), buffer, callback); -}; +function isInvalidStrategy(strategy) { + return typeof strategy !== 'number' || + strategy < constants.Z_DEFAULT_STRATEGY || + strategy > constants.Z_FIXED; -exports.inflateRawSync = function(buffer, opts) { - return zlibBufferSync(new InflateRaw(opts), buffer); -}; + // Covers: constants.Z_FILTERED, (1) + // constants.Z_HUFFMAN_ONLY (2), + // constants.Z_RLE (3), + // constants.Z_FIXED (4), and + // constants.Z_DEFAULT_STRATEGY (0) +} function zlibBuffer(engine, buffer, callback) { var buffers = []; @@ -225,230 +107,326 @@ function zlibBufferSync(engine, buffer) { return engine._processChunk(buffer, flushFlag); } -// generic zlib -// minimal 2-byte header -function Deflate(opts) { - if (!(this instanceof Deflate)) return new Deflate(opts); - Zlib.call(this, opts, constants.DEFLATE); +function zlibOnError(message, errno) { + // there is no way to cleanly recover. + // continuing only obscures problems. + _close(this); + this._hadError = true; + + var error = new Error(message); + error.errno = errno; + error.code = codes[errno]; + this.emit('error', error); } -function Inflate(opts) { - if (!(this instanceof Inflate)) return new Inflate(opts); - Zlib.call(this, opts, constants.INFLATE); +function flushCallback(level, strategy, callback) { + assert(this._handle, 'zlib binding closed'); + this._handle.params(level, strategy); + if (!this._hadError) { + this._level = level; + this._strategy = strategy; + if (callback) callback(); + } } +// the Zlib class they all inherit from +// This thing manages the queue of requests, and returns +// true or false if there is anything in the queue when +// you call the .write() method. +class Zlib extends Transform { + constructor(opts, mode) { + opts = opts || {}; + super(opts); -// gzip - bigger header, same deflate compression -function Gzip(opts) { - if (!(this instanceof Gzip)) return new Gzip(opts); - Zlib.call(this, opts, constants.GZIP); -} + this._opts = opts; + this._chunkSize = opts.chunkSize || constants.Z_DEFAULT_CHUNK; -function Gunzip(opts) { - if (!(this instanceof Gunzip)) return new Gunzip(opts); - Zlib.call(this, opts, constants.GUNZIP); -} + if (opts.flush && !isValidFlushFlag(opts.flush)) { + throw new Error('Invalid flush flag: ' + opts.flush); + } + if (opts.finishFlush && !isValidFlushFlag(opts.finishFlush)) { + throw new Error('Invalid flush flag: ' + opts.finishFlush); + } + this._flushFlag = opts.flush || constants.Z_NO_FLUSH; + this._finishFlushFlag = opts.finishFlush !== undefined ? + opts.finishFlush : constants.Z_FINISH; -// raw - no header -function DeflateRaw(opts) { - if (!(this instanceof DeflateRaw)) return new DeflateRaw(opts); - Zlib.call(this, opts, constants.DEFLATERAW); -} + if (opts.chunkSize) { + if (opts.chunkSize < constants.Z_MIN_CHUNK) { + throw new Error('Invalid chunk size: ' + opts.chunkSize); + } + } -function InflateRaw(opts) { - if (!(this instanceof InflateRaw)) return new InflateRaw(opts); - Zlib.call(this, opts, constants.INFLATERAW); -} + if (opts.windowBits) { + if (opts.windowBits < constants.Z_MIN_WINDOWBITS || + opts.windowBits > constants.Z_MAX_WINDOWBITS) { + throw new Error('Invalid windowBits: ' + opts.windowBits); + } + } + if (opts.level) { + if (opts.level < constants.Z_MIN_LEVEL || + opts.level > constants.Z_MAX_LEVEL) { + throw new Error('Invalid compression level: ' + opts.level); + } + } -// auto-detect header. -function Unzip(opts) { - if (!(this instanceof Unzip)) return new Unzip(opts); - Zlib.call(this, opts, constants.UNZIP); -} + if (opts.memLevel) { + if (opts.memLevel < constants.Z_MIN_MEMLEVEL || + opts.memLevel > constants.Z_MAX_MEMLEVEL) { + throw new Error('Invalid memLevel: ' + opts.memLevel); + } + } -function isValidFlushFlag(flag) { - return flag === constants.Z_NO_FLUSH || - flag === constants.Z_PARTIAL_FLUSH || - flag === constants.Z_SYNC_FLUSH || - flag === constants.Z_FULL_FLUSH || - flag === constants.Z_FINISH || - flag === constants.Z_BLOCK; -} + if (opts.strategy && isInvalidStrategy(opts.strategy)) + throw new Error('Invalid strategy: ' + opts.strategy); -const strategies = [constants.Z_FILTERED, - constants.Z_HUFFMAN_ONLY, - constants.Z_RLE, - constants.Z_FIXED, - constants.Z_DEFAULT_STRATEGY]; + if (opts.dictionary) { + if (!(opts.dictionary instanceof Buffer)) { + throw new Error('Invalid dictionary: it should be a Buffer instance'); + } + } -// the Zlib class they all inherit from -// This thing manages the queue of requests, and returns -// true or false if there is anything in the queue when -// you call the .write() method. + this._handle = new binding.Zlib(mode); + this._handle.onerror = zlibOnError.bind(this); + this._hadError = false; -function Zlib(opts, mode) { - this._opts = opts = opts || {}; - this._chunkSize = opts.chunkSize || constants.Z_DEFAULT_CHUNK; + var level = constants.Z_DEFAULT_COMPRESSION; + if (typeof opts.level === 'number') level = opts.level; - Transform.call(this, opts); + var strategy = constants.Z_DEFAULT_STRATEGY; + if (typeof opts.strategy === 'number') strategy = opts.strategy; - if (opts.flush && !isValidFlushFlag(opts.flush)) { - throw new Error('Invalid flush flag: ' + opts.flush); - } - if (opts.finishFlush && !isValidFlushFlag(opts.finishFlush)) { - throw new Error('Invalid flush flag: ' + opts.finishFlush); - } + this._handle.init(opts.windowBits || constants.Z_DEFAULT_WINDOWBITS, + level, + opts.memLevel || constants.Z_DEFAULT_MEMLEVEL, + strategy, + opts.dictionary); - this._flushFlag = opts.flush || constants.Z_NO_FLUSH; - this._finishFlushFlag = typeof opts.finishFlush !== 'undefined' ? - opts.finishFlush : constants.Z_FINISH; + this._buffer = Buffer.allocUnsafe(this._chunkSize); + this._offset = 0; + this._level = level; + this._strategy = strategy; - if (opts.chunkSize) { - if (opts.chunkSize < constants.Z_MIN_CHUNK) { - throw new Error('Invalid chunk size: ' + opts.chunkSize); - } + this.once('end', this.close); } - if (opts.windowBits) { - if (opts.windowBits < constants.Z_MIN_WINDOWBITS || - opts.windowBits > constants.Z_MAX_WINDOWBITS) { - throw new Error('Invalid windowBits: ' + opts.windowBits); - } + get _closed() { + return !this._handle; } - if (opts.level) { - if (opts.level < constants.Z_MIN_LEVEL || - opts.level > constants.Z_MAX_LEVEL) { - throw new Error('Invalid compression level: ' + opts.level); + params(level, strategy, callback) { + if (level < constants.Z_MIN_LEVEL || + level > constants.Z_MAX_LEVEL) { + throw new RangeError('Invalid compression level: ' + level); } - } + if (isInvalidStrategy(strategy)) + throw new TypeError('Invalid strategy: ' + strategy); - if (opts.memLevel) { - if (opts.memLevel < constants.Z_MIN_MEMLEVEL || - opts.memLevel > constants.Z_MAX_MEMLEVEL) { - throw new Error('Invalid memLevel: ' + opts.memLevel); + if (this._level !== level || this._strategy !== strategy) { + this.flush(constants.Z_SYNC_FLUSH, + flushCallback.bind(this, level, strategy, callback)); + } else { + process.nextTick(callback); } } - if (opts.strategy && !(strategies.includes(opts.strategy))) - throw new Error('Invalid strategy: ' + opts.strategy); + reset() { + assert(this._handle, 'zlib binding closed'); + return this._handle.reset(); + } - if (opts.dictionary) { - if (!(opts.dictionary instanceof Buffer)) { - throw new Error('Invalid dictionary: it should be a Buffer instance'); - } + // This is the _flush function called by the transform class, + // internally, when the last chunk has been written. + _flush(callback) { + this._transform(Buffer.alloc(0), '', callback); } - this._handle = new binding.Zlib(mode); + flush(kind, callback) { + var ws = this._writableState; - var self = this; - this._hadError = false; - this._handle.onerror = function(message, errno) { - // there is no way to cleanly recover. - // continuing only obscures problems. - _close(self); - self._hadError = true; + if (typeof kind === 'function' || (kind === undefined && !callback)) { + callback = kind; + kind = constants.Z_FULL_FLUSH; + } - var error = new Error(message); - error.errno = errno; - error.code = exports.codes[errno]; - self.emit('error', error); - }; + if (ws.ended) { + if (callback) + process.nextTick(callback); + } else if (ws.ending) { + if (callback) + this.once('end', callback); + } else if (ws.needDrain) { + if (callback) { + const drainHandler = () => this.flush(kind, callback); + this.once('drain', drainHandler); + } + } else { + this._flushFlag = kind; + this.write(Buffer.alloc(0), '', callback); + } + } - var level = constants.Z_DEFAULT_COMPRESSION; - if (typeof opts.level === 'number') level = opts.level; + close(callback) { + _close(this, callback); + process.nextTick(emitCloseNT, this); + } - var strategy = constants.Z_DEFAULT_STRATEGY; - if (typeof opts.strategy === 'number') strategy = opts.strategy; + _transform(chunk, encoding, cb) { + var flushFlag; + var ws = this._writableState; + var ending = ws.ending || ws.ended; + var last = ending && (!chunk || ws.length === chunk.length); + + if (chunk !== null && !(chunk instanceof Buffer)) + return cb(new Error('invalid input')); + + if (!this._handle) + return cb(new Error('zlib binding closed')); + + // If it's the last chunk, or a final flush, we use the Z_FINISH flush flag + // (or whatever flag was provided using opts.finishFlush). + // If it's explicitly flushing at some other time, then we use + // Z_FULL_FLUSH. Otherwise, use Z_NO_FLUSH for maximum compression + // goodness. + if (last) + flushFlag = this._finishFlushFlag; + else { + flushFlag = this._flushFlag; + // once we've flushed the last of the queue, stop flushing and + // go back to the normal behavior. + if (chunk.length >= ws.length) { + this._flushFlag = this._opts.flush || constants.Z_NO_FLUSH; + } + } - this._handle.init(opts.windowBits || constants.Z_DEFAULT_WINDOWBITS, - level, - opts.memLevel || constants.Z_DEFAULT_MEMLEVEL, - strategy, - opts.dictionary); + this._processChunk(chunk, flushFlag, cb); + } - this._buffer = Buffer.allocUnsafe(this._chunkSize); - this._offset = 0; - this._level = level; - this._strategy = strategy; + _processChunk(chunk, flushFlag, cb) { + var availInBefore = chunk && chunk.length; + var availOutBefore = this._chunkSize - this._offset; + var inOff = 0; - this.once('end', this.close); + var self = this; - Object.defineProperty(this, '_closed', { - get: () => !this._handle, - configurable: true, - enumerable: true - }); -} + var async = typeof cb === 'function'; + + if (!async) { + var buffers = []; + var nread = 0; + + var error; + this.on('error', function(er) { + error = er; + }); + + assert(this._handle, 'zlib binding closed'); + do { + var res = this._handle.writeSync(flushFlag, + chunk, // in + inOff, // in_off + availInBefore, // in_len + this._buffer, // out + this._offset, //out_off + availOutBefore); // out_len + } while (!this._hadError && callback(res[0], res[1])); + + if (this._hadError) { + throw error; + } -util.inherits(Zlib, Transform); + if (nread >= kMaxLength) { + _close(this); + throw new RangeError(kRangeErrorMessage); + } -Zlib.prototype.params = function(level, strategy, callback) { - if (level < constants.Z_MIN_LEVEL || - level > constants.Z_MAX_LEVEL) { - throw new RangeError('Invalid compression level: ' + level); - } - if (!(strategies.includes(strategy))) - throw new TypeError('Invalid strategy: ' + strategy); + var buf = Buffer.concat(buffers, nread); + _close(this); - if (this._level !== level || this._strategy !== strategy) { - var self = this; - this.flush(constants.Z_SYNC_FLUSH, function flushCallback() { - assert(self._handle, 'zlib binding closed'); - self._handle.params(level, strategy); - if (!self._hadError) { - self._level = level; - self._strategy = strategy; - if (callback) callback(); + return buf; + } + + assert(this._handle, 'zlib binding closed'); + var req = this._handle.write(flushFlag, + chunk, // in + inOff, // in_off + availInBefore, // in_len + this._buffer, // out + this._offset, //out_off + availOutBefore); // out_len + + req.buffer = chunk; + req.callback = callback; + + function callback(availInAfter, availOutAfter) { + // When the callback is used in an async write, the callback's + // context is the `req` object that was created. The req object + // is === this._handle, and that's why it's important to null + // out the values after they are done being used. `this._handle` + // can stay in memory longer than the callback and buffer are needed. + if (this) { + this.buffer = null; + this.callback = null; } - }); - } else { - process.nextTick(callback); - } -}; -Zlib.prototype.reset = function() { - assert(this._handle, 'zlib binding closed'); - return this._handle.reset(); -}; + if (self._hadError) + return; + + var have = availOutBefore - availOutAfter; + assert(have >= 0, 'have should not go down'); + + if (have > 0) { + var out = self._buffer.slice(self._offset, self._offset + have); + self._offset += have; + // serve some output to the consumer. + if (async) { + self.push(out); + } else { + buffers.push(out); + nread += out.length; + } + } -// This is the _flush function called by the transform class, -// internally, when the last chunk has been written. -Zlib.prototype._flush = function(callback) { - this._transform(Buffer.alloc(0), '', callback); -}; + // exhausted the output buffer, or used all the input create a new one. + if (availOutAfter === 0 || self._offset >= self._chunkSize) { + availOutBefore = self._chunkSize; + self._offset = 0; + self._buffer = Buffer.allocUnsafe(self._chunkSize); + } -Zlib.prototype.flush = function(kind, callback) { - var ws = this._writableState; + if (availOutAfter === 0) { + // Not actually done. Need to reprocess. + // Also, update the availInBefore to the availInAfter value, + // so that if we have to hit it a third (fourth, etc.) time, + // it'll have the correct byte counts. + inOff += (availInBefore - availInAfter); + availInBefore = availInAfter; + + if (!async) + return true; + + var newReq = self._handle.write(flushFlag, + chunk, + inOff, + availInBefore, + self._buffer, + self._offset, + self._chunkSize); + newReq.callback = callback; // this same function + newReq.buffer = chunk; + return; + } - if (typeof kind === 'function' || (kind === undefined && !callback)) { - callback = kind; - kind = constants.Z_FULL_FLUSH; - } + if (!async) + return false; - if (ws.ended) { - if (callback) - process.nextTick(callback); - } else if (ws.ending) { - if (callback) - this.once('end', callback); - } else if (ws.needDrain) { - if (callback) { - const drainHandler = () => this.flush(kind, callback); - this.once('drain', drainHandler); + // finished with the chunk. + cb(); } - } else { - this._flushFlag = kind; - this.write(Buffer.alloc(0), '', callback); } -}; - -Zlib.prototype.close = function(callback) { - _close(this, callback); - process.nextTick(emitCloseNT, this); -}; +} function _close(engine, callback) { if (callback) @@ -466,164 +444,127 @@ function emitCloseNT(self) { self.emit('close'); } -Zlib.prototype._transform = function(chunk, encoding, cb) { - var flushFlag; - var ws = this._writableState; - var ending = ws.ending || ws.ended; - var last = ending && (!chunk || ws.length === chunk.length); - - if (chunk !== null && !(chunk instanceof Buffer)) - return cb(new Error('invalid input')); - - if (!this._handle) - return cb(new Error('zlib binding closed')); - - // If it's the last chunk, or a final flush, we use the Z_FINISH flush flag - // (or whatever flag was provided using opts.finishFlush). - // If it's explicitly flushing at some other time, then we use - // Z_FULL_FLUSH. Otherwise, use Z_NO_FLUSH for maximum compression - // goodness. - if (last) - flushFlag = this._finishFlushFlag; - else { - flushFlag = this._flushFlag; - // once we've flushed the last of the queue, stop flushing and - // go back to the normal behavior. - if (chunk.length >= ws.length) { - this._flushFlag = this._opts.flush || constants.Z_NO_FLUSH; - } +// generic zlib +// minimal 2-byte header +class Deflate extends Zlib { + constructor(opts) { + super(opts, constants.DEFLATE); } +} - this._processChunk(chunk, flushFlag, cb); -}; - -Zlib.prototype._processChunk = function(chunk, flushFlag, cb) { - var availInBefore = chunk && chunk.length; - var availOutBefore = this._chunkSize - this._offset; - var inOff = 0; - - var self = this; - - var async = typeof cb === 'function'; - - if (!async) { - var buffers = []; - var nread = 0; - - var error; - this.on('error', function(er) { - error = er; - }); +class Inflate extends Zlib { + constructor(opts) { + super(opts, constants.INFLATE); + } +} - assert(this._handle, 'zlib binding closed'); - do { - var res = this._handle.writeSync(flushFlag, - chunk, // in - inOff, // in_off - availInBefore, // in_len - this._buffer, // out - this._offset, //out_off - availOutBefore); // out_len - } while (!this._hadError && callback(res[0], res[1])); - - if (this._hadError) { - throw error; - } +class Gzip extends Zlib { + constructor(opts) { + super(opts, constants.GZIP); + } +} - if (nread >= kMaxLength) { - _close(this); - throw new RangeError(kRangeErrorMessage); - } +class Gunzip extends Zlib { + constructor(opts) { + super(opts, constants.GUNZIP); + } +} - var buf = Buffer.concat(buffers, nread); - _close(this); +class DeflateRaw extends Zlib { + constructor(opts) { + super(opts, constants.DEFLATERAW); + } +} - return buf; +class InflateRaw extends Zlib { + constructor(opts) { + super(opts, constants.INFLATERAW); } +} - assert(this._handle, 'zlib binding closed'); - var req = this._handle.write(flushFlag, - chunk, // in - inOff, // in_off - availInBefore, // in_len - this._buffer, // out - this._offset, //out_off - availOutBefore); // out_len - - req.buffer = chunk; - req.callback = callback; - - function callback(availInAfter, availOutAfter) { - // When the callback is used in an async write, the callback's - // context is the `req` object that was created. The req object - // is === this._handle, and that's why it's important to null - // out the values after they are done being used. `this._handle` - // can stay in memory longer than the callback and buffer are needed. - if (this) { - this.buffer = null; - this.callback = null; - } +class Unzip extends Zlib { + constructor(opts) { + super(opts, constants.UNZIP); + } +} - if (self._hadError) - return; - - var have = availOutBefore - availOutAfter; - assert(have >= 0, 'have should not go down'); - - if (have > 0) { - var out = self._buffer.slice(self._offset, self._offset + have); - self._offset += have; - // serve some output to the consumer. - if (async) { - self.push(out); - } else { - buffers.push(out); - nread += out.length; +function createConvenienceMethod(type, sync) { + if (sync) { + return function(buffer, opts) { + return zlibBufferSync(new type(opts), buffer); + }; + } else { + return function(buffer, opts, callback) { + if (typeof opts === 'function') { + callback = opts; + opts = {}; } - } - - // exhausted the output buffer, or used all the input create a new one. - if (availOutAfter === 0 || self._offset >= self._chunkSize) { - availOutBefore = self._chunkSize; - self._offset = 0; - self._buffer = Buffer.allocUnsafe(self._chunkSize); - } - - if (availOutAfter === 0) { - // Not actually done. Need to reprocess. - // Also, update the availInBefore to the availInAfter value, - // so that if we have to hit it a third (fourth, etc.) time, - // it'll have the correct byte counts. - inOff += (availInBefore - availInAfter); - availInBefore = availInAfter; + return zlibBuffer(new type(opts), buffer, callback); + }; + } +} - if (!async) - return true; - - var newReq = self._handle.write(flushFlag, - chunk, - inOff, - availInBefore, - self._buffer, - self._offset, - self._chunkSize); - newReq.callback = callback; // this same function - newReq.buffer = chunk; - return; - } +function createProperty(type) { + return { + configurable: true, + enumerable: true, + value: type + }; +} - if (!async) - return false; +module.exports = { + Deflate: createClassWrapper(Deflate), + Inflate: createClassWrapper(Inflate), + Gzip: createClassWrapper(Gzip), + Gunzip: createClassWrapper(Gunzip), + DeflateRaw: createClassWrapper(DeflateRaw), + InflateRaw: createClassWrapper(InflateRaw), + Unzip: createClassWrapper(Unzip), + + // Convenience methods. + // compress/decompress a string or buffer in one step. + deflate: createConvenienceMethod(Deflate, false), + deflateSync: createConvenienceMethod(Deflate, true), + gzip: createConvenienceMethod(Gzip, false), + gzipSync: createConvenienceMethod(Gzip, true), + deflateRaw: createConvenienceMethod(DeflateRaw, false), + deflateRawSync: createConvenienceMethod(DeflateRaw, true), + unzip: createConvenienceMethod(Unzip, false), + unzipSync: createConvenienceMethod(Unzip, true), + inflate: createConvenienceMethod(Inflate, false), + inflateSync: createConvenienceMethod(Inflate, true), + gunzip: createConvenienceMethod(Gunzip, false), + gunzipSync: createConvenienceMethod(Gunzip, true), + inflateRaw: createConvenienceMethod(InflateRaw, false), + inflateRawSync: createConvenienceMethod(InflateRaw, true) +}; - // finished with the chunk. - cb(); +Object.defineProperties(module.exports, { + createDeflate: createProperty(module.exports.Deflate), + createInflate: createProperty(module.exports.Inflate), + createDeflateRaw: createProperty(module.exports.DeflateRaw), + createInflateRaw: createProperty(module.exports.InflateRaw), + createGzip: createProperty(module.exports.Gzip), + createGunzip: createProperty(module.exports.Gunzip), + createUnzip: createProperty(module.exports.Unzip), + constants: { + configurable: false, + enumerable: true, + value: constants + }, + codes: { + enumerable: true, + writable: false, + value: Object.freeze(codes) } -}; +}); -util.inherits(Deflate, Zlib); -util.inherits(Inflate, Zlib); -util.inherits(Gzip, Zlib); -util.inherits(Gunzip, Zlib); -util.inherits(DeflateRaw, Zlib); -util.inherits(InflateRaw, Zlib); -util.inherits(Unzip, Zlib); +// These should be considered deprecated +// expose all the zlib constants +const bkeys = Object.keys(constants); +for (var bk = 0; bk < bkeys.length; bk++) { + var bkey = bkeys[bk]; + Object.defineProperty(module.exports, bkey, { + enumerable: true, value: constants[bkey], writable: false + }); +}