@amritk/nish-x86_64-linux 0.17.0 → 0.20.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 (61) hide show
  1. package/INSTALL.md +43 -25
  2. package/bin/nish +0 -0
  3. package/package.json +2 -2
  4. package/runtime/nish.d.ts +55 -5
  5. package/runtime/nish.h +231 -20
  6. package/runtime/nish.mjs +16 -0
  7. package/runtime/runtime-aes.c +490 -0
  8. package/runtime/runtime-cross.c +366 -0
  9. package/runtime/runtime-host.c +7 -6
  10. package/runtime/runtime-net.c +93 -34
  11. package/runtime/runtime-os.c +213 -15
  12. package/runtime/runtime-parallel.c +590 -107
  13. package/runtime/runtime-simd.c +203 -0
  14. package/runtime/runtime-wasm.c +16 -3
  15. package/runtime/runtime.c +89 -23
  16. package/runtime/shim.mjs +71 -1
  17. package/scripts/build.sh +225 -20
  18. package/std/README.md +86 -55
  19. package/std/bytes.ts +163 -0
  20. package/std/crypto/LICENSE-libsecp256k1 +19 -0
  21. package/std/crypto/aes.ts +238 -76
  22. package/std/crypto/chacha20poly1305.ts +43 -9
  23. package/std/crypto/ed25519.ts +840 -0
  24. package/std/crypto/hkdf.ts +304 -12
  25. package/std/crypto/hmac.ts +245 -53
  26. package/std/crypto/p256.ts +1376 -42
  27. package/std/crypto/x25519.ts +55 -37
  28. package/std/crypto/x509-random.ts +71 -0
  29. package/std/crypto/x509.ts +9 -9
  30. package/std/json-tokenizer.ts +1296 -0
  31. package/std/json-typed.ts +1014 -0
  32. package/std/json-value.ts +1163 -0
  33. package/std/json.ts +1412 -70
  34. package/std/net/buffer-pool.ts +151 -0
  35. package/std/net/hpack.ts +416 -112
  36. package/std/net/http-fields.ts +93 -0
  37. package/std/net/http1-server.ts +417 -55
  38. package/std/net/http1.ts +31 -6
  39. package/std/net/http2-tls.ts +112 -24
  40. package/std/net/http2.ts +333 -75
  41. package/std/net/http3-server.ts +231 -45
  42. package/std/net/http3.ts +187 -28
  43. package/std/net/prefork.ts +412 -0
  44. package/std/net/quic-conn-params.ts +86 -16
  45. package/std/net/quic-listener.ts +576 -219
  46. package/std/net/quic-packet.ts +443 -58
  47. package/std/net/quic-recovery.ts +361 -30
  48. package/std/net/quic-stream.ts +434 -28
  49. package/std/net/quic.ts +922 -206
  50. package/std/net/router.ts +676 -0
  51. package/std/net/tls/codec.ts +426 -27
  52. package/std/net/tls/record-server.ts +478 -132
  53. package/std/net/tls/record.ts +133 -42
  54. package/std/net/tls/schedule.ts +464 -55
  55. package/std/net/tls/ticket.ts +610 -0
  56. package/std/net/tls-tcp.ts +204 -57
  57. package/std/net/tls.ts +1132 -305
  58. package/std/net/url.ts +571 -0
  59. package/std/random.ts +94 -0
  60. package/std/text.ts +77 -0
  61. package/std/threads.ts +438 -14
@@ -11,8 +11,8 @@
11
11
  * wrapper raised the ceiling over the arena and the string functions as well,
12
12
  * and the number a reader saw for "the runtime" moved for a reason that had
13
13
  * nothing to do with it. Each file now carries its own measured ceiling in
14
- * tests/run.js; docs/wp7-runtime.md §"Runtime additions and budget" has both
15
- * rows and the reasoning.
14
+ * tests/runtime-budgets.js; docs/wp7-runtime.md §"Runtime additions and budget"
15
+ * has both rows and the reasoning.
16
16
  *
