@amritk/nish-x86_64-linux 0.16.0 → 0.18.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 (58) hide show
  1. package/INSTALL.md +43 -34
  2. package/bin/nish +0 -0
  3. package/package.json +1 -1
  4. package/runtime/nish.d.ts +117 -8
  5. package/runtime/nish.h +50 -13
  6. package/runtime/nish.mjs +71 -7
  7. package/runtime/runtime-host.c +43 -24
  8. package/runtime/runtime-net.c +57 -10
  9. package/runtime/runtime-os.c +62 -20
  10. package/runtime/runtime-simd.c +203 -0
  11. package/runtime/runtime-wasm.c +12 -0
  12. package/runtime/runtime.c +66 -18
  13. package/runtime/shim.mjs +114 -5
  14. package/scripts/build.sh +14 -12
  15. package/std/README.md +168 -36
  16. package/std/crypto/aes.ts +51 -8
  17. package/std/crypto/base64url.ts +25 -12
  18. package/std/crypto/chacha20poly1305.ts +43 -29
  19. package/std/crypto/ct.ts +6 -1
  20. package/std/crypto/hkdf.ts +364 -3
  21. package/std/crypto/hmac.ts +307 -7
  22. package/std/crypto/p256.ts +618 -34
  23. package/std/crypto/sha1.ts +147 -0
  24. package/std/crypto/sha256.ts +3 -2
  25. package/std/crypto/x25519.ts +88 -36
  26. package/std/crypto/x509.ts +130 -47
  27. package/std/json.ts +297 -41
  28. package/std/net/hpack.ts +1094 -0
  29. package/std/net/http-fields.ts +604 -0
  30. package/std/net/http1-server.ts +1389 -0
  31. package/std/net/http1.ts +1492 -0
  32. package/std/net/http2-frame.ts +745 -0
  33. package/std/net/http2-tls.ts +216 -0
  34. package/std/net/http2.ts +1433 -0
  35. package/std/net/http3-frame.ts +457 -0
  36. package/std/net/http3-server.ts +911 -0
  37. package/std/net/http3.ts +2145 -0
  38. package/std/net/qpack.ts +1163 -0
  39. package/std/net/quic-conn-ack.ts +206 -0
  40. package/std/net/quic-conn-cid.ts +425 -0
  41. package/std/net/quic-conn-params.ts +462 -0
  42. package/std/net/quic-datagram.ts +130 -0
  43. package/std/net/quic-frame.ts +1206 -0
  44. package/std/net/quic-listener.ts +929 -0
  45. package/std/net/quic-packet.ts +1698 -0
  46. package/std/net/quic-recovery.ts +849 -0
  47. package/std/net/quic-stream.ts +1609 -0
  48. package/std/net/quic.ts +3578 -0
  49. package/std/net/tls/codec.ts +1089 -0
  50. package/std/net/tls/record-server.ts +1173 -0
  51. package/std/net/tls/record.ts +621 -0
  52. package/std/net/tls/schedule.ts +429 -0
  53. package/std/net/tls-tcp.ts +414 -0
  54. package/std/net/tls.ts +1437 -0
  55. package/std/net/websocket.ts +823 -0
  56. package/std/net/webtransport.ts +1334 -0
  57. package/std/secret.ts +62 -0
  58. package/std/text.ts +77 -0
package/std/json.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * `std/json` — the value of one field of one flat JSON object.
2
+ * `std/json` — the value of a field, or of several, of one flat JSON object.
3
3
  *
4
4
  * It exists because the compiler's own machine-readable surface is JSON: under
5
5
  * `--json` every diagnostic is one flat object on a line of stdout, whose `code`
@@ -26,10 +26,10 @@
26
26
  * whether the whole line was well-formed is asking a question this does not
27
27
  * answer.
28
28
  *
29
- * There is one exported function on purpose. A `std/` module is compiled into
30
- * the program that imports it and the symbol namespace is flat
29
+ * There are two exported functions, and few on purpose. A `std/` module is
30
+ * compiled into the program that imports it and the symbol namespace is flat
31
31
  * (`std/README.md`), so every name here is a name its importer cannot use: the
32
- * helpers are `json`-prefixed and the reader is the only export. Splitting a
32
+ * helpers are `json`-prefixed and the readers are the only exports. Splitting a
33
33
  * stream into lines and picking the ones that start with `{` is two calls the
34
34
  * caller already has — `splitLines` from `std/text` and `startsWith` — and
35
35
  * duplicating them here would cost more names than it saves.
@@ -46,6 +46,16 @@
46
46
  * }
47
47
  * }
48
48
  *
