From cb6d67d157a8139105a651567472d7bebd96afec Mon Sep 17 00:00:00 2001 From: Vitaly Puzrin Date: Mon, 9 Nov 2020 10:21:18 +0300 Subject: [PATCH] Rewrite top level wrappers --- CHANGELOG.md | 7 +- README.md | 62 +++++++++------ lib/deflate.js | 67 ++++++++++------ lib/inflate.js | 123 +++++++++++++++-------------- lib/zlib/constants.js | 2 +- test/chunks.js | 5 +- test/fixtures/gzip-joined-bgzip.gz | Bin 0 -> 13121 bytes test/gzip_specials.js | 53 +++++++++---- test/inflate.js | 2 +- test/inflate_cover_ported.js | 2 +- 10 files changed, 189 insertions(+), 134 deletions(-) create mode 100644 test/fixtures/gzip-joined-bgzip.gz diff --git a/CHANGELOG.md b/CHANGELOG.md index 385db56..289a8f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,13 +9,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [2.0.0] - WIP ### Changed - Removed binary strings and `Array` support. -- Removed fallbacks for unsupported TypedArray methods (`.set()`, `.subarray()`). +- Removed fallbacks for TypedArray methods (`.set()`, `.subarray()`). +- Rewritten top-level wrappers. - Removed support of `Inflate` & `Deflate` instance create without `new`. -- Removed `Z_SYNC_FLUSH` related code from wrappers (buggy and no tests). +- `Inflate.push()` no longer needs second param (end is auto-detected). +- Increased default inflate chunk size to 64K. - Switched to es6. Legacy es5 builds available in `/dist`. - Structure of `/dist` folder changed. - Upgraded build tools to modern ones. + ## [1.0.11] - 2020-01-29 ### Fixed - Fix tests in node.js v12+, #179. diff --git a/README.md b/README.md index 7c313f8..8f8d435 100644 --- a/README.md +++ b/README.md @@ -58,33 +58,32 @@ For deflate level 6 results can be considered as correct. __Install:__ -node.js: - ``` npm install pako ``` -Example & API -------------- +Examples / API +-------------- Full docs - http://nodeca.github.io/pako/ ```javascript -var pako = require('pako'); +const pako = require('pako'); // Deflate // -var input = new Uint8Array(); +const input = new Uint8Array(); //... fill input data here -var output = pako.deflate(input); +const output = pako.deflate(input); // Inflate (simple wrapper can throw exception on broken stream) // -var compressed = new Uint8Array(); +const compressed = new Uint8Array(); //... fill data to uncompress here try { - var result = pako.inflate(compressed); + const result = pako.inflate(compressed); + // ... continue processing } catch (err) { console.log(err); } @@ -93,37 +92,48 @@ try { // Alternate interface for chunking & without exceptions // -var inflator = new pako.Inflate(); +const deflator = new pako.Deflate(); -inflator.push(chunk1, false); -inflator.push(chunk2, false); +deflator.push(chunk1, false); +deflator.push(chunk2), false; ... -inflator.push(chunkN, true); // true -> last chunk +deflator.push(chunk_last, true); // `true` says this chunk is last + +if (deflator.err) { + console.log(deflator.msg); +} + +const output = deflator.result; + + +const inflator = new pako.Inflate(); + +inflator.push(chunk1); +inflator.push(chunk2); +... +inflator.push(chunk_last); // no second param because end is auto-detected if (inflator.err) { console.log(inflator.msg); } -var output = inflator.result; - +const output = inflator.result; ``` Sometime you can wish to work with strings. For example, to send -big objects as json to server. Pako detects input data type. You can -force output to be string with option `{ to: 'string' }`. +stringified objects to server. Pako's deflate detects input data type, and +automatically recode strings to utf-8 prior to compress. Inflate has special +option, to say compressed data has utf-8 encoding and should be recoded to +javascript's utf-16. ```javascript -var pako = require('pako'); +const pako = require('pako'); -var test = { my: 'super', puper: [456, 567], awesome: 'pako' }; +const test = { my: 'super', puper: [456, 567], awesome: 'pako' }; -var binaryString = pako.deflate(JSON.stringify(test), { to: 'string' }); +const compressed = pako.deflate(JSON.stringify(test)); -// -// Here you can do base64 encode, make xhr requests and so on. -// - -var restored = JSON.parse(pako.inflate(binaryString, { to: 'string' })); +const restored = JSON.parse(pako.inflate(compressed, { to: 'string' })); ``` @@ -137,7 +147,7 @@ Pako does not contain some specific zlib functions: - __inflate__ - methods `inflateCopy`, `inflateMark`, `inflatePrime`, `inflateGetDictionary`, `inflateSync`, `inflateSyncPoint`, `inflateUndermine`. - High level inflate/deflate wrappers (classes) may not support some flush - modes. Those should work: Z_NO_FLUSH, Z_FINISH, Z_SYNC_FLUSH. + modes. pako for enterprise diff --git a/lib/deflate.js b/lib/deflate.js index 2ba7869..2cea4bc 100644 --- a/lib/deflate.js +++ b/lib/deflate.js @@ -13,7 +13,7 @@ const toString = Object.prototype.toString; /* ===========================================================================*/ const { - Z_NO_FLUSH, Z_FINISH, + Z_NO_FLUSH, Z_SYNC_FLUSH, Z_FULL_FLUSH, Z_FINISH, Z_OK, Z_STREAM_END, Z_DEFAULT_COMPRESSION, Z_DEFAULT_STRATEGY, @@ -176,22 +176,19 @@ function Deflate(options) { } /** - * Deflate#push(data[, mode]) -> Boolean + * Deflate#push(data[, flush_mode]) -> Boolean * - data (Uint8Array|ArrayBuffer|String): input data. Strings will be * converted to utf8 byte sequence. - * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes. + * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes. * See constants. Skipped or `false` means Z_NO_FLUSH, `true` means 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 will flush internal pending buffers and call - * [[Deflate#onEnd]]. + * new compressed chunks. Returns `true` on success. The last data block must + * have `flush_mode` Z_FINISH (or `true`). That will flush internal pending + * buffers and call [[Deflate#onEnd]]. * * On fail call [[Deflate#onEnd]] with error code and return false. * - * Note. Don't skip last param and always use the same type in your code - * (boolean or number). That will improve JS speed. - * * ##### Example * * ```javascript @@ -200,14 +197,15 @@ function Deflate(options) { * push(chunk, true); // push last chunk * ``` **/ -Deflate.prototype.push = function (data, mode) { +Deflate.prototype.push = function (data, flush_mode) { const strm = this.strm; const chunkSize = this.options.chunkSize; - let status; + let status, _flush_mode; if (this.ended) { return false; } - const _mode = (mode === ~~mode) ? mode : ((mode === true) ? Z_FINISH : Z_NO_FLUSH); + if (flush_mode === ~~flush_mode) _flush_mode = flush_mode; + else _flush_mode = flush_mode === true ? Z_FINISH : Z_NO_FLUSH; // Convert data if needed if (typeof data === 'string') { @@ -222,30 +220,47 @@ Deflate.prototype.push = function (data, mode) { strm.next_in = 0; strm.avail_in = strm.input.length; - do { + for (;;) { if (strm.avail_out === 0) { strm.output = new Uint8Array(chunkSize); strm.next_out = 0; strm.avail_out = chunkSize; } - status = zlib_deflate.deflate(strm, _mode); /* no bad return value */ - if (status !== Z_STREAM_END && status !== Z_OK) { + // Make sure avail_out > 6 to avoid repeating markers + if ((_flush_mode === Z_SYNC_FLUSH || _flush_mode === Z_FULL_FLUSH) && strm.avail_out <= 6) { + this.onData(strm.output.subarray(0, strm.next_out)); + strm.avail_out = 0; + continue; + } + + status = zlib_deflate.deflate(strm, _flush_mode); + + // Ended => flush and finish + if (status === Z_STREAM_END) { + if (strm.next_out > 0) { + this.onData(strm.output.subarray(0, strm.next_out)); + } + status = zlib_deflate.deflateEnd(this.strm); this.onEnd(status); this.ended = true; - return false; + return status === Z_OK; } - if (strm.avail_out === 0 || (strm.avail_in === 0 && _mode === Z_FINISH)) { - this.onData(strm.output.length === strm.next_out ? strm.output : strm.output.subarray(0, strm.next_out)); - } - } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== Z_STREAM_END); - // Finalize on the last chunk. - if (_mode === Z_FINISH) { - status = zlib_deflate.deflateEnd(this.strm); - this.onEnd(status); - this.ended = true; - return status === Z_OK; + // Flush if out buffer full + if (strm.avail_out === 0) { + this.onData(strm.output); + continue; + } + + // Flush if requested and has data + if (_flush_mode > 0 && strm.next_out > 0) { + this.onData(strm.output.subarray(0, strm.next_out)); + strm.avail_out = 0; + continue; + } + + if (strm.avail_in === 0) break; } return true; diff --git a/lib/inflate.js b/lib/inflate.js index 9547fd1..cf693ce 100644 --- a/lib/inflate.js +++ b/lib/inflate.js @@ -15,7 +15,7 @@ const toString = Object.prototype.toString; const { Z_NO_FLUSH, Z_FINISH, - Z_OK, Z_STREAM_END, Z_NEED_DICT, Z_BUF_ERROR + Z_OK, Z_STREAM_END, Z_NEED_DICT, Z_STREAM_ERROR, Z_DATA_ERROR, Z_MEM_ERROR } = require('./zlib/constants'); /* ===========================================================================*/ @@ -100,7 +100,7 @@ const { **/ function Inflate(options) { this.options = utils.assign({ - chunkSize: 16384, + chunkSize: 1024 * 64, windowBits: 15, to: '' }, options || {}); @@ -169,21 +169,22 @@ function Inflate(options) { } /** - * Inflate#push(data[, mode]) -> Boolean + * Inflate#push(data[, flush_mode]) -> Boolean * - data (Uint8Array|ArrayBuffer): 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` means Z_FINISH. + * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE + * flush modes. See constants. Skipped or `false` means Z_NO_FLUSH, + * `true` means Z_FINISH. * * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with - * new output chunks. Returns `true` on success. The last data block must have - * mode Z_FINISH (or `true`). That will flush internal pending buffers and call - * [[Inflate#onEnd]]. + * new output chunks. Returns `true` on success. If end of stream detected, + * [[Inflate#onEnd]] will be called. + * + * `flush_mode` is not needed for normal operation, because end of stream + * detected automatically. You may try to use it for advanced things, but + * this functionality was not tested. * * On fail call [[Inflate#onEnd]] with error code and return false. * - * Note. Don't skip last param and always use the same type in your code - * (boolean or number). That will improve JS speed. - * * ##### Example * * ```javascript @@ -192,19 +193,16 @@ function Inflate(options) { * push(chunk, true); // push last chunk * ``` **/ -Inflate.prototype.push = function (data, mode) { +Inflate.prototype.push = function (data, flush_mode) { const strm = this.strm; const chunkSize = this.options.chunkSize; const dictionary = this.options.dictionary; - let status, _mode; - let next_out_utf8, tail, utf8str; + let status, _flush_mode, last_avail_out; - // Flag to properly process Z_BUF_ERROR on testing inflate call - // when we check that all output data was flushed. - let allowBufError = false; + if (this.ended) return false; - if (this.ended) { return false; } - _mode = (mode === ~~mode) ? mode : ((mode === true) ? Z_FINISH : Z_NO_FLUSH); + if (flush_mode === ~~flush_mode) _flush_mode = flush_mode; + else _flush_mode = flush_mode === true ? Z_FINISH : Z_NO_FLUSH; // Convert data if needed if (toString.call(data) === '[object ArrayBuffer]') { @@ -216,44 +214,64 @@ Inflate.prototype.push = function (data, mode) { strm.next_in = 0; strm.avail_in = strm.input.length; - do { + for (;;) { if (strm.avail_out === 0) { strm.output = new Uint8Array(chunkSize); strm.next_out = 0; strm.avail_out = chunkSize; } - status = zlib_inflate.inflate(strm, Z_NO_FLUSH); /* no bad return value */ + status = zlib_inflate.inflate(strm, _flush_mode); if (status === Z_NEED_DICT && dictionary) { - status = zlib_inflate.inflateSetDictionary(this.strm, dictionary); + status = zlib_inflate.inflateSetDictionary(strm, dictionary); + + if (status === Z_OK) { + status = zlib_inflate.inflate(strm, _flush_mode); + } else if (status === Z_DATA_ERROR) { + // Replace code with more verbose + status = Z_NEED_DICT; + } } - if (status === Z_BUF_ERROR && allowBufError === true) { - status = Z_OK; - allowBufError = false; + // Skip snyc markers if more data follows and not raw mode + while (strm.avail_in > 0 && + status === Z_STREAM_END && + strm.state.wrap > 0 && + data[strm.next_in] !== 0) + { + zlib_inflate.inflateReset(strm); + status = zlib_inflate.inflate(strm, _flush_mode); } - if (status !== Z_STREAM_END && status !== Z_OK) { - this.onEnd(status); - this.ended = true; - return false; + switch (status) { + case Z_STREAM_ERROR: + case Z_DATA_ERROR: + case Z_NEED_DICT: + case Z_MEM_ERROR: + this.onEnd(status); + this.ended = true; + return false; } + // Remember real `avail_out` value, because we may patch out buffer content + // to align utf8 strings boundaries. + last_avail_out = strm.avail_out; + if (strm.next_out) { - if (strm.avail_out === 0 || status === Z_STREAM_END || (strm.avail_in === 0 && _mode === Z_FINISH)) { + if (strm.avail_out === 0 || status === Z_STREAM_END) { if (this.options.to === 'string') { - next_out_utf8 = strings.utf8border(strm.output, strm.next_out); + let next_out_utf8 = strings.utf8border(strm.output, strm.next_out); - tail = strm.next_out - next_out_utf8; - utf8str = strings.buf2string(strm.output, next_out_utf8); + let tail = strm.next_out - next_out_utf8; + let utf8str = strings.buf2string(strm.output, next_out_utf8); - // move tail + // move tail & realign counters strm.next_out = tail; strm.avail_out = chunkSize - tail; - if (tail) { strm.output.set(strm.output.subarray(next_out_utf8, next_out_utf8 + tail), 0); } + if (tail) strm.output.set(strm.output.subarray(next_out_utf8, next_out_utf8 + tail), 0); this.onData(utf8str); @@ -263,29 +281,18 @@ Inflate.prototype.push = function (data, mode) { } } - // When no more input data, we should check that internal inflate buffers - // are flushed. The only way to do it when avail_out = 0 - run one more - // inflate pass. But if output data not exists, inflate return Z_BUF_ERROR. - // Here we set flag to process this error properly. - // - // NOTE. Deflate does not return error in this case and does not needs such - // logic. - if (strm.avail_in === 0 && strm.avail_out === 0) { - allowBufError = true; + // Must repeat iteration if out buffer is full + if (status === Z_OK && last_avail_out === 0) continue; + + // Finalize if end of stream reached. + if (status === Z_STREAM_END) { + status = zlib_inflate.inflateEnd(this.strm); + this.onEnd(status); + this.ended = true; + return true; } - } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== Z_STREAM_END); - - if (status === Z_STREAM_END) { - _mode = Z_FINISH; - } - - // Finalize on the last chunk. - if (_mode === Z_FINISH) { - status = zlib_inflate.inflateEnd(this.strm); - this.onEnd(status); - this.ended = true; - return status === Z_OK; + if (strm.avail_in === 0) break; } return true; @@ -371,10 +378,10 @@ Inflate.prototype.onEnd = function (status) { function inflate(input, options) { const inflator = new Inflate(options); - inflator.push(input, true); + inflator.push(input); // That will never happens, if you don't cheat with options :) - if (inflator.err) { throw inflator.msg || msg[inflator.err]; } + if (inflator.err) throw inflator.msg || msg[inflator.err]; return inflator.result; } diff --git a/lib/zlib/constants.js b/lib/zlib/constants.js index 569b872..b85cc01 100644 --- a/lib/zlib/constants.js +++ b/lib/zlib/constants.js @@ -39,7 +39,7 @@ module.exports = { Z_ERRNO: -1, Z_STREAM_ERROR: -2, Z_DATA_ERROR: -3, - //Z_MEM_ERROR: -4, + Z_MEM_ERROR: -4, Z_BUF_ERROR: -5, //Z_VERSION_ERROR: -6, diff --git a/test/chunks.js b/test/chunks.js index c8acaa4..9026eff 100644 --- a/test/chunks.js +++ b/test/chunks.js @@ -39,7 +39,7 @@ function testChunk(buf, expected, packer, chunkSize) { } //expected count of onData calls. 16384 output chunk size - expFlushCount = Math.ceil(packer.result.length / 16384); + expFlushCount = Math.ceil(packer.result.length / packer.options.chunkSize); assert(!packer.err, 'Packer error: ' + packer.err); assert.deepStrictEqual(packer.result, expected); @@ -92,7 +92,6 @@ describe('Dummy push (force end)', () => { const inflator = new pako.Inflate(); inflator.push(data); - inflator.push([], true); assert.deepStrictEqual(inflator.result, pako.inflate(data)); }); @@ -119,7 +118,7 @@ describe('Edge condition', () => { assert.ok(!inflator.err, 'Inflate failed with status ' + inflator.err); } - inflator.push(new Uint8Array(0), true); + inflator.push(new Uint8Array(0)); assert.ok(!inflator.err, 'Inflate failed with status ' + inflator.err); assert.deepStrictEqual(data, inflator.result); diff --git a/test/fixtures/gzip-joined-bgzip.gz b/test/fixtures/gzip-joined-bgzip.gz new file mode 100644 index 0000000000000000000000000000000000000000..fc9f652383f2b79bb430406c37ed38112850884c GIT binary patch literal 13121 zcmV-HGrr6piwFb&00000{{{d;LjnMZ<`1~(_`tvvMzxw!n`UKm?H&d zp#tj~rnO;#RYC_9*w;2KT%at|igno4&D^77fi*=>!G1W^tvKz%0xJbJbyU^(n09JK zy(2fgqRTT>KropNMXOybo zei)5LQ?=8Csk7lv%;SDo1;4P<54&MU?cuK88^>ZFcho7^4@Z+tbYU~kQ8U|BX)M~c zu28B&E3=zE6!7A08Z#=OaWciFCuo@-{E61wiZ-Yxg%8t%1$uWg`@lN%t%cJ@q2X?$ zyC3F-1*YYLDnj$2&HTiwScjEXSW-cOh#*N|Z05PHP?9k1bl2ewno;d+{Y1Yijb^{P zhP#)GTG1tAOb>NbuWm)J4Zlz=?R&eZ9|WZU3bC#(5bY?dd*(y$3oAJ-2K#LI6I0?# zbn+J@r9}^Qbzi-h9_)v08PzxLdV<^!o4fktCp@N9V5^zqLLoM;qh^kRdzK)7q2THs zRa>ekiosDmSfgE7Kr7#LVjVQCMLSCS(cMv_Z>(hZhB5~3SwkgE7glnXX^OLYOlu%x zZki8c)e=ghIhLipszCeZSUX=pO>FGtR`8B9j+8V}9;{v8XlFmv6 zYS&n-HfU|G=2^XXELQj4y?AwNVO3M?ho#kPdhiR`{k1gl3(I?!_I8?4GEKHAPMEd5 zHrGatbnSH1=B_^Jhl7guzBCG=l_t!-q-ttqHvEF-lqt%|T_xv5QBBgOS4?qJ+fDY7 zD9(r7Qptt_WOr|@yQ9jwy;F$dwBN&{xksfbCmrpWqDH!Bx_aXB-_uiWFVyAss&3{& z6=kpV!$y^L@8+)F9eZ+H8{F0^p}?^alN7;@I^O+d$IzVIHeSlP@F$Ly6VXona4u=} zq8q;u6-5=Hu{gy_&Z(&Mp6nN=;>tSJmfB&J^~9t-4HwR7%RkXjJviN6Es9_0Ru9fV z2_vn)nQr_-l-{$rqqe90*=Ud+O=!`Bn)x|31v=K@NSN2_BHLe(blQ3qGNS}Z_=u9y zE484wK&3e;NHa>5Tpaak)nDi|9|SE|a?oH28vX)qD3X$rPW3`-QINVw^uTb}YnN@x zUy$rDB?F{hr+F+6%?CkJ)kLwY%cX%9r72fSaZlQZhy_LpkkVW2s|v+B*z^$XAO;0( z7ii@A6O=kMa=TR-oWqp#2SHM~MQKx$jU*W*N?WraO%*kgw&oh&X0?_;1$OZlHYwhM zw!d(2(h71VdAPM^j_IVH)LcRDYYIWPrzbyt+Mf;=BI=Cn4e$I-t)OHcad zHpR7BM{|Kes+uA7WS#sIb_=K}o9E+V+Y|@HrJ(dskkUlBMlU9lt zwW3K-nv(J_Xr#cz0!0lpr8+cQi=qqbP(3Fb>XVvJ`H6iNd~DP3-dNf;jC?SkHsP%h#>u~76&Puf`(fmwGs1h28}||N83>Uw`=S{jY!Z)fb=r_qQMZ{ry*8eD}?NeNwLL({I14 ztM6;B|NZiBt?1vD^k(%%|CI`VcK`YN-@UHKv5e>!oue4qrYIwNk)mTtlcr0So1zHl znU~$F1{E0oiHd8ZH_xixaCP-I-OSEEVSl$M>6DUz#RUd;)W{bm%IiKQ!bEXa^{qK= z$^|UQ`X=w5AaBo|)s%i(38exu)}|>rQfr);qGZkqlANuhl3pvw>e{F%@kg~VRZn9j z+wxB|^37Gr5dTC+fj;yJQ&iW+kU?A7!zd_USSW1;Y2gfXNekNkiNil}y!6Jwoi7eV zQ5?;AO&8$7nnn6YjS~V_Y>4h(fww5#{HxrMn z>bkCJiUz>CJQk~vJJ@Hqmnr$I1xYU#rE}K0s`rc4$o3ZGDYqJqrXc@B!>(R^xssjS z98xB&_%8HG3G$R%1LwnM`qh=}A|? zN}g2m;EdXrx=0ia!Y!Lvlz0DKtV``eC$)7~>(;xH(@Ky#YB%&j>`0i+YU`G!CW?n% zf>Oy&CzP@sy!`h-QM|qHi{j#qk5Y8e!r6|<5#@eZ9<04l@!dPE*xc2}V(?rS_2lu1 zwJ0vV0S{fP-7?H#*%S%*1`J8Gw+Hk$WmTPr^HOhEyMPl=*>aE_#XyD);G#epbF zVDWtMW^N-OThQ_sFzlw4B?CkhmtOIKdMoG$ zX$ci=ai>xN$=Jq`pn{f`p3i)!fTv3HISCXrka^ilW8Z z?DsOIjnz;!#c5xA@fSMI>gp4eW}_DiSi;Df!b(~>afBf@(_y=!IIFE`nWChvj<*x# zR-CObs3Ykonm41SMNXB}qPjw9G!uo{;tYiX)j~{tO*2Y8r5Png&1{hm+IFxG_FdS~ zimm8vnvzj0$a1pbxx0)g?G&mA**Jnc+--6l?BWpRI@ov-B^^es^rUT@B4X76DS%h>$HL*Fo<4kn*2(pbcF`%s_tgy|ALTpCyo{K!*f`*&9=y0Lw18(N#emIwZ;*dAV zhgq)V!M%Gq%v2PIdtI6;Z55o8%YnxD@xa0T3mLbxS_0ANo(W6N%Wy8ML-Jjlv{^RtXo`mqPQJw zpENgi+zE0=?fOiBW#kNcWGJbwK3>smO5 zq3uSOZUkvd6vd^pyVis#`Fed4+d{D?h82X3SLy_Lb#9C=vVP-A;%$_+cX5GDu|0w` zo0`ZQOXnVqD9-!u*HykBjX&aO%{Sx!G3q2Ii&nPXhqVIhC) zB9?&0&F)lJ{owJgAQ_mF<8(42$XnGx|E(a;_JgOl?F^gzT2J^BzO5vRV&P1W(AlV0 z9A6a>MT`FUbf#Yy^@AW;!J;I?`{lccqNEH9lCi9AqHRKuEElQF#2IdnrE=|DFsU}sye1804;{?&b+js@He6>l{6M2Pi@cB&w)-Ci-eTac6^UzMX) z*!L@1NWjQ?^sH}{VsUL=suitKynrFsVUQ*(EA(onWS0n9t^r)!!!$R`(@eQHl@~Bt=^ko7wFw6s2JZZ;8rjLwm?Ic}B7=FRt z`aUKdISR^8;QbLn7INBDs1>tF$rZ&3GacyLCvKaDG@295&^{fl*XNt}E+b%OCQRtZ z0_NBM_*%rgM462)ePBF^l!+GcSSa@i3?$6 zk6IYbAxQP_3y&!TNmlqmDt^K7-kQtU58FpxeRuLQ3ex^2imlk*zI>&Xmh@s!{QyeNV<)I6X|{%%DH%gO*F8+J4h|i1TpE7CCV)fMu$jK@+NB=i7rJNE;jWhE z=${xkC(rO$92QEl$L70n6_R^~ifAY0PQNYvS3-+iUn92OX3sXl+BsluZ0ltDsTeT)Y-*hSm%HSgieq z#M(TiuKi#J`D^>#Xn2`-_@w)tU59TJNUfR`p_{(a=Kk?FZ`W!MZM5?tde>i5OB?FB zg0%WHv1qRxIcKm#6Th(1F5J)sX%lorCXH9~Jj*Yz0ET0HJF z&$?qyMt}3J<6Y4ugZ-vIp8XGVNq^xUSae2VD8EdO6hYlxgdwqpJPLS;(va#dNk_uL zLQwS0E7EEG@QwJJzEWv^w8ompG-0%M`VCP<3P;NV$7mc~+V@e9*}ZlR+~&~zQnTPF#b z2!iSh{i%uY{qC!A|J#@kkGbzUFWJNoriOpPO~L5Zu^s6sgZeGiGBp)cUg)!j<%cyL zAI(l*k@c;Q^;A>+f_h|k)E;)1!X(j8)`B4I2xD*D!1k8dmDNd}nh`&(PxGiVj?ac} z`ZKLTcgLtt4cFJyaIHJ-V)B6Df8}XOZR@EKe;jgU9Xz2JmFIY+fQuG z$I*TBD&!myB>6lq3=0t?WourY_HIM_g}&aqU;FugptEPhoIU_-*Wl>^Y8!9KmCirbA43g`2K|c>1}7ddZ9mS^*d%@mcMOSNB=gfHjG{N$CF8u^^&dlurU0q?y^o1LfWV<7Z_x- z&SZj|Wl?61={zc;n-;vG@IHc= z)Iw*^g4C0bv%{;(b`> zXHwTxw7&#x|HL8Rg`lpVaCk6uO>!R2A7noK`_2F&)=Q=JA8nK}(9Y9&uz$l|xFyGw`q?COR7w4Ap{ zaops!uO~V0LoZPl7~QuWXP=}23sRrVj%hj#X{z<}mT`Kw%eRSd`ZM=0@5OulXrj!j z{rIu9j&yNU59s?&7N)w&ZljrGi_Mfy2^WWl1E%BR|>4RVpi_9_{8+4 zKXX6I?0K>a{WW#qdh_5eKPsrd(ARFcryuRFI&d^QE+70W{t2~iVdf-5u1Q6GC1&6J zf${gV#PDpdqjG#EeV(N7ijZCClAk%8na(djRh`#(p+8e8&$L`mL#eNMC>I452$GTD zYqqqS_9gv6HaPic4b2z&^W?If80)G}3rAmbt=>$T^YWu*zR;iUtG{ms<*Bc>lQQ~3 zF-e|1to!Xrt~f(3uu*pHo!&ku{RBy39E{Yr^{82N@Mf1F>m8@R<%&qxo$TeI|C7aA z|F$B!eGT1-vZN+Gt=?n|o9E5Eh<8@=ssA|G)p*e>>j) zvGfA9sTgpiv87Jzx?jY&;Ii5=ihwy{;ywNSNZ(&uRkfTEA3<}qU5QW zIo7)A>;8L^C?8t@-a>n6^|AX#25P*u(Y@4Uo)`M#Mew5z{FgGXKRi_MV>$9O`^whR z!FV1)QzGr~f;dj_x2{6RZ-OM?B`xUoy&&0YqU3^HmnD-Z#R&^iCAE{2scy($!0i8m zx(jTQ9D+1Pt6-X;<+S~xl{5uGQUq4PWDBdY`iw#t=DVT;3(R`m@6ZK!l4yqs}L z6!WAnAJvFr7UNZ&fk>1LGeI(HSH367+Np%)uMPfFdVPV^KP%p4^E*j z-vhm#YzhCQyASVi36k))fu?3E781!;~h2f0xJD`WPR(|YSo2AdQ6 zQa^a}Go_IC)tzuxly>UnfEm$EVfTWTzi`OUE9huR56n#Zsx51)^jUuHm>+Q|ig#bo-(xUWc8M%ctsCz|zxa*;K%n%|vOV5|mbS3JO?5J!MLF zETw2${)*CI$;zvybl$#Zt;Lkwae^$tXGP&SodssuN~Z!co<%2x@Cr&R-aN3p3s^y2 z>mtv%^H8e}i<`L|R z$rQKz<)j17S3@FbIUm2wueT;lcCzK0OE-P3-aRl`}37bMBQuTEN{p)I;datiVn3Z{MC#gTE_4}oC;Eyi6MdFu`J7y2`OFIsEudUB)r zYpym1NE%wEBnfxXmS9RM`z|YAI5b0cj(D6CA12{Wu3Rs7`ibgjT<4p@FZdu+lqSH= zam}us_V=9x4U#J)uD8?vy>sGzSd80w>`ZBv3ewKLonnm&tW**+2ML;P<|f-_S6}+w z6>pPnC;lvx08UR=b^bL-fsgeiyKnD~e;$oN9VEy!yM7B0-d*_;;IW zCMMq!i%iaww^c>y*u{{)fO$QRpNR>jIqBC=-kpkBVhw4y zYaw0z)Y`WxEnY!eGpaTcVzMN(uKkt_byPH{Q!yQ$?=1NzFinFa4_(QPE2~<*9p}8H z0(KxA&Ss@uQ!-YR!hZzGHkVaIvidm9n<*uFu-PKV zH5sqkO8oc`4Yy2KbyP1X2D@*Gom|6(0uCh;r5oFVG)yN>V<_N99(*H?Mzi}SgD9B~ zg1jy|i^1+&Za%i9h2smhrWD0>lITkNGkNAut`Upkp4Bs0edC<$KEluf#??K1yd*V+ z>`g(Mu7@+-3!2ju<)rUtnW7jRc6yjnD{?2qr>it4$5{S}fzy7>>Pk*>KsilrHAN9L ztS*X@eIaO!P;7;B-JXGurwK|&9h@+EY*Q2`%ycSSQyPnz;hq;=w>{IkhbWnH+6IaX z;APn}{Scoi^+R656r~diL0Z=*3Gu1GzC!s6)+gt{N$ZF=Q*x4=IjwNcN&d#%))8Ig z13A;TNRPuisDP~lQxam+k9D7gSNzW^9*b4zhWCkAUkW}7(gb4p77De(r<MG?1OmedE&z zXFDh9+4#KkvB%03t%L2%gU%UF6DEnWL{Tf=et$_6rQG(3+p!x33uv;5;&5;0RuiSe z0zuxhHb(lLJhpi@zUF-rdnSrXuQvZ}N*4-_twB>9O&eOKbT8D9DtRhugR+7yQ#9p{ zg^wtXMIOoYc?DL|?h#SctF4Thl8fU=4XUBoQNCH~O!C-Fs}{b)iFL5re|-Il3i!_P zu{^~B-u(~eEimLSux@|+?20IgLCrR|0`pKG|M9yb0fTSQUzeZCG9xi%Oafhu-+;fa z=EqCL@BwAtJ1=1*w1xQz6f=Cm^vaW)_Srnt#YtGq$OJZaf5k_{R3y*yis7@=*R^YU z7ZZgW(-@24E)nc5f)7JqZgv>H%I;AtH^Uz)K5{j~$DOY@lg#j;Y-1^csfmq^7Dk$J zapI93Mt&`0`wDxgi!*PE;k$dnY=WENv%13i3?m)6_^F5t07f7RJDGLmkoBk0yE4-C z;;&S1<;5>g;4s#bVXOsT*uD~DXom0j8XIBwh_wfp3@_+xa1U zu0@z_a5L5!hEM9u9=_u1h-SRoJ8^~?W-lz({8E=g{u5&nssodilxyCK(!~6t#&k5_9QWGEcF_w*QVx~6-f0Y_Wo*`k* zM{GtGi?Q0m9{OcQA()E-nvBhi(V`glV5!UISvOBqoO$T@#n|vy-oD>>7g;sKxG;l; zu{Ogf?Srw%-PPIPkQSOB>g0+M=8U!0-#gdEcB|Na_{xdyy&l%ll4UH$aje4bjc;Sr zNV#&q(2Vw8I^c17KhuB6=rM)#0g~f@I!tf(~PIo zcC@D%Z&~|fvpE+Xco|Eh&x1L4O6=)_(TB!jxSzrvg5x+aF^oVAHg1OV<;VgsYdiqS zw<-3ps~yK6g`qe+GuG#M*pJRV(g%Zyg!!IMvFn6po&6%k4Bc_YEMIIm(g4`Z>S5S* zHduxkKHXHTWVSFIqv%^^ypE!JgrQ38Z7nUDVgqn#%z^)sJSk_NmVjdRK(7KFq91bFGbjJ*MIXaf;Ox%x2Uj=Da?zcIKLMc>G`2X zhvCL-{D|QSD8*Sg&Cn8d)OWrz(Yxiyrn?Bwy4|t*lj$#PISvT&z zap0TX#G^-@P3gWj-vmxwCyvOg~^O z%YNdEPr@AHWX2=C>^3*!&Bl2_!gz;WzT9a>;f_}MqBAT$HZH5D(W~|aESa@s)ky)jE_)5uw-46!G&Pkyd&$^wX z{j%|KQ)BBcf(KP)_nso1b%s5n5^wf3I~8XM!et-P>7!Uy3+lSnNUFItf+~<2~R^AB3wW^bVY{*hdGGVc3{R?>bNmi=5~$ z=TH!ovE6UR)y*DIFV>}mxkWWk$59dAJ~E>lgu)zyB-ZHJ4r3)hgpWrV^JJ(Gkt?>Y z@qBsID&~o1rU0*h!Iz7M~lGMo-*HYK{J4 zh69lFPG)Eysmrch#Q30HpHPFU3Q$}iCv%fxIL2Pn)wD>GEj zJLHA6L2 z*y=aKMKbRBG79eY!fY(pq@bcAIleH%SvOi5JsO@u2}{?FhGvUjMwo>FxAC0 z+Qli$XBe;P+&VJDUHymz(vu&d1$0WXahTQr+(lIrB)Tx8uv zDBF^-1Y4SMbw^a3`DW(Mmm+@4&5XA!Z4X}4bD^0lEK1vC_hL;xX0ZoFY3_{l+`I?O z6X`uMw~jG;l&aHYQjv9w=5?`(vq}WndQfIpPEf^IG_MxDPJ0c1rQct{yB5Mm#Nomu zX2$H_wU?e-G@}qWQ5hDo&&rHffFENILj$$0xEIA(G;e3CGUK_sa}HbinoF^6GQ!Zg zVyuPnezctuQH*0|>%I++{jfLgd%?=e#dWmXzJCZK5$e9pmfd+j+C3w^2T$)kq7dwd zWxq#Mzdi5}r0#i-5nj;CaZY~dK{D(WiMcT$t}r{t!XAq3G@66NtYV%p3=*s77Vr`w z*yvMy#8jOz0E_hdhcJ6rupP!4{K=tNO)fDi;*^7Ccn;exFQDQqN;J~#$_a-UY97W` z7)}PiTb-}4JuS?xt9D@qK_A>*`3fs-!q!~`U02PaM>pdiI#>GT;7B*eXxt3HVsFsR zXi7CZ2f}c-Z^g0TV)$&Vun73(!wD{Oe+Bci_;!c4@@C~5i>y?);@ccP`t-y!!}(Ir z{S}TzYcbNa+%A+1E$_gDVehttUx?v>Z);B9Bi2x@Xe~!6VyF_gcI>(mL$$((x#MQ| zL*4$6F#MsOZ9)tMS?j(SV1_5|t@{ZKGu+jLt#UK2OPrLguEFv?YhvEGIVe`5fR(ILO z7KYVbIleH%iMjlgUt$%wNCK$M_=iRYVDTYHTfPKkhEZ27kIaX0zs$Q|Fu#^C-(NN3 zxw~@u?>vFHNLDUrh@mno?Bs`@M*3K5Zkvydn=HL{GyIB^FNUGvE-d}B@j=z%-0t;e z*toq4{qtMKHypG51TqFpaA*4`aXBKI+%@CfqNMAuyH}aae7SE?he2 z&NKE>1a*df$ISM^b61#Ezy6>S_i~Ss!>+JMxA2g*g&u2TsqR3HtUHz`47KL&aFH-{ z-u7f04&$zKUa>H)?mY8mIMQWi1r)ZYo#6Gl8BWX+rOk}{#TJ+udfJ>jEG;?@1K>1C ze)b3*V7(Ua(~{4)O$f8y=!9fgmpoo+hC%MmWajGD&W)}DoS>H8v% z)E2|A+FpI4X~rv{I<>bM_inXFFng%352np{V&(}JGoE#IW!6UCYZNee?=O4v?a21X1D%NR9G zjAraE!gno=r7jN(*4kwlh8Y;EH^VKfIR#1>4r8-1Qk=by>td;4##>fPz|O$4s0GQ0@D04)2g}_akCGTqDVdF~?kN0~Q8J9W(-O#CY!J`4>kUqfzMGi9776NE9V)uw3Ncj|Sf>7Z$Z*EOeL(%Vsn1v0Mj7w|X=F zN`j%xa54-hi7-R!v(MZd5<_R4uuQ3W@c<+Npcscf8*5?c7U?q)?0w>EpT^R4v*HPA zpRohP@Tk_9M|$$5PGhNV=QTa=bu>GkaP(YOHbZxGPhvnb*6U=dr7oC-wfK3)^vlDp zHWND3rI+CVE*fDz`yAIqv&C2o!!a^; z7s1`$H>uKSj-nA}gJWD96#}Q5-eKs099PUaGn$y=imf&ZU#1^-iaRm%;tc0Y2}9j` zlpGlh-@f7^J72*hyT*J9H6F6yjgovGqwsZ1Y3JS>#ZWsmw!?7C8pUrenc?xpC^^D* z1|#klD=x*k!Es8JT$vTaon$zUJ`BC*#@sKJgHVNy6k#<~m_w?}_=j?UCx&7BBZ=$G zcs169UyRBTro&-+H{-838F_KmC$4T{Lr3MKl#vr~Gt@lB?ej3h>D}UX6+_WH@(h+4 z8b6NM^n7U*l@nnzKg4k!qdB{y8JfAmtdaSQ73XdnHDBS3pTc(PvUt|rK9BL>^Dy>O z(cL$w%-+tBrHV? zZMD&xP0R5R5qhp$Ji*i;zI=)3`EONK=Rh-ci=i!KAu1hY_ zjLx|toP(|23|FHStuWpLa&cPQ2`4ig+N>-YM&)Hc5}GK+KQv z8H=DbvXw6mtChXiQ{9P2*kj%EWi1v#ey;guJVyJj?tbXqefRD`=dG~pu#0_UTGAPS zePJw)uxweopx4i@F6JwIY;3HB;Zd!ze5J8u*ezSuZs^?-7M)>ph_A7ed)!cZkP*21vsjMXr-8;y0df?Jlc5r#oO!aM+C=*OH_Cc}Ji^i1c$4#Tu^&J3F$ z>bTnrvw>>HbxF+0yaqaYrZa|y;Zd!zy9i3m=`5XL551dUOEJ{or(ZeZWV7eutr$zG9?ipyf5=ZFnxQ%81mgEsFn5vP@=FnXfmR#%mAZ_Jr}wyZhH>E; z-y9{%$>3YHbKDBSU|;jhdzfYqUrEHE7_Wdkt(+LzYIF4aD|~@nm<=&AoZcfxwZ4Rn z191CaoZAdV(8#HR8BT_g3q5Asb-rF8hL5%f%MY!G>e^9fh8`tj8Af*A8oTR)mas7| zlEFuk^Ui`N#$z-cnqkJ$JE1&bT%=qn!ajIoI}ELfIqi}SdngjkL-GN8cXl6|nQ?Xd z{X^`@pNZ2E8)*Iu^U04Gp0>=HyHH{eKa_ACF*KH)V_=106wRC`TJyu*f4@`U$*}lV z%1kJa8M>o|t@|si;;bCV*rSJaS!LWs*qmE=pF|9O9&;sB-i&`JK6o*49952c`JtJ(=*;CGgd=a0)-{USa_@xm3VU~h_EzzUu++#w&iBHNOrC>i1B8# zi@l*{*e~jj6sZ_!BP>&DKiIIlN04m}*)*1Z!IxfxrC<2j5Mw8dRll9IEOYZqG{U0q zZhW0PSn4uZ$rpAPVf|&V%$L2{4%j=JuoyQwIR?ep`b^}J8Oz;s+iIuqNasO?Vj+dS zdv1F=k7`npet)H-cS~6Gm(y8F`Mv9#aieDp_rq6Crr#G1W9jY3W^aab_w1}jznIEJ z9HB8E?uZG?tUHJ0%ki7e&WDO#9AV=wf-V~E628lAMlD+RGZ5ZL9*X$XQVfH{jHND| z5>eRcH=}-8eyZ$d{6h)kbIu!@-om`>9ni}CGD}+I7+ow<@2|Wi;Q#ISf4H&!J-ds1 z{`J=%zW(st```WQt1mwL?{7c+`}?oH`0ktk`lMX@({I1)*YU>vEB*Utcggp^`SH(M zhZaM9UK_f7>0-F>WNOuX<)F;+p;y{>P;V8s^Fs&E$CmSwhvB}mN-o7OCuPGTPF!yG z@Rer!Z}~3X{lc<*3#a8=C28ob$l(vw+))ujnYHd~a$*<@uxgS^_>_~jL1XThhih`{ zeiT9s&9YS!=kNEm9*T^+2=2Elj!3gz{y)1y_-O$D001A02m}BC000301^_}s0swUY zeaC{R^|0*kSdXm&`zc5ek&yyl#Sd+u>MNpf)L6 zL@K02G@?I5U*eJyt1)C^IkDQ~gembhMXcq-`xi#?j4&!joImma-C*`_AprmYABzYC b000000RIL6LPG)o8vp|U0000000000Vge0~ literal 0 HcmV?d00001 diff --git a/test/gzip_specials.js b/test/gzip_specials.js index 4dbf8e8..db2bd54 100644 --- a/test/gzip_specials.js +++ b/test/gzip_specials.js @@ -4,8 +4,10 @@ const fs = require('fs'); const path = require('path'); const assert = require('assert'); +const zlib = require('zlib'); const pako = require('../index'); +const { Z_SYNC_FLUSH } = require('../lib/zlib/constants'); function a2s(array) { @@ -18,7 +20,7 @@ describe('Gzip special cases', () => { it('Read custom headers', () => { const data = fs.readFileSync(path.join(__dirname, 'fixtures/gzip-headers.gz')); const inflator = new pako.Inflate(); - inflator.push(data, true); + inflator.push(data); assert.strictEqual(inflator.header.name, 'test name'); assert.strictEqual(inflator.header.comment, 'test comment'); @@ -42,7 +44,7 @@ describe('Gzip special cases', () => { deflator.push(data, true); const inflator = new pako.Inflate({ to: 'string' }); - inflator.push(deflator.result, true); + inflator.push(deflator.result); assert.strictEqual(inflator.err, 0); assert.strictEqual(inflator.result, data); @@ -55,26 +57,45 @@ describe('Gzip special cases', () => { assert.deepStrictEqual(header.extra, new Uint8Array([ 4, 5, 6 ])); }); - it('Read stream with SYNC marks', () => { - let inflator, strm, _in, len, pos = 0, i = 0; + it('Read stream with SYNC marks (multistream source, file 1)', () => { const data = fs.readFileSync(path.join(__dirname, 'fixtures/gzip-joined.gz')); - do { - len = data.length - pos; - _in = new Uint8Array(len); - _in.set(data.subarray(pos, pos + len), 0); + assert.deepStrictEqual( + pako.ungzip(data), + new Uint8Array(zlib.gunzipSync(data)) + ); + }); - inflator = new pako.Inflate(); - strm = inflator.strm; - inflator.push(_in, true); + it.skip('Read stream with SYNC marks (multistream source, file 2)', () => { + const data = fs.readFileSync(path.join(__dirname, 'fixtures/gzip-joined-bgzip.gz')); - assert(!inflator.err, inflator.msg); + assert.deepStrictEqual( + // Currently fails with this chunk size + pako.ungzip(data, { chunkSize: 16384 }), + new Uint8Array(zlib.gunzipSync(data)) + ); + }); - pos += strm.next_in; - i++; - } while (strm.avail_in); + it('Write with Z_SYNC_FLUSH', () => { + const deflator = new pako.Deflate({ gzip: true }); - assert(i === 2, 'invalid blobs count'); + let count = 0; + + deflator.onData = function (chunk) { + this.chunks.push(chunk); + count++; + }; + + deflator.push('12345', Z_SYNC_FLUSH); + deflator.push('67890', true); + + const flushed = deflator.result; + const normal = pako.gzip('1234567890'); + + assert.strictEqual(count, 2); + + assert.deepStrictEqual(pako.ungzip(flushed), pako.ungzip(normal)); + assert.ok(flushed.length > normal.length); }); }); diff --git a/test/inflate.js b/test/inflate.js index 6b8d44a..0ede223 100644 --- a/test/inflate.js +++ b/test/inflate.js @@ -171,7 +171,7 @@ describe('Inflate with dictionary', () => { assert.throws(function () { pako.inflate(zCompressed, { dictionary: 'world' }); - }, /data error/); + }, /need dictionary/); }); it('trivial dictionary', () => { diff --git a/test/inflate_cover_ported.js b/test/inflate_cover_ported.js index ae82eee..ef844af 100644 --- a/test/inflate_cover_ported.js +++ b/test/inflate_cover_ported.js @@ -28,7 +28,7 @@ function testInflate(hex, wbits, status) { assert(e === msg[status]); return; } - inflator.push(new Uint8Array(h2b(hex)), true); + inflator.push(new Uint8Array(h2b(hex))); assert.strictEqual(inflator.err, status); }