@amritk/nish-x86_64-linux 0.20.0 → 0.21.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 (70) hide show
  1. package/INSTALL.md +22 -10
  2. package/bin/nish +0 -0
  3. package/docs/AI.md +2759 -0
  4. package/package.json +2 -1
  5. package/runtime/browser.d.ts +14 -0
  6. package/runtime/browser.mjs +88 -0
  7. package/runtime/core/arrays.d.ts +18 -0
  8. package/runtime/core/arrays.mjs +125 -0
  9. package/runtime/core/checks.mjs +20 -0
  10. package/runtime/core/host.d.ts +70 -0
  11. package/runtime/core/host.mjs +311 -0
  12. package/runtime/core/numbers.d.ts +53 -0
  13. package/runtime/core/numbers.mjs +316 -0
  14. package/runtime/core/result.d.ts +41 -0
  15. package/runtime/core/result.mjs +127 -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 +44 -0
  22. package/runtime/nish.h +75 -2
  23. package/runtime/nish.mjs +23 -3
  24. package/runtime/node.mjs +383 -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-os.c +1 -1
  29. package/runtime/runtime-parallel.c +1 -1
  30. package/runtime/runtime-simd.c +231 -7
  31. package/runtime/runtime-term.c +504 -0
  32. package/runtime/runtime-wasm.c +0 -11
  33. package/runtime/runtime.c +190 -6
  34. package/runtime/shim.mjs +6 -985
  35. package/runtime/sqlite/LICENSE.md +85 -0
  36. package/runtime/sqlite/README.md +45 -0
  37. package/runtime/sqlite/nish-sqlite.c +359 -0
  38. package/runtime/sqlite/sqlite3.c +271131 -0
  39. package/runtime/sqlite/sqlite3.h +14447 -0
  40. package/scripts/build.sh +261 -53
  41. package/std/LICENSE-fast-float +23 -0
  42. package/std/LICENSE-fdlibm +8 -0
  43. package/std/README.md +31 -11
  44. package/std/base64.ts +116 -0
  45. package/std/bytes.ts +256 -0
  46. package/std/cli.ts +328 -0
  47. package/std/collections.ts +6 -5
  48. package/std/crypto/LICENSE-crypt_blowfish +26 -0
  49. package/std/crypto/base64url.ts +125 -0
  50. package/std/crypto/bcrypt.ts +760 -0
  51. package/std/crypto/hmac.ts +138 -2
  52. package/std/crypto/x509.ts +11 -122
  53. package/std/json-tokenizer.ts +2998 -57
  54. package/std/json-value.ts +22 -3
  55. package/std/json.ts +83 -1
  56. package/std/math.ts +608 -0
  57. package/std/net/http1-server.ts +85 -8
  58. package/std/net/http3-server.ts +50 -12
  59. package/std/net/postgres.ts +1875 -0
  60. package/std/net/quic-listener.ts +20 -4
  61. package/std/regex.ts +4274 -0
  62. package/std/sqlite.ts +391 -0
  63. package/std/term-io.ts +978 -0
  64. package/std/term-pixels.ts +874 -0
  65. package/std/term-probe.ts +459 -0
  66. package/std/term-rate.ts +993 -0
  67. package/std/term.ts +2453 -0
  68. package/std/text.ts +90 -7
  69. package/std/yaml-lines.ts +141 -0
  70. package/std/yaml.ts +4675 -0
