From 9b09c9eedd74589111b9893f7b4154c45e027fc7 Mon Sep 17 00:00:00 2001 From: Brian White Date: Mon, 1 Jul 2013 05:44:17 -0400 Subject: [PATCH] zlib: allow changing of level and strategy --- doc/api/zlib.markdown | 5 +++++ lib/zlib.js | 32 +++++++++++++++++++++++++++++++- src/node_zlib.cc | 30 ++++++++++++++++++++++++++++++ test/simple/test-zlib-params.js | 33 +++++++++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 1 deletion(-) create mode 100644 test/simple/test-zlib-params.js diff --git a/doc/api/zlib.markdown b/doc/api/zlib.markdown index 92569b7d73..59d73554d5 100644 --- a/doc/api/zlib.markdown +++ b/doc/api/zlib.markdown @@ -151,6 +151,11 @@ class of the compressor/decompressor classes. Flush pending data. Don't call this frivolously, premature flushes negatively impact the effectiveness of the compression algorithm. +### zlib.params(level, strategy, callback) + +Dynamically update the compression level and compression strategy. +Only applicable to deflate algorithm. + ### zlib.reset() Reset the compressor/decompressor to factory defaults. Only applicable to diff --git a/lib/zlib.js b/lib/zlib.js index ef4edd6d7e..e41d27a5af 100644 --- a/lib/zlib.js +++ b/lib/zlib.js @@ -341,13 +341,43 @@ function Zlib(opts, mode) { this._buffer = new Buffer(this._chunkSize); this._offset = 0; this._closed = false; + this._level = level; + this._strategy = strategy; this.once('end', this.close); } util.inherits(Zlib, Transform); -Zlib.prototype.reset = function reset() { +Zlib.prototype.params = function(level, strategy, callback) { + if (level < exports.Z_MIN_LEVEL || + level > exports.Z_MAX_LEVEL) { + throw new RangeError('Invalid compression level: ' + level); + } + if (strategy != exports.Z_FILTERED && + strategy != exports.Z_HUFFMAN_ONLY && + strategy != exports.Z_RLE && + strategy != exports.Z_FIXED && + strategy != exports.Z_DEFAULT_STRATEGY) { + throw new TypeError('Invalid strategy: ' + strategy); + } + + if (this._level !== level || this._strategy !== strategy) { + var self = this; + this.flush(binding.Z_SYNC_FLUSH, function() { + self._binding.params(level, strategy); + if (!self._hadError) { + self._level = level; + self._strategy = strategy; + if (callback) callback(); + } + }); + } else { + process.nextTick(callback); + } +}; + +Zlib.prototype.reset = function() { return this._binding.reset(); }; diff --git a/src/node_zlib.cc b/src/node_zlib.cc index 229dacb69f..9bfc33e580 100644 --- a/src/node_zlib.cc +++ b/src/node_zlib.cc @@ -360,6 +360,18 @@ class ZCtx : public ObjectWrap { return Undefined(node_isolate); } + static Handle Params(const Arguments& args) { + HandleScope scope(node_isolate); + + assert(args.Length() == 2 && "params(level, strategy)"); + + ZCtx* ctx = ObjectWrap::Unwrap(args.This()); + + Params(ctx, args[0]->Int32Value(), args[1]->Int32Value()); + + return Undefined(node_isolate); + } + static Handle Reset(const Arguments &args) { HandleScope scope(node_isolate); @@ -455,6 +467,23 @@ class ZCtx : public ObjectWrap { } } + static void Params(ZCtx* ctx, int level, int strategy) { + ctx->err_ = Z_OK; + + switch (ctx->mode_) { + case DEFLATE: + case DEFLATERAW: + ctx->err_ = deflateParams(&ctx->strm_, level, strategy); + break; + default: + break; + } + + if (ctx->err_ != Z_OK && ctx->err_ != Z_BUF_ERROR) { + ZCtx::Error(ctx, "Failed to set parameters"); + } + } + static void Reset(ZCtx* ctx) { ctx->err_ = Z_OK; @@ -514,6 +543,7 @@ void InitZlib(Handle target) { NODE_SET_PROTOTYPE_METHOD(z, "write", ZCtx::Write); NODE_SET_PROTOTYPE_METHOD(z, "init", ZCtx::Init); NODE_SET_PROTOTYPE_METHOD(z, "close", ZCtx::Close); + NODE_SET_PROTOTYPE_METHOD(z, "params", ZCtx::Params); NODE_SET_PROTOTYPE_METHOD(z, "reset", ZCtx::Reset); z->SetClassName(String::NewSymbol("Zlib")); diff --git a/test/simple/test-zlib-params.js b/test/simple/test-zlib-params.js new file mode 100644 index 0000000000..006f1ea8e5 --- /dev/null +++ b/test/simple/test-zlib-params.js @@ -0,0 +1,33 @@ +var common = require('../common.js'); +var assert = require('assert'); +var zlib = require('zlib'); +var path = require('path'); +var fs = require('fs'); + +var file = fs.readFileSync(path.resolve(common.fixturesDir, 'person.jpg')), + chunkSize = 24 * 1024, + opts = { level: 9, strategy: zlib.Z_DEFAULT_STRATEGY }, + deflater = zlib.createDeflate(opts); + +var chunk1 = file.slice(0, chunkSize), + chunk2 = file.slice(chunkSize), + blkhdr = new Buffer([0x00, 0x48, 0x82, 0xb7, 0x7d]), + expected = Buffer.concat([blkhdr, chunk2]), + actual; + +deflater.write(chunk1, function() { + deflater.params(0, zlib.Z_DEFAULT_STRATEGY, function() { + while (deflater.read()); + deflater.end(chunk2, function() { + var bufs = [], buf; + while (buf = deflater.read()) + bufs.push(buf); + actual = Buffer.concat(bufs); + }); + }); + while (deflater.read()); +}); + +process.once('exit', function() { + assert.deepEqual(actual, expected); +});