17
17
  * What a linked binary pays is unchanged: `-ffunction-sections
18
18
  * -Wl,--gc-sections` drops every function no program calls, whichever
@@ -25,6 +25,9 @@
25
25
  * compiling against the header a C host sees is what keeps a definition here
26
26
  * from drifting from the contract it is published under.
27
27
  */
28
+ #if defined(__linux__) && !defined(_GNU_SOURCE)
29
+ #define _GNU_SOURCE /* `sched_getaffinity` and `CPU_COUNT`, for `nish_cpu_online` */
30
+ #endif
28
31
  #define _POSIX_C_SOURCE 200809L
29
32
  /* `realpath` is XSI rather than POSIX base, so _POSIX_C_SOURCE alone does not
30
33
  declare it (measured on glibc 2.39: undeclared, and clang then infers
@@ -46,10 +49,14 @@
46
49
  #include <unistd.h>
47
50
  #ifndef __wasi__
48
51
  /* WASI has no processes, so `spawn.h` and `wait.h` do not exist there. */
52
+ #include <signal.h>
49
53
  #include <spawn.h>
50
54
  #include <sys/wait.h>
51
55
  extern char **environ;
52
56
  #endif
57
+ #ifdef __linux__
58
+ #include <sched.h>
59
+ #endif
53
60
 
54
61
  #include "nish.h"
55
62
 
@@ -73,6 +80,7 @@ extern char **environ;
73
80
  /* ---- Process and files */
74
81
  void nish_exit(int32_t code) { exit(code); }
75
82
  static NISH_COLD void nish_io_fail(const char *what, const nish_str *path) {
83
+ nish_panic_claim();
76
84
  dprintf(2, "nish: cannot %s%.*s\n", what, (int)path->len, path->data);
77
85
  _exit(1);
78
86
  }