49
+ * `jsonField` scans from the start of the line on every call, so a caller that
50
+ * wants three fields of each line reads each line three times. `jsonFields`
51
+ * reads it once: `jsonFields(line, ["code", "line", "message"])` answers an
52
+ * array whose slot `k` is exactly what `jsonField(line, names[k])` answers —
53
+ * absent, first-of-a-duplicate and malformed input included — because the two
54
+ * share the step that reads one member (`jsonReadMember`) and stop at the same
55
+ * one. Its array goes back to the caller with the values in it, so a loop that
56
+ * calls it keeps its per-pass arena release, as one calling `jsonField` does;
57
+ * `jsonFields`' own comment says when that holds.
58
+ *
49
59
  * Every offset here is a **byte** offset and every width is spelled, with each
50
60
  * length and byte a builtin answers read through `toI32` — `.length` and
51
61
  * `charCodeAt` answer `number`, which is `f64` under `--number-mode f64`, so the
@@ -53,14 +63,17 @@
53
63
  * (`docs/wp26-stdlib.md` §4).
54
64
  */
55
65
 
66
+ const JSON_BACKSPACE: i32 = 8
56
67
  const JSON_TAB: i32 = 9
57
68
  const JSON_NEWLINE: i32 = 10
69
+ const JSON_FORM_FEED: i32 = 12
58
70
  const JSON_CARRIAGE_RETURN: i32 = 13
59
71
  const JSON_SPACE: i32 = 32
60
72
  const JSON_QUOTE: i32 = 34
61
73
  const JSON_COMMA: i32 = 44
62
74
  const JSON_COLON: i32 = 58
63
75
  const JSON_BACKSLASH: i32 = 92
76
+ const JSON_LETTER_U: i32 = 117
64
77
  const JSON_OPEN_BRACKET: i32 = 91
65
78
  const JSON_CLOSE_BRACKET: i32 = 93
66
79
  const JSON_OPEN_BRACE: i32 = 123
@@ -261,6 +274,37 @@ const jsonUtf8 = (code: i32): string => {
261
274
  return `${lead}${middle}${String.fromCharCode(JSON_UTF8_CONTINUATION + (code & JSON_UTF8_LOW_SIX))}`
262
275
  }
263
276
 
