json-as 1.3.6 → 1.3.8

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 (155) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +1 -1
  3. package/assembly/deserialize/helpers/uint.ts +4 -1
  4. package/assembly/deserialize/index/arbitrary.ts +7 -3
  5. package/assembly/deserialize/index/array.ts +42 -17
  6. package/assembly/deserialize/index/bool.ts +1 -1
  7. package/assembly/deserialize/index/date.ts +1 -1
  8. package/assembly/deserialize/index/float.ts +40 -1
  9. package/assembly/deserialize/index/integer.ts +68 -1
  10. package/assembly/deserialize/index/map.ts +1 -1
  11. package/assembly/deserialize/index/object.ts +1 -1
  12. package/assembly/deserialize/index/raw.ts +1 -1
  13. package/assembly/deserialize/index/set.ts +1 -1
  14. package/assembly/deserialize/index/staticarray.ts +4 -1
  15. package/assembly/deserialize/index/string.ts +32 -4
  16. package/assembly/deserialize/index/struct.ts +1 -1
  17. package/assembly/deserialize/index/typedarray.ts +30 -10
  18. package/assembly/deserialize/index/unsigned.ts +78 -1
  19. package/assembly/deserialize/index.ts +1 -0
  20. package/assembly/deserialize/{simple → naive}/array/arbitrary.ts +24 -5
  21. package/assembly/deserialize/{simple → naive}/array/array.ts +8 -2
  22. package/assembly/deserialize/naive/array/bool.ts +68 -0
  23. package/assembly/deserialize/{simple → naive}/array/box.ts +8 -2
  24. package/assembly/deserialize/naive/array/float.ts +63 -0
  25. package/assembly/deserialize/{simple → naive}/array/generic.ts +14 -7
  26. package/assembly/deserialize/naive/array/integer.ts +86 -0
  27. package/assembly/deserialize/naive/array/map.ts +47 -0
  28. package/assembly/deserialize/naive/array/object.ts +47 -0
  29. package/assembly/deserialize/{simple → naive}/array/raw.ts +34 -7
  30. package/assembly/deserialize/naive/array/string.ts +69 -0
  31. package/assembly/deserialize/naive/array/struct.ts +47 -0
  32. package/assembly/deserialize/{simple → naive}/array.ts +15 -10
  33. package/assembly/deserialize/{simple → naive}/bool.ts +6 -2
  34. package/assembly/deserialize/naive/float.ts +135 -0
  35. package/assembly/deserialize/{simple → naive}/integer.ts +10 -2
  36. package/assembly/deserialize/{simple → naive}/map.ts +106 -27
  37. package/assembly/deserialize/{simple → naive}/object.ts +65 -19
  38. package/assembly/deserialize/{simple → naive}/raw.ts +4 -1
  39. package/assembly/deserialize/{simple → naive}/set.ts +49 -19
  40. package/assembly/deserialize/{simple → naive}/staticarray/array.ts +1 -1
  41. package/assembly/deserialize/{simple → naive}/staticarray/bool.ts +1 -1
  42. package/assembly/deserialize/{simple → naive}/staticarray/float.ts +1 -1
  43. package/assembly/deserialize/{simple → naive}/staticarray/integer.ts +1 -1
  44. package/assembly/deserialize/{simple → naive}/staticarray/string.ts +11 -3
  45. package/assembly/deserialize/{simple → naive}/staticarray/struct.ts +1 -2
  46. package/assembly/deserialize/{simple → naive}/staticarray.ts +68 -18
  47. package/assembly/deserialize/naive/string.ts +199 -0
  48. package/assembly/deserialize/{simple → naive}/struct.ts +5 -1
  49. package/assembly/deserialize/{simple → naive}/typedarray.ts +17 -4
  50. package/assembly/deserialize/{simple → naive}/unsigned.ts +10 -15
  51. package/assembly/deserialize/simd/array/integer.ts +339 -62
  52. package/assembly/deserialize/simd/float.ts +303 -0
  53. package/assembly/deserialize/simd/integer.ts +233 -0
  54. package/assembly/deserialize/simd/string.ts +266 -107
  55. package/assembly/deserialize/swar/array/arbitrary.ts +11 -3
  56. package/assembly/deserialize/swar/array/array.ts +40 -9
  57. package/assembly/deserialize/swar/array/bool.ts +28 -5
  58. package/assembly/deserialize/swar/array/box.ts +11 -3
  59. package/assembly/deserialize/swar/array/float.ts +295 -7
  60. package/assembly/deserialize/swar/array/generic.ts +28 -7
  61. package/assembly/deserialize/swar/array/integer.ts +363 -112
  62. package/assembly/deserialize/swar/array/map.ts +11 -3
  63. package/assembly/deserialize/swar/array/object.ts +37 -25
  64. package/assembly/deserialize/swar/array/raw.ts +11 -3
  65. package/assembly/deserialize/swar/array/shared.ts +63 -14
  66. package/assembly/deserialize/swar/array/string.ts +140 -7
  67. package/assembly/deserialize/swar/array/struct.ts +66 -12
  68. package/assembly/deserialize/swar/array.ts +12 -51
  69. package/assembly/deserialize/swar/float.ts +304 -0
  70. package/assembly/deserialize/swar/integer.ts +246 -0
  71. package/assembly/deserialize/swar/string.ts +213 -294
  72. package/assembly/deserialize/swar/typedarray.ts +224 -0
  73. package/assembly/index.d.ts +3 -1
  74. package/assembly/index.ts +402 -261
  75. package/assembly/serialize/index/array.ts +1 -1
  76. package/assembly/serialize/index/bool.ts +1 -1
  77. package/assembly/serialize/index/date.ts +1 -1
  78. package/assembly/serialize/index/float.ts +5 -1
  79. package/assembly/serialize/index/integer.ts +1 -1
  80. package/assembly/serialize/index/map.ts +1 -1
  81. package/assembly/serialize/index/raw.ts +1 -1
  82. package/assembly/serialize/index/set.ts +1 -1
  83. package/assembly/serialize/index/staticarray.ts +1 -1
  84. package/assembly/serialize/index/string.ts +1 -1
  85. package/assembly/serialize/index/struct.ts +1 -1
  86. package/assembly/serialize/index/typedarray.ts +21 -12
  87. package/assembly/serialize/index.ts +1 -0
  88. package/assembly/serialize/naive/array.ts +351 -0
  89. package/assembly/serialize/{simple → naive}/float.ts +4 -1
  90. package/assembly/serialize/naive/integer.ts +19 -0
  91. package/assembly/serialize/{simple → naive}/map.ts +6 -2
  92. package/assembly/serialize/{simple → naive}/raw.ts +5 -1
  93. package/assembly/serialize/{simple → naive}/set.ts +6 -1
  94. package/assembly/serialize/{simple → naive}/staticarray.ts +6 -1
  95. package/assembly/serialize/{simple → naive}/string.ts +1 -2
  96. package/assembly/serialize/{simple → naive}/typedarray.ts +10 -3
  97. package/assembly/serialize/simd/string.ts +6 -2
  98. package/assembly/serialize/swar/string.ts +15 -141
  99. package/assembly/util/atoi-fast.ts +81 -0
  100. package/assembly/util/concat.ts +5 -1
  101. package/assembly/util/dragonbox-cache.ts +443 -2
  102. package/assembly/util/dragonbox.ts +53 -17
  103. package/assembly/util/itoa-fast.ts +241 -0
  104. package/assembly/util/masks.ts +18 -1
  105. package/assembly/util/parsefloat-fast.ts +167 -0
  106. package/assembly/util/scanValueEnd.ts +78 -0
  107. package/assembly/util/scientific.ts +132 -0
  108. package/assembly/util/simd-int.ts +191 -0
  109. package/assembly/util/snp.ts +4 -1
  110. package/assembly/util/swar-int.ts +248 -0
  111. package/assembly/util/swar.ts +13 -3
  112. package/lib/as-bs.ts +27 -6
  113. package/package.json +15 -11
  114. package/transform/lib/builder.d.ts.map +1 -1
  115. package/transform/lib/builder.js +13 -5
  116. package/transform/lib/builder.js.map +1 -1
  117. package/transform/lib/index.d.ts +5 -0
  118. package/transform/lib/index.d.ts.map +1 -1
  119. package/transform/lib/index.js +1046 -340
  120. package/transform/lib/index.js.map +1 -1
  121. package/transform/lib/linkers/alias.d.ts.map +1 -1
  122. package/transform/lib/linkers/alias.js.map +1 -1
  123. package/transform/lib/linkers/custom.d.ts.map +1 -1
  124. package/transform/lib/linkers/custom.js +3 -2
  125. package/transform/lib/linkers/custom.js.map +1 -1
  126. package/transform/lib/linkers/imports.d.ts.map +1 -1
  127. package/transform/lib/linkers/imports.js.map +1 -1
  128. package/transform/lib/types.d.ts.map +1 -1
  129. package/transform/lib/types.js +54 -16
  130. package/transform/lib/types.js.map +1 -1
  131. package/transform/lib/util.d.ts.map +1 -1
  132. package/transform/lib/util.js +1 -1
  133. package/transform/lib/util.js.map +1 -1
  134. package/transform/lib/visitor.d.ts.map +1 -1
  135. package/transform/lib/visitor.js +2 -1
  136. package/transform/lib/visitor.js.map +1 -1
  137. package/assembly/custom/util.ts +0 -310
  138. package/assembly/deserialize/simple/arbitrary.ts +0 -23
  139. package/assembly/deserialize/simple/array/bool.ts +0 -17
  140. package/assembly/deserialize/simple/array/float.ts +0 -28
  141. package/assembly/deserialize/simple/array/integer.ts +0 -27
  142. package/assembly/deserialize/simple/array/map.ts +0 -28
  143. package/assembly/deserialize/simple/array/object.ts +0 -28
  144. package/assembly/deserialize/simple/array/string.ts +0 -23
  145. package/assembly/deserialize/simple/array/struct.ts +0 -28
  146. package/assembly/deserialize/simple/float.ts +0 -201
  147. package/assembly/deserialize/simple/string.ts +0 -132
  148. package/assembly/serialize/simple/arbitrary.ts +0 -79
  149. package/assembly/serialize/simple/array.ts +0 -86
  150. package/assembly/serialize/simple/integer.ts +0 -20
  151. package/assembly/serialize/simple/object.ts +0 -42
  152. /package/assembly/deserialize/{simple → naive}/date.ts +0 -0
  153. /package/assembly/serialize/{simple → naive}/bool.ts +0 -0
  154. /package/assembly/serialize/{simple → naive}/date.ts +0 -0
  155. /package/assembly/serialize/{simple → naive}/struct.ts +0 -0
