json-as 1.4.0 → 1.5.0

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 (86) hide show
  1. package/CHANGELOG.md +50 -29
  2. package/README.md +84 -33
  3. package/assembly/custom/chars.ts +39 -78
  4. package/assembly/deserialize/index/arbitrary.ts +26 -8
  5. package/assembly/deserialize/index/float.ts +2 -4
  6. package/assembly/deserialize/index/integer.ts +2 -4
  7. package/assembly/deserialize/index/object.ts +6 -1
  8. package/assembly/deserialize/index/string.ts +2 -7
  9. package/assembly/deserialize/index/unsigned.ts +2 -4
  10. package/assembly/deserialize/naive/array/integer.ts +1 -1
  11. package/assembly/deserialize/naive/array/map.ts +1 -1
  12. package/assembly/deserialize/naive/array/object.ts +1 -1
  13. package/assembly/deserialize/naive/array/struct.ts +19 -1
  14. package/assembly/deserialize/naive/bool.ts +1 -5
  15. package/assembly/deserialize/naive/date.ts +1 -2
  16. package/assembly/deserialize/naive/float.ts +2 -7
  17. package/assembly/deserialize/naive/integer.ts +1 -2
  18. package/assembly/deserialize/naive/map.ts +5 -6
  19. package/assembly/deserialize/naive/object.ts +151 -13
  20. package/assembly/deserialize/naive/raw.ts +1 -5
  21. package/assembly/deserialize/naive/set.ts +2 -4
  22. package/assembly/deserialize/naive/staticarray.ts +1 -2
  23. package/assembly/deserialize/naive/string.ts +6 -9
  24. package/assembly/deserialize/naive/unsigned.ts +1 -2
  25. package/assembly/deserialize/simd/array/integer.ts +2 -7
  26. package/assembly/deserialize/simd/float.ts +3 -5
  27. package/assembly/deserialize/simd/integer.ts +2 -7
  28. package/assembly/deserialize/simd/string.ts +5 -22
  29. package/assembly/deserialize/swar/array/arbitrary.ts +1 -2
  30. package/assembly/deserialize/swar/array/array.ts +1 -2
  31. package/assembly/deserialize/swar/array/bool.ts +1 -2
  32. package/assembly/deserialize/swar/array/box.ts +1 -2
  33. package/assembly/deserialize/swar/array/float.ts +6 -18
  34. package/assembly/deserialize/swar/array/generic.ts +1 -2
  35. package/assembly/deserialize/swar/array/integer.ts +7 -16
  36. package/assembly/deserialize/swar/array/map.ts +1 -2
  37. package/assembly/deserialize/swar/array/object.ts +1 -2
  38. package/assembly/deserialize/swar/array/raw.ts +1 -2
  39. package/assembly/deserialize/swar/array/shared.ts +6 -13
  40. package/assembly/deserialize/swar/array/string.ts +3 -8
  41. package/assembly/deserialize/swar/array/struct.ts +2 -8
  42. package/assembly/deserialize/swar/array.ts +1 -3
  43. package/assembly/deserialize/swar/float.ts +4 -9
  44. package/assembly/deserialize/swar/integer.ts +2 -7
  45. package/assembly/deserialize/swar/string.ts +13 -15
  46. package/assembly/deserialize/swar/typedarray.ts +4 -4
  47. package/assembly/index.d.ts +29 -24
  48. package/assembly/index.ts +1362 -246
  49. package/assembly/serialize/index/arbitrary.ts +70 -4
  50. package/assembly/serialize/index/jsonarray.ts +51 -0
  51. package/assembly/serialize/index/object.ts +25 -3
  52. package/assembly/serialize/index/string.ts +1 -2
  53. package/assembly/serialize/index/typedarray.ts +1 -2
  54. package/assembly/serialize/index.ts +1 -0
  55. package/assembly/serialize/naive/array.ts +23 -34
  56. package/assembly/serialize/naive/bool.ts +0 -1
  57. package/assembly/serialize/naive/float.ts +16 -25
  58. package/assembly/serialize/naive/integer.ts +1 -5
  59. package/assembly/serialize/naive/raw.ts +1 -2
  60. package/assembly/serialize/naive/set.ts +0 -4
  61. package/assembly/serialize/naive/staticarray.ts +0 -5
  62. package/assembly/serialize/naive/string.ts +2 -5
  63. package/assembly/serialize/naive/typedarray.ts +0 -6
  64. package/assembly/serialize/simd/string.ts +1 -3
  65. package/assembly/serialize/swar/string.ts +1 -2
  66. package/assembly/util/atoi-fast.ts +4 -14
  67. package/assembly/util/bytes.ts +1 -2
  68. package/assembly/util/idofd.ts +1 -2
  69. package/assembly/util/isSpace.ts +1 -2
  70. package/assembly/util/itoa-fast.ts +6 -9
  71. package/assembly/util/nextPowerOf2.ts +1 -2
  72. package/assembly/util/parsefloat-fast.ts +3 -5
  73. package/assembly/util/ptrToStr.ts +1 -2
  74. package/assembly/util/scanValueEndSimd.ts +54 -16
  75. package/assembly/util/scanValueEndSwar.ts +67 -25
  76. package/assembly/util/scientific.ts +5 -8
  77. package/assembly/util/snp.ts +1 -2
  78. package/assembly/util/swar-int.ts +5 -10
  79. package/assembly/util/swar.ts +2 -4
  80. package/lib/as-bs.ts +23 -45
  81. package/package.json +14 -7
  82. package/transform/lib/index.js +108 -64
  83. package/transform/lib/types.d.ts +2 -1
  84. package/transform/lib/types.js +3 -0
  85. package/assembly/util/dragonbox-cache.ts +0 -445
  86. package/assembly/util/dragonbox.ts +0 -652
