json-as 1.3.5 → 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 (107) hide show
  1. package/CHANGELOG.md +17 -1
  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 +9 -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 +25 -8
  70. package/assembly/tsconfig.json +1 -21
  71. package/assembly/util/atoi-fast.ts +81 -0
  72. package/assembly/util/concat.ts +5 -1
  73. package/assembly/util/dragonbox-cache.ts +443 -2
  74. package/assembly/util/dragonbox.ts +43 -14
  75. package/assembly/util/itoa-fast.ts +230 -0
  76. package/assembly/util/masks.ts +18 -1
  77. package/assembly/util/parsefloat-fast.ts +167 -0
  78. package/assembly/util/simd-int.ts +191 -0
  79. package/assembly/util/snp.ts +4 -1
  80. package/assembly/util/swar-int.ts +248 -0
  81. package/assembly/util/swar.ts +13 -3
  82. package/lib/as-bs.ts +13 -5
  83. package/package.json +12 -4
  84. package/transform/lib/builder.d.ts.map +1 -1
  85. package/transform/lib/builder.js +13 -5
  86. package/transform/lib/builder.js.map +1 -1
  87. package/transform/lib/index.d.ts +1 -0
  88. package/transform/lib/index.d.ts.map +1 -1
  89. package/transform/lib/index.js +1030 -241
  90. package/transform/lib/index.js.map +1 -1
  91. package/transform/lib/linkers/alias.d.ts.map +1 -1
  92. package/transform/lib/linkers/alias.js.map +1 -1
  93. package/transform/lib/linkers/custom.d.ts.map +1 -1
  94. package/transform/lib/linkers/custom.js +3 -2
  95. package/transform/lib/linkers/custom.js.map +1 -1
  96. package/transform/lib/linkers/imports.d.ts.map +1 -1
  97. package/transform/lib/linkers/imports.js.map +1 -1
  98. package/transform/lib/types.d.ts.map +1 -1
  99. package/transform/lib/types.js +54 -16
  100. package/transform/lib/types.js.map +1 -1
  101. package/transform/lib/util.d.ts.map +1 -1
  102. package/transform/lib/util.js +1 -1
  103. package/transform/lib/util.js.map +1 -1
  104. package/transform/lib/visitor.d.ts.map +1 -1
  105. package/transform/lib/visitor.js +2 -1
  106. package/transform/lib/visitor.js.map +1 -1
  107. package/assembly/custom/util.ts +0 -310
@@ -1,5 +1,6 @@
1
- import { deserializeIntegerField } from "../../simple/integer";
2
- import { deserializeUnsignedField } from "../../simple/unsigned";
1
+ import { deserializeIntegerField } from "../../index/integer";
2
+ import { deserializeUnsignedField } from "../../index/unsigned";
3
+ import { deserializeIntegerArray as deserializeIntegerArray_NAIVE } from "../../simple/array/integer";
3
4
  import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
4
5
  import { isSpace } from "../../../util";
5
6
  import { ensureArrayElementSlot, ensureArrayField } from "./shared";
@@ -10,37 +11,10 @@ const ASCII_RANGE_MASK_4: u64 = 0xfff0fff0fff0fff0;
10
11
  const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
11
12
 
