@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
@@ -171,15 +171,26 @@ static int nish_cpu_has_avx2(void) {
171
171
  }
172
172
  #endif
173
173
 
174
- /* Which path `NISH_SIMD` and this machine pick. AVX2 is asked of the CPU only
175
- when the answer could matter, so a pinned `scalar` or `base` never reads
176
- the CPU's feature bits at all. */
177
- static nish_any_path nish_any_choose(void) {
174
+ /* Which path `NISH_SIMD` and this machine pick, for every kernel here. AVX2 is
175
+ asked of the CPU only when the answer could matter, so a pinned `scalar` or
176
+ `base` never reads the CPU's feature bits at all. */
177
+ enum { NISH_PATH_SCALAR, NISH_PATH_BASE, NISH_PATH_AVX2 };
178
+
179
+ static int nish_simd_choose(void) {
178
180
  const char *pin = getenv("NISH_SIMD");
179
- if (pin != NULL && strcmp(pin, "scalar") == 0) return nish_any_scalar;
180
- if (pin != NULL && strcmp(pin, "base") == 0) return nish_any_base;
181
+ if (pin != NULL && strcmp(pin, "scalar") == 0) return NISH_PATH_SCALAR;
182
+ if (pin != NULL && strcmp(pin, "base") == 0) return NISH_PATH_BASE;
183
+ #if defined(NISH_SIMD_SSE2)
184
+ if (nish_cpu_has_avx2()) return NISH_PATH_AVX2;
185
+ #endif
186
+ return NISH_PATH_BASE;
187
+ }
188
+
189
+ static nish_any_path nish_any_choose(void) {
190
+ int path = nish_simd_choose();
191
+ if (path == NISH_PATH_SCALAR) return nish_any_scalar;
181
192
  #if defined(NISH_SIMD_SSE2)
182
- if (nish_cpu_has_avx2()) return nish_any_avx2;
193
+ if (path == NISH_PATH_AVX2) return nish_any_avx2;
183
194
  #endif
184
195
  return nish_any_base;
185
196
  }
@@ -201,3 +212,216 @@ static int64_t nish_any_resolve(const nish_str *s, const nish_str *set, int64_t
201
212
  int64_t nish_str_index_of_any(const nish_str *s, const nish_str *set, int64_t from) {
202
213
  return __atomic_load_n(&nish_any, __ATOMIC_RELAXED)(s, set, from);
203
214
  }
215
+
216
+ /* `nish_regex_scan`, the search `nish/regex` starts its matches with: the
217
+ first `i` at or after `from` where each of the `depth` bytes from `i` is in
218
+ its position's byte set, the contract in nish.h. The vector paths look for
219
+ the `width` positions from `off`, the rarest run the caller found, and test
220
+ the others only where those hold.
221
+
222
+ A position's set is 32 bytes of `masks`, any of the 256 bytes, and is read
223
+ the way Hyperscan's "truffle" reads one (Intel, BSD-3-Clause; the layout
224
+ only, no code): byte `b`'s row is the low nibble, in the first sixteen
225
+ bytes when `b` is below 0x80 and in the second sixteen when not, and its
226
+ bit in the row is the high nibble's low three bits. A vector shuffle looks
227
+ up sixteen or thirty-two rows at once and answers 0 for an index with its
228
+ top bit set, so one shuffle of each half covers the two halves of the byte
229
+ range with no compare, and a third, of a constant, picks each byte's bit.
230
+ That is exact for every set, where `nish_str_index_of_any`'s compare per
231
+ byte stops at sixteen bytes: `[A-Z]` is twenty-six.
232
+
233
+ Each set is also a 256-byte table, one byte a member, after the 32-byte
234
+ forms. The scalar path and a vector block's candidates read those: a start
235
+ the window passes is checked a load a position, and a branch on each load
236
+ predicts better than a vector check of the whole block, which measured
237
+ slower for the same instruction count. */
238
+ typedef int64_t (*nish_scan_path)(const nish_str *s, const nish_array *masks, int64_t depth, int64_t off,
239
+ int64_t width, int64_t from);
240
+
241
+ /* Whether the bytes from `t` pass positions `j` to `end` of the membership
242
+ tables `m`, a load a byte where the 32-byte form takes a dozen steps. */
243
+ __attribute__((always_inline)) static inline int nish_scan_holds(const uint8_t *t, const uint8_t *m, uint64_t j, uint64_t end) {
244
+ for (; j < end; j++)
245
+ if (!m[j << 8 | t[j]]) return 0;
246
+ return 1;
247
+ }
248
+
249
+ /* Whether a start at `t` whose window from `off` holds passes the positions
250
+ around the window as well. */
251
+ __attribute__((always_inline)) static inline int nish_scan_rest(const uint8_t *t, const uint8_t *m, uint64_t depth, uint64_t off, uint64_t w) {
252
+ return nish_scan_holds(t, m, 0, off) && nish_scan_holds(t, m, off + w, depth);
253
+ }
254
+
255
+ /* The scalar path, and the tail of the vector ones: the window a byte at a
256
+ time, then the rest. */
257
+ static int64_t nish_scan_scalar(const nish_str *s, const nish_array *masks, int64_t depth, int64_t off,
258
+ int64_t width, int64_t from) {
259
+ const uint8_t *t = (const uint8_t *)s->data;
260
+ uint64_t n = s->len, d = (uint64_t)depth, o = (uint64_t)off, w = (uint64_t)width;
261
+ const uint8_t *m = (const uint8_t *)masks->data + 32 * d;
262
+ for (uint64_t i = (uint64_t)from; i + d <= n; i++) {
263
+ if (nish_scan_holds(t + i, m, o, o + w) && nish_scan_rest(t + i, m, d, o, w)) return (int64_t)i;
264
+ }
265
+ return -1;
266
+ }
267
+
268
+ /* The starts a vector block's `hit` bits mark from `i`, lowest first: the
269
+ first whose other positions hold, -1 when the text ends before one could,
270
+ or -2 to go on to the next block. `lanes` is 1 for a byte a bit and 4 for
271
+ NEON's narrowing. */
272
+ __attribute__((always_inline)) static inline int64_t nish_scan_pick(const uint8_t *t, const uint8_t *m, uint64_t n, uint64_t i, uint64_t hit,
273
+ unsigned lanes, uint64_t d, uint64_t o, uint64_t w) {
274
+ while (hit) {
275
+ uint64_t c = i + (uint64_t)__builtin_ctzll(hit) / lanes;
276
+ if (c + d > n) return -1;
277
+ if (nish_scan_rest(t + c, m, d, o, w)) return (int64_t)c;
278
+ hit &= lanes == 1 ? hit - 1 : ~(((uint64_t)15) << ((c - i) * 4));
279
+ }
280
+ return -2;
281
+ }
282
+
283
+ #if defined(NISH_SIMD_SSE2)
284
+ /* SSE2 has no byte shuffle (`pshufb` is SSSE3), so the x86-64 base path is
285
+ the scalar one; a machine with AVX2 takes the path below. */
286
+ #define nish_scan_base nish_scan_scalar
287
+
288
+ /* Thirty-two candidate starts a block: each window position's thirty-two
289
+ bytes are loaded from its own offset and looked up, the picked bit
290
+ compared into a whole byte (positions pick different bits, so the raw ANDs
291
+ would not meet), and the bytes ANDed, so a set byte is a start whose window
292
+ holds. The width is a constant in each copy, so the tables stay in
293
+ registers rather than an array the loop reloads. A width of 4 takes the
294
+ copy for 3: a narrower window is still a sound filter, since a candidate's
295
+ positions outside it are checked one by one, and `nish/regex` asks for 3 at
296
+ most, so a fourth copy would be 350 bytes nothing runs. */
297
+ __attribute__((target("avx2"), always_inline)) static inline int64_t
298
+ nish_scan_avx2_w(const nish_str *s, const nish_array *masks, uint64_t d, uint64_t o, uint64_t w, int64_t from) {
299
+ const uint8_t *t = (const uint8_t *)s->data, *mb = (const uint8_t *)masks->data;
300
+ const __m128i *m = (const __m128i *)(mb + 32 * o);
301
+ uint64_t n = s->len, i = (uint64_t)from;
302
+ __m256i lo[3], hi[3];
303
+ for (uint64_t j = 0; j < w; j++) {
304
+ lo[j] = _mm256_broadcastsi128_si256(_mm_loadu_si128(m + 2 * j));
305
+ hi[j] = _mm256_broadcastsi128_si256(_mm_loadu_si128(m + 2 * j + 1));
306
+ }
307
+ const __m256i bit = _mm256_setr_epi8(1, 2, 4, 8, 16, 32, 64, -128, 1, 2, 4, 8, 16, 32, 64, -128, 1, 2, 4, 8,
308
+ 16, 32, 64, -128, 1, 2, 4, 8, 16, 32, 64, -128);
309
+ const __m256i nib = _mm256_set1_epi8(15), top = _mm256_set1_epi8(-128);
310
+ for (; i + 31 + o + w <= n; i += 32) {
311
+ __m256i all = _mm256_set1_epi8(-1);
312
+ for (uint64_t j = 0; j < w; j++) {
313
+ __m256i v = _mm256_loadu_si256((const __m256i *)(t + i + o + j));
314
+ __m256i row = _mm256_or_si256(_mm256_shuffle_epi8(lo[j], v),
315
+ _mm256_shuffle_epi8(hi[j], _mm256_xor_si256(v, top)));
316
+ __m256i pick = _mm256_shuffle_epi8(bit, _mm256_and_si256(_mm256_srli_epi16(v, 4), nib));
317
+ all = _mm256_and_si256(all, _mm256_cmpeq_epi8(_mm256_and_si256(row, pick), pick));
318
+ }
319
+ unsigned hit = (unsigned)_mm256_movemask_epi8(all);
320
+ if (hit) {
321
+ int64_t c = nish_scan_pick(t, mb + 32 * d, n, i, hit, 1, d, o, w);
322
+ if (c != -2) return c;
323
+ }
324
+ }
325
+ return nish_scan_scalar(s, masks, (int64_t)d, (int64_t)o, (int64_t)w, (int64_t)i);
326
+ }
327
+
328
+ __attribute__((target("avx2"))) static int64_t nish_scan_avx2(const nish_str *s, const nish_array *masks,
329
+ int64_t depth, int64_t off, int64_t width,
330
+ int64_t from) {
331
+ uint64_t d = (uint64_t)depth, o = (uint64_t)off;
332
+ switch (width) {
333
+ case 1: return nish_scan_avx2_w(s, masks, d, o, 1, from);
334
+ case 2: return nish_scan_avx2_w(s, masks, d, o, 2, from);
335
+ default: return nish_scan_avx2_w(s, masks, d, o, 3, from);
336
+ }
337
+ }
338
+ #elif defined(NISH_SIMD_NEON)
339
+ /* `tbl` answers 0 for an index past its table rather than looking at four
340
+ bits, so the row is one two-register lookup, indexed by the low nibble
341
+ with the top bit moved down to 16. */
342
+ static int64_t nish_scan_base(const nish_str *s, const nish_array *masks, int64_t depth, int64_t off,
343
+ int64_t width, int64_t from) {
344
+ const uint8_t *t = (const uint8_t *)s->data, *mb = (const uint8_t *)masks->data;
345
+ uint64_t n = s->len, d = (uint64_t)depth, o = (uint64_t)off, w = (uint64_t)width, i = (uint64_t)from;
346
+ uint8x16x2_t row[4];
347
+ for (uint64_t j = 0; j < w; j++) row[j] = vld1q_u8_x2(mb + 32 * (o + j));
348
+ static const uint8_t bits[16] = {1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128};
349
+ const uint8x16_t bit = vld1q_u8(bits), nib = vdupq_n_u8(15), half = vdupq_n_u8(16);
350
+ for (; i + 15 + o + w <= n; i += 16) {
351
+ uint8x16_t all = vdupq_n_u8(255);
352
+ for (uint64_t j = 0; j < w; j++) {
353
+ uint8x16_t v = vld1q_u8(t + i + o + j);
354
+ uint8x16_t at = vorrq_u8(vandq_u8(v, nib), vandq_u8(vshrq_n_u8(v, 3), half));
355
+ all = vandq_u8(all, vtstq_u8(vqtbl2q_u8(row[j], at), vqtbl1q_u8(bit, vshrq_n_u8(v, 4))));
356
+ }
357
+ /* Kutenin's narrowing, as in `nish_any_base`: four bits a byte. */
358
+ uint64_t hit = vget_lane_u64(vreinterpret_u64_u8(vshrn_n_u16(vreinterpretq_u16_u8(all), 4)), 0);
359
+ if (hit) {
360
+ int64_t c = nish_scan_pick(t, mb + 32 * d, n, i, hit, 4, d, o, w);
361
+ if (c != -2) return c;
362
+ }
363
+ }
364
+ return nish_scan_scalar(s, masks, depth, off, width, (int64_t)i);
365
+ }
366
+ #elif defined(NISH_SIMD_WASM)
367
+ /* `swizzle` answers 0 for an index past 15, as `pshufb` does for one with its
368
+ top bit set, so the AVX2 path's lookups carry over with that bit kept and
369
+ the three below it cleared. */
370
+ static int64_t nish_scan_base(const nish_str *s, const nish_array *masks, int64_t depth, int64_t off,
371
+ int64_t width, int64_t from) {
372
+ const uint8_t *t = (const uint8_t *)s->data, *mb = (const uint8_t *)masks->data;
373
+ uint64_t n = s->len, d = (uint64_t)depth, o = (uint64_t)off, w = (uint64_t)width, i = (uint64_t)from;
374
+ v128_t lo[4], hi[4];
375
+ for (uint64_t j = 0; j < w; j++) {
376
+ lo[j] = wasm_v128_load(mb + 32 * (o + j));
377
+ hi[j] = wasm_v128_load(mb + 32 * (o + j) + 16);
378
+ }
379
+ const v128_t bit = wasm_u8x16_const(1, 2, 4, 8, 16, 32, 64, 128, 1, 2, 4, 8, 16, 32, 64, 128);
380
+ const v128_t keep = wasm_u8x16_splat(0x8f), top = wasm_u8x16_splat(0x80);
381
+ for (; i + 15 + o + w <= n; i += 16) {
382
+ v128_t all = wasm_u8x16_splat(255);
383
+ for (uint64_t j = 0; j < w; j++) {
384
+ v128_t v = wasm_v128_load(t + i + o + j);
385
+ v128_t row = wasm_v128_or(wasm_i8x16_swizzle(lo[j], wasm_v128_and(v, keep)),
386
+ wasm_i8x16_swizzle(hi[j], wasm_v128_and(wasm_v128_xor(v, top), keep)));
387
+ v128_t pick = wasm_v128_and(row, wasm_i8x16_swizzle(bit, wasm_u8x16_shr(v, 4)));
388
+ all = wasm_v128_and(all, wasm_i8x16_ne(pick, wasm_i64x2_const(0, 0)));
389
+ }
390
+ uint32_t hit = wasm_i8x16_bitmask(all);
391
+ if (hit) {
392
+ int64_t c = nish_scan_pick(t, mb + 32 * d, n, i, hit, 1, d, o, w);
393
+ if (c != -2) return c;
394
+ }
395
+ }
396
+ return nish_scan_scalar(s, masks, depth, off, width, (int64_t)i);
397
+ }
398
+ #else
399
+ #define nish_scan_base nish_scan_scalar
400
+ #endif
401
+
402
+ static nish_scan_path nish_scan_choose(void) {
403
+ int path = nish_simd_choose();
404
+ if (path == NISH_PATH_SCALAR) return nish_scan_scalar;
405
+ #if defined(NISH_SIMD_SSE2)
406
+ if (path == NISH_PATH_AVX2) return nish_scan_avx2;
407
+ #endif
408
+ return nish_scan_base;
409
+ }
410
+
411
+ static int64_t nish_scan_resolve(const nish_str *s, const nish_array *masks, int64_t depth, int64_t off,
412
+ int64_t width, int64_t from);
413
+
414
+ /* Resolved on the first call and stored as `nish_any` is. */
415
+ static nish_scan_path nish_scan = nish_scan_resolve;
416
+
417
+ static int64_t nish_scan_resolve(const nish_str *s, const nish_array *masks, int64_t depth, int64_t off,
418
+ int64_t width, int64_t from) {
419
+ nish_scan_path path = nish_scan_choose();
420
+ __atomic_store_n(&nish_scan, path, __ATOMIC_RELAXED);
421
+ return path(s, masks, depth, off, width, from);
422
+ }
423
+
424
+ int64_t nish_regex_scan(const nish_str *s, const nish_array *masks, int64_t depth, int64_t off, int64_t width,
425
+ int64_t from) {
426
+ return __atomic_load_n(&nish_scan, __ATOMIC_RELAXED)(s, masks, depth, off, width, from);
427
+ }