@@ -3,7 +3,7 @@
3
3
  // `parse8Digits_SIMD` (16 bytes / 8 digits) before falling through to
4
4
  // `parse4Digits_PairMul` (8 bytes / 4 digits) and finally the scalar tail.
5
5
  //
6
- // Output is bit-identical to `f64.parse` / `f32.parse` for every input the
6
+ // Output is bit-identical to `f64.parse` / `f32.parse` for every input - the
7
7
  // SIMD strides only change how the u64 mantissa is accumulated, not what it
8
8
  // becomes.
9
9
  //
@@ -23,8 +23,7 @@ const ASCII_ZERO: u16 = 48;
23
23
  const ASCII_E_UP: u16 = 69;
24
24
  const ASCII_E_LO: u16 = 101;
25
25
 
26
- // @ts-ignore: inline
27
- @inline function fallback<T>(srcStart: usize, srcEnd: usize): T {
26
+ function fallback<T>(srcStart: usize, srcEnd: usize): T {
28
27
  const s = ptrToStr(srcStart, srcEnd);
29
28
  // @ts-ignore
30
29
  const type: T = 0;
@@ -34,8 +33,7 @@ const ASCII_E_LO: u16 = 101;
34
33
  return <T>(<f32>f32.parse(s));
35
34
  }
36
35
 
37
- // @ts-ignore: inline
38
- @inline function fallbackField<T extends number>(
36
+ function fallbackField<T extends number>(
39
37
  origStart: usize,
40
38
  end: usize,
41
39
  fieldPtr: usize,
@@ -38,11 +38,7 @@ const ASCII_ZERO: u16 = 48;
38
38
  * @param value The `u64` accumulator, interpreted as a two's-complement
39
39
  * signed integer for narrower types.
40
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 {
41
+ function storeSignedToField<T extends number>(dstPtr: usize, value: u64): void {
46
42
  if (sizeof<T>() == 1) {
47
43
  store<i8>(dstPtr, <i8>value);
48
44
  } else if (sizeof<T>() == 2) {
@@ -61,8 +57,7 @@ const ASCII_ZERO: u16 = 48;
61
57
  * @param dstPtr Destination pointer (already includes any field offset).
62
58
  * @param value The `u64` accumulator.
63
59
  */
64
- // @ts-expect-error: decorator valid here
65
- @inline function storeUnsignedToField<T extends number>(
60
+ function storeUnsignedToField<T extends number>(
66
61
  dstPtr: usize,
67
62
  value: u64,
68
63
  ): void {
@@ -63,13 +63,8 @@ import { hex4_to_u16_swar } from "../../util/swar";
63
63
  * @param dst buffer to write to
64
64
  * @returns number of bytes written
65
65
  */
66
- // @ts-expect-error: @inline is a valid decorator
67
- @inline function copyStringFromSource_SIMD(
68
- srcStart: usize,
69
- byteLength: usize,
70
- ): string {
66
+ function copyStringFromSource_SIMD(srcStart: usize, byteLength: usize): string {
71
67
  if (byteLength == 0) return changetype<string>("");
72
- // @ts-expect-error: __new is a runtime builtin
73
68
  const out = __new(byteLength, idof<string>());
74
69
  memory.copy(out, srcStart, byteLength);
75
70
  return changetype<string>(out);
@@ -106,11 +101,7 @@ function writeStringToField_SIMD(
106
101
  // handling (no closing-quote search). Same HYBRID strategy as the field path
107
102
  // (see deserializeEscapedStringField_SIMD): escape blocks use a free
108
103
  // whole-block v128 store for the plain prefix; clean runs stream the first
109
- // block then bulk-memcpy the remainder. Output is sliced out of `bs` scratch.
110
- //
111
- // Deliberately NOT @inline: cold escape path. Inlining the nested-loop v128
112
- // body at every call site explodes `--converge` compile time on large schemas
113
- // for no runtime gain (one call per escaped string).
104
+
114
105
  function deserializeEscapedString_SIMD(
115
106
  payloadStart: usize,
116
107
  escapeStart: usize,
@@ -254,18 +245,14 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
254
245
  // reused `bs` scratch buffer, then written once via writeStringToField_SIMD.
255
246
  //
256
247
  // Strategy (validated against run-copy and pure-stream variants across escape
257
- // densities see __benches__/custom/simd-string-deser-variants-h2h):
248
+ // densities - see __benches__/custom/simd-string-deser-variants-h2h):
258
249
  // * Escape-bearing block: a single whole-block v128 store copies the plain
259
250
  // prefix for free; then the escape is decoded scalar.
260
251
  // * Clean block: stream the first one cheaply, but if the clean run keeps
261
- // going, switch to one bulk memory.copy for the remainder bandwidth-
252
+ // going, switch to one bulk memory.copy for the remainder - bandwidth-
262
253
  // optimal on long sparse runs, avoiding a per-block-store cliff on large
263
254
  // inputs. This dominates both alternatives: stream-cheap on dense escapes,
264
- // memcpy-fast on long runs.
265
- //
266
- // Deliberately NOT @inline: cold escape path. As an @inline it was inlined into
267
- // deserializeStringField_SIMD at every struct string-field site, exploding
268
- // `--converge` compile time ~24x on large schemas (4s → 99s) for no runtime
255
+
269
256
  // gain. The hot no-escape scan + writeStringToField stay inline in the caller.
270
257
  function deserializeEscapedStringField_SIMD(
271
258
  payloadStart: usize,
@@ -386,10 +373,6 @@ function deserializeEscapedStringField_SIMD(
386
373
  return srcStart;
387
374
  }
388
375
 
389
- // NOT @inline: as an @inline entry, binaryen inlined the (loop-bearing) escaped
390
- // scanner into every per-field copy, exploding `large` SIMD compile ~24x
391
- // (4s→99s, 221KB→555KB wasm). Kept as a single shared function — matches
392
- // deserializeStringField_SWAR (also non-inline) and costs only one call/field.
393
376
  export function deserializeStringField_SIMD<T extends string | null>(
394
377
  srcStart: usize,
395
378
  srcEnd: usize,
@@ -2,8 +2,7 @@ import { JSON } from "../../..";
2
2
  import { deserializeGenericArrayBody } from "./generic";
3
3
  import { ensureArrayField } from "./shared";
4
4
 
5
-
6
- @inline export function deserializeArbitraryArrayField(
5
+ export function deserializeArbitraryArrayField(
7
6
  srcStart: usize,
8
7
  srcEnd: usize,
9
8
  fieldPtr: usize,
@@ -90,8 +90,7 @@ export function deserializeArrayArrayBody<T extends unknown[][]>(
90
90
  throw new Error("Failed to parse JSON!");
91
91
  }
92
92
 
93
-
94
- @inline export function deserializeArrayArrayField<T extends unknown[][]>(
93
+ export function deserializeArrayArrayField<T extends unknown[][]>(
95
94
  srcStart: usize,
96
95
  srcEnd: usize,
97
96
  fieldPtr: usize,
@@ -59,8 +59,7 @@ function deserializeBooleanArrayBody<T extends boolean[]>(
59
59
  throw new Error("Failed to parse JSON!");
60
60
  }
61
61
 
62
-
63
- @inline export function deserializeBooleanArrayField<T extends boolean[]>(
62
+ export function deserializeBooleanArrayField<T extends boolean[]>(
64
63
  srcStart: usize,
65
64
  srcEnd: usize,
66
65
  fieldPtr: usize,
@@ -2,8 +2,7 @@ import { JSON } from "../../..";
2
2
  import { deserializeGenericArrayBody } from "./generic";
3
3
  import { ensureArrayField } from "./shared";
4
4
 
5
-
6
- @inline export function deserializeBoxArrayField<T extends JSON.Box<any>[]>(
5
+ export function deserializeBoxArrayField<T extends JSON.Box<any>[]>(
7
6
  srcStart: usize,
8
7
  srcEnd: usize,
9
8
  fieldPtr: usize,
@@ -8,21 +8,11 @@ import { parse4Digits_PairMul } from "../../../util/swar-int";
8
8
  import { loadPow10, MAX_EXACT_MANTISSA, MAX_EXACT_POW10 } from "../float";
9
9
  import { isSpace } from "../../../util";
10
10
 
11
-
12
- @inline function skipFloatArrayWhitespace(
13
- srcStart: usize,
14
- srcEnd: usize,
15
- ): usize {
11
+ function skipFloatArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
16
12
  while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
17
13
  return srcStart;
18
14
  }
19
-
20
- // @ts-ignore: inline
21
- @inline function fallbackStore<E>(
22
- origStart: usize,
23
- end: usize,
24
- slot: usize,
25
- ): void {
15
+ function fallbackStore<E>(origStart: usize, end: usize, slot: usize): void {
26
16
  const s = ptrToStr(origStart, end);
27
17
  if (sizeof<E>() == sizeof<f32>()) {
28
18
  store<f32>(slot, f32.parse(s));
@@ -77,7 +67,7 @@ export function parseFloatElementSWAR<E>(
77
67
  }
78
68
 
79
69
  // Fractional mantissa: parse4 SWAR stride + scalar tail. Same u64
80
- // accumulator as the integer part exponent compensates for fracDigits.
70
+ // accumulator as the integer part - exponent compensates for fracDigits.
81
71
  let fracDigits: i32 = 0;
82
72
  if (p < srcEnd && load<u16>(p) == 46) {
83
73
  p += 2;
@@ -164,7 +154,7 @@ export function parseFloatElementSWAR<E>(
164
154
  // the `ptrToStr` allocation + strtod re-parse.
165
155
  result = scientific(mantissa, exponent);
166
156
  } else {
167
- // >19 mantissa digits beyond u64 capacity, may need strtod's sticky-bit
157
+ // >19 mantissa digits - beyond u64 capacity, may need strtod's sticky-bit
168
158
  // pattern. Hand off to f*.parse on the float's substring.
169
159
  fallbackStore<E>(origStart, p, slot);
170
160
  return p;
@@ -255,7 +245,7 @@ export function deserializeFloatArray_SWAR<T extends number[]>(
255
245
  }
256
246
 
257
247
  /**
258
- * Field/into variant parses `[..]` into the existing `out` array and
248
+ * Field/into variant - parses `[..]` into the existing `out` array and
259
249
  * returns the cursor past the closing `]`.
260
250
  *
261
251
  * Worst-case pre-sizing (`(srcEnd - srcStart) >> 2`) used by the top-level
@@ -315,9 +305,7 @@ export function deserializeFloatArrayBody<T extends number[]>(
315
305
 
316
306
  throw new Error("Failed to parse JSON!");
317
307
  }
318
-
319
-
320
- @inline export function deserializeFloatArrayField<T extends number[]>(
308
+ export function deserializeFloatArrayField<T extends number[]>(
321
309
  srcStart: usize,
322
310
  srcEnd: usize,
323
311
  fieldPtr: usize,
@@ -47,8 +47,7 @@ export function deserializeGenericArrayBody<T extends unknown[]>(
47
47
  throw new Error("Failed to parse JSON!");
48
48
  }
49
49
 
50
-
51
- @inline export function deserializeGenericArrayField<T extends unknown[]>(
50
+ export function deserializeGenericArrayField<T extends unknown[]>(
52
51
  srcStart: usize,
53
52
  srcEnd: usize,
54
53
  fieldPtr: usize,
@@ -7,12 +7,9 @@ import { parse4Digits_PairMul } from "../../../util/swar-int";
7
7
  // Store helpers parameterised on the element type `E` directly, so they
8
8
  // serve both `Array<E>` and `TypedArray<E>` callers. The integer-array
9
9
  // callers below all pass `valueof<T>` and AS folds the resulting tower of
10
- // `sizeof<E>` comparisons at compile time same codegen as the prior
10
+ // `sizeof<E>` comparisons at compile time - same codegen as the prior
11
11
  // `T extends number[]` version, but reusable from `swar/typedarray.ts`.
12
- @inline function storeSignedIntegerE<E extends number>(
13
- slot: usize,
14
- value: i64,
15
- ): void {
12
+ function storeSignedIntegerE<E extends number>(slot: usize, value: i64): void {
16
13
  if (sizeof<E>() == sizeof<i8>()) {
17
14
  store<i8>(slot, <i8>value);
18
15
  } else if (sizeof<E>() == sizeof<i16>()) {
@@ -26,8 +23,7 @@ import { parse4Digits_PairMul } from "../../../util/swar-int";
26
23
  }
27
24
  }
28
25
 
29
-
30
- @inline function storeUnsignedIntegerE<E extends number>(
26
+ function storeUnsignedIntegerE<E extends number>(
31
27
  slot: usize,
32
28
  value: u64,
33
29
  ): void {
@@ -211,11 +207,7 @@ export function parseUnsignedIntegerSWAR<E extends number>(
211
207
  return srcStart;
212
208
  }
213
209
 
214
-
215
- @inline function skipIntegerArrayWhitespace(
216
- srcStart: usize,
217
- srcEnd: usize,
218
- ): usize {
210
+ function skipIntegerArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
219
211
  while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) {
220
212
  srcStart += 2;
221
213
  }
@@ -661,7 +653,7 @@ function deserializeIntegerArrayBody<T extends number[]>(
661
653
  const slot = ensureArrayElementSlot<T>(out, index);
662
654
  // Inline the array-optimized SWAR parser directly. The top-level
663
655
  // (`deserializeIntegerArrayImpl`) and the typed-array path
664
- // (`swar/typedarray.ts`) already call these having the field path
656
+ // (`swar/typedarray.ts`) already call these - having the field path
665
657
  // call them too means the per-element parser is identical across all
666
658
  // three call sites (parse4 + scalar fold for both signed and
667
659
  // unsigned, narrow-lane special case for i8/u8/i16/u16).
@@ -669,7 +661,7 @@ function deserializeIntegerArrayBody<T extends number[]>(
669
661
  // Why not `deserializeUnsignedField_SWAR` (which uses parse8 + scalar)?
670
662
  // That tuning targets the struct-single-field path where the digit
671
663
  // run is one aligned token. In an array, mixed element widths cause
672
- // parse8 to fail-and-retry at element boundaries see u32-64mib's
664
+ // parse8 to fail-and-retry at element boundaries - see u32-64mib's
673
665
  // 23% regression when parse8 was tried in the array path.
674
666
  srcStart = isSigned<valueof<T>>()
675
667
  ? parseSignedIntegerSWAR<valueof<T>>(srcStart, srcEnd, slot)
@@ -697,8 +689,7 @@ function deserializeIntegerArrayBody<T extends number[]>(
697
689
  throw new Error("Failed to parse JSON!");
698
690
  }
699
691
 
700
-
701
- @inline export function deserializeIntegerArrayField<T extends number[]>(
692
+ export function deserializeIntegerArrayField<T extends number[]>(
702
693
  srcStart: usize,
703
694
  srcEnd: usize,
704
695
  fieldPtr: usize,
@@ -1,8 +1,7 @@
1
1
  import { deserializeGenericArrayBody } from "./generic";
2
2
  import { ensureArrayField } from "./shared";
3
3
 
4
-
5
- @inline export function deserializeMapArrayField<T extends Map<any, any>[]>(
4
+ export function deserializeMapArrayField<T extends Map<any, any>[]>(
6
5
  srcStart: usize,
7
6
  srcEnd: usize,
8
7
  fieldPtr: usize,
@@ -58,8 +58,7 @@ function deserializeObjectArrayBody<T extends unknown[]>(
58
58
  throw new Error("Failed to parse JSON!");
59
59
  }
60
60
 
61
-
62
- @inline export function deserializeObjectArrayField<T extends unknown[]>(
61
+ export function deserializeObjectArrayField<T extends unknown[]>(
63
62
  srcStart: usize,
64
63
  srcEnd: usize,
65
64
  fieldPtr: usize,
@@ -2,8 +2,7 @@ import { JSON } from "../../..";
2
2
  import { deserializeGenericArrayBody } from "./generic";
3
3
  import { ensureArrayField } from "./shared";
4
4
 
5
-
6
- @inline export function deserializeRawArrayField(
5
+ export function deserializeRawArrayField(
7
6
  srcStart: usize,
8
7
  srcEnd: usize,
9
8
  fieldPtr: usize,
@@ -10,16 +10,12 @@ import {
10
10
  import { isSpace } from "../../../util";
11
11
 
12
12
  /** Advance past JSON whitespace (space, tab, LF, CR). */
13
- // @ts-expect-error: @inline is a valid decorator
14
- @inline export function skipWhitespace(srcStart: usize, srcEnd: usize): usize {
13
+ export function skipWhitespace(srcStart: usize, srcEnd: usize): usize {
15
14
  while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
16
15
  return srcStart;
17
16
  }
18
17
 
19
-
20
- @inline export function ensureArrayField<T extends Array<any>>(
21
- fieldPtr: usize,
22
- ): T {
18
+ export function ensureArrayField<T extends Array<any>>(fieldPtr: usize): T {
23
19
  let out = load<T>(fieldPtr);
24
20
  if (!changetype<usize>(out)) {
25
21
  out = changetype<T>(instantiate<T>());
@@ -28,8 +24,7 @@ import { isSpace } from "../../../util";
28
24
  return out;
29
25
  }
30
26
 
31
-
32
- @inline export function ensureArrayFieldAt<T extends Array<any>>(
27
+ export function ensureArrayFieldAt<T extends Array<any>>(
33
28
  dstObj: usize,
34
29
  dstOffset: usize,
35
30
  ): T {
@@ -41,8 +36,7 @@ import { isSpace } from "../../../util";
41
36
  return out;
42
37
  }
43
38
 
44
-
45
- @inline function backslashOrQuoteMask(block: u64): u64 {
39
+ function backslashOrQuoteMask(block: u64): u64 {
46
40
  const b = block ^ 0x005c_005c_005c_005c;
47
41
  const q = block ^ 0x0022_0022_0022_0022;
48
42
  return (
@@ -51,8 +45,7 @@ import { isSpace } from "../../../util";
51
45
  );
52
46
  }
53
47
 
54
-
55
- @inline export function ensureArrayElementSlot<T extends Array<any>>(
48
+ export function ensureArrayElementSlot<T extends Array<any>>(
56
49
  out: T,
57
50
  index: i32,
58
51
  ): usize {
@@ -60,7 +53,7 @@ import { isSpace } from "../../../util";
60
53
  if (out.length < nextLength) {
61
54
  // Grow via `push`, not `out.length = nextLength`. AS's `length=`
62
55
  // setter calls `ensureCapacity(canGrow=false)` which reallocates to
63
- // *exactly* the requested size fine for a one-shot resize, but
56
+ // *exactly* the requested size - fine for a one-shot resize, but
64
57
  // catastrophic in the per-element loop (every push triggers a full
65
58
  // copy of the array, giving O(N²) growth cost). `push` goes through
66
59
  // `canGrow=true`, doubling capacity geometrically as needed.
@@ -8,11 +8,7 @@ import { isSpace } from "../../../util";
8
8
  import { ensureArrayElementSlot, ensureArrayField } from "./shared";
9
9
  import { deserializeStringField_SWAR } from "../string";
10
10
 
11
-
12
- @inline function skipStringArrayWhitespace(
13
- srcStart: usize,
14
- srcEnd: usize,
15
- ): usize {
11
+ function skipStringArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
16
12
  while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
17
13
  return srcStart;
18
14
  }
@@ -41,7 +37,7 @@ function deserializeStringArrayBody<T extends string[]>(
41
37
  // and skip 8 bytes.
42
38
  //
43
39
  // We accept null tokens unconditionally rather than gating on
44
- // `isNullable<valueof<T>>()` AS's `valueof` of a nullable-array
40
+ // `isNullable<valueof<T>>()` - AS's `valueof` of a nullable-array
45
41
  // element type doesn't always preserve the nullable marker through
46
42
  // the dispatcher's `<T>` cast, so the gate would mis-fire for the
47
43
  // very case it's meant to handle. The runtime cost on plain
@@ -80,8 +76,7 @@ function deserializeStringArrayBody<T extends string[]>(
80
76
  throw new Error("Failed to parse JSON!");
81
77
  }
82
78
 
83
-
84
- @inline export function deserializeStringArrayField<T extends string[]>(
79
+ export function deserializeStringArrayField<T extends string[]>(
85
80
  srcStart: usize,
86
81
  srcEnd: usize,
87
82
  fieldPtr: usize,
@@ -2,11 +2,7 @@ import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
2
2
  import { isSpace } from "../../../util";
3
3
  import { ensureArrayElementSlot, ensureArrayField } from "./shared";
4
4
 
5
-
6
- @inline function skipStructArrayWhitespace(
7
- srcStart: usize,
8
- srcEnd: usize,
9
- ): usize {
5
+ function skipStructArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
10
6
  while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
11
7
  return srcStart;
12
8
  }
@@ -100,9 +96,7 @@ function deserializeStructArrayBody<T extends unknown[]>(
100
96
 
101
97
  throw new Error("Failed to parse JSON!");
102
98
  }
103
-
104
-
105
- @inline export function deserializeStructArrayField<T extends unknown[]>(
99
+ export function deserializeStructArrayField<T extends unknown[]>(
106
100
  srcStart: usize,
107
101
  srcEnd: usize,
108
102
  fieldPtr: usize,
@@ -13,9 +13,7 @@ import { deserializeStringArrayField } from "./array/string";
13
13
  import { deserializeStructArrayField } from "./array/struct";
14
14
 
15
15
  export { deserializeArrayField as deserializeArrayField_SWAR };
16
-
17
-
18
- @inline export function deserializeArrayField<T extends unknown[]>(
16
+ export function deserializeArrayField<T extends unknown[]>(
19
17
  srcStart: usize,
20
18
  srcEnd: usize,
21
19
  dstObj: usize,
@@ -12,7 +12,7 @@
12
12
  //
13
13
  // Inspired by Daniel Lemire, "Number parsing at a gigabyte per second" (2021)
14
14
  // and the simdjson `fast_float` implementation. The integer-part loop stays
15
- // scalar most JSON float payloads have 1-3 digit integer parts, so a parse4
15
+ // scalar - most JSON float payloads have 1-3 digit integer parts, so a parse4
16
16
  // stride there pays the wasted-validate cost on every call without saving
17
17
  // enough scalar iterations.
18
18
 
@@ -34,14 +34,11 @@ const ASCII_DOT: u16 = 46;
34
34
  const ASCII_ZERO: u16 = 48;
35
35
  const ASCII_E_UP: u16 = 69;
36
36
  const ASCII_E_LO: u16 = 101;
37
-
38
- // @ts-ignore: inline
39
- @inline export function loadPow10(exp: u32): f64 {
37
+ export function loadPow10(exp: u32): f64 {
40
38
  return load<f64>(POW10_F64_POS + ((<usize>exp) << 3));
41
39
  }
42
40
 
43
- // @ts-ignore: inline
44
- @inline function fallback<T>(srcStart: usize, srcEnd: usize): T {
41
+ function fallback<T>(srcStart: usize, srcEnd: usize): T {
45
42
  const s = ptrToStr(srcStart, srcEnd);
46
43
  // @ts-ignore
47
44
  const type: T = 0;
@@ -50,9 +47,7 @@ const ASCII_E_LO: u16 = 101;
50
47
  // @ts-ignore
51
48
  return <T>(<f32>f32.parse(s));
52
49
  }
53
-
54
- // @ts-ignore: inline
55
- @inline function fallbackField<T extends number>(
50
+ function fallbackField<T extends number>(
56
51
  origStart: usize,
57
52
  end: usize,
58
53
  fieldPtr: usize,
@@ -40,11 +40,7 @@ const ASCII_ZERO: u16 = 48;
40
40
  * @param value The `u64` accumulator, interpreted as a two's-complement
41
41
  * signed integer for narrower types.
42
42
  */
43
- // @ts-expect-error: @inline is a valid decorator
44
- @inline function storeSignedToField<T extends number>(
45
- dstPtr: usize,
46
- value: u64,
47
- ): void {
43
+ function storeSignedToField<T extends number>(dstPtr: usize, value: u64): void {
48
44
  if (sizeof<T>() == 1) {
49
45
  store<i8>(dstPtr, <i8>value);
50
46
  } else if (sizeof<T>() == 2) {
@@ -63,8 +59,7 @@ const ASCII_ZERO: u16 = 48;
63
59
  * @param dstPtr Destination pointer (already includes any field offset).
64
60
  * @param value The `u64` accumulator.
65
61
  */
66
- // @ts-expect-error: @inline is a valid decorator
67
- @inline function storeUnsignedToField<T extends number>(
62
+ function storeUnsignedToField<T extends number>(
68
63
  dstPtr: usize,
69
64
  value: u64,
70
65
  ): void {
@@ -41,11 +41,7 @@ import { hex4_to_u16_swar } from "../../util/swar";
41
41
  * @param dst buffer to write to
42
42
  * @returns number of bytes written
43
43
  */
44
- // @ts-expect-error: @inline is a valid decorator
45
- @inline function copyStringFromSource(
46
- srcStart: usize,
47
- byteLength: usize,
48
- ): string {
44
+ function copyStringFromSource(srcStart: usize, byteLength: usize): string {
49
45
  if (byteLength == 0) return changetype<string>("");
50
46
  const out = __new(byteLength, idof<string>());
51
47
  memory.copy(out, srcStart, byteLength);
@@ -245,7 +241,7 @@ function writeStringToField(
245
241
  // the next unread src pointer.
246
242
  //
247
243
  // HYBRID strategy (validated against the prior run-copy scanner across escape
248
- // densities see __benches__/custom/swar-string-deser-hybrid-h2h: +17–70%):
244
+ // densities - see __benches__/custom/swar-string-deser-hybrid-h2h: +17–70%):
249
245
  // * Escape-bearing block: one optimistic whole-block u64 store copies the
250
246
  // plain prefix for free, then the (scalar-confirmed) escape is decoded.
251
247
  // * Clean block: stream the first one, then if the clean run continues switch
@@ -483,9 +479,14 @@ export function deserializeStringField_SWAR<T extends string | null>(
483
479
  if (srcEnd >= 16) {
484
480
  const srcEnd16 = srcEnd - 16;
485
481
  while (srcStart <= srcEnd16) {
486
- const m0 = backslash_or_quote_mask(load<u64>(srcStart));
487
- const m1 = backslash_or_quote_mask(load<u64>(srcStart, 8));
488
- if ((m0 | m1) != 0) break;
482
+ // Test the first word before loading the second: short values and keys
483
+ // close (or escape) within the first 8 bytes, so this skips the second
484
+ // load on the common case while still skipping 16 bytes when both clean.
485
+ if (backslash_or_quote_mask(load<u64>(srcStart)) != 0) break;
486
+ if (backslash_or_quote_mask(load<u64>(srcStart, 8)) != 0) {
487
+ srcStart += 8;
488
+ break;
489
+ }
489
490
  srcStart += 16;
490
491
  }
491
492
  }
@@ -555,8 +556,7 @@ export function deserializeStringField_SWAR<T extends string | null>(
555
556
  * so callers must confirm the hit scalarly.
556
557
  * Each matching lane sets itself to 0x80.
557
558
  */
558
- // @ts-expect-error: @inline is a valid decorator
559
- @inline function backslash_or_quote_mask(block: u64): u64 {
559
+ function backslash_or_quote_mask(block: u64): u64 {
560
560
  const b = block ^ 0x005c_005c_005c_005c;
561
561
  const q = block ^ 0x0022_0022_0022_0022;
562
562
  return (
@@ -575,8 +575,7 @@ export function deserializeStringField_SWAR<T extends string | null>(
575
575
  *
576
576
  * Each matching lane sets itself to 0x80.
577
577
  */
578
- // @ts-expect-error: @inline is a valid decorator
579
- @inline function backslash_mask(block: u64): u64 {
578
+ function backslash_mask(block: u64): u64 {
580
579
  const b = block ^ 0x005c_005c_005c_005c;
581
580
  const backslash_mask =
582
581
  (b - 0x0001_0001_0001_0001) & ~b & 0x0080_0080_0080_0080;
@@ -597,8 +596,7 @@ export function deserializeStringField_SWAR<T extends string | null>(
597
596
  * WARNING: The low byte of a code unit *may* be a backslash, thus triggering false positives!
598
597
  * This is useful for a hot path where it is possible to detect the false positive scalarly.
599
598
  */
600
- // @ts-expect-error: @inline is a valid decorator
601
- @inline function backslash_mask_unsafe(block: u64): u64 {
599
+ function backslash_mask_unsafe(block: u64): u64 {
602
600
  const b = block ^ 0x005c_005c_005c_005c;
603
601
  const backslash_mask =
604
602
  (b - 0x0001_0001_0001_0001) & ~b & 0x0080_0080_0080_0080;
@@ -6,10 +6,10 @@
6
6
  //
7
7
  // - **No count pass.** TypedArrays have a fixed length at construction,
8
8
  // so the natural approach is to count first then allocate. We tried
9
- // that with a SWAR comma counter it cut the per-element cost but
9
+ // that with a SWAR comma counter - it cut the per-element cost but
10
10
  // kept us ~30% below the top-level `f64[]` path because the count
11
11
  // scan still touched the whole input twice. Instead we allocate
12
- // worst-case (`(srcEnd - srcStart) >> 2 + 1` elements each
12
+ // worst-case (`(srcEnd - srcStart) >> 2 + 1` elements - each
13
13
  // element needs >= "D," = 2 UTF-16 chars = 4 bytes) and `__renew`
14
14
  // the underlying buffer down to the exact byte count after parsing.
15
15
  // The over-allocation peaks at ~2-3× the final size for typical
@@ -46,7 +46,7 @@ import {
46
46
  * Worst-case element count: each element occupies >= 1 digit + 1
47
47
  * delimiter = 2 UTF-16 chars = 4 bytes. So `(srcEnd - srcStart) >> 2`
48
48
  * upper-bounds the count. Allocating to worst-case lets us skip a
49
- * full count pass over the input at the cost of an over-allocated
49
+ * full count pass over the input - at the cost of an over-allocated
50
50
  * underlying buffer that we trim via `__renew` once we know the
51
51
  * actual element count.
52
52
  *
@@ -125,7 +125,7 @@ export function deserializeTypedArray_SWAR<T extends ArrayLike<number>>(
125
125
  // directly: `__renew` the buffer to the actual byte length and
126
126
  // update the view's `byteLength` and `dataStart`. AS's TypedArray
127
127
  // structure has `buffer`, `dataStart` (= buffer), `byteLength`
128
- // (capacity in bytes) in that order same layout as ArrayBufferView.
128
+ // (capacity in bytes) in that order - same layout as ArrayBufferView.
129
129
  const actualCount = i32(<usize>(writePtr - dataStart) / elementSize);
130
130
  if (actualCount != maxElements) {
131
131
  const actualBytes = <usize>actualCount * elementSize;