Skip to content
This repository has been archived by the owner on Apr 22, 2023. It is now read-only.

Commit

Permalink
[zlib] added dictionary support
Browse files Browse the repository at this point in the history
  • Loading branch information
indutny authored and isaacs committed Dec 6, 2011
1 parent 21d081f commit e609195
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 6 deletions.
1 change: 1 addition & 0 deletions doc/api/zlib.markdown
Expand Up @@ -215,6 +215,7 @@ relevant when compressing, and are ignored by the decompression classes.
* level (compression only) * level (compression only)
* memLevel (compression only) * memLevel (compression only)
* strategy (compression only) * strategy (compression only)
* dictionary (deflate/inflate only, empty dictionary by default)


See the description of `deflateInit2` and `inflateInit2` at See the description of `deflateInit2` and `inflateInit2` at
<http://zlib.net/manual.html#Advanced> for more information on these. <http://zlib.net/manual.html#Advanced> for more information on these.
Expand Down
9 changes: 8 additions & 1 deletion lib/zlib.js
Expand Up @@ -259,11 +259,18 @@ function Zlib(opts, Binding) {
} }
} }


if (opts.dictionary) {
if (!Buffer.isBuffer(opts.dictionary)) {
throw new Error('Invalid dictionary: it should be a Buffer instance');
}
}

this._binding = new Binding(); this._binding = new Binding();
this._binding.init(opts.windowBits || exports.Z_DEFAULT_WINDOWBITS, this._binding.init(opts.windowBits || exports.Z_DEFAULT_WINDOWBITS,
opts.level || exports.Z_DEFAULT_COMPRESSION, opts.level || exports.Z_DEFAULT_COMPRESSION,
opts.memLevel || exports.Z_DEFAULT_MEMLEVEL, opts.memLevel || exports.Z_DEFAULT_MEMLEVEL,
opts.strategy || exports.Z_DEFAULT_STRATEGY); opts.strategy || exports.Z_DEFAULT_STRATEGY,
opts.dictionary);


this._chunkSize = opts.chunkSize || exports.Z_DEFAULT_CHUNK; this._chunkSize = opts.chunkSize || exports.Z_DEFAULT_CHUNK;
this._buffer = new Buffer(this._chunkSize); this._buffer = new Buffer(this._chunkSize);
Expand Down
67 changes: 62 additions & 5 deletions src/node_zlib.cc
Expand Up @@ -64,6 +64,7 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
public: public:


ZCtx() : ObjectWrap() { ZCtx() : ObjectWrap() {
dictionary_ = NULL;
} }


~ZCtx() { ~ZCtx() {
Expand All @@ -72,6 +73,8 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
} else if (mode == INFLATE || mode == GUNZIP || mode == INFLATERAW) { } else if (mode == INFLATE || mode == GUNZIP || mode == INFLATERAW) {
(void)inflateEnd(&strm_); (void)inflateEnd(&strm_);
} }

if (dictionary_ != NULL) delete[] dictionary_;
} }


// write(flush, in, in_off, in_len, out, out_off, out_len) // write(flush, in, in_off, in_len, out, out_off, out_len)
Expand Down Expand Up @@ -163,6 +166,22 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
case GUNZIP: case GUNZIP:
case INFLATERAW: case INFLATERAW:
err = inflate(&(ctx->strm_), ctx->flush_); err = inflate(&(ctx->strm_), ctx->flush_);

// If data was encoded with dictionary
if (err == Z_NEED_DICT) {
assert(ctx->dictionary_ != NULL && "Stream has no dictionary");

// Load it
err = inflateSetDictionary(
&(ctx->strm_),
ctx->dictionary_,
ctx->dictionary_len_
);
assert(err == Z_OK && "Failed to set dictionary");

// And try to decode again
err = inflate(&(ctx->strm_), ctx->flush_);
}
break; break;
default: default:
assert(0 && "wtf?"); assert(0 && "wtf?");
Expand Down Expand Up @@ -206,8 +225,8 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
Init(const Arguments& args) { Init(const Arguments& args) {
HandleScope scope; HandleScope scope;


assert(args.Length() == 4 && assert((args.Length() == 4 || args.Length() == 5) &&
"init(windowBits, level, memLevel, strategy)"); "init(windowBits, level, memLevel, strategy, [dictionary])");


ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This()); ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This());


Expand All @@ -227,7 +246,19 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
strategy == Z_FIXED || strategy == Z_FIXED ||
strategy == Z_DEFAULT_STRATEGY) && "invalid strategy"); strategy == Z_DEFAULT_STRATEGY) && "invalid strategy");