@@ -1,59 +1,50 @@
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;
6
7
 
7
-
8
+ // @ts-expect-error: decorators valid here
8
9
  @lazy const SPLAT_30 = i16x8.splat(0x30);
9
10
 
10
-
11
+ // @ts-expect-error: decorators valid here
11
12
  @lazy const SPLAT_09 = i16x8.splat(9);
12
13
 
13
-
14
+ // @ts-expect-error: decorators valid here
14
15
  @lazy const ZERO_I16X8 = i16x8.splat(0);
15
16
 
16
-
17
+ // @ts-expect-error: decorators valid here
17
18
  @lazy const ZERO_I32X4 = i32x4.splat(0);
18
19
 
19
-
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
-
22
-
20
+ // @ts-expect-error: decorators valid here
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
+ );
39
+
40
+ // @ts-expect-error: decorators valid here
23
41
  @lazy const PAIR_WEIGHTS_100_1 = i16x8(100, 1, 100, 1, 0, 0, 0, 0);
24
42
 
25
-
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 {
43
+ // @ts-expect-error: decorators valid here
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>()) {
@@ -67,8 +58,11 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
67
58
  }
68
59
  }
69
60
 
70
-
71
- @inline function storeUnsignedInteger<T extends number[]>(slot: usize, value: u64): void {
61
+ // @ts-expect-error: decorators valid here
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>()) {
@@ -82,7 +76,7 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
82
76
  }
