json-as 1.5.0 → 1.6.1

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 (66) hide show
  1. package/CHANGELOG.md +10 -1
  2. package/README.md +42 -0
  3. package/assembly/deserialize/error.ts +21 -0
  4. package/assembly/deserialize/index/arbitrary.ts +29 -8
  5. package/assembly/deserialize/index/array.ts +2 -1
  6. package/assembly/deserialize/index/bool.ts +1 -1
  7. package/assembly/deserialize/index/object.ts +3 -0
  8. package/assembly/deserialize/index/string.ts +37 -0
  9. package/assembly/deserialize/index.ts +0 -1
  10. package/assembly/deserialize/naive/array/arbitrary.ts +3 -2
  11. package/assembly/deserialize/naive/array/array.ts +1 -0
  12. package/assembly/deserialize/naive/array/bool.ts +21 -11
  13. package/assembly/deserialize/naive/array/box.ts +13 -33
  14. package/assembly/deserialize/naive/array/float.ts +23 -13
  15. package/assembly/deserialize/naive/array/integer.ts +33 -21
  16. package/assembly/deserialize/naive/array/object.ts +1 -0
  17. package/assembly/deserialize/naive/array/string.ts +29 -16
  18. package/assembly/deserialize/naive/bool.ts +17 -5
  19. package/assembly/deserialize/naive/float.ts +1 -2
  20. package/assembly/deserialize/naive/map.ts +339 -39
  21. package/assembly/deserialize/naive/object.ts +122 -65
  22. package/assembly/deserialize/naive/raw.ts +13 -1
  23. package/assembly/deserialize/naive/set.ts +0 -1
  24. package/assembly/deserialize/naive/staticarray.ts +3 -2
  25. package/assembly/deserialize/naive/string.ts +41 -52
  26. package/assembly/deserialize/parseMode.ts +216 -0
  27. package/assembly/deserialize/simd/array/integer.ts +6 -3
  28. package/assembly/deserialize/simd/float.ts +35 -29
  29. package/assembly/deserialize/simd/string.ts +79 -17
  30. package/assembly/deserialize/string-validation.ts +34 -0
  31. package/assembly/deserialize/swar/array/array.ts +63 -13
  32. package/assembly/deserialize/swar/array/bool.ts +14 -3
  33. package/assembly/deserialize/swar/array/float.ts +394 -13
  34. package/assembly/deserialize/swar/array/generic.ts +12 -2
  35. package/assembly/deserialize/swar/array/integer.ts +22 -62
  36. package/assembly/deserialize/swar/array/object.ts +11 -2
  37. package/assembly/deserialize/swar/array/shared.ts +1 -1
  38. package/assembly/deserialize/swar/array/string.ts +34 -5
  39. package/assembly/deserialize/swar/array/struct.ts +109 -15
  40. package/assembly/deserialize/swar/float.ts +19 -12
  41. package/assembly/deserialize/swar/string.ts +70 -111
  42. package/assembly/index.d.ts +6 -0
  43. package/assembly/index.ts +411 -33
  44. package/assembly/serialize/index/bool.ts +1 -1
  45. package/assembly/serialize/index/float.ts +1 -5
  46. package/assembly/serialize/index/integer.ts +1 -1
  47. package/assembly/serialize/index/object.ts +17 -0
  48. package/assembly/serialize/naive/array.ts +64 -0
  49. package/assembly/serialize/naive/float.ts +0 -5
  50. package/assembly/serialize/naive/map.ts +37 -0
  51. package/assembly/serialize/naive/set.ts +0 -1
  52. package/assembly/serialize/naive/staticarray.ts +0 -1
  53. package/assembly/serialize/naive/string.ts +0 -6
  54. package/assembly/serialize/naive/typedarray.ts +0 -1
  55. package/assembly/util/eisel-lemire.ts +179 -0
  56. package/assembly/util/prettyWhitespaceSimd.ts +69 -0
  57. package/assembly/util/scanValueEndSimd.ts +59 -36
  58. package/assembly/util/validateJson.ts +338 -0
  59. package/lib/as-bs.ts +28 -6
  60. package/package.json +17 -7
  61. package/transform/lib/index.d.ts +6 -0
  62. package/transform/lib/index.js +773 -112
  63. package/transform/lib/types.d.ts +1 -0
  64. package/transform/lib/types.js +3 -0
  65. package/assembly/deserialize/index/struct.ts +0 -1
  66. package/assembly/deserialize/naive/struct.ts +0 -21
