Permalink
Browse files

[zlib] added dictionary support

  • Loading branch information...
1 parent 21d081f commit e609195202d79221edc037309c3ef459514e3ff4 @indutny indutny committed with isaacs Dec 2, 2011
Showing with 144 additions and 6 deletions.
  1. +1 −0 doc/api/zlib.markdown
  2. +8 −1 lib/zlib.js
  3. +62 −5 src/node_zlib.cc
  4. +73 −0 test/simple/test-zlib-dictionary.js
@@ -215,6 +215,7 @@ relevant when compressing, and are ignored by the decompression classes.
* level (compression only)
* memLevel (compression only)
* strategy (compression only)
+* dictionary (deflate/inflate only, empty dictionary by default)
See the description of `deflateInit2` and `inflateInit2` at
<http://zlib.net/manual.html#Advanced> for more information on these.
View
@@ -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.init(opts.windowBits || exports.Z_DEFAULT_WINDOWBITS,
opts.level || exports.Z_DEFAULT_COMPRESSION,
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._buffer = new Buffer(this._chunkSize);
View
@@ -64,6 +64,7 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
public:
ZCtx() : ObjectWrap() {
+ dictionary_ = NULL;
}
~ZCtx() {
@@ -72,6 +73,8 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
} else if (mode == INFLATE || mode == GUNZIP || mode == INFLATERAW) {
(void)inflateEnd(&strm_);
}
+
+ if (dictionary_ != NULL) delete[] dictionary_;
}
// write(flush, in, in_off, in_len, out, out_off, out_len)
@@ -163,6 +166,22 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
case GUNZIP:
case INFLATERAW:
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;
default:
assert(0 && "wtf?");
@@ -206,8 +225,8 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
Init(const Arguments& args) {
HandleScope scope;
- assert(args.Length() == 4 &&
- "init(windowBits, level, memLevel, strategy)");
+ assert((args.Length() == 4 || args.Length() == 5) &&
+ "init(windowBits, level, memLevel, strategy, [dictionary])");
ZCtx<mode> *ctx = ObjectWrap::Unwrap< ZCtx<mode> >(args.This());
@@ -227,7 +246,19 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
strategy == Z_FIXED ||
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();
}
@@ -236,7 +267,9 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
int level,
int windowBits,
int memLevel,
- int strategy) {
+ int strategy,
+ char* dictionary,
+ size_t dictionary_len) {
ctx->level_ = level;
ctx->windowBits_ = windowBits;
ctx->memLevel_ = memLevel;
@@ -282,8 +315,29 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
assert(0 && "wtf?");
}
- ctx->init_done_ = true;
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:
@@ -296,6 +350,9 @@ template <node_zlib_mode mode> class ZCtx : public ObjectWrap {
int memLevel_;
int strategy_;
+ Bytef* dictionary_;
+ size_t dictionary_len_;
+
int flush_;
int chunk_size_;
@@ -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
ry commented on e609195 Dec 6, 2011

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

Please sign in to comment.