@@ -0,0 +1,401 @@
1
+ /* Nish runtime, the byte-class half: the runs `nish/bytes` steps over -- JSON's
2
+ * blanks, the digits, the bytes a JSON string holds as themselves, and the bytes
3
+ * a JSON writer copies without an escape -- read
4
+ * sixteen or thirty-two bytes at a time (WP38 S6a, docs/wp38-simd.md §3.1).
5
+ *
6
+ * A translation unit of its own for the reason runtime-aes.c is one: each file
7
+ * carries its own measured `.text*` ceiling in tests/runtime-budgets.js, and
8
+ * these kernels would not fit in what runtime-simd.c has left
9
+ * (RUNTIME_BYTES_TEXT_BUDGET says how much they need). Section GC means a
10
+ * program that never calls a kernel links none of this file.
11
+ *
12
+ * The paths and their choice are runtime-simd.c's: a scalar path that runs
13
+ * anywhere, a base path on the vectors every machine of the target has, an
14
+ * AVX2 path compiled for that extension alone, and a choice made on the first
15
+ * call and pinned by `NISH_SIMD` the same way. The one difference is a
16
+ * freestanding wasm32 build, which has no libc to read `NISH_SIMD` with: there
17
+ * the kernels call the base path directly, and the base path is the scalar one
18
+ * unless the build adds `-msimd128`.
19
+ */
20
+ #include <stdint.h>
21
+
22
+ #include "nish.h"
23
+
24
+ #if defined(__x86_64__) || defined(__i386__)
25
+ #if defined(__SSE2__)
26
+ #include <cpuid.h>
27
+ #include <immintrin.h>
28
+ #define NISH_SIMD_SSE2 1
29
+ #endif
30
+ #elif defined(__ARM_NEON)
31
+ #include <arm_neon.h>
32
+ #define NISH_SIMD_NEON 1
33
+ #elif defined(__wasm_simd128__)
34
+ #include <wasm_simd128.h>
35
+ #define NISH_SIMD_WASM 1
36
+ #endif
37
+
38
+ /* Whether `NISH_SIMD` can be read: everywhere but a freestanding wasm32 build. */
39
+ #if !defined(__wasm__) || defined(__wasi__)
40
+ #include <stdlib.h>
41
+ #include <string.h>
42
+ #define NISH_RUN_CHOOSES 1
43
+ #endif
44
+
45
+ #if defined(NISH_SIMD_SSE2)
46
+ /* runtime-simd.c's `nish_cpu_has_avx2`, which says why it asks `cpuid.h`
47
+ rather than `__builtin_cpu_supports`. A copy rather than a shared symbol, as
48
+ runtime-aes.c keeps its own question, so that each unit links alone. */
49
+ static int nish_cpu_has_avx2(void) {
50
+ unsigned a, b, c, d;
51
+ if (!__get_cpuid(1, &a, &b, &c, &d) || !(c & bit_OSXSAVE) || !(c & bit_AVX)) return 0;
52
+ unsigned lo, hi;
53
+ __asm__("xgetbv" : "=a"(lo), "=d"(hi) : "c"(0));
54
+ if ((lo & 6) != 6) return 0;
55
+ return __get_cpuid_count(7, 0, &a, &b, &c, &d) && (b & bit_AVX2);
56
+ }
57
+ #endif
58
+
59
+ /* The byte-class runs of `nish/bytes` -- `skipBlank`, `skipDigits`,
60
+ `skipJsonText` and `skipJsonPlain` -- which answer the first index in
61
+ [from, to) whose byte is outside a class, or `to`. The contracts are in
62
+ nish.h. The first three share one resolver and one chooser, because they
63
+ are one question asked of three classes: a path is one function taking the
64
+ class, and each exported kernel calls it with its own constant. The fourth
65
+ has paths and a pointer of its own, the same shape, which the one resolver
66
+ sets beside the first's.
67
+
68
+ The JSON text class is the bytes a string's scan steps over without a
69
+ second look: printable ASCII other than `"` and `\`, and every complete,
70
+ well-formed UTF-8 sequence that ends at or before `to` (RFC 3629 §4, the
71
+ ranges `jsonTokUtf8Length` in std/json-tokenizer.ts checks). A block of
72
+ ASCII is classified sixteen or thirty-two bytes at a time; a byte with its
73
+ top bit set stops the block, and the sequence it leads is checked one byte
74
+ at a time, as is every sequence after it until the next ASCII byte, so a
75
+ run of CJK text costs one block load per run rather than one per
76
+ character. A sequence that is malformed, or cut short by `to`, ends the
77
+ run at its lead, where the caller's own checks judge it.
78
+
79
+ The JSON plain class is the bytes a writer copies into a string as they
80
+ are: every byte from 0x20 up other than `"` and `\`. It is the text class
81
+ with no UTF-8 check -- a writer passes what it is given through, and the
82
+ reader that checks it is on the other side -- so its vector stops are the
83
+ text class's without the high bytes, and nothing in it resumes. It is kept
84
+ out of the class paths because a fourth class there costs the other three:
85
+ choosing its stops inside the shared block loop is a branch on the class in
86
+ every block of the text loop JsonTokenizer runs, and a test on entry costs
87
+ each path its loop unswitching at -O3 and a prologue's worth of bytes at
88
+ -Oz. Its base path is the 16-byte one on every machine, AVX2 or not: a
89
+ writer's strings are short, and a 32-byte copy would not fit the file's
90
+ ceiling. */
91
+ enum { NISH_RUN_BLANK = 0, NISH_RUN_DIGIT = 1, NISH_RUN_TEXT = 2, NISH_RUN_PLAIN = 3 };
92
+
93
+ /* One path of a run kernel: the class, then the nish.h contract. The plain
94
+ kernel's paths take the class too and ignore it, so one resolver serves
95
+ both pointers. */
96
+ typedef int64_t (*nish_run_path)(int cls, const uint8_t *p, int64_t from, int64_t to);
97
+
98
+ /* The length of the well-formed UTF-8 sequence whose lead is at `p[i]` and
99
+ that ends at or before `to`, or 0. */
100
+ static inline int64_t nish_run_utf8(const uint8_t *p, int64_t i, int64_t to) {
101
+ uint8_t lead = p[i];
102
+ uint8_t low = 0x80, high = 0xbf;
103
+ int64_t n;
104
+ if (lead >= 0xc2 && lead <= 0xdf) {
105
+ n = 2;
106
+ } else if (lead >= 0xe0 && lead <= 0xef) {
107
+ n = 3;
108
+ if (lead == 0xe0) low = 0xa0;
109
+ if (lead == 0xed) high = 0x9f;
110
+ } else if (lead >= 0xf0 && lead <= 0xf4) {
111
+ n = 4;
112
+ if (lead == 0xf0) low = 0x90;
113
+ if (lead == 0xf4) high = 0x8f;
114
+ } else {
115
+ return 0;
116
+ }
117
+ if (to - i < n) return 0;
118
+ if (p[i + 1] < low || p[i + 1] > high) return 0;
119
+ for (int64_t k = 2; k < n; k++) {
120
+ if (p[i + k] < 0x80 || p[i + k] > 0xbf) return 0;
121
+ }
122
+ return n;
123
+ }
124
+
125
+ /* Whether ASCII-range byte `b` is in the class; for the text class a byte
126
+ with its top bit set answers no here and is taken by `nish_run_utf8`. */
127
+ static inline int nish_run_in(int cls, uint8_t b) {
128
+ if (cls == NISH_RUN_BLANK) return b == 0x20 || b == 0x0a || b == 0x0d || b == 0x09;
129
+ if (cls == NISH_RUN_DIGIT) return (uint8_t)(b - 0x30) < 10;
130
+ return b >= 0x20 && b < 0x80 && b != 0x22 && b != 0x5c;
131
+ }
132
+
133
+ /* Steps `i` over the run of UTF-8 sequences that starts there, stopping at
134
+ the first ASCII byte, the first malformed sequence or `to`. */
135
+ static inline int64_t nish_run_wide(const uint8_t *p, int64_t i, int64_t to) {
136
+ while (i < to && p[i] >= 0x80) {
137
+ int64_t n = nish_run_utf8(p, i, to);
138
+ if (n == 0) return i;
139
+ i += n;
140
+ }
141
+ return i;
142
+ }
143
+
144
+ /* The plain class's scalar path, and the tail of its vector ones. */
145
+ static int64_t nish_plain_scalar(int cls, const uint8_t *p, int64_t i, int64_t to) {
146
+ (void)cls;
147
+ while (i < to && p[i] >= 0x20 && p[i] != 0x22 && p[i] != 0x5c) i++;
148
+ return i;
149
+ }
150
+
151
+ /* The scalar path, and the tail of the vector ones. */
152
+ static int64_t nish_run_scalar(int cls, const uint8_t *p, int64_t from, int64_t to) {
153
+ int64_t i = from;
154
+ while (i < to) {
155
+ uint8_t b = p[i];
156
+ if (nish_run_in(cls, b)) {
157
+ i++;
158
+ } else if (cls == NISH_RUN_TEXT && b >= 0x80) {
159
+ int64_t j = nish_run_wide(p, i, to);
160
+ if (j == i) return i;
161
+ i = j;
162
+ } else {
163
+ return i;
164
+ }
165
+ }
166
+ return to;
167
+ }
168
+
169
+ #if defined(NISH_SIMD_SSE2) || defined(NISH_SIMD_NEON) || defined(NISH_SIMD_WASM)
170
+ /* A vector path's step past a block's first stop at `i`: `i` again when the
171
+ stop ends the run, else where the run resumes. Only a text run resumes,
172
+ and only past UTF-8. */
173
+ static inline int64_t nish_run_resume(int cls, const uint8_t *p, int64_t i, int64_t to) {
174
+ if (cls != NISH_RUN_TEXT || p[i] < 0x80) return i;
175
+ return nish_run_wide(p, i, to);
176
+ }
177
+ #endif
178
+
179
+ /* Each vector path computes a mask of the block's stops -- the bytes outside
180
+ the class, where for text every byte with its top bit set counts as a stop
181
+ -- and either moves on a whole block or jumps to the first stop. */
182
+ #if defined(NISH_SIMD_SSE2)
183
+ static inline unsigned nish_run_stops_sse2(int cls, __m128i v) {
184
+ if (cls == NISH_RUN_BLANK) {
185
+ __m128i in = _mm_or_si128(_mm_or_si128(_mm_cmpeq_epi8(v, _mm_set1_epi8(0x20)), _mm_cmpeq_epi8(v, _mm_set1_epi8(0x0a))),
186
+ _mm_or_si128(_mm_cmpeq_epi8(v, _mm_set1_epi8(0x0d)), _mm_cmpeq_epi8(v, _mm_set1_epi8(0x09))));
187
+ return ~(unsigned)_mm_movemask_epi8(in) & 0xffffu;
188
+ }
189
+ if (cls == NISH_RUN_DIGIT) {
190
+ __m128i x = _mm_sub_epi8(v, _mm_set1_epi8(0x30));
191
+ __m128i in = _mm_cmpeq_epi8(_mm_min_epu8(x, _mm_set1_epi8(9)), x);
192
+ return ~(unsigned)_mm_movemask_epi8(in) & 0xffffu;
193
+ }
194
+ /* A control byte is one that `min(v, 0x1f)` leaves alone; a high byte is
195
+ its own sign bit, which movemask reads straight off `v`. */
196
+ __m128i stop = _mm_or_si128(_mm_or_si128(_mm_cmpeq_epi8(v, _mm_set1_epi8(0x22)), _mm_cmpeq_epi8(v, _mm_set1_epi8(0x5c))),
197
+ _mm_cmpeq_epi8(_mm_min_epu8(v, _mm_set1_epi8(0x1f)), v));
198
+ return (unsigned)_mm_movemask_epi8(_mm_or_si128(stop, v));
199
+ }
200
+
201
+ static int64_t nish_plain_base(int cls, const uint8_t *p, int64_t i, int64_t to) {
202
+ for (; to - i >= 16; i += 16) {
203
+ __m128i v = _mm_loadu_si128((const __m128i *)(p + i));
204
+ __m128i stop = _mm_or_si128(_mm_or_si128(_mm_cmpeq_epi8(v, _mm_set1_epi8(0x22)), _mm_cmpeq_epi8(v, _mm_set1_epi8(0x5c))),
205
+ _mm_cmpeq_epi8(_mm_min_epu8(v, _mm_set1_epi8(0x1f)), v));
206
+ unsigned m = (unsigned)_mm_movemask_epi8(stop);
207
+ if (m != 0) return i + __builtin_ctz(m);
208
+ }
209
+ return nish_plain_scalar(cls, p, i, to);
210
+ }
211
+
212
+ static int64_t nish_run_base(int cls, const uint8_t *p, int64_t from, int64_t to) {
213
+ int64_t i = from;
214
+ while (to - i >= 16) {
215
+ unsigned m = nish_run_stops_sse2(cls, _mm_loadu_si128((const __m128i *)(p + i)));
216
+ if (m == 0) {
217
+ i += 16;
218
+ continue;
219
+ }
220
+ int64_t at = i + __builtin_ctz(m);
221
+ i = nish_run_resume(cls, p, at, to);
222
+ if (i == at) return at;
223
+ }
224
+ return nish_run_scalar(cls, p, i, to);
225
+ }
226
+
227
+ __attribute__((target("avx2"))) static inline unsigned nish_run_stops_avx2(int cls, __m256i v) {
228
+ if (cls == NISH_RUN_BLANK) {
229
+ __m256i in = _mm256_or_si256(
230
+ _mm256_or_si256(_mm256_cmpeq_epi8(v, _mm256_set1_epi8(0x20)), _mm256_cmpeq_epi8(v, _mm256_set1_epi8(0x0a))),
231
+ _mm256_or_si256(_mm256_cmpeq_epi8(v, _mm256_set1_epi8(0x0d)), _mm256_cmpeq_epi8(v, _mm256_set1_epi8(0x09))));
232
+ return ~(unsigned)_mm256_movemask_epi8(in);
233
+ }
234
+ if (cls == NISH_RUN_DIGIT) {
235
+ __m256i x = _mm256_sub_epi8(v, _mm256_set1_epi8(0x30));
236
+ __m256i in = _mm256_cmpeq_epi8(_mm256_min_epu8(x, _mm256_set1_epi8(9)), x);
237
+ return ~(unsigned)_mm256_movemask_epi8(in);
238
+ }
239
+ __m256i stop = _mm256_or_si256(
240
+ _mm256_or_si256(_mm256_cmpeq_epi8(v, _mm256_set1_epi8(0x22)), _mm256_cmpeq_epi8(v, _mm256_set1_epi8(0x5c))),
241
+ _mm256_cmpeq_epi8(_mm256_min_epu8(v, _mm256_set1_epi8(0x1f)), v));
242
+ return (unsigned)_mm256_movemask_epi8(_mm256_or_si256(stop, v));
243
+ }
244
+
245
+ /* Thirty-two bytes a block, then the base path for what is left. */
246
+ __attribute__((target("avx2"))) static int64_t nish_run_avx2(int cls, const uint8_t *p, int64_t from, int64_t to) {
247
+ int64_t i = from;
248
+ while (to - i >= 32) {
249
+ unsigned m = nish_run_stops_avx2(cls, _mm256_loadu_si256((const __m256i *)(p + i)));
250
+ if (m == 0) {
251
+ i += 32;
252
+ continue;
253
+ }
254
+ int64_t at = i + __builtin_ctz(m);
255
+ i = nish_run_resume(cls, p, at, to);
256
+ if (i == at) return at;
257
+ }
258
+ return nish_run_base(cls, p, i, to);
259
+ }
260
+ #elif defined(NISH_SIMD_NEON)
261
+ static inline uint64_t nish_run_stops_neon(int cls, uint8x16_t v) {
262
+ uint8x16_t stop;
263
+ if (cls == NISH_RUN_BLANK) {
264
+ stop = vmvnq_u8(vorrq_u8(vorrq_u8(vceqq_u8(v, vdupq_n_u8(0x20)), vceqq_u8(v, vdupq_n_u8(0x0a))),
265
+ vorrq_u8(vceqq_u8(v, vdupq_n_u8(0x0d)), vceqq_u8(v, vdupq_n_u8(0x09)))));
266
+ } else if (cls == NISH_RUN_DIGIT) {
267
+ stop = vcgtq_u8(vsubq_u8(v, vdupq_n_u8(0x30)), vdupq_n_u8(9));
268
+ } else {
269
+ stop = vorrq_u8(vorrq_u8(vceqq_u8(v, vdupq_n_u8(0x22)), vceqq_u8(v, vdupq_n_u8(0x5c))),
270
+ vorrq_u8(vcleq_u8(v, vdupq_n_u8(0x1f)), vcgeq_u8(v, vdupq_n_u8(0x80))));
271
+ }
272
+ /* runtime-simd.c's NEON movemask: four bits a byte, in order. */
273
+ return vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(stop), 4)), 0);
274
+ }
275
+
276
+ static int64_t nish_plain_base(int cls, const uint8_t *p, int64_t i, int64_t to) {
277
+ for (; to - i >= 16; i += 16) {
278
+ uint8x16_t v = vld1q_u8(p + i);
279
+ uint8x16_t stop = vorrq_u8(vorrq_u8(vceqq_u8(v, vdupq_n_u8(0x22)), vceqq_u8(v, vdupq_n_u8(0x5c))), vcleq_u8(v, vdupq_n_u8(0x1f)));
280
+ uint64_t m = vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(stop), 4)), 0);
281
+ if (m != 0) return i + (int64_t)(__builtin_ctzll(m) >> 2);
282
+ }
283
+ return nish_plain_scalar(cls, p, i, to);
284
+ }
285
+
286
+ static int64_t nish_run_base(int cls, const uint8_t *p, int64_t from, int64_t to) {
287
+ int64_t i = from;
288
+ while (to - i >= 16) {
289
+ uint64_t m = nish_run_stops_neon(cls, vld1q_u8(p + i));
290
+ if (m == 0) {
291
+ i += 16;
292
+ continue;
293
+ }
294
+ int64_t at = i + (int64_t)(__builtin_ctzll(m) >> 2);
295
+ i = nish_run_resume(cls, p, at, to);
296
+ if (i == at) return at;
297
+ }
298
+ return nish_run_scalar(cls, p, i, to);
299
+ }
300
+ #elif defined(NISH_SIMD_WASM)
301
+ static inline uint32_t nish_run_stops_wasm(int cls, v128_t v) {
302
+ v128_t stop;
303
+ if (cls == NISH_RUN_BLANK) {
304
+ stop = wasm_v128_not(wasm_v128_or(wasm_v128_or(wasm_i8x16_eq(v, wasm_i8x16_splat(0x20)), wasm_i8x16_eq(v, wasm_i8x16_splat(0x0a))),
305
+ wasm_v128_or(wasm_i8x16_eq(v, wasm_i8x16_splat(0x0d)), wasm_i8x16_eq(v, wasm_i8x16_splat(0x09)))));
306
+ } else if (cls == NISH_RUN_DIGIT) {
307
+ stop = wasm_u8x16_gt(wasm_i8x16_sub(v, wasm_i8x16_splat(0x30)), wasm_i8x16_splat(9));
308
+ } else {
309
+ stop = wasm_v128_or(wasm_v128_or(wasm_i8x16_eq(v, wasm_i8x16_splat(0x22)), wasm_i8x16_eq(v, wasm_i8x16_splat(0x5c))),
310
+ wasm_v128_or(wasm_u8x16_le(v, wasm_i8x16_splat(0x1f)), v));
311
+ }
312
+ return wasm_i8x16_bitmask(stop);
313
+ }
314
+
315
+ static int64_t nish_plain_base(int cls, const uint8_t *p, int64_t i, int64_t to) {
316
+ for (; to - i >= 16; i += 16) {
317
+ v128_t v = wasm_v128_load(p + i);
318
+ v128_t stop = wasm_v128_or(wasm_v128_or(wasm_i8x16_eq(v, wasm_i8x16_splat(0x22)), wasm_i8x16_eq(v, wasm_i8x16_splat(0x5c))),
319
+ wasm_u8x16_le(v, wasm_i8x16_splat(0x1f)));
320
+ uint32_t m = wasm_i8x16_bitmask(stop);
321
+ if (m != 0) return i + __builtin_ctz(m);
322
+ }
323
+ return nish_plain_scalar(cls, p, i, to);
324
+ }
325
+
326
+ static int64_t nish_run_base(int cls, const uint8_t *p, int64_t from, int64_t to) {
327
+ int64_t i = from;
328
+ while (to - i >= 16) {
329
+ uint32_t m = nish_run_stops_wasm(cls, wasm_v128_load(p + i));
330
+ if (m == 0) {
331
+ i += 16;
332
+ continue;
333
+ }
334
+ int64_t at = i + __builtin_ctz(m);
335
+ i = nish_run_resume(cls, p, at, to);
336
+ if (i == at) return at;
337
+ }
338
+ return nish_run_scalar(cls, p, i, to);
339
+ }
340
+ #else
341
+ #define nish_run_base nish_run_scalar
342
+ #define nish_plain_base nish_plain_scalar
343
+ #endif
344
+
345
+ #if defined(NISH_RUN_CHOOSES)
346
+ /* Which path `NISH_SIMD` and this machine pick: runtime-simd.c's
347
+ `nish_any_choose`, for the run kernels. */
348
+ static nish_run_path nish_run_choose(void) {
349
+ const char *pin = getenv("NISH_SIMD");
350
+ if (pin != NULL && strcmp(pin, "scalar") == 0) return nish_run_scalar;
351
+ if (pin != NULL && strcmp(pin, "base") == 0) return nish_run_base;
352
+ #if defined(NISH_SIMD_SSE2)
353
+ if (nish_cpu_has_avx2()) return nish_run_avx2;
354
+ #endif
355
+ return nish_run_base;
356
+ }
357
+
358
+ static int64_t nish_run_resolve(int cls, const uint8_t *p, int64_t from, int64_t to);
359
+
360
+ /* runtime-simd.c's `nish_any`, for the run kernels: a pointer that starts at
361
+ the resolver, read and written with relaxed atomics, plain moves, because
362
+ under `--threads` two workers may make the first call at once and both
363
+ store the same path. The plain kernel has a pointer of its own. */
364
+ static nish_run_path nish_run = nish_run_resolve;
365
+ static nish_run_path nish_plain = nish_run_resolve;
366
+
367
+ /* Asks the chooser once and sets both pointers: the plain kernel takes its
368
+ scalar path when the chooser picks the scalar one, else its base path. */
369
+ static void nish_run_settle(void) {
370
+ nish_run_path path = nish_run_choose();
371
+ __atomic_store_n(&nish_run, path, __ATOMIC_RELAXED);
372
+ __atomic_store_n(&nish_plain, path == nish_run_scalar ? nish_plain_scalar : nish_plain_base, __ATOMIC_RELAXED);
373
+ }
374
+
375
+ #define NISH_RUN_PATH __atomic_load_n(&nish_run, __ATOMIC_RELAXED)
376
+ #define NISH_PLAIN_PATH __atomic_load_n(&nish_plain, __ATOMIC_RELAXED)
377
+
378
+ static int64_t nish_run_resolve(int cls, const uint8_t *p, int64_t from, int64_t to) {
379
+ nish_run_settle();
380
+ return (cls == NISH_RUN_PLAIN ? NISH_PLAIN_PATH : NISH_RUN_PATH)(cls, p, from, to);
381
+ }
382
+ #else
383
+ #define NISH_RUN_PATH nish_run_base
384
+ #define NISH_PLAIN_PATH nish_plain_base
385
+ #endif
386
+
387
+ int64_t nish_bytes_skip_blank(const uint8_t *p, int64_t from, int64_t to) {
388
+ return NISH_RUN_PATH(NISH_RUN_BLANK, p, from, to);
389
+ }
390
+
391
+ int64_t nish_bytes_skip_digits(const uint8_t *p, int64_t from, int64_t to) {
392
+ return NISH_RUN_PATH(NISH_RUN_DIGIT, p, from, to);
393
+ }
394
+
395
+ int64_t nish_bytes_skip_json_text(const uint8_t *p, int64_t from, int64_t to) {
396
+ return NISH_RUN_PATH(NISH_RUN_TEXT, p, from, to);
397
+ }
398
+
399
+ int64_t nish_bytes_skip_json_plain(const uint8_t *p, int64_t from, int64_t to) {
400
+ return NISH_PLAIN_PATH(NISH_RUN_PLAIN, p, from, to);
401
+ }
@@ -64,7 +64,7 @@ _Static_assert(offsetof(nish_xchunk, cap) == 8, "runtime.c's nish_chunk: cap sec
64
64
  static void nish_cross_oom(void) {
65
65
  nish_panic_claim();
66
66
  (void)!write(2, "nish: out of memory\n", 20);
67
- _exit(1);
67
+ nish_fatal_exit();
68
68
  }
69
69
 
70
70
  static void *nish_cross_block(uint64_t size) {
@@ -64,7 +64,7 @@ static NISH_COLD void nish_entropy_fail(uint64_t asked) {
64
64
  } else {
65
65
  dprintf(2, "crypto.getRandomValues: the system's entropy source failed\n");
66
66
  }
67
- _exit(1);
67
+ nish_fatal_exit();
68
68
  }
69
69
 
70
70
  /* `crypto.getRandomValues(bytes)`: every byte of `bytes` from the kernel's
@@ -82,7 +82,7 @@ void nish_exit(int32_t code) { exit(code); }
82
82
  static NISH_COLD void nish_io_fail(const char *what, const nish_str *path) {
83
83
  nish_panic_claim();
84
84
  dprintf(2, "nish: cannot %s%.*s\n", what, (int)path->len, path->data);
85
- _exit(1);
85
+ nish_fatal_exit();
86
86
  }
87
87
 
88
88
  /* The longest string or array a read hands back: 2^31 - 1, the same limit
@@ -546,7 +546,7 @@ static void nish_task_run(nish_job *j) {
546
546
  static void nish_par_oom(void) {
547
547
  nish_panic_claim();
548
548
  (void)!write(2, "nish: out of memory\n", 20);
549
- _exit(1);
549
+ nish_fatal_exit();
550
550
  }
551
551
 
552
552
  void nish_scope_spawn(void *scope, nish_task_fn run, nish_task_fn finish, const void *payload, int64_t size) {