@amritk/nish-x86_64-linux 0.20.0 → 0.22.1

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 (74) hide show
  1. package/INSTALL.md +52 -10
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +2777 -0
  4. package/package.json +2 -1
  5. package/runtime/browser.d.ts +6 -0
  6. package/runtime/browser.mjs +77 -0
  7. package/runtime/core/arrays.d.ts +36 -0
  8. package/runtime/core/arrays.mjs +153 -0
  9. package/runtime/core/checks.mjs +20 -0
  10. package/runtime/core/host.d.ts +80 -0
  11. package/runtime/core/host.mjs +325 -0
  12. package/runtime/core/numbers.d.ts +66 -0
  13. package/runtime/core/numbers.mjs +343 -0
  14. package/runtime/core/result.d.ts +45 -0
  15. package/runtime/core/result.mjs +140 -0
  16. package/runtime/core/strings.d.ts +14 -0
  17. package/runtime/core/strings.mjs +169 -0
  18. package/runtime/core.d.ts +9 -0
  19. package/runtime/core.mjs +55 -0
  20. package/runtime/host.d.ts +6 -0
  21. package/runtime/nish.d.ts +46 -2
  22. package/runtime/nish.h +75 -2
  23. package/runtime/nish.mjs +23 -3
  24. package/runtime/node.mjs +386 -0
  25. package/runtime/runtime-bytes.c +401 -0
  26. package/runtime/runtime-cross.c +1 -1
  27. package/runtime/runtime-host.c +1 -1
  28. package/runtime/runtime-net.c +12 -1
  29. package/runtime/runtime-os.c +1 -1
  30. package/runtime/runtime-parallel.c +1 -1
  31. package/runtime/runtime-simd.c +231 -7
  32. package/runtime/runtime-term.c +504 -0
  33. package/runtime/runtime-wasm.c +0 -11
  34. package/runtime/runtime.c +190 -6
  35. package/runtime/shim.mjs +6 -985
  36. package/runtime/sqlite/LICENSE.md +85 -0
  37. package/runtime/sqlite/README.md +45 -0
  38. package/runtime/sqlite/nish-sqlite.c +359 -0
  39. package/runtime/sqlite/sqlite3.c +271131 -0
  40. package/runtime/sqlite/sqlite3.h +14447 -0
  41. package/scripts/build.sh +311 -59
  42. package/std/LICENSE-fast-float +23 -0
  43. package/std/LICENSE-fdlibm +8 -0
  44. package/std/README.md +32 -12
  45. package/std/base64.ts +116 -0
  46. package/std/bytes.ts +256 -0
  47. package/std/cli.ts +344 -0
  48. package/std/collections.ts +6 -5
  49. package/std/crypto/LICENSE-crypt_blowfish +26 -0
  50. package/std/crypto/base64url.ts +125 -0
  51. package/std/crypto/bcrypt.ts +1557 -0
  52. package/std/crypto/hmac.ts +138 -2
  53. package/std/crypto/x509.ts +11 -122
  54. package/std/json-tokenizer.ts +2998 -57
  55. package/std/json-value.ts +22 -3
  56. package/std/json.ts +87 -8
  57. package/std/math.ts +608 -0
  58. package/std/net/http1-server.ts +282 -40
  59. package/std/net/http1.ts +40 -21
  60. package/std/net/http3-server.ts +50 -12
  61. package/std/net/postgres.ts +2294 -0
  62. package/std/net/prefork.ts +23 -3
  63. package/std/net/quic-listener.ts +20 -4
  64. package/std/random.ts +66 -4
  65. package/std/regex.ts +4274 -0
  66. package/std/sqlite.ts +391 -0
  67. package/std/term-io.ts +978 -0
  68. package/std/term-pixels.ts +874 -0
  69. package/std/term-probe.ts +459 -0
  70. package/std/term-rate.ts +993 -0
  71. package/std/term.ts +2453 -0
  72. package/std/text.ts +90 -7
  73. package/std/yaml-lines.ts +141 -0
  74. package/std/yaml.ts +4675 -0
package/std/net/http1.ts CHANGED
@@ -80,6 +80,7 @@
80
80
  *
81
81
  * Written from RFC 9112 and RFC 9110, not ported from another implementation.
82
82
  */
83
+ import { copyBytes } from "nish/bytes"
83
84
 
84
85
  /** `next()`: the input so far holds no further event; feed more. */
85
86
  export const HTTP1_NEED_MORE: i32 = 0