@@ -20,12 +20,22 @@ export function deserializeGenericArrayBody<T extends unknown[]>(
20
20
  if (load<u16>(srcStart) == BRACKET_RIGHT) return srcStart + 2;
21
21
 
22
22
  let index = 0;
23
+ const reusableLength = load<i32>(
24
+ changetype<usize>(out),
25
+ offsetof<T>("length_"),
26
+ );
27
+ const reusableDataStart = out.dataStart;
28
+ const elementSize = sizeof<valueof<T>>();
23
29
 
24
30
  while (srcStart < srcEnd) {
25
31
  const valueEnd = scanValueEnd(srcStart, srcEnd);
26
32
  if (!valueEnd || valueEnd <= srcStart) break;
27
33
 
28
- const slot = ensureArrayElementSlot<T>(out, index++);
34
+ const slot =
35
+ index < reusableLength
36
+ ? reusableDataStart + <usize>index * elementSize
37
+ : ensureArrayElementSlot<T>(out, index);
38
+ index++;
29
39
  store<valueof<T>>(slot, JSON.__deserialize<valueof<T>>(srcStart, valueEnd));
30
40
  srcStart = valueEnd;
31
41
 
@@ -38,7 +48,7 @@ export function deserializeGenericArrayBody<T extends unknown[]>(
38
48
  continue;
39
49
  }
40
50
  if (code == BRACKET_RIGHT) {
41
- out.length = index;
51
+ if (reusableLength != index) out.length = index;
42
52
  return srcStart + 2;
43
53
  }
44
54
  break;
@@ -1,6 +1,7 @@
1
1
  import { deserializeIntegerArray_NAIVE } from "../../naive/array/integer";
2
2
  import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
3
3
  import { isSpace } from "../../../util";
4
+ import { markProductionParseError } from "../../error";
4
5
  import { ensureArrayElementSlot, ensureArrayField } from "./shared";
5
6
  import { parse4Digits_PairMul } from "../../../util/swar-int";
6
7
 
@@ -40,7 +41,7 @@ function storeUnsignedIntegerE<E extends number>(
40
41
  }
41
42
  }
42
43
 
43
- // The four parse helpers below take a `slot` pointer (`writePtr`) and store
44
+ // The parse helpers below take a `slot` pointer (`writePtr`) and store
44
45
  // the value directly via `store<valueof<T>>(slot, ...)`. The outer dispatcher
45
46
  // owns the array's `out.length = maxElements` pre-allocation and the
46
47
  // `writePtr` advance, so the per-element `Array.push` capacity check and
@@ -49,57 +50,6 @@ function storeUnsignedIntegerE<E extends number>(
49
50
  // Parsers are also E-parameterised so they're shareable with
50
51
  // `swar/typedarray.ts`. The body is byte-identical to the prior version
51
52
  // modulo s/valueof<T>/E/.
52
- export function parseSignedIntegerScalar<E extends number>(
53
- srcStart: usize,
54
- srcEnd: usize,
55
- slot: usize,
56
- ): usize {
57
- let negative = false;
58
- let code = load<u16>(srcStart);
59
- if (code == 45) {
60
- negative = true;
61
- srcStart += 2;
62
- if (srcStart >= srcEnd) return 0;
63
- code = load<u16>(srcStart);
64
- }
65
-
66
- let digit = <u32>code - 48;
67
- if (digit > 9) return 0;
68
-
69
- let value: u64 = digit;
70
- srcStart += 2;
71
- while (srcStart < srcEnd) {
72
- digit = <u32>load<u16>(srcStart) - 48;
73
- if (digit > 9) break;
74
- value = value * 10 + digit;
75
- srcStart += 2;
76
- }
77
-
78
- storeSignedIntegerE<E>(slot, negative ? -(<i64>value) : <i64>value);
79
- return srcStart;
80
- }
81
-
82
- export function parseUnsignedIntegerScalar<E extends number>(
83
- srcStart: usize,
84
- srcEnd: usize,
85
- slot: usize,
86
- ): usize {
87
- let digit = <u32>load<u16>(srcStart) - 48;
88
- if (digit > 9) return 0;
89
-
90
- let value: u64 = digit;
91
- srcStart += 2;
92
- while (srcStart < srcEnd) {
93
- digit = <u32>load<u16>(srcStart) - 48;
94
- if (digit > 9) break;
95
- value = value * 10 + digit;
96
- srcStart += 2;
97
- }
98
-
99
- storeUnsignedIntegerE<E>(slot, value);
100
- return srcStart;
101
- }
102
-
103
53
  export function parseSignedIntegerSWAR<E extends number>(
104
54
  srcStart: usize,
105
55
  srcEnd: usize,
@@ -227,7 +177,8 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(
227
177
 
228
178
  out.length = 0;
229
179
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) {
230
- throw new Error("Failed to parse JSON!");
180
+ markProductionParseError();
181
+ return changetype<T>(0);
231
182
  }
232
183
 
233
184
  srcStart += 2;
@@ -295,7 +246,8 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(
295
246
  break;
296
247
  }
297
248
 
298
- throw new Error("Failed to parse JSON!");
249
+ markProductionParseError();
250
+ return changetype<T>(0);
299
251
  }
300
252
 
301
253
  function deserializeIntegerArrayImpl<T extends number[]>(
@@ -308,7 +260,10 @@ function deserializeIntegerArrayImpl<T extends number[]>(
308
260
  dst || changetype<usize>(instantiate<T>()),
309
261
  );
310
262
  const originalSrcStart = srcStart;
311
- const reusableLength = out.length;
263
+ const reusableLength = load<i32>(
264
+ changetype<usize>(out),
265
+ offsetof<T>("length_"),
266
+ );
312
267
 
313
268
  if (useSWAR && reusableLength != 0) {
314
269
  const dataStart = out.dataStart;
@@ -448,9 +403,11 @@ function deserializeIntegerArrayImpl<T extends number[]>(
448
403
 
449
404
  if (isSigned<valueof<T>>()) {
450
405
  while (srcStart < srcEnd) {
451
- const next = useSWAR
452
- ? parseSignedIntegerSWAR<valueof<T>>(srcStart, srcEnd, writePtr)
453
- : parseSignedIntegerScalar<valueof<T>>(srcStart, srcEnd, writePtr);
406
+ const next = parseSignedIntegerSWAR<valueof<T>>(
407
+ srcStart,
408
+ srcEnd,
409
+ writePtr,
410
+ );
454
411
  if (!next) break;
455
412
  writePtr += elementSize;
456
413
  srcStart = next;
@@ -469,9 +426,11 @@ function deserializeIntegerArrayImpl<T extends number[]>(
469
426
  }
470
427
  } else {
471
428
  while (srcStart < srcEnd) {
472
- const next = useSWAR
473
- ? parseUnsignedIntegerSWAR<valueof<T>>(srcStart, srcEnd, writePtr)
474
- : parseUnsignedIntegerScalar<valueof<T>>(srcStart, srcEnd, writePtr);
429
+ const next = parseUnsignedIntegerSWAR<valueof<T>>(
430
+ srcStart,
431
+ srcEnd,
432
+ writePtr,
433
+ );
475
434
  if (!next) break;
476
435
  writePtr += elementSize;
477
436
  srcStart = next;
@@ -686,7 +645,8 @@ function deserializeIntegerArrayBody<T extends number[]>(
686
645
  }
687
646
  } while (false);
688
647
 
689
- throw new Error("Failed to parse JSON!");
648
+ markProductionParseError();
649
+ return 0;
690
650
  }
691
651
 
692
652
  export function deserializeIntegerArrayField<T extends number[]>(
@@ -13,6 +13,12 @@ function deserializeObjectArrayBody<T extends unknown[]>(
13
13
  out: T,
14
14
  ): usize {
15
15
  let index = 0;
16
+ const reusableLength = load<i32>(
17
+ changetype<usize>(out),
18
+ offsetof<T>("length_"),
19
+ );
20
+ const reusableDataStart = out.dataStart;
21
+ const elementSize = sizeof<valueof<T>>();
16
22
 
17
23
  do {
18
24
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
@@ -25,7 +31,10 @@ function deserializeObjectArrayBody<T extends unknown[]>(
25
31
  }
26
32
 
27
33
  while (srcStart < srcEnd) {
28
- const slot = ensureArrayElementSlot<T>(out, index);
34
+ const slot =
35
+ index < reusableLength
36
+ ? reusableDataStart + <usize>index * elementSize
37
+ : ensureArrayElementSlot<T>(out, index);
29
38
  const valueStart = srcStart;
30
39
  const valueEnd = scanValueEnd(valueStart, srcEnd);
31
40
  if (!valueEnd) break;
@@ -48,7 +57,7 @@ function deserializeObjectArrayBody<T extends unknown[]>(
48
57
  }
49
58
  if (code == BRACKET_RIGHT) {
50
59
  const nextLen = index + 1;
51
- if (out.length != nextLen) out.length = nextLen;
60
+ if (reusableLength != nextLen) out.length = nextLen;
52
61
  return srcStart + 2;
53
62
  }
54
63
  break;
@@ -72,7 +72,7 @@ export function ensureArrayElementSlot<T extends Array<any>>(
72
72
 
73
73
  export function scanQuotedValueEnd_SWAR(srcStart: usize, srcEnd: usize): usize {
74
74
  srcStart += 2;
75
- const srcEnd8 = srcEnd >= 8 ? srcEnd - 8 : 0;
75
+ const srcEnd8 = srcEnd - 8;
76
76
 
77
77
  while (srcStart <= srcEnd8) {
78
78
  let mask = backslashOrQuoteMask(load<u64>(srcStart));
@@ -7,6 +7,7 @@ import {
7
7
  import { isSpace } from "../../../util";
8
8
  import { ensureArrayElementSlot, ensureArrayField } from "./shared";
9
9
  import { deserializeStringField_SWAR } from "../string";
10
+ import { markProductionParseError } from "../../error";
10
11
 
11
12
  function skipStringArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
12
13
  while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
@@ -19,6 +20,15 @@ function deserializeStringArrayBody<T extends string[]>(
19
20
  out: T,
20
21
  ): usize {
21
22
  let index = 0;
23
+ // Reused struct fields normally retain their array backing store. Cache the
24
+ // original slot range so the common path avoids an Array.length load and
25
+ // growth branch for every string.
26
+ const reusableLength = load<i32>(
27
+ changetype<usize>(out),
28
+ offsetof<T>("length_"),
29
+ );
30
+ const reusableDataStart = out.dataStart;
31
+ const elementSize = sizeof<valueof<T>>();
22
32
 
23
33
  do {
24
34
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
@@ -31,7 +41,10 @@ function deserializeStringArrayBody<T extends string[]>(
31
41
  }
32
42
 
33
43
  while (srcStart < srcEnd) {
34
- const slot = ensureArrayElementSlot<T>(out, index);
44
+ const slot =
45
+ index < reusableLength
46
+ ? reusableDataStart + <usize>index * elementSize
47
+ : ensureArrayElementSlot<T>(out, index);
35
48
  // Null fast path for `(string | null)[]`. "null" is 4 UTF-16 chars
36
49
  // = 8 bytes, exactly one u64 compare. Store 0 (the AS null reference)
37
50
  // and skip 8 bytes.
@@ -66,14 +79,15 @@ function deserializeStringArrayBody<T extends string[]>(
66
79
  }
67
80
  if (code == BRACKET_RIGHT) {
68
81
  const nextLen = index + 1;
69
- if (out.length != nextLen) out.length = nextLen;
82
+ if (reusableLength != nextLen) out.length = nextLen;
70
83
  return srcStart + 2;
71
84
  }
72
85
  break;
73
86
  }
74
87
  } while (false);
75
88
 
76
- throw new Error("Failed to parse JSON!");
89
+ markProductionParseError();
90
+ return 0;
77
91
  }
78
92
 
79
93
  export function deserializeStringArrayField<T extends string[]>(
@@ -106,6 +120,11 @@ export function deserializeStringArray_SWAR<T extends string[]>(
106
120
  dst || changetype<usize>(instantiate<T>()),
107
121
  );
108
122
 
123
+ // Top-level exact-range entry. Field arrays use the cursor-returning body
124
+ // above, so trimming here preserves valid document whitespace without
125
+ // touching the hot generated-struct path.
126
+ while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
127
+
109
128
  // Worst-case sizing: shortest possible element is `""` followed by `,` =
110
129
  // 6 UTF-16 bytes, so `(srcLen + 5) / 6` upper-bounds the count. Clamp to
111
130
  // AS's BLOCK_MAXSIZE / sizeof<string ref> = (1<<28) - 4 elements; payloads
@@ -126,11 +145,16 @@ export function deserializeStringArray_SWAR<T extends string[]>(
126
145
  // Caller guarantees srcStart is at the opening `[`.
127
146
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) {
128
147
  out.length = 0;
129
- return out;
148
+ markProductionParseError();
149
+ return changetype<T>(0);
130
150
  }
131
151
  srcStart += 2;
132
152
  srcStart = skipStringArrayWhitespace(srcStart, srcEnd);
133
153
  if (srcStart < srcEnd && load<u16>(srcStart) == BRACKET_RIGHT) {
154
+ if (srcStart + 2 != srcEnd) {
155
+ markProductionParseError();
156
+ return changetype<T>(0);
157
+ }
134
158
  out.length = 0;
135
159
  return out;
136
160
  }
@@ -159,6 +183,10 @@ export function deserializeStringArray_SWAR<T extends string[]>(
159
183
  continue;
160
184
  }
161
185
  if (code == BRACKET_RIGHT) {
186
+ if (srcStart + 2 != srcEnd) {
187
+ markProductionParseError();
188
+ return changetype<T>(0);
189
+ }
162
190
  const finalLen = i32(<usize>(writePtr - dataStart) / elementSize);
163
191
  if (out.length != finalLen) out.length = finalLen;
164
192
  return out;
@@ -166,5 +194,6 @@ export function deserializeStringArray_SWAR<T extends string[]>(
166
194
  break;
167
195
  }
168
196
 
169
- throw new Error("Failed to parse JSON!");
197
+ markProductionParseError();
198
+ return changetype<T>(0);
170
199
  }
@@ -1,8 +1,33 @@
1
- import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
1
+ import {
2
+ BRACE_LEFT,
3
+ BRACKET_LEFT,
4
+ BRACKET_RIGHT,
5
+ COMMA,
6
+ } from "../../../custom/chars";
2
7
  import { isSpace } from "../../../util";
3
- import { ensureArrayElementSlot, ensureArrayField } from "./shared";
8
+ import {
9
+ ensureArrayElementSlot,
10
+ ensureArrayField,
11
+ scanValueEnd,
12
+ } from "./shared";
13
+ import { markProductionParseError } from "../../error";
4
14
 
15
+
16
+ @inline
5
17
  function skipStructArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
18
+ // JSON.stringify(..., null, 2) at a root struct-array boundary emits exactly
19
+ // LF + two spaces + the next object (or LF + closing bracket). Keep that
20
+ // overwhelmingly common shape to two scalar loads; nested/foreign layouts
21
+ // retain the fully general fallback below.
22
+ if (
23
+ ASC_FEATURE_SIMD &&
24
+ srcStart + 8 <= srcEnd &&
25
+ load<u16>(srcStart) == 10 &&
26
+ load<u32>(srcStart, 2) == 0x0020_0020
27
+ ) {
28
+ const next = load<u16>(srcStart, 6);
29
+ if (next == BRACE_LEFT || next == BRACKET_RIGHT) return srcStart + 6;
30
+ }
6
31
  while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
7
32
  return srcStart;
8
33
  }
@@ -28,11 +53,22 @@ function deserializeStructArrayBody<T extends unknown[]>(
28
53
  out: T,
29
54
  ): usize {
30
55
  let index = 0;
56
+ const reusableLength = load<i32>(
57
+ changetype<usize>(out),
58
+ offsetof<T>("length_"),
59
+ );
60
+ const reusableDataStart = out.dataStart;
61
+ const elementSize = sizeof<valueof<T>>();
62
+ let pretty = false;
31
63
 
32
64
  do {
33
65
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
34
66
  srcStart += 2;
35
- srcStart = skipStructArrayWhitespace(srcStart, srcEnd);
67
+ if (srcStart < srcEnd && load<u16>(srcStart) != BRACE_LEFT) {
68
+ const code = load<u16>(srcStart);
69
+ pretty = ASC_FEATURE_SIMD && (code == 10 || code == 13);
70
+ srcStart = skipStructArrayWhitespace(srcStart, srcEnd);
71
+ }
36
72
  if (srcStart >= srcEnd) break;
37
73
  if (load<u16>(srcStart) == BRACKET_RIGHT) {
38
74
  out.length = 0;
@@ -40,7 +76,10 @@ function deserializeStructArrayBody<T extends unknown[]>(
40
76
  }
41
77
 
42
78
  while (srcStart < srcEnd) {
43
- const slot = ensureArrayElementSlot<T>(out, index);
79
+ const slot =
80
+ index < reusableLength
81
+ ? reusableDataStart + <usize>index * elementSize
82
+ : ensureArrayElementSlot<T>(out, index);
44
83
  let value = load<valueof<T>>(slot);
45
84
  if (changetype<usize>(value) == 0) {
46
85
  value = changetype<valueof<T>>(
@@ -54,31 +93,70 @@ function deserializeStructArrayBody<T extends unknown[]>(
54
93
  store<valueof<T>>(slot, value);
55
94
  }
56
95
 
96
+ const valueStart = srcStart;
57
97
  let next: usize = 0;
58
98
  if (
59
99
  // @ts-ignore: supplied by transform
60
100
  isDefined(changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST)
61
101
  ) {
102
+ // Omit the explicit type arg so AS infers it from `value` — passing
103
+ // `<valueof<T>>` explicitly collides when the element type is itself
104
+ // generic (e.g. GenericTest<string>) because AS resolves `T` against
105
+ // the element class's own generic rather than this function's `T`.
62
106
  // @ts-ignore: supplied by transform
63
- next = changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST<
64
- valueof<T>
65
- >(srcStart, srcEnd, value);
107
+ // @ts-ignore: supplied by transform in SIMD builds
108
+ if (
109
+ pretty &&
110
+ isDefined(
111
+ changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST_PRETTY,
112
+ )
113
+ ) {
114
+ // @ts-ignore: supplied by transform
115
+ next = changetype<nonnull<valueof<T>>>(
116
+ value,
117
+ ).__DESERIALIZE_FAST_PRETTY(srcStart, srcEnd, value);
118
+ } else {
119
+ next = changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST(
120
+ srcStart,
121
+ srcEnd,
122
+ value,
123
+ );
124
+ }
66
125
  }
67
126
  if (!next) {
127
+ // __DESERIALIZE_SLOW requires srcEnd to point just past the closing
128
+ // `}` of *this* element (it asserts srcEnd-2 == `}`). Scan the value
129
+ // boundary first, then re-initialize the object so any partial state
130
+ // from the failed FAST attempt is cleared.
131
+ const valueEnd = scanValueEnd(valueStart, srcEnd);
132
+ if (!valueEnd) break;
133
+ // @ts-ignore: supplied by transform
134
+ if (isDefined(changetype<nonnull<valueof<T>>>(value).__INITIALIZE)) {
135
+ // @ts-ignore: supplied by transform
136
+ changetype<nonnull<valueof<T>>>(value).__INITIALIZE();
137
+ }
68
138
  // @ts-ignore: supplied by transform
69
- next = changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_SLOW<
70
- valueof<T>
71
- >(srcStart, srcEnd, value);
139
+ changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_SLOW(
140
+ valueStart,
141
+ valueEnd,
142
+ value,
143
+ );
144
+ next = valueEnd;
72
145
  }
73
146
  if (!next) break;
74
147
  srcStart = next;
75
- srcStart = skipStructArrayWhitespace(srcStart, srcEnd);
76
148
  if (srcStart >= srcEnd) break;
77
149
 
78
- const code = load<u16>(srcStart);
150
+ let code = load<u16>(srcStart);
151
+ if (code != COMMA && code != BRACKET_RIGHT) {
152
+ srcStart = skipStructArrayWhitespace(srcStart, srcEnd);
153
+ if (srcStart >= srcEnd) break;
154
+ code = load<u16>(srcStart);
155
+ }
79
156
  if (code == COMMA) {
80
157
  srcStart += 2;
81
- srcStart = skipStructArrayWhitespace(srcStart, srcEnd);
158
+ if (srcStart < srcEnd && load<u16>(srcStart) != BRACE_LEFT)
159
+ srcStart = skipStructArrayWhitespace(srcStart, srcEnd);
82
160
  index++;
83
161
  continue;
84
162
  }
@@ -87,15 +165,31 @@ function deserializeStructArrayBody<T extends unknown[]>(
87
165
  // right length (e.g. canada-style geometry rings whose count
88
166
  // matches a previous parse).
89
167
  const nextLen = index + 1;
90
- if (out.length != nextLen) out.length = nextLen;
168
+ if (reusableLength != nextLen) out.length = nextLen;
91
169
  return srcStart + 2;
92
170
  }
93
171
  break;
94
172
  }
95
173
  } while (false);
96
174
 
97
- throw new Error("Failed to parse JSON!");
175
+ markProductionParseError();
176
+ return 0;
98
177
  }
178
+ export function deserializeStructArray_SWAR<T extends unknown[]>(
179
+ srcStart: usize,
180
+ srcEnd: usize,
181
+ dst: usize,
182
+ ): T {
183
+ while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
184
+ const out = changetype<nonnull<T>>(
185
+ dst || changetype<usize>(instantiate<T>()),
186
+ );
187
+ const end = deserializeStructArrayBody<T>(srcStart, srcEnd, out);
188
+ if (end == srcEnd) return out;
189
+ markProductionParseError();
190
+ return changetype<T>(0);
191
+ }
192
+
99
193
  export function deserializeStructArrayField<T extends unknown[]>(
100
194
  srcStart: usize,
101
195
  srcEnd: usize,
@@ -19,6 +19,7 @@
19
19
  import { ptrToStr } from "../../util/ptrToStr";
20
20
  import { parse4Digits_PairMul } from "../../util/swar-int";
21
21
  import { scientific } from "../../util/scientific";
22
+ import { eiselLemire22 } from "../../util/eisel-lemire";
22
23
 
23
24
  export const POW10_F64_POS: usize = memory.data<f64>([
24
25
  1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14,
@@ -72,37 +73,37 @@ export function deserializeFloat_SWAR<T>(srcStart: usize, srcEnd: usize): T {
72
73
  // Integer part: scalar. Most JSON integers are 1-3 digits, so parse4 would
73
74
  // waste a validate per call.
74
75
  let mantissa: u64 = 0;
75
- let intDigits: i32 = 0;
76
+ const intStart = p;
76
77
  while (p < srcEnd) {
77
78
  const d = <u32>load<u16>(p) - ASCII_ZERO;
78
79
  if (d > 9) break;
79
80
  mantissa = mantissa * 10 + <u64>d;
80
- intDigits++;
81
81
  p += 2;
82
82
  }
83
+ const intDigits = <i32>((p - intStart) >> 1);
83
84
 
84
85
  // Fractional part: parse4 stride (8 bytes / 4 digits) → scalar tail.
85
86
  // parse8 was tried and benchmarked even/worse; the saved mantissa mul
86
87
  // didn't outweigh the extra load and combined-validation latency, and the
87
88
  // dependency chain is the same length either way.
88
- let fracDigits: i32 = 0;
89
+ let fracStart = p;
89
90
  if (p < srcEnd && load<u16>(p) == ASCII_DOT) {
90
91
  p += 2;
92
+ fracStart = p;
91
93
  while (p + 6 < srcEnd) {
92
94
  const parsed = parse4Digits_PairMul(load<u64>(p));
93
95
  if (parsed == U32.MAX_VALUE) break;
94
96
  mantissa = mantissa * 10_000 + <u64>parsed;
95
- fracDigits += 4;
96
97
  p += 8;
97
98
  }
98
99
  while (p < srcEnd) {
99
100
  const d = <u32>load<u16>(p) - ASCII_ZERO;
100
101
  if (d > 9) break;
101
102
  mantissa = mantissa * 10 + <u64>d;
102
- fracDigits++;
103
103
  p += 2;
104
104
  }
105
105
  }
106
+ const fracDigits = <i32>((p - fracStart) >> 1);
106
107
 
107
108
  const mantDigits = intDigits + fracDigits;
108
109
  if (mantDigits == 0) return fallback<T>(origStart, srcEnd);
@@ -158,7 +159,10 @@ export function deserializeFloat_SWAR<T>(srcStart: usize, srcEnd: usize): T {
158
159
  result /= loadPow10(<u32>-exponent);
159
160
  }
160
161
  } else if (mantDigits <= 19) {
161
- result = scientific(mantissa, exponent);
162
+ result =
163
+ exponent >= -22 && exponent <= 22
164
+ ? eiselLemire22(mantissa, exponent)
165
+ : scientific(mantissa, exponent);
162
166
  } else {
163
167
  return fallback<T>(origStart, srcEnd);
164
168
  }
@@ -188,33 +192,33 @@ export function deserializeFloatField_SWAR<T extends number>(
188
192
  }
189
193
 
190
194
  let mantissa: u64 = 0;
191
- let intDigits: i32 = 0;
195
+ const intStart = p;
192
196
  while (p < srcEnd) {
193
197
  const d = <u32>load<u16>(p) - ASCII_ZERO;
194
198
  if (d > 9) break;
195
199
  mantissa = mantissa * 10 + <u64>d;
196
- intDigits++;
197
200
  p += 2;
198
201
  }
202
+ const intDigits = <i32>((p - intStart) >> 1);
199
203
 
200
- let fracDigits: i32 = 0;
204
+ let fracStart = p;
201
205
  if (p < srcEnd && load<u16>(p) == ASCII_DOT) {
202
206
  p += 2;
207
+ fracStart = p;
203
208
  while (p + 6 < srcEnd) {
204
209
  const parsed = parse4Digits_PairMul(load<u64>(p));
205
210
  if (parsed == U32.MAX_VALUE) break;
206
211
  mantissa = mantissa * 10_000 + <u64>parsed;
207
- fracDigits += 4;
208
212
  p += 8;
209
213
  }
210
214
  while (p < srcEnd) {
211
215
  const d = <u32>load<u16>(p) - ASCII_ZERO;
212
216
  if (d > 9) break;
213
217
  mantissa = mantissa * 10 + <u64>d;
214
- fracDigits++;
215
218
  p += 2;
216
219
  }
217
220
  }
221
+ const fracDigits = <i32>((p - fracStart) >> 1);
218
222
 
219
223
  const mantDigits = intDigits + fracDigits;
220
224
  if (mantDigits == 0) unreachable();
@@ -277,7 +281,10 @@ export function deserializeFloatField_SWAR<T extends number>(
277
281
  result /= loadPow10(<u32>-exponent);
278
282
  }
279
283
  } else if (mantDigits <= 19) {
280
- result = scientific(mantissa, exponent);
284
+ result =
285
+ exponent >= -22 && exponent <= 22
286
+ ? eiselLemire22(mantissa, exponent)
287
+ : scientific(mantissa, exponent);
281
288
  } else {
282
289
  fallbackField<T>(origStart, p, fieldPtr);
283
290
  return p;