@amritk/nish-x86_64-linux 0.27.2 → 0.29.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 (54) hide show
  1. package/INSTALL.md +16 -8
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +118 -26
  4. package/package.json +1 -1
  5. package/runtime/core/host.d.ts +2 -1
  6. package/runtime/core/host.mjs +4 -0
  7. package/runtime/core.d.ts +47 -0
  8. package/runtime/core.mjs +67 -0
  9. package/runtime/nish.d.ts +176 -0
  10. package/runtime/nish.h +120 -6
  11. package/runtime/nish.mjs +24 -4
  12. package/runtime/runtime-checksum.c +270 -0
  13. package/runtime/runtime-net.c +386 -5
  14. package/runtime/runtime-os.c +59 -8
  15. package/runtime/runtime-parallel.c +81 -5
  16. package/runtime/runtime-sandbox.c +479 -0
  17. package/runtime/runtime-sha256.c +239 -0
  18. package/runtime/runtime-shm.c +541 -0
  19. package/runtime/runtime.c +827 -791
  20. package/scripts/build.sh +79 -28
  21. package/std/README.md +25 -13
  22. package/std/collections.ts +5 -5
  23. package/std/compress/checksum.ts +88 -75
  24. package/std/compress/deflate.ts +224 -0
  25. package/std/compress/gzip.ts +52 -0
  26. package/std/crypto/bcrypt.ts +816 -280
  27. package/std/crypto/hmac.ts +305 -2
  28. package/std/crypto/jwt.ts +1661 -0
  29. package/std/json-equal.ts +335 -0
  30. package/std/json-format.ts +1070 -0
  31. package/std/json-number.ts +428 -0
  32. package/std/json-pointer.ts +245 -0
  33. package/std/json-typed.ts +34 -0
  34. package/std/json-value.ts +510 -17
  35. package/std/json.ts +35 -6
  36. package/std/net/handler.ts +1348 -0
  37. package/std/net/http-client.ts +97 -16
  38. package/std/net/http-compress.ts +301 -16
  39. package/std/net/http1-client.ts +104 -19
  40. package/std/net/http1-server.ts +127 -24
  41. package/std/net/http1.ts +24 -4
  42. package/std/net/outbound.ts +409 -0
  43. package/std/net/postgres.ts +405 -0
  44. package/std/net/prefork.ts +344 -20
  45. package/std/net/websocket.ts +295 -14
  46. package/std/regex.ts +613 -52
  47. package/std/secret.ts +1 -1
  48. package/std/sync/ring.ts +587 -0
  49. package/std/sync/shm.ts +284 -0
  50. package/std/term-io.ts +35 -8
  51. package/std/term-rate.ts +13 -1
  52. package/std/threads.ts +35 -3
  53. package/std/yaml-json.ts +841 -0
  54. /package/runtime/{LICENSE-ryu → LICENSE-zmij} +0 -0