277
+ /** The number of bytes `jsonUtf8(code)` answers. */
278
+ const jsonUtf8Width = (code: i32): i32 => {
279
+ if (code < JSON_UTF8_TWO_BYTE_FLOOR) {
280
+ return 1
281
+ }
282
+ if (code < JSON_UTF8_THREE_BYTE_FLOOR) {
283
+ return 2
284
+ }
285
+ return 3
286
+ }
287
+
288
+ /**
289
+ * Byte `k` of the `width` bytes `jsonUtf8(code)` answers: its arithmetic, one
290
+ * byte at a time, for a caller that compares the bytes rather than keeps them.
291
+ */
292
+ const jsonUtf8Byte = (code: i32, width: i32, k: i32): i32 => {
293
+ if (width === 1) {
294
+ return code
295
+ }
296
+ if (k === 0) {
297
+ if (width === 2) {
298
+ return JSON_UTF8_TWO_BYTE_LEAD + (code >> 6)
299
+ }
300
+ return JSON_UTF8_THREE_BYTE_LEAD + (code >> 12)
301
+ }
302
+ if (k === width - 1) {
303
+ return JSON_UTF8_CONTINUATION + (code & JSON_UTF8_LOW_SIX)
304
+ }
305
+ return JSON_UTF8_CONTINUATION + ((code >> 6) & JSON_UTF8_LOW_SIX)
306
+ }
307
+
264
308
  /**
265
309
  * The bytes of `text[at..end)` with the JSON escapes decoded — the body of a
266
310
  * string literal, without its quotes.
@@ -343,6 +387,190 @@ const jsonUnescape = (text: string, at: i32, end: i32): string => {
343
387
  return parts.join("")
344
388
  }
345
389
 
390
+ /** Whether byte `j` of `name` is `code`; an index past either end is not. */
391
+ const jsonNameHas = (name: string, j: i32, code: i32): boolean =>
392
+ j >= 0 && j < toI32(name.length) && toI32(name.charCodeAt(j)) === code
393
+
394
+ /**
395
+ * Whether `jsonUnescape(text, at, end)` would answer `name`, decided without
396
+ * building it.
397
+ *
398
+ * `jsonField` and `jsonFields` ask this of every key they walk past, and almost
399
+ * every answer is no, so the question is answered the way it is asked: the literal and `name`
400
+ * are walked together, each escape decoded where it stands, and the first byte
401
+ * that differs ends the walk. Every escape reads as `jsonUnescape` reads it —
402
+ * the short forms, a `\u` as the bytes `jsonUtf8` would build, a broken `\u`
403
+ * as the two bytes `\u`, a backslash with no letter after it as itself, and
404
+ * any other escape as the byte it escapes — so a key matches here exactly when
405
+ * its decoded text would.
406
+ */
407
+ const jsonKeyEquals = (text: string, at: i32, end: i32, name: string): boolean => {
408
+ // `jsonUnescape`'s guard, for the same reason: every caller passes the
409
+ // inside of a key `jsonReadMember` found with `jsonEndOfString`.
410
+ if (at < 0 || end > toI32(text.length)) {
411
+ return false
412
+ }
413
+ let i: i32 = at
414
+ let j: i32 = 0
415
+ while (i < end) {
416
+ const code: i32 = toI32(text.charCodeAt(i))
417
+ if (code !== JSON_BACKSLASH) {
418
+ if (!jsonNameHas(name, j, code)) {
419
+ return false
420
+ }
421
+ i += 1
422
+ j += 1
423
+ continue
424
+ }
425
+ const letter: i32 = i + 1 < end ? toI32(text.charCodeAt(i + 1)) : 0
426
+ if (letter === JSON_LETTER_U) {
427
+ const point: i32 = jsonHex4(text, i + 2, end)
428
+ if (point < 0) {
429
+ if (!jsonNameHas(name, j, JSON_BACKSLASH) || !jsonNameHas(name, j + 1, JSON_LETTER_U)) {
430
+ return false
431
+ }
432
+ i += 2
433
+ j += 2
434
+ continue
435
+ }
436
+ const width: i32 = jsonUtf8Width(point)
437
+ let k: i32 = 0
438
+ while (k < width) {
439
+ if (!jsonNameHas(name, j + k, jsonUtf8Byte(point, width, k))) {
440
+ return false
441
+ }
442
+ k += 1
443
+ }
444
+ i += 6
445
+ j += width
446
+ continue
447
+ }
448
+ // Every other escape decodes to one byte.
449
+ let decoded: i32 = letter
450
+ if (letter === 110) {
451
+ decoded = JSON_NEWLINE
452
+ } else if (letter === 116) {
453
+ decoded = JSON_TAB
454
+ } else if (letter === 114) {
455
+ decoded = JSON_CARRIAGE_RETURN
456
+ } else if (letter === 98) {
457
+ decoded = JSON_BACKSPACE
458
+ } else if (letter === 102) {
459
+ decoded = JSON_FORM_FEED
460
+ } else if (letter === 0) {
461
+ decoded = JSON_BACKSLASH
462
+ }
463
+ if (!jsonNameHas(name, j, decoded)) {
464
+ return false
465
+ }
466
+ // A backslash with no letter after it is one byte of the literal and every
467
+ // other escape two. The steps are constants rather than a variable because
468
+ // a cursor moved by a variable is one the bounds proof stops following.
469
+ if (letter === 0) {
470
+ i += 1
471
+ } else {
472
+ i += 2
473
+ }
474
+ j += 1
475
+ }
476
+ return j === toI32(name.length)
477
+ }
478
+
479
+ /**
480
+ * Where one member of an object lies, as `jsonReadMember` found it: the inside
481
+ * of its key literal, its value, and the opening quote of the key after it.
482
+ *
483
+ * It is a class so that one step can answer the four offsets together, and
484
+ * each reader allocates exactly one, which never leaves the reader: that is
485
+ * what keeps it a stack slot rather than an arena allocation.
486
+ */
487
+ class JsonMember {
488
+ keyAt: i32 = 0
489
+ keyEnd: i32 = 0
490
+ valueAt: i32 = 0
491
+ valueEnd: i32 = 0
492
+ /** The opening quote of the next key, or `-1` when no comma follows the value. */
493
+ next: i32 = -1
494
+ }
495
+
496
+ /**
497
+ * The opening quote of the first key of `object`, or `-1` when `object` does
498
+ * not start, past any blanks, with a `{`.
499
+ */
500
+ const jsonFirstKey = (object: string): i32 => {
501
+ const length: i32 = toI32(object.length)
502
+ const i: i32 = jsonSkipBlank(object, 0)
503
+ if (i >= length || toI32(object.charCodeAt(i)) !== JSON_OPEN_BRACE) {
504
+ return -1
505
+ }
506
+ return jsonSkipBlank(object, i + 1)
507
+ }
508
+
509
+ /**
510
+ * Reads the member whose key's opening quote is at `at` into `member`, and
511
+ * answers whether there was one.
512
+ *
513
+ * This is the per-key step `jsonField` and `jsonFields` share, so they cannot
514
+ * disagree about where a member ends: `false` for anything that is not a key,
515
+ * a colon and a whole value in that order, which is the point at which both
516
+ * stop. A value need not be followed by a comma — a truncated object still
517
+ * answers the fields before the cut — so a missing one only sets `next` to
518
+ * `-1`, and the read that follows answers `false`.
519
+ */
520
+ const jsonReadMember = (object: string, at: i32, member: JsonMember): boolean => {
521
+ const length: i32 = toI32(object.length)
522
+ // `jsonSkipBlank` never answers a negative index for a non-negative one, but
523
+ // the bounds proof does not look inside a callee, so every cursor it answers
524
+ // is tested for `i < 0` beside `i >= length`: the pair is what lets each read
525
+ // below go without a check.
526
+ if (at < 0 || at >= length || toI32(object.charCodeAt(at)) !== JSON_QUOTE) {
527
+ return false
528
+ }
529
+ const keyEnd: i32 = jsonEndOfString(object, at)
530
+ if (keyEnd < 0) {
531
+ return false
532
+ }
533
+ const colon: i32 = jsonSkipBlank(object, keyEnd)
534
+ if (colon < 0 || colon >= length || toI32(object.charCodeAt(colon)) !== JSON_COLON) {
535
+ return false
536
+ }
537
+ const valueAt: i32 = jsonSkipBlank(object, colon + 1)
538
+ const valueEnd: i32 = jsonEndOfValue(object, valueAt)
539
+ if (valueEnd < 0) {
540
+ return false
541
+ }
542
+ member.keyAt = at + 1
543
+ member.keyEnd = keyEnd - 1
544
+ member.valueAt = valueAt
545
+ member.valueEnd = valueEnd
546
+ const comma: i32 = jsonSkipBlank(object, valueEnd)
547
+ if (comma < 0 || comma >= length || toI32(object.charCodeAt(comma)) !== JSON_COMMA) {
548
+ member.next = -1
549
+ } else {
550
+ member.next = jsonSkipBlank(object, comma + 1)
551
+ }
552
+ return true
553
+ }
554
+
555
+ /**
556
+ * The text a member's value answers: a string unquoted and unescaped, anything
557
+ * else as the bytes it was written with.
558
+ */
559
+ const jsonValueText = (object: string, member: JsonMember): string => {
560
+ const valueAt: i32 = member.valueAt
561
+ const valueEnd: i32 = member.valueEnd
562
+ // `jsonReadMember` only fills in a value `jsonEndOfValue` ended, which starts
563
+ // inside the object and never ends past it, so this changes no answer: it is
564
+ // the range the value's first byte and its `substring` are proved in.
565
+ if (valueAt < 0 || valueAt >= valueEnd || valueEnd > toI32(object.length)) {
566
+ return ""
567
+ }
568
+ if (toI32(object.charCodeAt(valueAt)) === JSON_QUOTE) {
569
+ return jsonUnescape(object, valueAt + 1, valueEnd - 1)
570
+ }
571
+ return object.substring(valueAt, valueEnd)
572
+ }
573
+
346
574
  /**
347
575
  * The value of `name` in `object`, or `null` when the object does not have that
348
576
  * field.
@@ -358,45 +586,73 @@ const jsonUnescape = (text: string, at: i32, end: i32): string => {
358
586
  * and it is the shape the compiler's `--json` line has.
359
587
  */