Init(ctx, level, windowBits, memLevel, strategy); char* dictionary = NULL;
size_t dictionary_len = 0;
if (args.Length() >= 5 && Buffer::HasInstance(args[4])) {
Local<Object> dictionary_ = args[4]->ToObject();

dictionary_len = Buffer::Length(dictionary_);
dictionary = new char[dictionary_len];

memcpy(dictionary, Buffer::Data(dictionary_), dictionary_len);
}

Init(ctx, level, windowBits, memLevel, strategy,
dictionary, dictionary_len);
return Undefined(); return Undefined();
} }


Expand All @@ -236,7 +267,9 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
int level, int level,
int windowBits, int windowBits,
int memLevel, int memLevel,
int strategy) { int strategy,
char* dictionary,
size_t dictionary_len) {
ctx->level_ = level; ctx->level_ = level;
ctx->windowBits_ = windowBits; ctx->windowBits_ = windowBits;
ctx->memLevel_ = memLevel; ctx->memLevel_ = memLevel;
Expand Down Expand Up @@ -282,8 +315,29 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
assert(0 && "wtf?"); assert(0 && "wtf?");
} }


ctx->init_done_ = true;
assert(err == Z_OK); assert(err == Z_OK);

ctx->dictionary_ = reinterpret_cast<Bytef *>(dictionary);
ctx->dictionary_len_ = dictionary_len;

if (dictionary != NULL) {
switch (mode) {
case DEFLATE:
case DEFLATERAW:
err = deflateSetDictionary(
&(ctx->strm_),
ctx->dictionary_,
dictionary_len
);
break;
default:
break;
}

assert(err == Z_OK && "Failed to set dictionary");
}

ctx->init_done_ = true;
} }


private: private:
Expand All @@ -296,6 +350,9 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
int memLevel_; int memLevel_;
int strategy_; int strategy_;


Bytef* dictionary_;
size_t dictionary_len_;

int flush_; int flush_;


int chunk_size_; int chunk_size_;
Expand Down
73 changes: 73 additions & 0 deletions test/simple/test-zlib-dictionary.js
@@ -0,0 +1,73 @@
// Copyright Joyent, Inc. and other Node contributors.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.

// test compression/decompresion with dictionary

var common = require('../common.js');
var assert = require('assert');
var zlib = require('zlib');
var path = require('path');

var spdyDict = new Buffer([
'optionsgetheadpostputdeletetraceacceptaccept-charsetaccept-encodingaccept-',
'languageauthorizationexpectfromhostif-modified-sinceif-matchif-none-matchi',
'f-rangeif-unmodifiedsincemax-forwardsproxy-authorizationrangerefererteuser',
'-agent10010120020120220320420520630030130230330430530630740040140240340440',
'5406407408409410411412413414415416417500501502503504505accept-rangesageeta',
'glocationproxy-authenticatepublicretry-afterservervarywarningwww-authentic',
'ateallowcontent-basecontent-encodingcache-controlconnectiondatetrailertran',
'sfer-encodingupgradeviawarningcontent-languagecontent-lengthcontent-locati',
'oncontent-md5content-rangecontent-typeetagexpireslast-modifiedset-cookieMo',
'ndayTuesdayWednesdayThursdayFridaySaturdaySundayJanFebMarAprMayJunJulAugSe',
'pOctNovDecchunkedtext/htmlimage/pngimage/jpgimage/gifapplication/xmlapplic',
'ation/xhtmltext/plainpublicmax-agecharset=iso-8859-1utf-8gzipdeflateHTTP/1',
'.1statusversionurl\0'
].join(''));

var deflate = zlib.createDeflate({ dictionary: spdyDict });
var inflate = zlib.createInflate({ dictionary: spdyDict });

var input = [
'HTTP/1.1 200 Ok',
'Server: node.js',
'Content-Length: 0',
''
].join('\r\n');

// Put data into deflate stream
deflate.on('data', function(chunk) {
inflate.write(chunk);
});
deflate.on('end', function() {
inflate.end();
});

// Get data from inflate stream
var output = [];
inflate.on('data', function(chunk) {
output.push(chunk);
});
inflate.on('end', function() {
assert.equal(output.join(''), input);
});

deflate.write(input);
deflate.end();

1 comment on commit e609195

@ry
Copy link

@ry ry commented on e609195 Dec 6, 2011

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@indutny can you please add an example use to the documentation?

Please sign in to comment.