~holger-seelig/cobweb.js/trunk

« back to all changes in this revision

Viewing changes to cobweb.js/lib/pako/lib/inflate.js

  • Committer: Holger Seelig
  • Date: 2017-08-22 04:53:24 UTC
  • Revision ID: holger.seelig@yahoo.de-20170822045324-4of4xxgt79669gbt
Switched to npm.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
'use strict';
2
 
 
3
 
 
4
 
var zlib_inflate = require('./zlib/inflate.js');
5
 
var utils = require('./utils/common');
6
 
var strings = require('./utils/strings');
7
 
var c = require('./zlib/constants');
8
 
var msg = require('./zlib/messages');
9
 
var zstream = require('./zlib/zstream');
10
 
var gzheader = require('./zlib/gzheader');
11
 
 
12
 
var toString = Object.prototype.toString;
13
 
 
14
 
/**
15
 
 * class Inflate
16
 
 *
17
 
 * Generic JS-style wrapper for zlib calls. If you don't need
18
 
 * streaming behaviour - use more simple functions: [[inflate]]
19
 
 * and [[inflateRaw]].
20
 
 **/
21
 
 
22
 
/* internal
23
 
 * inflate.chunks -> Array
24
 
 *
25
 
 * Chunks of output data, if [[Inflate#onData]] not overriden.
26
 
 **/
27
 
 
28
 
/**
29
 
 * Inflate.result -> Uint8Array|Array|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) or if you
34
 
 * push a chunk with explicit flush (call [[Inflate#push]] with
35
 
 * `Z_SYNC_FLUSH` param).
36
 
 **/
37
 
 
38
 
/**
39
 
 * Inflate.err -> Number
40
 
 *
41
 
 * Error code after inflate finished. 0 (Z_OK) on success.
42
 
 * Should be checked if broken data possible.
43
 
 **/
44
 
 
45
 
/**
46
 
 * Inflate.msg -> String
47
 
 *
48
 
 * Error message, if [[Inflate.err]] != 0
49
 
 **/
50
 
 
51
 
 
52
 
/**
53
 
 * new Inflate(options)
54
 
 * - options (Object): zlib inflate options.
55
 
 *
56
 
 * Creates new inflator instance with specified params. Throws exception
57
 
 * on bad params. Supported options:
58
 
 *
59
 
 * - `windowBits`
60
 
 *
61
 
 * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
62
 
 * for more information on these.
63
 
 *
64
 
 * Additional options, for internal needs:
65
 
 *
66
 
 * - `chunkSize` - size of generated data chunks (16K by default)
67
 
 * - `raw` (Boolean) - do raw inflate
68
 
 * - `to` (String) - if equal to 'string', then result will be converted
69
 
 *   from utf8 to utf16 (javascript) string. When string output requested,
70
 
 *   chunk length can differ from `chunkSize`, depending on content.
71
 
 *
72
 
 * By default, when no options set, autodetect deflate/gzip data format via
73
 
 * wrapper header.
74
 
 *
75
 
 * ##### Example:
76
 
 *
77
 
 * ```javascript
78
 
 * var pako = require('pako')
79
 
 *   , chunk1 = Uint8Array([1,2,3,4,5,6,7,8,9])
80
 
 *   , chunk2 = Uint8Array([10,11,12,13,14,15,16,17,18,19]);
81
 
 *
82
 
 * var inflate = new pako.Inflate({ level: 3});
83
 
 *
84
 
 * inflate.push(chunk1, false);
85
 
 * inflate.push(chunk2, true);  // true -> last chunk
86
 
 *
87
 
 * if (inflate.err) { throw new Error(inflate.err); }
88
 
 *
89
 
 * console.log(inflate.result);
90
 
 * ```
91
 
 **/
92
 
