Skip to content

Commit e534dcd

Browse files
addaleaxtargos
authored andcommitted
zlib: split JS code as prep for non-zlib-backed streams
Split the `Zlib` class into `ZlibBase` and `Zlib` classes, to facilitate introduction of similar streams with minor implementation differences. Backport-PR-URL: #25228 PR-URL: #24939 Reviewed-By: Ben Noordhuis <[email protected]>
1 parent d718625 commit e534dcd

File tree

1 file changed

+128
-111
lines changed

1 file changed

+128
-111
lines changed

lib/zlib.js

+128-111
Original file line numberDiff line numberDiff line change
@@ -206,21 +206,10 @@ function checkRangesOrGetDefault(number, name, lower, upper, def) {
206206
return number;
207207
}
208208

209-
// the Zlib class they all inherit from
210-
// This thing manages the queue of requests, and returns
211-
// true or false if there is anything in the queue when
212-
// you call the .write() method.
213-
function Zlib(opts, mode) {
209+
// The base class for all Zlib-style streams.
210+
function ZlibBase(opts, mode, handle, { flush, finishFlush, fullFlush }) {
214211
var chunkSize = Z_DEFAULT_CHUNK;
215-
var flush = Z_NO_FLUSH;
216-
var finishFlush = Z_FINISH;
217-
var windowBits = Z_DEFAULT_WINDOWBITS;
218-
var level = Z_DEFAULT_COMPRESSION;
219-
var memLevel = Z_DEFAULT_MEMLEVEL;
220-
var strategy = Z_DEFAULT_STRATEGY;
221-
var dictionary;
222-
223-
// The Zlib class is not exported to user land, the mode should only be
212+
// The ZlibBase class is not exported to user land, the mode should only be
224213
// passed in by us.
225214
assert(typeof mode === 'number');
226215
assert(mode >= DEFLATE && mode <= UNZIP);
@@ -236,50 +225,11 @@ function Zlib(opts, mode) {
236225

237226
flush = checkRangesOrGetDefault(
238227
opts.flush, 'options.flush',
239-
Z_NO_FLUSH, Z_BLOCK, Z_NO_FLUSH);
228+
Z_NO_FLUSH, Z_BLOCK, flush);
240229

241230
finishFlush = checkRangesOrGetDefault(
242231
opts.finishFlush, 'options.finishFlush',
243-
Z_NO_FLUSH, Z_BLOCK, Z_FINISH);
244-
245-
// windowBits is special. On the compression side, 0 is an invalid value.
246-
// But on the decompression side, a value of 0 for windowBits tells zlib
247-
// to use the window size in the zlib header of the compressed stream.
248-
if ((opts.windowBits == null || opts.windowBits === 0) &&
249-
(mode === INFLATE ||
250-
mode === GUNZIP ||
251-
mode === UNZIP)) {
252-
windowBits = 0;
253-
} else {
254-
windowBits = checkRangesOrGetDefault(
255-
opts.windowBits, 'options.windowBits',
256-
Z_MIN_WINDOWBITS, Z_MAX_WINDOWBITS, Z_DEFAULT_WINDOWBITS);
257-
}
258-
259-
level = checkRangesOrGetDefault(
260-
opts.level, 'options.level',
261-
Z_MIN_LEVEL, Z_MAX_LEVEL, Z_DEFAULT_COMPRESSION);
262-
263-
memLevel = checkRangesOrGetDefault(
264-
opts.memLevel, 'options.memLevel',
265-
Z_MIN_MEMLEVEL, Z_MAX_MEMLEVEL, Z_DEFAULT_MEMLEVEL);
266-
267-
strategy = checkRangesOrGetDefault(
268-
opts.strategy, 'options.strategy',
269-
Z_DEFAULT_STRATEGY, Z_FIXED, Z_DEFAULT_STRATEGY);
270-
271-
dictionary = opts.dictionary;
272-
if (dictionary !== undefined && !isArrayBufferView(dictionary)) {
273-
if (isAnyArrayBuffer(dictionary)) {
274-
dictionary = Buffer.from(dictionary);
275-
} else {
276-
throw new ERR_INVALID_ARG_TYPE(
277-
'options.dictionary',
278-
['Buffer', 'TypedArray', 'DataView', 'ArrayBuffer'],
279-
dictionary
280-
);
281-
}
282-
}
232+
Z_NO_FLUSH, Z_BLOCK, finishFlush);
283233

284234
if (opts.encoding || opts.objectMode || opts.writableObjectMode) {
285235
opts = _extend({}, opts);
@@ -288,39 +238,29 @@ function Zlib(opts, mode) {
288238
opts.writableObjectMode = false;
289239
}
290240
}
241+
291242
Transform.call(this, opts);
243+
this._hadError = false;
292244
this.bytesWritten = 0;
293-
this._handle = new binding.Zlib(mode);
245+
this._handle = handle;
246+
handle[owner_symbol] = this;
294247
// Used by processCallback() and zlibOnError()
295-
this._handle[owner_symbol] = this;
296-
this._handle.onerror = zlibOnError;
297-
this._hadError = false;
298-
this._writeState = new Uint32Array(2);
299-
300-
if (!this._handle.init(windowBits,
301-
level,
302-
memLevel,
303-
strategy,
304-
this._writeState,
305-
processCallback,
306-
dictionary)) {
307-
throw new ERR_ZLIB_INITIALIZATION_FAILED();
308-
}
309-
248+
handle.onerror = zlibOnError;
310249
this._outBuffer = Buffer.allocUnsafe(chunkSize);
311250
this._outOffset = 0;
312-
this._level = level;
313-
this._strategy = strategy;
251+
314252
this._chunkSize = chunkSize;
315253
this._defaultFlushFlag = flush;
316254
this._finishFlushFlag = finishFlush;
317255
this._nextFlush = -1;
318-
this._info = opts && opts.info;
256+
this._defaultFullFlushFlag = fullFlush;
319257
this.once('end', this.close);
258+
this._info = opts && opts.info;
320259
}
321-
inherits(Zlib, Transform);
322260