360
588
  export const jsonField = (object: string, name: string): string | null => {
361
- const length: i32 = toI32(object.length)
362
- let i: i32 = jsonSkipBlank(object, 0)
363
- if (i >= length || toI32(object.charCodeAt(i)) !== JSON_OPEN_BRACE) {
364
- return null
365
- }
366
- i = jsonSkipBlank(object, i + 1)
367
- // `jsonSkipBlank` never answers a negative index for a non-negative one, but
368
- // the bounds proof does not look inside a callee, so every cursor it answers
369
- // is tested for `i < 0` beside `i >= length`: the pair is what lets each read
370
- // below go without a check.
371
- while (i >= 0 && i < length && toI32(object.charCodeAt(i)) === JSON_QUOTE) {
372
- const keyEnd: i32 = jsonEndOfString(object, i)
373
- if (keyEnd < 0) {
374
- return null
375
- }
376
- const key: string = jsonUnescape(object, i + 1, keyEnd - 1)
377
- i = jsonSkipBlank(object, keyEnd)
378
- if (i < 0 || i >= length || toI32(object.charCodeAt(i)) !== JSON_COLON) {
379
- return null
589
+ const member: JsonMember = new JsonMember()
590
+ let at: i32 = jsonFirstKey(object)
591
+ while (jsonReadMember(object, at, member)) {
592
+ // The key is compared where it stands rather than unescaped into a string
593
+ // first: every key before the one asked for is a no, and a no should not
594
+ // cost an allocation.
595
+ if (jsonKeyEquals(object, member.keyAt, member.keyEnd, name)) {
596
+ return jsonValueText(object, member)
380
597
  }
381
- const valueAt: i32 = jsonSkipBlank(object, i + 1)
382
- const valueEnd: i32 = jsonEndOfValue(object, valueAt)
383
- // `jsonEndOfValue` answers `-1` for a `valueAt` outside the object and never
384
- // answers past its end, so the last three tests change no answer: they are
385
- // the range the value's first byte and its `substring` are proved in.
386
- if (valueEnd < 0 || valueAt < 0 || valueAt >= length || valueEnd > toI32(object.length)) {
387
- return null
388
- }
389
- if (key === name) {
390
- if (toI32(object.charCodeAt(valueAt)) === JSON_QUOTE) {
391
- return jsonUnescape(object, valueAt + 1, valueEnd - 1)
598
+ at = member.next
599
+ }
600
+ return null
601
+ }
602
+
603
+ /**
604
+ * The value of every one of `names` in `object`, in the order of `names`: slot
605
+ * `k` is exactly what `jsonField(object, names[k])` answers.
606
+ *
607
+ * It is for a caller that wants several fields of one line — a diagnostic's
608
+ * `code`, `line` and `message` — and would otherwise scan the line once per
609
+ * field. This scans it once, left to right, and stops as soon as every name has
610
+ * answered: a speed path and nothing more, since a slot is filled only once
611
+ * and reading on could change no answer. Each slot keeps the **first** field of its name, as `jsonField`
612
+ * does; a name asked for twice answers in both slots; and a malformed object
613
+ * answers in each slot what `jsonField` answers for that name, because both
614
+ * stop at the same member.
615
+ *
616
+ * The answer is a fresh array and the only allocation besides the values. Each
617
+ * value is stored into it by a plain `values[k] = text` statement, and the
618
+ * array is only stored into, tested and returned, so the values travel with
619
+ * the array: the call lets nothing else out (LANGUAGE.md, "Memory model", a
620
+ * value stored into an array that is returned with it). A loop calling this
621
+ * keeps its per-pass release on a line older than the pass, as `jsonField`'s
622
+ * does, and a `using a = arena()` block may hold the call. On a line built in
623
+ * the pass neither reader keeps the release (LANGUAGE.md, "Memory model").
624
+ * Reading `values` back, passing it to a function, or storing it anywhere
625
+ * before the return would undo this; `tests/link/std_json` pins the flat
626
+ * arena and the `using` block.
627
+ */
628
+ export const jsonFields = (object: string, names: string[]): (string | null)[] => {
629
+ const count: i32 = toI32(names.length)
630
+ const values: (string | null)[] = new Array<string | null>(count)
631
+ const member: JsonMember = new JsonMember()
632
+ let missing: i32 = count
633
+ let at: i32 = jsonFirstKey(object)
634
+ while (missing > 0 && jsonReadMember(object, at, member)) {
635
+ // A value is built once per member however many names it answers, and
636
+ // only when one of them does.
637
+ let text: string | null = null
638
+ let k: i32 = 0
639
+ // Bounded by both lengths, which are the same, because the bounds proof
640
+ // follows each array by its own length and not by `count`.
641
+ while (k < toI32(values.length) && k < toI32(names.length)) {
642
+ if (values[k] === null && jsonKeyEquals(object, member.keyAt, member.keyEnd, names[k])) {
643
+ if (text === null) {
644
+ text = jsonValueText(object, member)
645
+ }
646
+ // Always true: the loop's own test. A call stands between it and the
647
+ // store, and a call is where the bounds proof forgets a length.
648
+ if (k < toI32(values.length)) {
649
+ values[k] = text
650
+ }
651
+ missing -= 1
392
652
  }
393
- return object.substring(valueAt, valueEnd)
394
- }
395
- i = jsonSkipBlank(object, valueEnd)
396
- if (i < 0 || i >= length || toI32(object.charCodeAt(i)) !== JSON_COMMA) {
397
- return null
653
+ k += 1
398
654
  }
399
- i = jsonSkipBlank(object, i + 1)
655
+ at = member.next
400
656
  }
401
- return null
657
+ return values
402
658
  }