json-as 1.3.6 → 1.3.7

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 (106) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/assembly/deserialize/helpers/uint.ts +4 -1
  3. package/assembly/deserialize/index/arbitrary.ts +5 -1
  4. package/assembly/deserialize/index/array.ts +13 -3
  5. package/assembly/deserialize/index/integer.ts +68 -1
  6. package/assembly/deserialize/index/string.ts +4 -1
  7. package/assembly/deserialize/index/typedarray.ts +13 -3
  8. package/assembly/deserialize/index/unsigned.ts +78 -1
  9. package/assembly/deserialize/simd/array/integer.ts +327 -50
  10. package/assembly/deserialize/simd/integer.ts +233 -0
  11. package/assembly/deserialize/simd/string.ts +45 -11
  12. package/assembly/deserialize/simple/arbitrary.ts +11 -4
  13. package/assembly/deserialize/simple/array/arbitrary.ts +24 -5
  14. package/assembly/deserialize/simple/array/array.ts +8 -2
  15. package/assembly/deserialize/simple/array/bool.ts +38 -7
  16. package/assembly/deserialize/simple/array/box.ts +8 -2
  17. package/assembly/deserialize/simple/array/float.ts +36 -9
  18. package/assembly/deserialize/simple/array/generic.ts +12 -4
  19. package/assembly/deserialize/simple/array/integer.ts +8 -2
  20. package/assembly/deserialize/simple/array/map.ts +26 -6
  21. package/assembly/deserialize/simple/array/object.ts +26 -6
  22. package/assembly/deserialize/simple/array/raw.ts +34 -7
  23. package/assembly/deserialize/simple/array/string.ts +8 -2
  24. package/assembly/deserialize/simple/array/struct.ts +26 -6
  25. package/assembly/deserialize/simple/array.ts +13 -3
  26. package/assembly/deserialize/simple/bool.ts +6 -2
  27. package/assembly/deserialize/simple/float.ts +6 -1
  28. package/assembly/deserialize/simple/integer.ts +10 -2
  29. package/assembly/deserialize/simple/map.ts +95 -22
  30. package/assembly/deserialize/simple/object.ts +63 -14
  31. package/assembly/deserialize/simple/raw.ts +4 -1
  32. package/assembly/deserialize/simple/set.ts +59 -14
  33. package/assembly/deserialize/simple/staticarray/string.ts +11 -3
  34. package/assembly/deserialize/simple/staticarray.ts +64 -14
  35. package/assembly/deserialize/simple/string.ts +5 -92
  36. package/assembly/deserialize/simple/struct.ts +5 -1
  37. package/assembly/deserialize/simple/typedarray.ts +16 -3
  38. package/assembly/deserialize/simple/unsigned.ts +10 -15
  39. package/assembly/deserialize/swar/array/arbitrary.ts +5 -1
  40. package/assembly/deserialize/swar/array/array.ts +30 -6
  41. package/assembly/deserialize/swar/array/bool.ts +22 -4
  42. package/assembly/deserialize/swar/array/box.ts +5 -1
  43. package/assembly/deserialize/swar/array/float.ts +15 -3
  44. package/assembly/deserialize/swar/array/generic.ts +24 -7
  45. package/assembly/deserialize/swar/array/integer.ts +328 -84
  46. package/assembly/deserialize/swar/array/map.ts +5 -1
  47. package/assembly/deserialize/swar/array/object.ts +27 -7
  48. package/assembly/deserialize/swar/array/raw.ts +5 -1
  49. package/assembly/deserialize/swar/array/shared.ts +36 -11
  50. package/assembly/deserialize/swar/array/string.ts +20 -4
  51. package/assembly/deserialize/swar/array/struct.ts +27 -7
  52. package/assembly/deserialize/swar/array.ts +19 -4
  53. package/assembly/deserialize/swar/integer.ts +246 -0
  54. package/assembly/deserialize/swar/string.ts +98 -194
  55. package/assembly/index.d.ts +3 -1
  56. package/assembly/index.ts +312 -81
  57. package/assembly/serialize/index/float.ts +5 -1
  58. package/assembly/serialize/index/typedarray.ts +25 -7
  59. package/assembly/serialize/simd/string.ts +6 -2
  60. package/assembly/serialize/simple/array.ts +179 -1
  61. package/assembly/serialize/simple/float.ts +4 -1
  62. package/assembly/serialize/simple/integer.ts +8 -9
  63. package/assembly/serialize/simple/map.ts +6 -2
  64. package/assembly/serialize/simple/raw.ts +5 -1
  65. package/assembly/serialize/simple/set.ts +6 -1
  66. package/assembly/serialize/simple/staticarray.ts +6 -1
  67. package/assembly/serialize/simple/string.ts +0 -1
  68. package/assembly/serialize/simple/typedarray.ts +10 -3
  69. package/assembly/serialize/swar/string.ts +18 -5
  70. package/assembly/util/atoi-fast.ts +81 -0
  71. package/assembly/util/concat.ts +5 -1
  72. package/assembly/util/dragonbox-cache.ts +443 -2
  73. package/assembly/util/dragonbox.ts +43 -14
  74. package/assembly/util/itoa-fast.ts +230 -0
  75. package/assembly/util/masks.ts +18 -1
  76. package/assembly/util/parsefloat-fast.ts +167 -0
  77. package/assembly/util/simd-int.ts +191 -0
  78. package/assembly/util/snp.ts +4 -1
  79. package/assembly/util/swar-int.ts +248 -0
  80. package/assembly/util/swar.ts +13 -3
  81. package/lib/as-bs.ts +13 -5
  82. package/package.json +5 -2
  83. package/transform/lib/builder.d.ts.map +1 -1
  84. package/transform/lib/builder.js +13 -5
  85. package/transform/lib/builder.js.map +1 -1
  86. package/transform/lib/index.d.ts +1 -0
  87. package/transform/lib/index.d.ts.map +1 -1
  88. package/transform/lib/index.js +1030 -241
  89. package/transform/lib/index.js.map +1 -1
  90. package/transform/lib/linkers/alias.d.ts.map +1 -1
  91. package/transform/lib/linkers/alias.js.map +1 -1
  92. package/transform/lib/linkers/custom.d.ts.map +1 -1
  93. package/transform/lib/linkers/custom.js +3 -2
  94. package/transform/lib/linkers/custom.js.map +1 -1
  95. package/transform/lib/linkers/imports.d.ts.map +1 -1
  96. package/transform/lib/linkers/imports.js.map +1 -1
  97. package/transform/lib/types.d.ts.map +1 -1
  98. package/transform/lib/types.js +54 -16
  99. package/transform/lib/types.js.map +1 -1
  100. package/transform/lib/util.d.ts.map +1 -1
  101. package/transform/lib/util.js +1 -1
  102. package/transform/lib/util.js.map +1 -1
  103. package/transform/lib/visitor.d.ts.map +1 -1
  104. package/transform/lib/visitor.js +2 -1
  105. package/transform/lib/visitor.js.map +1 -1
  106. package/assembly/custom/util.ts +0 -310
