Skip to content

Commit

Permalink
zlib: Make the finish flush flag configurable
Browse files Browse the repository at this point in the history
Up to now, `Z_FINISH` was always the flushing flag that was used
for the last chunk of input data. This patch makes this choice
configurable so that advanced users can perform e.g. decompression of
partial data using `Z_SYNC_FLUSH`, if that suits their needs.

Add tests to make sure that an error is thrown upon encountering
invalid `flush` or `finishFlush` flags.

Fixes: nodejs#5761
  • Loading branch information
addaleax committed Apr 7, 2016
1 parent 937ac37 commit 35ca9ad
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 12 deletions.
1 change: 1 addition & 0 deletions doc/api/zlib.markdown
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ Note that some options are only relevant when compressing, and are
ignored by the decompression classes.

* flush (default: `zlib.Z_NO_FLUSH`)
* finishFlush (default: `zlib.Z_FINISH`)
* chunkSize (default: 16*1024)
* windowBits
* level (compression only)
Expand Down
32 changes: 20 additions & 12 deletions lib/zlib.js
Original file line number Diff line number Diff line change
Expand Up @@ -234,7 +234,7 @@ function zlibBufferSync(engine, buffer) {
if (!(buffer instanceof Buffer))
throw new TypeError('Not a string or buffer');

var flushFlag = binding.Z_FINISH;
var flushFlag = engine._finishFlushFlag;

return engine._processChunk(buffer, flushFlag);
}
Expand Down Expand Up @@ -282,6 +282,14 @@ function Unzip(opts) {
Zlib.call(this, opts, binding.UNZIP);
}

function isValidFlushFlag(flag) {
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;
}

// the Zlib class they all inherit from
// This thing manages the queue of requests, and returns
Expand All @@ -294,17 +302,16 @@ function Zlib(opts, mode) {

Transform.call(this, opts);

if (opts.flush) {
if (opts.flush !== binding.Z_NO_FLUSH &&
opts.flush !== binding.Z_PARTIAL_FLUSH &&
opts.flush !== binding.Z_SYNC_FLUSH &&
opts.flush !== binding.Z_FULL_FLUSH &&
opts.flush !== binding.Z_FINISH &&
opts.flush !== binding.Z_BLOCK) {
throw new Error('Invalid flush flag: ' + opts.flush);
}
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 || binding.Z_NO_FLUSH;
this._finishFlushFlag = typeof opts.finishFlush !== 'undefined' ?
opts.finishFlush : binding.Z_FINISH;

if (opts.chunkSize) {
if (opts.chunkSize < exports.Z_MIN_CHUNK ||
Expand Down Expand Up @@ -486,12 +493,13 @@ Zlib.prototype._transform = function(chunk, encoding, cb) {
if (this._closed)
return cb(new Error('zlib binding closed'));

// If it's the last chunk, or a final flush, we use the Z_FINISH flush flag.
// 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 = binding.Z_FINISH;
flushFlag = this._finishFlushFlag;
else {
flushFlag = this._flushFlag;
// once we've flushed the last of the queue, stop flushing and
Expand Down
28 changes: 28 additions & 0 deletions test/parallel/test-zlib-flush-flags.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
'use strict';
require('../common');
const assert = require('assert');
const zlib = require('zlib');

assert.doesNotThrow(() => {
zlib.createGzip({ flush: zlib.Z_SYNC_FLUSH });
});

assert.throws(() => {
zlib.createGzip({ flush: 'foobar' });
}, /Invalid flush flag: foobar/);

assert.throws(() => {
zlib.createGzip({ flush: 10000 });
}, /Invalid flush flag: 10000/);

assert.doesNotThrow(() => {
zlib.createGzip({ finishFlush: zlib.Z_SYNC_FLUSH });
});

assert.throws(() => {
zlib.createGzip({ finishFlush: 'foobar' });
}, /Invalid flush flag: foobar/);

assert.throws(() => {
zlib.createGzip({ finishFlush: 10000 });
}, /Invalid flush flag: 10000/);
17 changes: 17 additions & 0 deletions test/parallel/test-zlib-truncated.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,22 @@ const inputString = 'ΩΩLorem ipsum dolor sit amet, consectetur adipiscing el'
zlib[methods.decomp](truncated, function(err, result) {
assert(/unexpected end of file/.test(err.message));
});

const syncFlushOpt = { finishFlush: zlib.Z_SYNC_FLUSH };

// sync truncated input test, finishFlush = Z_SYNC_FLUSH
assert.doesNotThrow(function() {
const result = zlib[methods.decompSync](truncated, syncFlushOpt)
.toString();
assert.equal(result, inputString.substr(0, result.length));
});

// async truncated input test, finishFlush = Z_SYNC_FLUSH
zlib[methods.decomp](truncated, syncFlushOpt, function(err, decompressed) {
assert.ifError(err);

const result = decompressed.toString();
assert.equal(result, inputString.substr(0, result.length));
});
});
});

0 comments on commit 35ca9ad

Please sign in to comment.