@scriptc/cli-darwin-arm64 0.2.4 → 0.2.5

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 (121) hide show
  1. package/dist/THIRD_PARTY_NOTICES +1 -1
  2. package/dist/bin/scriptc +0 -0
  3. package/dist/bin/scriptc.json +1 -1
  4. package/dist/lib/declarations/scriptc-node-fallback.d.ts +915 -215
  5. package/dist/lib/declarations/scriptc-overrides.d.ts +5 -1
  6. package/dist/lib/declarations/scriptc.d.ts +22 -2
  7. package/dist/lib/llvm/bin/scriptc-llvm-codegen +0 -0
  8. package/dist/lib/llvm/package.json +1 -1
  9. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_array.o +0 -0
  10. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_bytes.o +0 -0
  11. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_bytes_io.o +0 -0
  12. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_copying.o +0 -0
  13. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_json.o +0 -0
  14. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_lib.o +0 -0
  15. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_map.o +0 -0
  16. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_number.o +0 -0
  17. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_regex.o +0 -0
  18. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/default/scr_string.o +0 -0
  19. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_array.o +0 -0
  20. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_bytes.o +0 -0
  21. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_bytes_io.o +0 -0
  22. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_copying.o +0 -0
  23. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_json.o +0 -0
  24. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_lib.o +0 -0
  25. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_map.o +0 -0
  26. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_number.o +0 -0
  27. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_regex.o +0 -0
  28. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic/scr_string.o +0 -0
  29. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/dynamic-text-decoder-legacy/scr_bytes.o +0 -0
  30. package/dist/lib/runtime-darwin-arm64/artifacts/dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  31. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_array.o +0 -0
  32. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_bytes.o +0 -0
  33. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_bytes_io.o +0 -0
  34. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_copying.o +0 -0
  35. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_json.o +0 -0
  36. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_lib.o +0 -0
  37. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_map.o +0 -0
  38. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_number.o +0 -0
  39. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_regex.o +0 -0
  40. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/default/scr_string.o +0 -0
  41. package/dist/lib/runtime-darwin-arm64/artifacts/library-dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  42. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_array.o +0 -0
  43. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_bytes.o +0 -0
  44. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_bytes_io.o +0 -0
  45. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_copying.o +0 -0
  46. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_json.o +0 -0
  47. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_lib.o +0 -0
  48. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_map.o +0 -0
  49. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_number.o +0 -0
  50. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_regex.o +0 -0
  51. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/default/scr_string.o +0 -0
  52. package/dist/lib/runtime-darwin-arm64/artifacts/library-release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  53. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_array.o +0 -0
  54. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_bytes.o +0 -0
  55. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_bytes_io.o +0 -0
  56. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_copying.o +0 -0
  57. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_json.o +0 -0
  58. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_lib.o +0 -0
  59. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_map.o +0 -0
  60. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_number.o +0 -0
  61. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_regex.o +0 -0
  62. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/default/scr_string.o +0 -0
  63. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  64. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_array.o +0 -0
  65. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_bytes.o +0 -0
  66. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_bytes_io.o +0 -0
  67. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_copying.o +0 -0
  68. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_json.o +0 -0
  69. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_lib.o +0 -0
  70. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_map.o +0 -0
  71. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_number.o +0 -0
  72. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_regex.o +0 -0
  73. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/default/scr_string.o +0 -0
  74. package/dist/lib/runtime-darwin-arm64/artifacts/library-thread-release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  75. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_array.o +0 -0
  76. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_bytes.o +0 -0
  77. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_bytes_io.o +0 -0
  78. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_copying.o +0 -0
  79. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_json.o +0 -0
  80. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_lib.o +0 -0
  81. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_map.o +0 -0
  82. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_number.o +0 -0
  83. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_regex.o +0 -0
  84. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_string.o +0 -0
  85. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/default/scr_util_compare.o +0 -0
  86. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_array.o +0 -0
  87. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_bytes.o +0 -0
  88. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_bytes_io.o +0 -0
  89. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_copying.o +0 -0
  90. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_json.o +0 -0
  91. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_lib.o +0 -0
  92. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_map.o +0 -0
  93. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_number.o +0 -0
  94. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_regex.o +0 -0
  95. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_string.o +0 -0
  96. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic/scr_util_compare.o +0 -0
  97. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/dynamic-text-decoder-legacy/scr_bytes.o +0 -0
  98. package/dist/lib/runtime-darwin-arm64/artifacts/release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  99. package/dist/lib/runtime-darwin-arm64/package.json +1 -1
  100. package/dist/lib/runtime-darwin-arm64/runtime-pack.json +181 -181
  101. package/dist/lib/runtime-sources/package.json +1 -1
  102. package/dist/lib/runtime-sources/src/scr_array.c +267 -6
  103. package/dist/lib/runtime-sources/src/scr_async.test.ts +16 -6
  104. package/dist/lib/runtime-sources/src/scr_bytes.c +437 -216
  105. package/dist/lib/runtime-sources/src/scr_bytes.test.c +85 -0
  106. package/dist/lib/runtime-sources/src/scr_bytes_io.c +0 -34
  107. package/dist/lib/runtime-sources/src/scr_copying.c +12 -2
  108. package/dist/lib/runtime-sources/src/scr_json.c +479 -150
  109. package/dist/lib/runtime-sources/src/scr_json.test.c +323 -1
  110. package/dist/lib/runtime-sources/src/scr_json.test.ts +58 -25
  111. package/dist/lib/runtime-sources/src/scr_key.h +68 -0
  112. package/dist/lib/runtime-sources/src/scr_lib.c +132 -73
  113. package/dist/lib/runtime-sources/src/scr_map.c +50 -66
  114. package/dist/lib/runtime-sources/src/scr_number.c +2 -4
  115. package/dist/lib/runtime-sources/src/scr_numeric.h +25 -0
  116. package/dist/lib/runtime-sources/src/scr_numeric.test.c +37 -0
  117. package/dist/lib/runtime-sources/src/scr_numeric.test.ts +33 -0
  118. package/dist/lib/runtime-sources/src/scr_regex.c +238 -167
  119. package/dist/lib/runtime-sources/src/scr_runtime.h +37 -6
  120. package/dist/lib/runtime-sources/src/scr_string.c +155 -42
  121. package/package.json +1 -1
