@scriptc/cli-darwin-x64 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 (123) 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-x64/artifacts/dev/runtime/default/scr_array.o +0 -0
  10. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_bytes.o +0 -0
  11. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_bytes_io.o +0 -0
  12. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_copying.o +0 -0
  13. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_json.o +0 -0
  14. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_lib.o +0 -0
  15. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_map.o +0 -0
  16. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_number.o +0 -0
  17. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_regex.o +0 -0
  18. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_string.o +0 -0
  19. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/default/scr_util_compare.o +0 -0
  20. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_array.o +0 -0
  21. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_bytes.o +0 -0
  22. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_bytes_io.o +0 -0
  23. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_copying.o +0 -0
  24. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_json.o +0 -0
  25. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_lib.o +0 -0
  26. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_map.o +0 -0
  27. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_number.o +0 -0
  28. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_regex.o +0 -0
  29. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_string.o +0 -0
  30. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic/scr_util_compare.o +0 -0
  31. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/dynamic-text-decoder-legacy/scr_bytes.o +0 -0
  32. package/dist/lib/runtime-darwin-x64/artifacts/dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  33. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_array.o +0 -0
  34. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_bytes.o +0 -0
  35. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_bytes_io.o +0 -0
  36. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_copying.o +0 -0
  37. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_json.o +0 -0
  38. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_lib.o +0 -0
  39. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_map.o +0 -0
  40. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_number.o +0 -0
  41. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_regex.o +0 -0
  42. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/default/scr_string.o +0 -0
  43. package/dist/lib/runtime-darwin-x64/artifacts/library-dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  44. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_array.o +0 -0
  45. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_bytes.o +0 -0
  46. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_bytes_io.o +0 -0
  47. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_copying.o +0 -0
  48. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_json.o +0 -0
  49. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_lib.o +0 -0
  50. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_map.o +0 -0
  51. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_number.o +0 -0
  52. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_regex.o +0 -0
  53. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/default/scr_string.o +0 -0
  54. package/dist/lib/runtime-darwin-x64/artifacts/library-release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  55. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_array.o +0 -0
  56. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_bytes.o +0 -0
  57. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_bytes_io.o +0 -0
  58. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_copying.o +0 -0
  59. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_json.o +0 -0
  60. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_lib.o +0 -0
  61. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_map.o +0 -0
  62. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_number.o +0 -0
  63. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_regex.o +0 -0
  64. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/default/scr_string.o +0 -0
  65. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-dev/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  66. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_array.o +0 -0
  67. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_bytes.o +0 -0
  68. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_bytes_io.o +0 -0
  69. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_copying.o +0 -0
  70. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_json.o +0 -0
  71. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_lib.o +0 -0
  72. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_map.o +0 -0
  73. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_number.o +0 -0
  74. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_regex.o +0 -0
  75. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/default/scr_string.o +0 -0
  76. package/dist/lib/runtime-darwin-x64/artifacts/library-thread-release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  77. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_array.o +0 -0
  78. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_bytes.o +0 -0
  79. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_bytes_io.o +0 -0
  80. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_copying.o +0 -0
  81. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_json.o +0 -0
  82. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_lib.o +0 -0
  83. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_map.o +0 -0
  84. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_number.o +0 -0
  85. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_regex.o +0 -0
  86. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_string.o +0 -0
  87. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/default/scr_util_compare.o +0 -0
  88. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_array.o +0 -0
  89. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_bytes.o +0 -0
  90. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_bytes_io.o +0 -0
  91. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_copying.o +0 -0
  92. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_json.o +0 -0
  93. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_lib.o +0 -0
  94. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_map.o +0 -0
  95. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_number.o +0 -0
  96. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_regex.o +0 -0
  97. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_string.o +0 -0
  98. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic/scr_util_compare.o +0 -0
  99. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/dynamic-text-decoder-legacy/scr_bytes.o +0 -0
  100. package/dist/lib/runtime-darwin-x64/artifacts/release/runtime/text-decoder-legacy/scr_bytes.o +0 -0
  101. package/dist/lib/runtime-darwin-x64/package.json +1 -1
  102. package/dist/lib/runtime-darwin-x64/runtime-pack.json +183 -183
  103. package/dist/lib/runtime-sources/package.json +1 -1
  104. package/dist/lib/runtime-sources/src/scr_array.c +267 -6
  105. package/dist/lib/runtime-sources/src/scr_async.test.ts +16 -6
  106. package/dist/lib/runtime-sources/src/scr_bytes.c +437 -216
  107. package/dist/lib/runtime-sources/src/scr_bytes.test.c +85 -0
  108. package/dist/lib/runtime-sources/src/scr_bytes_io.c +0 -34
  109. package/dist/lib/runtime-sources/src/scr_copying.c +12 -2
  110. package/dist/lib/runtime-sources/src/scr_json.c +479 -150
  111. package/dist/lib/runtime-sources/src/scr_json.test.c +323 -1
  112. package/dist/lib/runtime-sources/src/scr_json.test.ts +58 -25
  113. package/dist/lib/runtime-sources/src/scr_key.h +68 -0
  114. package/dist/lib/runtime-sources/src/scr_lib.c +132 -73
  115. package/dist/lib/runtime-sources/src/scr_map.c +50 -66
  116. package/dist/lib/runtime-sources/src/scr_number.c +2 -4
  117. package/dist/lib/runtime-sources/src/scr_numeric.h +25 -0
  118. package/dist/lib/runtime-sources/src/scr_numeric.test.c +37 -0
  119. package/dist/lib/runtime-sources/src/scr_numeric.test.ts +33 -0
  120. package/dist/lib/runtime-sources/src/scr_regex.c +238 -167
  121. package/dist/lib/runtime-sources/src/scr_runtime.h +37 -6
  122. package/dist/lib/runtime-sources/src/scr_string.c +155 -42
  123. package/package.json +1 -1
@@ -7,6 +7,7 @@
7
7
  * encoding conversions (utf8 with WHATWG replacement, hex, base64) match
8
8
  * Node byte-for-byte — the differential corpus holds them to it. */
9
9
  #include "scr_runtime.h"
10
+ #include "scr_numeric.h"
10
11
  #include "scr_text_decoder_labels.h"
11
12
  #ifdef SCR_TEXT_DECODER_LEGACY
12
13
  #include "scr_text_decoder_data.h"
