deflate.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376
  1. 'use strict';
  2. var zlib_deflate = require('./zlib/deflate.js');
  3. var utils = require('./utils/common');
  4. var strings = require('./utils/strings');
  5. var msg = require('./zlib/messages');
  6. var zstream = require('./zlib/zstream');
  7. var toString = Object.prototype.toString;
  8. /* Public constants ==========================================================*/
  9. /* ===========================================================================*/
  10. var Z_NO_FLUSH = 0;
  11. var Z_FINISH = 4;
  12. var Z_OK = 0;
  13. var Z_STREAM_END = 1;
  14. var Z_SYNC_FLUSH = 2;
  15. var Z_DEFAULT_COMPRESSION = -1;
  16. var Z_DEFAULT_STRATEGY = 0;
  17. var Z_DEFLATED = 8;
  18. /* ===========================================================================*/
  19. /**
  20. * class Deflate
  21. *
  22. * Generic JS-style wrapper for zlib calls. If you don't need
  23. * streaming behaviour - use more simple functions: [[deflate]],
  24. * [[deflateRaw]] and [[gzip]].
  25. **/
  26. /* internal
  27. * Deflate.chunks -> Array
  28. *
  29. * Chunks of output data, if [[Deflate#onData]] not overriden.
  30. **/
  31. /**
  32. * Deflate.result -> Uint8Array|Array
  33. *
  34. * Compressed result, generated by default [[Deflate#onData]]
  35. * and [[Deflate#onEnd]] handlers. Filled after you push last chunk
  36. * (call [[Deflate#push]] with `Z_FINISH` / `true` param) or if you
  37. * push a chunk with explicit flush (call [[Deflate#push]] with
  38. * `Z_SYNC_FLUSH` param).
  39. **/
  40. /**
  41. * Deflate.err -> Number
  42. *
  43. * Error code after deflate finished. 0 (Z_OK) on success.
  44. * You will not need it in real life, because deflate errors
  45. * are possible only on wrong options or bad `onData` / `onEnd`
  46. * custom handlers.
  47. **/
  48. /**
  49. * Deflate.msg -> String
  50. *
  51. * Error message, if [[Deflate.err]] != 0
  52. **/
  53. /**
  54. * new Deflate(options)
  55. * - options (Object): zlib deflate options.
  56. *
  57. * Creates new deflator instance with specified params. Throws exception
  58. * on bad params. Supported options:
  59. *
  60. * - `level`
  61. * - `windowBits`
  62. * - `memLevel`
  63. * - `strategy`
  64. *
  65. * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
  66. * for more information on these.
  67. *
  68. * Additional options, for internal needs:
  69. *
  70. * - `chunkSize` - size of generated data chunks (16K by default)
  71. * - `raw` (Boolean) - do raw deflate
  72. * - `gzip` (Boolean) - create gzip wrapper
  73. * - `to` (String) - if equal to 'string', then result will be "binary string"
  74. * (each char code [0..255])
  75. * - `header` (Object) - custom header for gzip
  76. * - `text` (Boolean) - true if compressed data believed to be text
  77. * - `time` (Number) - modification time, unix timestamp
  78. * - `os` (Number) - operation system code
  79. * - `extra` (Array) - array of bytes with extra data (max 65536)
  80. * - `name` (String) - file name (binary string)
  81. * - `comment` (String) - comment (binary string)
  82. * - `hcrc` (Boolean) - true if header crc should be added
  83. *
  84. * ##### Example:
  85. *
  86. * ```javascript
  87. * var pako = require('pako')
  88. * , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9])
  89. * , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]);
  90. *
  91. * var deflate = new pako.Deflate({ level: 3});
  92. *
  93. * deflate.push(chunk1, false);
  94. * deflate.push(chunk2, true); // true -> last chunk
  95. *
  96. * if (deflate.err) { throw new Error(deflate.err); }
  97. *
  98. * console.log(deflate.result);
  99. * ```
  100. **/
  101. var Deflate = function(options) {
  102. this.options = utils.assign({
  103. level: Z_DEFAULT_COMPRESSION,
  104. method: Z_DEFLATED,
  105. chunkSize: 16384,
  106. windowBits: 15,
  107. memLevel: 8,
  108. strategy: Z_DEFAULT_STRATEGY,
  109. to: ''
  110. }, options || {});
  111. var opt = this.options;
  112. if (opt.raw && (opt.windowBits > 0)) {
  113. opt.windowBits = -opt.windowBits;
  114. }
  115. else if (opt.gzip && (opt.windowBits > 0) && (opt.windowBits < 16)) {
  116. opt.windowBits += 16;
  117. }
  118. this.err = 0; // error code, if happens (0 = Z_OK)
  119. this.msg = ''; // error message
  120. this.ended = false; // used to avoid multiple onEnd() calls
  121. this.chunks = []; // chunks of compressed data
  122. this.strm = new zstream();
  123. this.strm.avail_out = 0;
  124. var status = zlib_deflate.deflateInit2(
  125. this.strm,
  126. opt.level,
  127. opt.method,
  128. opt.windowBits,
  129. opt.memLevel,
  130. opt.strategy
  131. );
  132. if (status !== Z_OK) {
  133. throw new Error(msg[status]);
  134. }
  135. if (opt.header) {
  136. zlib_deflate.deflateSetHeader(this.strm, opt.header);
  137. }
  138. };
  139. /**
  140. * Deflate#push(data[, mode]) -> Boolean
  141. * - data (Uint8Array|Array|ArrayBuffer|String): input data. Strings will be
  142. * converted to utf8 byte sequence.
  143. * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
  144. * See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH.
  145. *
  146. * Sends input data to deflate pipe, generating [[Deflate#onData]] calls with
  147. * new compressed chunks. Returns `true` on success. The last data block must have
  148. * mode Z_FINISH (or `true`). That will flush internal pending buffers and call
  149. * [[Deflate#onEnd]]. For interim explicit flushes (without ending the stream) you
  150. * can use mode Z_SYNC_FLUSH, keeping the compression context.
  151. *
  152. * On fail call [[Deflate#onEnd]] with error code and return false.
  153. *
  154. * We strongly recommend to use `Uint8Array` on input for best speed (output
  155. * array format is detected automatically). Also, don't skip last param and always
  156. * use the same type in your code (boolean or number). That will improve JS speed.
  157. *
  158. * For regular `Array`-s make sure all elements are [0..255].
  159. *
  160. * ##### Example
  161. *
  162. * ```javascript
  163. * push(chunk, false); // push one of data chunks
  164. * ...
  165. * push(chunk, true); // push last chunk
  166. * ```
  167. **/
  168. Deflate.prototype.push = function(data, mode) {
  169. var strm = this.strm;
  170. var chunkSize = this.options.chunkSize;
  171. var status, _mode;
  172. if (this.ended) { return false; }
  173. _mode = (mode === ~~mode) ? mode : ((mode === true) ? Z_FINISH : Z_NO_FLUSH);
  174. // Convert data if needed
  175. if (typeof data === 'string') {
  176. // If we need to compress text, change encoding to utf8.
  177. strm.input = strings.string2buf(data);
  178. } else if (toString.call(data) === '[object ArrayBuffer]') {
  179. strm.input = new Uint8Array(data);
  180. } else {
  181. strm.input = data;
  182. }
  183. strm.next_in = 0;
  184. strm.avail_in = strm.input.length;
  185. do {
  186. if (strm.avail_out === 0) {
  187. strm.output = new utils.Buf8(chunkSize);
  188. strm.next_out = 0;
  189. strm.avail_out = chunkSize;
  190. }
  191. status = zlib_deflate.deflate(strm, _mode); /* no bad return value */
  192. if (status !== Z_STREAM_END && status !== Z_OK) {
  193. this.onEnd(status);
  194. this.ended = true;
  195. return false;
  196. }
  197. if (strm.avail_out === 0 || (strm.avail_in === 0 && (_mode === Z_FINISH || _mode === Z_SYNC_FLUSH))) {
  198. if (this.options.to === 'string') {
  199. this.onData(strings.buf2binstring(utils.shrinkBuf(strm.output, strm.next_out)));
  200. } else {
  201. this.onData(utils.shrinkBuf(strm.output, strm.next_out));
  202. }
  203. }
  204. } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== Z_STREAM_END);
  205. // Finalize on the last chunk.
  206. if (_mode === Z_FINISH) {
  207. status = zlib_deflate.deflateEnd(this.strm);
  208. this.onEnd(status);
  209. this.ended = true;
  210. return status === Z_OK;
  211. }
  212. // callback interim results if Z_SYNC_FLUSH.
  213. if (_mode === Z_SYNC_FLUSH) {
  214. this.onEnd(Z_OK);
  215. strm.avail_out = 0;
  216. return true;
  217. }
  218. return true;
  219. };
  220. /**
  221. * Deflate#onData(chunk) -> Void
  222. * - chunk (Uint8Array|Array|String): ouput data. Type of array depends
  223. * on js engine support. When string output requested, each chunk
  224. * will be string.
  225. *
  226. * By default, stores data blocks in `chunks[]` property and glue
  227. * those in `onEnd`. Override this handler, if you need another behaviour.
  228. **/
  229. Deflate.prototype.onData = function(chunk) {
  230. this.chunks.push(chunk);
  231. };
  232. /**
  233. * Deflate#onEnd(status) -> Void
  234. * - status (Number): deflate status. 0 (Z_OK) on success,
  235. * other if not.
  236. *
  237. * Called once after you tell deflate that the input stream is
  238. * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH)
  239. * or if an error happened. By default - join collected chunks,
  240. * free memory and fill `results` / `err` properties.
  241. **/
  242. Deflate.prototype.onEnd = function(status) {
  243. // On success - join
  244. if (status === Z_OK) {
  245. if (this.options.to === 'string') {
  246. this.result = this.chunks.join('');
  247. } else {
  248. this.result = utils.flattenChunks(this.chunks);
  249. }
  250. }
  251. this.chunks = [];
  252. this.err = status;
  253. this.msg = this.strm.msg;
  254. };
  255. /**
  256. * deflate(data[, options]) -> Uint8Array|Array|String
  257. * - data (Uint8Array|Array|String): input data to compress.
  258. * - options (Object): zlib deflate options.
  259. *
  260. * Compress `data` with deflate alrorythm and `options`.
  261. *
  262. * Supported options are:
  263. *
  264. * - level
  265. * - windowBits
  266. * - memLevel
  267. * - strategy
  268. *
  269. * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
  270. * for more information on these.
  271. *
  272. * Sugar (options):
  273. *
  274. * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
  275. * negative windowBits implicitly.
  276. * - `to` (String) - if equal to 'string', then result will be "binary string"
  277. * (each char code [0..255])
  278. *
  279. * ##### Example:
  280. *
  281. * ```javascript
  282. * var pako = require('pako')
  283. * , data = Uint8Array([1,2,3,4,5,6,7,8,9]);
  284. *
  285. * console.log(pako.deflate(data));
  286. * ```
  287. **/
  288. function deflate(input, options) {
  289. var deflator = new Deflate(options);
  290. deflator.push(input, true);
  291. // That will never happens, if you don't cheat with options :)
  292. if (deflator.err) { throw deflator.msg; }
  293. return deflator.result;
  294. }
  295. /**
  296. * deflateRaw(data[, options]) -> Uint8Array|Array|String
  297. * - data (Uint8Array|Array|String): input data to compress.
  298. * - options (Object): zlib deflate options.
  299. *
  300. * The same as [[deflate]], but creates raw data, without wrapper
  301. * (header and adler32 crc).
  302. **/
  303. function deflateRaw(input, options) {
  304. options = options || {};
  305. options.raw = true;
  306. return deflate(input, options);
  307. }
  308. /**
  309. * gzip(data[, options]) -> Uint8Array|Array|String
  310. * - data (Uint8Array|Array|String): input data to compress.
  311. * - options (Object): zlib deflate options.
  312. *
  313. * The same as [[deflate]], but create gzip wrapper instead of
  314. * deflate one.
  315. **/
  316. function gzip(input, options) {
  317. options = options || {};
  318. options.gzip = true;
  319. return deflate(input, options);
  320. }
  321. exports.Deflate = Deflate;
  322. exports.deflate = deflate;
  323. exports.deflateRaw = deflateRaw;
  324. exports.gzip = gzip;