@@ -1,5 +1,6 @@
1
1
  import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
2
2
  import { deserializeIntegerArray_SLOW } from "../../swar/array/integer";
3
+ import { isSpace } from "../../../util";
3
4
 
4
5
  const ASCII_LANE_MASK_4: u64 = 0x00ff00ff00ff00ff;
5
6
  const ASCII_ZERO_4: u64 = 0x0030003000300030;
@@ -17,43 +18,33 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
17
18
  @lazy const ZERO_I32X4 = i32x4.splat(0);
18
19
 
19
20
 
20
- @lazy const PACK_WEIGHTS_10_1 = i8x16(10, 1, 10, 1, 10, 1, 10, 1, 0, 0, 0, 0, 0, 0, 0, 0);
21
+ @lazy const PACK_WEIGHTS_10_1 = i8x16(
22
+ 10,
23
+ 1,
24
+ 10,
25
+ 1,
26
+ 10,
27
+ 1,
28
+ 10,
29
+ 1,
30
+ 0,
31
+ 0,
32
+ 0,
33
+ 0,
34
+ 0,
35
+ 0,
36
+ 0,
37
+ 0,
38
+ );
21
39
 
22
40
 
23
41
  @lazy const PAIR_WEIGHTS_100_1 = i16x8(100, 1, 100, 1, 0, 0, 0, 0);
