[zlib] added dictionary support

This commit is contained in:
Fedor Indutny 2011-12-02 11:53:56 +04:00 committed by isaacs
parent 21d081fd7f
commit e609195202
4 changed files with 144 additions and 6 deletions

View File

@ -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 File

@ -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 File

@ -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_;

View File

@ -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();