package/runtime/nish.d.ts CHANGED
@@ -333,6 +333,14 @@ declare function spawnSyncTo(argv: string[], stdoutPath: string, stderrPath: str
333
333
  * the child is then killed and each array keeps at most the first 16 MiB.
334
334
  */
335
335
  declare function spawnPiped(argv: string[], stdin: readonly u8[], stdout: u8[], stderr: u8[]): i32;
336
+ /**
337
+ * `spawn` with the child holding `fds[i]` as descriptor `3 + i`, inherited
338
+ * across the exec; every other descriptor stays close-on-exec. The child's pid,
339
+ * or a negative errno: -22 more than 64 descriptors or a negative one, -9 one
340
+ * that is not open, -24 (or -22 above the descriptor limit) a scratch
341
+ * descriptor the system cannot give. No reading under Node, which throws.
342
+ */
343
+ declare function spawnWithFds(argv: string[], fds: readonly i32[]): i32;
336
344
  /**
337
345
  * Start `argv[0]` through `PATH` without waiting: the child's pid, or a
338
346
  * negative errno. The child is the program's until `waitPid` reaps it. No
@@ -460,6 +468,11 @@ declare function pollWait(loop: i32, ready: i32[], timeoutMs: i32): i32;
460
468
  * a negative errno (-9 a closed descriptor, -88 not a socket).
461
469
  */
462
470
  declare function netSetOption(fd: i32, option: i32, value: i32): i32;
471
+ /**
472
+ * A connected TCP socket's send MSS, the payload bytes one segment carries;
473
+ * -1, never an errno, for anything else, and always on wasm.
474
+ */
475
+ declare function netMss(fd: i32): i32;
463
476
  /** `TCP_NODELAY`: `value` 1 sends a small write at once, 0 lets Nagle batch it. */
464
477
  declare const NET_TCP_NODELAY: i32;
465
478
  /**
@@ -474,6 +487,57 @@ declare const NET_SO_KEEPALIVE: i32;
474
487
  declare const NET_SO_SNDBUF: i32;
475
488
  /** `SO_RCVBUF`: the receive buffer, `value` in bytes; Linux doubles the value it stores. */
476
489
  declare const NET_SO_RCVBUF: i32;
490
+ /**
491
+ * A listening Unix stream socket at `path` (`@name`: Linux's abstract
492
+ * namespace), its file mode 0600. A path that exists is -98, unless `flags`
493
+ * holds `NET_UNIX_UNLINK_STALE`; -36 a path too long, -22 an empty one.
494
+ */
495
+ declare function unixListen(path: string, backlog: i32, flags: i32): i32;
496
+ /** `unixListen` with the socket file's mode chosen (0 to 0o777); -22 for an `@name`. */
497
+ declare function unixListenMode(path: string, backlog: i32, flags: i32, mode: i32): i32;
498
+ /** A non-blocking socket connected to the listener at `path`; -11 a full backlog on Linux (-111 on Darwin), -111 nothing listening. */
499
+ declare function unixConnect(path: string): i32;
500
+ /** The next connection on a Unix listener, non-blocking; -11 when none is waiting. */
501
+ declare function unixAccept(fd: i32): i32;
502
+ /**
503
+ * Two connected sockets into `fds[0]` and `fds[1]`, and 0: `kind` is
504
+ * `NET_UNIX_STREAM` or `NET_UNIX_SEQPACKET` (-95 on Darwin); -22 another kind
505
+ * or an `fds` shorter than 2.
506
+ */
507
+ declare function unixSocketPair(kind: i32, fds: i32[]): i32;
508
+ /**
509
+ * Send `buf[off..off+len)` over the Unix socket `sock` with a copy of the
510
+ * descriptor `fd` (`SCM_RIGHTS`): the bytes sent, or a negative errno; -22 a
511
+ * zero `len` or a negative `fd`, -9 a closed `sock` or `fd`, -88 a `sock`
512
+ * that is not a socket, -95 one that is not Unix. A stream socket can send
513
+ * short: the descriptor travels with the first bytes, so send the rest with
514
+ * `netWrite`.
515
+ */
516
+ declare function sendFd(sock: i32, fd: i32, buf: readonly u8[], off: i32, len: i32): i32;
517
+ /**
518
+ * Read at most `len` bytes into `buf[off..]` from the Unix socket `sock`, and
519
+ * the first descriptor that came with them into `fd[0]`, or -1: the bytes read,
520
+ * or a negative errno. Every other descriptor in the message is closed; the
521
+ * one kept is close-on-exec and keeps the sender's blocking mode. -22 a zero
522
+ * `len` or an empty `fd`, -9 a closed `sock`, -88 not a socket, -95 not
523
+ * Unix.
524
+ */
525
+ declare function recvFd(sock: i32, buf: u8[], off: i32, len: i32, fd: i32[]): i32;
526
+ /**
527
+ * The peer of the Unix socket `fd` as of its connect: `out` gets its pid, uid
528
+ * and gid, and the answer is 0; the pid is -1 where Darwin has no
529
+ * `LOCAL_PEERPID`. A uid or gid of 2^31 or above wraps (-2 is uid
530
+ * 4294967294). -22 an `out` shorter than 3, -9 a closed `fd`, -88 not a
531
+ * socket, -95 not Unix, -107 a socket with no peer (a listener, or one never
532
+ * connected).
533
+ */
534
+ declare function peerCredentials(fd: i32, out: i32[]): i32;
535
+ /** `unixListen` flag: unlink a socket file nothing listens on, then bind. */
536
+ declare const NET_UNIX_UNLINK_STALE: i32;
537
+ /** `unixSocketPair` kind: a byte stream. */
538
+ declare const NET_UNIX_STREAM: i32;
539
+ /** `unixSocketPair` kind: each write one message (`SOCK_SEQPACKET`). */
540
+ declare const NET_UNIX_SEQPACKET: i32;
477
541
 
478
542
  // ---- `nish:term` ------------------------------------------------------------------
479
543
  //
@@ -580,6 +644,13 @@ declare module "nish:process" {
580
644
  export function spawnPiped(argv: string[], stdin: readonly u8[], stdout: u8[], stderr: u8[]): i32;
581
645
  /** Start a child without waiting: its pid, or a negative errno. */
582
646
  export function spawn(argv: string[]): i32;
647
+ /**
648
+ * `spawn` with the child holding `fds[i]` as descriptor `3 + i`: its pid, or
649
+ * a negative errno; -22 more than 64 descriptors or a negative one, -9 one
650
+ * that is not open, -24 (or -22 above the descriptor limit) a scratch
651
+ * descriptor the system cannot give.
652
+ */
653
+ export function spawnWithFds(argv: string[], fds: readonly i32[]): i32;
583
654
  /** Send 0, 2, 9 or 15 to `pid`: 0, or a negative errno; any other signal is -22. */
584
655
  export function kill(pid: i32, sig: i32): i32;
585
656
  /** `[pid, status]` for a child that exited, `[0, 0]` under WNOHANG (flag 1) with none, or `[-errno, 0]`. */
@@ -667,6 +738,11 @@ declare module "nish:net" {
667
738
  * a negative errno (-9 a closed descriptor, -88 not a socket).
668
739
  */
669
740
  export function netSetOption(fd: i32, option: i32, value: i32): i32;
741
+ /**
742
+ * A connected TCP socket's send MSS, the payload bytes one segment carries;
743
+ * -1, never an errno, for anything else, and always on wasm.
744
+ */
745
+ export function netMss(fd: i32): i32;
670
746
  /** `TCP_NODELAY`: `value` 1 sends a small write at once, 0 lets Nagle batch it. */
671
747
  export const NET_TCP_NODELAY: i32;
672
748
  /**
@@ -681,6 +757,57 @@ declare module "nish:net" {
681
757
  export const NET_SO_SNDBUF: i32;
682
758
  /** `SO_RCVBUF`: the receive buffer, `value` in bytes; Linux doubles the value it stores. */
683
759
  export const NET_SO_RCVBUF: i32;
760
+ /**
761
+ * A listening Unix stream socket at `path` (`@name`: Linux's abstract
762
+ * namespace), its file mode 0600. A path that exists is -98, unless `flags`
763
+ * holds `NET_UNIX_UNLINK_STALE`; -36 a path too long, -22 an empty one.
764
+ */
765
+ export function unixListen(path: string, backlog: i32, flags: i32): i32;
766
+ /** `unixListen` with the socket file's mode chosen (0 to 0o777); -22 for an `@name`. */
767
+ export function unixListenMode(path: string, backlog: i32, flags: i32, mode: i32): i32;
768
+ /** A non-blocking socket connected to the listener at `path`; -11 a full backlog on Linux (-111 on Darwin), -111 nothing listening. */
769
+ export function unixConnect(path: string): i32;
770
+ /** The next connection on a Unix listener, non-blocking; -11 when none is waiting. */
771
+ export function unixAccept(fd: i32): i32;
772
+ /**
773
+ * Two connected sockets into `fds[0]` and `fds[1]`, and 0: `kind` is
774
+ * `NET_UNIX_STREAM` or `NET_UNIX_SEQPACKET` (-95 on Darwin); -22 another kind
775
+ * or an `fds` shorter than 2.
776
+ */
777
+ export function unixSocketPair(kind: i32, fds: i32[]): i32;
778
+ /**
779
+ * Send `buf[off..off+len)` over the Unix socket `sock` with a copy of the
780
+ * descriptor `fd` (`SCM_RIGHTS`): the bytes sent, or a negative errno; -22 a
781
+ * zero `len` or a negative `fd`, -9 a closed `sock` or `fd`, -88 a `sock`
782
+ * that is not a socket, -95 one that is not Unix. A stream socket can send
783
+ * short: the descriptor travels with the first bytes, so send the rest with
784
+ * `netWrite`.
785
+ */
786
+ export function sendFd(sock: i32, fd: i32, buf: readonly u8[], off: i32, len: i32): i32;
787
+ /**
788
+ * Read at most `len` bytes into `buf[off..]` from the Unix socket `sock`, and
789
+ * the first descriptor that came with them into `fd[0]`, or -1: the bytes read,
790
+ * or a negative errno. Every other descriptor in the message is closed; the
791
+ * one kept is close-on-exec and keeps the sender's blocking mode. -22 a zero
792
+ * `len` or an empty `fd`, -9 a closed `sock`, -88 not a socket, -95 not
793
+ * Unix.
794
+ */
795
+ export function recvFd(sock: i32, buf: u8[], off: i32, len: i32, fd: i32[]): i32;
796
+ /**
797
+ * The peer of the Unix socket `fd` as of its connect: `out` gets its pid, uid
798
+ * and gid, and the answer is 0; the pid is -1 where Darwin has no
799
+ * `LOCAL_PEERPID`. A uid or gid of 2^31 or above wraps (-2 is uid
800
+ * 4294967294). -22 an `out` shorter than 3, -9 a closed `fd`, -88 not a
801
+ * socket, -95 not Unix, -107 a socket with no peer (a listener, or one never
802
+ * connected).
803
+ */
804
+ export function peerCredentials(fd: i32, out: i32[]): i32;
805
+ /** `unixListen` flag: unlink a socket file nothing listens on, then bind. */
806
+ export const NET_UNIX_UNLINK_STALE: i32;
807
+ /** `unixSocketPair` kind: a byte stream. */
808
+ export const NET_UNIX_STREAM: i32;
809
+ /** `unixSocketPair` kind: each write one message (`SOCK_SEQPACKET`). */
810
+ export const NET_UNIX_SEQPACKET: i32;
684
811
  }
685
812
 
686
813
  declare module "nish:unsafe" {
@@ -698,6 +825,55 @@ declare module "nish:unsafe" {
698
825
  export function readCString(p: CPtr): string;
699
826
  }
700
827
 
828
+ // `nish:simd` (docs/LANGUAGE.md -> Vectors): the six sixteen-byte vector
829
+ // types and the calls that make, load, store and take one apart. Under Node,
830
+ // `runtime/nish.mjs` answers the import with `runtime/core.mjs`'s names.
831
+ declare module "nish:simd" {
832
+ /**
833
+ * A sixteen-byte vector of lanes. Opaque: TypeScript refuses `+` on one, as
834
+ * `nish` does, but lets `===` and `<` through, which `nish` refuses. Under
835
+ * Node it is a typed array made once and never written after.
836
+ */
837
+ export interface u8x16 { readonly __nishSimd: "u8x16"; readonly __lane: u8 }
838
+ export interface u16x8 { readonly __nishSimd: "u16x8"; readonly __lane: u16 }
839
+ export interface i32x4 { readonly __nishSimd: "i32x4"; readonly __lane: i32 }
840
+ export interface u32x4 { readonly __nishSimd: "u32x4"; readonly __lane: u32 }
841
+ export interface f32x4 { readonly __nishSimd: "f32x4"; readonly __lane: f32 }
842
+ export interface f64x2 { readonly __nishSimd: "f64x2"; readonly __lane: f64 }
843
+ /** Any of the six. */
844
+ export type SimdVector = u8x16 | u16x8 | i32x4 | u32x4 | f32x4 | f64x2;
845
+ /** One value per lane, lane 0 first. */
846
+ export function u8x16(
847
+ a0: u8, a1: u8, a2: u8, a3: u8, a4: u8, a5: u8, a6: u8, a7: u8,
848
+ a8: u8, a9: u8, a10: u8, a11: u8, a12: u8, a13: u8, a14: u8, a15: u8
849
+ ): u8x16;
850
+ export function u16x8(a0: u16, a1: u16, a2: u16, a3: u16, a4: u16, a5: u16, a6: u16, a7: u16): u16x8;
851
+ export function i32x4(a0: i32, a1: i32, a2: i32, a3: i32): i32x4;
852
+ export function u32x4(a0: u32, a1: u32, a2: u32, a3: u32): u32x4;
853
+ export function f32x4(a0: f32, a1: f32, a2: f32, a3: f32): f32x4;
854
+ export function f64x2(a0: f64, a1: f64): f64x2;
855
+ /** `x` in every lane. */
856
+ export function splatU8x16(x: u8): u8x16;
857
+ export function splatU16x8(x: u16): u16x8;
858
+ export function splatI32x4(x: i32): i32x4;
859
+ export function splatU32x4(x: u32): u32x4;
860
+ export function splatF32x4(x: f32): f32x4;
861
+ export function splatF64x2(x: f64): f64x2;
862
+ /** `xs[i]` to `xs[i + N - 1]`, bounds-checked once for the whole vector. */
863
+ export function loadU8x16(xs: readonly u8[], i: i32): u8x16;
864
+ export function loadU16x8(xs: readonly u16[], i: i32): u16x8;
865
+ export function loadI32x4(xs: readonly i32[], i: i32): i32x4;
866
+ export function loadU32x4(xs: readonly u32[], i: i32): u32x4;
867
+ export function loadF32x4(xs: readonly f32[], i: i32): f32x4;
868
+ export function loadF64x2(xs: readonly f64[], i: i32): f64x2;
869
+ /** The lanes of `v` into `xs[i]` to `xs[i + N - 1]`, checked once. */
870
+ export function store<V extends SimdVector>(v: V, xs: V["__lane"][], i: i32): void;
871
+ /** Lane `k`, an integer literal in range. */
872
+ export function extract<V extends SimdVector>(v: V, k: i32): V["__lane"];
873
+ /** A new vector: `v` with lane `k` set to `x`. */
874
+ export function replace<V extends SimdVector>(v: V, k: i32, x: V["__lane"]): V;
875
+ }
876
+
701
877
  declare module "nish:io" {
702
878
  /** `s` to stdout with no trailing newline and no conversion. */
703
879
  export function write(s: string): void;
package/runtime/nish.h CHANGED
@@ -9,14 +9,17 @@
9
9
  * functions, the directory and subprocess calls, `nish_getenv`, the monotonic
10
10
  * clock, `nish_platform` / `nish_arch` — and runtime-parallel.c, runtime-host.c,
11
11
  * runtime-net.c, runtime-simd.c (the byte searches that read a vector at a
12
- * time), runtime-aes.c (AES-GCM on the CPU's AES instructions) and
13
- * runtime-term.c (stdin and the terminal, `nish:term`) split off
12
+ * time), runtime-aes.c (AES-GCM on the CPU's AES instructions),
13
+ * runtime-sha256.c (SHA-256 on the CPU's SHA instructions), runtime-checksum.c
14
+ * (CRC-32 on its carry-less multiply or CRC instructions) and runtime-term.c
15
+ * (stdin and the terminal, `nish:term`) split off
14
16
  * the same way since. They are separate so that each carries its own measured
15
17
  * code-size ceiling (docs/wp7-runtime.md, "Runtime additions and budget"); a
16
18
  * host links all of them, which `scripts/build.sh` does for you when handed
17
- * runtime.c, runtime-aes.c included: the compiler calls its kernels from
18
- * std/crypto/aes.ts, so `scripts/build.sh` pairs it and `src/run-cache.ts`
19
- * caches it like the others. `tests/run.js` checks that every runtime function
19
+ * runtime.c, runtime-aes.c, runtime-sha256.c and runtime-checksum.c included: the
20
+ * compiler calls their kernels from std/crypto/aes.ts, std/crypto/sha256.ts and
21
+ * std/compress/checksum.ts, so `scripts/build.sh` pairs each of them with
22
+ * runtime.c and `src/run-cache.ts` caches them like the others. `tests/run.js` checks that every runtime function
20
23
  * declared in runtime.ts has a prototype in this file.
21
24
  *
22
25
  * Headers generated by `nish --emit-header` include this one, so keep
@@ -197,6 +200,16 @@ extern void (*nish_fatal_hook)(void);
197
200
  /* Number to string, as `${n}` does: decimal for integers, shortest round-trip (JS Number#toString) for f64. */
198
201
  nish_str *nish_str_from_i32(int32_t v);
199
202
  nish_str *nish_str_from_f64(double v);
203
+ /* The bytes `nish_str_from_f64` would hold, written into `out` with no NUL;
204
+ returns the length. 32 bytes is room to spare: the longest spelling,
205
+ -0.0000012345678901234567, is 25 (runtime.c says why, and runtime-test.c
206
+ pins it). For a caller that writes numbers into its own buffer. */
207
+ #define NISH_F64_MAX 32
208
+ int nish_f64_format(double v, char out[NISH_F64_MAX]);
209
+ /* `nish_f64_format`'s bytes into `buf[at]` (std/json.ts's JsonWriter): the new
210
+ end, or -1, with nothing written, when `at < 0` or they do not fit before
211
+ `len`. `buf` may be null only when nothing fits. */
212
+ int32_t nish_json_put_f64(uint8_t *buf, int32_t len, int32_t at, double v);
200
213
  nish_str *nish_str_from_i64(int64_t v);
201
214
  /* Unsigned decimal (WP15). u8/u16/u32 are zero-extended by the caller, so one
202
215
  symbol serves every unsigned width. */
@@ -348,6 +361,40 @@ void nish_aes_ctr(const nish_array *rk, int32_t rounds, const nish_array *j0, co
348
361
  `rk` instead, and they are the same H. 0 <= length <= data.len. */
349
362
  void nish_aes_ghash(nish_array *y, uint64_t hHi, uint64_t hLo, const nish_array *rk, const nish_array *data,
350
363
  int32_t length);
364
+ /* ---- SHA-256 on the CPU's SHA instructions --------------------------------
365
+ * runtime/runtime-sha256.c: SHA-NI on x86-64, the ARMv8 SHA-256 instructions
366
+ * on AArch64. The compiler calls the kernel in place of std/crypto/sha256.ts's
367
+ * private `sha256Compress`, behind `nish_sha256_hw`, and calls the Nish
368
+ * function itself when either answers 0. Constant time in the state and the
369
+ * block. */
370
+ /* 1 when this process runs the kernel, 0 when it runs the Nish code. Resolved
371
+ on the first call and fixed from then on: `NISH_SHA256=soft` pins 0; unset,
372
+ `hw` or any other value is the best this machine has, read on that call from
373
+ CPUID (leaf 7's SHA, leaf 1's SSE4.1 and SSSE3), AArch64 Linux's AT_HWCAP
374
+ (SHA2) or macOS's `hw.optional.arm.FEAT_SHA256`. Always 0 on other targets,
375
+ wasm32 among them. */
376
+ int32_t nish_sha256_hw(void);
377
+ /* FIPS 180-4 §6.2.2 on the 64 bytes at src[at..at + 64) (u8[]), into the hash
378
+ value h[0..8) (u32[]); 1 when it ran. Answers 0 and writes nothing unless
379
+ h.len >= 8, w.len >= 64 and 0 <= at <= src.len - 64, `sha256Compress`'s
380
+ guard, so the caller's fallback panics as the Nish function does. `w` (u32[],
381
+ the message schedule the Nish code fills) is neither read nor written. */
382
+ int32_t nish_sha256_compress(nish_array *h, const nish_array *w, const nish_array *src, int32_t at);
383
+ /* ---- CRC-32 on the CPU's carry-less multiply or CRC instructions ---------
384
+ * runtime/runtime-checksum.c: PCLMULQDQ folding on x86-64, the ARMv8 CRC32
385
+ * instructions on AArch64. The compiler calls the kernel in
386
+ * place of std/compress/checksum.ts's private `crc32Walk`, behind
387
+ * `nish_crc32_hw`, and calls the Nish walker itself when that answers 0. */
388
+ /* 1 when this process runs the kernel, 0 when it runs the Nish tables.
389
+ Resolved on the first call and fixed from then on: `NISH_CRC32=soft` pins 0;
390
+ unset or any other value is the best this machine has, read on that call
391
+ from CPUID leaf 1 (PCLMULQDQ, SSE4.1), AArch64's AT_HWCAP (CRC32) or
392
+ Darwin's hw.optional sysctls. Always 0 on other targets, wasm32 among them. */
393
+ int32_t nish_crc32_hw(void);
394
+ /* The CRC-32 (ISO 3309, RFC 1952) `crc` extended over buf[off..off+len)
395
+ (u8[]), conditioned as zlib's crc32(): 0 starts a message, and the answer
396
+ continues one. The caller has checked 0 <= off <= off + len <= buf.len. */
397
+ uint32_t nish_crc32_update(uint32_t crc, const nish_array *buf, int32_t off, int32_t len);
351
398
 
352
399
  /* `secureZero(bytes)` (#385): sets every byte of `bytes` to zero through
353
400
  * `volatile` stores, out of line, so that neither `-O2` nor `-flto` can drop
@@ -477,6 +524,14 @@ int32_t nish_spawn_child(const nish_array *argv);
477
524
  * copied; SIGPIPE is blocked on the calling thread for the call, a SIGPIPE the
478
525
  * call raised is consumed, and the thread's mask is restored after it. */
479
526
  int32_t nish_spawn_piped(const nish_array *argv, const nish_array *in, nish_array *out, nish_array *err);
527
+ /* `spawnWithFds(argv, fds)`: `nish_spawn_child` with the child holding
528
+ * `fds[i]` (element type `int32_t`) as descriptor `3 + i`, close-on-exec
529
+ * cleared there. More than 64 descriptors or a negative one is -EINVAL before
530
+ * anything starts, a source that is not open is -EBADF, and a scratch
531
+ * descriptor the system cannot give is -EMFILE (-EINVAL above the descriptor
532
+ * limit); -ENOSYS under
533
+ * WASI. This process's descriptors are left as they were. */
534
+ int32_t nish_spawn_with_fds(const nish_array *argv, const nish_array *fds);
480
535
  /* `kill(pid, sig)`: send `sig` to `pid` and answer 0, or a negative errno. Only
481
536
  * 0, 2 (SIGINT), 9 (SIGKILL) and 15 (SIGTERM) are sent; any other signal, and
482
537
  * a pid of 0 or below, is -EINVAL. -ESRCH when no process has the pid, -EPERM
@@ -614,6 +669,9 @@ bool nish_is_executable(const nish_str *path);
614
669
  * whitelist, by the runtime's own id (1 `TCP_NODELAY`, 2 `TCP_QUICKACK`, 3
615
670
  * `SO_KEEPALIVE`, 4 `SO_SNDBUF`, 5 `SO_RCVBUF`); any other id is -22, and
616
671
  * `TCP_QUICKACK` off Linux is -95.
672
+ * `nish_net_mss(fd)`: a connected TCP socket's send MSS, from `TCP_INFO`'s
673
+ * `tcpi_snd_mss` or else `TCP_MAXSEG`; a plain -1, never an errno, for
674
+ * anything else (a closed descriptor, an unconnected or non-TCP socket).
617
675
  * `nish_tcp_connect(addr)`: a new socket connecting to `addr`, answered at
618
676
  * once; its `EINPROGRESS` is not a failure, and any other closes it.
619
677
  * `nish_connect_result(fd)`: `SO_ERROR` once the socket is writable, 0 when
@@ -638,7 +696,36 @@ bool nish_is_executable(const nish_str *path);
638
696
  * into `ready` (element type `int32_t`) a token and its events, 4 added for a
639
697
  * hang-up or an error, per ready descriptor: at most `ready->len / 2` pairs
640
698
  * and at most 64 in one call. It answers the count, 0 on a timeout or an
641
- * interrupting signal, and -22 for a `ready` shorter than 2. */
699
+ * interrupting signal, and -22 for a `ready` shorter than 2.
700
+ *
701
+ * Unix-domain sockets. A `path` names a socket file, or with a leading `@`
702
+ * Linux's abstract namespace (-95 on Darwin); empty, holding a NUL, or a bare
703
+ * `@` is -22, and one too long for `sun_path` with its NUL is -36, before any
704
+ * socket exists. `nish_unix_listen(path, backlog, flags)` is a listening
705
+ * stream socket whose file has mode 0600; a path that exists is -98, unless
706
+ * flag 1 (`NET_UNIX_UNLINK_STALE`) unlinks a socket file nothing listens on
707
+ * (anything but a socket is -17, a live listener -98). `nish_unix_listen_mode`
708
+ * takes the mode (0..0o777, -22 for an abstract name). `nish_unix_connect(path)`
709
+ * connects, -11 for a full backlog on Linux (-111 on Darwin). `nish_unix_accept(fd)` takes the next
710
+ * connection, -11 when none waits. `nish_unix_socket_pair(kind, fds)` writes
711
+ * two connected sockets into `fds` (element type `int32_t`, at least 2): kind
712
+ * 1 a stream, kind 2 `SOCK_SEQPACKET` (-95 on Darwin). Every descriptor is
713
+ * non-blocking and close-on-exec.
714
+ *
715
+ * Descriptors over a Unix socket: each of the three refuses a descriptor that
716
+ * is closed with -9, one that is not a socket with -88 and a socket of another
717
+ * family with -95.
718
+ * `nish_send_fd(sock, fd, buf, off, len)` sends `len` bytes (at least 1, else
719
+ * -22, as is a negative `fd`) carrying one `SCM_RIGHTS` copy of `fd`; a
720
+ * stream socket can send short, the descriptor gone with the first bytes. `nish_recv_fd(sock, buf, off,
721
+ * len, fd)` reads at most `len` bytes (at least 1, and `fd` at least one
722
+ * `int32_t` long, else -22) and writes the first descriptor received, or -1,
723
+ * into `fd[0]`, closing every other; the one kept is close-on-exec and its
724
+ * status flags are left as the sender's. `nish_peer_credentials(fd, out)`
725
+ * writes the peer's pid, uid and gid as of the connect into `out` (at least
726
+ * 3, else -22), wrapping a uid or gid of 2^31 or above into `int32_t`; a
727
+ * socket with no peer is -ENOTCONN (-107) on both platforms, and Darwin's pid
728
+ * is -1 where `LOCAL_PEERPID` is missing. */
642
729
  int32_t nish_net_address(nish_array *out, const nish_str *host, int32_t port);
643
730
  int32_t nish_net_local_port(int32_t fd);
644
731
  int32_t nish_tcp_listen(const nish_str *host, int32_t port, int32_t backlog);
@@ -649,6 +736,7 @@ int32_t nish_net_write(int32_t fd, const nish_array *buf, int64_t off, int64_t l
649
736
  int32_t nish_net_shutdown(int32_t fd, int32_t how);
650
737
  int32_t nish_net_close(int32_t fd);
651
738
  int32_t nish_net_set_option(int32_t fd, int32_t option, int32_t value);
739
+ int32_t nish_net_mss(int32_t fd);
652
740
  int32_t nish_tcp_connect(const nish_array *addr);
653
741
  int32_t nish_connect_result(int32_t fd);
654
742
  int32_t nish_udp_bind(const nish_str *host, int32_t port, int32_t flags);
@@ -661,6 +749,14 @@ int32_t nish_poll_add(int32_t loop, int32_t fd, int32_t events, int32_t token);
661
749
  int32_t nish_poll_modify(int32_t loop, int32_t fd, int32_t events, int32_t token);
662
750
  int32_t nish_poll_remove(int32_t loop, int32_t fd);
663
751
  int32_t nish_poll_wait(int32_t loop, nish_array *ready, int32_t timeout_ms);
752
+ int32_t nish_unix_listen(const nish_str *path, int32_t backlog, int32_t flags);
753
+ int32_t nish_unix_listen_mode(const nish_str *path, int32_t backlog, int32_t flags, int32_t mode);
754
+ int32_t nish_unix_connect(const nish_str *path);
755
+ int32_t nish_unix_accept(int32_t fd);
756
+ int32_t nish_unix_socket_pair(int32_t kind, nish_array *fds);
757
+ int32_t nish_send_fd(int32_t sock, int32_t fd, const nish_array *buf, int64_t off, int64_t len);
758
+ int32_t nish_recv_fd(int32_t sock, nish_array *buf, int64_t off, int64_t len, nish_array *fd);
759
+ int32_t nish_peer_credentials(int32_t fd, nish_array *out);
664
760
 
665
761
  /* ---- The terminal, runtime/runtime-term.c, `nish:term` -------------------
666
762
  *
@@ -741,6 +837,17 @@ int64_t nish_cpu_count(void);
741
837
  * rounded up, and `NISH_NUM_THREADS` when it is set to a positive number. At
742
838
  * least 1; 1 under WASI. Read each call (runtime-os.c). */
743
839
  int64_t nish_cpu_online(void);
840
+ /* WP37 `--sandbox` (runtime-sandbox.c): `nish_sandbox` confines the process to
841
+ the capability mask `caps` from `.preinit_array`, `mode` 1 strict and 2
842
+ best-effort, and `nish_sandbox_check` refuses to run at the top of `main`
843
+ when it never ran. `read_dirs` and `write_dirs` are the directories
844
+ Landlock scopes `fs.read` and `fs.write` to, `exec_paths` the programs
845
+ `process.spawn` may execute, each a run of NUL-terminated paths ended by an
846
+ empty one; null leaves the capability whole.
847
+ `nish_cpu_prime` caches the pool's CPU count while /sys is still readable. */
848
+ void nish_sandbox(int32_t caps, int32_t mode, const char *read_dirs, const char *write_dirs, const char *exec_paths);
849
+ void nish_sandbox_check(void);
850
+ void nish_cpu_prime(void);
744
851
  void nish_parallel_range(nish_par_body body, void *ctx, int64_t len, int64_t grain);
745
852
  /* Keeps every later region and join on threads created and joined per call,
746
853
  * as before the pool existed, so no parked worker outlives the code it would
@@ -766,6 +873,13 @@ __attribute__((constructor)) static void nish_napi_unpooled(void) { nish_pool_di
766
873
  * calling thread, so running short of threads costs speed and never an
767
874
  * answer.
768
875
  *
876
+ * `scope` points at a `ThreadScope` (std/threads.ts), whose first `int32_t` is
877
+ * the stack `scopeWithStack` asked for, or 0. A nonzero one runs every task on
878
+ * a new thread of its own started with that stack, rounded up to the page,
879
+ * plus a guard page, none on the calling thread; if one cannot be started the
880
+ * program ends, saying whether the system was out of threads or memory or
881
+ * refused the size (`nish_stack_fail`). Without threads the word is ignored.
882
+ *
769
883
  * The preconditions are the language's (docs/LANGUAGE.md, "Scoped tasks"):
770
884
  * `run` writes nothing another task or the caller can see, its result is a
771
885
  * scalar it writes into its own payload, and only `finish`, on the calling
package/runtime/nish.mjs CHANGED
@@ -104,9 +104,11 @@ provide("realpathSync", shim.realpathSync);
104
104
  provide("spawnSync", shim.spawnSync);
105
105
  provide("spawnSyncTo", shim.spawnSyncTo);
106
106
  provide("spawnPiped", shim.spawnPiped);
107
- // WP34 N12. `spawn` and `waitPid` throw, naming themselves: a child is reaped
108
- // by Node's event loop. `kill`, `availableParallelism` and `totalMemory` answer.
107
+ // WP34 N12. `spawn`, `spawnWithFds` and `waitPid` throw, naming themselves: a
108
+ // child is reaped by Node's event loop. `kill`, `availableParallelism` and
109
+ // `totalMemory` answer.
109
110
  provide("spawn", shim.spawn);
111
+ provide("spawnWithFds", shim.spawnWithFds);
110
112
  provide("kill", shim.kill);
111
113
  provide("waitPid", shim.waitPid);
112
114
  provide("availableParallelism", shim.availableParallelism);
@@ -147,16 +149,28 @@ for (const name of [
147
149
  "pollRemove",
148
150
  "pollWait",
149
151
  "netSetOption",
152
+ "netMss",
153
+ "unixListen",
154
+ "unixListenMode",
155
+ "unixConnect",
156
+ "unixAccept",
157
+ "unixSocketPair",
158
+ "sendFd",
159
+ "recvFd",
160
+ "peerCredentials",
150
161
  ]) {
151
162
  provide(name, () => shim.noNetReading(name));
152
163
  }
153
- // `netSetOption`'s option ids are plain numbers, so they read the same here
154
- // as natively: only the call that takes one throws.
164
+ // `netSetOption`'s option ids and the `NET_UNIX_` flag and kinds are plain
165
+ // numbers, so they read the same here as natively: only a call throws.
155
166
  provide("NET_TCP_NODELAY", 1);
156
167
  provide("NET_TCP_QUICKACK", 2);
157
168
  provide("NET_SO_KEEPALIVE", 3);
158
169
  provide("NET_SO_SNDBUF", 4);
159
170
  provide("NET_SO_RCVBUF", 5);
171
+ provide("NET_UNIX_UNLINK_STALE", 1);
172
+ provide("NET_UNIX_STREAM", 1);
173
+ provide("NET_UNIX_SEQPACKET", 2);
160
174
 
161
175
  // `nish:term`. Whether a descriptor is a terminal and how big stdout's is have
162
176
  // Node's answers; reading stdin, waiting for it and raw mode throw, each naming
@@ -314,6 +328,9 @@ provide("arena", shim.arena);
314
328
  // zero-fills it. `nish:unsafe` and `nish:term` are each answered by a module
315
329
  // that hands back the globals installed above: its functions are globals of this module either
316
330
  // way, and the import is what the compiler requires a program to write.
331
+ // `nish:simd` is `runtime/core.mjs`, whose vectors are typed arrays made once
332
+ // and never written after, so `--emit-ts` output and a program run as it was
333
+ // written share one implementation.
317
334
  const UNSAFE_EXPORTS = ["uncheckedGet", "uncheckedSet", "wrappingAdd", "wrappingSub", "wrappingMul"];
318
335
  const unsafeSource = UNSAFE_EXPORTS.map((name) => `export const ${name} = globalThis.${name};`).join("\n");
319
336
  const TERM_EXPORTS = ["readStdin", "waitStdin", "isTerminal", "terminalSize", "setRawMode", "setTerminalRestore"];
@@ -326,6 +343,9 @@ registerHooks({
326
343
  if (specifier === "nish:unsafe") {
327
344
  return { url: `data:text/javascript,${encodeURIComponent(unsafeSource)}`, shortCircuit: true };
328
345
  }
346
+ if (specifier === "nish:simd") {
347
+ return next(new URL("./core.mjs", import.meta.url).href, context);
348
+ }
329
349
  if (specifier === "nish:term") {
330
350
  return { url: `data:text/javascript,${encodeURIComponent(termSource)}`, shortCircuit: true };
331
351
  }