Projekt

Obecné

Profil

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