@@ -215,18 +223,20 @@ _Bool nish_mkdir(const nish_str *path) {
215
223
  value rather than through a child that has already run.
216
224
 
217
225
  `out` and `err` are paths for the child's stdout and stderr, or NULL to let
218
- it inherit this process's. Both spawn builtins are this function with those
219
- two arguments answered differently, so the argv vector, the wait and the
220
- signal convention are written once; it is `static`, so a program that spawns
221
- nothing still loses all three to `--gc-sections`. */
222
- static int32_t nish_spawn_impl(const nish_array *argv, const nish_str *out, const nish_str *err) {
226
+ it inherit this process's. Every spawn builtin starts its child here, so the
227
+ argv vector and the redirects are written once; it is `static`, so a program
228
+ that spawns nothing loses it to `--gc-sections`. The answer is the child's
229
+ pid, or a negative errno: -EINVAL for an empty argv or an argument holding a
230
+ NUL, and `posix_spawnp`'s own error otherwise (-ENOENT for a command that is
231
+ not on PATH, on glibc). */
232
+ static int32_t nish_spawn_start(const nish_array *argv, const nish_str *out, const nish_str *err) {
223
233
  #ifdef __wasi__
224
234
  (void)argv;
225
235
  (void)out;
226
236
  (void)err;
227
- return -1;
237
+ return -ENOSYS;
228
238
  #else
229
- if (!argv->len) return -1; /* argv[0] would read past an empty array */
239
+ if (!argv->len) return -EINVAL; /* argv[0] would read past an empty array */
230
240
  nish_str **s = (nish_str **)argv->data;
231
241
  /* NULL-terminated, borrowing each string's bytes; the arena owns it, so no
232
242
  path out of here has anything to free. */
@@ -237,14 +247,15 @@ static int32_t nish_spawn_impl(const nish_array *argv, const nish_str *out, cons
237
247
  the one whose C string is not its own bytes. */
238
248
  for (; i < argv->len; i++) {
239
249
  v[i] = (char *)nish_cpath(s[i]);
240
- if (v[i] != s[i]->data) return -1;
250
+ if (v[i] != s[i]->data) return -EINVAL;
241
251
  }
242
252
  v[i] = 0;
243
253
  /* One list of file actions whether or not a stream is redirected: an empty
244
254
  list is the same spawn as none, and one path through is less code than
245
255
  creating the list only when a stream needs it. */
246
256
  posix_spawn_file_actions_t fa;
247
- if (posix_spawn_file_actions_init(&fa)) return -1;
257
+ int failed = posix_spawn_file_actions_init(&fa);
258
+ if (failed) return -failed;
248
259
  const nish_str *to[2] = { out, err };
249
260
  for (int k = 0; k < 2; k++) {
250
261
  /* The child opens the file, not this process: a redirect that this process
@@ -253,12 +264,31 @@ static int32_t nish_spawn_impl(const nish_array *argv, const nish_str *out, cons
253
264
  if (to[k]) posix_spawn_file_actions_addopen(&fa, k + 1, nish_cpath(to[k]), O_WRONLY | O_CREAT | O_TRUNC | O_NOFOLLOW, 0644);
254
265
  }
255
266
  pid_t pid;
256
- int status;
257
- int failed = posix_spawnp(&pid, v[0], &fa, 0, v, environ);
267
+ failed = posix_spawnp(&pid, v[0], &fa, 0, v, environ);
258
268
  posix_spawn_file_actions_destroy(&fa);
259
- if (failed) return -1;
260
- if (waitpid(pid, &status, 0) < 0) return -1;
269
+ return failed ? -failed : (int32_t)pid;
270
+ #endif
271
+ }
272
+
273
+ #ifndef __wasi__
274
+ /* A wait status in spawnSync's convention: the exit code, or 128 plus the
275
+ signal that ended the child, as a shell reports it. */
276
+ static int32_t nish_exit_code(int status) {
261
277
  return WIFSIGNALED(status) ? 128 + WTERMSIG(status) : WEXITSTATUS(status);
278
+ }
279
+ #endif
280
+
281
+ /* The two blocking builtins: start, then wait for that one child. A failed
282
+ start is -1, the answer they have always given. */
283
+ static int32_t nish_spawn_impl(const nish_array *argv, const nish_str *out, const nish_str *err) {
284
+ int32_t pid = nish_spawn_start(argv, out, err);
285
+ if (pid < 0) return -1;
286
+ #ifdef __wasi__
287
+ return -1;
288
+ #else
289
+ int status;
290
+ if (waitpid(pid, &status, 0) < 0) return -1;
291
+ return nish_exit_code(status);
262
292
  #endif
263
293
  }
264
294
 
@@ -279,6 +309,174 @@ int32_t nish_spawn_to(const nish_array *argv, const nish_str *out, const nish_st
279
309
  return nish_spawn_impl(argv, out->len ? out : 0, err->len ? err : 0);
280
310
  }
281
311
 
312
+ /* `spawn(argv)`: the child's pid, or a negative errno, without waiting. The
313
+ child is this process's until a `waitPid` reaps it; one never reaped stays
314
+ a zombie until the program exits, as it would in C. */
315
+ int32_t nish_spawn_child(const nish_array *argv) { return nish_spawn_start(argv, 0, 0); }
316
+
317
+ /* `kill(pid, sig)`: 0, or a negative errno. Only the four signals a program
318
+ asks a child to stop or report with are sent -- 0 (is it there), SIGINT,
319
+ SIGKILL and SIGTERM, whose numbers are the same on Linux and Darwin -- and
320
+ any other is -EINVAL, so the number a program writes means the same signal
321
+ on every host. A pid of 0 or below is -EINVAL too: `kill(2)` reads those as
322
+ a process group or every process the user owns, which is not one process.
323
+ A positive pid may be any process of this uid, not only a child: the same
324
+ reach `kill(1)` has (docs/security/runtime.md, RT-19). */
325
+ int32_t nish_kill(int32_t pid, int32_t sig) {
326
+ #ifdef __wasi__
327
+ (void)pid;
328
+ (void)sig;
329
+ return -ENOSYS;
330
+ #else
331
+ if (pid <= 0 || !(sig == 0 || sig == SIGINT || sig == SIGKILL || sig == SIGTERM)) return -EINVAL;
332
+ return kill(pid, sig) == 0 ? 0 : -errno;
333
+ #endif
334
+ }
335
+
336
+ /* `waitPid(pid, flags)`: `[pid, status]`, a fresh two-element `i32[]`. `pid`
337
+ is a child's or -1 for any child; flag 1 is WNOHANG, and any other bit, like
338
+ a pid of 0 or below -1 (a process group), is -EINVAL. The status is
339
+ spawnSync's: the exit code, or 128 plus the signal. `[0, 0]` is a WNOHANG
340
+ wait with a child left and none exited, and a failure is `[-errno, 0]`:
341
+ -ECHILD with no child to wait for, -EINTR when a signal interrupted it. */
342
+ nish_array *nish_wait_pid(int32_t pid, int32_t flags) {
343
+ nish_array *out = nish_alloc_array(sizeof(int32_t), 2);
344
+ int32_t *w = (int32_t *)out->data;
345
+ w[1] = 0;
346
+ #ifdef __wasi__
347
+ (void)pid;
348
+ (void)flags;
349
+ w[0] = -ENOSYS;
350
+ #else
351
+ if ((pid <= 0 && pid != -1) || (flags & ~1)) {
352
+ w[0] = -EINVAL;
353
+ return out;
354
+ }
355
+ int status = 0;
356
+ pid_t got = waitpid(pid, &status, flags ? WNOHANG : 0);
357
+ w[0] = got < 0 ? -errno : (int32_t)got;
358
+ if (got > 0) w[1] = nish_exit_code(status);
359
+ #endif
360
+ return out;
361
+ }
362
+
363
+ #ifdef __linux__
364
+ /* Reads up to `cap - 1` bytes of a small file into `buf`, terminated; 0 bytes
365
+ * when it cannot be opened. */
366
+ static void nish_read_small(const char *path, char *buf, size_t cap) {
367
+ ssize_t got = -1;
368
+ int fd = open(path, O_RDONLY | O_CLOEXEC);
369
+ if (fd >= 0) {
370
+ got = read(fd, buf, cap - 1);
371
+ close(fd);
372
+ }
373
+ buf[got > 0 ? got : 0] = 0;
374
+ }
375
+
376
+ /* The cgroup v2 CPU quota of this process and of every cgroup above it, as a
377
+ * whole number of CPUs rounded up, and `n` when it is lower or there is none.
378
+ * A container limited to 1.5 CPUs on a 64-core host is what this is for: its
379
+ * affinity mask still names all 64, and 63 workers time-sliced into a budget
380
+ * of 1.5 cores is the slowest way there is to run a loop. `cpu.max` reads
381
+ * `<quota> <period>`, or `max <period>` when there is no limit, which the
382
+ * digit test leaves out. The path is the `0::` line of /proc/self/cgroup under
383
+ * /sys/fs/cgroup, where a v2-only system and a container mount the hierarchy;
384
+ * a v1 system has no `cpu.max` there and keeps `n`. */
385
+ static int64_t nish_cgroup_cpus(int64_t n) {
386
+ char text[512];
387
+ char path[512] = "/sys/fs/cgroup";
388
+ nish_read_small("/proc/self/cgroup", text, sizeof text);
389
+ char *at = strstr(text, "0::/");
390
+ if (!at || (at != text && at[-1] != '\n')) return n;
391
+ size_t len = strcspn(at + 3, "\n");
392
+ if (len > sizeof path - 24) return n;
393
+ memcpy(path + 14, at + 3, len);
394
+ for (;;) {
395
+ memcpy(path + 14 + len, "/cpu.max", 9);
396
+ nish_read_small(path, text, sizeof text);
397
+ if (text[0] >= '1' && text[0] <= '9') {
398
+ char *rest;
399
+ int64_t quota = strtoll(text, &rest, 10);
400
+ int64_t period = strtoll(rest, 0, 10);
401
+ if (period > 0) {
402
+ int64_t cpus = quota / period + (quota % period != 0);
403
+ if (cpus < n) n = cpus;
404
+ }
405
+ }
406
+ /* Up one level, by dropping the last component, until the root is read. */
407
+ if (len <= 1) return n;
408
+ while (len > 1 && path[14 + len - 1] != '/') len--;
409
+ if (len > 1) len--;
410
+ }
411
+ }
412
+ #endif
413
+
414
+ #ifdef __APPLE__
415
+ /* <sys/sysctl.h> does not compile at the strict feature level this file
416
+ selects, so the one function is declared by hand, as Darwin's libc has it. */
417
+ int sysctlbyname(const char *, void *, size_t *, void *, size_t);
418
+ #endif
419
+
420
+ /* The CPUs this program may use: the smallest of the CPUs online, the ones
421
+ this thread may run on (`taskset`, a container's cpuset), the cgroup quota,
422
+ and `NISH_NUM_THREADS` when it is a positive number. The variable can lower
423
+ the count and never raise it, so it caps a program's threads (a CI job
424
+ sharing its machine, a benchmark pinned to one core) without ever putting
425
+ more of them on the machine than it has. At least 1, and 1 on WASI.
426
+
427
+ It lives here rather than in runtime-parallel.c because two builtins read
428
+ it: `availableParallelism`, in every build, and `nish_cpu_count`, which
429
+ divides regions by it under `--threads` and answers 1 without, as a
430
+ sequential build divides nothing. Uncached: runtime-parallel.c caches its
431
+ own answer, and a program asking how many workers to start asks once. */
432
+ int64_t nish_cpu_online(void) {
433
+ #ifdef __wasi__
434
+ return 1;
435
+ #else
436
+ #ifdef __APPLE__
437
+ /* `_SC_NPROCESSORS_ONLN` is hidden on Darwin at this file's feature level. */
438
+ int got = 0;
439
+ size_t size = sizeof got;
440
+ if (sysctlbyname("hw.activecpu", &got, &size, 0, 0) != 0) got = 0;
441
+ #else
442
+ long got = sysconf(_SC_NPROCESSORS_ONLN);
443
+ #endif
444
+ int64_t n = got > 0 ? (int64_t)got : 1;
445
+ #ifdef __linux__
446
+ cpu_set_t set;
447
+ if (sched_getaffinity(0, sizeof set, &set) == 0) {
448
+ int mine = CPU_COUNT(&set);
449
+ if (mine > 0 && mine < n) n = mine;
450
+ }
451
+ n = nish_cgroup_cpus(n);
452
+ #endif
453
+ const char *env = getenv("NISH_NUM_THREADS");
454
+ if (env) {
455
+ char *end;
456
+ long long want = strtoll(env, &end, 10);
457
+ if (end != env && *end == 0 && want > 0 && want < n) n = (int64_t)want;
458
+ }
459
+ return n;
460
+ #endif
461
+ }
462
+
463
+ /* `totalMemory()`: bytes of physical memory, or -1 when the host does not say.
464
+ The machine's, not a cgroup's limit: what a container may use is a policy
465
+ the program reads elsewhere. */
466
+ int64_t nish_total_memory(void) {
467
+ #if defined(__APPLE__)
468
+ uint64_t bytes = 0;
469
+ size_t size = sizeof bytes;
470
+ return sysctlbyname("hw.memsize", &bytes, &size, 0, 0) == 0 ? (int64_t)bytes : -1;
471
+ #elif defined(__linux__)
472
+ long pages = sysconf(_SC_PHYS_PAGES);
473
+ long page = sysconf(_SC_PAGESIZE);
474
+ return pages > 0 && page > 0 ? (int64_t)pages * page : -1;
475
+ #else
476
+ return -1;
477
+ #endif
478
+ }
479
+
282
480
  /* `readdirSync(path)`: the listing a driver needs to discover its own inputs.
283
481
 
284
482
  **Sorted by bytes**, which `readdir(3)` and Node are not, for two reasons