var Inflate = function(options) {
93
 
 
94
 
  this.options = utils.assign({
95
 
    chunkSize: 16384,
96
 
    windowBits: 0,
97
 
    to: ''
98
 
  }, options || {});
99
 
 
100
 
  var opt = this.options;
101
 
 
102
 
  // Force window size for `raw` data, if not set directly,
103
 
  // because we have no header for autodetect.
104
 
  if (opt.raw && (opt.windowBits >= 0) && (opt.windowBits < 16)) {
105
 
    opt.windowBits = -opt.windowBits;
106
 
    if (opt.windowBits === 0) { opt.windowBits = -15; }
107
 
  }
108
 
 
109
 
  // If `windowBits` not defined (and mode not raw) - set autodetect flag for gzip/deflate
110
 
  if ((opt.windowBits >= 0) && (opt.windowBits < 16) &&
111
 
      !(options && options.windowBits)) {
112
 
    opt.windowBits += 32;
113
 
  }
114
 
 
115
 
  // Gzip header has no info about windows size, we can do autodetect only
116
 
  // for deflate. So, if window size not set, force it to max when gzip possible
117
 
  if ((opt.windowBits > 15) && (opt.windowBits < 48)) {
118
 
    // bit 3 (16) -> gzipped data
119
 
    // bit 4 (32) -> autodetect gzip/deflate
120
 
    if ((opt.windowBits & 15) === 0) {
121
 
      opt.windowBits |= 15;
122
 
    }
123
 
  }
124
 
 
125
 
  this.err    = 0;      // error code, if happens (0 = Z_OK)
126
 
  this.msg    = '';     // error message
127
 
  this.ended  = false;  // used to avoid multiple onEnd() calls
128
 
  this.chunks = [];     // chunks of compressed data
129
 
 
130
 
  this.strm   = new zstream();
131
 
  this.strm.avail_out = 0;
132
 
 
133
 
  var status  = zlib_inflate.inflateInit2(
134
 
    this.strm,
135
 
    opt.windowBits
136
 
  );
137
 
 
138
 
  if (status !== c.Z_OK) {
139
 
    throw new Error(msg[status]);
140
 
  }
141
 
 
142
 
  this.header = new gzheader();
143
 
 
144
 
  zlib_inflate.inflateGetHeader(this.strm, this.header);
145
 
};
146
 
 
147
 
/**
148
 
 * Inflate#push(data[, mode]) -> Boolean
149
 
 * - data (Uint8Array|Array|ArrayBuffer|String): input data
150
 
 * - mode (Number|Boolean): 0..6 for corresponding Z_NO_FLUSH..Z_TREE modes.
151
 
 *   See constants. Skipped or `false` means Z_NO_FLUSH, `true` meansh Z_FINISH.
152
 
 *
153
 
 * Sends input data to inflate pipe, generating [[Inflate#onData]] calls with
154
 
 * new output chunks. Returns `true` on success. The last data block must have
155
 
 * mode Z_FINISH (or `true`). That will flush internal pending buffers and call
156
 
 * [[Inflate#onEnd]]. For interim explicit flushes (without ending the stream) you
157
 
 * can use mode Z_SYNC_FLUSH, keeping the decompression context.
158
 
 *
159
 
 * On fail call [[Inflate#onEnd]] with error code and return false.
160
 
 *
161
 
 * We strongly recommend to use `Uint8Array` on input for best speed (output
162
 
 * format is detected automatically). Also, don't skip last param and always
163
 
 * use the same type in your code (boolean or number). That will improve JS speed.
164
 
 *
165
 
 * For regular `Array`-s make sure all elements are [0..255].
166
 
 *
167
 
 * ##### Example
168
 
 *
169
 
 * ```javascript
170
 
 * push(chunk, false); // push one of data chunks
171
 
 * ...
172
 
 * push(chunk, true);  // push last chunk
173
 
 * ```
174
 
 **/
175
 