@@ -234,11 +235,15 @@ export class Http1Parser {
234
235
  this.maxHeaderBytes = http1Clamp(maxHeaderBytes)
235
236
  this.maxHeaders = http1Clamp(maxHeaders)
236
237
  this.maxBody = http1Clamp(maxBody)
237
- this.buf = new Array<u8>(1024)
238
+ // The input and the head are made by the first byte fed and the first
239
+ // head kept, at HTTP1_FIRST_INPUT and HTTP1_FIRST_HEAD bytes: a parser
240
+ // that is built and never fed (a pool's spare slot) costs neither, and a
241
+ // caller that lends them out between requests puts empty ones here.
242
+ this.buf = []
238
243
  this.body = this.buf
239
244
  this.names = []
240
245
  this.values = []
241
- this.head = new Array<u8>(256)
246
+ this.head = []
242
247
  this.spans = new Array<i32>(64)
243
248
  this.tally = new Http1ListTally()
244
249
  this.restart()
@@ -261,19 +266,9 @@ export class Http1Parser {
261
266
  http1Reserve(this, len)
262
267
  const buf: u8[] = this.buf
263
268
  const at: i32 = this.end
264
- // A whole array is one `set`, a memmove; a window is copied byte by byte,
265
- // because `set` takes no window of its source.
266
- if (off === 0 && len === size) {
267
- buf.set(data, at)
268
- this.end = at + len
269
- return
270
- }
271
- for (let k: i32 = 0; k < len; k += 1) {
272
- const to: i32 = at + k
273
- if (to >= 0 && to < toI32(buf.length)) {
274
- buf[to] = data[off + k]
275
- }
276
- }
269
+ // One move: http1Reserve made room for `len` bytes at `at`, and the
270
+ // window was checked above.
271
+ copyBytes(buf, at, data, off, len)
277
272
  this.end = at + len
278
273
  }
279
274
 
@@ -359,6 +354,16 @@ export class Http1Parser {
359
354
  return http1HeadIs(this.head, spans[4 * found + 2], spans[4 * found + 3], protocol, true)
360
355
  }
361
356
 
357
+ /**
358
+ * Whether the parser waits for a request line with nothing fed and unread,
359
+ * so its input and head buffers hold nothing it needs: a caller may lend
360
+ * them out and leave empty arrays in `buf`, `body` and `head`, which the
361
+ * next feed and head make or the caller puts back first.
362
+ */
363
+ resting(): boolean {
364
+ return this.state === HTTP1_S_REQUEST_LINE && this.end === this.start
365
+ }
366
+
362
367
  /** How many bytes were fed and not consumed yet: what a caller bounds its reads by. */
363
368
  buffered(): i32 {
364
369
  return this.end - this.start
@@ -410,6 +415,11 @@ export class Http1Parser {
410
415
  }
411
416
  }
412
417
 
418
+ /** The input buffer a parser makes on its first feed, the smallest it grows from. */
419
+ export const HTTP1_FIRST_INPUT: i32 = 1024
420
+ /** The head buffer a parser makes for its first head. */
421
+ export const HTTP1_FIRST_HEAD: i32 = 256
422
+
413
423
  /** Makes room for `len` more bytes at `p.end`, moving the live bytes down first. */
414
424
  const http1Reserve = (p: Http1Parser, len: i32): void => {
415
425
  const old: u8[] = p.buf
@@ -418,7 +428,7 @@ const http1Reserve = (p: Http1Parser, len: i32): void => {
418
428
  return
419
429
  }
420
430
  const live: i32 = p.end - p.start
421
- let size: i32 = capacity
431
+ let size: i32 = capacity < HTTP1_FIRST_INPUT ? HTTP1_FIRST_INPUT : capacity
422
432
  while (size - live < len) {
423
433
  if (size > 1073741823) {
424
434
  panic("Http1Parser: more than 2^31 - 1 bytes buffered")
@@ -429,8 +439,9 @@ const http1Reserve = (p: Http1Parser, len: i32): void => {
429
439
  // the live bytes move to the front, and every index into them moves too.
430
440
  const buf: u8[] = size === capacity ? old : new Array<u8>(size)
431
441
  const from: i32 = p.start
432
- for (let k: i32 = 0; k < live && k < toI32(buf.length); k += 1) {
433
- buf[k] = old[from + k]
442
+ if (live > 0) {
443
+ // One move, overlapping when the buffer is the same one.
444
+ copyBytes(buf, 0, old, from, live)
434
445
  }
435
446
  p.buf = buf
436
447
  p.lineStart -= from
@@ -601,6 +612,9 @@ const http1HeadAppend = (p: Http1Parser, from: i32, to: i32): i32 => {
601
612
  const at: i32 = p.headLength
602
613
  let size: i32 = toI32(p.head.length)
603
614
  if (n > size - at) {
615
+ if (size < HTTP1_FIRST_HEAD) {
616
+ size = HTTP1_FIRST_HEAD
617
+ }
604
618
  while (size - at < n) {
605
619
  if (size > 1073741823) {
606
620
  panic("Http1Parser: a head of more than 2^31 - 1 bytes")
@@ -616,9 +630,14 @@ const http1HeadAppend = (p: Http1Parser, from: i32, to: i32): i32 => {
616
630
  }
617
631
  const head: u8[] = p.head
618
632
  const buf: u8[] = p.buf
619
- for (let k: i32 = 0; k < n && at + k < toI32(head.length) && from + k < toI32(buf.length); k += 1) {
620
- if (from + k >= 0) {
621
- head[at + k] = buf[from + k]
633
+ if (n > 0 && from >= 0 && from <= toI32(buf.length) - n && at <= toI32(head.length) - n) {
634
+ // The line lies in `buf` and the head was grown to hold it: one move.
635
+ copyBytes(head, at, buf, from, n)
636
+ } else {
637
+ for (let k: i32 = 0; k < n && at + k < toI32(head.length) && from + k < toI32(buf.length); k += 1) {
638
+ if (from + k >= 0) {
639
+ head[at + k] = buf[from + k]
640
+ }
622
641
  }
623
642
  }
624
643
  p.headLength = at + n
@@ -41,6 +41,14 @@
41
41
  * **Sending.** `flush` writes each touched slot's datagrams with
42
42
  * `quicListenerTakeFlight`, a paced flight at a time in one `udpSendTo` with
43
43
  * GSO where the platform has it and one datagram at a time where it does not.
44
+ * Each flight goes with the ECN codepoint its connection marked it with
45
+ * (`QuicFlight.ecn`, RFC 9000 §13.4), so the peer's ACK_ECN counts are
46
+ * validated and a CE mark shrinks the window. Where the platform sends no
47
+ * ECN (Darwin, whose `udpSendTo` answers -95 to a marked send), the first
48
+ * marked send goes again unmarked and no slot marks after it. A -95 is also
49
+ * what a platform without segmentation offload answers, so a refused GSO
50
+ * send goes again one datagram at a time, still marked, and only a -95 to
51
+ * one of those turns marking off.
44
52
  * The pacer counts the microseconds `clock` gives it, so on a path whose
45
53
  * round trip is under a millisecond it neither waits for the next
46
54
  * millisecond to earn credit nor holds a slot back for a whole one; without
@@ -122,8 +130,8 @@ export const H3_SERVER_ANSWER_INTERVAL: i64 = 100
122
130
  /** The address form `nish:net` reads and writes. */
123
131
  const H3_SERVER_ADDRESS: i32 = 18
124
132
 
125
- /** What `udpSendTo` answers where the platform has no segmentation offload. */
126
- const H3_SERVER_NO_GSO: i32 = -95
133
+ /** What `udpSendTo` answers where the platform has no segmentation offload, or sends no ECN. */
134
+ const H3_SERVER_UNSUPPORTED: i32 = -95
127
135
 
128
136
  /** The largest datagram read: a GRO receive of up to 64 KB. */
129
137
  const H3_SERVER_RECEIVE_SIZE: i32 = 65536
@@ -484,6 +492,8 @@ export class Http3Server {
484
492
  refused: i32 = 0
485
493
  limited: i32 = 0
486
494
  sendErrors: i32 = 0
495
+ /** Whether the platform sends ECN marks: true until a marked send answers -95, and every slot's `ecnMarking` with it. */
496
+ ecn: boolean = true
487
497
  /**
488
498
  * The stream buffers every slot's connection borrows from, made by the
489
499
  * constructor for the stream table `quicConfig` gives every slot: `null`
@@ -578,6 +588,7 @@ export class Http3Server {
578
588
  build(): void {
579
589
  crypto.getRandomValues(this.entropy)
580
590
  const quic = new QuicConnection(this.quicConfig, this.entropy)
591
+ quic.ecnMarking = this.ecn
581
592
  this.quics.push(quic)
582
593
  const pools: QuicStreamPools | null = this.streamPools
583
594
  if (pools !== null && pools.data.size === quic.streams.bufferSize && quic.streams.slots.length > 0) {
@@ -751,7 +762,7 @@ export class Http3Server {
751
762
  if (this.listener.handleWindow(buf, at, len, address, now, answer) !== QUIC_LISTEN_ACCEPT) {
752
763
  const reply: i32 = toI32(answer.reply.length)
753
764
  if (reply > 0) {
754
- this.send(answer.reply, H3_SERVER_ZERO, reply, address, H3_SERVER_ZERO)
765
+ this.send(answer.reply, H3_SERVER_ZERO, reply, address, H3_SERVER_ZERO, H3_SERVER_ZERO)
755
766
  }
756
767
  return
757
768
  }
@@ -904,21 +915,40 @@ export class Http3Server {
904
915
  }
905
916
  }
906
917
 
907
- /** Sends `buf[at .. at + len)` to `to`, cut into `segment`-byte datagrams when it is above 0; answers whether it went. */
908
- send(buf: u8[], at: i32, len: i32, to: u8[], segment: i32): boolean {
909
- const sent: i32 = udpSendTo(this.fd, buf, at, len, to, segment, H3_SERVER_ZERO)
918
+ /**
919
+ * Sends `buf[at .. at + len)` to `to` with the ECN codepoint `ecn`, cut
920
+ * into `segment`-byte datagrams when it is above 0; answers whether it went.
921
+ */
922
+ send(buf: u8[], at: i32, len: i32, to: u8[], segment: i32, ecn: i32): boolean {
923
+ const sent: i32 = udpSendTo(this.fd, buf, at, len, to, segment, ecn)
910
924
  if (sent >= 0) {
911
925
  return true
912
926
  }
913
- if (sent !== H3_SERVER_NO_GSO || segment <= 0) {
927
+ if (sent !== H3_SERVER_UNSUPPORTED || (segment <= 0 && ecn === 0)) {
914
928
  this.sendErrors = h3Count(this.sendErrors)
915
929
  return false
916
930
  }
917
- // No segmentation offload here: one datagram at a time.
931
+ // -95: no segmentation offload, or no ECN, or both. One datagram at a
932
+ // time, still marked: a -95 to a datagram with no segment is the mark's.
933
+ let mark: i32 = ecn
934
+ if (segment <= 0) {
935
+ this.unmark()
936
+ mark = H3_SERVER_ZERO
937
+ }
938
+ const step: i32 = segment > 0 ? segment : len
918
939
  let p: i32 = at
919
940
  while (p < at + len) {
920
- const n: i32 = at + len - p < segment ? at + len - p : segment
921
- if (udpSendTo(this.fd, buf, p, n, to, H3_SERVER_ZERO, H3_SERVER_ZERO) < 0) {
941
+ const n: i32 = at + len - p < step ? at + len - p : step
942
+ let one: i32 = udpSendTo(this.fd, buf, p, n, to, H3_SERVER_ZERO, mark)
943
+ if (one === H3_SERVER_UNSUPPORTED && mark !== 0) {
944
+ // No ECN here: this datagram and the rest go unmarked and no slot
945
+ // marks again. The packets just counted as marked fail their
946
+ // connection's validation.
947
+ this.unmark()
948
+ mark = H3_SERVER_ZERO
949
+ one = udpSendTo(this.fd, buf, p, n, to, H3_SERVER_ZERO, mark)
950
+ }
951
+ if (one < 0) {
922
952
  this.sendErrors = h3Count(this.sendErrors)
923
953
  return false
924
954
  }
@@ -927,6 +957,14 @@ export class Http3Server {
927
957
  return true
928
958
  }
929
959
 
960
+ /** The platform sends no ECN: no slot's connection marks from now on. */
961
+ unmark(): void {
962
+ this.ecn = false
963
+ for (let slot: i32 = 0; slot < toI32(this.quics.length); slot++) {
964
+ this.quics[slot].ecnMarking = false
965
+ }
966
+ }
967
+
930
968
  /**
931
969
  * Sends what every touched slot has, as far as its pacer lets it, closes a
932
970
  * slot whose HTTP/3 side is done after GOAWAY, frees a slot whose
@@ -972,7 +1010,7 @@ export class Http3Server {
972
1010
  break
973
1011
  }
974
1012
  const segment: i32 = this.flight.count > 1 ? this.flight.segment : H3_SERVER_ZERO
975
- if (!this.send(this.tx, H3_SERVER_ZERO, n, to, segment)) {
1013
+ if (!this.send(this.tx, H3_SERVER_ZERO, n, to, segment, this.flight.ecn)) {
976
1014
  break
977
1015
  }
978
1016
  retried = false
@@ -981,7 +1019,7 @@ export class Http3Server {
981
1019
  // The CONNECTION_CLOSE goes whatever the pacer says: nothing follows it.
982
1020
  const n: i32 = quic.takeDatagramInto(this.tx, H3_SERVER_ZERO, now)
983
1021
  if (n > 0) {
984
- this.send(this.tx, H3_SERVER_ZERO, n, to, H3_SERVER_ZERO)
1022
+ this.send(this.tx, H3_SERVER_ZERO, n, to, H3_SERVER_ZERO, quic.datagramEcn)
985
1023
  }
986
1024
  this.release(slot)
987
1025
  return