@amritk/nish-x86_64-linux 0.23.1 → 0.25.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.
Files changed (51) hide show
  1. package/bin/nish +0 -0
  2. package/docs/AI.md +50 -23
  3. package/package.json +1 -1
  4. package/runtime/browser.mjs +4 -3
  5. package/runtime/core/arrays.d.ts +0 -2
  6. package/runtime/core/arrays.mjs +0 -2
  7. package/runtime/core/host.d.ts +8 -0
  8. package/runtime/core/host.mjs +50 -2
  9. package/runtime/core/numbers.mjs +2 -3
  10. package/runtime/nish.d.ts +100 -4
  11. package/runtime/nish.h +34 -0
  12. package/runtime/nish.mjs +23 -5
  13. package/runtime/node.mjs +57 -6
  14. package/runtime/runtime-net.c +45 -4
  15. package/runtime/runtime-os.c +115 -12
  16. package/runtime/runtime-term.c +2 -2
  17. package/runtime/runtime.c +1 -1
  18. package/scripts/build.sh +13 -3
  19. package/std/README.md +44 -7
  20. package/std/cli-args.ts +1216 -0
  21. package/std/cli-flags.ts +585 -0
  22. package/std/cli.ts +308 -19
  23. package/std/compress/checksum.ts +290 -0
  24. package/std/compress/deflate.ts +2790 -0
  25. package/std/compress/gzip.ts +761 -0
  26. package/std/compress/zlib.ts +168 -0
  27. package/std/crypto/LICENSE-fiat-crypto-p384 +21 -0
  28. package/std/crypto/p256.ts +63 -1
  29. package/std/crypto/p384.ts +6044 -0
  30. package/std/crypto/pki-roots.ts +322 -0
  31. package/std/crypto/pki.ts +1207 -0
  32. package/std/crypto/rsa.ts +663 -0
  33. package/std/crypto/sha256.ts +2 -3
  34. package/std/crypto/x25519.ts +3 -4
  35. package/std/crypto/x509.ts +1054 -58
  36. package/std/net/dns-config.ts +734 -0
  37. package/std/net/dns-message.ts +517 -0
  38. package/std/net/dns-resolver.ts +1015 -0
  39. package/std/net/dns.ts +478 -0
  40. package/std/net/http-compress.ts +393 -0
  41. package/std/net/http1-client.ts +1227 -0
  42. package/std/net/http1-server.ts +101 -1
  43. package/std/net/http1.ts +9 -9
  44. package/std/net/tls/client.ts +1094 -0
  45. package/std/net/tls/codec.ts +585 -3
  46. package/std/net/tls/record-client.ts +1192 -0
  47. package/std/net/tls/verify.ts +221 -0
  48. package/std/net/tls-tcp.ts +642 -11
  49. package/std/net/tls.ts +2 -2
  50. package/std/net/url.ts +559 -4
  51. package/std/yaml-write.ts +415 -0