Inflate.prototype.push = function(data, mode) {
176
 
  var strm = this.strm;
177
 
  var chunkSize = this.options.chunkSize;
178
 
  var status, _mode;
179
 
  var next_out_utf8, tail, utf8str;
180
 
 
181
 
  // Flag to properly process Z_BUF_ERROR on testing inflate call
182
 
  // when we check that all output data was flushed.
183
 
  var allowBufError = false;
184
 
 
185
 
  if (this.ended) { return false; }
186
 
  _mode = (mode === ~~mode) ? mode : ((mode === true) ? c.Z_FINISH : c.Z_NO_FLUSH);
187
 
 
188
 
  // Convert data if needed
189
 
  if (typeof data === 'string') {
190
 
    // Only binary strings can be decompressed on practice
191
 
    strm.input = strings.binstring2buf(data);
192
 
  } else if (toString.call(data) === '[object ArrayBuffer]') {
193
 
    strm.input = new Uint8Array(data);
194
 
  } else {
195
 
    strm.input = data;
196
 
  }
197
 
 
198
 
  strm.next_in = 0;
199
 
  strm.avail_in = strm.input.length;
200
 
 
201
 
  do {
202
 
    if (strm.avail_out === 0) {
203
 
      strm.output = new utils.Buf8(chunkSize);
204
 
      strm.next_out = 0;
205
 
      strm.avail_out = chunkSize;
206
 
    }
207
 
 
208
 
    status = zlib_inflate.inflate(strm, c.Z_NO_FLUSH);    /* no bad return value */
209
 
 
210
 
    if (status === c.Z_BUF_ERROR && allowBufError === true) {
211
 
      status = c.Z_OK;
212
 
      allowBufError = false;
213
 
    }
214
 
 
215
 
    if (status !== c.Z_STREAM_END && status !== c.Z_OK) {
216
 
      this.onEnd(status);
217
 
      this.ended = true;
218
 
      return false;
219
 
    }
220
 
 
221
 
    if (strm.next_out) {
222
 
      if (strm.avail_out === 0 || status === c.Z_STREAM_END || (strm.avail_in === 0 && (_mode === c.Z_FINISH || _mode === c.Z_SYNC_FLUSH))) {
223
 
 
224
 
        if (this.options.to === 'string') {
225
 
 
226
 
          next_out_utf8 = strings.utf8border(strm.output, strm.next_out);
227
 
 
228
 
          tail = strm.next_out - next_out_utf8;
229
 
          utf8str = strings.buf2string(strm.output, next_out_utf8);
230
 
 
231
 
          // move tail
232
 
          strm.next_out = tail;
233
 
          strm.avail_out = chunkSize - tail;
234
 
          if (tail) { utils.arraySet(strm.output, strm.output, next_out_utf8, tail, 0); }
235
 
 
236
 
          this.onData(utf8str);
237
 
 
238
 
        } else {
239
 
          this.onData(utils.shrinkBuf(strm.output, strm.next_out));
240
 
        }
241
 
      }
242
 
    }
243
 
 
244
 
    // When no more input data, we should check that internal inflate buffers
245
 
    // are flushed. The only way to do it when avail_out = 0 - run one more
246
 
    // inflate pass. But if output data not exists, inflate return Z_BUF_ERROR.
247
 
    // Here we set flag to process this error properly.
248
 
    //
249
 
    // NOTE. Deflate does not return error in this case and does not needs such
250
 
    // logic.
251
 
    if (strm.avail_in === 0 && strm.avail_out === 0) {
252
 
      allowBufError = true;
253
 
    }
254
 
 
255
 
  } while ((strm.avail_in > 0 || strm.avail_out === 0) && status !== c.Z_STREAM_END);
256
 
 
257
 
  if (status === c.Z_STREAM_END) {
258
 
    _mode = c.Z_FINISH;
259
 
  }
260
 
 
261
 
  // Finalize on the last chunk.
262
 
  if (_mode === c.Z_FINISH) {
263
 
    status = zlib_inflate.inflateEnd(this.strm);
264
 
    this.onEnd(status);
265
 
    this.ended = true;
266
 
    return status === c.Z_OK;
267
 
  }
268
 
 
269
 
  // callback interim results if Z_SYNC_FLUSH.
270
 
  if (_mode === c.Z_SYNC_FLUSH) {
271
 
    this.onEnd(c.Z_OK);
272
 
    strm.avail_out = 0;
273
 
    return true;
274
 
  }
275
 
 
276
 
  return true;
277
 
};
278
 
 
279
 
 
280
 
/**
281
 
 * Inflate#onData(chunk) -> Void
282
 
 * - chunk (Uint8Array|Array|String): ouput data. Type of array depends
283
 
 *   on js engine support. When string output requested, each chunk
284
 
 *   will be string.
285
 
 *
286
 
 * By default, stores data blocks in `chunks[]` property and glue
287
 
 * those in `onEnd`. Override this handler, if you need another behaviour.
288
 
 **/
