Updated wrapper interface and added docs

master
Vitaly Puzrin 2014-02-19 01:38:34 +04:00
rodzic 5dfa74f2e2
commit d4a42e9489
1 zmienionych plików z 153 dodań i 58 usunięć

Wyświetl plik

@ -7,12 +7,35 @@ var c = require('./zlib/constants');
var msg = require('./zlib/messages');
var zstream = require('./zlib/zstream');
// return sliced buffer, trying to avoid new objects creation and mem copy
function sliceBuf(buf, size) {
if (buf.length === size) { return buf; }
var sliced = utils.arrayCreate(size);
utils.arraySet(sliced, buf, 0, size, 0);
return sliced;
}
/**
* Deflate
* new Deflate(ootions)
*
* @param {Object} [options] zlib options
* @constructor
* - options (Object): zlib deflate options.
*
* Creates new deflator instance with specified params. Supported options:
*
* - level
* - windowBits
* - memLevel
* - strategy
*
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
* for more information on these.
*
* Additional options, for internal needs:
*
* - chunkSize
* - raw (boolean) - do raw deflate
* - gzip (boolean) - create gzip wrapper
*/
var Deflate = function(options) {
@ -35,6 +58,9 @@ var Deflate = function(options) {
opt.windowBits += 16;
}
this.ended = false; // used to avoid multiple onEnd() calls
this.chunks = []; // chunks of compressed data
this.strm = new zstream();
var status = zlib_deflate.deflateInit2(
@ -52,110 +78,171 @@ var Deflate = function(options) {
};
/**
* Compresses the input data and fills output buffer with compressed data.
*/
Deflate.prototype.push = function(data_in) {
* Deflate#push(data) -> boolean
*
* - data (Uint8Array|Array) input data
*
* Compress input data, generating [Deflate.onData] calls with new data chunks.
* On fail call [Deflate.onEnd] and return false.
**/
Deflate.prototype.push = function(data) {
var strm = this.strm;
var chunkSize = this.options.chunkSize;
var sliced;
var status;
strm.next_in = data_in;
if (this.ended) { return false; }
strm.next_in = data;
strm.next_in_index = 0;
strm.avail_in = strm.next_in.length;
strm.next_out = utils.arrayCreate(chunkSize);
/* run deflate() on input until output buffer not full, finish
compression if all of source has been read in */
do {
strm.avail_out = this.options.chunkSize;
strm.next_out_index = 0;
zlib_deflate.deflate(strm, c.Z_NO_FLUSH);
// TODO: check logic & why onEnd called. Check that onEnd not called twice.
//var ret = zlib_deflate.deflate(strm, c.Z_NO_FLUSH); /* no bad return value */
//if (ret !== c.Z_STREAM_END && ret !== c.Z_OK) {
// this.onEnd(ret);
//}
status = zlib_deflate.deflate(strm, c.Z_NO_FLUSH); /* no bad return value */
if (status !== c.Z_STREAM_END && status !== c.Z_OK) {
this.onEnd(status);
this.ended = true;
return false;
}
if(strm.next_out_index) {
if (strm.next_out_index === chunkSize) {
// out buffer completely filled, push it as is
this.onData(strm.next_out);
} else {
// out buffer partially filled, slice it first
sliced = utils.arrayCreate(strm.next_out_index);
utils.arraySet(sliced, strm.next_out, 0, strm.next_out_index, 0);
this.onData(sliced);
}
this.onData(sliceBuf(strm.next_out, strm.next_out_index));
// Allocate buffer for next chunk
strm.next_out = utils.arrayCreate(this.options.chunkSize);
}
} while (strm.avail_in > 0 || strm.avail_out === 0);
return true;
};
/**
* Deflate#flush() -> boolean
*
* Flush internal deflate data to output buffer. Does [Deflate.onData] call on
* success. On fail call [Deflate.onEnd] and return false.
**/
Deflate.prototype.flush = function() {
var strm = this.strm;
var out, status;
var chunkSize = this.options.chunkSize;
var status;
if (this.ended) { return false; }
strm.next_out = utils.arrayCreate(chunkSize);
do {
strm.avail_out = this.options.chunkSize;
strm.next_out_index = 0;
status = zlib_deflate.deflate(strm, c.Z_FINISH); /* no bad return value */
if (status !== c.Z_STREAM_END && status !== c.Z_OK) {
this.onEnd(status);
this.ended = true;
return false;
}
if(strm.next_out_index) {
out = utils.arrayCreate(strm.next_out_index);
utils.arraySet(out, strm.next_out, 0, strm.next_out_index, 0);
this.onData(out);
this.onData(sliceBuf(strm.next_out, strm.next_out_index));
// Allocate buffer for next chunk
strm.next_out = utils.arrayCreate(this.options.chunkSize);
}
} while (strm.avail_out === 0);
return true;
};
/**
* Deflate#finish() -> boolean
*
* Must be called when no more input data available. This function initiates
* [Deflate#onEnd] call. Returns `false` if something gone wrong.
**/
Deflate.prototype.finish = function() {
if (this.ended) { return false; }
this.flush();
zlib_deflate.deflateEnd(this.strm);
this.onEnd(c.Z_OK);
this.ended = true;
return true;
};
Deflate.prototype.onData = function(/*data_out*/) {
};
Deflate.prototype.onEnd = function(/*err*/) {
};
exports.Deflate = Deflate;
/**
* Compresses the input data
* @param input
* @param [options]
* @returns {Array|Uint8Array}
*/
* Deflate#onData(chunk) -> Void
*
* - chunk (Uint8Array|Array)- ouput data. Type of array depends
* on js engine support.
*
* By default, it store chunks in [Deflate.chunks]. Override this handler, if
* you need another behaviour.
**/
Deflate.prototype.onData = function(chunk) {
this.chunks.push(chunk);
};
/**
* Deflate#onEnd(status) -> Void
*
* - status (Number) - deflate status. 0 (Z_OK) on success,
* other if not.
*
* Called once after you tell deflate that input stream complete. See
* [Deflate.finish]. By default - join collected chunks, free memory and fill
* states properties.
**/
Deflate.prototype.onEnd = function(status) {
// On success - join
if (status === c.Z_OK) {
this.result = utils.flattenChunks(this.chunks);
}
this.chunks = [];
this.err = status;
// TODO: detect message by status, if not set in zstream
this.msg = this.strm.msg || msg[status];
this.ended = true;
};
/**
* deflate(data, options) -> (Uint8Array|Array)
*
* - data (Uint8Array|Array): input data to compress.
* - options (Object): zlib deflate options.
*
* Simple [Deflate] wrapper to compress data with one call.
*
* Supported options:
*
* - level
* - windowBits
* - memLevel
* - strategy
*
* [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
* for more information on these.
**/
function deflate(input, options) {
var result;
var chunks = [];
var deflator = new Deflate(options);
deflator.onData = function(data_out) {
chunks.push(data_out);
};
deflator.onEnd = function(error) {
if (error) { throw error; }
result = utils.flattenChunks(chunks);
};
deflator.push(input);
deflator.finish();
return result;
if (deflator.err) { throw msg[deflator.err]; }
return deflator.result;
}
/**
* deflateRaw(data, options) -> (Uint8Array|Array)
*
* - data (Uint8Array|Array): input data to compress.
* - options (Object): zlib deflate options.
*
* The same as [deflate], but auoput raw data, without wrapper.
**/
function deflateRaw(input, options) {
options = options || {};
options.raw = true;
@ -163,6 +250,14 @@ function deflateRaw(input, options) {
}
/**
* gzip(data, options) -> (Uint8Array|Array)
*
* - data (Uint8Array|Array): input data to compress.
* - options (Object): zlib deflate options.
*
* The same as [deflate], but create gzip wrapper instead of deflate one.
**/
function gzip(input, options) {
options = options || {};
options.gzip = true;