83
77
  }
84
78
 
85
-
79
+ // @ts-expect-error: decorators valid here
86
80
  @inline function tryParseEightDigitsSIMD(srcStart: usize, value: u64): u64 {
87
81
  const block = load<v128>(srcStart);
88
82
  const digits = i16x8.sub(block, SPLAT_30);
@@ -99,8 +93,15 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
99
93
  return value * 100000000 + (<u64>lo * 10000 + <u64>hi);
100
94
  }
101
95
 
102
-
103
- @inline function parseSignedIntegerSIMD<T extends number[]>(srcStart: usize, srcEnd: usize, out: T): usize {
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
+ // @ts-expect-error: decorators valid here
100
+ @inline function parseSignedIntegerSIMD<T extends number[]>(
101
+ srcStart: usize,
102
+ srcEnd: usize,
103
+ slot: usize,
104
+ ): usize {
104
105
  let negative = false;
105
106
  let code = load<u16>(srcStart);
106
107
  if (code == 45) {
@@ -130,12 +131,16 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
130
131
  srcStart += 2;
131
132
  }
132
133
 
133
- pushSignedInteger<T>(out, negative ? -(<i64>value) : <i64>value);
134
+ storeSignedInteger<T>(slot, negative ? -(<i64>value) : <i64>value);
134
135
  return srcStart;
135
136
  }
136
137
 
137
-
138
- @inline function parseUnsignedIntegerSIMD<T extends number[]>(srcStart: usize, srcEnd: usize, out: T): usize {
138
+ // @ts-expect-error: decorators valid here
139
+ @inline function parseUnsignedIntegerSIMD<T extends number[]>(
140
+ srcStart: usize,
141
+ srcEnd: usize,
142
+ slot: usize,
143
+ ): usize {
139
144
  let digit = <u32>load<u16>(srcStart) - 48;
140
145
  if (digit > 9) return 0;
141
146
 
@@ -156,13 +161,249 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
156
161
  srcStart += 2;
157
162
  }
158
163
 
159
- pushUnsignedInteger<T>(out, value);
164
+ storeUnsignedInteger<T>(slot, value);
160
165
  return srcStart;
161
166
  }
162
167
 
168
+ // @ts-expect-error: decorators valid here
169
+ @lazy const COMMA_SPLAT_8 = i16x8.splat(<i16>COMMA);
170
+
171
+ // Pair-multiply weights for the common two-element packings in 8-char blocks.
172
+ // Lanes that fall on a `,` become garbage after the subtract-by-`0` but are
173
+ // killed by zero weights in `i32x4.dot_i16x8_s`. The 9 (a, b) combinations
174
+ // where a+b in {2..6} all reach via these weight vectors; the bottom three
175
+ // (one digit + one digit, etc.) are left to the SWAR cascade fallback since
176
+ // they hit <2% on the uniform 0..255 sweep and add branch cost here.
177
+ // 3+3 (`DDD,DDD,`) bitmask 0x88, advance 16
178
+ // 3+2 (`DDD,DD,?`) bitmask 0x48, advance 14
179
+ // 2+3 (`DD,DDD,?`) bitmask 0x44, advance 14
180
+ // 2+2 (`DD,DD,??`) bitmask 0x24, advance 12
181
+ // 3+1 (`DDD,D,??`) bitmask 0x28, advance 12
182
+ // 1+3 (`D,DDD,??`) bitmask 0x22, advance 12
183
+ // @ts-expect-error: decorators valid here
184
+ @lazy const PAIR_WEIGHTS_3_3_8 = i16x8(100, 10, 1, 0, 100, 10, 1, 0);
185
+
186
+ // @ts-expect-error: decorators valid here
187
+ @lazy const PAIR_WEIGHTS_3_2_8 = i16x8(100, 10, 1, 0, 10, 1, 0, 0);
188
+
189
+ // @ts-expect-error: decorators valid here
190
+ @lazy const PAIR_WEIGHTS_2_3_8 = i16x8(10, 1, 0, 100, 10, 1, 0, 0);
191
+
192
+ // @ts-expect-error: decorators valid here
193
+ @lazy const PAIR_WEIGHTS_2_2_8 = i16x8(10, 1, 0, 10, 1, 0, 0, 0);
194
+
195
+ // @ts-expect-error: decorators valid here
196
+ @lazy const PAIR_WEIGHTS_3_1_8 = i16x8(100, 10, 1, 0, 1, 0, 0, 0);
197
+
198
+ // @ts-expect-error: decorators valid here
199
+ @lazy const PAIR_WEIGHTS_1_3_8 = i16x8(1, 0, 100, 10, 1, 0, 0, 0);
200
+
201
+ // @ts-expect-error: decorators valid here
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
  }