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,53 +1,25 @@
1
- import { deserializeIntegerField } from "../../simple/integer";
2
- import { deserializeUnsignedField } from "../../simple/unsigned";
1
+ import { deserializeIntegerArray_NAIVE } from "../../naive/array/integer";
3
2
  import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
4
3
  import { isSpace } from "../../../util";
5
4
  import { ensureArrayElementSlot, ensureArrayField } from "./shared";
6
-
7
- const ASCII_LANE_MASK_4: u64 = 0x00ff00ff00ff00ff;
8
- const ASCII_ZERO_4: u64 = 0x0030003000300030;
9
- const ASCII_RANGE_MASK_4: u64 = 0xfff0fff0fff0fff0;
10
- const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
11
-
12
-
13
- @inline function pushSignedInteger<T extends number[]>(out: T, value: i64): void {
14
- if (sizeof<valueof<T>>() == sizeof<i8>()) {
15
- out.push(<valueof<T>>(<i8>value));
16
- } else if (sizeof<valueof<T>>() == sizeof<i16>()) {
17
- out.push(<valueof<T>>(<i16>value));
18
- } else if (sizeof<valueof<T>>() == sizeof<i32>()) {
19
- out.push(<valueof<T>>(<i32>value));
20
- } else if (sizeof<valueof<T>>() == sizeof<isize>()) {
21
- out.push(<valueof<T>>(<isize>value));
22
- } else {
23
- out.push(<valueof<T>>value);
24
- }
25
- }
26
-
27
-
28
- @inline function pushUnsignedInteger<T extends number[]>(out: T, value: u64): void {
29
- if (sizeof<valueof<T>>() == sizeof<u8>()) {
30
- out.push(<valueof<T>>(<u8>value));
31
- } else if (sizeof<valueof<T>>() == sizeof<u16>()) {
32
- out.push(<valueof<T>>(<u16>value));
33
- } else if (sizeof<valueof<T>>() == sizeof<u32>()) {
34
- out.push(<valueof<T>>(<u32>value));
35
- } else if (sizeof<valueof<T>>() == sizeof<usize>()) {
36
- out.push(<valueof<T>>(<usize>value));
37
- } else {
38
- out.push(<valueof<T>>value);
39
- }
40
- }
41
-
42
-
43
- @inline function storeSignedInteger<T extends number[]>(slot: usize, value: i64): void {
44
- if (sizeof<valueof<T>>() == sizeof<i8>()) {
5
+ import { parse4Digits_PairMul } from "../../../util/swar-int";
6
+
7
+ // Store helpers parameterised on the element type `E` directly, so they
8
+ // serve both `Array<E>` and `TypedArray<E>` callers. The integer-array
9
+ // callers below all pass `valueof<T>` and AS folds the resulting tower of
10
+ // `sizeof<E>` comparisons at compile time — same codegen as the prior
11
+ // `T extends number[]` version, but reusable from `swar/typedarray.ts`.
12
+ @inline function storeSignedIntegerE<E extends number>(
13
+ slot: usize,
14
+ value: i64,
15
+ ): void {
16
+ if (sizeof<E>() == sizeof<i8>()) {
45
17
  store<i8>(slot, <i8>value);
46
- } else if (sizeof<valueof<T>>() == sizeof<i16>()) {
18
+ } else if (sizeof<E>() == sizeof<i16>()) {
47
19
  store<i16>(slot, <i16>value);
48
- } else if (sizeof<valueof<T>>() == sizeof<i32>()) {
20
+ } else if (sizeof<E>() == sizeof<i32>()) {
49
21
  store<i32>(slot, <i32>value);
50
- } else if (sizeof<valueof<T>>() == sizeof<isize>()) {
22
+ } else if (sizeof<E>() == sizeof<isize>()) {
51
23
  store<isize>(slot, <isize>value);
52
24
  } else {
53
25
  store<i64>(slot, value);
@@ -55,30 +27,37 @@ const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
55
27
  }
56
28
 
57
29
 
58
- @inline function storeUnsignedInteger<T extends number[]>(slot: usize, value: u64): void {
59
- if (sizeof<valueof<T>>() == sizeof<u8>()) {
30
+ @inline function storeUnsignedIntegerE<E extends number>(
31
+ slot: usize,
32
+ value: u64,
33
+ ): void {
34
+ if (sizeof<E>() == sizeof<u8>()) {
60
35
  store<u8>(slot, <u8>value);
61
- } else if (sizeof<valueof<T>>() == sizeof<u16>()) {
36
+ } else if (sizeof<E>() == sizeof<u16>()) {
62
37
  store<u16>(slot, <u16>value);
63
- } else if (sizeof<valueof<T>>() == sizeof<u32>()) {
38
+ } else if (sizeof<E>() == sizeof<u32>()) {
64
39
  store<u32>(slot, <u32>value);
65
- } else if (sizeof<valueof<T>>() == sizeof<usize>()) {
40
+ } else if (sizeof<E>() == sizeof<usize>()) {
66
41
  store<usize>(slot, <usize>value);
67
42
  } else {
68
43
  store<u64>(slot, value);
69
44
  }
70
45
  }
71
46
 
72
-
73
- @inline function parse4DigitsASCII(block: u64): u32 {
74
- const digits = (block & ASCII_LANE_MASK_4) - ASCII_ZERO_4;
75
- if (((digits | (digits + ASCII_RANGE_ADD_4)) & ASCII_RANGE_MASK_4) != 0) return U32.MAX_VALUE;
76
-
77
- return <u32>(<u32>(digits & 0xffff) * 1000 + <u32>((digits >> 16) & 0xffff) * 100 + <u32>((digits >> 32) & 0xffff) * 10 + <u32>(digits >> 48));
78
- }
79
-
80
-
81
- @inline function parseSignedIntegerScalar<T extends number[]>(srcStart: usize, srcEnd: usize, out: T): usize {
47
+ // The four parse helpers below take a `slot` pointer (`writePtr`) and store
48
+ // the value directly via `store<valueof<T>>(slot, ...)`. The outer dispatcher
49
+ // owns the array's `out.length = maxElements` pre-allocation and the
50
+ // `writePtr` advance, so the per-element `Array.push` capacity check and
51
+ // length write are eliminated for every integer width, not just the u8/i8
52
+ // narrow-lane path.
53
+ // Parsers are also E-parameterised so they're shareable with
54
+ // `swar/typedarray.ts`. The body is byte-identical to the prior version
55
+ // modulo s/valueof<T>/E/.
56
+ @inline export function parseSignedIntegerScalar<E extends number>(
57
+ srcStart: usize,
58
+ srcEnd: usize,
59
+ slot: usize,
60
+ ): usize {
82
61
  let negative = false;
83
62
  let code = load<u16>(srcStart);
84
63
  if (code == 45) {
@@ -100,12 +79,16 @@ const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
100
79
  srcStart += 2;
101
80
  }
102
81
 
103
- pushSignedInteger<T>(out, negative ? -(<i64>value) : <i64>value);
82
+ storeSignedIntegerE<E>(slot, negative ? -(<i64>value) : <i64>value);
104
83
  return srcStart;
105
84
  }
106
85
 
107
86
 
108
- @inline function parseUnsignedIntegerScalar<T extends number[]>(srcStart: usize, srcEnd: usize, out: T): usize {
87
+ @inline export function parseUnsignedIntegerScalar<E extends number>(
88
+ srcStart: usize,
89
+ srcEnd: usize,
90
+ slot: usize,
91
+ ): usize {
109
92
  let digit = <u32>load<u16>(srcStart) - 48;
110
93
  if (digit > 9) return 0;
111
94
 
@@ -118,12 +101,16 @@ const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
118
101
  srcStart += 2;
119
102
  }
120
103
 
121
- pushUnsignedInteger<T>(out, value);
104
+ storeUnsignedIntegerE<E>(slot, value);
122
105
  return srcStart;
123
106
  }
124
107
 
125
108
 
126
- @inline function parseSignedIntegerSWAR<T extends number[]>(srcStart: usize, srcEnd: usize, out: T): usize {
109
+ @inline export function parseSignedIntegerSWAR<E extends number>(
110
+ srcStart: usize,
111
+ srcEnd: usize,
112
+ slot: usize,
113
+ ): usize {
127
114
  let negative = false;
128
115
  let code = load<u16>(srcStart);
129
116
  if (code == 45) {
@@ -139,11 +126,21 @@ const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
139
126
  let value: u64 = digit;
140
127
  srcStart += 2;
141
128
 
142
- while (srcStart + 6 < srcEnd) {
143
- const parsed = parse4DigitsASCII(load<u64>(srcStart));
144
- if (parsed == U32.MAX_VALUE) break;
145
- value = value * 10000 + parsed;
146
- srcStart += 8;
129
+ // Signed scan uses parse4 + scalar only (matches the asymmetric tuning in
130
+ // swar/integer.ts:deserializeIntegerField_SWAR). The leading minus shifts
131
+ // the digit run into parse8's "terminator-in-load" zone where validate-fail
132
+ // is common, so parse4's smaller failure unit wins.
133
+ //
134
+ // i8 tops out at 3 digits (-128..127), so the 4-digit kernel can never fire
135
+ // and the failing load + range check just burns cycles. Gate on the lane
136
+ // width so AS folds the loop away at compile time.
137
+ if (sizeof<E>() > 1) {
138
+ while (srcStart + 6 < srcEnd) {
139
+ const parsed = parse4Digits_PairMul(load<u64>(srcStart));
140
+ if (parsed == U32.MAX_VALUE) break;
141
+ value = value * 10000 + parsed;
142
+ srcStart += 8;
143
+ }
147
144
  }
148
145
 
149
146
  while (srcStart < srcEnd) {
@@ -153,20 +150,54 @@ const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
153
150
  srcStart += 2;
154
151
  }
155
152
 
156
- pushSignedInteger<T>(out, negative ? -(<i64>value) : <i64>value);
153
+ storeSignedIntegerE<E>(slot, negative ? -(<i64>value) : <i64>value);
157
154
  return srcStart;
158
155
  }
159
156
 
160
157
 
161
- @inline function parseUnsignedIntegerSWAR<T extends number[]>(srcStart: usize, srcEnd: usize, out: T): usize {
158
+ @inline export function parseUnsignedIntegerSWAR<E extends number>(
159
+ srcStart: usize,
160
+ srcEnd: usize,
161
+ slot: usize,
162
+ ): usize {
163
+ // Narrow-type path mirrors the NAIVE structure: a tight scan loop to find
164
+ // the element terminator, then a fixed-count fold with no per-digit break.
165
+ // TurboFan tends to schedule this better than a single combined
166
+ // scan-and-fold loop because the fold has no data-dependent exit.
167
+ if (sizeof<E>() <= 2) {
168
+ const first = <u32>load<u16>(srcStart) - 48;
169
+ if (first > 9) return 0;
170
+ const lastIndex = srcStart;
171
+ srcStart += 2;
172
+ while (srcStart < srcEnd) {
173
+ const c = <u32>load<u16>(srcStart) - 48;
174
+ if (c > 9) break;
175
+ srcStart += 2;
176
+ }
177
+ let value: u64 = 0;
178
+ let p = lastIndex;
179
+ while (p < srcStart) {
180
+ value = value * 10 + (<u32>load<u16>(p) - 48);
181
+ p += 2;
182
+ }
183
+ storeUnsignedIntegerE<E>(slot, value);
184
+ return srcStart;
185
+ }
186
+
162
187
  let digit = <u32>load<u16>(srcStart) - 48;
163
188
  if (digit > 9) return 0;
164
189
 
165
190
  let value: u64 = digit;
166
191
  srcStart += 2;
167
192
 
193
+ // Array unsigned path uses parse4 + scalar (not parse8 + scalar as in the
194
+ // struct-field path swar/integer.ts:deserializeUnsignedField_SWAR). The
195
+ // bench corpus for arrays mixes element widths (e.g. 1/4/7/10 digits in
196
+ // u32-64mib), so most parse8 strides hit the `,` separator mid-load and
197
+ // pay the load+validate cost for a guaranteed miss. parse4 has a smaller
198
+ // failure unit and matches the typical 1-4 digit run between separators.
168
199
  while (srcStart + 6 < srcEnd) {
169
- const parsed = parse4DigitsASCII(load<u64>(srcStart));
200
+ const parsed = parse4Digits_PairMul(load<u64>(srcStart));
170
201
  if (parsed == U32.MAX_VALUE) break;
171
202
  value = value * 10000 + parsed;
172
203
  srcStart += 8;
@@ -179,12 +210,15 @@ const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
179
210
  srcStart += 2;
180
211
  }
181
212
 
182
- pushUnsignedInteger<T>(out, value);
213
+ storeUnsignedIntegerE<E>(slot, value);
183
214
  return srcStart;
184
215
  }
185
216
 
186
217
 
187
- @inline function skipIntegerArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
218
+ @inline function skipIntegerArrayWhitespace(
219
+ srcStart: usize,
220
+ srcEnd: usize,
221
+ ): usize {
188
222
  while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) {
189
223
  srcStart += 2;
190
224
  }
@@ -192,12 +226,17 @@ const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
192
226
  }
193
227
 
194
228
  // @ts-ignore: Decorator valid here
195
- export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
196
- const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
229
+ export function deserializeIntegerArray_SLOW<T extends number[]>(
230
+ srcStart: usize,
231
+ srcEnd: usize,
232
+ dst: usize,
233
+ ): T {
234
+ const out = changetype<nonnull<T>>(
235
+ dst || changetype<usize>(instantiate<T>()),
236
+ );
197
237
  let index = 0;
198
238
 
199
239
  out.length = 0;
200
- srcStart = skipIntegerArrayWhitespace(srcStart, srcEnd);
201
240
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) {
202
241
  throw new Error("Failed to parse JSON!");
203
242
  }
@@ -231,7 +270,10 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize
231
270
  srcStart += 2;
232
271
  }
233
272
 
234
- storeSignedInteger<T>(ensureArrayElementSlot<T>(out, index), negative ? -(<i64>value) : <i64>value);
273
+ storeSignedIntegerE<valueof<T>>(
274
+ ensureArrayElementSlot<T>(out, index),
275
+ negative ? -(<i64>value) : <i64>value,
276
+ );
235
277
  } else {
236
278
  let digit = <u32>code - 48;
237
279
  if (digit > 9) break;
@@ -245,7 +287,10 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize
245
287
  srcStart += 2;
246
288
  }
247
289
 
248
- storeUnsignedInteger<T>(ensureArrayElementSlot<T>(out, index), value);
290
+ storeUnsignedIntegerE<valueof<T>>(
291
+ ensureArrayElementSlot<T>(out, index),
292
+ value,
293
+ );
249
294
  }
250
295
 
251
296
  index++;
@@ -265,8 +310,15 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize
265
310
  }
266
311
 
267
312
 
268
- @inline function deserializeIntegerArrayImpl<T extends number[]>(srcStart: usize, srcEnd: usize, dst: usize, useSWAR: bool): T {
269
- const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
313
+ @inline function deserializeIntegerArrayImpl<T extends number[]>(
314
+ srcStart: usize,
315
+ srcEnd: usize,
316
+ dst: usize,
317
+ useSWAR: bool,
318
+ ): T {
319
+ const out = changetype<nonnull<T>>(
320
+ dst || changetype<usize>(instantiate<T>()),
321
+ );
270
322
  const originalSrcStart = srcStart;
271
323
  const reusableLength = out.length;
272
324
 
@@ -300,11 +352,13 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize
300
352
  let value: u64 = digit;
301
353
  srcStart += 2;
302
354
 
303
- while (srcStart + 6 < srcEnd) {
304
- const parsed = parse4DigitsASCII(load<u64>(srcStart));
305
- if (parsed == U32.MAX_VALUE) break;
306
- value = value * 10000 + parsed;
307
- srcStart += 8;
355
+ if (sizeof<valueof<T>>() > 1) {
356
+ while (srcStart + 6 < srcEnd) {
357
+ const parsed = parse4Digits_PairMul(load<u64>(srcStart));
358
+ if (parsed == U32.MAX_VALUE) break;
359
+ value = value * 10000 + parsed;
360
+ srcStart += 8;
361
+ }
308
362
  }
309
363
 
310
364
  while (srcStart < srcEnd) {
@@ -315,7 +369,10 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize
315
369
  }
316
370
 
317
371
  if (index >= reusableLength) break;
318
- storeSignedInteger<T>(dataStart + <usize>index * sizeof<valueof<T>>(), negative ? -(<i64>value) : <i64>value);
372
+ storeSignedIntegerE<valueof<T>>(
373
+ dataStart + <usize>index * sizeof<valueof<T>>(),
374
+ negative ? -(<i64>value) : <i64>value,
375
+ );
319
376
  index++;
320
377
  if (srcStart >= srcEnd) break;
321
378
 
@@ -338,11 +395,13 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize
338
395
  let value: u64 = digit;
339
396
  srcStart += 2;
340
397
 
341
- while (srcStart + 6 < srcEnd) {
342
- const parsed = parse4DigitsASCII(load<u64>(srcStart));
343
- if (parsed == U32.MAX_VALUE) break;
344
- value = value * 10000 + parsed;
345
- srcStart += 8;
398
+ if (sizeof<valueof<T>>() > 1) {
399
+ while (srcStart + 6 < srcEnd) {
400
+ const parsed = parse4Digits_PairMul(load<u64>(srcStart));
401
+ if (parsed == U32.MAX_VALUE) break;
402
+ value = value * 10000 + parsed;
403
+ srcStart += 8;
404
+ }
346
405
  }
347
406
 
348
407
  while (srcStart < srcEnd) {
@@ -353,7 +412,10 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize
353
412
  }
354
413
 
355
414
  if (index >= reusableLength) break;
356
- storeUnsignedInteger<T>(dataStart + <usize>index * sizeof<valueof<T>>(), value);
415
+ storeUnsignedIntegerE<valueof<T>>(
416
+ dataStart + <usize>index * sizeof<valueof<T>>(),
417
+ value,
418
+ );
357
419
  index++;
358
420
  if (srcStart >= srcEnd) break;
359
421
 
@@ -374,63 +436,226 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize
374
436
  srcStart = originalSrcStart;
375
437
  }
376
438
 
377
- out.length = 0;
439
+ // Worst-case sizing: every element is at least 1 digit + 1 delimiter = 2
440
+ // UTF-16 chars = 4 bytes. AS skips zero-fill on `length=` for unmanaged
441
+ // primitive types (every concrete `valueof<T>` here is an integer), so the
442
+ // over-allocation costs only a small amount of trimmed storage at the end.
443
+ // The parse helpers below store through `writePtr` directly, eliminating
444
+ // `Array.push`'s per-element capacity check + length write for every
445
+ // integer width.
446
+ const elementSize = sizeof<valueof<T>>();
447
+ const maxElements = i32((<usize>(srcEnd - srcStart)) >> 2);
448
+ if (maxElements > 0) out.length = maxElements;
449
+ const dataStart = out.dataStart;
450
+ let writePtr = dataStart;
378
451
 
379
452
  do {
380
453
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
381
454
  srcStart += 2;
382
455
  if (srcStart >= srcEnd) break;
383
- if (load<u16>(srcStart) == BRACKET_RIGHT) return out;
456
+ if (load<u16>(srcStart) == BRACKET_RIGHT) {
457
+ out.length = 0;
458
+ return out;
459
+ }
384
460
 
385
461
  if (isSigned<valueof<T>>()) {
386
462
  while (srcStart < srcEnd) {
387
- srcStart = useSWAR ? parseSignedIntegerSWAR<T>(srcStart, srcEnd, out) : parseSignedIntegerScalar<T>(srcStart, srcEnd, out);
388
- if (!srcStart || srcStart >= srcEnd) break;
463
+ const next = useSWAR
464
+ ? parseSignedIntegerSWAR<valueof<T>>(srcStart, srcEnd, writePtr)
465
+ : parseSignedIntegerScalar<valueof<T>>(srcStart, srcEnd, writePtr);
466
+ if (!next) break;
467
+ writePtr += elementSize;
468
+ srcStart = next;
469
+ if (srcStart >= srcEnd) break;
389
470
 
390
471
  const code = load<u16>(srcStart);
391
472
  if (code == COMMA) {
392
473
  srcStart += 2;
393
474
  continue;
394
475
  }
395
- if (code == BRACKET_RIGHT) return out;
476
+ if (code == BRACKET_RIGHT) {
477
+ out.length = i32(<usize>(writePtr - dataStart) / elementSize);
478
+ return out;
479
+ }
396
480
  break;
397
481
  }
398
482
  } else {
399
483
  while (srcStart < srcEnd) {
400
- srcStart = useSWAR ? parseUnsignedIntegerSWAR<T>(srcStart, srcEnd, out) : parseUnsignedIntegerScalar<T>(srcStart, srcEnd, out);
401
- if (!srcStart || srcStart >= srcEnd) break;
484
+ const next = useSWAR
485
+ ? parseUnsignedIntegerSWAR<valueof<T>>(srcStart, srcEnd, writePtr)
486
+ : parseUnsignedIntegerScalar<valueof<T>>(srcStart, srcEnd, writePtr);
487
+ if (!next) break;
488
+ writePtr += elementSize;
489
+ srcStart = next;
490
+ if (srcStart >= srcEnd) break;
402
491
 
403
492
  const code = load<u16>(srcStart);
404
493
  if (code == COMMA) {
405
494
  srcStart += 2;
406
495
  continue;
407
496
  }
408
- if (code == BRACKET_RIGHT) return out;
497
+ if (code == BRACKET_RIGHT) {
498
+ out.length = i32(<usize>(writePtr - dataStart) / elementSize);
499
+ return out;
500
+ }
409
501
  break;
410
502
  }
411
503
  }
412
504
  } while (false);
413
505
 
414
- return deserializeIntegerArray_SLOW<T>(originalSrcStart, srcEnd, changetype<usize>(out));
506
+ // Fast path bailed (whitespace, malformed numbers, etc). Hand off to the
507
+ // SLOW path which resets `out.length` to 0 and re-parses from the original
508
+ // input. The pre-allocated buffer is retained, so SLOW's per-element
509
+ // `ensureArrayElementSlot` grows-or-reuses through the existing capacity.
510
+ return deserializeIntegerArray_SLOW<T>(
511
+ originalSrcStart,
512
+ srcEnd,
513
+ changetype<usize>(out),
514
+ );
415
515
  }
416
516
 
417
517
  // @ts-ignore: Decorator valid here
418
- export function deserializeIntegerArray<T extends number[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
419
- return deserializeIntegerArrayImpl<T>(srcStart, srcEnd, dst, false);
518
+ export function deserializeIntegerArray_SWAR<T extends number[]>(
519
+ srcStart: usize,
520
+ srcEnd: usize,
521
+ dst: usize,
522
+ ): T {
523
+ // u8/i8 elements use a dedicated two-pass SWAR path: a 4-char-stride
524
+ // comma counter pre-sizes the array so the parse pass can drop the
525
+ // per-push capacity check and write through a direct pointer. The
526
+ // wider-lane SWAR kernel never amortizes itself for narrow elements
527
+ // (u8 max 3 digits) so we skip it entirely here.
528
+ if (sizeof<valueof<T>>() <= 1) {
529
+ return deserializeNarrowIntegerArray_SWAR<T>(srcStart, srcEnd, dst);
530
+ }
531
+ return deserializeIntegerArrayImpl<T>(srcStart, srcEnd, dst, true);
420
532
  }
421
533
 
422
- // @ts-ignore: Decorator valid here
423
- export function deserializeIntegerArray_SWAR<T extends number[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
424
- return deserializeIntegerArrayImpl<T>(srcStart, srcEnd, dst, true);
534
+ /**
535
+ * Narrow-lane (u8/i8) integer-array deserializer.
536
+ *
537
+ * Two passes:
538
+ * 1) SWAR comma counter (4 chars per stride, popcnt over the lane mask)
539
+ * sizes the array exactly so the parse pass can use unchecked stores.
540
+ * 2) Walks the input NAIVE-style (skip non-digits, scan to separator,
541
+ * fold digits) but writes through a direct pointer, eliminating
542
+ * `Array.push`'s per-element capacity check and length write.
543
+ *
544
+ * V8 already auto-SIMDs NAIVE's scalar scan loop tighter than a hand-rolled
545
+ * SWAR scan in pass 2, so we keep that pattern there and only pay SWAR cost
546
+ * on the pre-count - which is a tight load-mask-popcnt loop where V8's
547
+ * scalar code can't compete with the explicit 4-lane stride.
548
+ */
549
+ function deserializeNarrowIntegerArray_SWAR<T extends number[]>(
550
+ srcStart: usize,
551
+ srcEnd: usize,
552
+ dst: usize,
553
+ ): T {
554
+ const out = changetype<nonnull<T>>(
555
+ dst || changetype<usize>(instantiate<T>()),
556
+ );
557
+
558
+ // Worst-case sizing: every element is at least 1 digit + 1 delimiter = 2
559
+ // UTF-16 chars = 4 bytes, so the body inside `[...]` can't hold more than
560
+ // `(srcEnd - srcStart) / 4` elements. AS skips zero-fill on `length=` for
561
+ // unmanaged element types, so over-allocation is essentially free here
562
+ // and saves a full SWAR pass over the input.
563
+ const maxElements = i32((<usize>(srcEnd - srcStart)) >> 2);
564
+ if (maxElements > 0) out.length = maxElements;
565
+ const dataStart = out.dataStart;
566
+ const elementSize = sizeof<valueof<T>>();
567
+ let writePtr = dataStart;
568
+
569
+ while (srcStart < srcEnd) {
570
+ // Fast paths: 1-, 2-, or 3-digit unsigned element followed by `,` packs
571
+ // into one u64 load (covers ~100% of the typical 0..255 cycle). Ordered
572
+ // 3 -> 2 -> 1 because 3-digit values dominate any wide-range payload;
573
+ // the 2- and 1-digit branches cover the rest of `out`.
574
+ if (!isSigned<valueof<T>>() && srcStart + 6 < srcEnd) {
575
+ const block = load<u64>(srcStart);
576
+ if (((block >> 48) & 0xffff) == COMMA) {
577
+ const digits = (block & 0x0000_00ff_00ff_00ff) - 0x0000_0030_0030_0030;
578
+ const oor =
579
+ (digits | (digits + 0x0000_0006_0006_0006)) & 0x0000_fff0_fff0_fff0;
580
+ if (oor == 0) {
581
+ const d0 = <u32>(digits & 0xffff);
582
+ const d1 = <u32>((digits >> 16) & 0xffff);
583
+ const d2 = <u32>((digits >> 32) & 0xffff);
584
+ store<valueof<T>>(writePtr, <valueof<T>>(d0 * 100 + d1 * 10 + d2));
585
+ writePtr += elementSize;
586
+ srcStart += 8;
587
+ continue;
588
+ }
589
+ } else if (((block >> 32) & 0xffff) == COMMA) {
590
+ const digits = (block & 0x0000_0000_00ff_00ff) - 0x0000_0000_0030_0030;
591
+ const oor =
592
+ (digits | (digits + 0x0000_0000_0006_0006)) & 0x0000_0000_fff0_fff0;
593
+ if (oor == 0) {
594
+ const d0 = <u32>(digits & 0xffff);
595
+ const d1 = <u32>((digits >> 16) & 0xffff);
596
+ store<valueof<T>>(writePtr, <valueof<T>>(d0 * 10 + d1));
597
+ writePtr += elementSize;
598
+ srcStart += 6;
599
+ continue;
600
+ }
601
+ } else if (((block >> 16) & 0xffff) == COMMA) {
602
+ const d0 = <u32>(block & 0xffff) - 48;
603
+ if (d0 <= 9) {
604
+ store<valueof<T>>(writePtr, <valueof<T>>d0);
605
+ writePtr += elementSize;
606
+ srcStart += 4;
607
+ continue;
608
+ }
609
+ }
610
+ }
611
+ const code = load<u16>(srcStart);
612
+ if (<u32>code - 48 <= 9 || (isSigned<valueof<T>>() && code == 45)) {
613
+ const lastIndex = srcStart;
614
+ srcStart += 2;
615
+ while (srcStart < srcEnd) {
616
+ const c = load<u16>(srcStart);
617
+ if (c == COMMA || c == BRACKET_RIGHT || isSpace(c)) {
618
+ let value: u64 = 0;
619
+ let p = lastIndex;
620
+ if (isSigned<valueof<T>>() && load<u16>(p) == 45) {
621
+ p += 2;
622
+ while (p < srcStart) {
623
+ value = value * 10 + (<u32>load<u16>(p) - 48);
624
+ p += 2;
625
+ }
626
+ store<valueof<T>>(writePtr, <valueof<T>>-(<i64>value));
627
+ } else {
628
+ while (p < srcStart) {
629
+ value = value * 10 + (<u32>load<u16>(p) - 48);
630
+ p += 2;
631
+ }
632
+ store<valueof<T>>(writePtr, <valueof<T>>value);
633
+ }
634
+ writePtr += elementSize;
635
+ break;
636
+ }
637
+ srcStart += 2;
638
+ }
639
+ }
640
+ srcStart += 2;
641
+ }
642
+
643
+ out.length = i32(<usize>(writePtr - dataStart) / elementSize);
644
+ return out;
425
645
  }
426
646
 
427
647
 
428
- @inline export function deserializeIntegerArrayInto<T extends number[]>(srcStart: usize, srcEnd: usize, out: T): usize {
648
+ @inline function deserializeIntegerArrayBody<T extends number[]>(
649
+ srcStart: usize,
650
+ srcEnd: usize,
651
+ out: T,
652
+ ): usize {
429
653
  let index = 0;
430
654
 
431
655
  do {
432
656
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
433
657
  srcStart += 2;
658
+ while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
434
659
  if (srcStart >= srcEnd) break;
435
660
  if (load<u16>(srcStart) == BRACKET_RIGHT) {
436
661
  out.length = 0;
@@ -439,17 +664,35 @@ export function deserializeIntegerArray_SWAR<T extends number[]>(srcStart: usize
439
664
 
440
665
  while (srcStart < srcEnd) {
441
666
  const slot = ensureArrayElementSlot<T>(out, index);
442
- srcStart = isSigned<valueof<T>>() ? deserializeIntegerField<valueof<T>>(srcStart, srcEnd, slot) : deserializeUnsignedField<valueof<T>>(srcStart, srcEnd, slot);
443
- if (!srcStart || srcStart >= srcEnd) break;
667
+ // Inline the array-optimized SWAR parser directly. The top-level
668
+ // (`deserializeIntegerArrayImpl`) and the typed-array path
669
+ // (`swar/typedarray.ts`) already call these — having the field path
670
+ // call them too means the per-element parser is identical across all
671
+ // three call sites (parse4 + scalar fold for both signed and
672
+ // unsigned, narrow-lane special case for i8/u8/i16/u16).
673
+ //
674
+ // Why not `deserializeUnsignedField_SWAR` (which uses parse8 + scalar)?
675
+ // That tuning targets the struct-single-field path where the digit
676
+ // run is one aligned token. In an array, mixed element widths cause
677
+ // parse8 to fail-and-retry at element boundaries — see u32-64mib's
678
+ // 23% regression when parse8 was tried in the array path.
679
+ srcStart = isSigned<valueof<T>>()
680
+ ? parseSignedIntegerSWAR<valueof<T>>(srcStart, srcEnd, slot)
681
+ : parseUnsignedIntegerSWAR<valueof<T>>(srcStart, srcEnd, slot);
682
+ if (!srcStart) break;
683
+ while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
684
+ if (srcStart >= srcEnd) break;
444
685
 
445
686
  const code = load<u16>(srcStart);
446
687
  if (code == COMMA) {
447
688
  srcStart += 2;
689
+ while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
448
690
  index++;
449
691
  continue;
450
692
  }
451
693
  if (code == BRACKET_RIGHT) {
452
- out.length = index + 1;
694
+ const nextLen = index + 1;
695
+ if (out.length != nextLen) out.length = nextLen;
453
696
  return srcStart + 2;
454
697
  }
455
698
  break;
@@ -460,6 +703,14 @@ export function deserializeIntegerArray_SWAR<T extends number[]>(srcStart: usize
460
703
  }
461
704
 
462
705
 
463
- @inline export function deserializeIntegerArrayField<T extends number[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
464
- return deserializeIntegerArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
706
+ @inline export function deserializeIntegerArrayField<T extends number[]>(
707
+ srcStart: usize,
708
+ srcEnd: usize,
709
+ fieldPtr: usize,
710
+ ): usize {
711
+ return deserializeIntegerArrayBody<T>(
712
+ srcStart,
713
+ srcEnd,
714
+ ensureArrayField<T>(fieldPtr),
715
+ );
465
716
  }