@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.
- package/bin/nish +0 -0
- package/docs/AI.md +50 -23
- package/package.json +1 -1
- package/runtime/browser.mjs +4 -3
- package/runtime/core/arrays.d.ts +0 -2
- package/runtime/core/arrays.mjs +0 -2
- package/runtime/core/host.d.ts +8 -0
- package/runtime/core/host.mjs +50 -2
- package/runtime/core/numbers.mjs +2 -3
- package/runtime/nish.d.ts +100 -4
- package/runtime/nish.h +34 -0
- package/runtime/nish.mjs +23 -5
- package/runtime/node.mjs +57 -6
- package/runtime/runtime-net.c +45 -4
- package/runtime/runtime-os.c +115 -12
- package/runtime/runtime-term.c +2 -2
- package/runtime/runtime.c +1 -1
- package/scripts/build.sh +13 -3
- package/std/README.md +44 -7
- package/std/cli-args.ts +1216 -0
- package/std/cli-flags.ts +585 -0
- package/std/cli.ts +308 -19
- package/std/compress/checksum.ts +290 -0
- package/std/compress/deflate.ts +2790 -0
- package/std/compress/gzip.ts +761 -0
- package/std/compress/zlib.ts +168 -0
- package/std/crypto/LICENSE-fiat-crypto-p384 +21 -0
- package/std/crypto/p256.ts +63 -1
- package/std/crypto/p384.ts +6044 -0
- package/std/crypto/pki-roots.ts +322 -0
- package/std/crypto/pki.ts +1207 -0
- package/std/crypto/rsa.ts +663 -0
- package/std/crypto/sha256.ts +2 -3
- package/std/crypto/x25519.ts +3 -4
- package/std/crypto/x509.ts +1054 -58
- package/std/net/dns-config.ts +734 -0
- package/std/net/dns-message.ts +517 -0
- package/std/net/dns-resolver.ts +1015 -0
- package/std/net/dns.ts +478 -0
- package/std/net/http-compress.ts +393 -0
- package/std/net/http1-client.ts +1227 -0
- package/std/net/http1-server.ts +101 -1
- package/std/net/http1.ts +9 -9
- package/std/net/tls/client.ts +1094 -0
- package/std/net/tls/codec.ts +585 -3
- package/std/net/tls/record-client.ts +1192 -0
- package/std/net/tls/verify.ts +221 -0
- package/std/net/tls-tcp.ts +642 -11
- package/std/net/tls.ts +2 -2
- package/std/net/url.ts +559 -4
- package/std/yaml-write.ts +415 -0
package/std/cli.ts
CHANGED
|
@@ -12,13 +12,15 @@
|
|
|
12
12
|
* menu.row("--times", "<n>", "say it n times");
|
|
13
13
|
* console.log(menu.text());
|
|
14
14
|
*
|
|
15
|
-
* **The terminal's own colours.** A `Style` paints with the
|
|
16
|
-
* colours
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
15
|
+
* **The terminal's own colours.** A `Style` paints with the sixteen SGR
|
|
16
|
+
* colours (the eight basic ones and their bright twins), their backgrounds
|
|
17
|
+
* and the attributes, never a fixed RGB or 256-palette tone unless the caller
|
|
18
|
+
* asks for one through `sgr`. Every theme redefines those sixteen, so the
|
|
19
|
+
* menu comes out in whatever palette the reader chose, and a terminal with no
|
|
20
|
+
* theme set paints them in its built-in defaults. A terminal with no colours
|
|
21
|
+
* at all (`tput colors` says fewer than eight) gets the failsafe: the same
|
|
22
|
+
* structure in bold and underline, which a monochrome terminal still shows,
|
|
23
|
+
* and no colour from a colour painter. Colour that is forced is the theme's.
|
|
22
24
|
*
|
|
23
25
|
* **Colour is for a person reading a terminal**, and nothing else may see it:
|
|
24
26
|
* a script that captures `--help`, a test that compares it, a pipe into `less`
|
|
@@ -56,18 +58,92 @@ const PALETTE_THEME: i32 = 0
|
|
|
56
58
|
/** `paletteOf`'s answer: marked up in attributes only, the failsafe. */
|
|
57
59
|
const PALETTE_MONO: i32 = 2
|
|
58
60
|
|
|
61
|
+
/*
|
|
62
|
+
* The SGR parameters `Style`'s painters use, exported so a caller composes a
|
|
63
|
+
* list for `sgr` (`${SGR_BOLD};${SGR_RED}`) or a role (`flagThemed`) without
|
|
64
|
+
* spelling the numbers. The first sixteen are the colours a theme redefines:
|
|
65
|
+
* the eight basic ones and their bright twins.
|
|
66
|
+
*/
|
|
67
|
+
export const SGR_BLACK: string = "30"
|
|
68
|
+
export const SGR_RED: string = "31"
|
|
69
|
+
export const SGR_GREEN: string = "32"
|
|
70
|
+
export const SGR_YELLOW: string = "33"
|
|
71
|
+
export const SGR_BLUE: string = "34"
|
|
72
|
+
export const SGR_MAGENTA: string = "35"
|
|
73
|
+
export const SGR_CYAN: string = "36"
|
|
74
|
+
export const SGR_WHITE: string = "37"
|
|
75
|
+
export const SGR_BRIGHT_BLACK: string = "90"
|
|
76
|
+
export const SGR_BRIGHT_RED: string = "91"
|
|
77
|
+
export const SGR_BRIGHT_GREEN: string = "92"
|
|
78
|
+
export const SGR_BRIGHT_YELLOW: string = "93"
|
|
79
|
+
export const SGR_BRIGHT_BLUE: string = "94"
|
|
80
|
+
export const SGR_BRIGHT_MAGENTA: string = "95"
|
|
81
|
+
export const SGR_BRIGHT_CYAN: string = "96"
|
|
82
|
+
export const SGR_BRIGHT_WHITE: string = "97"
|
|
83
|
+
export const SGR_BG_BLACK: string = "40"
|
|
84
|
+
export const SGR_BG_RED: string = "41"
|
|
85
|
+
export const SGR_BG_GREEN: string = "42"
|
|
86
|
+
export const SGR_BG_YELLOW: string = "43"
|
|
87
|
+
export const SGR_BG_BLUE: string = "44"
|
|
88
|
+
export const SGR_BG_MAGENTA: string = "45"
|
|
89
|
+
export const SGR_BG_CYAN: string = "46"
|
|
90
|
+
export const SGR_BG_WHITE: string = "47"
|
|
91
|
+
export const SGR_BG_BRIGHT_BLACK: string = "100"
|
|
92
|
+
export const SGR_BG_BRIGHT_RED: string = "101"
|
|
93
|
+
export const SGR_BG_BRIGHT_GREEN: string = "102"
|
|
94
|
+
export const SGR_BG_BRIGHT_YELLOW: string = "103"
|
|
95
|
+
export const SGR_BG_BRIGHT_BLUE: string = "104"
|
|
96
|
+
export const SGR_BG_BRIGHT_MAGENTA: string = "105"
|
|
97
|
+
export const SGR_BG_BRIGHT_CYAN: string = "106"
|
|
98
|
+
export const SGR_BG_BRIGHT_WHITE: string = "107"
|
|
99
|
+
export const SGR_BOLD: string = "1"
|
|
100
|
+
export const SGR_FAINT: string = "2"
|
|
101
|
+
export const SGR_ITALIC: string = "3"
|
|
102
|
+
export const SGR_UNDERLINE: string = "4"
|
|
103
|
+
export const SGR_INVERSE: string = "7"
|
|
104
|
+
export const SGR_STRIKETHROUGH: string = "9"
|
|
105
|
+
|
|
59
106
|
/**
|
|
60
107
|
* How one output stream is painted. `on` says whether to mark the text up at
|
|
61
108
|
* all, and `colour` whether the terminal has colours to mark it up with;
|
|
62
109
|
* without them each mark falls back to bold, underline or nothing.
|
|
110
|
+
*
|
|
111
|
+
* Each help role (`heading`, `name`, `flag`, `meta`, `dim`) keeps a pair of
|
|
112
|
+
* SGR lists, the theme's and the failsafe's, so a program recolours its menu
|
|
113
|
+
* by setting a pair (`flagThemed`, `flagMono`) and `styleFor` still decides
|
|
114
|
+
* whether, and which of the two, it paints. An empty list leaves the role
|
|
115
|
+
* plain.
|
|
63
116
|
*/
|
|
64
117
|
export class Style {
|
|
65
118
|
on: boolean
|
|
66
119
|
colour: boolean
|
|
120
|
+
headingThemed: string
|
|
121
|
+
headingMono: string
|
|
122
|
+
nameThemed: string
|
|
123
|
+
nameMono: string
|
|
124
|
+
flagThemed: string
|
|
125
|
+
flagMono: string
|
|
126
|
+
metaThemed: string
|
|
127
|
+
metaMono: string
|
|
128
|
+
dimThemed: string
|
|
129
|
+
dimMono: string
|
|
67
130
|
|
|
68
131
|
constructor(on: boolean, colour: boolean) {
|
|
69
132
|
this.on = on
|
|
70
133
|
this.colour = colour
|
|
134
|
+
// Bold magenta, spelled out: a template here would allocate per Style.
|
|
135
|
+
this.headingThemed = "1;35"
|
|
136
|
+
this.headingMono = SGR_BOLD
|
|
137
|
+
this.nameThemed = "1;35"
|
|
138
|
+
this.nameMono = SGR_BOLD
|
|
139
|
+
this.flagThemed = SGR_CYAN
|
|
140
|
+
this.flagMono = SGR_BOLD
|
|
141
|
+
this.metaThemed = SGR_YELLOW
|
|
142
|
+
this.metaMono = SGR_UNDERLINE
|
|
143
|
+
// Faint rather than bright black, which Solarized makes the background;
|
|
144
|
+
// plain without colour, where the bold flags already stand apart from it.
|
|
145
|
+
this.dimThemed = SGR_FAINT
|
|
146
|
+
this.dimMono = ""
|
|
71
147
|
}
|
|
72
148
|
|
|
73
149
|
/**
|
|
@@ -84,33 +160,246 @@ export class Style {
|
|
|
84
160
|
return `${CSI}${code}m${text}${CSI}0m`
|
|
85
161
|
}
|
|
86
162
|
|
|
87
|
-
/** A section heading
|
|
163
|
+
/** A section heading, in `headingThemed` or `headingMono`. */
|
|
88
164
|
heading(text: string): string {
|
|
89
|
-
return this.paint(text,
|
|
165
|
+
return this.paint(text, this.headingThemed, this.headingMono)
|
|
90
166
|
}
|
|
91
167
|
|
|
92
|
-
/**
|
|
168
|
+
/**
|
|
169
|
+
* The tool's own name where it opens a line, in `nameThemed` or
|
|
170
|
+
* `nameMono`.
|
|
171
|
+
*/
|
|
93
172
|
name(text: string): string {
|
|
94
|
-
return this.paint(text,
|
|
173
|
+
return this.paint(text, this.nameThemed, this.nameMono)
|
|
95
174
|
}
|
|
96
175
|
|
|
97
|
-
/**
|
|
176
|
+
/**
|
|
177
|
+
* A flag, or a command a person would type, in `flagThemed` or
|
|
178
|
+
* `flagMono`.
|
|
179
|
+
*/
|
|
98
180
|
flag(text: string): string {
|
|
99
|
-
return this.paint(text,
|
|
181
|
+
return this.paint(text, this.flagThemed, this.flagMono)
|
|
100
182
|
}
|
|
101
183
|
|
|
102
|
-
/**
|
|
184
|
+
/**
|
|
185
|
+
* A placeholder the person fills in, such as `<file.ts>` or `speed|size`, in
|
|
186
|
+
* `metaThemed` or `metaMono`.
|
|
187
|
+
*/
|
|
103
188
|
meta(text: string): string {
|
|
104
|
-
return this.paint(text,
|
|
189
|
+
return this.paint(text, this.metaThemed, this.metaMono)
|
|
105
190
|
}
|
|
106
191
|
|
|
107
192
|
/**
|
|
108
|
-
* Text that explains rather than names
|
|
109
|
-
*
|
|
110
|
-
* colour, where the bold flags already stand apart from it.
|
|
193
|
+
* Text that explains rather than names, such as descriptions and notes, in
|
|
194
|
+
* `dimThemed` or `dimMono`.
|
|
111
195
|
*/
|
|
112
196
|
dim(text: string): string {
|
|
113
|
-
return this.paint(text,
|
|
197
|
+
return this.paint(text, this.dimThemed, this.dimMono)
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* `text` painted with any SGR parameter list (`"1;31"`, `"38;5;208"`,
|
|
202
|
+
* `"38;2;255;128;0"`) when colour is on, and unchanged otherwise: the way to
|
|
203
|
+
* 256-colour and truecolor, which ignore the reader's theme, so they stay
|
|
204
|
+
* the caller's explicit choice. The failsafe paints nothing, since the list
|
|
205
|
+
* may name a colour a monochrome terminal cannot show.
|
|
206
|
+
*/
|
|
207
|
+
sgr(text: string, codes: string): string {
|
|
208
|
+
return this.paint(text, codes, "")
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// The colour painters go through `sgr`, so the failsafe, which has no
|
|
212
|
+
// colours to show, paints none of them; the attribute painters paint under
|
|
213
|
+
// both.
|
|
214
|
+
|
|
215
|
+
/** `text` in the theme's black. */
|
|
216
|
+
black(text: string): string {
|
|
217
|
+
return this.sgr(text, SGR_BLACK)
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/** `text` in the theme's red. */
|
|
221
|
+
red(text: string): string {
|
|
222
|
+
return this.sgr(text, SGR_RED)
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/** `text` in the theme's green. */
|
|
226
|
+
green(text: string): string {
|
|
227
|
+
return this.sgr(text, SGR_GREEN)
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/** `text` in the theme's yellow. */
|
|
231
|
+
yellow(text: string): string {
|
|
232
|
+
return this.sgr(text, SGR_YELLOW)
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/** `text` in the theme's blue. */
|
|
236
|
+
blue(text: string): string {
|
|
237
|
+
return this.sgr(text, SGR_BLUE)
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/** `text` in the theme's magenta. */
|
|
241
|
+
magenta(text: string): string {
|
|
242
|
+
return this.sgr(text, SGR_MAGENTA)
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/** `text` in the theme's cyan. */
|
|
246
|
+
cyan(text: string): string {
|
|
247
|
+
return this.sgr(text, SGR_CYAN)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/** `text` in the theme's white. */
|
|
251
|
+
white(text: string): string {
|
|
252
|
+
return this.sgr(text, SGR_WHITE)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/** `text` in the theme's bright black (gray). */
|
|
256
|
+
brightBlack(text: string): string {
|
|
257
|
+
return this.sgr(text, SGR_BRIGHT_BLACK)
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** `text` in the theme's bright red. */
|
|
261
|
+
brightRed(text: string): string {
|
|
262
|
+
return this.sgr(text, SGR_BRIGHT_RED)
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/** `text` in the theme's bright green. */
|
|
266
|
+
brightGreen(text: string): string {
|
|
267
|
+
return this.sgr(text, SGR_BRIGHT_GREEN)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/** `text` in the theme's bright yellow. */
|
|
271
|
+
brightYellow(text: string): string {
|
|
272
|
+
return this.sgr(text, SGR_BRIGHT_YELLOW)
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** `text` in the theme's bright blue. */
|
|
276
|
+
brightBlue(text: string): string {
|
|
277
|
+
return this.sgr(text, SGR_BRIGHT_BLUE)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/** `text` in the theme's bright magenta. */
|
|
281
|
+
brightMagenta(text: string): string {
|
|
282
|
+
return this.sgr(text, SGR_BRIGHT_MAGENTA)
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** `text` in the theme's bright cyan. */
|
|
286
|
+
brightCyan(text: string): string {
|
|
287
|
+
return this.sgr(text, SGR_BRIGHT_CYAN)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** `text` in the theme's bright white. */
|
|
291
|
+
brightWhite(text: string): string {
|
|
292
|
+
return this.sgr(text, SGR_BRIGHT_WHITE)
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/** `text` on the theme's black. */
|
|
296
|
+
bgBlack(text: string): string {
|
|
297
|
+
return this.sgr(text, SGR_BG_BLACK)
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
/** `text` on the theme's red. */
|
|
301
|
+
bgRed(text: string): string {
|
|
302
|
+
return this.sgr(text, SGR_BG_RED)
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/** `text` on the theme's green. */
|
|
306
|
+
bgGreen(text: string): string {
|
|
307
|
+
return this.sgr(text, SGR_BG_GREEN)
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/** `text` on the theme's yellow. */
|
|
311
|
+
bgYellow(text: string): string {
|
|
312
|
+
return this.sgr(text, SGR_BG_YELLOW)
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** `text` on the theme's blue. */
|
|
316
|
+
bgBlue(text: string): string {
|
|
317
|
+
return this.sgr(text, SGR_BG_BLUE)
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
/** `text` on the theme's magenta. */
|
|
321
|
+
bgMagenta(text: string): string {
|
|
322
|
+
return this.sgr(text, SGR_BG_MAGENTA)
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
/** `text` on the theme's cyan. */
|
|
326
|
+
bgCyan(text: string): string {
|
|
327
|
+
return this.sgr(text, SGR_BG_CYAN)
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/** `text` on the theme's white. */
|
|
331
|
+
bgWhite(text: string): string {
|
|
332
|
+
return this.sgr(text, SGR_BG_WHITE)
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/** `text` on the theme's bright black. */
|
|
336
|
+
bgBrightBlack(text: string): string {
|
|
337
|
+
return this.sgr(text, SGR_BG_BRIGHT_BLACK)
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
/** `text` on the theme's bright red. */
|
|
341
|
+
bgBrightRed(text: string): string {
|
|
342
|
+
return this.sgr(text, SGR_BG_BRIGHT_RED)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
/** `text` on the theme's bright green. */
|
|
346
|
+
bgBrightGreen(text: string): string {
|
|
347
|
+
return this.sgr(text, SGR_BG_BRIGHT_GREEN)
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
/** `text` on the theme's bright yellow. */
|
|
351
|
+
bgBrightYellow(text: string): string {
|
|
352
|
+
return this.sgr(text, SGR_BG_BRIGHT_YELLOW)
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/** `text` on the theme's bright blue. */
|
|
356
|
+
bgBrightBlue(text: string): string {
|
|
357
|
+
return this.sgr(text, SGR_BG_BRIGHT_BLUE)
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** `text` on the theme's bright magenta. */
|
|
361
|
+
bgBrightMagenta(text: string): string {
|
|
362
|
+
return this.sgr(text, SGR_BG_BRIGHT_MAGENTA)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/** `text` on the theme's bright cyan. */
|
|
366
|
+
bgBrightCyan(text: string): string {
|
|
367
|
+
return this.sgr(text, SGR_BG_BRIGHT_CYAN)
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
/** `text` on the theme's bright white. */
|
|
371
|
+
bgBrightWhite(text: string): string {
|
|
372
|
+
return this.sgr(text, SGR_BG_BRIGHT_WHITE)
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/** `text` bold. */
|
|
376
|
+
bold(text: string): string {
|
|
377
|
+
return this.paint(text, SGR_BOLD, SGR_BOLD)
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
/** `text` faint. */
|
|
381
|
+
faint(text: string): string {
|
|
382
|
+
return this.paint(text, SGR_FAINT, SGR_FAINT)
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/** `text` italic, where the terminal has it. */
|
|
386
|
+
italic(text: string): string {
|
|
387
|
+
return this.paint(text, SGR_ITALIC, SGR_ITALIC)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/** `text` underlined. */
|
|
391
|
+
underline(text: string): string {
|
|
392
|
+
return this.paint(text, SGR_UNDERLINE, SGR_UNDERLINE)
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/** `text` with foreground and background swapped. */
|
|
396
|
+
inverse(text: string): string {
|
|
397
|
+
return this.paint(text, SGR_INVERSE, SGR_INVERSE)
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** `text` struck through. */
|
|
401
|
+
strikethrough(text: string): string {
|
|
402
|
+
return this.paint(text, SGR_STRIKETHROUGH, SGR_STRIKETHROUGH)
|
|
114
403
|
}
|
|
115
404
|
}
|
|
116
405
|
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `nish/compress/checksum` — the two checksums the compressed formats carry:
|
|
3
|
+
* CRC-32 (ISO 3309, the polynomial gzip's trailer uses, RFC 1952 §8) and
|
|
4
|
+
* Adler-32 (zlib's trailer, RFC 1950 §9).
|
|
5
|
+
*
|
|
6
|
+
* import { Crc32 } from "nish/compress/checksum";
|
|
7
|
+
*
|
|
8
|
+
* const crc = new Crc32(); // the tables, once
|
|
9
|
+
* let c: u32 = crc.update(toU32(0), body, 0, 100);
|
|
10
|
+
* c = crc.update(c, body, 100, toI32(body.length) - 100); // the same as one call
|
|
11
|
+
*
|
|
12
|
+
* A module has no global state, so CRC-32's tables live in a `Crc32` object:
|
|
13
|
+
* build one and keep it, because building it is 4,096 table entries (about
|
|
14
|
+
* ten microseconds) and an update of a 5 KB body is under one. `crc32`
|
|
15
|
+
* is the one-off convenience that builds a table for one call.
|
|
16
|
+
*
|
|
17
|
+
* CRC-32 is slicing-by-8: eight 256-entry tables, eight bytes per step with
|
|
18
|
+
* one table read per byte and no dependency between the reads of a step
|
|
19
|
+
* (Kounavis and Berry, "A Systematic Approach to Building High Performance
|
|
20
|
+
* Software-based CRC Generators", 2005). From 80 bytes on it is also braided
|
|
21
|
+
* five ways (Kadatch and Jenkins, 2010), so five steps are in flight at once;
|
|
22
|
+
* the braids have a second set of eight tables. Adler-32 defers its two modulo
|
|
23
|
+
* reductions to once every 5,552 bytes, the most that cannot overflow 32 bits
|
|
24
|
+
* (RFC 1950 §9 and zlib's `NMAX`).
|
|
25
|
+
*
|
|
26
|
+
* Neither is a cryptographic hash: both detect accidental corruption only.
|
|
27
|
+
* Written from the specifications, not ported from another implementation.
|
|
28
|
+
*
|
|
29
|
+
* The table reads in `update` are `nish:unsafe`'s `uncheckedGet`: every index
|
|
30
|
+
* is a byte plus a multiple of 256 below 2,048, and both tables are built here
|
|
31
|
+
* at exactly that length and never replaced, which is the whole proof. The input
|
|
32
|
+
* window is checked once, before the loop.
|
|
33
|
+
*/
|
|
34
|
+
import { uncheckedGet, uncheckedSet } from "nish:unsafe"
|
|
35
|
+
|
|
36
|
+
/** The reflected CRC-32 polynomial, x^32 + x^26 + ... + 1 (RFC 1952 §8). */
|
|
37
|
+
const CRC32_POLY: i64 = 0xedb88320
|
|
38
|
+
|
|
39
|
+
/** The entries of the eight slicing tables, 256 each. */
|
|
40
|
+
const CRC32_TABLE_SIZE: i32 = 2048
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The braids `Crc32.update` runs at once over a long input, each over every
|
|
44
|
+
* fifth 8-byte word, and the bytes a block of one word per braid spans.
|
|
45
|
+
*/
|
|
46
|
+
const CRC32_BRAIDS: i32 = 5
|
|
47
|
+
const CRC32_BLOCK: i32 = 40
|
|
48
|
+
|
|
49
|
+
/** The shortest input `Crc32.update` braids: two blocks, one braided and the last joined. */
|
|
50
|
+
const CRC32_BRAID_MIN: i32 = 80
|
|
51
|
+
|
|
52
|
+
/** zlib's `NMAX`: the most bytes Adler-32's sums take before they must be reduced. */
|
|
53
|
+
const ADLER32_NMAX: i32 = 5552
|
|
54
|
+
|
|
55
|
+
/** The Adler-32 modulus, the largest prime below 2^16 (RFC 1950 §9). */
|
|
56
|
+
const ADLER32_BASE: i32 = 65521
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* The little-endian 64-bit word at `buf[p .. p + 8)`, which the caller has
|
|
60
|
+
* checked. `p`'s range makes each `p + k` an `add nsw`, which lets LLVM load
|
|
61
|
+
* the word at once.
|
|
62
|
+
*/
|
|
63
|
+
const checksumLoad64 = (buf: u8[], p: integer<0, 2147483639>): u64 =>
|
|
64
|
+
toU64(uncheckedGet(buf, p)) |
|
|
65
|
+
(toU64(uncheckedGet(buf, p + 1)) << toU64(8)) |
|
|
66
|
+
(toU64(uncheckedGet(buf, p + 2)) << toU64(16)) |
|
|
67
|
+
(toU64(uncheckedGet(buf, p + 3)) << toU64(24)) |
|
|
68
|
+
(toU64(uncheckedGet(buf, p + 4)) << toU64(32)) |
|
|
69
|
+
(toU64(uncheckedGet(buf, p + 5)) << toU64(40)) |
|
|
70
|
+
(toU64(uncheckedGet(buf, p + 6)) << toU64(48)) |
|
|
71
|
+
(toU64(uncheckedGet(buf, p + 7)) << toU64(56))
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* One slicing-by-8 step: the register `c` advanced over the eight bytes of
|
|
75
|
+
* `w`. `t` is the 2,048-entry table, which every caller has checked.
|
|
76
|
+
*/
|
|
77
|
+
const crc32Step = (t: u32[], c: u32, w: u64): u32 => {
|
|
78
|
+
const lo: u32 = c ^ toU32(w)
|
|
79
|
+
const hi: u32 = toU32(w >> toU64(32))
|
|
80
|
+
return (
|
|
81
|
+
uncheckedGet(t, 1792 + toI32(lo & 255)) ^
|
|
82
|
+
uncheckedGet(t, 1536 + toI32((lo >>> 8) & 255)) ^
|
|
83
|
+
uncheckedGet(t, 1280 + toI32((lo >>> 16) & 255)) ^
|
|
84
|
+
uncheckedGet(t, 1024 + toI32(lo >>> 24)) ^
|
|
85
|
+
uncheckedGet(t, 768 + toI32(hi & 255)) ^
|
|
86
|
+
uncheckedGet(t, 512 + toI32((hi >>> 8) & 255)) ^
|
|
87
|
+
uncheckedGet(t, 256 + toI32((hi >>> 16) & 255)) ^
|
|
88
|
+
uncheckedGet(t, toI32(hi >>> 24))
|
|
89
|
+
)
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Panics unless `buf[off .. off + len)` lies inside `buf`. */
|
|
93
|
+
const checksumWindow = (what: string, buf: u8[], off: i32, len: i32): void => {
|
|
94
|
+
if (off < 0 || len < 0 || off > toI32(buf.length) - len) {
|
|
95
|
+
panic(`${what}: the window lies outside its array`)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* CRC-32's lookup tables, built once and read by every `update`. The object
|
|
101
|
+
* holds nothing else, so one may be shared by every compressor and reader in
|
|
102
|
+
* a thread, and by threads that only read it.
|
|
103
|
+
*/
|
|
104
|
+
export class Crc32 {
|
|
105
|
+
/** `table[k * 256 + b]`: the CRC of byte `b` followed by `k` zero bytes. */
|
|
106
|
+
readonly table: u32[]
|
|
107
|
+
/**
|
|
108
|
+
* `braid[k * 256 + b]`: the same, followed by the 32 bytes of the other
|
|
109
|
+
* four braids' words as well, which a braid steps over.
|
|
110
|
+
*/
|
|
111
|
+
readonly braid: u32[]
|
|
112
|
+
|
|
113
|
+
/** Builds the eight tables and their braided form. */
|
|
114
|
+
constructor() {
|
|
115
|
+
const t: u32[] = new Array<u32>(CRC32_TABLE_SIZE)
|
|
116
|
+
for (let i: i32 = 0; i < 256; i += 1) {
|
|
117
|
+
let c: u32 = toU32(i)
|
|
118
|
+
for (let k: i32 = 0; k < 8; k += 1) {
|
|
119
|
+
c = (c & 1) !== 0 ? toU32(CRC32_POLY) ^ (c >>> 1) : c >>> 1
|
|
120
|
+
}
|
|
121
|
+
uncheckedSet(t, i, c)
|
|
122
|
+
}
|
|
123
|
+
for (let i: i32 = 256; i < CRC32_TABLE_SIZE; i += 1) {
|
|
124
|
+
const prev: u32 = uncheckedGet(t, i - 256)
|
|
125
|
+
uncheckedSet(t, i, (prev >>> 8) ^ uncheckedGet(t, toI32(prev & 255)))
|
|
126
|
+
}
|
|
127
|
+
this.table = t
|
|
128
|
+
// A register is linear in its bytes, so each braided table is its eight
|
|
129
|
+
// one-bit entries, stepped over the 32 bytes, and XORs of them.
|
|
130
|
+
const braid: u32[] = new Array<u32>(CRC32_TABLE_SIZE)
|
|
131
|
+
const bits: u32[] = new Array<u32>(8)
|
|
132
|
+
for (let k: i32 = 0; k < 8; k += 1) {
|
|
133
|
+
for (let i: i32 = 0; i < 8; i += 1) {
|
|
134
|
+
let c: u32 = uncheckedGet(t, k * 256 + (toI32(1) << i))
|
|
135
|
+
for (let z: i32 = 0; z < (CRC32_BRAIDS - 1) * 8; z += 1) {
|
|
136
|
+
c = (c >>> 8) ^ uncheckedGet(t, toI32(c & 255))
|
|
137
|
+
}
|
|
138
|
+
bits[i] = c
|
|
139
|
+
}
|
|
140
|
+
uncheckedSet(braid, k * 256, toU32(0))
|
|
141
|
+
for (let b: i32 = 1; b < 256; b += 1) {
|
|
142
|
+
const low: i32 = 31 - toI32(Math.clz32(toU32(b & -b)))
|
|
143
|
+
if (low >= 0 && low < 8) {
|
|
144
|
+
uncheckedSet(braid, k * 256 + b, uncheckedGet(braid, k * 256 + (b & (b - 1))) ^ bits[low])
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
this.braid = braid
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* The CRC-32 of the bytes `crc` was the CRC of, followed by
|
|
153
|
+
* `buf[off .. off + len)`. Start a new checksum from 0. A window outside
|
|
154
|
+
* `buf` panics.
|
|
155
|
+
*/
|
|
156
|
+
update(crc: u32, buf: u8[], off: i32, len: i32): u32 {
|
|
157
|
+
checksumWindow("Crc32.update", buf, off, len)
|
|
158
|
+
const t: u32[] = this.table
|
|
159
|
+
const bt: u32[] = this.braid
|
|
160
|
+
if (toI32(t.length) !== CRC32_TABLE_SIZE || toI32(bt.length) !== CRC32_TABLE_SIZE) {
|
|
161
|
+
panic("Crc32.update: the table is not its size")
|
|
162
|
+
}
|
|
163
|
+
let c: u32 = ~crc
|
|
164
|
+
let p: i32 = off
|
|
165
|
+
const end: i32 = off + len
|
|
166
|
+
if (len >= CRC32_BRAID_MIN) {
|
|
167
|
+
// Braided (Kadatch and Jenkins, "Everything we know about CRC but
|
|
168
|
+
// afraid to forget", 2010): five chains, each over every fifth 8-byte
|
|
169
|
+
// word, so a step's table reads overlap the other four's instead of
|
|
170
|
+
// waiting on its own last. A braid's table also steps it over the
|
|
171
|
+
// other braids' 32 bytes, so before each block every register sits at
|
|
172
|
+
// its own word's place. The last block is taken one word at a time
|
|
173
|
+
// with the plain table, each braid's register joining as its word
|
|
174
|
+
// comes, which adds them into one.
|
|
175
|
+
const blocks: i32 = len / CRC32_BLOCK
|
|
176
|
+
const lastBlock: i32 = off + (blocks - 1) * CRC32_BLOCK
|
|
177
|
+
let c1: u32 = 0
|
|
178
|
+
let c2: u32 = 0
|
|
179
|
+
let c3: u32 = 0
|
|
180
|
+
let c4: u32 = 0
|
|
181
|
+
// Each word's position is a local the loop condition bounds, which
|
|
182
|
+
// is what lets it enter the loader's range without a check.
|
|
183
|
+
let p1: i32 = p + 8
|
|
184
|
+
let p2: i32 = p + 16
|
|
185
|
+
let p3: i32 = p + 24
|
|
186
|
+
let p4: i32 = p + 32
|
|
187
|
+
while (
|
|
188
|
+
p < lastBlock &&
|
|
189
|
+
p >= 0 &&
|
|
190
|
+
p1 >= 0 &&
|
|
191
|
+
p2 >= 0 &&
|
|
192
|
+
p3 >= 0 &&
|
|
193
|
+
p4 >= 0 &&
|
|
194
|
+
p4 <= 2147483639 &&
|
|
195
|
+
p3 <= 2147483639 &&
|
|
196
|
+
p2 <= 2147483639 &&
|
|
197
|
+
p1 <= 2147483639 &&
|
|
198
|
+
p <= 2147483639
|
|
199
|
+
) {
|
|
200
|
+
c = crc32Step(bt, c, checksumLoad64(buf, p))
|
|
201
|
+
c1 = crc32Step(bt, c1, checksumLoad64(buf, p1))
|
|
202
|
+
c2 = crc32Step(bt, c2, checksumLoad64(buf, p2))
|
|
203
|
+
c3 = crc32Step(bt, c3, checksumLoad64(buf, p3))
|
|
204
|
+
c4 = crc32Step(bt, c4, checksumLoad64(buf, p4))
|
|
205
|
+
p += CRC32_BLOCK
|
|
206
|
+
p1 += CRC32_BLOCK
|
|
207
|
+
p2 += CRC32_BLOCK
|
|
208
|
+
p3 += CRC32_BLOCK
|
|
209
|
+
p4 += CRC32_BLOCK
|
|
210
|
+
}
|
|
211
|
+
if (
|
|
212
|
+
p >= 0 &&
|
|
213
|
+
p1 >= 0 &&
|
|
214
|
+
p2 >= 0 &&
|
|
215
|
+
p3 >= 0 &&
|
|
216
|
+
p4 >= 0 &&
|
|
217
|
+
p4 <= 2147483639 &&
|
|
218
|
+
p3 <= 2147483639 &&
|
|
219
|
+
p2 <= 2147483639 &&
|
|
220
|
+
p1 <= 2147483639 &&
|
|
221
|
+
p <= 2147483639
|
|
222
|
+
) {
|
|
223
|
+
c = crc32Step(t, c, checksumLoad64(buf, p))
|
|
224
|
+
c = crc32Step(t, c ^ c1, checksumLoad64(buf, p1))
|
|
225
|
+
c = crc32Step(t, c ^ c2, checksumLoad64(buf, p2))
|
|
226
|
+
c = crc32Step(t, c ^ c3, checksumLoad64(buf, p3))
|
|
227
|
+
c = crc32Step(t, c ^ c4, checksumLoad64(buf, p4))
|
|
228
|
+
p += CRC32_BLOCK
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
const whole: i32 = end - (len & 7)
|
|
232
|
+
while (p < whole && p >= 0 && p <= 2147483639) {
|
|
233
|
+
c = crc32Step(t, c, checksumLoad64(buf, p))
|
|
234
|
+
p += 8
|
|
235
|
+
}
|
|
236
|
+
while (p < end) {
|
|
237
|
+
c = uncheckedGet(t, toI32((c ^ toU32(uncheckedGet(buf, p))) & 255)) ^ (c >>> 8)
|
|
238
|
+
p += 1
|
|
239
|
+
}
|
|
240
|
+
return ~c
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* The CRC-32 of `buf[off .. off + len)`. It builds a `Crc32` for the one
|
|
246
|
+
* call, which costs about as much as checksumming 5 KB: keep a `Crc32` for
|
|
247
|
+
* anything repeated. A window outside `buf` panics.
|
|
248
|
+
*/
|
|
249
|
+
export const crc32 = (buf: u8[], off: i32, len: i32): u32 => new Crc32().update(toU32(0), buf, off, len)
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* The Adler-32 of the bytes `adler` was the Adler-32 of, followed by
|
|
253
|
+
* `buf[off .. off + len)` (RFC 1950 §9). Start a new checksum from 1. A
|
|
254
|
+
* window outside `buf` panics.
|
|
255
|
+
*/
|
|
256
|
+
export const adler32Update = (adler: u32, buf: u8[], off: i32, len: i32): u32 => {
|
|
257
|
+
checksumWindow("adler32Update", buf, off, len)
|
|
258
|
+
let s1: u32 = adler & 0xffff
|
|
259
|
+
let s2: u32 = adler >>> 16
|
|
260
|
+
let p: i32 = off
|
|
261
|
+
const end: i32 = off + len
|
|
262
|
+
while (p < end) {
|
|
263
|
+
let stop: i32 = p + ADLER32_NMAX
|
|
264
|
+
if (stop > end) {
|
|
265
|
+
stop = end
|
|
266
|
+
}
|
|
267
|
+
while (p + 4 <= stop) {
|
|
268
|
+
s1 += toU32(uncheckedGet(buf, p))
|
|
269
|
+
s2 += s1
|
|
270
|
+
s1 += toU32(uncheckedGet(buf, p + 1))
|
|
271
|
+
s2 += s1
|
|
272
|
+
s1 += toU32(uncheckedGet(buf, p + 2))
|
|
273
|
+
s2 += s1
|
|
274
|
+
s1 += toU32(uncheckedGet(buf, p + 3))
|
|
275
|
+
s2 += s1
|
|
276
|
+
p += 4
|
|
277
|
+
}
|
|
278
|
+
while (p < stop) {
|
|
279
|
+
s1 += toU32(uncheckedGet(buf, p))
|
|
280
|
+
s2 += s1
|
|
281
|
+
p += 1
|
|
282
|
+
}
|
|
283
|
+
s1 = s1 % toU32(ADLER32_BASE)
|
|
284
|
+
s2 = s2 % toU32(ADLER32_BASE)
|
|
285
|
+
}
|
|
286
|
+
return (s2 << 16) | s1
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/** The Adler-32 of `buf[off .. off + len)`. A window outside `buf` panics. */
|
|
290
|
+
export const adler32 = (buf: u8[], off: i32, len: i32): u32 => adler32Update(toU32(1), buf, off, len)
|