@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.
- package/INSTALL.md +22 -10
- package/bin/nish +0 -0
- package/docs/AI.md +2759 -0
- package/package.json +2 -1
- package/runtime/browser.d.ts +14 -0
- package/runtime/browser.mjs +88 -0
- package/runtime/core/arrays.d.ts +18 -0
- package/runtime/core/arrays.mjs +125 -0
- package/runtime/core/checks.mjs +20 -0
- package/runtime/core/host.d.ts +70 -0
- package/runtime/core/host.mjs +311 -0
- package/runtime/core/numbers.d.ts +53 -0
- package/runtime/core/numbers.mjs +316 -0
- package/runtime/core/result.d.ts +41 -0
- package/runtime/core/result.mjs +127 -0
- package/runtime/core/strings.d.ts +14 -0
- package/runtime/core/strings.mjs +169 -0
- package/runtime/core.d.ts +9 -0
- package/runtime/core.mjs +55 -0
- package/runtime/host.d.ts +6 -0
- package/runtime/nish.d.ts +44 -0
- package/runtime/nish.h +75 -2
- package/runtime/nish.mjs +23 -3
- package/runtime/node.mjs +383 -0
- package/runtime/runtime-bytes.c +401 -0
- package/runtime/runtime-cross.c +1 -1
- package/runtime/runtime-host.c +1 -1
- package/runtime/runtime-os.c +1 -1
- package/runtime/runtime-parallel.c +1 -1
- package/runtime/runtime-simd.c +231 -7
- package/runtime/runtime-term.c +504 -0
- package/runtime/runtime-wasm.c +0 -11
- package/runtime/runtime.c +190 -6
- package/runtime/shim.mjs +6 -985
- package/runtime/sqlite/LICENSE.md +85 -0
- package/runtime/sqlite/README.md +45 -0
- package/runtime/sqlite/nish-sqlite.c +359 -0
- package/runtime/sqlite/sqlite3.c +271131 -0
- package/runtime/sqlite/sqlite3.h +14447 -0
- package/scripts/build.sh +261 -53
- package/std/LICENSE-fast-float +23 -0
- package/std/LICENSE-fdlibm +8 -0
- package/std/README.md +31 -11
- package/std/base64.ts +116 -0
- package/std/bytes.ts +256 -0
- package/std/cli.ts +328 -0
- package/std/collections.ts +6 -5
- package/std/crypto/LICENSE-crypt_blowfish +26 -0
- package/std/crypto/base64url.ts +125 -0
- package/std/crypto/bcrypt.ts +760 -0
- package/std/crypto/hmac.ts +138 -2
- package/std/crypto/x509.ts +11 -122
- package/std/json-tokenizer.ts +2998 -57
- package/std/json-value.ts +22 -3
- package/std/json.ts +83 -1
- package/std/math.ts +608 -0
- package/std/net/http1-server.ts +85 -8
- package/std/net/http3-server.ts +50 -12
- package/std/net/postgres.ts +1875 -0
- package/std/net/quic-listener.ts +20 -4
- package/std/regex.ts +4274 -0
- package/std/sqlite.ts +391 -0
- package/std/term-io.ts +978 -0
- package/std/term-pixels.ts +874 -0
- package/std/term-probe.ts +459 -0
- package/std/term-rate.ts +993 -0
- package/std/term.ts +2453 -0
- package/std/text.ts +90 -7
- package/std/yaml-lines.ts +141 -0
- package/std/yaml.ts +4675 -0
package/runtime/runtime-simd.c
CHANGED
|
@@ -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
|
|
175
|
-
when the answer could matter, so a pinned `scalar` or
|
|
176
|
-
the CPU's feature bits at all. */
|
|
177
|
-
|
|
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
|
|
180
|
-
if (pin != NULL && strcmp(pin, "base") == 0) return
|
|
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 (
|
|
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
|
+
}
|