@amritk/nish-x86_64-linux 0.23.0 → 0.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,747 @@
1
+ /**
2
+ * `nish/compress/gzip` — the gzip file format (RFC 1952) around
3
+ * `nish/compress/deflate`: a ten-byte header, the DEFLATE stream, and a
4
+ * trailer of the CRC-32 and the length of the data mod 2^32. What an HTTP
5
+ * `Content-Encoding: gzip` body is.
6
+ *
7
+ * import { DEFLATE_FAST, compressBound } from "nish/compress/deflate";
8
+ * import { GzipCompressor, GzipDecompressor } from "nish/compress/gzip";
9
+ *
10
+ * const gz = new GzipCompressor(DEFLATE_FAST); // once per thread
11
+ * const out: u8[] = new Array<u8>(compressBound(8192)); // once
12
+ * const end: i32 = gz.compress(body, 0, n, out, 0); // -1: no room
13
+ *
14
+ * const gunzip = new GzipDecompressor();
15
+ * const got: i32 = gunzip.decompress(src, 0, len, back, 0, 1 << 20); // < 0: an error
16
+ *
17
+ * **One shot.** `GzipCompressor` and `GzipDecompressor` own every table a
18
+ * call needs and allocate nothing per call; `gzipCompress` and
19
+ * `gzipDecompress` build one for a single call. The header written carries no
20
+ * name, no comment and a zero modification time, `XFL` 4 at level 1 and 2 at
21
+ * 9, and `OS` 255 ("unknown"), so the same input gives the same bytes on
22
+ * every machine.
23
+ *
24
+ * **Reading is strict.** A header with a method other than 8 or a reserved
25
+ * flag bit is `GZIP_ERR_HEADER`; `FEXTRA`, `FNAME`, `FCOMMENT` and `FHCRC` are
26
+ * read and skipped, the header CRC checked. A wrong CRC-32 is `GZIP_ERR_CRC`,
27
+ * a wrong length `GZIP_ERR_SIZE`. Members may follow one another (§2.2) and
28
+ * are decompressed one after another into the same output; anything after the
29
+ * last member that is not another member is `GZIP_ERR_HEADER`, so trailing
30
+ * garbage is refused, not ignored. The `limit` bounds the whole output.
31
+ *
32
+ * **Streaming.** `GzipWriter` compresses a body that arrives in pieces and
33
+ * `GzipReader` decompresses one: see each class.
34
+ *
35
+ * Written from RFC 1952.
36
+ */
37
+ import { Crc32 } from "nish/compress/checksum"
38
+ import {
39
+ DEFLATE_ERR_OUTPUT_LIMIT,
40
+ DEFLATE_ERR_TRUNCATED,
41
+ DEFLATE_NO_ROOM,
42
+ Deflater,
43
+ INFLATE_DONE,
44
+ INFLATE_NEED_INPUT,
45
+ INFLATE_NEED_OUTPUT,
46
+ Inflater,
47
+ deflateBound,
48
+ } from "nish/compress/deflate"
49
+ import { copyBytes } from "nish/bytes"
50
+
51
+ /** A gzip header that is not one: the wrong magic, a method other than DEFLATE, a reserved flag, or a bad header CRC. */
52
+ export const GZIP_ERR_HEADER: i32 = -9
53
+
54
+ /** The trailer's CRC-32 is not the CRC-32 of the data. */
55
+ export const GZIP_ERR_CRC: i32 = -10
56
+
57
+ /** The trailer's length is not the data's length mod 2^32. */
58
+ export const GZIP_ERR_SIZE: i32 = -11
59
+
60
+ /** `GzipReader`: no more output yet; give it input. */
61
+ export const GZIP_NEED_INPUT: i32 = 0
62
+
63
+ /** `GzipReader`: the stream has ended and every byte of it was read. */
64
+ export const GZIP_END: i32 = -100
65
+
66
+ /** The gzip header's and trailer's bytes. */
67
+ const GZIP_HEADER_SIZE: i32 = 10
68
+ const GZIP_TRAILER_SIZE: i32 = 8
69
+
70
+ /** The bytes a `GzipWriter` holds before it compresses them: one window of history and two of input. */
71
+ const GZIP_WRITER_HISTORY: i32 = 32768
72
+ const GZIP_WRITER_INPUT: i32 = 65536
73
+
74
+ /** The bytes of decoded output a `GzipReader` keeps: the 32 KB window and 32 KB to hand out. */
75
+ const GZIP_READER_WINDOW: i32 = 32768
76
+ const GZIP_READER_OUTPUT: i32 = 65536
77
+
78
+ /** The bytes a `GzipReader` buffers of input. */
79
+ const GZIP_READER_INPUT: i32 = 16384
80
+
81
+ /** Panics unless `buf[off .. off + len)` lies inside `buf`. */
82
+ const gzipWindow = (what: string, buf: u8[], off: i32, len: i32): void => {
83
+ if (off < 0 || len < 0 || off > toI32(buf.length) - len) {
84
+ panic(`${what}: the window [${off}, ${off} + ${len}) is outside a buffer of ${toI32(buf.length)} bytes`)
85
+ }
86
+ }
87
+
88
+ /** Writes `v` little-endian into `out[at .. at + 4)`; the caller has checked the room. */
89
+ const gzipPut32 = (out: u8[], at: i32, v: u32): void => {
90
+ out[at] = toU8(v & 255)
91
+ out[at + 1] = toU8((v >>> 8) & 255)
92
+ out[at + 2] = toU8((v >>> 16) & 255)
93
+ out[at + 3] = toU8(v >>> 24)
94
+ }
95
+
96
+ /** The little-endian word at `b[at .. at + 4)`; the caller has checked the range. */
97
+ const gzipGet32 = (b: u8[], at: i32): u32 =>
98
+ toU32(b[at]) | (toU32(b[at + 1]) << 8) | (toU32(b[at + 2]) << 16) | (toU32(b[at + 3]) << 24)
99
+
100
+ /** Writes the ten-byte header for `level` at `out[at]`; the caller has checked the room. */
101
+ const gzipWriteHeader = (out: u8[], at: i32, level: i32): void => {
102
+ out[at] = 0x1f
103
+ out[at + 1] = 0x8b
104
+ out[at + 2] = 8
105
+ out[at + 3] = 0
106
+ gzipPut32(out, at + 4, toU32(0))
107
+ // XFL (§2.3.1): 2 for the slowest level, 4 for the fastest.
108
+ let xfl: i32 = 0
109
+ if (level === 1) {
110
+ xfl = 4
111
+ } else if (level === 9) {
112
+ xfl = 2
113
+ }
114
+ out[at + 8] = toU8(xfl)
115
+ out[at + 9] = 255
116
+ }
117
+
118
+ /**
119
+ * Reads a member header at `src[p .. end)`: answers the index of the DEFLATE
120
+ * stream after it, `DEFLATE_ERR_TRUNCATED` when the input ends inside it,
121
+ * or `GZIP_ERR_HEADER`.
122
+ */
123
+ const gzipReadHeader = (crc: Crc32, src: u8[], p: i32, end: i32): i32 => {
124
+ if (end - p < GZIP_HEADER_SIZE) {
125
+ return end - p >= 2 && (src[p] !== 0x1f || src[p + 1] !== 0x8b) ? GZIP_ERR_HEADER : DEFLATE_ERR_TRUNCATED
126
+ }
127
+ if (src[p] !== 0x1f || src[p + 1] !== 0x8b || src[p + 2] !== 8) {
128
+ return GZIP_ERR_HEADER
129
+ }
130
+ const flags: i32 = toI32(src[p + 3])
131
+ if ((flags & 0xe0) !== 0) {
132
+ return GZIP_ERR_HEADER
133
+ }
134
+ let q: i32 = p + GZIP_HEADER_SIZE
135
+ if ((flags & 4) !== 0) {
136
+ if (end - q < 2) {
137
+ return DEFLATE_ERR_TRUNCATED
138
+ }
139
+ const xlen: i32 = toI32(src[q]) | (toI32(src[q + 1]) << 8)
140
+ if (end - q - 2 < xlen) {
141
+ return DEFLATE_ERR_TRUNCATED
142
+ }
143
+ q += 2 + xlen
144
+ }
145
+ for (let field: i32 = 8; field <= 16; field += 8) {
146
+ if ((flags & field) !== 0) {
147
+ while (q >= 0 && q < end && q < toI32(src.length) && src[q] !== 0) {
148
+ q += 1
149
+ }
150
+ if (q >= end) {
151
+ return DEFLATE_ERR_TRUNCATED
152
+ }
153
+ q += 1
154
+ }
155
+ }
156
+ if ((flags & 2) !== 0) {
157
+ if (end - q < 2) {
158
+ return DEFLATE_ERR_TRUNCATED
159
+ }
160
+ const want: i32 = toI32(src[q]) | (toI32(src[q + 1]) << 8)
161
+ if (toI32(crc.update(toU32(0), src, p, q - p) & 0xffff) !== want) {
162
+ return GZIP_ERR_HEADER
163
+ }
164
+ q += 2
165
+ }
166
+ return q
167
+ }
168
+
169
+ /**
170
+ * A gzip compressor at one level: a `Deflater` and the CRC-32 tables,
171
+ * allocated once and reused by every call. Not shared between threads.
172
+ */
173
+ export class GzipCompressor {
174
+ readonly deflater: Deflater
175
+ readonly crc: Crc32
176
+
177
+ /** A compressor at `level`, 0 to 9 (`DEFLATE_FAST`, `DEFLATE_DEFAULT`, ...); another level panics. */
178
+ constructor(level: i32) {
179
+ this.deflater = new Deflater(level)
180
+ this.crc = new Crc32()
181
+ }
182
+
183
+ /**
184
+ * Compresses `src[off .. off + len)` into `out` from `at` as one gzip
185
+ * member, and answers where it ends, or `DEFLATE_NO_ROOM` (-1) when `out`
186
+ * cannot hold it; `compressBound(len)` bytes always can. A window outside
187
+ * its array panics. Allocates nothing.
188
+ */
189
+ compress(src: u8[], off: i32, len: i32, out: u8[], at: i32): i32 {
190
+ gzipWindow("GzipCompressor.compress", src, off, len)
191
+ gzipWindow("GzipCompressor.compress", out, at, 0)
192
+ if (toI32(out.length) - at < GZIP_HEADER_SIZE + GZIP_TRAILER_SIZE) {
193
+ return DEFLATE_NO_ROOM
194
+ }
195
+ gzipWriteHeader(out, at, this.deflater.level)
196
+ const end: i32 = this.deflater.compress(src, off, len, out, at + GZIP_HEADER_SIZE)
197
+ if (end < 0 || toI32(out.length) - end < GZIP_TRAILER_SIZE) {
198
+ return DEFLATE_NO_ROOM
199
+ }
200
+ gzipPut32(out, end, this.crc.update(toU32(0), src, off, len))
201
+ gzipPut32(out, end + 4, toU32(len))
202
+ return end + GZIP_TRAILER_SIZE
203
+ }
204
+ }
205
+
206
+ /**
207
+ * A gzip decompressor: an `Inflater` and the CRC-32 tables, allocated once.
208
+ * Not shared between threads.
209
+ */
210
+ export class GzipDecompressor {
211
+ readonly inflater: Inflater
212
+ readonly crc: Crc32
213
+
214
+ /** Builds the tables. */
215
+ constructor() {
216
+ this.inflater = new Inflater()
217
+ this.crc = new Crc32()
218
+ }
219
+
220
+ /**
221
+ * Decompresses every member of `src[off .. off + len)` into `out` from
222
+ * `at`, writing at most `limit` bytes and never past `out`'s end, and
223
+ * answers where the output ends; or `DEFLATE_ERR_OUTPUT_LIMIT` when the
224
+ * data is longer, `GZIP_ERR_HEADER`, `GZIP_ERR_CRC`, `GZIP_ERR_SIZE`,
225
+ * `DEFLATE_ERR_TRUNCATED` or another `DEFLATE_ERR_*` for a malformed
226
+ * stream. An empty input is truncated. A window outside its array, or a
227
+ * negative `limit`, panics. Allocates nothing.
228
+ */
229
+ decompress(src: u8[], off: i32, len: i32, out: u8[], at: i32, limit: i32): i32 {
230
+ gzipWindow("GzipDecompressor.decompress", src, off, len)
231
+ gzipWindow("GzipDecompressor.decompress", out, at, 0)
232
+ if (limit < 0) {
233
+ panic("GzipDecompressor.decompress: a negative limit")
234
+ }
235
+ const room: i32 = toI32(out.length) - at
236
+ const outEnd: i32 = at + (limit < room ? limit : room)
237
+ const end: i32 = off + len
238
+ let p: i32 = off
239
+ let op: i32 = at
240
+ let first: boolean = true
241
+ while (first || p < end) {
242
+ first = false
243
+ const body: i32 = gzipReadHeader(this.crc, src, p, end)
244
+ if (body < 0) {
245
+ return body
246
+ }
247
+ const inf: Inflater = this.inflater
248
+ inf.reset()
249
+ const r: i32 = inf.inflate(src, body, end, false, out, op, outEnd, op)
250
+ if (r === INFLATE_NEED_OUTPUT) {
251
+ return DEFLATE_ERR_OUTPUT_LIMIT
252
+ }
253
+ if (r !== INFLATE_DONE) {
254
+ return r
255
+ }
256
+ const q: i32 = inf.inPos
257
+ if (end - q < GZIP_TRAILER_SIZE) {
258
+ return DEFLATE_ERR_TRUNCATED
259
+ }
260
+ const n: i32 = inf.outPos - op
261
+ if (gzipGet32(src, q) !== this.crc.update(toU32(0), out, op, n)) {
262
+ return GZIP_ERR_CRC
263
+ }
264
+ if (gzipGet32(src, q + 4) !== toU32(n)) {
265
+ return GZIP_ERR_SIZE
266
+ }
267
+ op = inf.outPos
268
+ p = q + GZIP_TRAILER_SIZE
269
+ }
270
+ return op
271
+ }
272
+ }
273
+
274
+ /**
275
+ * `GzipCompressor.compress` at `level`, building the compressor for this call
276
+ * alone (about 330 KB, allocated and filled): keep a `GzipCompressor` for
277
+ * anything repeated.
278
+ */
279
+ export const gzipCompress = (src: u8[], off: i32, len: i32, out: u8[], at: i32, level: i32): i32 =>
280
+ new GzipCompressor(level).compress(src, off, len, out, at)
281
+
282
+ /** `GzipDecompressor.decompress`, building the decompressor for this call alone. */
283
+ export const gzipDecompress = (src: u8[], off: i32, len: i32, out: u8[], at: i32, limit: i32): i32 =>
284
+ new GzipDecompressor().decompress(src, off, len, out, at, limit)
285
+
286
+ /**
287
+ * A streaming gzip compressor, for a body that does not fit in one buffer:
288
+ * `write` pieces, `flush` to make everything so far decodable, `finish` once.
289
+ * It buffers up to 64 KB of input (and 32 KB of history to match against),
290
+ * compressing whenever that fills, so output arrives in bursts. Every call
291
+ * writes into a caller window and answers where its output ends, or
292
+ * `DEFLATE_NO_ROOM` when the window is shorter than `bound(len)`, the most
293
+ * that call can produce; nothing is consumed then. About 430 KB, allocated
294
+ * once; `start` begins the next body.
295
+ */
296
+ export class GzipWriter {
297
+ readonly deflater: Deflater
298
+ readonly crc: Crc32
299
+ /** The history and the pending input. */
300
+ readonly window: u8[]
301
+ /** Where the pending input starts in `window`, and where it ends. */
302
+ pending: i32
303
+ fill: i32
304
+ /** The CRC-32 and length of every byte written so far. */
305
+ checksum: u32
306
+ size: u32
307
+ /** Whether the header has been written, and whether `finish` has. */
308
+ started: boolean
309
+ finished: boolean
310
+
311
+ /** A writer at `level`, 0 to 9; another level panics. */
312
+ constructor(level: i32) {
313
+ this.deflater = new Deflater(level)
314
+ this.crc = new Crc32()
315
+ this.window = new Array<u8>(GZIP_WRITER_HISTORY + GZIP_WRITER_INPUT)
316
+ this.pending = 0
317
+ this.fill = 0
318
+ this.checksum = 0
319
+ this.size = 0
320
+ this.started = false
321
+ this.finished = false
322
+ this.start()
323
+ }
324
+
325
+ /** Begins a new body: the next output starts with a header. */
326
+ start(): void {
327
+ // Push every position the tables hold a window behind the new body, so
328
+ // that no match reaches into the last one.
329
+ this.deflater.finish(this.fill)
330
+ this.deflater.reset()
331
+ this.deflater.begin(toI32(0))
332
+ this.pending = 0
333
+ this.fill = 0
334
+ this.checksum = 0
335
+ this.size = 0
336
+ this.started = false
337
+ this.finished = false
338
+ }
339
+
340
+ /** The most bytes a `write` of `len` bytes, or a `flush` or `finish` after it, can produce. */
341
+ bound(len: i32): i32 {
342
+ return deflateBound(GZIP_WRITER_INPUT + len) + GZIP_HEADER_SIZE + GZIP_TRAILER_SIZE + 10
343
+ }
344
+
345
+ /** Writes the header into `out` at `at` if it has not been; answers the new position. */
346
+ header(out: u8[], at: i32): i32 {
347
+ if (this.started) {
348
+ return at
349
+ }
350
+ gzipWriteHeader(out, at, this.deflater.level)
351
+ this.started = true
352
+ return at + GZIP_HEADER_SIZE
353
+ }
354
+
355
+ /**
356
+ * Compresses the pending input as non-final blocks into `out[at ..)`,
357
+ * then slides the window so that the last 32 KB stay as history. Answers
358
+ * the new position, or -1.
359
+ */
360
+ drain(out: u8[], at: i32, final: boolean): i32 {
361
+ const d: Deflater = this.deflater
362
+ const end: i32 = d.compressRange(
363
+ this.window,
364
+ this.pending,
365
+ this.fill,
366
+ toI32(0),
367
+ final,
368
+ out,
369
+ at,
370
+ toI32(out.length)
371
+ )
372
+ if (end < 0) {
373
+ return -1
374
+ }
375
+ this.pending = this.fill
376
+ if (this.fill > GZIP_WRITER_HISTORY) {
377
+ const shift: i32 = this.fill - GZIP_WRITER_HISTORY
378
+ copyBytes(this.window, 0, this.window, shift, GZIP_WRITER_HISTORY)
379
+ this.fill = GZIP_WRITER_HISTORY
380
+ this.pending = GZIP_WRITER_HISTORY
381
+ d.slide(shift)
382
+ }
383
+ return end
384
+ }
385
+
386
+ /**
387
+ * Takes `src[off .. off + len)` and answers where the output it produced
388
+ * in `out` from `at` ends (often `at`: input waits until 64 KB are
389
+ * pending), or `DEFLATE_NO_ROOM` when `out` has less than `bound(len)`
390
+ * bytes from `at`. Writing after `finish` panics, as does a window outside
391
+ * its array.
392
+ */
393
+ write(src: u8[], off: i32, len: i32, out: u8[], at: i32): i32 {
394
+ gzipWindow("GzipWriter.write", src, off, len)
395
+ gzipWindow("GzipWriter.write", out, at, 0)
396
+ if (this.finished) {
397
+ panic("GzipWriter.write: the body has been finished")
398
+ }
399
+ if (toI32(out.length) - at < this.bound(len)) {
400
+ return DEFLATE_NO_ROOM
401
+ }
402
+ let o: i32 = this.header(out, at)
403
+ this.checksum = this.crc.update(this.checksum, src, off, len)
404
+ this.size = this.size + toU32(len)
405
+ let p: i32 = off
406
+ const end: i32 = off + len
407
+ const cap: i32 = toI32(this.window.length)
408
+ while (p < end) {
409
+ let take: i32 = cap - this.fill
410
+ if (take > end - p) {
411
+ take = end - p
412
+ }
413
+ copyBytes(this.window, this.fill, src, p, take)
414
+ this.fill += take
415
+ p += take
416
+ if (this.fill === cap) {
417
+ o = this.drain(out, o, false)
418
+ if (o < 0) {
419
+ panic("GzipWriter.write: a block larger than its bound")
420
+ }
421
+ }
422
+ }
423
+ return o
424
+ }
425
+
426
+ /**
427
+ * Compresses everything pending and ends with an empty stored block, so a
428
+ * reader can decode every byte written so far (zlib's `Z_SYNC_FLUSH`).
429
+ * Answers the new end of `out`, or `DEFLATE_NO_ROOM` below `bound(0)`.
430
+ */
431
+ flush(out: u8[], at: i32): i32 {
432
+ gzipWindow("GzipWriter.flush", out, at, 0)
433
+ if (this.finished) {
434
+ panic("GzipWriter.flush: the body has been finished")
435
+ }
436
+ if (toI32(out.length) - at < this.bound(toI32(0))) {
437
+ return DEFLATE_NO_ROOM
438
+ }
439
+ let o: i32 = this.header(out, at)
440
+ if (this.fill > this.pending) {
441
+ o = this.drain(out, o, false)
442
+ }
443
+ o = this.deflater.writeStored(this.window, toI32(0), toI32(0), false, out, o, toI32(out.length))
444
+ return o
445
+ }
446
+
447
+ /**
448
+ * Compresses everything pending as the final block and writes the
449
+ * trailer; answers the new end of `out`, or `DEFLATE_NO_ROOM` below
450
+ * `bound(0)`. `start` begins another body.
451
+ */
452
+ finish(out: u8[], at: i32): i32 {
453
+ gzipWindow("GzipWriter.finish", out, at, 0)
454
+ if (this.finished) {
455
+ panic("GzipWriter.finish: the body has been finished")
456
+ }
457
+ if (toI32(out.length) - at < this.bound(toI32(0))) {
458
+ return DEFLATE_NO_ROOM
459
+ }
460
+ let o: i32 = this.header(out, at)
461
+ o = this.drain(out, o, true)
462
+ o = this.deflater.flushBits(out, o, toI32(out.length))
463
+ if (o < 0 || toI32(out.length) - o < GZIP_TRAILER_SIZE) {
464
+ panic("GzipWriter.finish: a block larger than its bound")
465
+ }
466
+ gzipPut32(out, o, this.checksum)
467
+ gzipPut32(out, o + 4, this.size)
468
+ this.finished = true
469
+ return o + GZIP_TRAILER_SIZE
470
+ }
471
+ }
472
+
473
+ /**
474
+ * A streaming gzip decompressor, for a body that arrives in pieces or does
475
+ * not fit in memory. Sans-IO in the `nish/net/postgres` shape: copy input
476
+ * into `inputBuffer()` at `inputEnd()` and `commit` it (or `feed` a window),
477
+ * then call `read` until it answers `GZIP_NEED_INPUT`; at the end of the
478
+ * input, `endInput()`. `read(out, at, max)` copies up to `max` decoded bytes
479
+ * and answers how many, `GZIP_NEED_INPUT`, `GZIP_END` once the stream and its
480
+ * trailer are through, or a negative error, final. A total of more than
481
+ * `limit` decoded bytes is `DEFLATE_ERR_OUTPUT_LIMIT`. Concatenated members
482
+ * are read as one body, as by `GzipDecompressor`. About 100 KB, allocated
483
+ * once; `start` begins the next body.
484
+ */
485
+ export class GzipReader {
486
+ readonly inflater: Inflater
487
+ readonly crc: Crc32
488
+ /** Input not yet decoded. */
489
+ readonly input: u8[]
490
+ /** The decoded window: history from `windowStart`, unread output from `readPos` to `writePos`. */
491
+ readonly output: u8[]
492
+ inStart: i32
493
+ inEnd: i32
494
+ readPos: i32
495
+ writePos: i32
496
+ /** Where the current member's output began, for its distances. */
497
+ histStart: i32
498
+ /** Where the bytes not yet checksummed start. */
499
+ crcPos: i32
500
+ /** 0: a member header next; 1: inside a member's stream; 2: its trailer; 3: done; < 0: failed. */
501
+ phase: i32
502
+ /** Whether more input may come. */
503
+ more: boolean
504
+ /** The member's running CRC-32 and length, and the body's total length. */
505
+ checksum: u32
506
+ size: u32
507
+ total: i64
508
+ /** The most decoded bytes `read` hands out in all. */
509
+ readonly limit: i64
510
+ /** Whether a member has been read; another may follow. */
511
+ members: i32
512
+
513
+ /** A reader that refuses more than `limit` decoded bytes in all; a negative `limit` panics. */
514
+ constructor(limit: i64) {
515
+ if (limit < 0) {
516
+ panic("GzipReader: a negative limit")
517
+ }
518
+ this.inflater = new Inflater()
519
+ this.crc = new Crc32()
520
+ this.input = new Array<u8>(GZIP_READER_INPUT)
521
+ this.output = new Array<u8>(GZIP_READER_WINDOW + GZIP_READER_OUTPUT)
522
+ this.limit = limit
523
+ this.inStart = 0
524
+ this.inEnd = 0
525
+ this.readPos = 0
526
+ this.writePos = 0
527
+ this.histStart = 0
528
+ this.crcPos = 0
529
+ this.phase = 0
530
+ this.more = true
531
+ this.checksum = 0
532
+ this.size = 0
533
+ this.total = 0
534
+ this.members = 0
535
+ }
536
+
537
+ /** Begins a new body, dropping any input and output left over. */
538
+ start(): void {
539
+ this.inStart = 0
540
+ this.inEnd = 0
541
+ this.readPos = 0
542
+ this.writePos = 0
543
+ this.histStart = 0
544
+ this.crcPos = 0
545
+ this.phase = 0
546
+ this.more = true
547
+ this.checksum = 0
548
+ this.size = 0
549
+ this.total = 0
550
+ this.members = 0
551
+ }
552
+
553
+ /** The buffer to copy input into, from `inputEnd()` for at most `inputRoom()` bytes. */
554
+ inputBuffer(): u8[] {
555
+ this.compact()
556
+ return this.input
557
+ }
558
+
559
+ /** Where new input goes in `inputBuffer()`. */
560
+ inputEnd(): i32 {
561
+ return this.inEnd
562
+ }
563
+
564
+ /** How many bytes `inputBuffer()` has room for. */
565
+ inputRoom(): i32 {
566
+ this.compact()
567
+ return toI32(this.input.length) - this.inEnd
568
+ }
569
+
570
+ /** Takes `n` bytes copied into `inputBuffer()` at `inputEnd()`; more than `inputRoom()` panics. */
571
+ commit(n: i32): void {
572
+ if (n < 0 || n > toI32(this.input.length) - this.inEnd) {
573
+ panic("GzipReader.commit: more bytes than the buffer has room for")
574
+ }
575
+ this.inEnd += n
576
+ }
577
+
578
+ /** Copies as much of `src[off .. off + len)` as fits and answers how much. */
579
+ feed(src: u8[], off: i32, len: i32): i32 {
580
+ gzipWindow("GzipReader.feed", src, off, len)
581
+ const room: i32 = this.inputRoom()
582
+ const take: i32 = len < room ? len : room
583
+ copyBytes(this.input, this.inEnd, src, off, take)
584
+ this.inEnd += take
585
+ return take
586
+ }
587
+
588
+ /** Says that no input follows what has been given. */
589
+ endInput(): void {
590
+ this.more = false
591
+ }
592
+
593
+ /** Moves the undecoded input to the front of its buffer. */
594
+ compact(): void {
595
+ if (this.inStart > 0) {
596
+ const n: i32 = this.inEnd - this.inStart
597
+ copyBytes(this.input, 0, this.input, this.inStart, n)
598
+ this.inStart = 0
599
+ this.inEnd = n
600
+ }
601
+ }
602
+
603
+ /** Adds the decoded bytes not yet checksummed to the member's CRC-32 and length. */
604
+ sum(): void {
605
+ const n: i32 = this.writePos - this.crcPos
606
+ if (n > 0) {
607
+ this.checksum = this.crc.update(this.checksum, this.output, this.crcPos, n)
608
+ this.size = this.size + toU32(n)
609
+ this.crcPos = this.writePos
610
+ }
611
+ }
612
+
613
+ /** Slides the decoded window down once the unread output has been read and the buffer is past half. */
614
+ slide(): void {
615
+ if (this.readPos === this.writePos && this.writePos > GZIP_READER_WINDOW) {
616
+ this.sum()
617
+ const keep: i32 = GZIP_READER_WINDOW
618
+ const shift: i32 = this.writePos - keep
619
+ copyBytes(this.output, 0, this.output, shift, keep)
620
+ this.readPos -= shift
621
+ this.writePos -= shift
622
+ this.crcPos -= shift
623
+ this.histStart = this.histStart > shift ? this.histStart - shift : 0
624
+ }
625
+ }
626
+
627
+ /** Decodes as far as the input and the output buffer allow; answers 0 or a final code. */
628
+ advance(): i32 {
629
+ while (true) {
630
+ const avail: i32 = this.inEnd - this.inStart
631
+ if (this.phase === 0) {
632
+ if (avail === 0 && !this.more) {
633
+ if (this.members === 0) {
634
+ return DEFLATE_ERR_TRUNCATED
635
+ }
636
+ this.phase = 3
637
+ return 0
638
+ }
639
+ const body: i32 = gzipReadHeader(this.crc, this.input, this.inStart, this.inEnd)
640
+ if (body === DEFLATE_ERR_TRUNCATED) {
641
+ if (this.more) {
642
+ if (this.inputRoomRaw() === 0 && this.inStart === 0) {
643
+ return GZIP_ERR_HEADER
644
+ }
645
+ return 0
646
+ }
647
+ return DEFLATE_ERR_TRUNCATED
648
+ }
649
+ if (body < 0) {
650
+ return body
651
+ }
652
+ this.inStart = body
653
+ this.inflater.reset()
654
+ this.histStart = this.writePos
655
+ this.checksum = 0
656
+ this.size = 0
657
+ this.phase = 1
658
+ } else if (this.phase === 1) {
659
+ this.slide()
660
+ const outEnd: i32 = toI32(this.output.length)
661
+ if (this.writePos === outEnd) {
662
+ return 0
663
+ }
664
+ const inf: Inflater = this.inflater
665
+ const r: i32 = inf.inflate(
666
+ this.input,
667
+ this.inStart,
668
+ this.inEnd,
669
+ this.more,
670
+ this.output,
671
+ this.writePos,
672
+ outEnd,
673
+ this.histStart
674
+ )
675
+ this.inStart = inf.inPos
676
+ const made: i32 = inf.outPos - this.writePos
677
+ this.writePos = inf.outPos
678
+ this.total = this.total + toI64(made)
679
+ if (this.total > this.limit) {
680
+ return DEFLATE_ERR_OUTPUT_LIMIT
681
+ }
682
+ if (r === INFLATE_DONE) {
683
+ this.sum()
684
+ this.phase = 2
685
+ } else if (r === INFLATE_NEED_INPUT || r === INFLATE_NEED_OUTPUT) {
686
+ return 0
687
+ } else {
688
+ return r
689
+ }
690
+ } else if (this.phase === 2) {
691
+ if (avail < GZIP_TRAILER_SIZE) {
692
+ return this.more ? 0 : DEFLATE_ERR_TRUNCATED
693
+ }
694
+ if (gzipGet32(this.input, this.inStart) !== this.checksum) {
695
+ return GZIP_ERR_CRC
696
+ }
697
+ if (gzipGet32(this.input, this.inStart + 4) !== this.size) {
698
+ return GZIP_ERR_SIZE
699
+ }
700
+ this.inStart += GZIP_TRAILER_SIZE
701
+ this.members += 1
702
+ this.phase = 0
703
+ } else {
704
+ return 0
705
+ }
706
+ }
707
+ }
708
+
709
+ /** The room `input` has without compacting. */
710
+ inputRoomRaw(): i32 {
711
+ return toI32(this.input.length) - this.inEnd
712
+ }
713
+
714
+ /**
715
+ * Copies up to `max` decoded bytes into `out` from `at` and answers how
716
+ * many (at least 1; `max` is at least 1), or `GZIP_NEED_INPUT` when it needs more input first,
717
+ * `GZIP_END` once the body has ended, or a negative error, after which it
718
+ * answers that error again until `start`. A window outside `out` panics.
719
+ */
720
+ read(out: u8[], at: i32, max: i32): i32 {
721
+ gzipWindow("GzipReader.read", out, at, max)
722
+ if (max < 1) {
723
+ panic("GzipReader.read: room for no bytes")
724
+ }
725
+ if (this.phase < 0) {
726
+ return this.phase
727
+ }
728
+ if (this.readPos === this.writePos) {
729
+ const r: i32 = this.advance()
730
+ if (r < 0) {
731
+ this.phase = r
732
+ return r
733
+ }
734
+ }
735
+ const n0: i32 = this.writePos - this.readPos
736
+ if (n0 === 0) {
737
+ if (this.phase === 3) {
738
+ return GZIP_END
739
+ }
740
+ return GZIP_NEED_INPUT
741
+ }
742
+ const n: i32 = n0 < max ? n0 : max
743
+ copyBytes(out, at, this.output, this.readPos, n)
744
+ this.readPos += n
745
+ return n
746
+ }
747
+ }