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
@@ -0,0 +1,233 @@
1
+ // SIMD integer deserializers (signed + unsigned) over UTF-16 sources.
2
+ //
3
+ // Consume-to-end paths use the full tiered hierarchy:
4
+ //
5
+ // - parse16_SIMD (32 bytes / 16 digits): best for long inputs
6
+ // - parse8_SIMD (16 bytes / 8 digits)
7
+ // - parse4_SWAR ( 8 bytes / 4 digits): SWAR is fine for short tails
8
+ // - scalar ( 2 bytes / 1 digit )
9
+ //
10
+ // Scan paths use the same asymmetric tuning as the SWAR version:
11
+ //
12
+ // - Unsigned scan: parse8_SIMD + scalar (no parse4).
13
+ // - Signed scan: parse4_SWAR + scalar (no parse8). After the leading
14
+ // minus, the digit run lands in parse8's "terminator-in-load" boundary
15
+ // zone.
16
+ //
17
+ // Requires `--enable simd` at compile time. Dead-code-eliminated when
18
+ // JSON_MODE != SIMD.
19
+
20
+ import {
21
+ parse4Digits_PairMul,
22
+ parse4Digits_PairMul_Unsafe,
23
+ } from "../../util/swar-int";
24
+ import {
25
+ parse8Digits_SIMD,
26
+ parse8Digits_SIMD_Unsafe,
27
+ parse16Digits_SIMD,
28
+ parse16Digits_SIMD_Unsafe,
29
+ } from "../../util/simd-int";
30
+
31
+ const ASCII_MINUS: u16 = 45;
32
+ const ASCII_ZERO: u16 = 48;
33
+
34
+ /**
35
+ * Store a signed value into a typed integer field, truncating to `T`'s width.
36
+ *
37
+ * @param dstPtr Destination pointer (already includes any field offset).
38
+ * @param value The `u64` accumulator, interpreted as a two's-complement
39
+ * signed integer for narrower types.
40
+ */
41
+ // @ts-expect-error: @inline is a valid decorator
42
+ @inline function storeSignedToField<T extends number>(
43
+ dstPtr: usize,
44
+ value: u64,
45
+ ): void {
46
+ if (sizeof<T>() == 1) {
47
+ store<i8>(dstPtr, <i8>value);
48
+ } else if (sizeof<T>() == 2) {
49
+ store<i16>(dstPtr, <i16>value);
50
+ } else if (sizeof<T>() == 4) {
51
+ store<i32>(dstPtr, <i32>value);
52
+ } else {
53
+ store<i64>(dstPtr, <i64>value);
54
+ }
55
+ }
56
+
57
+ /**
58
+ * Store an unsigned value into a typed integer field, truncating to `T`'s
59
+ * width.
60
+ *
61
+ * @param dstPtr Destination pointer (already includes any field offset).
62
+ * @param value The `u64` accumulator.
63
+ */
64
+ // @ts-expect-error: decorator valid here
65
+ @inline function storeUnsignedToField<T extends number>(
66
+ dstPtr: usize,
67
+ value: u64,
68
+ ): void {
69
+ if (sizeof<T>() == 1) {
70
+ store<u8>(dstPtr, <u8>value);
71
+ } else if (sizeof<T>() == 2) {
72
+ store<u16>(dstPtr, <u16>value);
73
+ } else if (sizeof<T>() == 4) {
74
+ store<u32>(dstPtr, <u32>value);
75
+ } else {
76
+ store<u64>(dstPtr, value);
77
+ }
78
+ }
79
+
80
+ /**
81
+ * Parse a signed integer by consuming the entire `[srcStart, srcEnd)` range
82
+ * as a digit run, with an optional leading `-`.
83
+ *
84
+ * Caller guarantees the range is well-formed. Uses the unsafe SIMD kernels
85
+ * with no per-stride validation.
86
+ *
87
+ * @param srcStart Pointer to the first UTF-16 code unit.
88
+ * @param srcEnd Pointer just past the last code unit.
89
+ * @returns The parsed value, two's-complement truncated to `T`.
90
+ */
91
+ // @ts-expect-error: decorator valid here
92
+ @inline export function deserializeInteger_SIMD<T extends number>(
93
+ srcStart: usize,
94
+ srcEnd: usize,
95
+ ): T {
96
+ let negative = false;
97
+ if (srcStart < srcEnd && load<u16>(srcStart) == ASCII_MINUS) {
98
+ negative = true;
99
+ srcStart += 2;
100
+ }
101
+ let value: u64 = 0;
102
+ while (srcStart + 30 < srcEnd) {
103
+ value =
104
+ value * 10_000_000_000_000_000 + parse16Digits_SIMD_Unsafe(srcStart);
105
+ srcStart += 32;
106
+ }
107
+ while (srcStart + 14 < srcEnd) {
108
+ value = value * 100_000_000 + parse8Digits_SIMD_Unsafe(srcStart);
109
+ srcStart += 16;
110
+ }
111
+ while (srcStart + 6 < srcEnd) {
112
+ value = value * 10_000 + parse4Digits_PairMul_Unsafe(load<u64>(srcStart));
113
+ srcStart += 8;
114
+ }
115
+ while (srcStart < srcEnd) {
116
+ value = value * 10 + (<u32>load<u16>(srcStart) - ASCII_ZERO);
117
+ srcStart += 2;
118
+ }
119
+ return <T>(negative ? 0 - value : value);
120
+ }
121
+
122
+ /**
123
+ * Scan for a signed integer field, stopping at the first non-digit
124
+ * character. Writes the parsed value through `dstObj + dstOffset` and
125
+ * returns the source position immediately after the last digit.
126
+ *
127
+ * @param srcStart Pointer to the first UTF-16 code unit.
128
+ * @param srcEnd Pointer just past the last code unit.
129
+ * @param dstObj Destination object pointer.
130
+ * @param dstOffset Byte offset of the field within `dstObj`.
131
+ * @returns The source position immediately after the last digit consumed.
132
+ */
133
+ // @ts-expect-error: @inline is a valid decorator
134
+ @inline export function deserializeIntegerField_SIMD<T extends number>(
135
+ srcStart: usize,
136
+ srcEnd: usize,
137
+ dstObj: usize,
138
+ dstOffset: usize = 0,
139
+ ): usize {
140
+ let negative = false;
141
+ if (srcStart < srcEnd && load<u16>(srcStart) == ASCII_MINUS) {
142
+ negative = true;
143
+ srcStart += 2;
144
+ }
145
+ let value: u64 = 0;
146
+ // Signed scan uses parse4 + scalar only (see file header).
147
+ while (srcStart + 6 < srcEnd) {
148
+ const parsed = parse4Digits_PairMul(load<u64>(srcStart));
149
+ if (parsed == U32.MAX_VALUE) break;
150
+ value = value * 10_000 + parsed;
151
+ srcStart += 8;
152
+ }
153
+ while (srcStart < srcEnd) {
154
+ const digit = <u32>load<u16>(srcStart) - ASCII_ZERO;
155
+ if (digit > 9) break;
156
+ value = value * 10 + digit;
157
+ srcStart += 2;
158
+ }
159
+ storeSignedToField<T>(dstObj + dstOffset, negative ? 0 - value : value);
160
+ return srcStart;
161
+ }
162
+
163
+ /**
164
+ * Parse an unsigned integer by consuming the entire `[srcStart, srcEnd)`
165
+ * range as a digit run.
166
+ *
167
+ * Caller guarantees the range is digits only. Uses the unsafe SIMD kernels.
168
+ *
169
+ * @param srcStart Pointer to the first UTF-16 code unit.
170
+ * @param srcEnd Pointer just past the last code unit.
171
+ * @returns The parsed value, truncated to `T`.
172
+ */
173
+ // @ts-expect-error: @inline is a valid decorator
174
+ @inline export function deserializeUnsigned_SIMD<T extends number>(
175
+ srcStart: usize,
176
+ srcEnd: usize,
177
+ ): T {
178
+ let value: u64 = 0;
179
+ while (srcStart + 30 < srcEnd) {
180
+ value =
181
+ value * 10_000_000_000_000_000 + parse16Digits_SIMD_Unsafe(srcStart);
182
+ srcStart += 32;
183
+ }
184
+ while (srcStart + 14 < srcEnd) {
185
+ value = value * 100_000_000 + parse8Digits_SIMD_Unsafe(srcStart);
186
+ srcStart += 16;
187
+ }
188
+ while (srcStart + 6 < srcEnd) {
189
+ value = value * 10_000 + parse4Digits_PairMul_Unsafe(load<u64>(srcStart));
190
+ srcStart += 8;
191
+ }
192
+ while (srcStart < srcEnd) {
193
+ value = value * 10 + (<u32>load<u16>(srcStart) - ASCII_ZERO);
194
+ srcStart += 2;
195
+ }
196
+ return <T>value;
197
+ }
198
+
199
+ /**
200
+ * Scan for an unsigned integer field, stopping at the first non-digit
201
+ * character. Writes the parsed value through `dstObj + dstOffset` and
202
+ * returns the source position immediately after the last digit.
203
+ *
204
+ * @param srcStart Pointer to the first UTF-16 code unit.
205
+ * @param srcEnd Pointer just past the last code unit.
206
+ * @param dstObj Destination object pointer.
207
+ * @param dstOffset Byte offset of the field within `dstObj`.
208
+ * @returns The source position immediately after the last digit consumed.
209
+ */
210
+ // @ts-expect-error: @inline is a valid decorator
211
+ @inline export function deserializeUnsignedField_SIMD<T extends number>(
212
+ srcStart: usize,
213
+ srcEnd: usize,
214
+ dstObj: usize,
215
+ dstOffset: usize = 0,
216
+ ): usize {
217
+ let value: u64 = 0;
218
+ // Unsigned scan uses parse8 + scalar only (see file header).
219
+ while (srcStart + 14 < srcEnd) {
220
+ const parsed = parse8Digits_SIMD(srcStart);
221
+ if (parsed == U32.MAX_VALUE) break;
222
+ value = value * 100_000_000 + parsed;
223
+ srcStart += 16;
224
+ }
225
+ while (srcStart < srcEnd) {
226
+ const digit = <u32>load<u16>(srcStart) - ASCII_ZERO;
227
+ if (digit > 9) break;
228
+ value = value * 10 + digit;
229
+ srcStart += 2;
230
+ }
231
+ storeUnsignedToField<T>(dstObj + dstOffset, value);
232
+ return srcStart;
233
+ }
@@ -3,7 +3,10 @@ import { OBJECT, TOTAL_OVERHEAD } from "rt/common";
3
3
  import { __heap_base } from "memory";