24
42
 
25
43
 
26
- @inline function pushSignedInteger<T extends number[]>(out: T, value: i64): void {
27
- if (sizeof<valueof<T>>() == sizeof<i8>()) {
28
- out.push(<valueof<T>>(<i8>value));
29
- } else if (sizeof<valueof<T>>() == sizeof<i16>()) {
30
- out.push(<valueof<T>>(<i16>value));
31
- } else if (sizeof<valueof<T>>() == sizeof<i32>()) {
32
- out.push(<valueof<T>>(<i32>value));
33
- } else if (sizeof<valueof<T>>() == sizeof<isize>()) {
34
- out.push(<valueof<T>>(<isize>value));
35
- } else {
36
- out.push(<valueof<T>>value);
37
- }
38
- }
39
-
40
-
41
- @inline function pushUnsignedInteger<T extends number[]>(out: T, value: u64): void {
42
- if (sizeof<valueof<T>>() == sizeof<u8>()) {
43
- out.push(<valueof<T>>(<u8>value));
44
- } else if (sizeof<valueof<T>>() == sizeof<u16>()) {
45
- out.push(<valueof<T>>(<u16>value));
46
- } else if (sizeof<valueof<T>>() == sizeof<u32>()) {
47
- out.push(<valueof<T>>(<u32>value));
48
- } else if (sizeof<valueof<T>>() == sizeof<usize>()) {
49
- out.push(<valueof<T>>(<usize>value));
50
- } else {
51
- out.push(<valueof<T>>value);
52
- }
53
- }
54
-
55
-
56
- @inline function storeSignedInteger<T extends number[]>(slot: usize, value: i64): void {
44
+ @inline function storeSignedInteger<T extends number[]>(
45
+ slot: usize,
46
+ value: i64,
47
+ ): void {
57
48
  if (sizeof<valueof<T>>() == sizeof<i8>()) {
58
49
  store<i8>(slot, <i8>value);
59
50
  } else if (sizeof<valueof<T>>() == sizeof<i16>()) {
@@ -68,7 +59,10 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
68
59
  }
69
60
 
70
61
 
71
- @inline function storeUnsignedInteger<T extends number[]>(slot: usize, value: u64): void {
62
+ @inline function storeUnsignedInteger<T extends number[]>(
63
+ slot: usize,
64
+ value: u64,
65
+ ): void {
72
66
  if (sizeof<valueof<T>>() == sizeof<u8>()) {
73
67
  store<u8>(slot, <u8>value);
74
68
  } else if (sizeof<valueof<T>>() == sizeof<u16>()) {
@@ -99,8 +93,16 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
99
93
  return value * 100000000 + (<u64>lo * 10000 + <u64>hi);
100
94
  }
101
95
 
96
+ // As in the SWAR variant: the parse helpers take a `slot` (`writePtr`) and
97
+ // store directly. The dispatcher owns `out.length = maxElements` and the
98
+ // per-element `writePtr` advance so `Array.push` is removed for every
99
+ // integer width, not just the narrow-lane fast path.
102
100
 
103
- @inline function parseSignedIntegerSIMD<T extends number[]>(srcStart: usize, srcEnd: usize, out: T): usize {
101
+ @inline function parseSignedIntegerSIMD<T extends number[]>(
102
+ srcStart: usize,
103
+ srcEnd: usize,
104
+ slot: usize,
105
+ ): usize {
104
106
  let negative = false;
105
107
  let code = load<u16>(srcStart);
106
108
  if (code == 45) {
@@ -130,12 +132,16 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
130
132
  srcStart += 2;
131
133
  }
132
134
 
133
- pushSignedInteger<T>(out, negative ? -(<i64>value) : <i64>value);
135
+ storeSignedInteger<T>(slot, negative ? -(<i64>value) : <i64>value);
134
136
  return srcStart;
135
137
  }
136
138
 
137
139
 
138
- @inline function parseUnsignedIntegerSIMD<T extends number[]>(srcStart: usize, srcEnd: usize, out: T): usize {
140
+ @inline function parseUnsignedIntegerSIMD<T extends number[]>(
141
+ srcStart: usize,
142
+ srcEnd: usize,
143
+ slot: usize,
144
+ ): usize {
139
145
  let digit = <u32>load<u16>(srcStart) - 48;
140
146
  if (digit > 9) return 0;
141
147
 
@@ -156,13 +162,248 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
156
162
  srcStart += 2;
157
163
  }
158
164
 
159
- pushUnsignedInteger<T>(out, value);
165
+ storeUnsignedInteger<T>(slot, value);
160
166
  return srcStart;
161
167
  }
162
168
 
169
+
170
+ @lazy const COMMA_SPLAT_8 = i16x8.splat(<i16>COMMA);
171
+
172
+ // Pair-multiply weights for the common two-element packings in 8-char blocks.
173
+ // Lanes that fall on a `,` become garbage after the subtract-by-`0` but are
174
+ // killed by zero weights in `i32x4.dot_i16x8_s`. The 9 (a, b) combinations
175
+ // where a+b in {2..6} all reach via these weight vectors; the bottom three
176
+ // (one digit + one digit, etc.) are left to the SWAR cascade fallback since
177
+ // they hit <2% on the uniform 0..255 sweep and add branch cost here.
178
+ // 3+3 (`DDD,DDD,`) bitmask 0x88, advance 16
179
+ // 3+2 (`DDD,DD,?`) bitmask 0x48, advance 14
180
+ // 2+3 (`DD,DDD,?`) bitmask 0x44, advance 14
181
+ // 2+2 (`DD,DD,??`) bitmask 0x24, advance 12
182
+ // 3+1 (`DDD,D,??`) bitmask 0x28, advance 12
183
+ // 1+3 (`D,DDD,??`) bitmask 0x22, advance 12
184
+ @lazy const PAIR_WEIGHTS_3_3_8 = i16x8(100, 10, 1, 0, 100, 10, 1, 0);
185
+
186
+
187
+ @lazy const PAIR_WEIGHTS_3_2_8 = i16x8(100, 10, 1, 0, 10, 1, 0, 0);
188
+
189
+
190
+ @lazy const PAIR_WEIGHTS_2_3_8 = i16x8(10, 1, 0, 100, 10, 1, 0, 0);
191
+
192
+
193
+ @lazy const PAIR_WEIGHTS_2_2_8 = i16x8(10, 1, 0, 10, 1, 0, 0, 0);
194
+
195
+
196
+ @lazy const PAIR_WEIGHTS_3_1_8 = i16x8(100, 10, 1, 0, 1, 0, 0, 0);
197
+
198
+
199
+ @lazy const PAIR_WEIGHTS_1_3_8 = i16x8(1, 0, 100, 10, 1, 0, 0, 0);
200
+
201
+
202
+ @lazy const SPLAT_30_8 = i16x8.splat(0x30);
203
+
204
+ /**
205
+ * Narrow-lane (u8/i8) integer-array deserializer for SIMD mode.
206
+ *
207
+ * The 8-digit SIMD kernel (`tryParseEightDigitsSIMD`) can't fire for u8/i8
208
+ * (max 3 digits), so we skip it entirely and instead pay SIMD cost where
209
+ * it's a strict win: pre-counting commas with a v128 stride. The exact
210
+ * pre-size lets the parse pass use unchecked pointer stores in place of
211
+ * `Array.push`, eliminating its per-element capacity check / length write.
212
+ */
213
+ function deserializeNarrowIntegerArray_SIMD<T extends number[]>(
214
+ srcStart: usize,
215
+ srcEnd: usize,
216
+ dst: usize,
217
+ ): T {
218
+ const out = changetype<nonnull<T>>(
219
+ dst || changetype<usize>(instantiate<T>()),
220
+ );
221
+
222
+ // See SWAR variant: worst-case sizing is `(srcLen / 4)` elements (1 digit
223
+ // + 1 delimiter per element). Zero-fill is skipped for u8/i8 in AS so the
224
+ // over-allocation costs only a small amount of trimmed storage.
225
+ const maxElements = i32((<usize>(srcEnd - srcStart)) >> 2);
226
+ if (maxElements > 0) out.length = maxElements;
227
+ const dataStart = out.dataStart;
228
+ const elementSize = sizeof<valueof<T>>();
229
+ let writePtr = dataStart;
230
+
231
+ while (srcStart < srcEnd) {
232
+ // Fast path: two packed 3-digit elements in one v128 load. Pattern
233
+ // matches `DDD,DDD,` (commas at lanes 3 and 7) which is the majority of
234
+ // pairs for any payload whose values frequently land in 100..255.
235
+ // `i32x4.dot_i16x8_s` with the (100, 10, 1, 0) weights collapses the two
236
+ // 3-digit folds into the dot lanes, then a pair of extracts+adds gives
237
+ // both values without per-element scalar loops.
238
+ if (!isSigned<valueof<T>>() && srcStart + 14 < srcEnd) {
239
+ const block = load<v128>(srcStart);
240
+ const bitmask = i16x8.bitmask(i16x8.eq(block, COMMA_SPLAT_8));
241
+ // Switch on the comma layout. Each branch is a single i32x4.dot with
242
+ // its own (a, b) weight vector, plus 2-3 lane extracts that AS lowers
243
+ // to constant-index reads. Ordered roughly by hit-rate on a uniform
244
+ // 0..255 sweep (0x88 ~37%, 0x48/0x44 ~21% each, 0x24 ~12%, 0x28/0x22
245
+ // ~2.4% each).
246
+ switch (bitmask) {
247
+ case 0x88: {
248
+ const digits = i16x8.sub(block, SPLAT_30_8);
249
+ const dot = i32x4.dot_i16x8_s(digits, PAIR_WEIGHTS_3_3_8);
250
+ const v1 = i32x4.extract_lane(dot, 0) + i32x4.extract_lane(dot, 1);
251
+ const v2 = i32x4.extract_lane(dot, 2) + i32x4.extract_lane(dot, 3);
252
+ store<valueof<T>>(writePtr, <valueof<T>>v1);
253
+ store<valueof<T>>(writePtr + elementSize, <valueof<T>>v2);
254
+ writePtr += elementSize << 1;
255
+ srcStart += 16;
256
+ continue;
257
+ }
258
+ case 0x48: {
259
+ const digits = i16x8.sub(block, SPLAT_30_8);
260
+ const dot = i32x4.dot_i16x8_s(digits, PAIR_WEIGHTS_3_2_8);
261
+ const v1 = i32x4.extract_lane(dot, 0) + i32x4.extract_lane(dot, 1);
262
+ const v2 = i32x4.extract_lane(dot, 2);
263
+ store<valueof<T>>(writePtr, <valueof<T>>v1);
264
+ store<valueof<T>>(writePtr + elementSize, <valueof<T>>v2);
265
+ writePtr += elementSize << 1;
266
+ srcStart += 14;
267
+ continue;
268
+ }
269
+ case 0x44: {
270
+ const digits = i16x8.sub(block, SPLAT_30_8);
271
+ const dot = i32x4.dot_i16x8_s(digits, PAIR_WEIGHTS_2_3_8);
272
+ const v1 = i32x4.extract_lane(dot, 0);
273
+ const v2 = i32x4.extract_lane(dot, 1) + i32x4.extract_lane(dot, 2);
274
+ store<valueof<T>>(writePtr, <valueof<T>>v1);
275
+ store<valueof<T>>(writePtr + elementSize, <valueof<T>>v2);
276
+ writePtr += elementSize << 1;
277
+ srcStart += 14;
278
+ continue;
279
+ }
280
+ case 0x24: {
281
+ const digits = i16x8.sub(block, SPLAT_30_8);
282
+ const dot = i32x4.dot_i16x8_s(digits, PAIR_WEIGHTS_2_2_8);
283
+ const v1 = i32x4.extract_lane(dot, 0);
284
+ const v2 = i32x4.extract_lane(dot, 1) + i32x4.extract_lane(dot, 2);
285
+ store<valueof<T>>(writePtr, <valueof<T>>v1);
286
+ store<valueof<T>>(writePtr + elementSize, <valueof<T>>v2);
287
+ writePtr += elementSize << 1;
288
+ srcStart += 12;
289
+ continue;
290
+ }
291
+ case 0x28: {
292
+ const digits = i16x8.sub(block, SPLAT_30_8);
293
+ const dot = i32x4.dot_i16x8_s(digits, PAIR_WEIGHTS_3_1_8);
294
+ const v1 = i32x4.extract_lane(dot, 0) + i32x4.extract_lane(dot, 1);
295
+ const v2 = i32x4.extract_lane(dot, 2);
296
+ store<valueof<T>>(writePtr, <valueof<T>>v1);
297
+ store<valueof<T>>(writePtr + elementSize, <valueof<T>>v2);
298
+ writePtr += elementSize << 1;
299
+ srcStart += 12;
300
+ continue;
301
+ }
302
+ case 0x22: {
303
+ const digits = i16x8.sub(block, SPLAT_30_8);
304
+ const dot = i32x4.dot_i16x8_s(digits, PAIR_WEIGHTS_1_3_8);
305
+ const v1 = i32x4.extract_lane(dot, 0);
306
+ const v2 = i32x4.extract_lane(dot, 1) + i32x4.extract_lane(dot, 2);
307
+ store<valueof<T>>(writePtr, <valueof<T>>v1);
308
+ store<valueof<T>>(writePtr + elementSize, <valueof<T>>v2);
309
+ writePtr += elementSize << 1;
310
+ srcStart += 12;
311
+ continue;
312
+ }
313
+ }
314
+ }
315
+ // Single-element SWAR fast paths cover the cases where the v128 block
316
+ // didn't match 0x88 (mixed widths, partial element trailing the block).
317
+ if (!isSigned<valueof<T>>() && srcStart + 6 < srcEnd) {
318
+ const block64 = load<u64>(srcStart);
319
+ if (((block64 >> 48) & 0xffff) == COMMA) {
320
+ const digits =
321
+ (block64 & 0x0000_00ff_00ff_00ff) - 0x0000_0030_0030_0030;
322
+ const oor =
323
+ (digits | (digits + 0x0000_0006_0006_0006)) & 0x0000_fff0_fff0_fff0;
324
+ if (oor == 0) {
325
+ const d0 = <u32>(digits & 0xffff);
326
+ const d1 = <u32>((digits >> 16) & 0xffff);
327
+ const d2 = <u32>((digits >> 32) & 0xffff);
328
+ store<valueof<T>>(writePtr, <valueof<T>>(d0 * 100 + d1 * 10 + d2));
329
+ writePtr += elementSize;
330
+ srcStart += 8;
331
+ continue;
332
+ }
333
+ } else if (((block64 >> 32) & 0xffff) == COMMA) {
334
+ const digits =
335
+ (block64 & 0x0000_0000_00ff_00ff) - 0x0000_0000_0030_0030;
336
+ const oor =
337
+ (digits | (digits + 0x0000_0000_0006_0006)) & 0x0000_0000_fff0_fff0;
338
+ if (oor == 0) {
339
+ const d0 = <u32>(digits & 0xffff);
340
+ const d1 = <u32>((digits >> 16) & 0xffff);
341
+ store<valueof<T>>(writePtr, <valueof<T>>(d0 * 10 + d1));
342
+ writePtr += elementSize;
343
+ srcStart += 6;
344
+ continue;
345
+ }
346
+ } else if (((block64 >> 16) & 0xffff) == COMMA) {
347
+ const d0 = <u32>(block64 & 0xffff) - 48;
348
+ if (d0 <= 9) {
349
+ store<valueof<T>>(writePtr, <valueof<T>>d0);
350
+ writePtr += elementSize;
351
+ srcStart += 4;
352
+ continue;
353
+ }
354
+ }
355
+ }
356
+ const code = load<u16>(srcStart);
357
+ if (<u32>code - 48 <= 9 || (isSigned<valueof<T>>() && code == 45)) {
358
+ const lastIndex = srcStart;
359
+ srcStart += 2;
360
+ while (srcStart < srcEnd) {
361
+ const c = load<u16>(srcStart);
362
+ if (c == COMMA || c == BRACKET_RIGHT || isSpace(c)) {
363
+ let value: u64 = 0;
364
+ let p = lastIndex;
365
+ if (isSigned<valueof<T>>() && load<u16>(p) == 45) {
366
+ p += 2;
367
+ while (p < srcStart) {
368
+ value = value * 10 + (<u32>load<u16>(p) - 48);
369
+ p += 2;
370
+ }
371
+ store<valueof<T>>(writePtr, <valueof<T>>-(<i64>value));
372
+ } else {
373
+ while (p < srcStart) {
374
+ value = value * 10 + (<u32>load<u16>(p) - 48);
375
+ p += 2;
376
+ }
377
+ store<valueof<T>>(writePtr, <valueof<T>>value);
378
+ }
379
+ writePtr += elementSize;
380
+ break;
381
+ }
382
+ srcStart += 2;
383
+ }
384
+ }
385
+ srcStart += 2;
386
+ }
387
+
388
+ out.length = i32(<usize>(writePtr - dataStart) / elementSize);
389
+ return out;
390
+ }
391
+
163
392
  // @ts-ignore: Decorator valid here
164
- export function deserializeIntegerArray_SIMD<T extends number[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
165
- const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
393
+ export function deserializeIntegerArray_SIMD<T extends number[]>(
394
+ srcStart: usize,
395
+ srcEnd: usize,
396
+ dst: usize,
397
+ ): T {
398
+ // u8/i8 elements never amortize the 8-digit SIMD kernel; route them
399
+ // through the SIMD-counted narrow-lane fast path. AS folds the sizeof
400
+ // check at compile time.
401
+ if (sizeof<valueof<T>>() <= 1) {
402
+ return deserializeNarrowIntegerArray_SIMD<T>(srcStart, srcEnd, dst);
403
+ }
404
+ const out = changetype<nonnull<T>>(
405
+ dst || changetype<usize>(instantiate<T>()),
406
+ );
166
407
  const originalSrcStart = srcStart;
167
408
  const reusableLength = out.length;
168
409
 
@@ -209,7 +450,10 @@ export function deserializeIntegerArray_SIMD<T extends number[]>(srcStart: usize
209
450
  }
210
451
 
211
452
  if (index >= reusableLength) break;
212
- storeSignedInteger<T>(dataStart + <usize>index * sizeof<valueof<T>>(), negative ? -(<i64>value) : <i64>value);
453
+ storeSignedInteger<T>(
454
+ dataStart + <usize>index * sizeof<valueof<T>>(),
455
+ negative ? -(<i64>value) : <i64>value,
456
+ );
213
457
  index++;
214
458
  if (srcStart >= srcEnd) break;
215
459
 
@@ -245,7 +489,10 @@ export function deserializeIntegerArray_SIMD<T extends number[]>(srcStart: usize
245
489
  }
246
490
 
247
491
  if (index >= reusableLength) break;
248
- storeUnsignedInteger<T>(dataStart + <usize>index * sizeof<valueof<T>>(), value);
492
+ storeUnsignedInteger<T>(
493
+ dataStart + <usize>index * sizeof<valueof<T>>(),
494
+ value,
495
+ );
249
496
  index++;
250
497
  if (srcStart >= srcEnd) break;
251
498
 
@@ -266,42 +513,72 @@ export function deserializeIntegerArray_SIMD<T extends number[]>(srcStart: usize
266
513
  srcStart = originalSrcStart;
267
514
  }
268
515
 
269
- out.length = 0;
516
+ // Worst-case sizing: every element is at least 1 digit + 1 delimiter = 2
517
+ // UTF-16 chars = 4 bytes. The parse helpers below store through `writePtr`
518
+ // directly, eliminating `Array.push`'s per-element capacity check + length
519
+ // write for every integer width.
520
+ const elementSize = sizeof<valueof<T>>();
521
+ const maxElements = i32((<usize>(srcEnd - srcStart)) >> 2);
522
+ if (maxElements > 0) out.length = maxElements;
523
+ const dataStart = out.dataStart;
524
+ let writePtr = dataStart;
270
525
 
271
526
  do {
272
527
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
273
528
  srcStart += 2;
274
529
  if (srcStart >= srcEnd) break;
275
- if (load<u16>(srcStart) == BRACKET_RIGHT) return out;
530
+ if (load<u16>(srcStart) == BRACKET_RIGHT) {
531
+ out.length = 0;
532
+ return out;
533
+ }
276
534
 
277
535
  if (isSigned<valueof<T>>()) {
278
536
  while (srcStart < srcEnd) {
279
- srcStart = parseSignedIntegerSIMD<T>(srcStart, srcEnd, out);
280
- if (!srcStart || srcStart >= srcEnd) break;
537
+ const next = parseSignedIntegerSIMD<T>(srcStart, srcEnd, writePtr);
538
+ if (!next) break;
539
+ writePtr += elementSize;
540
+ srcStart = next;
541
+ if (srcStart >= srcEnd) break;
281
542
 
282
543
  const code = load<u16>(srcStart);
283
544
  if (code == COMMA) {
284
545
  srcStart += 2;
285
546
  continue;
286
547
  }
287
- if (code == BRACKET_RIGHT) return out;
548
+ if (code == BRACKET_RIGHT) {
549
+ out.length = i32(<usize>(writePtr - dataStart) / elementSize);
550
+ return out;
551
+ }
288
552
  break;
289
553
  }
290
554
  } else {
291
555
  while (srcStart < srcEnd) {
292
- srcStart = parseUnsignedIntegerSIMD<T>(srcStart, srcEnd, out);
293
- if (!srcStart || srcStart >= srcEnd) break;
556
+ const next = parseUnsignedIntegerSIMD<T>(srcStart, srcEnd, writePtr);
557
+ if (!next) break;
558
+ writePtr += elementSize;
559
+ srcStart = next;
560
+ if (srcStart >= srcEnd) break;
294
561
 
295
562
  const code = load<u16>(srcStart);
296
563
  if (code == COMMA) {
297
564
  srcStart += 2;
298
565
  continue;
299
566
  }
300
- if (code == BRACKET_RIGHT) return out;
567
+ if (code == BRACKET_RIGHT) {
568
+ out.length = i32(<usize>(writePtr - dataStart) / elementSize);
569
+ return out;
570
+ }
301
571
  break;
302
572
  }
303
573
  }
304
574
  } while (false);
305
575
 
306
- return deserializeIntegerArray_SLOW<T>(originalSrcStart, srcEnd, changetype<usize>(out));
576
+ // Fall through to SWAR's SLOW path; it resets `out.length = 0` and
577
+ // re-parses from the original input. The pre-allocated buffer is retained
578
+ // so SLOW's per-element `ensureArrayElementSlot` reuses the capacity.
579
+ return deserializeIntegerArray_SLOW<T>(
580
+ originalSrcStart,
581
+ srcEnd,
582
+ changetype<usize>(out),
583
+ );
307
584
  }