289
 
Inflate.prototype.onData = function(chunk) {
290
 
  this.chunks.push(chunk);
291
 
};
292
 
 
293
 
 
294
 
/**
295
 
 * Inflate#onEnd(status) -> Void
296
 
 * - status (Number): inflate status. 0 (Z_OK) on success,
297
 
 *   other if not.
298
 
 *
299
 
 * Called either after you tell inflate that the input stream is
300
 
 * complete (Z_FINISH) or should be flushed (Z_SYNC_FLUSH)
301
 
 * or if an error happened. By default - join collected chunks,
302
 
 * free memory and fill `results` / `err` properties.
303
 
 **/
304
 
Inflate.prototype.onEnd = function(status) {
305
 
  // On success - join
306
 
  if (status === c.Z_OK) {
307
 
    if (this.options.to === 'string') {
308
 
      // Glue & convert here, until we teach pako to send
309
 
      // utf8 alligned strings to onData
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
 
 * inflate(data[, options]) -> Uint8Array|Array|String
323
 
 * - data (Uint8Array|Array|String): input data to decompress.
324
 
 * - options (Object): zlib inflate options.
325
 
 *
326
 
 * Decompress `data` with inflate/ungzip and `options`. Autodetect
327
 
 * format via wrapper header by default. That's why we don't provide
328
 
 * separate `ungzip` method.
329
 
 *
330
 
 * Supported options are:
331
 
 *
332
 
 * - windowBits
333
 
 *
334
 
 * [http://zlib.net/manual.html#Advanced](http://zlib.net/manual.html#Advanced)
335
 
 * for more information.
336
 
 *
337
 
 * Sugar (options):
338
 
 *
339
 
 * - `raw` (Boolean) - say that we work with raw stream, if you don't wish to specify
340
 
 *   negative windowBits implicitly.
341
 
 * - `to` (String) - if equal to 'string', then result will be converted
342
 
 *   from utf8 to utf16 (javascript) string. When string output requested,
343
 
 *   chunk length can differ from `chunkSize`, depending on content.
344
 
 *
345
 
 *
346
 
 * ##### Example:
347
 
 *
348
 
 * ```javascript
349
 
 * var pako = require('pako')
350
 
 *   , input = pako.deflate([1,2,3,4,5,6,7,8,9])
351
 
 *   , output;
352
 
 *
353
 
 * try {
354
 
 *   output = pako.inflate(input);
355
 
 * } catch (err)
356
 
 *   console.log(err);
357
 
 * }
358
 
 * ```
359
 
 **/
360
 
function inflate(input, options) {
361
 
  var inflator = new Inflate(options);
362
 
 
363
 
  inflator.push(input, true);
364
 
 
365
 
  // That will never happens, if you don't cheat with options :)
366
 
  if (inflator.err) { throw inflator.msg; }
367
 
 
368
 
  return inflator.result;
369
 
}
370
 
 
371
 
 
372
 
/**
373
 
 * inflateRaw(data[, options]) -> Uint8Array|Array|String
374
 
 * - data (Uint8Array|Array|String): input data to decompress.
375
 
 * - options (Object): zlib inflate options.
376
 
 *
377
 
 * The same as [[inflate]], but creates raw data, without wrapper
378
 
 * (header and adler32 crc).
379
 
 **/
380
 
function inflateRaw(input, options) {
381
 
  options = options || {};
382
 
  options.raw = true;
383
 
  return inflate(input, options);
384
 
}
385
 
 
386
 
 
387
 
/**
388
 
 * ungzip(data[, options]) -> Uint8Array|Array|String
389
 
 * - data (Uint8Array|Array|String): input data to decompress.
390
 
 * - options (Object): zlib inflate options.
391
 
 *
392
 
 * Just shortcut to [[inflate]], because it autodetects format
393
 
 * by header.content. Done for convenience.
394
 
 **/
395
 
 
396
 
 
397
 
exports.Inflate = Inflate;
398
 
exports.inflate = inflate;
399
 
exports.inflateRaw = inflateRaw;
400
 
exports.ungzip  = inflate;