323-
Object.defineProperty(Zlib.prototype, '_closed', {
261+
inherits(ZlibBase, Transform);
262+
263+
Object.defineProperty(ZlibBase.prototype, '_closed', {
324264
configurable: true,
325265
enumerable: true,
326266
get() {
@@ -332,7 +272,7 @@ Object.defineProperty(Zlib.prototype, '_closed', {
332272
// perspective, but it is inconsistent with all other streams exposed by Node.js
333273
// that have this concept, where it stands for the number of bytes read
334274
// *from* the stream (that is, net.Socket/tls.Socket & file system streams).
335-
Object.defineProperty(Zlib.prototype, 'bytesRead', {
275+
Object.defineProperty(ZlibBase.prototype, 'bytesRead', {
336276
configurable: true,
337277
enumerable: true,
338278
get: deprecate(function() {
@@ -345,41 +285,15 @@ Object.defineProperty(Zlib.prototype, 'bytesRead', {
345285
'This feature will be removed in the future.', 'DEP0108')
346286
});
347287

348-
// This callback is used by `.params()` to wait until a full flush happened
349-
// before adjusting the parameters. In particular, the call to the native
350-
// `params()` function should not happen while a write is currently in progress
351-
// on the threadpool.
352-
function paramsAfterFlushCallback(level, strategy, callback) {
353-
assert(this._handle, 'zlib binding closed');
354-
this._handle.params(level, strategy);
355-
if (!this._hadError) {
356-
this._level = level;
357-
this._strategy = strategy;
358-
if (callback) callback();
359-
}
360-
}
361-
362-
Zlib.prototype.params = function params(level, strategy, callback) {
363-
checkRangesOrGetDefault(level, 'level', Z_MIN_LEVEL, Z_MAX_LEVEL);
364-
checkRangesOrGetDefault(strategy, 'strategy', Z_DEFAULT_STRATEGY, Z_FIXED);
365-
366-
if (this._level !== level || this._strategy !== strategy) {
367-
this.flush(Z_SYNC_FLUSH,
368-
paramsAfterFlushCallback.bind(this, level, strategy, callback));
369-
} else {
370-
process.nextTick(callback);
371-
}
372-
};
373-
374-
Zlib.prototype.reset = function reset() {
288+
ZlibBase.prototype.reset = function() {
375289
if (!this._handle)
376290
assert(false, 'zlib binding closed');
377291
return this._handle.reset();
378292
};
379293

380294
// This is the _flush function called by the transform class,
381295
// internally, when the last chunk has been written.
382-
Zlib.prototype._flush = function _flush(callback) {
296+
ZlibBase.prototype._flush = function(callback) {
383297
this._transform(Buffer.alloc(0), '', callback);
384298
};
385299

@@ -400,12 +314,12 @@ function maxFlush(a, b) {
400314
}
401315

402316
const flushBuffer = Buffer.alloc(0);
403-
Zlib.prototype.flush = function flush(kind, callback) {
317+
ZlibBase.prototype.flush = function(kind, callback) {
404318
var ws = this._writableState;
405319

406320
if (typeof kind === 'function' || (kind === undefined && !callback)) {
407321
callback = kind;
408-
kind = Z_FULL_FLUSH;
322+
kind = this._defaultFullFlushFlag;
409323
}
410324

411325
if (ws.ended) {
@@ -424,17 +338,17 @@ Zlib.prototype.flush = function flush(kind, callback) {
424338
}
425339
};
426340

427-
Zlib.prototype.close = function close(callback) {
341+
ZlibBase.prototype.close = function(callback) {
428342
_close(this, callback);
429343
this.destroy();
430344
};
431345

432-
Zlib.prototype._destroy = function _destroy(err, callback) {
346+
ZlibBase.prototype._destroy = function(err, callback) {
433347
_close(this);
434348
callback(err);
435349
};
436350

437-
Zlib.prototype._transform = function _transform(chunk, encoding, cb) {
351+
ZlibBase.prototype._transform = function(chunk, encoding, cb) {
438352
var flushFlag = this._defaultFlushFlag;
439353
// We use a 'fake' zero-length chunk to carry information about flushes from
440354
// the public API to the actual stream implementation.
@@ -451,7 +365,7 @@ Zlib.prototype._transform = function _transform(chunk, encoding, cb) {
451365
processChunk(this, chunk, flushFlag, cb);
452366
};
453367

454-
Zlib.prototype._processChunk = function _processChunk(chunk, flushFlag, cb) {
368+
ZlibBase.prototype._processChunk = function(chunk, flushFlag, cb) {
455369
// _processChunk() is left for backwards compatibility
456370
if (typeof cb === 'function')
457371
processChunk(this, chunk, flushFlag, cb);
@@ -641,6 +555,109 @@ function _close(engine, callback) {
641555
engine._handle = null;
642556
}
643557

558+
const zlibDefaultOpts = {
559+
flush: Z_NO_FLUSH,
560+
finishFlush: Z_FINISH,
561+
fullFlush: Z_FULL_FLUSH
562+
};
563+
// Base class for all streams actually backed by zlib and using zlib-specific
564+
// parameters.
565+
function Zlib(opts, mode) {
566+
var windowBits = Z_DEFAULT_WINDOWBITS;
567+
var level = Z_DEFAULT_COMPRESSION;
568+
var memLevel = Z_DEFAULT_MEMLEVEL;
569+
var strategy = Z_DEFAULT_STRATEGY;
570+
var dictionary;
571+
572+
if (opts) {
573+
// windowBits is special. On the compression side, 0 is an invalid value.
574+
// But on the decompression side, a value of 0 for windowBits tells zlib
575+
// to use the window size in the zlib header of the compressed stream.
576+
if ((opts.windowBits == null || opts.windowBits === 0) &&
577+
(mode === INFLATE ||
578+
mode === GUNZIP ||
579+
mode === UNZIP)) {
580+
windowBits = 0;
581+
} else {
582+
windowBits = checkRangesOrGetDefault(
583+
opts.windowBits, 'options.windowBits',
584+
Z_MIN_WINDOWBITS, Z_MAX_WINDOWBITS, Z_DEFAULT_WINDOWBITS);
585+
}
586+
587+
level = checkRangesOrGetDefault(
588+
opts.level, 'options.level',
589+
Z_MIN_LEVEL, Z_MAX_LEVEL, Z_DEFAULT_COMPRESSION);
590+
591+
memLevel = checkRangesOrGetDefault(
592+
opts.memLevel, 'options.memLevel',
593+
Z_MIN_MEMLEVEL, Z_MAX_MEMLEVEL, Z_DEFAULT_MEMLEVEL);
594+
595+
strategy = checkRangesOrGetDefault(
596+
opts.strategy, 'options.strategy',
597+
Z_DEFAULT_STRATEGY, Z_FIXED, Z_DEFAULT_STRATEGY);
598+
599+
dictionary = opts.dictionary;
600+
if (dictionary !== undefined && !isArrayBufferView(dictionary)) {
601+
if (isAnyArrayBuffer(dictionary)) {
602+
dictionary = Buffer.from(dictionary);
603+
} else {
604+
throw new ERR_INVALID_ARG_TYPE(
605+
'options.dictionary',
606+
['Buffer', 'TypedArray', 'DataView', 'ArrayBuffer'],
607+
dictionary
608+
);
609+
}
610+
}
611+
}
612+
613+
const handle = new binding.Zlib(mode);
614+
// Ideally, we could let ZlibBase() set up _writeState. I haven't been able
615+
// to come up with a good solution that doesn't break our internal API,
616+
// and with it all supported npm versions at the time of writing.
617+
this._writeState = new Uint32Array(2);
618+
if (!handle.init(windowBits,
619+
level,
620+
memLevel,
621+
strategy,
622+
this._writeState,
623+
processCallback,
624+
dictionary)) {
625+
throw new ERR_ZLIB_INITIALIZATION_FAILED();
626+
}
627+
628+
ZlibBase.call(this, opts, mode, handle, zlibDefaultOpts);
629+
630+
this._level = level;
631+
this._strategy = strategy;
632+
}
633+
inherits(Zlib, ZlibBase);
634+
635+
// This callback is used by `.params()` to wait until a full flush happened
636+
// before adjusting the parameters. In particular, the call to the native
637+
// `params()` function should not happen while a write is currently in progress
638+
// on the threadpool.
639+
function paramsAfterFlushCallback(level, strategy, callback) {
640+
assert(this._handle, 'zlib binding closed');
641+
this._handle.params(level, strategy);
642+
if (!this._hadError) {
643+
this._level = level;
644+
this._strategy = strategy;
645+
if (callback) callback();
646+
}
647+
}
648+
649+
Zlib.prototype.params = function params(level, strategy, callback) {
650+
checkRangesOrGetDefault(level, 'level', Z_MIN_LEVEL, Z_MAX_LEVEL);
651+
checkRangesOrGetDefault(strategy, 'strategy', Z_DEFAULT_STRATEGY, Z_FIXED);
652+
653+
if (this._level !== level || this._strategy !== strategy) {
654+
this.flush(Z_SYNC_FLUSH,
655+
paramsAfterFlushCallback.bind(this, level, strategy, callback));
656+
} else {
657+
process.nextTick(callback);
658+
}
659+
};
660+
644661
// generic zlib
645662
// minimal 2-byte header
646663
function Deflate(opts) {

0 commit comments

Comments
 (0)