@@ -0,0 +1,761 @@
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: 32 KB of history, and up to 64 KB of input before it compresses them. */
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
+ /**
341
+ * The most bytes a `write` of `len` bytes, or a `flush` or `finish` after
342
+ * it, can produce. Less than 64 KB is pending before the call, and the
343
+ * call compresses once per 64 KB pending: `deflateBound` of all of it,
344
+ * with the constant part of `deflateBound` again for each compression
345
+ * after the first.
346
+ */
347
+ bound(len: i32): i32 {
348
+ const drains: i32 = (len >> 16) + 1
349
+ return deflateBound(GZIP_WRITER_INPUT + len) + drains * 18 + GZIP_HEADER_SIZE + GZIP_TRAILER_SIZE + 10
350
+ }
351
+
352
+ /** Writes the header into `out` at `at` if it has not been; answers the new position. */
353
+ header(out: u8[], at: i32): i32 {
354
+ if (this.started) {
355
+ return at
356
+ }
357
+ gzipWriteHeader(out, at, this.deflater.level)
358
+ this.started = true
359
+ return at + GZIP_HEADER_SIZE
360
+ }
361
+
362
+ /**
363
+ * Compresses the pending input as non-final blocks into `out[at ..)`,
364
+ * then slides the window so that the last 32 KB stay as history. Answers
365
+ * the new position, or -1.
366
+ */
367
+ drain(out: u8[], at: i32, final: boolean): i32 {
368
+ const d: Deflater = this.deflater
369
+ const end: i32 = d.compressRange(
370
+ this.window,
371
+ this.pending,
372
+ this.fill,
373
+ toI32(0),
374
+ final,
375
+ out,
376
+ at,
377
+ toI32(out.length)
378
+ )
379
+ if (end < 0) {
380
+ return -1
381
+ }
382
+ this.pending = this.fill
383
+ if (this.fill > GZIP_WRITER_HISTORY) {
384
+ const shift: i32 = this.fill - GZIP_WRITER_HISTORY
385
+ copyBytes(this.window, 0, this.window, shift, GZIP_WRITER_HISTORY)
386
+ this.fill = GZIP_WRITER_HISTORY
387
+ this.pending = GZIP_WRITER_HISTORY
388
+ d.slide(shift)
389
+ }
390
+ return end
391
+ }
392
+
393
+ /**
394
+ * Takes `src[off .. off + len)` and answers where the output it produced
395
+ * in `out` from `at` ends (often `at`: input waits until 64 KB are
396
+ * pending), or `DEFLATE_NO_ROOM` when `out` has less than `bound(len)`
397
+ * bytes from `at`. Writing after `finish` panics, as does a window outside
398
+ * its array.
399
+ */
400
+ write(src: u8[], off: i32, len: i32, out: u8[], at: i32): i32 {
401
+ gzipWindow("GzipWriter.write", src, off, len)
402
+ gzipWindow("GzipWriter.write", out, at, 0)
403
+ if (this.finished) {
404
+ panic("GzipWriter.write: the body has been finished")
405
+ }
406
+ if (toI32(out.length) - at < this.bound(len)) {
407
+ return DEFLATE_NO_ROOM
408
+ }
409
+ let o: i32 = this.header(out, at)
410
+ this.checksum = this.crc.update(this.checksum, src, off, len)
411
+ this.size = this.size + toU32(len)
412
+ let p: i32 = off
413
+ const end: i32 = off + len
414
+ // Compressed once 64 KB is pending, the first window too: `pending` is
415
+ // at most `GZIP_WRITER_HISTORY` here, so the window always has the room.
416
+ while (p < end) {
417
+ let take: i32 = GZIP_WRITER_INPUT - (this.fill - this.pending)
418
+ if (take > end - p) {
419
+ take = end - p
420
+ }
421
+ copyBytes(this.window, this.fill, src, p, take)
422
+ this.fill += take
423
+ p += take
424
+ if (this.fill - this.pending === GZIP_WRITER_INPUT) {
425
+ o = this.drain(out, o, false)
426
+ if (o < 0) {
427
+ panic("GzipWriter.write: a block larger than its bound")
428
+ }
429
+ }
430
+ }
431
+ return o
432
+ }
433
+
434
+ /**
435
+ * Compresses everything pending and ends with an empty stored block, so a
436
+ * reader can decode every byte written so far (zlib's `Z_SYNC_FLUSH`).
437
+ * Answers the new end of `out`, or `DEFLATE_NO_ROOM` below `bound(0)`.
438
+ */
439
+ flush(out: u8[], at: i32): i32 {
440
+ gzipWindow("GzipWriter.flush", out, at, 0)
441
+ if (this.finished) {
442
+ panic("GzipWriter.flush: the body has been finished")
443
+ }
444
+ if (toI32(out.length) - at < this.bound(toI32(0))) {
445
+ return DEFLATE_NO_ROOM
446
+ }
447
+ let o: i32 = this.header(out, at)
448
+ if (this.fill > this.pending) {
449
+ o = this.drain(out, o, false)
450
+ if (o < 0) {
451
+ panic("GzipWriter.flush: a block larger than its bound")
452
+ }
453
+ }
454
+ o = this.deflater.writeStored(this.window, toI32(0), toI32(0), false, out, o, toI32(out.length))
455
+ return o
456
+ }
457
+
458
+ /**
459
+ * Compresses everything pending as the final block and writes the
460
+ * trailer; answers the new end of `out`, or `DEFLATE_NO_ROOM` below
461
+ * `bound(0)`. `start` begins another body.
462
+ */
463
+ finish(out: u8[], at: i32): i32 {
464
+ gzipWindow("GzipWriter.finish", out, at, 0)
465
+ if (this.finished) {
466
+ panic("GzipWriter.finish: the body has been finished")
467
+ }
468
+ if (toI32(out.length) - at < this.bound(toI32(0))) {
469
+ return DEFLATE_NO_ROOM
470
+ }
471
+ let o: i32 = this.header(out, at)
472
+ o = this.drain(out, o, true)
473
+ if (o < 0) {
474
+ panic("GzipWriter.finish: a block larger than its bound")
475
+ }
476
+ o = this.deflater.flushBits(out, o, toI32(out.length))
477
+ if (o < 0 || toI32(out.length) - o < GZIP_TRAILER_SIZE) {
478
+ panic("GzipWriter.finish: a block larger than its bound")
479
+ }
480
+ gzipPut32(out, o, this.checksum)
481
+ gzipPut32(out, o + 4, this.size)
482
+ this.finished = true
483
+ return o + GZIP_TRAILER_SIZE
484
+ }
485
+ }
486
+
487
+ /**
488
+ * A streaming gzip decompressor, for a body that arrives in pieces or does
489
+ * not fit in memory. Sans-IO in the `nish/net/postgres` shape: copy input
490
+ * into `inputBuffer()` at `inputEnd()` and `commit` it (or `feed` a window),
491
+ * then call `read` until it answers `GZIP_NEED_INPUT`; at the end of the
492
+ * input, `endInput()`. `read(out, at, max)` copies up to `max` decoded bytes
493
+ * and answers how many, `GZIP_NEED_INPUT`, `GZIP_END` once the stream and its
494
+ * trailer are through, or a negative error, final. A total of more than
495
+ * `limit` decoded bytes is `DEFLATE_ERR_OUTPUT_LIMIT`. Concatenated members
496
+ * are read as one body, as by `GzipDecompressor`. About 100 KB, allocated
497
+ * once; `start` begins the next body.
498
+ */
499
+ export class GzipReader {
500
+ readonly inflater: Inflater
501
+ readonly crc: Crc32
502
+ /** Input not yet decoded. */
503
+ readonly input: u8[]
504
+ /** The decoded window: history from `windowStart`, unread output from `readPos` to `writePos`. */
505
+ readonly output: u8[]
506
+ inStart: i32
507
+ inEnd: i32
508
+ readPos: i32
509
+ writePos: i32
510
+ /** Where the current member's output began, for its distances. */
511
+ histStart: i32
512
+ /** Where the bytes not yet checksummed start. */
513
+ crcPos: i32
514
+ /** 0: a member header next; 1: inside a member's stream; 2: its trailer; 3: done; < 0: failed. */
515
+ phase: i32
516
+ /** Whether more input may come. */
517
+ more: boolean
518
+ /** The member's running CRC-32 and length, and the body's total length. */
519
+ checksum: u32
520
+ size: u32
521
+ total: i64
522
+ /** The most decoded bytes `read` hands out in all. */
523
+ readonly limit: i64
524
+ /** Whether a member has been read; another may follow. */
525
+ members: i32
526
+
527
+ /** A reader that refuses more than `limit` decoded bytes in all; a negative `limit` panics. */
528
+ constructor(limit: i64) {
529
+ if (limit < 0) {
530
+ panic("GzipReader: a negative limit")
531
+ }
532
+ this.inflater = new Inflater()
533
+ this.crc = new Crc32()
534
+ this.input = new Array<u8>(GZIP_READER_INPUT)
535
+ this.output = new Array<u8>(GZIP_READER_WINDOW + GZIP_READER_OUTPUT)
536
+ this.limit = limit
537
+ this.inStart = 0
538
+ this.inEnd = 0
539
+ this.readPos = 0
540
+ this.writePos = 0
541
+ this.histStart = 0
542
+ this.crcPos = 0
543
+ this.phase = 0
544
+ this.more = true
545
+ this.checksum = 0
546
+ this.size = 0
547
+ this.total = 0
548
+ this.members = 0
549
+ }
550
+
551
+ /** Begins a new body, dropping any input and output left over. */
552
+ start(): void {
553
+ this.inStart = 0
554
+ this.inEnd = 0
555
+ this.readPos = 0
556
+ this.writePos = 0
557
+ this.histStart = 0
558
+ this.crcPos = 0
559
+ this.phase = 0
560
+ this.more = true
561
+ this.checksum = 0
562
+ this.size = 0
563
+ this.total = 0
564
+ this.members = 0
565
+ }
566
+
567
+ /** The buffer to copy input into, from `inputEnd()` for at most `inputRoom()` bytes. */
568
+ inputBuffer(): u8[] {
569
+ this.compact()
570
+ return this.input
571
+ }
572
+
573
+ /** Where new input goes in `inputBuffer()`. */
574
+ inputEnd(): i32 {
575
+ return this.inEnd
576
+ }
577
+
578
+ /** How many bytes `inputBuffer()` has room for. */
579
+ inputRoom(): i32 {
580
+ this.compact()
581
+ return toI32(this.input.length) - this.inEnd
582
+ }
583
+
584
+ /** Takes `n` bytes copied into `inputBuffer()` at `inputEnd()`; more than `inputRoom()` panics. */
585
+ commit(n: i32): void {
586
+ if (n < 0 || n > toI32(this.input.length) - this.inEnd) {
587
+ panic("GzipReader.commit: more bytes than the buffer has room for")
588
+ }
589
+ this.inEnd += n
590
+ }
591
+
592
+ /** Copies as much of `src[off .. off + len)` as fits and answers how much. */
593
+ feed(src: u8[], off: i32, len: i32): i32 {
594
+ gzipWindow("GzipReader.feed", src, off, len)
595
+ const room: i32 = this.inputRoom()
596
+ const take: i32 = len < room ? len : room
597
+ copyBytes(this.input, this.inEnd, src, off, take)
598
+ this.inEnd += take
599
+ return take
600
+ }
601
+
602
+ /** Says that no input follows what has been given. */
603
+ endInput(): void {
604
+ this.more = false
605
+ }
606
+
607
+ /** Moves the undecoded input to the front of its buffer. */
608
+ compact(): void {
609
+ if (this.inStart > 0) {
610
+ const n: i32 = this.inEnd - this.inStart
611
+ copyBytes(this.input, 0, this.input, this.inStart, n)
612
+ this.inStart = 0
613
+ this.inEnd = n
614
+ }
615
+ }
616
+
617
+ /** Adds the decoded bytes not yet checksummed to the member's CRC-32 and length. */
618
+ sum(): void {
619
+ const n: i32 = this.writePos - this.crcPos
620
+ if (n > 0) {
621
+ this.checksum = this.crc.update(this.checksum, this.output, this.crcPos, n)
622
+ this.size = this.size + toU32(n)
623
+ this.crcPos = this.writePos
624
+ }
625
+ }
626
+
627
+ /** Slides the decoded window down once the unread output has been read and the buffer is past half. */
628
+ slide(): void {
629
+ if (this.readPos === this.writePos && this.writePos > GZIP_READER_WINDOW) {
630
+ this.sum()
631
+ const keep: i32 = GZIP_READER_WINDOW
632
+ const shift: i32 = this.writePos - keep
633
+ copyBytes(this.output, 0, this.output, shift, keep)
634
+ this.readPos -= shift
635
+ this.writePos -= shift
636
+ this.crcPos -= shift
637
+ this.histStart = this.histStart > shift ? this.histStart - shift : 0
638
+ }
639
+ }
640
+
641
+ /** Decodes as far as the input and the output buffer allow; answers 0 or a final code. */
642
+ advance(): i32 {
643
+ while (true) {
644
+ const avail: i32 = this.inEnd - this.inStart
645
+ if (this.phase === 0) {
646
+ if (avail === 0 && !this.more) {
647
+ if (this.members === 0) {
648
+ return DEFLATE_ERR_TRUNCATED
649
+ }
650
+ this.phase = 3
651
+ return 0
652
+ }
653
+ const body: i32 = gzipReadHeader(this.crc, this.input, this.inStart, this.inEnd)
654
+ if (body === DEFLATE_ERR_TRUNCATED) {
655
+ if (this.more) {
656
+ if (this.inputRoomRaw() === 0 && this.inStart === 0) {
657
+ return GZIP_ERR_HEADER
658
+ }
659
+ return 0
660
+ }
661
+ return DEFLATE_ERR_TRUNCATED
662
+ }
663
+ if (body < 0) {
664
+ return body
665
+ }
666
+ this.inStart = body
667
+ this.inflater.reset()
668
+ this.histStart = this.writePos
669
+ this.checksum = 0
670
+ this.size = 0
671
+ this.phase = 1
672
+ } else if (this.phase === 1) {
673
+ this.slide()
674
+ const outEnd: i32 = toI32(this.output.length)
675
+ if (this.writePos === outEnd) {
676
+ return 0
677
+ }
678
+ const inf: Inflater = this.inflater
679
+ const r: i32 = inf.inflate(
680
+ this.input,
681
+ this.inStart,
682
+ this.inEnd,
683
+ this.more,
684
+ this.output,
685
+ this.writePos,
686
+ outEnd,
687
+ this.histStart
688
+ )
689
+ this.inStart = inf.inPos
690
+ const made: i32 = inf.outPos - this.writePos
691
+ this.writePos = inf.outPos
692
+ this.total = this.total + toI64(made)
693
+ if (this.total > this.limit) {
694
+ return DEFLATE_ERR_OUTPUT_LIMIT
695
+ }
696
+ if (r === INFLATE_DONE) {
697
+ this.sum()
698
+ this.phase = 2
699
+ } else if (r === INFLATE_NEED_INPUT || r === INFLATE_NEED_OUTPUT) {
700
+ return 0
701
+ } else {
702
+ return r
703
+ }
704
+ } else if (this.phase === 2) {
705
+ if (avail < GZIP_TRAILER_SIZE) {
706
+ return this.more ? 0 : DEFLATE_ERR_TRUNCATED
707
+ }
708
+ if (gzipGet32(this.input, this.inStart) !== this.checksum) {
709
+ return GZIP_ERR_CRC
710
+ }
711
+ if (gzipGet32(this.input, this.inStart + 4) !== this.size) {
712
+ return GZIP_ERR_SIZE
713
+ }
714
+ this.inStart += GZIP_TRAILER_SIZE
715
+ this.members += 1
716
+ this.phase = 0
717
+ } else {
718
+ return 0
719
+ }
720
+ }
721
+ }
722
+
723
+ /** The room `input` has without compacting. */
724
+ inputRoomRaw(): i32 {
725
+ return toI32(this.input.length) - this.inEnd
726
+ }
727
+
728
+ /**
729
+ * Copies up to `max` decoded bytes into `out` from `at` and answers how
730
+ * many (at least 1; `max` is at least 1), or `GZIP_NEED_INPUT` when it needs more input first,
731
+ * `GZIP_END` once the body has ended, or a negative error, after which it
732
+ * answers that error again until `start`. A window outside `out` panics.
733
+ */
734
+ read(out: u8[], at: i32, max: i32): i32 {
735
+ gzipWindow("GzipReader.read", out, at, max)
736
+ if (max < 1) {
737
+ panic("GzipReader.read: room for no bytes")
738
+ }
739
+ if (this.phase < 0) {
740
+ return this.phase
741
+ }
742
+ if (this.readPos === this.writePos) {
743
+ const r: i32 = this.advance()
744
+ if (r < 0) {
745
+ this.phase = r
746
+ return r
747
+ }
748
+ }
749
+ const n0: i32 = this.writePos - this.readPos
750
+ if (n0 === 0) {
751
+ if (this.phase === 3) {
752
+ return GZIP_END
753
+ }
754
+ return GZIP_NEED_INPUT
755
+ }
756
+ const n: i32 = n0 < max ? n0 : max
757
+ copyBytes(out, at, this.output, this.readPos, n)
758
+ this.readPos += n
759
+ return n
760
+ }
761
+ }