@@ -2299,34 +2299,125 @@ static FILE *scr_fs_fopen(const ScrStr *path, const char *mode) {
2299
2299
  #endif
2300
2300
  }
2301
2301
 
2302
+ /* File sizes are hints, never EOF. Pseudo-files, pipes and concurrently
2303
+ * changing files still read to EOF; descriptor reads keep their position
2304
+ * and ownership. Limit individual reads for the CRT and small ssize_t. */
2305
+ typedef struct {
2306
+ ScrStr *text;
2307
+ uint8_t *bytes;
2308
+ size_t len, cap;
2309
+ } ScrFileRead;
2310
+
2311
+ static size_t scr_file_read_hint(FILE *stream, int fd) {
2312
+ if (stream) fd = fileno(stream);
2313
+ #ifdef _WIN32
2314
+ struct _stat64 st;
2315
+ if (_fstat64(fd, &st) != 0 || !S_ISREG(st.st_mode) || st.st_size <= 0) return 4096;
2316
+ int64_t position = stream ? 0 : _lseeki64(fd, 0, SEEK_CUR);
2317
+ #else
2318
+ struct stat st;
2319
+ if (fstat(fd, &st) != 0 || !S_ISREG(st.st_mode) || st.st_size <= 0) return 4096;
2320
+ off_t position = stream ? 0 : lseek(fd, 0, SEEK_CUR);
2321
+ #endif
2322
+ if (position < 0 || position >= st.st_size) return 4096;
2323
+ uintmax_t remaining = (uintmax_t)st.st_size - (uintmax_t)position;
2324
+ /* A bounded initial allocation also tolerates sparse files that shrink
2325
+ * between metadata and the first read without reserving their full size. */
2326
+ size_t limit = 64 * 1024 * 1024;
2327
+ return remaining > limit ? limit : (size_t)remaining;
2328
+ }
2329
+
2330
+ static uint8_t *scr_file_read_data(ScrFileRead *out) {
2331
+ return out->text ? (uint8_t *)out->text->data : out->bytes;
2332
+ }
2333
+
2334
+ static void scr_file_read_grow(ScrFileRead *out) {
2335
+ size_t limit = SIZE_MAX - sizeof(ScrStr) - 1;
2336
+ if (out->cap >= limit) scr_trap("scriptc: file too large\n");
2337
+ size_t cap = out->cap > limit / 2 ? limit : out->cap * 2;
2338
+ if (out->text) {
2339
+ out->text->len = out->len;
2340
+ out->text = scr_str_regrow(out->text, cap);
2341
+ } else {
2342
+ uint8_t *grown = realloc(out->bytes, cap);
2343
+ if (!grown) scr_trap("scriptc: out of memory\n");
2344
+ out->bytes = grown;
2345
+ }
2346
+ out->cap = cap;
2347
+ }
2348
+
2349
+ /* Fill the final allocation. At a full capacity, probe one byte before
2350
+ * growing so an exact-size regular file never doubles its retained space. */
2351
+ static int scr_file_read_all(FILE *stream, int fd, bool text, ScrFileRead *out) {
2352
+ *out = (ScrFileRead){0};
2353
+ out->cap = scr_file_read_hint(stream, fd);
2354
+ if (text) {
2355
+ out->text = scr_str_alloc_raw(0, out->cap);
2356
+ out->cap = out->text->cap;
2357
+ out->text->data[0] = '\0';
2358
+ } else {
2359
+ out->bytes = malloc(out->cap);
2360
+ if (!out->bytes) scr_trap("scriptc: out of memory\n");
2361
+ }
2362
+ for (;;) {
2363
+ uint8_t extra;
2364
+ bool full = out->len == out->cap;
2365
+ size_t room = full ? 1 : out->cap - out->len;
2366
+ if (room > 512 * 1024) room = 512 * 1024;
2367
+ uint8_t *dest = full ? &extra : scr_file_read_data(out) + out->len;
2368
+ size_t count;
2369
+ int error = 0;
2370
+ if (stream) {
2371
+ count = fread(dest, 1, room, stream);
2372
+ if (ferror(stream)) error = errno ? errno : EIO;
2373
+ } else {
2374
+ ssize_t n = read(fd, dest, room);
2375
+ if (n < 0) { count = 0; error = errno; }
2376
+ else count = (size_t)n;
2377
+ }
2378
+ if (count) {
2379
+ if (full) {
2380
+ scr_file_read_grow(out);
2381
+ scr_file_read_data(out)[out->len] = extra;
2382
+ }
2383
+ out->len += count;
2384
+ }
2385
+ if (error == EINTR) {
2386
+ if (stream) clearerr(stream);
2387
+ continue;
2388
+ }
2389
+ if (error) {
2390
+ /* Raw strings must be safe for the normal cache-aware destructor. */
2391
+ if (out->text) { out->text->len = out->len; out->text->data[out->len] = '\0'; }
2392
+ scr_str_release(out->text);
2393
+ free(out->bytes);
2394
+ *out = (ScrFileRead){0};
2395
+ return error;
2396
+ }
2397
+ if (!count) break;
2398
+ }
2399
+ if (out->text) { out->text->len = out->len; out->text->data[out->len] = '\0'; }
2400
+ // Do not retain a large hint after truncation or a tiny pipe result.
2401
+ if (out->cap > 256 && out->len < out->cap / 2) {
2402
+ if (out->text) out->text = scr_str_regrow(out->text, out->len);
2403
+ else {
2404
+ uint8_t *trimmed = realloc(out->bytes, out->len ? out->len : 1);
2405
+ if (trimmed) out->bytes = trimmed;
2406
+ }
2407
+ }
2408
+ return 0;
2409
+ }
2410
+
2302
2411
  static ScrStr *scr_fs_read_file_core(ScrStr *path, bool env_file) {
2303
2412
  FILE *f = scr_fs_fopen(path, "rb");
2304
2413
  if (!f) {
2305
2414
  scr_fs_throw(errno, "open", path);
2306
2415
  return NULL;
2307
2416
  }
2308
- size_t cap = 4096, len = 0;
2309
- char *buf = malloc(cap);
2310
- if (!buf) {
2311
- scr_trap("scriptc: out of memory\n");
2312
- }
2313
- for (;;) {
2314
- if (cap - len < 2048) {
2315
- cap *= 2;
2316
- char *grown = realloc(buf, cap);
2317
- if (!grown) {
2318
- scr_trap("scriptc: out of memory\n");
2319
- }
2320
- buf = grown;
2321
- }
2322
- size_t n = fread(buf + len, 1, cap - len, f);
2323
- len += n;
2324
- if (n == 0) break;
2325
- }
2326
- if (ferror(f)) {
2327
- int e = errno;
2328
- fclose(f);
2329
- free(buf);
2417
+ ScrFileRead out;
2418
+ int error = scr_file_read_all(f, -1, true, &out);
2419
+ fclose(f);
2420
+ if (error) {
2330
2421
  if (env_file) {
2331
2422
  ScrJsonBuf b;
2332
2423
  scr_jb_init(&b);
@@ -2336,13 +2427,20 @@ static ScrStr *scr_fs_read_file_core(ScrStr *path, bool env_file) {
2336
2427
  ScrStr *message = scr_jb_finish(&b);
2337
2428
  scr_throw_error_msg_code(SCR_ERR_TYPE, message->data, message->len, "ERR_INVALID_ARG_TYPE");
2338
2429
  scr_str_release(message);
2339
- } else scr_fs_throw(e, "read", path);
2430
+ } else scr_fs_throw(error, "read", path);
2340
2431
  return NULL;
2341
2432
  }
2433
+ return out.text;
2434
+ }
2435
+
2436
+ ScrBytes *scr_fs_read_file_bytes(ScrStr *path) {
2437
+ FILE *f = scr_fs_fopen(path, "rb");
2438
+ if (!f) { scr_fs_throw(errno, "open", path); return NULL; }
2439
+ ScrFileRead out;
2440
+ int error = scr_file_read_all(f, -1, false, &out);
2342
2441
  fclose(f);
2343
- ScrStr *s = scr_str_new(buf, len);
2344
- free(buf);
2345
- return s;
2442
+ if (error) { scr_fs_throw(error, "read", path); return NULL; }
2443
+ return scr_bytes_take_data(out.bytes, out.len);
2346
2444
  }
2347
2445
 
2348
2446
  ScrStr *scr_fs_read_file(ScrStr *path) {
@@ -3799,57 +3897,18 @@ static void scr_fs_throw_nopath(int e, const char *op) {
3799
3897
  scr_throw_error_msg_code(SCR_ERR_ERROR, msg, (size_t)len, name);
3800
3898
  }
3801
3899
 
3802
- /* read(2) loop to EOF from the CURRENT position — Node's
3803
- * readFileSync(fd) semantics for pipes and files alike (the stdin
3804
- * pattern: readFileSync(0, "utf8")). Returns the malloc'd buffer and
3805
- * its length, or NULL with the exception pending. */
3806
- static char *scr_read_fd_all(double fd, size_t *out_len) {
3807
- size_t cap = 4096, len = 0;
3808
- char *buf = malloc(cap);
3809
- if (!buf) {
3810
- scr_trap("scriptc: out of memory\n");
3811
- }
3812
- for (;;) {
3813
- if (cap - len < 2048) {
3814
- cap *= 2;
3815
- char *grown = realloc(buf, cap);
3816
- if (!grown) {
3817
- scr_trap("scriptc: out of memory\n");
3818
- }
3819
- buf = grown;
3820
- }
3821
- ssize_t n = read((int)fd, buf + len, cap - len);
3822
- if (n < 0) {
3823
- if (errno == EINTR) continue;
3824
- int e = errno;
3825
- free(buf);
3826
- scr_fs_throw_nopath(e, "read");
3827
- return NULL;
3828
- }
3829
- if (n == 0) break;
3830
- len += (size_t)n;
3831
- }
3832
- *out_len = len;
3833
- return buf;
3834
- }
3835
-
3836
3900
  ScrStr *scr_fs_read_fd(double fd) {
3837
- size_t len;
3838
- char *buf = scr_read_fd_all(fd, &len);
3839
- if (!buf) return NULL;
3840
- ScrStr *s = scr_str_new(buf, len);
3841
- free(buf);
3842
- return s;
3901
+ ScrFileRead out;
3902
+ int error = scr_file_read_all(NULL, (int)fd, true, &out);
3903
+ if (error) { scr_fs_throw_nopath(error, "read"); return NULL; }
3904
+ return out.text;
3843
3905
  }
3844
3906
 
3845
3907
  ScrBytes *scr_fs_read_fd_bytes(double fd) {
3846
- size_t len;
3847
- char *buf = scr_read_fd_all(fd, &len);
3848
- if (!buf) return NULL;
3849
- ScrBytes *b = scr_bytes_new(SCR_BYTES_U8, (double)len);
3850
- if (len > 0) memcpy(b->data, buf, len);
3851
- free(buf);
3852
- return b;
3908
+ ScrFileRead out;
3909
+ int error = scr_file_read_all(NULL, (int)fd, false, &out);
3910
+ if (error) { scr_fs_throw_nopath(error, "read"); return NULL; }
3911
+ return scr_bytes_take_data(out.bytes, out.len);
3853
3912
  }
3854
3913
 
3855
3914
  /* ── Atomics.wait: the synchronous-sleep idiom ───────────────────────
@@ -15,6 +15,7 @@
15
15
  * keep the lean 1-word header and never touch the collector.
16
16
  */
17
17
  #include "scr_runtime.h"
18
+ #include "scr_key.h"
18
19
 
19
20
  #include <stdio.h>
20
21
  #include <stdlib.h>
@@ -46,27 +47,14 @@ static uint64_t scr_map_f64_bits(double k) {
46
47
  return bits;
47
48
  }
48
49
 
49
- static uint64_t scr_map_fnv1a(const unsigned char *bytes, size_t n) {
50
- uint64_t h = UINT64_C(0xcbf29ce484222325);
51
- for (size_t i = 0; i < n; i++) {
52
- h ^= bytes[i];
53
- h *= UINT64_C(0x100000001b3);
54
- }
55
- return h;
56
- }
57
-
58
50
  static uint64_t scr_map_hash_str(const ScrStr *k) {
59
- return scr_map_fnv1a((const unsigned char *)k->data, k->len);
51
+ return scr_key_hash(k->data, k->len);
60
52
  }
61
53
 
62
54
  /* Mix the complete normalized number or identity word. Float exponents
63
55
  * and aligned pointers need high bits to reach the low bucket-index bits. */
64
56
  static uint64_t scr_map_hash_word(uint64_t value) {
65
- value ^= value >> 30;
66
- value *= UINT64_C(0xbf58476d1ce4e5b9);
67
- value ^= value >> 27;
68
- value *= UINT64_C(0x94d049bb133111eb);
69
- return value ^ (value >> 31);
57
+ return scr_key_mix(value);
70
58
  }
71
59
 
72
60
  /* ── slot packing (8-byte slots, like ScrArr) ──────────────────────────── */
@@ -209,8 +197,19 @@ static size_t scr_map_probe(const ScrMap *m, uint64_t hash, uint64_t key) {
209
197
  }
210
198
  }
211
199
 
200
+ #define SCR_MAP_LINEAR_LIMIT 4
201
+
202
+ /* Small collections keep only their ordered entries. Cached hashes make
203
+ * the bounded scan cheap without allocating a separate bucket table. */
212
204
  static size_t scr_map_find(const ScrMap *m, uint64_t hash, uint64_t key) {
213
- size_t slot = scr_map_probe(m, hash ? hash : 1, key);
205
+ hash = hash ? hash : 1;
206
+ if (m->nbuckets == 0) {
207
+ for (size_t e = 0; e < m->nentries; e++) {
208
+ if (m->entries[e].hash == hash && scr_map_key_eq(m, m->entries[e].key, key)) return e;
209
+ }
210
+ return SCR_MAP_EMPTY;
211
+ }
212
+ size_t slot = scr_map_probe(m, hash, key);
214
213
  return slot == SCR_MAP_EMPTY ? SCR_MAP_EMPTY : m->buckets[slot];
215
214
  }
216
215
 
@@ -248,12 +247,15 @@ static void scr_map_compact(ScrMap *m) {
248
247
  * reuse their storage) and grows otherwise; while an iteration is active it
249
248
  * ONLY grows — indices must stay stable under callback mutation. */
250
249
  static void scr_map_reserve_append(ScrMap *m) {
251
- if (m->nentries < m->ecap && m->nbuckets >= 2 * (m->nentries + 1)) return;
252
- if (m->iter_depth == 0 && m->nlive <= m->nentries / 2 && m->nentries > 0) {
250
+ if (m->nentries < m->ecap &&
251
+ ((m->nbuckets == 0 && m->nentries < SCR_MAP_LINEAR_LIMIT) ||
252
+ m->nbuckets >= 2 * (m->nentries + 1))) return;
253
+ if (m->iter_depth == 0 && m->nentries > 0 &&
254
+ (m->nbuckets == 0 ? m->nlive < m->nentries : m->nlive <= m->nentries / 2)) {
253
255
  scr_map_compact(m);
254
256
  }
255
257
  if (m->nentries == m->ecap) {
256
- size_t cap = m->ecap ? m->ecap : 8;
258
+ size_t cap = m->ecap ? m->ecap : 4;
257
259
  while (cap < m->nentries + 1) {
258
260
  if (cap > SIZE_MAX / 2 / sizeof(ScrMapEntry)) scr_map_oom();
259
261
  cap *= 2;
@@ -263,6 +265,7 @@ static void scr_map_reserve_append(ScrMap *m) {
263
265
  m->entries = entries;
264
266
  m->ecap = cap;
265
267
  }
268
+ if (m->nbuckets == 0 && m->nentries < SCR_MAP_LINEAR_LIMIT) return;
266
269
  if (m->nbuckets < 2 * (m->nentries + 1)) {
267
270
  size_t nbuckets = m->nbuckets ? m->nbuckets : 16;
268
271
  while (nbuckets < 2 * (m->nentries + 1)) {
@@ -469,7 +472,7 @@ bool scr_map_delete_ref(ScrMap *m, const void *key) {
469
472
  static void scr_map_set(ScrMap *m, uint64_t hash, uint64_t key, uint64_t val) {
470
473
  hash = hash ? hash : 1;
471
474
  size_t slot = scr_map_probe(m, hash, key);
472
- size_t e = slot == SCR_MAP_EMPTY ? SCR_MAP_EMPTY : m->buckets[slot];
475
+ size_t e = m->nbuckets == 0 ? scr_map_find(m, hash, key) : m->buckets[slot];
473
476
  if (e != SCR_MAP_EMPTY) {
474
477
  uint64_t old = m->entries[e].val;
475
478
  m->entries[e].val = val; /* unlink before releasing (cycle collector) */
@@ -480,7 +483,7 @@ static void scr_map_set(ScrMap *m, uint64_t hash, uint64_t key, uint64_t val) {
480
483
  scr_map_reserve_append(m);
481
484
  /* Growth or compaction can move the insertion bucket. Otherwise keep the
482
485
  * empty slot from the first probe instead of walking the chain twice. */
483
- if (m->nentries != old_entries || m->nbuckets != old_buckets) {
486
+ if (m->nbuckets && (m->nentries != old_entries || m->nbuckets != old_buckets)) {
484
487
  size_t mask = m->nbuckets - 1;
485
488
  slot = hash & mask;
486
489
  while (m->buckets[slot] != SCR_MAP_EMPTY) slot = (slot + 1) & mask;
@@ -510,7 +513,7 @@ static void scr_map_set(ScrMap *m, uint64_t hash, uint64_t key, uint64_t val) {
510
513
  } else if (scr_map_ref_key(m->key_kind)) {
511
514
  m->key_retain(scr_map_slot_to_ptr(key)); /* key is borrowed */
512
515
  }
513
- m->buckets[slot] = idx;
516
+ if (m->nbuckets) m->buckets[slot] = idx;
514
517
  }
515
518
 
516
519
  static void scr_map_set_f64_key(ScrMap *m, double key, uint64_t val) {
@@ -792,6 +795,7 @@ ScrMap *scr_map_clone(const ScrMap *source, bool keys_only) {
792
795
  else if (out->val_kind == SCR_MAP_VAL_REF) out->val_retain(scr_map_slot_to_ptr(copy->val));
793
796
  }
794
797
  out->nlive = count;
798
+ if (count <= SCR_MAP_LINEAR_LIMIT) return out;
795
799
  size_t buckets = 16;
796
800
  while (buckets < 2 * count) {
797
801
  if (buckets > SIZE_MAX / 2 / sizeof(size_t)) scr_map_oom();
@@ -890,59 +894,39 @@ void scr_set_add_all(ScrMap *set, ScrArr *values) {
890
894
  /* Canonical array index test: "0".."4294967294" — digits only, no leading
891
895
  * zero (except "0" itself), value <= 2^32 - 2. JS orders these OWN keys
892
896
  * first, ascending numerically, before every other string key. */
893
- static bool scr_map_key_array_index(const ScrStr *k, uint32_t *out) {
894
- size_t n = k->len;
895
- if (n == 0 || n > 10) return false;
896
- const char *s = k->data;
897
- if (s[0] == '0' && n > 1) return false;
898
- uint64_t v = 0;
899
- for (size_t i = 0; i < n; i++) {
900
- if (s[i] < '0' || s[i] > '9') return false;
901
- v = v * 10 + (uint64_t)(s[i] - '0');
902
- }
903
- if (v > UINT64_C(4294967294)) return false;
904
- *out = (uint32_t)v;
905
- return true;
897
+ static bool scr_map_key_array_index(const ScrStr *key, uint32_t *out) {
898
+ return scr_key_array_index(key->data, key->len, out);
906
899
  }
907
900
 
908
- /* The live STRING keys of `m` in JS OWN-KEY ORDER (Object.keys over the
909
- * index-signature overflow): integer-like keys (canonical array indices)
910
- * first in ascending numeric order, then the rest in insertion order.
911
- * Returns a fresh ScrStr* array (+1); the map is borrowed. Insertion sort
912
- * over the integer-like subset — overflow maps are small, and ties are
913
- * impossible (keys are unique). */
901
+ /* Enumerate numeric keys in ascending order, then other keys in insertion
902
+ * order. Scratch entries refer to stable map indices, not copied strings. */
914
903
  ScrArr *scr_map_keys_js_order(const ScrMap *m) {
915
- size_t n = m->nentries;
916
904
  ScrArr *out = scr_arr_new(SCR_ELEM_STR, m->nlive);
917
- size_t nidx = 0;
918
- struct { uint32_t v; ScrStr *k; } *idx = NULL;
919
- for (size_t i = 0; i < n; i++) {
905
+ if (m->nlive > SIZE_MAX / sizeof(ScrKeyIndex)) scr_map_oom();
906
+ ScrKeyIndex *indices = NULL;
907
+ size_t count = 0;
908
+ for (size_t i = 0; i < m->nentries; i++) {
920
909
  if (!m->entries[i].hash) continue;
921
- ScrStr *k = (ScrStr *)scr_map_slot_to_ptr(m->entries[i].key);
922
- uint32_t v;
923
- if (!scr_map_key_array_index(k, &v)) continue;
924
- if (nidx % 16 == 0) {
925
- idx = realloc(idx, (nidx + 16) * sizeof *idx);
926
- if (!idx) scr_map_oom();
910
+ ScrStr *key = scr_map_slot_to_ptr(m->entries[i].key);
911
+ uint32_t index;
912
+ if (!scr_map_key_array_index(key, &index)) continue;
913
+ if (!indices) {
914
+ indices = malloc(m->nlive * sizeof *indices);
915
+ if (!indices) scr_map_oom();
927
916
  }
928
- size_t j = nidx++;
929
- while (j > 0 && idx[j - 1].v > v) {
930
- idx[j] = idx[j - 1];
931
- j--;
932
- }
933
- idx[j].v = v;
934
- idx[j].k = k;
917
+ indices[count++] = (ScrKeyIndex){index, i};
935
918
  }
936
- for (size_t i = 0; i < nidx; i++) {
937
- scr_arr_push_ref(out, scr_str_retain(idx[i].k));
919
+ scr_key_index_sort(indices, count);
920
+ for (size_t i = 0; i < count; i++) {
921
+ ScrStr *key = scr_map_slot_to_ptr(m->entries[indices[i].entry].key);
922
+ scr_arr_push_ref(out, scr_str_retain(key));
938
923
  }
939
- free(idx);
940
- for (size_t i = 0; i < n; i++) {
924
+ free(indices);
925
+ for (size_t i = 0; i < m->nentries; i++) {
941
926
  if (!m->entries[i].hash) continue;
942
- ScrStr *k = (ScrStr *)scr_map_slot_to_ptr(m->entries[i].key);
943
- uint32_t v;
944
- if (scr_map_key_array_index(k, &v)) continue;
945
- scr_arr_push_ref(out, scr_str_retain(k));
927
+ ScrStr *key = scr_map_slot_to_ptr(m->entries[i].key);
928
+ uint32_t index;
929
+ if (!scr_map_key_array_index(key, &index)) scr_arr_push_ref(out, scr_str_retain(key));
946
930
  }
947
931
  return out;
948
932
  }
@@ -14,6 +14,7 @@
14
14
  * and the 1M-double fuzz gate (packages/runtime/test/gen-number-cases.mjs).
15
15
  */
16
16
  #include "scr_runtime.h"
17
+ #include "scr_numeric.h"
17
18
 
18
19
  #include <math.h>
19
20
  #include <stdint.h>
@@ -27,10 +28,7 @@
27
28
  /* ECMA ToUint32, shared by bitwise operators and split's limit: non-finite
28
29
  * values become zero, finite values truncate toward zero and wrap mod 2^32. */
29
30
  uint32_t scr_to_uint32(double d) {
30
- if (!isfinite(d)) return 0;
31
- double t = fmod(trunc(d), 4294967296.0);
32
- if (t < 0) t += 4294967296.0;
33
- return (uint32_t)t;
31
+ return scr_numeric_to_u32(d);
34
32
  }
35
33
 
36
34
  /* ── bitwise operators ─────────────────────────────────────────────────
@@ -0,0 +1,25 @@
1
+ /* Internal numeric coercions shared by scalar operators and typed storage. */
2
+ #ifndef SCR_NUMERIC_H
3
+ #define SCR_NUMERIC_H
4
+ #include <float.h>
5
+ #include <stdint.h>
6
+ #include <string.h>
7
+
8
+ _Static_assert(sizeof(double) == sizeof(uint64_t) && DBL_MANT_DIG == 53 && DBL_MAX_EXP == 1024,
9
+ "numeric coercions require binary64 doubles");
10
+
11
+ /* ToUint32 truncates toward zero and keeps the low 32 bits. Reading the
12
+ * binary64 exponent also handles non-finite values without floating modulo.
13
+ * Subnormals have magnitude below one; exponents >= 84 have no low bits. */
14
+ static inline uint32_t scr_numeric_to_u32(double value) {
15
+ uint64_t bits;
16
+ memcpy(&bits, &value, sizeof bits);
17
+ unsigned exponent = (unsigned)((bits >> 52) & 0x7ff);
18
+ if (exponent < 1023 || exponent >= 1023 + 84) return 0;
19
+ unsigned shift = exponent - 1023;
20
+ uint64_t significand = (bits & UINT64_C(0x000fffffffffffff)) | UINT64_C(0x0010000000000000);
21
+ uint32_t integer = shift >= 52 ? (uint32_t)(significand << (shift - 52))
22
+ : (uint32_t)(significand >> (52 - shift));
23
+ return bits >> 63 ? UINT32_C(0) - integer : integer;
24
+ }
25
+ #endif
@@ -0,0 +1,37 @@
1
+ #include "scr_numeric.h"
2
+ #include <assert.h>
3
+ #include <math.h>
4
+ #include <stdio.h>
5
+
6
+ /* Independent arithmetic oracle: do not derive expectations from bit fields. */
7
+ static uint32_t reference(double value) {
8
+ if (!isfinite(value)) return 0;
9
+ double residue = fmod(trunc(value), 4294967296.0);
10
+ if (residue < 0) residue += 4294967296.0;
11
+ return (uint32_t)residue;
12
+ }
13
+
14
+ static void check(uint64_t bits) {
15
+ double value;
16
+ memcpy(&value, &bits, sizeof value);
17
+ assert(scr_numeric_to_u32(value) == reference(value));
18
+ }
19
+
20
+ int main(void) {
21
+ uint64_t bits = UINT64_C(0x0123456789abcdef);
22
+ for (unsigned i = 0; i < 2000000; i++) {
23
+ bits ^= bits << 13;
24
+ bits ^= bits >> 7;
25
+ bits ^= bits << 17;
26
+ check(bits);
27
+ }
28
+ /* Both signs at every exponent, including non-finite and subnormal values. */
29
+ for (unsigned exponent = 0; exponent < 2048; exponent++) {
30
+ for (unsigned sign = 0; sign < 2; sign++) {
31
+ for (unsigned low = 0; low < 64; low++) {
32
+ check(((uint64_t)sign << 63) | ((uint64_t)exponent << 52) | low);
33
+ }
34
+ }
35
+ }
36
+ puts("numeric coercions passed");
37
+ }
@@ -0,0 +1,33 @@
1
+ import { execFile } from "node:child_process";
2
+ import { mkdtemp, rm } from "node:fs/promises";
3
+ import { tmpdir } from "node:os";
4
+ import { join } from "node:path";
5
+ import { promisify } from "node:util";
6
+ import { expect, test } from "vitest";
7
+
8
+ const exec = promisify(execFile);
9
+
10
+ test("integer coercion matches modular truncation across binary64 encodings", async () => {
11
+ const scratch = await mkdtemp(join(tmpdir(), "scriptc-numeric-"));
12
+ try {
13
+ const binary = join(scratch, "numeric");
14
+ await exec("clang", [
15
+ "-std=c11",
16
+ "-O2",
17
+ "-Wall",
18
+ "-Wextra",
19
+ "-fsanitize=address,undefined",
20
+ join(import.meta.dirname, "scr_numeric.test.c"),
21
+ "-o",
22
+ binary,
23
+ ...(process.platform === "linux" ? ["-lm"] : []),
24
+ ]);
25
+ const result = await exec(binary, [], {
26
+ env: { ...process.env, UBSAN_OPTIONS: "halt_on_error=1" },
27
+ });
28
+ expect(result.stdout).toBe("numeric coercions passed\n");
29
+ expect(result.stderr).toBe("");
30
+ } finally {
31
+ await rm(scratch, { recursive: true, force: true });
32
+ }
33
+ });