@@ -116,13 +117,16 @@ double scr_bytes_to_u8_clamp(double value) {
116
117
 
117
118
  /* ── lifecycle ─────────────────────────────────────────────────────────── */
118
119
 
119
- static ScrBytes *scr_bytes_alloc(ScrBytesElem elem, size_t len) {
120
+ /* Adopt private malloc storage without clearing or copying its contents. The
121
+ * caller transfers ownership; views keep it alive through the usual owner. */
122
+ ScrBytes *scr_bytes_take_data(uint8_t *data, size_t len) {
123
+ if (!data && len) scr_trap("scriptc: NULL owned byte storage\n");
120
124
  ScrBytes *b = malloc(sizeof(ScrBytes));
121
125
  if (!b) scr_bytes_oom();
122
126
  b->rc = 1;
123
127
  b->len = len;
124
- b->elem = elem;
125
- b->data = calloc(len ? len : 1, scr_bytes_elem_size(elem));
128
+ b->elem = SCR_BYTES_U8;
129
+ b->data = data ? data : malloc(1);
126
130
  if (!b->data) scr_bytes_oom();
127
131
  b->backing = NULL;
128
132
  b->is_buffer = false;
@@ -134,6 +138,27 @@ static ScrBytes *scr_bytes_alloc(ScrBytesElem elem, size_t len) {
134
138
  return b;
135
139
  }
136
140
 
141
+ /* Only complete, non-observable initialization may use private storage. */
142
+ static ScrBytes *scr_bytes_alloc_private(ScrBytesElem elem, size_t len) {
143
+ size_t width = scr_bytes_elem_size(elem);
144
+ if (len > SIZE_MAX / width) scr_bytes_oom();
145
+ uint8_t *data = malloc(len ? len * width : 1);
146
+ if (!data) scr_bytes_oom();
147
+ ScrBytes *b = scr_bytes_take_data(data, len);
148
+ b->elem = elem;
149
+ return b;
150
+ }
151
+
152
+ static ScrBytes *scr_bytes_alloc(ScrBytesElem elem, size_t len) {
153
+ size_t width = scr_bytes_elem_size(elem);
154
+ if (len > SIZE_MAX / width) scr_bytes_oom();
155
+ uint8_t *data = calloc(len ? len : 1, width);
156
+ if (!data) scr_bytes_oom();
157
+ ScrBytes *b = scr_bytes_take_data(data, len);
158
+ b->elem = elem;
159
+ return b;
160
+ }
161
+
137
162
  ScrBytes *scr_bytes_from_external(void *data, size_t length) {
138
163
  ScrBytes *b = scr_bytes_alloc(SCR_BYTES_U8, 0);
139
164
  /* Keep the owned empty allocation for NULL/zero-length views: existing
@@ -168,13 +193,13 @@ ScrBytes *scr_bytes_from_data(const uint8_t *data, size_t len) {
168
193
  if (data == NULL && len != 0) {
169
194
  scr_trap("scriptc: native callback passed a NULL span with nonzero length\n");
170
195
  }
171
- ScrBytes *b = scr_bytes_alloc(SCR_BYTES_U8, len);
196
+ ScrBytes *b = scr_bytes_alloc_private(SCR_BYTES_U8, len);
172
197
  if (len != 0) memcpy(b->data, data, len);
173
198
  return b;
174
199
  }
175
200
 
176
201
  ScrBytes *scr_bytes_copy(const ScrBytes *src) {
177
- ScrBytes *b = scr_bytes_alloc(src->elem, src->len);
202
+ ScrBytes *b = scr_bytes_alloc_private(src->elem, src->len);
178
203
  memcpy(b->data, src->data, src->len * scr_bytes_elem_size(src->elem));
179
204
  return b;
180
205
  }
@@ -190,13 +215,6 @@ ScrBytes *scr_bytes_as_buffer(ScrBytes *bytes) {
190
215
  return scr_bytes_retain(bytes);
191
216
  }
192
217
 
193
- ScrBytes *scr_bytes_convert(ScrBytesElem elem, const ScrBytes *src) {
194
- if (elem == src->elem) return scr_bytes_copy(src);
195
- ScrBytes *out = scr_bytes_alloc(elem, src->len);
196
- for (size_t i = 0; i < src->len; i++) scr_bytes_set(out, (double)i, scr_bytes_get(src, (double)i));
197
- return out;
198
- }
199
-
200
218
  void scr_bytes_release(ScrBytes *b) {
201
219
  if (!b || b->rc == SIZE_MAX) return; /* NULL: an uninitialized `let` local */
202
220
  if (--b->rc == 0) {
@@ -246,11 +264,7 @@ static size_t scr_bytes_check_index(const ScrBytes *b, double i) {
246
264
  /* ToUint32: NaN/±Infinity → 0, truncate toward zero, wrap mod 2^32.
247
265
  * ToUint8 is its low byte (2^8 divides 2^32, so the residues agree). */
248
266
  static uint32_t scr_bytes_to_u32(double v) {
249
- if (v != v || isinf(v)) return 0;
250
- double t = trunc(v);
251
- t = fmod(t, 4294967296.0);
252
- if (t < 0) t += 4294967296.0;
253
- return (uint32_t)t;
267
+ return scr_numeric_to_u32(v);
254
268
  }
255
269
 
256
270
  double scr_bytes_get(const ScrBytes *b, double i) {
@@ -336,6 +350,86 @@ void scr_bytes_set(ScrBytes *b, double i, double v) {
336
350
  }
337
351
  }
338
352
 
353
+ /* Dispatch once per bounded block, not once per element. Loads and stores
354
+ * use memcpy so views and external storage need no stronger alignment or
355
+ * aliasing promise. A bounded scratch block avoids a kind-pair code matrix. */
356
+ static void scr_bytes_read_numbers(double *out, const uint8_t *src, ScrBytesElem elem, size_t count) {
357
+ #define SCR_READ_NUMBERS(kind, type) \
358
+ case kind: \
359
+ for (size_t i = 0; i < count; i++) { \
360
+ type value; \
361
+ memcpy(&value, src + i * sizeof value, sizeof value); \
362
+ out[i] = (double)value; \
363
+ } \
364
+ return
365
+ switch (elem) {
366
+ case SCR_BYTES_U8C:
367
+ SCR_READ_NUMBERS(SCR_BYTES_U8, uint8_t);
368
+ SCR_READ_NUMBERS(SCR_BYTES_I8, int8_t);
369
+ SCR_READ_NUMBERS(SCR_BYTES_U16, uint16_t);
370
+ SCR_READ_NUMBERS(SCR_BYTES_I16, int16_t);
371
+ SCR_READ_NUMBERS(SCR_BYTES_U32, uint32_t);
372
+ SCR_READ_NUMBERS(SCR_BYTES_I32, int32_t);
373
+ SCR_READ_NUMBERS(SCR_BYTES_F32, float);
374
+ SCR_READ_NUMBERS(SCR_BYTES_F64, double);
375
+ }
376
+ #undef SCR_READ_NUMBERS
377
+ }
378
+
379
+ static void scr_bytes_write_numbers(uint8_t *dst, ScrBytesElem elem, const double *src, size_t count) {
380
+ #define SCR_WRITE_NUMBERS(type, expression) \
381
+ for (size_t i = 0; i < count; i++) { \
382
+ type value = (expression); \
383
+ memcpy(dst + i * sizeof value, &value, sizeof value); \
384
+ } \
385
+ return
386
+ switch (elem) {
387
+ case SCR_BYTES_U8: case SCR_BYTES_I8: {
388
+ SCR_WRITE_NUMBERS(uint8_t, (uint8_t)scr_numeric_to_u32(src[i]));
389
+ }
390
+ case SCR_BYTES_U8C: {
391
+ SCR_WRITE_NUMBERS(uint8_t, (uint8_t)scr_bytes_to_u8_clamp(src[i]));
392
+ }
393
+ case SCR_BYTES_U16: case SCR_BYTES_I16: {
394
+ SCR_WRITE_NUMBERS(uint16_t, (uint16_t)scr_numeric_to_u32(src[i]));
395
+ }
396
+ case SCR_BYTES_U32: case SCR_BYTES_I32: {
397
+ SCR_WRITE_NUMBERS(uint32_t, scr_numeric_to_u32(src[i]));
398
+ }
399
+ case SCR_BYTES_F32: {
400
+ SCR_WRITE_NUMBERS(float, (float)src[i]);
401
+ }
402
+ case SCR_BYTES_F64: {
403
+ memcpy(dst, src, count * sizeof(double));
404
+ return;
405
+ }
406
+ }
407
+ #undef SCR_WRITE_NUMBERS
408
+ }
409
+
410
+ /* Caller guarantees disjoint spans, or snapshots the source before entry. */
411
+ static void scr_bytes_convert_span(uint8_t *dst, ScrBytesElem dest_elem,
412
+ const uint8_t *src, ScrBytesElem src_elem, size_t count) {
413
+ size_t source_width = scr_bytes_elem_size(src_elem);
414
+ size_t dest_width = scr_bytes_elem_size(dest_elem);
415
+ double values[128];
416
+ while (count) {
417
+ size_t block = count < 128 ? count : 128;
418
+ scr_bytes_read_numbers(values, src, src_elem, block);
419
+ scr_bytes_write_numbers(dst, dest_elem, values, block);
420
+ src += block * source_width;
421
+ dst += block * dest_width;
422
+ count -= block;
423
+ }
424
+ }
425
+
426
+ ScrBytes *scr_bytes_convert(ScrBytesElem elem, const ScrBytes *src) {
427
+ if (elem == src->elem) return scr_bytes_copy(src);
428
+ ScrBytes *out = scr_bytes_alloc_private(elem, src->len);
429
+ scr_bytes_convert_span(out->data, elem, src->data, src->elem, src->len);
430
+ return out;
431
+ }
432
+
339
433
  /* ── slice (copy) / subarray (view) ────────────────────────────────────── */
340
434
 
341
435
  /* Relative index: ToIntegerOrInfinity, negatives from the end, clamped. */
@@ -353,7 +447,7 @@ ScrBytes *scr_bytes_slice(const ScrBytes *b, double start, double end) {
353
447
  size_t e = scr_bytes_rel_index(end, b->len);
354
448
  size_t count = e > s ? e - s : 0;
355
449
  size_t esize = scr_bytes_elem_size(b->elem);
356
- ScrBytes *out = scr_bytes_alloc(b->elem, count);
450
+ ScrBytes *out = scr_bytes_alloc_private(b->elem, count);
357
451
  memcpy(out->data, b->data + s * esize, count * esize);
358
452
  return out;
359
453
  }
@@ -376,7 +470,23 @@ ScrBytes *scr_bytes_copy_within(ScrBytes *b, double target, double start, double
376
470
  ScrBytes *scr_bytes_fill_elem(ScrBytes *b, double v, double start, double end) {
377
471
  size_t s = scr_bytes_rel_index(start, b->len);
378
472
  size_t e = scr_bytes_rel_index(end, b->len);
379
- for (size_t i = s; i < e; i++) scr_bytes_set(b, (double)i, v);
473
+ if (e > s) {
474
+ size_t width = scr_bytes_elem_size(b->elem);
475
+ uint8_t pattern[8];
476
+ scr_bytes_write_numbers(pattern, b->elem, &v, 1);
477
+ uint8_t *dst = b->data + s * width;
478
+ size_t bytes = (e - s) * width;
479
+ if (width == 1) memset(dst, pattern[0], bytes);
480
+ else {
481
+ memcpy(dst, pattern, width);
482
+ size_t filled = width;
483
+ while (filled < bytes) {
484
+ size_t copy = filled < bytes - filled ? filled : bytes - filled;
485
+ memcpy(dst + filled, dst, copy);
486
+ filled += copy;
487
+ }
488
+ }
489
+ }
380
490
  return scr_bytes_retain(b);
381
491
  }
382
492
 
@@ -414,16 +524,27 @@ void scr_bytes_set_from(ScrBytes *dst, const ScrBytes *src, double offset) {
414
524
  scr_throw_error_msg(SCR_ERR_RANGE, msg, sizeof msg - 1);
415
525
  return;
416
526
  }
417
- if (dst->elem != src->elem) {
418
- // Snapshot before writing: views can overlap even with distinct kinds.
419
- ScrBytes *copy = scr_bytes_convert(dst->elem, src);
420
- memcpy(dst->data + (size_t)t * scr_bytes_elem_size(dst->elem), copy->data,
421
- copy->len * scr_bytes_elem_size(copy->elem));
422
- scr_bytes_release(copy);
527
+ size_t width = scr_bytes_elem_size(dst->elem);
528
+ uint8_t *target = dst->data + (size_t)t * width;
529
+ if (dst->elem == src->elem) {
530
+ memmove(target, src->data, src->len * width);
423
531
  return;
424
532
  }
425
- size_t esize = scr_bytes_elem_size(dst->elem);
426
- memmove(dst->data + (size_t)t * esize, src->data, src->len * esize);
533
+ size_t source_bytes = src->len * scr_bytes_elem_size(src->elem);
534
+ size_t dest_bytes = src->len * width;
535
+ uintptr_t to = (uintptr_t)target, from = (uintptr_t)src->data;
536
+ bool overlap = to <= from ? from - to < dest_bytes : to - from < source_bytes;
537
+ if (overlap) {
538
+ /* Snapshot raw source bytes before cross-kind writes. Distinct external
539
+ * wrappers can alias even without a shared backing identity. */
540
+ uint8_t *copy = malloc(source_bytes ? source_bytes : 1);
541
+ if (!copy) scr_bytes_oom();
542
+ memcpy(copy, src->data, source_bytes);
543
+ scr_bytes_convert_span(target, dst->elem, copy, src->elem, src->len);
544
+ free(copy);
545
+ } else {
546
+ scr_bytes_convert_span(target, dst->elem, src->data, src->elem, src->len);
547
+ }
427
548
  }
428
549
 
429
550
  /* Checked-native array-like sources. Runtime objects with an unsupported
@@ -766,79 +887,85 @@ static void scr_td_invalid(const char *encoding) {
766
887
  scr_throw_error_msg_code(SCR_ERR_TYPE, message, (size_t)n, "ERR_ENCODING_INVALID_ENCODED_DATA");
767
888
  }
768
889
 
769
- static ScrStr *scr_bytes_decode_utf8_options(const uint8_t *in, size_t n, bool fatal) {
770
- if (in == NULL && n != 0) {
771
- scr_trap("scriptc: native callback passed a NULL span with nonzero length\n");
890
+ /* Stop before the first non-ASCII byte; every word load stays in the span. */
891
+ static size_t scr_bytes_ascii_prefix(const uint8_t *data, size_t len) {
892
+ size_t i = 0;
893
+ while (len - i >= sizeof(uint64_t)) {
894
+ uint64_t word;
895
+ memcpy(&word, data + i, sizeof word);
896
+ if (word & UINT64_C(0x8080808080808080)) break;
897
+ i += sizeof word;
772
898
  }
773
- if (n > (SIZE_MAX - 1) / 3) scr_bytes_oom();
774
- char *out = malloc(n * 3 + 1);
775
- if (!out) scr_bytes_oom();
776
- size_t o = 0;
777
- uint32_t cp = 0;
778
- int needed = 0;
779
- uint8_t lower = 0x80, upper = 0xbf;
780
- for (size_t i = 0; i <= n; i++) {
781
- if (i == n) {
782
- if (needed > 0) { /* EOF inside a sequence: one replacement */
783
- if (fatal) { free(out); scr_td_invalid("utf-8"); return NULL; }
784
- memcpy(out + o, "\xef\xbf\xbd", 3);
785
- o += 3;
786
- }
787
- break;
788
- }
789
- uint8_t byte = in[i];
790
- if (needed == 0) {
791
- if (byte <= 0x7f) {
792
- out[o++] = (char)byte;
793
- } else if (byte >= 0xc2 && byte <= 0xdf) {
794
- needed = 1; cp = byte & 0x1f;
795
- } else if (byte >= 0xe0 && byte <= 0xef) {
796
- if (byte == 0xe0) lower = 0xa0;
797
- if (byte == 0xed) upper = 0x9f;
798
- needed = 2; cp = byte & 0xf;
799
- } else if (byte >= 0xf0 && byte <= 0xf4) {
800
- if (byte == 0xf0) lower = 0x90;
801
- if (byte == 0xf4) upper = 0x8f;
802
- needed = 3; cp = byte & 0x7;
803
- } else {
804
- if (fatal) { free(out); scr_td_invalid("utf-8"); return NULL; }
805
- memcpy(out + o, "\xef\xbf\xbd", 3);
806
- o += 3;
807
- }
808
- continue;
809
- }
810
- if (byte < lower || byte > upper) {
811
- /* Invalid continuation: replace the subpart, REPROCESS this byte. */
812
- if (fatal) { free(out); scr_td_invalid("utf-8"); return NULL; }
813
- memcpy(out + o, "\xef\xbf\xbd", 3);
814
- o += 3;
815
- needed = 0; lower = 0x80; upper = 0xbf;
816
- i--;
817
- continue;
818
- }
819
- lower = 0x80; upper = 0xbf;
820
- cp = (cp << 6) | (byte & 0x3f);
821
- if (--needed == 0) {
822
- /* Re-encode the (valid, non-surrogate, <= 10FFFF) code point. */
823
- if (cp <= 0x7f) {
824
- out[o++] = (char)cp;
825
- } else if (cp <= 0x7ff) {
826
- out[o++] = (char)(0xc0 | (cp >> 6));
827
- out[o++] = (char)(0x80 | (cp & 0x3f));
828
- } else if (cp <= 0xffff) {
829
- out[o++] = (char)(0xe0 | (cp >> 12));
830
- out[o++] = (char)(0x80 | ((cp >> 6) & 0x3f));
831
- out[o++] = (char)(0x80 | (cp & 0x3f));
832
- } else {
833
- out[o++] = (char)(0xf0 | (cp >> 18));
834
- out[o++] = (char)(0x80 | ((cp >> 12) & 0x3f));
835
- out[o++] = (char)(0x80 | ((cp >> 6) & 0x3f));
836
- out[o++] = (char)(0x80 | (cp & 0x3f));
899
+ while (i < len && data[i] < 0x80) i++;
900
+ return i;
901
+ }
902
+
903
+ /* Length of the well-formed prefix. On failure, next skips exactly the
904
+ * maximal invalid subpart; an offending continuation is reprocessed. */
905
+ static size_t scr_bytes_utf8_prefix(const uint8_t *data, size_t len, size_t *next) {
906
+ size_t i = 0;
907
+ while (i < len) {
908
+ i += scr_bytes_ascii_prefix(data + i, len - i);
909
+ if (i == len) break;
910
+ size_t start = i;
911
+ unsigned byte = data[i++], count;
912
+ unsigned low = 0x80, high = 0xbf;
913
+ if (byte < 0x80) continue;
914
+ if (byte >= 0xc2 && byte <= 0xdf) count = 1;
915
+ else if (byte >= 0xe0 && byte <= 0xef) {
916
+ count = 2;
917
+ if (byte == 0xe0) low = 0xa0;
918
+ if (byte == 0xed) high = 0x9f;
919
+ } else if (byte >= 0xf0 && byte <= 0xf4) {
920
+ count = 3;
921
+ if (byte == 0xf0) low = 0x90;
922
+ if (byte == 0xf4) high = 0x8f;
923
+ } else { *next = i; return start; }
924
+ for (unsigned j = 0; j < count; j++) {
925
+ if (i == len || data[i] < low || data[i] > high) {
926
+ *next = i;
927
+ return start;
837
928
  }
929
+ i++;
930
+ low = 0x80;
931
+ high = 0xbf;
838
932
  }
839
933
  }
840
- ScrStr *s = scr_str_new(out, o);
841
- free(out);
934
+ *next = len;
935
+ return len;
936
+ }
937
+
938
+ /* Measure first, then write into final string storage. Invalid input can
939
+ * expand, but neither valid prefixes nor retained output need a 3x buffer. */
940
+ static size_t scr_bytes_utf8_repair(const uint8_t *in, size_t n,
941
+ size_t prefix, size_t next, char *out) {
942
+ size_t written = 0, offset = 0;
943
+ for (;;) {
944
+ if (prefix > SIZE_MAX - written) scr_bytes_oom();
945
+ if (out && prefix) memcpy(out + written, in + offset, prefix);
946
+ written += prefix;
947
+ if (prefix == n - offset) return written;
948
+ if (written > SIZE_MAX - 3) scr_bytes_oom();
949
+ if (out) memcpy(out + written, "\xef\xbf\xbd", 3);
950
+ written += 3;
951
+ offset += next;
952
+ prefix = scr_bytes_utf8_prefix(in + offset, n - offset, &next);
953
+ }
954
+ }
955
+
956
+ static ScrStr *scr_bytes_decode_utf8_options(const uint8_t *in, size_t n, bool fatal) {
957
+ if (in == NULL && n != 0) {
958
+ scr_trap("scriptc: native callback passed a NULL span with nonzero length\n");
959
+ }
960
+ if (n == 0) return scr_str_new("", 0);
961
+ size_t next;
962
+ size_t prefix = scr_bytes_utf8_prefix(in, n, &next);
963
+ if (prefix == n) return scr_str_new((const char *)in, n);
964
+ if (fatal) { scr_td_invalid("utf-8"); return NULL; }
965
+ size_t length = scr_bytes_utf8_repair(in, n, prefix, next, NULL);
966
+ ScrStr *s = scr_str_alloc_raw(length, length);
967
+ scr_bytes_utf8_repair(in, n, prefix, next, s->data);
968
+ s->data[length] = '\0';
842
969
  return s;
843
970
  }
844
971
 
@@ -1109,23 +1236,30 @@ static ScrStr *scr_bytes_encode_b64(const uint8_t *in, size_t n, bool url) {
1109
1236
  static const char url_abc[] =
1110
1237
  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
1111
1238
  const char *abc = url ? url_abc : std_abc;
1112
- size_t outlen = (n + 2) / 3 * 4;
1113
- char *out = malloc(outlen + 1);
1114
- if (!out) scr_bytes_oom();
1115
- size_t o = 0;
1116
- for (size_t i = 0; i < n; i += 3) {
1239
+ size_t groups = n / 3, tail = n % 3;
1240
+ size_t suffix = tail ? (url ? tail + 1 : 4) : 0;
1241
+ if (groups > (SIZE_MAX - suffix) / 4) scr_bytes_oom();
1242
+ size_t outlen = groups * 4 + suffix;
1243
+ ScrStr *s = scr_str_alloc_raw(outlen, outlen);
1244
+ char *out = s->data;
1245
+ size_t i = 0, o = 0;
1246
+ for (; i < groups * 3; i += 3) {
1247
+ unsigned v = ((unsigned)in[i] << 16) | ((unsigned)in[i + 1] << 8) | in[i + 2];
1248
+ out[o++] = abc[v >> 18];
1249
+ out[o++] = abc[(v >> 12) & 63];
1250
+ out[o++] = abc[(v >> 6) & 63];
1251
+ out[o++] = abc[v & 63];
1252
+ }
1253
+ if (tail) {
1117
1254
  unsigned v = (unsigned)in[i] << 16;
1118
- if (i + 1 < n) v |= (unsigned)in[i + 1] << 8;
1119
- if (i + 2 < n) v |= (unsigned)in[i + 2];
1120
- out[o++] = abc[(v >> 18) & 63];
1255
+ if (tail == 2) v |= (unsigned)in[i + 1] << 8;
1256
+ out[o++] = abc[v >> 18];
1121
1257
  out[o++] = abc[(v >> 12) & 63];
1122
- if (i + 1 < n) out[o++] = abc[(v >> 6) & 63];
1123
- else if (!url) out[o++] = '=';
1124
- if (i + 2 < n) out[o++] = abc[v & 63];
1258
+ if (tail == 2) out[o++] = abc[(v >> 6) & 63];
1125
1259
  else if (!url) out[o++] = '=';
1260
+ if (!url) out[o++] = '=';
1126
1261
  }
1127
- ScrStr *s = scr_str_new(out, o);
1128
- free(out);
1262
+ out[outlen] = '\0';
1129
1263
  return s;
1130
1264
  }
1131
1265
 
@@ -1817,7 +1951,12 @@ ScrDyn *scr_text_encode_into(const ScrDyn *source, const ScrDyn *destination) {
1817
1951
  const ScrStr *text = source->v.str;
1818
1952
  ScrBytes *dest = destination->v.bytes;
1819
1953
  size_t read = 0, written = 0;
1820
- while (written < text->len) {
1954
+ size_t limit = text->len < dest->len ? text->len : dest->len;
1955
+ while (written < limit) {
1956
+ size_t ascii = scr_bytes_ascii_prefix((const uint8_t *)text->data + written, limit - written);
1957
+ written += ascii;
1958
+ read += ascii;
1959
+ if (written == limit) break;
1821
1960
  size_t end = written;
1822
1961
  uint32_t cp = scr_bytes_next_cp((const uint8_t *)text->data, &end);
1823
1962
  if (end > dest->len) break;
@@ -1840,33 +1979,14 @@ static ScrBytes *scr_buffer_validation_input(const ScrDyn *input) {
1840
1979
  }
1841
1980
 
1842
1981
  static bool scr_buffer_utf8_valid(const uint8_t *data, size_t len) {
1843
- size_t i = 0;
1844
- while (i < len) {
1845
- unsigned byte = data[i++], count;
1846
- unsigned low = 0x80, high = 0xbf;
1847
- if (byte < 0x80) continue;
1848
- if (byte >= 0xc2 && byte <= 0xdf) count = 1;
1849
- else if (byte >= 0xe0 && byte <= 0xef) {
1850
- count = 2;
1851
- if (byte == 0xe0) low = 0xa0;
1852
- if (byte == 0xed) high = 0x9f;
1853
- } else if (byte >= 0xf0 && byte <= 0xf4) {
1854
- count = 3;
1855
- if (byte == 0xf0) low = 0x90;
1856
- if (byte == 0xf4) high = 0x8f;
1857
- } else return false;
1858
- if (count > len - i || data[i] < low || data[i] > high) return false;
1859
- i++;
1860
- for (unsigned j = 1; j < count; j++, i++) if (data[i] < 0x80 || data[i] > 0xbf) return false;
1861
- }
1862
- return true;
1982
+ size_t next;
1983
+ return scr_bytes_utf8_prefix(data, len, &next) == len;
1863
1984
  }
1864
1985
 
1865
1986
  bool scr_buffer_is_ascii(const ScrDyn *input) {
1866
1987
  ScrBytes *bytes = scr_buffer_validation_input(input);
1867
1988
  if (!bytes) return false;
1868
- bool result = true;
1869
- for (size_t i = 0; i < bytes->len; i++) if (bytes->data[i] > 0x7f) { result = false; break; }
1989
+ bool result = scr_bytes_ascii_prefix(bytes->data, bytes->len) == bytes->len;
1870
1990
  scr_bytes_release(bytes);
1871
1991
  return result;
1872
1992
  }
@@ -2021,25 +2141,28 @@ static bool scr_enc_is(const ScrStr *enc, const char *name) {
2021
2141
  * surrogate becomes U+FFFD — Node keeps it as a lone UTF-16 unit, which
2022
2142
  * UTF-8 string storage cannot hold (SEMANTICS.md divergence; stdout
2023
2143
  * agrees byte-for-byte because Node's own write replaces it there too). */
2144
+ /* Consume one scalar, replacing an unpaired surrogate at the storage boundary. */
2145
+ static uint32_t scr_bytes_utf16le_cp(const uint8_t *in, size_t units, size_t *u) {
2146
+ uint32_t cu = (uint32_t)in[*u * 2] | ((uint32_t)in[*u * 2 + 1] << 8);
2147
+ (*u)++;
2148
+ if (cu >= 0xd800 && cu <= 0xdbff && *u < units) {
2149
+ uint32_t lo = (uint32_t)in[*u * 2] | ((uint32_t)in[*u * 2 + 1] << 8);
2150
+ if (lo >= 0xdc00 && lo <= 0xdfff) {
2151
+ (*u)++;
2152
+ return 0x10000 + ((cu - 0xd800) << 10) + (lo - 0xdc00);
2153
+ }
2154
+ }
2155
+ return cu >= 0xd800 && cu <= 0xdfff ? 0xfffd : cu;
2156
+ }
2157
+
2024
2158
  static ScrStr *scr_bytes_decode_utf16le(const uint8_t *in, size_t n) {
2025
2159
  size_t units = n / 2;
2026
- char *out = malloc(units * 3 + 2);
2027
- if (!out) scr_bytes_oom();
2160
+ if (units > SIZE_MAX / 3) scr_bytes_oom();
2161
+ ScrStr *s = scr_str_alloc_raw(0, units * 3);
2028
2162
  size_t o = 0;
2029
- for (size_t u = 0; u < units; u++) {
2030
- uint32_t cu = (uint32_t)in[u * 2] | ((uint32_t)in[u * 2 + 1] << 8);
2031
- if (cu >= 0xd800 && cu <= 0xdbff && u + 1 < units) {
2032
- uint32_t lo = (uint32_t)in[(u + 1) * 2] | ((uint32_t)in[(u + 1) * 2 + 1] << 8);
2033
- if (lo >= 0xdc00 && lo <= 0xdfff) {
2034
- o = scr_bytes_put_cp(out, o, 0x10000 + ((cu - 0xd800) << 10) + (lo - 0xdc00));
2035
- u++;
2036
- continue;
2037
- }
2038
- }
2039
- o = scr_bytes_put_cp(out, o, cu >= 0xd800 && cu <= 0xdfff ? 0xfffd : cu);
2040
- }
2041
- ScrStr *s = scr_str_new(out, o);
2042
- free(out);
2163
+ for (size_t u = 0; u < units;) o = scr_bytes_put_cp(s->data, o, scr_bytes_utf16le_cp(in, units, &u));
2164
+ s->len = o;
2165
+ s->data[o] = '\0';
2043
2166
  return s;
2044
2167
  }
2045
2168
 
@@ -2049,35 +2172,34 @@ ScrStr *scr_bytes_to_str(const ScrBytes *b, const ScrStr *enc) {
2049
2172
  /* Implicit native string/number coercions have no encoding argument. */
2050
2173
  if (!enc) return scr_bytes_decode_utf8(in, n);
2051
2174
  if (scr_enc_is(enc, "hex")) {
2052
- char *out = malloc(n * 2 + 1);
2053
- if (!out) scr_bytes_oom();
2175
+ if (n > SIZE_MAX / 2) scr_bytes_oom();
2176
+ ScrStr *s = scr_str_alloc_raw(n * 2, n * 2);
2177
+ char *out = s->data;
2054
2178
  for (size_t i = 0; i < n; i++) {
2055
2179
  out[i * 2] = scr_hex_digits[in[i] >> 4];
2056
2180
  out[i * 2 + 1] = scr_hex_digits[in[i] & 0xf];
2057
2181
  }
2058
- ScrStr *s = scr_str_new(out, n * 2);
2059
- free(out);
2182
+ out[n * 2] = '\0';
2060
2183
  return s;
2061
2184
  }
2062
2185
  if (scr_enc_is(enc, "base64")) return scr_bytes_encode_b64(in, n, false);
2063
2186
  if (scr_enc_is(enc, "base64url")) return scr_bytes_encode_b64(in, n, true);
2064
2187
  if (scr_enc_is(enc, "latin1")) {
2065
2188
  /* Each byte is U+00XX ("binary" normalizes here upstream). */
2066
- char *out = malloc(n * 2 + 1);
2067
- if (!out) scr_bytes_oom();
2189
+ size_t high = 0;
2190
+ for (size_t i = 0; i < n; i++) high += in[i] >> 7;
2191
+ if (n > SIZE_MAX - high) scr_bytes_oom();
2192
+ ScrStr *s = scr_str_alloc_raw(n + high, n + high);
2068
2193
  size_t o = 0;
2069
- for (size_t i = 0; i < n; i++) o = scr_bytes_put_cp(out, o, in[i]);
2070
- ScrStr *s = scr_str_new(out, o);
2071
- free(out);
2194
+ for (size_t i = 0; i < n; i++) o = scr_bytes_put_cp(s->data, o, in[i]);
2195
+ s->data[o] = '\0';
2072
2196
  return s;
2073
2197
  }
2074
2198
  if (scr_enc_is(enc, "ascii")) {
2075
2199
  /* Node's ascii decode masks the high bit: byte & 0x7f. */
2076
- char *out = malloc(n + 1);
2077
- if (!out) scr_bytes_oom();
2078
- for (size_t i = 0; i < n; i++) out[i] = (char)(in[i] & 0x7f);
2079
- ScrStr *s = scr_str_new(out, n);
2080
- free(out);
2200
+ ScrStr *s = scr_str_alloc_raw(n, n);
2201
+ for (size_t i = 0; i < n; i++) s->data[i] = (char)(in[i] & 0x7f);
2202
+ s->data[n] = '\0';
2081
2203
  return s;
2082
2204
  }
2083
2205
  if (scr_enc_is(enc, "utf16le")) return scr_bytes_decode_utf16le(in, n);
@@ -2158,13 +2280,72 @@ static int scr_hex_val(uint8_t c) {
2158
2280
  return -1;
2159
2281
  }
2160
2282
 
2161
- static int scr_b64_val(uint8_t c) {
2162
- if (c >= 'A' && c <= 'Z') return c - 'A';
2163
- if (c >= 'a' && c <= 'z') return c - 'a' + 26;
2164
- if (c >= '0' && c <= '9') return c - '0' + 52;
2165
- if (c == '+' || c == '-') return 62; /* '-': base64url, accepted like Node */
2166
- if (c == '/' || c == '_') return 63; /* '_': base64url */
2167
- return -1;
2283
+ /* Both alphabets are accepted by both Buffer base64 encoding names. */
2284
+ static const int8_t scr_b64_values[256] = {
2285
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2286
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2287
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, 62, -1, 63,
2288
+ 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
2289
+ -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
2290
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
2291
+ -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
2292
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
2293
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2294
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2295
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2296
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2297
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2298
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2299
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2300
+ -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
2301
+ };
2302
+
2303
+ /* Conversion destinations are private until their initialized length is set.
2304
+ * General typed-array allocation must remain zero-filled. */
2305
+ static ScrBytes *scr_bytes_conversion_storage(size_t capacity) {
2306
+ uint8_t *data = malloc(capacity ? capacity : 1);
2307
+ if (!data) scr_bytes_oom();
2308
+ return scr_bytes_take_data(data, 0);
2309
+ }
2310
+
2311
+ static ScrBytes *scr_bytes_decode_b64(const uint8_t *in, size_t n) {
2312
+ ScrBytes *b = scr_bytes_conversion_storage(n / 4 * 3 + 2);
2313
+ size_t i = 0, o = 0;
2314
+ unsigned acc = 0, have = 0;
2315
+ while (i < n) {
2316
+ /* Common complete groups need neither per-character branches nor state. */
2317
+ if (have == 0 && n - i >= 4) {
2318
+ int a = scr_b64_values[in[i]], c = scr_b64_values[in[i + 1]];
2319
+ int d = scr_b64_values[in[i + 2]], e = scr_b64_values[in[i + 3]];
2320
+ if ((a | c | d | e) >= 0) {
2321
+ unsigned v = ((unsigned)a << 18) | ((unsigned)c << 12) | ((unsigned)d << 6) | (unsigned)e;
2322
+ b->data[o++] = (uint8_t)(v >> 16);
2323
+ b->data[o++] = (uint8_t)(v >> 8);
2324
+ b->data[o++] = (uint8_t)v;
2325
+ i += 4;
2326
+ continue;
2327
+ }
2328
+ }
2329
+ uint8_t byte = in[i++];
2330
+ if (byte == '=') break;
2331
+ int v = scr_b64_values[byte];
2332
+ if (v < 0) continue;
2333
+ acc = (acc << 6) | (unsigned)v;
2334
+ if (++have == 4) {
2335
+ b->data[o++] = (uint8_t)(acc >> 16);
2336
+ b->data[o++] = (uint8_t)(acc >> 8);
2337
+ b->data[o++] = (uint8_t)acc;
2338
+ acc = 0;
2339
+ have = 0;
2340
+ }
2341
+ }
2342
+ if (have == 2) b->data[o++] = (uint8_t)(acc >> 4);
2343
+ else if (have == 3) {
2344
+ b->data[o++] = (uint8_t)(acc >> 10);
2345
+ b->data[o++] = (uint8_t)(acc >> 2);
2346
+ }
2347
+ b->len = o;
2348
+ return b;
2168
2349
  }
2169
2350
 
2170
2351
  ScrBytes *scr_bytes_from_str(const ScrStr *s, const ScrStr *enc) {
@@ -2173,7 +2354,7 @@ ScrBytes *scr_bytes_from_str(const ScrStr *s, const ScrStr *enc) {
2173
2354
  if (scr_enc_is(enc, "latin1") || scr_enc_is(enc, "ascii")) {
2174
2355
  /* Node writes charCodeAt(i) & 0xFF for BOTH spellings — an astral
2175
2356
  * code point contributes its two surrogates' low bytes. */
2176
- ScrBytes *b = scr_bytes_alloc(SCR_BYTES_U8, n);
2357
+ ScrBytes *b = scr_bytes_conversion_storage(n);
2177
2358
  size_t o = 0, i = 0;
2178
2359
  while (i < n) {
2179
2360
  uint32_t cp = scr_bytes_next_cp(in, &i);
@@ -2189,7 +2370,8 @@ ScrBytes *scr_bytes_from_str(const ScrStr *s, const ScrStr *enc) {
2189
2370
  return b;
2190
2371
  }
2191
2372
  if (scr_enc_is(enc, "utf16le")) {
2192
- ScrBytes *b = scr_bytes_alloc(SCR_BYTES_U8, n * 2);
2373
+ if (n > SIZE_MAX / 2) scr_bytes_oom();
2374
+ ScrBytes *b = scr_bytes_conversion_storage(n * 2);
2193
2375
  size_t o = 0, i = 0;
2194
2376
  while (i < n) {
2195
2377
  uint32_t cp = scr_bytes_next_cp(in, &i);
@@ -2208,61 +2390,70 @@ ScrBytes *scr_bytes_from_str(const ScrStr *s, const ScrStr *enc) {
2208
2390
  b->len = o;
2209
2391
  return b;
2210
2392
  }
2211
- if (enc->len == 3 && memcmp(enc->data, "hex", 3) == 0) {
2212
- /* Node-lenient: parse pairs, stop at the first invalid pair or the
2213
- * odd tail (Buffer.from("a1g2", "hex") is <Buffer a1>). */
2214
- ScrBytes *b = scr_bytes_alloc(SCR_BYTES_U8, n / 2);
2215
- size_t o = 0;
2216
- for (size_t i = 0; i + 1 < n; i += 2) {
2217
- int hi = scr_hex_val(in[i]);
2218
- int lo = scr_hex_val(in[i + 1]);
2219
- if (hi < 0 || lo < 0) break;
2220
- b->data[o++] = (uint8_t)((hi << 4) | lo);
2221
- }
2222
- b->len = o; /* never grows: shrinking the count is safe */
2223
- return b;
2224
- }
2225
- if (scr_enc_is(enc, "base64") || scr_enc_is(enc, "base64url")) {
2226
- /* Node-lenient: skip bytes outside the (standard + url-safe)
2227
- * alphabets — whitespace, '=', stray punctuation; decode 4-char
2228
- * groups, and a 2/3-char tail into 1/2 bytes. Both spellings share
2229
- * the decoder (Node accepts either alphabet under either name). */
2230
- ScrBytes *b = scr_bytes_alloc(SCR_BYTES_U8, n / 4 * 3 + 2);
2231
- size_t o = 0;
2232
- unsigned acc = 0;
2233
- int have = 0;
2234
- for (size_t i = 0; i < n; i++) {
2235
- int v = scr_b64_val(in[i]);
2236
- if (v < 0) continue;
2237
- acc = (acc << 6) | (unsigned)v;
2238
- if (++have == 4) {
2239
- b->data[o++] = (uint8_t)(acc >> 16);
2240
- b->data[o++] = (uint8_t)(acc >> 8);
2241
- b->data[o++] = (uint8_t)acc;
2242
- acc = 0;
2243
- have = 0;
2393
+ bool hex = scr_enc_is(enc, "hex");
2394
+ if (hex || scr_enc_is(enc, "base64") || scr_enc_is(enc, "base64url")) {
2395
+ /* Node interprets encoded text as the low byte of each UTF-16 unit.
2396
+ * ASCII can borrow storage; non-ASCII must preserve that truncation. */
2397
+ ScrBytes *narrowed = NULL;
2398
+ if (scr_bytes_ascii_prefix(in, n) != n) {
2399
+ ScrStr *latin1 = scr_str_new("latin1", 6);
2400
+ narrowed = scr_bytes_from_str(s, latin1);
2401
+ scr_str_release(latin1);
2402
+ in = narrowed->data;
2403
+ n = narrowed->len;
2404
+ }
2405
+ ScrBytes *b;
2406
+ if (hex) {
2407
+ /* Stop at the first invalid pair, discarding an odd tail. */
2408
+ b = scr_bytes_conversion_storage(n / 2);
2409
+ size_t o = 0;
2410
+ for (size_t i = 0; i + 1 < n; i += 2) {
2411
+ int hi = scr_hex_val(in[i]), lo = scr_hex_val(in[i + 1]);
2412
+ if (hi < 0 || lo < 0) break;
2413
+ b->data[o++] = (uint8_t)((hi << 4) | lo);
2244
2414
  }
2415
+ b->len = o;
2416
+ } else {
2417
+ b = scr_bytes_decode_b64(in, n);
2245
2418
  }
2246
- if (have == 2) b->data[o++] = (uint8_t)(acc >> 4);
2247
- else if (have == 3) {
2248
- b->data[o++] = (uint8_t)(acc >> 10);
2249
- b->data[o++] = (uint8_t)(acc >> 2);
2250
- }
2251
- b->len = o;
2419
+ scr_bytes_release(narrowed);
2252
2420
  return b;
2253
2421
  }
2254
2422
  /* utf8: ScrStr storage IS the bytes */
2255
- ScrBytes *b = scr_bytes_alloc(SCR_BYTES_U8, n);
2423
+ ScrBytes *b = scr_bytes_conversion_storage(n);
2256
2424
  memcpy(b->data, in, n);
2425
+ b->len = n;
2257
2426
  return b;
2258
2427
  }
2259
2428
 
2429
+ /* Runtime encoding variables share the output conversion alias/error table.
2430
+ * Buffer.from treats an empty encoding as utf8, even for an empty source. */
2431
+ ScrBytes *scr_bytes_from_str_checked(const ScrStr *s, const ScrStr *enc) {
2432
+ ScrStr *normalized = enc->len ? scr_bytes_normalize_encoding(enc) : scr_str_new("utf8", 4);
2433
+ if (!normalized) return NULL;
2434
+ ScrBytes *out = scr_bytes_from_str(s, normalized);
2435
+ scr_str_release(normalized);
2436
+ return out;
2437
+ }
2438
+
2260
2439
  ScrBytes *scr_bytes_from_arr(ScrBytesElem elem, const ScrArr *arr) {
2261
- ScrBytes *b = scr_bytes_alloc(elem, arr->len);
2262
- for (size_t i = 0; i < arr->len; i++) {
2263
- // Iteration reads holes as undefined: floating arrays store NaN and
2264
- // integer arrays store zero through their normal element conversion.
2265
- scr_bytes_set(b, (double)i, scr_arr_get_number(arr, (double)i));
2440
+ ScrBytes *b = scr_bytes_alloc_private(elem, arr->len);
2441
+ size_t width = scr_bytes_elem_size(elem);
2442
+ double values[128];
2443
+ for (size_t start = 0; start < arr->len;) {
2444
+ size_t count = arr->len - start < 128 ? arr->len - start : 128;
2445
+ for (size_t i = 0; i < count; i++) {
2446
+ size_t index = start + i;
2447
+ /* Sparse tails and explicit undefined retain ordinary iteration
2448
+ * semantics. Numeric array slots own no conversion callbacks. */
2449
+ if (index < arr->cap) {
2450
+ if (arr->present[index] == SCR_ARR_VALUE)
2451
+ memcpy(&values[i], &arr->data[index], sizeof(double));
2452
+ else values[i] = NAN;
2453
+ } else values[i] = scr_arr_get_number(arr, (double)index);
2454
+ }
2455
+ scr_bytes_write_numbers(b->data + start * width, elem, values, count);
2456
+ start += count;
2266
2457
  }
2267
2458
  return b;
2268
2459
  }
@@ -2487,6 +2678,36 @@ double scr_bytes_write_str(ScrBytes *b, const ScrStr *s, const ScrStr *enc,
2487
2678
  } else {
2488
2679
  budget = remaining;
2489
2680
  }
2681
+ /* UTF-8 already has its final representation. Copy only the requested
2682
+ * prefix, backing off when the destination ends inside a scalar. */
2683
+ if (scr_enc_is(enc, "utf8")) {
2684
+ size_t k = s->len < budget ? s->len : budget;
2685
+ if (k < s->len) {
2686
+ while (k > 0 && ((uint8_t)s->data[k] & 0xc0) == 0x80) k--;
2687
+ }
2688
+ if (k) memcpy(b->data + o, s->data, k);
2689
+ return (double)k;
2690
+ }
2691
+ bool utf16 = scr_enc_is(enc, "utf16le");
2692
+ if (utf16 || scr_enc_is(enc, "latin1") || scr_enc_is(enc, "ascii")) {
2693
+ size_t i = 0, written = 0, width = utf16 ? 2 : 1;
2694
+ while (i < s->len && budget - written >= width) {
2695
+ uint32_t cp = scr_bytes_next_cp((const uint8_t *)s->data, &i);
2696
+ uint32_t first = cp, second = 0;
2697
+ if (cp > 0xffff) {
2698
+ cp -= 0x10000;
2699
+ first = 0xd800 + (cp >> 10);
2700
+ second = 0xdc00 + (cp & 0x3ff);
2701
+ }
2702
+ b->data[o + written++] = (uint8_t)first;
2703
+ if (utf16) b->data[o + written++] = (uint8_t)(first >> 8);
2704
+ if (second && budget - written >= width) {
2705
+ b->data[o + written++] = (uint8_t)second;
2706
+ if (utf16) b->data[o + written++] = (uint8_t)(second >> 8);
2707
+ }
2708
+ }
2709
+ return (double)written;
2710
+ }
2490
2711
  ScrBytes *encd = scr_bytes_from_str(s, enc);
2491
2712
  size_t k = encd->len < budget ? encd->len : budget;
2492
2713
  if (k < encd->len) {