Added gzip interface (without custom headers support)

This commit is contained in:
Vitaly Puzrin 2014-02-15 09:11:24 +04:00
parent 1b3f7253a8
commit 285e2d4cff
4 changed files with 128 additions and 52 deletions

View file

@ -31,6 +31,10 @@ var Deflate = function(options) {
opt.windowBits = -opt.windowBits;
}
else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) {
opt.windowBits += 16;
}
this.strm = new zstream();
var status = zlib_deflate.deflateInit2(
@ -159,6 +163,14 @@ function deflateRaw(input, options) {
}
function gzip(input, options) {
options = options || {};
options.gzip = true;
return deflate(input, options);
}
exports.Deflate = Deflate;
exports.deflate = deflate;
exports.deflateRaw = deflateRaw;
exports.deflateRaw = deflateRaw;
exports.gzip = gzip;

View file

@ -1,9 +1,7 @@
'use strict';
// CRC table.
// Use ordinary array, since untyped makes no boost here
//
var crcTable = function makeTable() {
function makeTable() {
var c, table = [];
for(var n =0; n < 256; n++){
@ -15,16 +13,20 @@ var crcTable = function makeTable() {
}
return table;
} ();
}
var crcTable = makeTable();
function crc32(crc, buf, len, pos) {
var t = crcTable,
, end = pos + len
, crc = 0 ^ (-1);
var t = crcTable
, end = pos + len;
crc = 0 ^ (-1);
for (var i = pos; i < end; i++ ) {
crc = (crc >>> 8) ^ t[(crc ^ input[i]) & 0xFF];
crc = (crc >>> 8) ^ t[(crc ^ buf[i]) & 0xFF];
}
return (crc ^ (-1)); // >>> 0;

View file

@ -1,9 +1,10 @@
'use strict';
var c = require('./constants');
var utils = require('./utils');
var trees = require('./trees');
var c = require('./constants');
var utils = require('./utils');
var trees = require('./trees');
var adler32 = require('./adler32');
var crc32 = require('./crc32');
var Z_NULL = c.Z_NULL;
@ -30,6 +31,8 @@ var BS_BLOCK_DONE = 2; /* block flush performed */
var BS_FINISH_STARTED = 3; /* finish started, need only more output at next deflate */
var BS_FINISH_DONE = 4; /* finish done, accept no more input or output */
var OS_CODE = 0x03; // Unix :) . Don't detect, use this default.
function rank (f) {
return ((f) << 1) - ((f) > 4 ? 9 : 0);
}
@ -71,6 +74,10 @@ function update_hash (s, h, c) {
return (((h) << s.hash_shift) ^ (c)) & s.hash_mask;
}
function put_byte(s, b) {
s.pending_buf[s.pending++] = b;
}
/* =========================================================================
* Put a short in the pending buffer. The 16-bit value is put in MSB order.
* IN assertion: the stream state is correct and there is enough room in
@ -102,6 +109,11 @@ function read_buf(strm, buf, start, size) {
if (strm.state.wrap === 1) {
strm.adler = adler32(strm.adler, buf, len, start);
}
else if (strm.state.wrap === 2) {
strm.adler = crc32(strm.adler, buf, len, start);
}
strm.next_in_index += len;
strm.total_in += len;
@ -464,9 +476,9 @@ function DeflateState() {
this.pending_out = 0; /* next pending byte to output to the stream */
this.pending = 0; /* nb of bytes in the pending buffer */
this.wrap = 0; /* bit 0 true for zlib, bit 1 true for gzip */
// gz_headerp gzhead; /* gzip header information to write */
// uInt gzindex; /* where in extra, name, or comment */
// Byte method; /* can only be DEFLATED */
this.gzhead = null; /* gzip header information to write */
this.gzindex = 0; /* where in extra, name, or comment */
this.method = c.Z_DEFLATED; /* can only be DEFLATED */
this.last_flush = Z_NULL; /* value of flush param for previous deflate call */
this.w_size = 0; /* LZ77 window size (32K by default) */
@ -563,7 +575,10 @@ function deflateResetKeep(strm) {
/* was made negative by deflate(..., Z_FINISH); */
}
s.status = s.wrap ? INIT_STATE : BUSY_STATE;
strm.adler = 1; // adler32(0, Z_NULL, 0);
strm.adler = (s.wrap === 2) ?
0 // crc32(0, Z_NULL, 0)
:
1; // adler32(0, Z_NULL, 0)
s.last_flush = c.Z_NO_FLUSH;
trees._tr_init(s);
return c.Z_OK;
@ -592,6 +607,11 @@ function deflateInit2(strm, level, method, windowBits, memLevel, strategy) {
windowBits = -windowBits;
}
else if (windowBits > 15) {
wrap = 2; /* write gzip wrapper instead */
windowBits -= 16;
}
if (windowBits === 8) {
windowBits = 9;
}
@ -603,22 +623,27 @@ function deflateInit2(strm, level, method, windowBits, memLevel, strategy) {
s.strm = strm;
s.wrap = wrap;
s.gzhead = Z_NULL;
s.gzhead = null;
s.w_bits = windowBits;
s.w_size = 1 << s.w_bits;
s.w_mask = s.w_size - 1;
s.window = utils.arrayCreate(s.w_size * 2);
s.hash_bits = memLevel + 7;
s.hash_size = 1 << s.hash_bits;
s.hash_mask = s.hash_size - 1;
s.hash_shift = (s.hash_bits + MIN_MATCH - 1) / MIN_MATCH;
s.high_water = 0;
/* nothing written to s->window yet */
s.lit_bufsize = 1 << (memLevel + 6);
/* 16K elements by default */
s.window = utils.arrayCreate(s.w_size * 2);
s.head = utils.array16Create(s.hash_size);
s.prev = utils.array16Create(s.w_size);
// precreate & prefill head/prev arrays to optimize v8 types
// TODO: check if we can remove it, should be inited in lm_init()
utils.fill(s.head, 0);
utils.fill(s.prev, 0);
s.high_water = 0; /* nothing written to s->window yet */
s.lit_bufsize = 1 << (memLevel + 6); /* 16K elements by default */
s.pending_buf_size = s.lit_bufsize * 4;
s.pending_buf = utils.arrayCreate(s.pending_buf_size);
@ -627,12 +652,6 @@ function deflateInit2(strm, level, method, windowBits, memLevel, strategy) {
s.strategy = strategy;
s.method = method;
// precreate & prefill head/prev arrays to optimize v8 types
s.head = utils.array16Create(s.hash_size);
s.prev = utils.array16Create(s.w_size);
utils.fill(s.head, 0);
utils.fill(s.prev, 0);
return deflateReset(strm);
}
@ -681,31 +700,55 @@ function deflate(strm, flush) {
/* Write the header */
if (s.status === INIT_STATE) {
var header = (c.Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8;
var level_flags = -1;
if (s.strategy >= c.Z_HUFFMAN_ONLY || s.level < 2) {
level_flags = 0;
} else if (s.level < 6) {
level_flags = 1;
} else if (s.level === 6) {
level_flags = 2;
} else {
level_flags = 3;
if (s.wrap === 2) { // GZIP header
strm.adler = 0; //crc32(0L, Z_NULL, 0);
put_byte(s, 31);
put_byte(s, 139);
put_byte(s, 8);
if (!s.gzhead) { // s->gzhead == Z_NULL
put_byte(s, 0);
put_byte(s, 0);
put_byte(s, 0);
put_byte(s, 0);
put_byte(s, 0);
put_byte(s, s.level === 9 ? 2 :
(s.strategy >= c.Z_HUFFMAN_ONLY || s.level < 2 ?
4 : 0));
put_byte(s, OS_CODE);
s.status = BUSY_STATE;
}
else {
throw new Error('Custom GZIP headers not supported');
}
}
header |= (level_flags << 6);
if (s.strstart !== 0) { header |= PRESET_DICT; }
header += 31 - (header % 31);
else // DEFLATE header
{
var header = (c.Z_DEFLATED + ((s.w_bits - 8) << 4)) << 8;
var level_flags = -1;
s.status = BUSY_STATE;
putShortMSB(s, header);
if (s.strategy >= c.Z_HUFFMAN_ONLY || s.level < 2) {
level_flags = 0;
} else if (s.level < 6) {
level_flags = 1;
} else if (s.level === 6) {
level_flags = 2;
} else {
level_flags = 3;
}
header |= (level_flags << 6);
if (s.strstart !== 0) { header |= PRESET_DICT; }
header += 31 - (header % 31);
/* Save the adler32 of the preset dictionary: */
if (s.strstart !== 0) {
putShortMSB(s, strm.adler >>> 16);
putShortMSB(s, strm.adler & 0xffff);
s.status = BUSY_STATE;
putShortMSB(s, header);
/* Save the adler32 of the preset dictionary: */
if (s.strstart !== 0) {
putShortMSB(s, strm.adler >>> 16);
putShortMSB(s, strm.adler & 0xffff);
}
strm.adler = 1; // adler32(0L, Z_NULL, 0);
}
strm.adler = 1; // adler32(0L, Z_NULL, 0);
}
/* Flush as much pending output as possible */
@ -796,8 +839,22 @@ function deflate(strm, flush) {
if (flush !== c.Z_FINISH) { return c.Z_OK; }
if (s.wrap <= 0) { return c.Z_STREAM_END; }
putShortMSB(s, strm.adler >>> 16);
putShortMSB(s, strm.adler & 0xffff);
/* Write the trailer */
if (s.wrap === 2) {
put_byte(s, strm.adler & 0xff);
put_byte(s, (strm.adler >> 8) & 0xff);
put_byte(s, (strm.adler >> 16) & 0xff);
put_byte(s, (strm.adler >> 24) & 0xff);
put_byte(s, strm.total_in & 0xff);
put_byte(s, (strm.total_in >> 8) & 0xff);
put_byte(s, (strm.total_in >> 16) & 0xff);
put_byte(s, (strm.total_in >> 24) & 0xff);
}
else
{
putShortMSB(s, strm.adler >>> 16);
putShortMSB(s, strm.adler & 0xffff);
}
flush_pending(strm);
/* If avail_out is zero, the application will call deflate again

View file

@ -32,6 +32,11 @@ describe('Deflate defaults', function () {
testDeflate(zlib.createDeflateRaw, pako.deflateRaw, sample, { level: 0 }, done);
});
// OS_CODE can differ. Probably should add param to compare function
// to ignore some buffer positions
it.skip('gzip (level 0)', function(done) {
testDeflate(zlib.createGzip, pako.gzip, sample, { level: 0 }, done);
});
});