4
4
  import { QUOTE } from "../../custom/chars";
5
5
  import { BACK_SLASH } from "../../custom/chars";
6
- import { DESERIALIZE_ESCAPE_TABLE, ESCAPE_HEX_TABLE } from "../../globals/tables";
6
+ import {
7
+ DESERIALIZE_ESCAPE_TABLE,
8
+ ESCAPE_HEX_TABLE,
9
+ } from "../../globals/tables";
7
10
  import { hex4_to_u16_swar } from "../../util/swar";
8
11
  import { deserializeStringField_SWAR } from "../swar/string";
9
12
 
@@ -65,7 +68,10 @@ import { deserializeStringField_SWAR } from "../swar/string";
65
68
  * @returns number of bytes written
66
69
  */
67
70
  // @ts-expect-error: @inline is a valid decorator
68
- @inline function copyStringFromSource_SIMD(srcStart: usize, byteLength: usize): string {
71
+ @inline function copyStringFromSource_SIMD(
72
+ srcStart: usize,
73
+ byteLength: usize,
74
+ ): string {
69
75
  if (byteLength == 0) return changetype<string>("");
70
76
  // @ts-expect-error: __new is a runtime builtin
71
77
  const out = __new(byteLength, idof<string>());
@@ -74,7 +80,11 @@ import { deserializeStringField_SWAR } from "../swar/string";
74
80
  }
75
81
 
76
82
  // @ts-expect-error: @inline is a valid decorator
77
- @inline function writeStringToField_SIMD(dstFieldPtr: usize, srcStart: usize, byteLength: u32): void {
83
+ @inline function writeStringToField_SIMD(
84
+ dstFieldPtr: usize,
85
+ srcStart: usize,
86
+ byteLength: u32,
87
+ ): void {
78
88
  if (byteLength == 0) {
79
89
  store<usize>(dstFieldPtr, changetype<usize>(""));
80
90
  return;
@@ -100,7 +110,11 @@ import { deserializeStringField_SWAR } from "../swar/string";
100
110
 
101
111
  // todo: optimize and stuff. it works, its not pretty. ideally, i'd like this to be (nearly) branchless
102
112
  // @ts-expect-error: @inline is a valid decorator
103
- @inline function deserializeEscapedString_SIMD(payloadStart: usize, escapeStart: usize, srcEnd: usize): string {
113
+ @inline function deserializeEscapedString_SIMD(
114
+ payloadStart: usize,
115
+ escapeStart: usize,
116
+ srcEnd: usize,
117
+ ): string {
104
118
  const prefixLen = <u32>(escapeStart - payloadStart);
105
119
  let srcStart = escapeStart;
106
120
  const srcEnd16 = srcEnd - 16;
@@ -238,12 +252,16 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
238
252
  }
239
253
 
240
254
  const laneIdx = usize(ctz(mask) << 1);
241
- return inline.always(deserializeEscapedString_SIMD(payloadStart, srcStart + laneIdx, srcEnd));
255
+ return inline.always(
256
+ deserializeEscapedString_SIMD(payloadStart, srcStart + laneIdx, srcEnd),
257
+ );
242
258
  }
243
259
 
244
260
  while (srcStart < srcEnd) {
245
261
  if (load<u16>(srcStart) == BACK_SLASH) {
246
- return inline.always(deserializeEscapedString_SIMD(payloadStart, srcStart, srcEnd));
262
+ return inline.always(
263
+ deserializeEscapedString_SIMD(payloadStart, srcStart, srcEnd),
264
+ );
247
265
  }
248
266
  srcStart += 2;
249
267
  }
@@ -252,9 +270,15 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
252
270
  }
253
271
 
254
272
  // @ts-expect-error: @inline is a valid decorator
255
- @inline export function deserializeStringField_SIMD<T extends string | null>(srcStart: usize, srcEnd: usize, dstObj: usize, dstOffset: usize = 0): usize {
273
+ @inline export function deserializeStringField_SIMD<T extends string | null>(
274
+ srcStart: usize,
275
+ srcEnd: usize,
276
+ dstObj: usize,
277
+ dstOffset: usize = 0,
278
+ ): usize {
256
279
  const dstFieldPtr = dstObj + dstOffset;
257
- if (srcStart + 2 > srcEnd || load<u16>(srcStart) != QUOTE) abort("Expected leading quote");
280
+ if (srcStart + 2 > srcEnd || load<u16>(srcStart) != QUOTE)
281
+ abort("Expected leading quote");
258
282
 
259
283
  const quotedStart = srcStart;
260
284
  const payloadStart = srcStart + 2;
@@ -263,7 +287,9 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
263
287
 
264
288
  while (srcStart <= srcEnd16) {
265
289
  const block = load<v128>(srcStart);
266
- let mask = i16x8.bitmask(v128.or(i16x8.eq(block, SPLAT_5C), i16x8.eq(block, SPLAT_22)));
290
+ let mask = i16x8.bitmask(
291
+ v128.or(i16x8.eq(block, SPLAT_5C), i16x8.eq(block, SPLAT_22)),
292
+ );
267
293
 
268
294
  if (mask == 0) {
269
295
  srcStart += 16;
@@ -277,7 +303,11 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
277
303
  const char = load<u16>(srcIdx);
278
304
 
279
305
  if (char == QUOTE) {
280
- writeStringToField_SIMD(dstFieldPtr, payloadStart, <u32>(srcIdx - payloadStart));
306
+ writeStringToField_SIMD(
307
+ dstFieldPtr,
308
+ payloadStart,
309
+ <u32>(srcIdx - payloadStart),
310
+ );
281
311
  return srcIdx + 2;
282
312
  }
283
313
 
@@ -292,7 +322,11 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
292
322
  while (srcStart < srcEnd) {
293
323
  const char = load<u16>(srcStart);
294
324
  if (char == QUOTE) {
295
- writeStringToField_SIMD(dstFieldPtr, payloadStart, <u32>(srcStart - payloadStart));
325
+ writeStringToField_SIMD(
326
+ dstFieldPtr,
327
+ payloadStart,
328
+ <u32>(srcStart - payloadStart),
329
+ );
296
330
  return srcStart + 2;
297
331
  }
298
332
  if (char == BACK_SLASH) {
@@ -6,15 +6,22 @@ import { deserializeString } from "./string";
6
6
  import { deserializeObject } from "./object";
7
7
  import { BRACE_LEFT, BRACKET_LEFT, CHAR_N, QUOTE } from "../../custom/chars";
8
8
 
9
- export function deserializeArbitrary(srcStart: usize, srcEnd: usize, dst: usize): JSON.Value {
9
+ export function deserializeArbitrary(
10
+ srcStart: usize,
11
+ srcEnd: usize,
12
+ dst: usize,
13
+ ): JSON.Value {
10
14
  const firstChar = load<u16>(srcStart);
11
15
  if (firstChar == QUOTE) {
12
16
  return JSON.Value.from(deserializeString(srcStart, srcEnd));
13
- } else if (firstChar == BRACE_LEFT) return JSON.Value.from(deserializeObject(srcStart, srcEnd, 0));
14
- else if (firstChar - 48 <= 9 || firstChar == 45) return JSON.Value.from(deserializeFloat<f64>(srcStart, srcEnd));
17
+ } else if (firstChar == BRACE_LEFT)
18
+ return JSON.Value.from(deserializeObject(srcStart, srcEnd, 0));
19
+ else if (firstChar - 48 <= 9 || firstChar == 45)
20
+ return JSON.Value.from(deserializeFloat<f64>(srcStart, srcEnd));
15
21
  else if (firstChar == BRACKET_LEFT) {
16
22
  return JSON.Value.from(deserializeArray<JSON.Value[]>(srcStart, srcEnd, 0));
17
- } else if (firstChar == 116 || firstChar == 102) return JSON.Value.from(deserializeBoolean(srcStart, srcEnd));
23
+ } else if (firstChar == 116 || firstChar == 102)
24
+ return JSON.Value.from(deserializeBoolean(srcStart, srcEnd));
18
25
  else if (firstChar == CHAR_N) {
19
26
  const value = JSON.Value.from<usize>(0);
20
27
  return value;
@@ -1,10 +1,27 @@
1
- import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, CHAR_F, CHAR_N, CHAR_T, COMMA, QUOTE } from "../../../custom/chars";
1
+ import {
2
+ BACK_SLASH,
3
+ BRACE_LEFT,
4
+ BRACE_RIGHT,
5
+ BRACKET_LEFT,
6
+ BRACKET_RIGHT,
7
+ CHAR_F,
8
+ CHAR_N,
9
+ CHAR_T,
10
+ COMMA,
11
+ QUOTE,
12
+ } from "../../../custom/chars";
2
13
  import { JSON } from "../../../";
3
14
  import { isSpace, scanStringEnd } from "../../../util";
4
15
  import { ptrToStr } from "../../../util/ptrToStr";
5
16
 
6
- export function deserializeArbitraryArray(srcStart: usize, srcEnd: usize, dst: usize): JSON.Value[] {
7
- const out = changetype<JSON.Value[]>(dst || changetype<usize>(instantiate<JSON.Value[]>()));
17
+ export function deserializeArbitraryArray(
18
+ srcStart: usize,
19
+ srcEnd: usize,
20
+ dst: usize,
21
+ ): JSON.Value[] {
22
+ const out = changetype<JSON.Value[]>(
23
+ dst || changetype<usize>(instantiate<JSON.Value[]>()),
24
+ );
8
25
  let lastIndex: usize = 0;
9
26
  let depth: u32 = 0;
10
27
  // if (load<u16>(srcStart) != BRACKET_LEFT)
@@ -31,7 +48,8 @@ export function deserializeArbitraryArray(srcStart: usize, srcEnd: usize, dst: u
31
48
  } else if (code == QUOTE) {
32
49
  lastIndex = srcStart;
33
50
  srcStart = scanStringEnd(srcStart, srcEnd);
34
- if (srcStart >= srcEnd) throw new Error("Unterminated string in JSON array");
51
+ if (srcStart >= srcEnd)
52
+ throw new Error("Unterminated string in JSON array");
35
53
  // @ts-ignore: exists
36
54
  out.push(JSON.__deserialize<JSON.Value>(lastIndex, srcStart + 2));
37
55
  srcStart += 2;
@@ -78,7 +96,8 @@ export function deserializeArbitraryArray(srcStart: usize, srcEnd: usize, dst: u
78
96
  const code = load<u16>(srcStart);
79
97
  if (code == QUOTE) {
80
98
  srcStart = scanStringEnd(srcStart, srcEnd);
81
- if (srcStart >= srcEnd) throw new Error("Unterminated string in JSON array");
99
+ if (srcStart >= srcEnd)
100
+ throw new Error("Unterminated string in JSON array");
82
101
  } else if (code == BRACKET_RIGHT) {
83
102
  if (--depth == 0) {
84
103
  // @ts-ignore: type
@@ -1,8 +1,14 @@
1
1
  import { BRACKET_LEFT, BRACKET_RIGHT } from "../../../custom/chars";
2
2
  import { JSON } from "../../../";
3
3
 
4
- export function deserializeArrayArray<T extends unknown[][]>(srcStart: usize, srcEnd: usize, dst: usize): T {
5
- const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
4
+ export function deserializeArrayArray<T extends unknown[][]>(
5
+ srcStart: usize,
6
+ srcEnd: usize,
7
+ dst: usize,
8
+ ): T {
9
+ const out = changetype<nonnull<T>>(
10
+ dst || changetype<usize>(instantiate<T>()),
11
+ );
6
12
  let lastIndex: usize = 0;
7
13
  let depth: u32 = 0;
8
14
  srcStart += 2;
@@ -1,17 +1,48 @@
1
- export function deserializeBooleanArray<T extends boolean[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
2
- const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
3
- srcStart += 2; // skip [
1
+ import { FALSE_WORD_U64, TRUE_WORD_U64 } from "../../../custom/chars";
2
+
3
+ /**
4
+ * Boolean-array deserializer (used by every JSON_MODE for top-level
5
+ * `JSON.parse<bool[]>`). Worst-case sizing is one element per `"true,"` =
6
+ * 10 UTF-16 bytes, so pre-allocating `(srcEnd - srcStart) / 10` slots
7
+ * upper-bounds the element count exactly once and lets the loop write
8
+ * through a direct pointer instead of `Array.push`.
9
+ *
10
+ * The token check itself is already SWAR-shaped: a single `u64` load
11
+ * matches all four chars of `true`, and the `false` case adds one `u16`
12
+ * load to confirm the trailing `e`.
13
+ */
14
+ export function deserializeBooleanArray<T extends boolean[]>(
15
+ srcStart: usize,
16
+ srcEnd: usize,
17
+ dst: usize,
18
+ ): T {
19
+ const out = changetype<nonnull<T>>(
20
+ dst || changetype<usize>(instantiate<T>()),
21
+ );
22
+
23
+ // Worst case: every element is `true,` = 5 UTF-16 chars = 10 bytes.
24
+ // `bool` is unmanaged so AS skips zero-fill on `length=`, making the
25
+ // over-allocation essentially free.
26
+ const maxElements = i32(<usize>(srcEnd - srcStart) / 10);
27
+ if (maxElements > 0) out.length = maxElements;
28
+ const dataStart = out.dataStart;
29
+ let writePtr = dataStart;
30
+
31
+ srcStart += 2; // skip `[`
4
32
  while (srcStart < srcEnd) {
5
33
  const block = load<u64>(srcStart);
6
- if (block == 28429475166421108) {
7
- out.push(true);
34
+ if (block == TRUE_WORD_U64) {
35
+ store<bool>(writePtr, true);
36
+ writePtr += 1;
8
37
  srcStart += 10;
9
- } else if (block == 32370086184550502 && load<u16>(srcStart, 8) == 101) {
10
- out.push(false);
38
+ } else if (block == FALSE_WORD_U64 && load<u16>(srcStart, 8) == 101) {
39
+ store<bool>(writePtr, false);
40
+ writePtr += 1;
11
41
  srcStart += 12;
12
42
  } else {
13
43
  srcStart += 2;
14
44
  }
15
45
  }
46
+ out.length = i32(writePtr - dataStart);
16
47
  return out;
17
48
  }
@@ -2,8 +2,14 @@ import { isSpace } from "../../../util";
2
2
  import { COMMA, BRACKET_RIGHT } from "../../../custom/chars";
3
3
  import { JSON } from "../../..";
4
4
 
5
- export function deserializeBoxArray<T extends JSON.Box<any>[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
6
- const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
5
+ export function deserializeBoxArray<T extends JSON.Box<any>[]>(
6
+ srcStart: usize,
7
+ srcEnd: usize,
8
+ dst: usize,
9
+ ): T {
10
+ const out = changetype<nonnull<T>>(
11
+ dst || changetype<usize>(instantiate<T>()),
12
+ );
7
13
  if (isBoolean<valueof<T>>()) {
8
14
  srcStart += 2; // skip [
9
15
  while (srcStart < srcEnd) {
@@ -2,21 +2,46 @@ import { isSpace } from "../../../util";
2
2
  import { COMMA, BRACKET_RIGHT } from "../../../custom/chars";
3
3
  import { JSON } from "../../..";
4
4
 
5
- export function deserializeFloatArray<T extends number[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
6
- const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
5
+ /**
6
+ * Float-array deserializer (`f64[]` / `f32[]`).
7
+ *
8
+ * Worst-case sizing: every element is at least `D,` = 2 UTF-16 chars = 4
9
+ * bytes (a single-digit value followed by `,` or `]`). The `srcLen >> 2`
10
+ * upper bound holds even for valid JSON containing negative or fractional
11
+ * widths, because each element advance still consumes >= 4 bytes.
12
+ *
13
+ * f64/f32 are unmanaged so AS skips zero-fill on `length=`. The parse loop
14
+ * writes through a direct `writePtr` pointer, eliminating `Array.push`'s
15
+ * per-element capacity check + length write. Final `out.length` is trimmed
16
+ * to the actual element count.
17
+ */
18
+ export function deserializeFloatArray<T extends number[]>(
19
+ srcStart: usize,
20
+ srcEnd: usize,
21
+ dst: usize,
22
+ ): T {
23
+ const out = changetype<nonnull<T>>(
24
+ dst || changetype<usize>(instantiate<T>()),
25
+ );
26
+
27
+ const elementSize = sizeof<valueof<T>>();
28
+ const maxElements = i32((<usize>(srcEnd - srcStart)) >> 2);
29
+ if (maxElements > 0) out.length = maxElements;
30
+ const dataStart = out.dataStart;
31
+ let writePtr = dataStart;
32
+
7
33
  let lastIndex: usize = 0;
8
34
  while (srcStart < srcEnd) {
9
35
  const code = load<u16>(srcStart);
10
- if (code - 48 <= 9 || code == 45) {
36
+ if (<u32>code - 48 <= 9 || code == 45) {
11
37
  lastIndex = srcStart;
12
38
  srcStart += 2;
13
39
  while (srcStart < srcEnd) {
14
- const code = load<u16>(srcStart);
15
- if (code == COMMA || code == BRACKET_RIGHT || isSpace(code)) {
16
- out.push(JSON.__deserialize<valueof<T>>(lastIndex, srcStart));
17
- // while (isSpace(load<u16>((srcStart += 2)))) {
18
- // /* empty */
19
- // }
40
+ const c = load<u16>(srcStart);
41
+ if (c == COMMA || c == BRACKET_RIGHT || isSpace(c)) {
42
+ const value = JSON.__deserialize<valueof<T>>(lastIndex, srcStart);
43
+ store<valueof<T>>(writePtr, value);
44
+ writePtr += elementSize;
20
45
  break;
21
46
  }
22
47
  srcStart += 2;
@@ -24,5 +49,7 @@ export function deserializeFloatArray<T extends number[]>(srcStart: usize, srcEn
24
49
  }
25
50
  srcStart += 2;
26
51
  }
52
+
53
+ out.length = i32(<usize>(writePtr - dataStart) / elementSize);
27
54
  return out;
28
55
  }
@@ -3,15 +3,23 @@ import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
3
3
  import { isSpace } from "../../../util";
4
4
  import { scanValueEnd } from "../../swar/array/shared";
5
5
 
6
- export function deserializeGenericArray<T extends unknown[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
7
- const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
6
+ export function deserializeGenericArray<T extends unknown[]>(
7
+ srcStart: usize,
8
+ srcEnd: usize,
9
+ dst: usize,
10
+ ): T {
11
+ const out = changetype<nonnull<T>>(
12
+ dst || changetype<usize>(instantiate<T>()),
13
+ );
8
14
  out.length = 0;
9
15
 
10
16
  while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
11
17
  while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
12
18
 
13
- if (srcStart >= srcEnd) throw new Error("Input string had zero length or was all whitespace");
14
- if (load<u16>(srcStart) != BRACKET_LEFT) throw new Error("Expected '[' at start of array");
19
+ if (srcStart >= srcEnd)
20
+ throw new Error("Input string had zero length or was all whitespace");
21
+ if (load<u16>(srcStart) != BRACKET_LEFT)
22
+ throw new Error("Expected '[' at start of array");
15
23
  srcStart += 2;
16
24
 
17
25
  while (srcStart < srcEnd) {
@@ -1,8 +1,14 @@
1
1
  import { atoi, isSpace } from "../../../util";
2
2
  import { COMMA, BRACKET_RIGHT } from "../../../custom/chars";
3
3
 
4
- export function deserializeIntegerArray<T extends number[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
5
- const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
4
+ export function deserializeIntegerArray<T extends number[]>(
5
+ srcStart: usize,
6
+ srcEnd: usize,
7
+ dst: usize,
8
+ ): T {
9
+ const out = changetype<nonnull<T>>(
10
+ dst || changetype<usize>(instantiate<T>()),
11
+ );
6
12
  let lastIndex: usize = 0;
7
13
  while (srcStart < srcEnd) {
8
14
  const code = load<u16>(srcStart);