pako/lib/deflate.js

264 wiersze
6.7 KiB
JavaScript

'use strict';
var zlib_deflate = require('./zlib/deflate.js');
var utils = require('./zlib/utils');
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; }
return utils.typedOk() ? buf.subarray(0, size) : buf.slice(0, size);
}
/**
* class Deflate
*
* Generic JS-style wrapper for zlib calls. If you don't need
* streaming behaviour - use more simple functions: [[deflate]],
* [[deflateRaw]] and [[gzip]].
**/
/* internal
* Deflate.chunks -> Array
*
* Chunks of output data, if [[Deflate#onData]] not overriden.
**/
/**
* Deflate.result -> Uint8Array|Array
*
* Compressed result, generated by default [[Deflate#onData]]
* and [[Deflate#onEnd]] handlers. Filled after you push last chunk
* (call [[Deflate#push]] with `Z_FINISH` / `true` param).
**/
/**
* Deflate.err -> Number
*
* Error code after deflate finished. 0 (Z_OK) on success.
* You will not need it in real life, because deflate errors
* are possible only on wrong options or bad `onData` / `onEnd`
* custom handlers.
**/
/**
* new Deflate(options)
* - 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` - size of generated data chunks (16K by default)
* - `raw` (boolean) - do raw deflate
* - `gzip` (boolean) - create gzip wrapper
**/
var Deflate = function(options) {
this.options = utils.assign({
level: 6,
method: c.Z_DEFLATED,
chunkSize: 16384,
windowBits: 15,
memLevel: 8,
strategy: c.Z_DEFAULT_STRATEGY
}, options || {});
var opt = this.options;
if (opt.raw && (opt.windowBits > 0)) {
opt.windowBits = -opt.windowBits;
}
else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) {
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(
this.strm,
opt.level,
opt.method,
opt.windowBits,
opt.memLevel,
opt.strategy
);
if (status !== c.Z_OK) {
throw new Error(msg[status]);
}
};
/**
* Deflate#push(data[, mode]) -> Boolean
* - data (Uint8Array|Array): input data
* - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
* See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH.
*
* Sends input data to deflate pipe, generating [[Deflate#onData]] calls with
* new compressed chunks. Returns `true` on success. The last data block must have
* mode Z_FINISH (or `true`). That flush internal pending buffers and call
* [[Deflate#onEnd]].
*
* On fail call [[Deflate#onEnd]] with error code and return false.
**/
Deflate.prototype.push = function(data, mode) {
var strm = this.strm;
var chunkSize = this.options.chunkSize;
var status, _mode;
if (this.ended) { return false; }
_mode = (mode === ~~mode) ? mode : ((mode === true) ? c.Z_FINISH : c.Z_NO_FLUSH);
strm.next_in = data;
strm.next_in_index = 0;
strm.avail_in = strm.next_in.length;
strm.next_out = utils.arrayCreate(chunkSize);
do {
strm.avail_out = this.options.chunkSize;
strm.next_out_index = 0;
status = zlib_deflate.deflate(strm, _mode); /* 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) {
this.onData(sliceBuf(strm.next_out, strm.next_out_index));
// Allocate buffer for next chunk, if not last
if (strm.avail_in > 0 || strm.avail_out === 0) {
strm.next_out = utils.arrayCreate(this.options.chunkSize);
}
}
} while (strm.avail_in > 0 || strm.avail_out === 0);
// Finalize on the last chunk.
if (_mode === c.Z_FINISH) {
status = zlib_deflate.deflateEnd(this.strm);
this.onEnd(status);
this.ended = true;
return status === c.Z_OK;
}
return true;
};
/**
* Deflate#onData(chunk) -> Void
* - chunk (Uint8Array|Array): ouput data. Type of array depends
* on js engine support.
*
* By default, stores data blocks in `chunks[]` property and glue
* those in `onEnd`. 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.
* By default - join collected chunks, free memory and fill
* `results` / `err` 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.
*
* Compress `data` with deflate alrorythm and `options`.
*
* Supported options are:
*
* - 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 deflator = new Deflate(options);
deflator.push(input, true);
// That will never happens, if you don't cheat with options :)
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 creates raw data, without wrapper
* (header and adler32 crc).
**/
function deflateRaw(input, options) {
options = options || {};
options.raw = true;
return deflate(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;
return deflate(input, options);
}
exports.Deflate = Deflate;
exports.deflate = deflate;
exports.deflateRaw = deflateRaw;
exports.gzip = gzip;