12
13
 
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 {
14
+ @inline function storeSignedInteger<T extends number[]>(
15
+ slot: usize,
16
+ value: i64,
17
+ ): void {
44
18
  if (sizeof<valueof<T>>() == sizeof<i8>()) {
45
19
  store<i8>(slot, <i8>value);
46
20
  } else if (sizeof<valueof<T>>() == sizeof<i16>()) {
@@ -55,7 +29,10 @@ const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
55
29
  }
56
30
 
57
31
 
58
- @inline function storeUnsignedInteger<T extends number[]>(slot: usize, value: u64): void {
32
+ @inline function storeUnsignedInteger<T extends number[]>(
33
+ slot: usize,
34
+ value: u64,
35
+ ): void {
59
36
  if (sizeof<valueof<T>>() == sizeof<u8>()) {
60
37
  store<u8>(slot, <u8>value);
61
38
  } else if (sizeof<valueof<T>>() == sizeof<u16>()) {
@@ -72,13 +49,29 @@ const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
72
49
 
73
50
  @inline function parse4DigitsASCII(block: u64): u32 {
74
51
  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));
52
+ if (((digits | (digits + ASCII_RANGE_ADD_4)) & ASCII_RANGE_MASK_4) != 0)
53
+ return U32.MAX_VALUE;
54
+
55
+ return <u32>(
56
+ (<u32>(digits & 0xffff) * 1000 +
57
+ <u32>((digits >> 16) & 0xffff) * 100 +
58
+ <u32>((digits >> 32) & 0xffff) * 10 +
59
+ <u32>(digits >> 48))
60
+ );
78
61
  }
79
62
 
80
-
81
- @inline function parseSignedIntegerScalar<T extends number[]>(srcStart: usize, srcEnd: usize, out: T): usize {
63
+ // The four parse helpers below take a `slot` pointer (`writePtr`) and store
64
+ // the value directly via `store<valueof<T>>(slot, ...)`. The outer dispatcher
65
+ // owns the array's `out.length = maxElements` pre-allocation and the
66
+ // `writePtr` advance, so the per-element `Array.push` capacity check and
67
+ // length write are eliminated for every integer width, not just the u8/i8
68
+ // narrow-lane path.
69
+
70
+ @inline function parseSignedIntegerScalar<T extends number[]>(
71
+ srcStart: usize,
72
+ srcEnd: usize,
73
+ slot: usize,
74
+ ): usize {
82
75
  let negative = false;
83
76
  let code = load<u16>(srcStart);
84
77
  if (code == 45) {
@@ -100,12 +93,16 @@ const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
100
93
  srcStart += 2;
101
94
  }
102
95
 
103
- pushSignedInteger<T>(out, negative ? -(<i64>value) : <i64>value);
96
+ storeSignedInteger<T>(slot, negative ? -(<i64>value) : <i64>value);
104
97
  return srcStart;
105
98
  }
106
99
 
107
100
 
108
- @inline function parseUnsignedIntegerScalar<T extends number[]>(srcStart: usize, srcEnd: usize, out: T): usize {
101
+ @inline function parseUnsignedIntegerScalar<T extends number[]>(
102
+ srcStart: usize,
103
+ srcEnd: usize,
104
+ slot: usize,
105
+ ): usize {
109
106
  let digit = <u32>load<u16>(srcStart) - 48;
110
107
  if (digit > 9) return 0;
111
108
 
@@ -118,12 +115,16 @@ const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
118
115
  srcStart += 2;
119
116
  }
120
117
 
121
- pushUnsignedInteger<T>(out, value);
118
+ storeUnsignedInteger<T>(slot, value);
122
119
  return srcStart;
123
120
  }
124
121
 
125
122
 
126
- @inline function parseSignedIntegerSWAR<T extends number[]>(srcStart: usize, srcEnd: usize, out: T): usize {
123
+ @inline function parseSignedIntegerSWAR<T extends number[]>(
124
+ srcStart: usize,
125
+ srcEnd: usize,
126
+ slot: usize,
127
+ ): usize {
127
128
  let negative = false;
128
129
  let code = load<u16>(srcStart);
129
130
  if (code == 45) {
@@ -139,11 +140,16 @@ const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
139
140
  let value: u64 = digit;
140
141
  srcStart += 2;
141
142
 
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;
143
+ // i8 tops out at 3 digits (-128..127), so the 4-digit kernel can never fire
144
+ // and the failing load + range check just burns cycles. Gate on the lane
145
+ // width so AS folds the loop away at compile time.
146
+ if (sizeof<valueof<T>>() > 1) {
147
+ while (srcStart + 6 < srcEnd) {
148
+ const parsed = parse4DigitsASCII(load<u64>(srcStart));
149
+ if (parsed == U32.MAX_VALUE) break;
150
+ value = value * 10000 + parsed;
151
+ srcStart += 8;
152
+ }
147
153
  }
148
154
 
149
155
  while (srcStart < srcEnd) {
@@ -153,12 +159,40 @@ const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
153
159
  srcStart += 2;
154
160
  }
155
161
 
156
- pushSignedInteger<T>(out, negative ? -(<i64>value) : <i64>value);
162
+ storeSignedInteger<T>(slot, negative ? -(<i64>value) : <i64>value);
157
163
  return srcStart;
158
164
  }
159
165
 
160
166
 
161
- @inline function parseUnsignedIntegerSWAR<T extends number[]>(srcStart: usize, srcEnd: usize, out: T): usize {
167
+ @inline function parseUnsignedIntegerSWAR<T extends number[]>(
168
+ srcStart: usize,
169
+ srcEnd: usize,
170
+ slot: usize,
171
+ ): usize {
172
+ // Narrow-type path mirrors the NAIVE structure: a tight scan loop to find
173
+ // the element terminator, then a fixed-count fold with no per-digit break.
174
+ // TurboFan tends to schedule this better than a single combined
175
+ // scan-and-fold loop because the fold has no data-dependent exit.
176
+ if (sizeof<valueof<T>>() <= 2) {
177
+ const first = <u32>load<u16>(srcStart) - 48;
178
+ if (first > 9) return 0;
179
+ const lastIndex = srcStart;
180
+ srcStart += 2;
181
+ while (srcStart < srcEnd) {
182
+ const c = <u32>load<u16>(srcStart) - 48;
183
+ if (c > 9) break;
184
+ srcStart += 2;
185
+ }
186
+ let value: u64 = 0;
187
+ let p = lastIndex;
188
+ while (p < srcStart) {
189
+ value = value * 10 + (<u32>load<u16>(p) - 48);
190
+ p += 2;
191
+ }
192
+ storeUnsignedInteger<T>(slot, value);
193
+ return srcStart;
194
+ }
195
+
162
196
  let digit = <u32>load<u16>(srcStart) - 48;
163
197
  if (digit > 9) return 0;
164
198
 
@@ -179,12 +213,15 @@ const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
179
213
  srcStart += 2;
180
214
  }
181
215
 
182
- pushUnsignedInteger<T>(out, value);
216
+ storeUnsignedInteger<T>(slot, value);
183
217
  return srcStart;
184
218
  }
185
219
 
186
220
 
187
- @inline function skipIntegerArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
221
+ @inline function skipIntegerArrayWhitespace(
222
+ srcStart: usize,
223
+ srcEnd: usize,
224
+ ): usize {
188
225
  while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) {
189
226
  srcStart += 2;
190
227
  }
@@ -192,8 +229,14 @@ const ASCII_RANGE_ADD_4: u64 = 0x0006000600060006;
192
229
  }
193
230
 
194
231
  // @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>()));
232
+ export function deserializeIntegerArray_SLOW<T extends number[]>(
233
+ srcStart: usize,
234
+ srcEnd: usize,
235
+ dst: usize,
236
+ ): T {
237
+ const out = changetype<nonnull<T>>(
238
+ dst || changetype<usize>(instantiate<T>()),
239
+ );
197
240
  let index = 0;
198
241
 
199
242
  out.length = 0;
@@ -231,7 +274,10 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize
231
274
  srcStart += 2;
232
275
  }
233
276
 
234
- storeSignedInteger<T>(ensureArrayElementSlot<T>(out, index), negative ? -(<i64>value) : <i64>value);
277
+ storeSignedInteger<T>(
278
+ ensureArrayElementSlot<T>(out, index),
279
+ negative ? -(<i64>value) : <i64>value,
280
+ );
235
281
  } else {
236
282
  let digit = <u32>code - 48;
237
283
  if (digit > 9) break;
@@ -265,8 +311,15 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize
265
311
  }
266
312
 
267
313
 
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>()));
314
+ @inline function deserializeIntegerArrayImpl<T extends number[]>(
315
+ srcStart: usize,
316
+ srcEnd: usize,
317
+ dst: usize,
318
+ useSWAR: bool,
319
+ ): T {
320
+ const out = changetype<nonnull<T>>(
321
+ dst || changetype<usize>(instantiate<T>()),
322
+ );
270
323
  const originalSrcStart = srcStart;
271
324
  const reusableLength = out.length;
272
325
 
@@ -300,11 +353,13 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize
300
353
  let value: u64 = digit;
301
354
  srcStart += 2;
302
355
 
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;
356
+ if (sizeof<valueof<T>>() > 1) {
357
+ while (srcStart + 6 < srcEnd) {
358
+ const parsed = parse4DigitsASCII(load<u64>(srcStart));
359
+ if (parsed == U32.MAX_VALUE) break;
360
+ value = value * 10000 + parsed;
361
+ srcStart += 8;
362
+ }
308
363
  }
309
364
 
310
365
  while (srcStart < srcEnd) {
@@ -315,7 +370,10 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize
315
370
  }
316
371
 
317
372
  if (index >= reusableLength) break;
318
- storeSignedInteger<T>(dataStart + <usize>index * sizeof<valueof<T>>(), negative ? -(<i64>value) : <i64>value);
373
+ storeSignedInteger<T>(
374
+ dataStart + <usize>index * sizeof<valueof<T>>(),
375
+ negative ? -(<i64>value) : <i64>value,
376
+ );
319
377
  index++;
320
378
  if (srcStart >= srcEnd) break;
321
379
 
@@ -338,11 +396,13 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize
338
396
  let value: u64 = digit;
339
397
  srcStart += 2;
340
398
 
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;
399
+ if (sizeof<valueof<T>>() > 1) {
400
+ while (srcStart + 6 < srcEnd) {
401
+ const parsed = parse4DigitsASCII(load<u64>(srcStart));
402
+ if (parsed == U32.MAX_VALUE) break;
403
+ value = value * 10000 + parsed;
404
+ srcStart += 8;
405
+ }
346
406
  }
347
407
 
348
408
  while (srcStart < srcEnd) {
@@ -353,7 +413,10 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize
353
413
  }
354
414
 
355
415
  if (index >= reusableLength) break;
356
- storeUnsignedInteger<T>(dataStart + <usize>index * sizeof<valueof<T>>(), value);
416
+ storeUnsignedInteger<T>(
417
+ dataStart + <usize>index * sizeof<valueof<T>>(),
418
+ value,
419
+ );
357
420
  index++;
358
421
  if (srcStart >= srcEnd) break;
359
422
 
@@ -374,58 +437,229 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(srcStart: usize
374
437
  srcStart = originalSrcStart;
375
438
  }
376
439
 
377
- out.length = 0;
440
+ // Worst-case sizing: every element is at least 1 digit + 1 delimiter = 2
441
+ // UTF-16 chars = 4 bytes. AS skips zero-fill on `length=` for unmanaged
442
+ // primitive types (every concrete `valueof<T>` here is an integer), so the
443
+ // over-allocation costs only a small amount of trimmed storage at the end.
444
+ // The parse helpers below store through `writePtr` directly, eliminating
445
+ // `Array.push`'s per-element capacity check + length write for every
446
+ // integer width.
447
+ const elementSize = sizeof<valueof<T>>();
448
+ const maxElements = i32((<usize>(srcEnd - srcStart)) >> 2);
449
+ if (maxElements > 0) out.length = maxElements;
450
+ const dataStart = out.dataStart;
451
+ let writePtr = dataStart;
378
452
 
379
453
  do {
380
454
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
381
455
  srcStart += 2;
382
456
  if (srcStart >= srcEnd) break;
383
- if (load<u16>(srcStart) == BRACKET_RIGHT) return out;
457
+ if (load<u16>(srcStart) == BRACKET_RIGHT) {
458
+ out.length = 0;
459
+ return out;
460
+ }
384
461
 
385
462
  if (isSigned<valueof<T>>()) {
386
463
  while (srcStart < srcEnd) {
387
- srcStart = useSWAR ? parseSignedIntegerSWAR<T>(srcStart, srcEnd, out) : parseSignedIntegerScalar<T>(srcStart, srcEnd, out);
388
- if (!srcStart || srcStart >= srcEnd) break;
464
+ const next = useSWAR
465
+ ? parseSignedIntegerSWAR<T>(srcStart, srcEnd, writePtr)
466
+ : parseSignedIntegerScalar<T>(srcStart, srcEnd, writePtr);
467
+ if (!next) break;
468
+ writePtr += elementSize;
469
+ srcStart = next;
470
+ if (srcStart >= srcEnd) break;
389
471
 
390
472
  const code = load<u16>(srcStart);
391
473
  if (code == COMMA) {
392
474
  srcStart += 2;
393
475
  continue;
394
476
  }
395
- if (code == BRACKET_RIGHT) return out;
477
+ if (code == BRACKET_RIGHT) {
478
+ out.length = i32(<usize>(writePtr - dataStart) / elementSize);
479
+ return out;
480
+ }
396
481
  break;
397
482
  }
398
483
  } else {
399
484
  while (srcStart < srcEnd) {
400
- srcStart = useSWAR ? parseUnsignedIntegerSWAR<T>(srcStart, srcEnd, out) : parseUnsignedIntegerScalar<T>(srcStart, srcEnd, out);
401
- if (!srcStart || srcStart >= srcEnd) break;
485
+ const next = useSWAR
486
+ ? parseUnsignedIntegerSWAR<T>(srcStart, srcEnd, writePtr)
487
+ : parseUnsignedIntegerScalar<T>(srcStart, srcEnd, writePtr);
488
+ if (!next) break;
489
+ writePtr += elementSize;
490
+ srcStart = next;
491
+ if (srcStart >= srcEnd) break;
402
492
 
403
493
  const code = load<u16>(srcStart);
404
494
  if (code == COMMA) {
405
495
  srcStart += 2;
406
496
  continue;
407
497
  }
408
- if (code == BRACKET_RIGHT) return out;
498
+ if (code == BRACKET_RIGHT) {
499
+ out.length = i32(<usize>(writePtr - dataStart) / elementSize);
500
+ return out;
501
+ }
409
502
  break;
410
503
  }
411
504
  }
412
505
  } while (false);
413
506
 
414
- return deserializeIntegerArray_SLOW<T>(originalSrcStart, srcEnd, changetype<usize>(out));
507
+ // Fast path bailed (whitespace, malformed numbers, etc). Hand off to the
508
+ // SLOW path which resets `out.length` to 0 and re-parses from the original
509
+ // input. The pre-allocated buffer is retained, so SLOW's per-element
510
+ // `ensureArrayElementSlot` grows-or-reuses through the existing capacity.
511
+ return deserializeIntegerArray_SLOW<T>(
512
+ originalSrcStart,
513
+ srcEnd,
514
+ changetype<usize>(out),
515
+ );
415
516
  }
416
517
 
417
518
  // @ts-ignore: Decorator valid here
418
- export function deserializeIntegerArray<T extends number[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
519
+ export function deserializeIntegerArray<T extends number[]>(
520
+ srcStart: usize,
521
+ srcEnd: usize,
522
+ dst: usize,
523
+ ): T {
419
524
  return deserializeIntegerArrayImpl<T>(srcStart, srcEnd, dst, false);
420
525
  }
421
526
 
422
527
  // @ts-ignore: Decorator valid here
423
- export function deserializeIntegerArray_SWAR<T extends number[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
528
+ export function deserializeIntegerArray_SWAR<T extends number[]>(
529
+ srcStart: usize,
530
+ srcEnd: usize,
531
+ dst: usize,
532
+ ): T {
533
+ // u8/i8 elements use a dedicated two-pass SWAR path: a 4-char-stride
534
+ // comma counter pre-sizes the array so the parse pass can drop the
535
+ // per-push capacity check and write through a direct pointer. The
536
+ // wider-lane SWAR kernel never amortizes itself for narrow elements
537
+ // (u8 max 3 digits) so we skip it entirely here.
538
+ if (sizeof<valueof<T>>() <= 1) {
539
+ return deserializeNarrowIntegerArray_SWAR<T>(srcStart, srcEnd, dst);
540
+ }
424
541
  return deserializeIntegerArrayImpl<T>(srcStart, srcEnd, dst, true);
425
542
  }
426
543
 
544
+ /**
545
+ * Narrow-lane (u8/i8) integer-array deserializer.
546
+ *
547
+ * Two passes:
548
+ * 1) SWAR comma counter (4 chars per stride, popcnt over the lane mask)
549
+ * sizes the array exactly so the parse pass can use unchecked stores.
550
+ * 2) Walks the input NAIVE-style (skip non-digits, scan to separator,
551
+ * fold digits) but writes through a direct pointer, eliminating
552
+ * `Array.push`'s per-element capacity check and length write.
553
+ *
554
+ * V8 already auto-SIMDs NAIVE's scalar scan loop tighter than a hand-rolled
555
+ * SWAR scan in pass 2, so we keep that pattern there and only pay SWAR cost
556
+ * on the pre-count - which is a tight load-mask-popcnt loop where V8's
557
+ * scalar code can't compete with the explicit 4-lane stride.
558
+ */
559
+ function deserializeNarrowIntegerArray_SWAR<T extends number[]>(
560
+ srcStart: usize,
561
+ srcEnd: usize,
562
+ dst: usize,
563
+ ): T {
564
+ const out = changetype<nonnull<T>>(
565
+ dst || changetype<usize>(instantiate<T>()),
566
+ );
567
+
568
+ // Worst-case sizing: every element is at least 1 digit + 1 delimiter = 2
569
+ // UTF-16 chars = 4 bytes, so the body inside `[...]` can't hold more than
570
+ // `(srcEnd - srcStart) / 4` elements. AS skips zero-fill on `length=` for
571
+ // unmanaged element types, so over-allocation is essentially free here
572
+ // and saves a full SWAR pass over the input.
573
+ const maxElements = i32((<usize>(srcEnd - srcStart)) >> 2);
574
+ if (maxElements > 0) out.length = maxElements;
575
+ const dataStart = out.dataStart;
576
+ const elementSize = sizeof<valueof<T>>();
577
+ let writePtr = dataStart;
578
+
579
+ while (srcStart < srcEnd) {
580
+ // Fast paths: 1-, 2-, or 3-digit unsigned element followed by `,` packs
581
+ // into one u64 load (covers ~100% of the typical 0..255 cycle). Ordered
582
+ // 3 -> 2 -> 1 because 3-digit values dominate any wide-range payload;
583
+ // the 2- and 1-digit branches cover the rest of `out`.
584
+ if (!isSigned<valueof<T>>() && srcStart + 6 < srcEnd) {
585
+ const block = load<u64>(srcStart);
586
+ if (((block >> 48) & 0xffff) == COMMA) {
587
+ const digits = (block & 0x0000_00ff_00ff_00ff) - 0x0000_0030_0030_0030;
588
+ const oor =
589
+ (digits | (digits + 0x0000_0006_0006_0006)) & 0x0000_fff0_fff0_fff0;
590
+ if (oor == 0) {
591
+ const d0 = <u32>(digits & 0xffff);
592
+ const d1 = <u32>((digits >> 16) & 0xffff);
593
+ const d2 = <u32>((digits >> 32) & 0xffff);
594
+ store<valueof<T>>(writePtr, <valueof<T>>(d0 * 100 + d1 * 10 + d2));
595
+ writePtr += elementSize;
596
+ srcStart += 8;
597
+ continue;
598
+ }
599
+ } else if (((block >> 32) & 0xffff) == COMMA) {
600
+ const digits = (block & 0x0000_0000_00ff_00ff) - 0x0000_0000_0030_0030;
601
+ const oor =
602
+ (digits | (digits + 0x0000_0000_0006_0006)) & 0x0000_0000_fff0_fff0;
603
+ if (oor == 0) {
604
+ const d0 = <u32>(digits & 0xffff);
605
+ const d1 = <u32>((digits >> 16) & 0xffff);
606
+ store<valueof<T>>(writePtr, <valueof<T>>(d0 * 10 + d1));
607
+ writePtr += elementSize;
608
+ srcStart += 6;
609
+ continue;
610
+ }
611
+ } else if (((block >> 16) & 0xffff) == COMMA) {
612
+ const d0 = <u32>(block & 0xffff) - 48;
613
+ if (d0 <= 9) {
614
+ store<valueof<T>>(writePtr, <valueof<T>>d0);
615
+ writePtr += elementSize;
616
+ srcStart += 4;
617
+ continue;
618
+ }
619
+ }
620
+ }
621
+ const code = load<u16>(srcStart);
622
+ if (<u32>code - 48 <= 9 || (isSigned<valueof<T>>() && code == 45)) {
623
+ const lastIndex = srcStart;
624
+ srcStart += 2;
625
+ while (srcStart < srcEnd) {
626
+ const c = load<u16>(srcStart);
627
+ if (c == COMMA || c == BRACKET_RIGHT || isSpace(c)) {
628
+ let value: u64 = 0;
629
+ let p = lastIndex;
630
+ if (isSigned<valueof<T>>() && load<u16>(p) == 45) {
631
+ p += 2;
632
+ while (p < srcStart) {
633
+ value = value * 10 + (<u32>load<u16>(p) - 48);
634
+ p += 2;
635
+ }
636
+ store<valueof<T>>(writePtr, <valueof<T>>-(<i64>value));
637
+ } else {
638
+ while (p < srcStart) {
639
+ value = value * 10 + (<u32>load<u16>(p) - 48);
640
+ p += 2;
641
+ }
642
+ store<valueof<T>>(writePtr, <valueof<T>>value);
643
+ }
644
+ writePtr += elementSize;
645
+ break;
646
+ }
647
+ srcStart += 2;
648
+ }
649
+ }
650
+ srcStart += 2;
651
+ }
652
+
653
+ out.length = i32(<usize>(writePtr - dataStart) / elementSize);
654
+ return out;
655
+ }
656
+
427
657
 
428
- @inline export function deserializeIntegerArrayInto<T extends number[]>(srcStart: usize, srcEnd: usize, out: T): usize {
658
+ @inline export function deserializeIntegerArrayInto<T extends number[]>(
659
+ srcStart: usize,
660
+ srcEnd: usize,
661
+ out: T,
662
+ ): usize {
429
663
  let index = 0;
430
664
 
431
665
  do {
@@ -439,7 +673,9 @@ export function deserializeIntegerArray_SWAR<T extends number[]>(srcStart: usize
439
673
 
440
674
  while (srcStart < srcEnd) {
441
675
  const slot = ensureArrayElementSlot<T>(out, index);
442
- srcStart = isSigned<valueof<T>>() ? deserializeIntegerField<valueof<T>>(srcStart, srcEnd, slot) : deserializeUnsignedField<valueof<T>>(srcStart, srcEnd, slot);
676
+ srcStart = isSigned<valueof<T>>()
677
+ ? deserializeIntegerField<valueof<T>>(srcStart, srcEnd, slot)
678
+ : deserializeUnsignedField<valueof<T>>(srcStart, srcEnd, slot);
443
679
  if (!srcStart || srcStart >= srcEnd) break;
444
680
 
445
681
  const code = load<u16>(srcStart);
@@ -460,6 +696,14 @@ export function deserializeIntegerArray_SWAR<T extends number[]>(srcStart: usize
460
696
  }
461
697
 
462
698
 
463
- @inline export function deserializeIntegerArrayField<T extends number[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
464
- return deserializeIntegerArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
699
+ @inline export function deserializeIntegerArrayField<T extends number[]>(
700
+ srcStart: usize,
701
+ srcEnd: usize,
702
+ fieldPtr: usize,
703
+ ): usize {
704
+ return deserializeIntegerArrayInto<T>(
705
+ srcStart,
706
+ srcEnd,
707
+ ensureArrayField<T>(fieldPtr),
708
+ );
465
709
  }
@@ -1,7 +1,11 @@
1
1
  import { ensureArrayField } from "./shared";
2
2
 
3
3
 
4
- @inline export function deserializeMapArrayField<T extends Map<any, any>[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
4
+ @inline export function deserializeMapArrayField<T extends Map<any, any>[]>(
5
+ srcStart: usize,
6
+ srcEnd: usize,
7
+ fieldPtr: usize,
8
+ ): usize {
5
9
  ensureArrayField<T>(fieldPtr);
6
10
  throw new Error("Failed to parse JSON!");
7
11
  }
@@ -2,7 +2,11 @@ import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
2
2
  import { ensureArrayElementSlot, ensureArrayField } from "./shared";
3
3
 
4
4
 
5
- @inline export function deserializeObjectArrayInto<T extends unknown[]>(srcStart: usize, srcEnd: usize, out: T): usize {
5
+ @inline export function deserializeObjectArrayInto<T extends unknown[]>(
6
+ srcStart: usize,
7
+ srcEnd: usize,
8
+ out: T,
9
+ ): usize {
6
10
  let index = 0;
7
11
 
8
12
  do {
@@ -18,7 +22,9 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
18
22
  const slot = ensureArrayElementSlot<T>(out, index);
19
23
  let value = load<valueof<T>>(slot);
20
24
  if (changetype<usize>(value) == 0) {
21
- value = changetype<valueof<T>>(__new(offsetof<nonnull<valueof<T>>>(), idof<nonnull<valueof<T>>>()));
25
+ value = changetype<valueof<T>>(
26
+ __new(offsetof<nonnull<valueof<T>>>(), idof<nonnull<valueof<T>>>()),
27
+ );
22
28
  // @ts-ignore: supplied by transform
23
29
  if (isDefined(changetype<nonnull<valueof<T>>>(value).__INITIALIZE)) {
24
30
  // @ts-ignore: supplied by transform
@@ -29,12 +35,18 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
29
35
 
30
36
  const valueStart = srcStart;
31
37
  // @ts-ignore: supplied by transform
32
- if (isDefined(changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST)) {
38
+ if (
39
+ isDefined(changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST)
40
+ ) {
33
41
  // @ts-ignore: supplied by transform
34
- srcStart = changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST<valueof<T>>(valueStart, srcEnd, value);
42
+ srcStart = changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST<
43
+ valueof<T>
44
+ >(valueStart, srcEnd, value);
35
45
  } else {
36
46
  // @ts-ignore: supplied by transform
37
- srcStart = changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_SLOW<valueof<T>>(valueStart, srcEnd, value);
47
+ srcStart = changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_SLOW<
48
+ valueof<T>
49
+ >(valueStart, srcEnd, value);
38
50
  }
39
51
  if (!srcStart || srcStart >= srcEnd) break;
40
52
 
@@ -56,6 +68,14 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
56
68
  }
57
69
 
58
70
 
59
- @inline export function deserializeObjectArrayField<T extends unknown[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
60
- return deserializeObjectArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
71
+ @inline export function deserializeObjectArrayField<T extends unknown[]>(
72
+ srcStart: usize,
73
+ srcEnd: usize,
74
+ fieldPtr: usize,
75
+ ): usize {
76
+ return deserializeObjectArrayInto<T>(
77
+ srcStart,
78
+ srcEnd,
79
+ ensureArrayField<T>(fieldPtr),
80
+ );
61
81
  }
@@ -2,7 +2,11 @@ import { JSON } from "../../..";
2
2
  import { ensureArrayField } from "./shared";
3
3
 
4
4
 
5
- @inline export function deserializeRawArrayField(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
5
+ @inline export function deserializeRawArrayField(
6
+ srcStart: usize,
7
+ srcEnd: usize,
8
+ fieldPtr: usize,
9
+ ): usize {
6
10
  ensureArrayField<JSON.Raw[]>(fieldPtr);
7
11
  throw new Error("Failed to parse JSON!");
8
12
  }