You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

419 lines
12 KiB

1 year ago
  1. 'use strict';
  2. const zlib_inflate = require('./zlib/inflate');
  3. const utils = require('./utils/common');
  4. const strings = require('./utils/strings');
  5. const msg = require('./zlib/messages');
  6. const ZStream = require('./zlib/zstream');
  7. const GZheader = require('./zlib/gzheader');
  8. const toString = Object.prototype.toString;
  9. /* Public constants ==========================================================*/
  10. /* ===========================================================================*/
  11. const {
  12. Z_NO_FLUSH, Z_FINISH,
  13. Z_OK, Z_STREAM_END, Z_NEED_DICT, Z_STREAM_ERROR, Z_DATA_ERROR, Z_MEM_ERROR
  14. } = require('./zlib/constants');
  15. /* ===========================================================================*/
  16. /**
  17. * class Inflate
  18. *
  19. * Generic JS-style wrapper for zlib calls. If you don't need
  20. * streaming behaviour - use more simple functions: [[inflate]]
  21. * and [[inflateRaw]].
  22. **/
  23. /* internal
  24. * inflate.chunks -> Array
  25. *
  26. * Chunks of output data, if [[Inflate#onData]] not overridden.
  27. **/
  28. /**
  29. * Inflate.result -> Uint8Array|String
  30. *
  31. * Uncompressed result, generated by default [[Inflate#onData]]
  32. * and [[Inflate#onEnd]] handlers. Filled after you push last chunk
  33. * (call [[Inflate#push]] with `Z_FINISH` / `true` param).
  34. **/
  35. /**
  36. * Inflate.err -> Number
  37. *
  38. * Error code after inflate finished. 0 (Z_OK) on success.
  39. * Should be checked if broken data possible.
  40. **/
  41. /**
  42. * Inflate.msg -> String
  43. *
  44. * Error message, if [[Inflate.err]] != 0
  45. **/
  46. /**
  47. * new Inflate(options)
  48. * - options (Object): zlib inflate options.
  49. *
  50. * Creates new inflator instance with specified params. Throws exception
  51. * on bad params. Supported options:
  52. *
  53. * - `windowBits`
  54. * - `dictionary`
  55. *
  56. * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
  57. * for more information on these.
  58. *
  59. * Additional options, for internal needs:
  60. *
  61. * - `chunkSize` - size of generated data chunks (16K by default)
  62. * - `raw` (Boolean) - do raw inflate
  63. * - `to` (String) - if equal to 'string', then result will be converted
  64. * from utf8 to utf16 (javascript) string. When string output requested,
  65. * chunk length can differ from `chunkSize`, depending on content.
  66. *
  67. * By default, when no options set, autodetect deflate/gzip data format via
  68. * wrapper header.
  69. *
  70. * ##### Example:
  71. *
  72. * ```javascript
  73. * const pako = require('pako')
  74. * const chunk1 = new Uint8Array([1,2,3,4,5,6,7,8,9])
  75. * const chunk2 = new Uint8Array([10,11,12,13,14,15,16,17,18,19]);
  76. *
  77. * const inflate = new pako.Inflate({ level: 3});
  78. *
  79. * inflate.push(chunk1, false);
  80. * inflate.push(chunk2, true); // true -> last chunk
  81. *
  82. * if (inflate.err) { throw new Error(inflate.err); }
  83. *
  84. * console.log(inflate.result);
  85. * ```
  86. **/
  87. function Inflate(options) {
  88. this.options = utils.assign({
  89. chunkSize: 1024 * 64,
  90. windowBits: 15,
  91. to: ''
  92. }, options || {});
  93. const opt = this.options;
  94. // Force window size for `raw` data, if not set directly,
  95. // because we have no header for autodetect.
  96. if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) {
  97. opt.windowBits = -opt.windowBits;
  98. if (opt.windowBits === 0) { opt.windowBits = -15; }
  99. }
  100. // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate
  101. if ((opt.windowBits >= 0) && (opt.windowBits < 16) &&
  102. !(options && options.windowBits)) {
  103. opt.windowBits += 32;
  104. }
  105. // Gzip header has no info about windows size, we can do autodetect only
  106. // for deflate. So, if window size not set, force it to max when gzip possible
  107. if ((opt.windowBits > 15) && (opt.windowBits < 48)) {
  108. // bit 3 (16) -> gzipped data
  109. // bit 4 (32) -> autodetect gzip/deflate
  110. if ((opt.windowBits & 15) === 0) {
  111. opt.windowBits |= 15;
  112. }
  113. }
  114. this.err = 0; // error code, if happens (0 = Z_OK)
  115. this.msg = ''; // error message
  116. this.ended = false; // used to avoid multiple onEnd() calls
  117. this.chunks = []; // chunks of compressed data
  118. this.strm = new ZStream();
  119. this.strm.avail_out = 0;
  120. let status = zlib_inflate.inflateInit2(
  121. this.strm,
  122. opt.windowBits
  123. );
  124. if (status !== Z_OK) {
  125. throw new Error(msg[status]);
  126. }
  127. this.header = new GZheader();
  128. zlib_inflate.inflateGetHeader(this.strm, this.header);
  129. // Setup dictionary
  130. if (opt.dictionary) {
  131. // Convert data if needed
  132. if (typeof opt.dictionary === 'string') {
  133. opt.dictionary = strings.string2buf(opt.dictionary);
  134. } else if (toString.call(opt.dictionary) === '[object ArrayBuffer]') {
  135. opt.dictionary = new Uint8Array(opt.dictionary);
  136. }
  137. if (opt.raw) { //In raw mode we need to set the dictionary early
  138. status = zlib_inflate.inflateSetDictionary(this.strm, opt.dictionary);
  139. if (status !== Z_OK) {
  140. throw new Error(msg[status]);
  141. }
  142. }
  143. }
  144. }
  145. /**
  146. * Inflate#push(data[, flush_mode]) -> Boolean
  147. * - data (Uint8Array|ArrayBuffer): input data
  148. * - flush_mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE
  149. * flush modes. See constants. Skipped or `false` means Z_NO_FLUSH,
  150. * `true` means Z_FINISH.
  151. *
  152. * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with
  153. * new output chunks. Returns `true` on success. If end of stream detected,
  154. * [[Inflate#onEnd]] will be called.
  155. *
  156. * `flush_mode` is not needed for normal operation, because end of stream
  157. * detected automatically. You may try to use it for advanced things, but
  158. * this functionality was not tested.
  159. *
  160. * On fail call [[Inflate#onEnd]] with error code and return false.
  161. *
  162. * ##### Example
  163. *
  164. * ```javascript
  165. * push(chunk, false); // push one of data chunks
  166. * ...
  167. * push(chunk, true); // push last chunk
  168. * ```
  169. **/
  170. Inflate.prototype.push = function (data, flush_mode) {
  171. const strm = this.strm;
  172. const chunkSize = this.options.chunkSize;
  173. const dictionary = this.options.dictionary;
  174. let status, _flush_mode, last_avail_out;
  175. if (this.ended) return false;
  176. if (flush_mode === ~~flush_mode) _flush_mode = flush_mode;
  177. else _flush_mode = flush_mode === true ? Z_FINISH : Z_NO_FLUSH;
  178. // Convert data if needed
  179. if (toString.call(data) === '[object ArrayBuffer]') {
  180. strm.input = new Uint8Array(data);
  181. } else {
  182. strm.input = data;
  183. }
  184. strm.next_in = 0;
  185. strm.avail_in = strm.input.length;
  186. for (;;) {
  187. if (strm.avail_out === 0) {
  188. strm.output = new Uint8Array(chunkSize);
  189. strm.next_out = 0;
  190. strm.avail_out = chunkSize;
  191. }
  192. status = zlib_inflate.inflate(strm, _flush_mode);
  193. if (status === Z_NEED_DICT && dictionary) {
  194. status = zlib_inflate.inflateSetDictionary(strm, dictionary);
  195. if (status === Z_OK) {
  196. status = zlib_inflate.inflate(strm, _flush_mode);
  197. } else if (status === Z_DATA_ERROR) {
  198. // Replace code with more verbose
  199. status = Z_NEED_DICT;
  200. }
  201. }
  202. // Skip snyc markers if more data follows and not raw mode
  203. while (strm.avail_in > 0 &&
  204. status === Z_STREAM_END &&
  205. strm.state.wrap > 0 &&
  206. data[strm.next_in] !== 0)
  207. {
  208. zlib_inflate.inflateReset(strm);
  209. status = zlib_inflate.inflate(strm, _flush_mode);
  210. }
  211. switch (status) {
  212. case Z_STREAM_ERROR:
  213. case Z_DATA_ERROR:
  214. case Z_NEED_DICT:
  215. case Z_MEM_ERROR:
  216. this.onEnd(status);
  217. this.ended = true;
  218. return false;
  219. }
  220. // Remember real `avail_out` value, because we may patch out buffer content
  221. // to align utf8 strings boundaries.
  222. last_avail_out = strm.avail_out;
  223. if (strm.next_out) {
  224. if (strm.avail_out === 0 || status === Z_STREAM_END) {
  225. if (this.options.to === 'string') {
  226. let next_out_utf8 = strings.utf8border(strm.output, strm.next_out);
  227. let tail = strm.next_out - next_out_utf8;
  228. let utf8str = strings.buf2string(strm.output, next_out_utf8);
  229. // move tail & realign counters
  230. strm.next_out = tail;
  231. strm.avail_out = chunkSize - tail;
  232. if (tail) strm.output.set(strm.output.subarray(next_out_utf8, next_out_utf8 + tail), 0);
  233. this.onData(utf8str);
  234. } else {
  235. this.onData(strm.output.length === strm.next_out ? strm.output : strm.output.subarray(0, strm.next_out));
  236. }
  237. }
  238. }
  239. // Must repeat iteration if out buffer is full
  240. if (status === Z_OK && last_avail_out === 0) continue;
  241. // Finalize if end of stream reached.
  242. if (status === Z_STREAM_END) {
  243. status = zlib_inflate.inflateEnd(this.strm);
  244. this.onEnd(status);
  245. this.ended = true;
  246. return true;
  247. }
  248. if (strm.avail_in === 0) break;
  249. }
  250. return true;
  251. };
  252. /**
  253. * Inflate#onData(chunk) -> Void
  254. * - chunk (Uint8Array|String): output data. When string output requested,
  255. * each chunk will be string.
  256. *
  257. * By default, stores data blocks in `chunks[]` property and glue
  258. * those in `onEnd`. Override this handler, if you need another behaviour.
  259. **/
  260. Inflate.prototype.onData = function (chunk) {
  261. this.chunks.push(chunk);
  262. };
  263. /**
  264. * Inflate#onEnd(status) -> Void
  265. * - status (Number): inflate status. 0 (Z_OK) on success,
  266. * other if not.
  267. *
  268. * Called either after you tell inflate that the input stream is
  269. * complete (Z_FINISH). By default - join collected chunks,
  270. * free memory and fill `results` / `err` properties.
  271. **/
  272. Inflate.prototype.onEnd = function (status) {
  273. // On success - join
  274. if (status === Z_OK) {
  275. if (this.options.to === 'string') {
  276. this.result = this.chunks.join('');
  277. } else {
  278. this.result = utils.flattenChunks(this.chunks);
  279. }
  280. }
  281. this.chunks = [];
  282. this.err = status;
  283. this.msg = this.strm.msg;
  284. };
  285. /**
  286. * inflate(data[, options]) -> Uint8Array|String
  287. * - data (Uint8Array): input data to decompress.
  288. * - options (Object): zlib inflate options.
  289. *
  290. * Decompress `data` with inflate/ungzip and `options`. Autodetect
  291. * format via wrapper header by default. That's why we don't provide
  292. * separate `ungzip` method.
  293. *
  294. * Supported options are:
  295. *
  296. * - windowBits
  297. *
  298. * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
  299. * for more information.
  300. *
  301. * Sugar (options):
  302. *
  303. * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
  304. * negative windowBits implicitly.
  305. * - `to` (String) - if equal to 'string', then result will be converted
  306. * from utf8 to utf16 (javascript) string. When string output requested,
  307. * chunk length can differ from `chunkSize`, depending on content.
  308. *
  309. *
  310. * ##### Example:
  311. *
  312. * ```javascript
  313. * const pako = require('pako');
  314. * const input = pako.deflate(new Uint8Array([1,2,3,4,5,6,7,8,9]));
  315. * let output;
  316. *
  317. * try {
  318. * output = pako.inflate(input);
  319. * } catch (err) {
  320. * console.log(err);
  321. * }
  322. * ```
  323. **/
  324. function inflate(input, options) {
  325. const inflator = new Inflate(options);
  326. inflator.push(input);
  327. // That will never happens, if you don't cheat with options :)
  328. if (inflator.err) throw inflator.msg || msg[inflator.err];
  329. return inflator.result;
  330. }
  331. /**
  332. * inflateRaw(data[, options]) -> Uint8Array|String
  333. * - data (Uint8Array): input data to decompress.
  334. * - options (Object): zlib inflate options.
  335. *
  336. * The same as [[inflate]], but creates raw data, without wrapper
  337. * (header and adler32 crc).
  338. **/
  339. function inflateRaw(input, options) {
  340. options = options || {};
  341. options.raw = true;
  342. return inflate(input, options);
  343. }
  344. /**
  345. * ungzip(data[, options]) -> Uint8Array|String
  346. * - data (Uint8Array): input data to decompress.
  347. * - options (Object): zlib inflate options.
  348. *
  349. * Just shortcut to [[inflate]], because it autodetects format
  350. * by header.content. Done for convenience.
  351. **/
  352. module.exports.Inflate = Inflate;
  353. module.exports.inflate = inflate;
  354. module.exports.inflateRaw = inflateRaw;
  355. module.exports.ungzip = inflate;
  356. module.exports.constants = require('./zlib/constants');