json-as 1.3.6 → 1.3.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (155) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +1 -1
  3. package/assembly/deserialize/helpers/uint.ts +4 -1
  4. package/assembly/deserialize/index/arbitrary.ts +7 -3
  5. package/assembly/deserialize/index/array.ts +42 -17
  6. package/assembly/deserialize/index/bool.ts +1 -1
  7. package/assembly/deserialize/index/date.ts +1 -1
  8. package/assembly/deserialize/index/float.ts +40 -1
  9. package/assembly/deserialize/index/integer.ts +68 -1
  10. package/assembly/deserialize/index/map.ts +1 -1
  11. package/assembly/deserialize/index/object.ts +1 -1
  12. package/assembly/deserialize/index/raw.ts +1 -1
  13. package/assembly/deserialize/index/set.ts +1 -1
  14. package/assembly/deserialize/index/staticarray.ts +4 -1
  15. package/assembly/deserialize/index/string.ts +32 -4
  16. package/assembly/deserialize/index/struct.ts +1 -1
  17. package/assembly/deserialize/index/typedarray.ts +30 -10
  18. package/assembly/deserialize/index/unsigned.ts +78 -1
  19. package/assembly/deserialize/index.ts +1 -0
  20. package/assembly/deserialize/{simple → naive}/array/arbitrary.ts +24 -5
  21. package/assembly/deserialize/{simple → naive}/array/array.ts +8 -2
  22. package/assembly/deserialize/naive/array/bool.ts +68 -0
  23. package/assembly/deserialize/{simple → naive}/array/box.ts +8 -2
  24. package/assembly/deserialize/naive/array/float.ts +63 -0
  25. package/assembly/deserialize/{simple → naive}/array/generic.ts +14 -7
  26. package/assembly/deserialize/naive/array/integer.ts +86 -0
  27. package/assembly/deserialize/naive/array/map.ts +47 -0
  28. package/assembly/deserialize/naive/array/object.ts +47 -0
  29. package/assembly/deserialize/{simple → naive}/array/raw.ts +34 -7
  30. package/assembly/deserialize/naive/array/string.ts +69 -0
  31. package/assembly/deserialize/naive/array/struct.ts +47 -0
  32. package/assembly/deserialize/{simple → naive}/array.ts +15 -10
  33. package/assembly/deserialize/{simple → naive}/bool.ts +6 -2
  34. package/assembly/deserialize/naive/float.ts +135 -0
  35. package/assembly/deserialize/{simple → naive}/integer.ts +10 -2
  36. package/assembly/deserialize/{simple → naive}/map.ts +106 -27
  37. package/assembly/deserialize/{simple → naive}/object.ts +65 -19
  38. package/assembly/deserialize/{simple → naive}/raw.ts +4 -1
  39. package/assembly/deserialize/{simple → naive}/set.ts +49 -19
  40. package/assembly/deserialize/{simple → naive}/staticarray/array.ts +1 -1
  41. package/assembly/deserialize/{simple → naive}/staticarray/bool.ts +1 -1
  42. package/assembly/deserialize/{simple → naive}/staticarray/float.ts +1 -1
  43. package/assembly/deserialize/{simple → naive}/staticarray/integer.ts +1 -1
  44. package/assembly/deserialize/{simple → naive}/staticarray/string.ts +11 -3
  45. package/assembly/deserialize/{simple → naive}/staticarray/struct.ts +1 -2
  46. package/assembly/deserialize/{simple → naive}/staticarray.ts +68 -18
  47. package/assembly/deserialize/naive/string.ts +199 -0
  48. package/assembly/deserialize/{simple → naive}/struct.ts +5 -1
  49. package/assembly/deserialize/{simple → naive}/typedarray.ts +17 -4
  50. package/assembly/deserialize/{simple → naive}/unsigned.ts +10 -15
  51. package/assembly/deserialize/simd/array/integer.ts +339 -62
  52. package/assembly/deserialize/simd/float.ts +303 -0
  53. package/assembly/deserialize/simd/integer.ts +233 -0
  54. package/assembly/deserialize/simd/string.ts +266 -107
  55. package/assembly/deserialize/swar/array/arbitrary.ts +11 -3
  56. package/assembly/deserialize/swar/array/array.ts +40 -9
  57. package/assembly/deserialize/swar/array/bool.ts +28 -5
  58. package/assembly/deserialize/swar/array/box.ts +11 -3
  59. package/assembly/deserialize/swar/array/float.ts +295 -7
  60. package/assembly/deserialize/swar/array/generic.ts +28 -7
  61. package/assembly/deserialize/swar/array/integer.ts +363 -112
  62. package/assembly/deserialize/swar/array/map.ts +11 -3
  63. package/assembly/deserialize/swar/array/object.ts +37 -25
  64. package/assembly/deserialize/swar/array/raw.ts +11 -3
  65. package/assembly/deserialize/swar/array/shared.ts +63 -14
  66. package/assembly/deserialize/swar/array/string.ts +140 -7
  67. package/assembly/deserialize/swar/array/struct.ts +66 -12
  68. package/assembly/deserialize/swar/array.ts +12 -51
  69. package/assembly/deserialize/swar/float.ts +304 -0
  70. package/assembly/deserialize/swar/integer.ts +246 -0
  71. package/assembly/deserialize/swar/string.ts +213 -294
  72. package/assembly/deserialize/swar/typedarray.ts +224 -0
  73. package/assembly/index.d.ts +3 -1
  74. package/assembly/index.ts +402 -261
  75. package/assembly/serialize/index/array.ts +1 -1
  76. package/assembly/serialize/index/bool.ts +1 -1
  77. package/assembly/serialize/index/date.ts +1 -1
  78. package/assembly/serialize/index/float.ts +5 -1
  79. package/assembly/serialize/index/integer.ts +1 -1
  80. package/assembly/serialize/index/map.ts +1 -1
  81. package/assembly/serialize/index/raw.ts +1 -1
  82. package/assembly/serialize/index/set.ts +1 -1
  83. package/assembly/serialize/index/staticarray.ts +1 -1
  84. package/assembly/serialize/index/string.ts +1 -1
  85. package/assembly/serialize/index/struct.ts +1 -1
  86. package/assembly/serialize/index/typedarray.ts +21 -12
  87. package/assembly/serialize/index.ts +1 -0
  88. package/assembly/serialize/naive/array.ts +351 -0
  89. package/assembly/serialize/{simple → naive}/float.ts +4 -1
  90. package/assembly/serialize/naive/integer.ts +19 -0
  91. package/assembly/serialize/{simple → naive}/map.ts +6 -2
  92. package/assembly/serialize/{simple → naive}/raw.ts +5 -1
  93. package/assembly/serialize/{simple → naive}/set.ts +6 -1
  94. package/assembly/serialize/{simple → naive}/staticarray.ts +6 -1
  95. package/assembly/serialize/{simple → naive}/string.ts +1 -2
  96. package/assembly/serialize/{simple → naive}/typedarray.ts +10 -3
  97. package/assembly/serialize/simd/string.ts +6 -2
  98. package/assembly/serialize/swar/string.ts +15 -141
  99. package/assembly/util/atoi-fast.ts +81 -0
  100. package/assembly/util/concat.ts +5 -1
  101. package/assembly/util/dragonbox-cache.ts +443 -2
  102. package/assembly/util/dragonbox.ts +53 -17
  103. package/assembly/util/itoa-fast.ts +241 -0
  104. package/assembly/util/masks.ts +18 -1
  105. package/assembly/util/parsefloat-fast.ts +167 -0
  106. package/assembly/util/scanValueEnd.ts +78 -0
  107. package/assembly/util/scientific.ts +132 -0
  108. package/assembly/util/simd-int.ts +191 -0
  109. package/assembly/util/snp.ts +4 -1
  110. package/assembly/util/swar-int.ts +248 -0
  111. package/assembly/util/swar.ts +13 -3
  112. package/lib/as-bs.ts +27 -6
  113. package/package.json +15 -11
  114. package/transform/lib/builder.d.ts.map +1 -1
  115. package/transform/lib/builder.js +13 -5
  116. package/transform/lib/builder.js.map +1 -1
  117. package/transform/lib/index.d.ts +5 -0
  118. package/transform/lib/index.d.ts.map +1 -1
  119. package/transform/lib/index.js +1046 -340
  120. package/transform/lib/index.js.map +1 -1
  121. package/transform/lib/linkers/alias.d.ts.map +1 -1
  122. package/transform/lib/linkers/alias.js.map +1 -1
  123. package/transform/lib/linkers/custom.d.ts.map +1 -1
  124. package/transform/lib/linkers/custom.js +3 -2
  125. package/transform/lib/linkers/custom.js.map +1 -1
  126. package/transform/lib/linkers/imports.d.ts.map +1 -1
  127. package/transform/lib/linkers/imports.js.map +1 -1
  128. package/transform/lib/types.d.ts.map +1 -1
  129. package/transform/lib/types.js +54 -16
  130. package/transform/lib/types.js.map +1 -1
  131. package/transform/lib/util.d.ts.map +1 -1
  132. package/transform/lib/util.js +1 -1
  133. package/transform/lib/util.js.map +1 -1
  134. package/transform/lib/visitor.d.ts.map +1 -1
  135. package/transform/lib/visitor.js +2 -1
  136. package/transform/lib/visitor.js.map +1 -1
  137. package/assembly/custom/util.ts +0 -310
  138. package/assembly/deserialize/simple/arbitrary.ts +0 -23
  139. package/assembly/deserialize/simple/array/bool.ts +0 -17
  140. package/assembly/deserialize/simple/array/float.ts +0 -28
  141. package/assembly/deserialize/simple/array/integer.ts +0 -27
  142. package/assembly/deserialize/simple/array/map.ts +0 -28
  143. package/assembly/deserialize/simple/array/object.ts +0 -28
  144. package/assembly/deserialize/simple/array/string.ts +0 -23
  145. package/assembly/deserialize/simple/array/struct.ts +0 -28
  146. package/assembly/deserialize/simple/float.ts +0 -201
  147. package/assembly/deserialize/simple/string.ts +0 -132
  148. package/assembly/serialize/simple/arbitrary.ts +0 -79
  149. package/assembly/serialize/simple/array.ts +0 -86
  150. package/assembly/serialize/simple/integer.ts +0 -20
  151. package/assembly/serialize/simple/object.ts +0 -42
  152. /package/assembly/deserialize/{simple → naive}/date.ts +0 -0
  153. /package/assembly/serialize/{simple → naive}/bool.ts +0 -0
  154. /package/assembly/serialize/{simple → naive}/date.ts +0 -0
  155. /package/assembly/serialize/{simple → naive}/struct.ts +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,50 @@
1
1
  # Changelog
2
2
 
3
+ ## 2026-06-01 - v1.3.8
4
+
5
+ - feat(strict): the **naive value-path deserializers now reject malformed JSON** instead of silently accepting it (RFC 8259). Numbers (`deserializeFloat_NAIVE`) are validated against the JSON number grammar — no leading zeros, bare `-`, empty fraction/exponent, `+` sign, hex, or trailing garbage (the `NaN`/`Infinity` extension is preserved). Strings (`deserializeString_NAIVE`) reject unescaped control chars, illegal/incomplete escapes, non-hex `\u`, and missing surrounding quotes. The scalar/typed **array scanners** (`f64[]`/`i*[]`/`u*[]`/`bool[]`/`string[]`) were rewritten as strict state machines: `[`-framed, single-comma separated, no leading/trailing/doubled commas, no inter-value garbage. On the JSONTestSuite `n_` corpus (parsed against concrete types, one spec per case in `assembly/__tests__/rfc/`), naive now **rejects 179/188** reject-cases — up from 55 — the remaining 9 being the intentional `NaN`/`Infinity` extension (5) plus arbitrary-`JSON.Value`/formfeed/struct-trailing edges (4). Non-object rejects compile to uncatchable aborts until try-as traces the value path; object rejects (struct `__DESERIALIZE_SLOW`) are catchable today. Main suite (10,557) and curated rfc suite (801) stay green across all three modes
6
+ - feat(strict): under `JSON_STRICT=true` the generated `__DESERIALIZE_SLOW` object scanner now rejects malformed key positions — an unquoted / single-quoted / numeric key, or garbage after a value with no separating comma — by requiring the first non-space char at each key position to be a string-opening quote, `,`, or `}` (`transform/src/index.ts`). Combined with the existing strict unknown-key-by-value-type guards, this brings RFC 8259 `n_object` coverage to **28/28** (all three modes) in the conformance suite (`assembly/__tests__/rfc/struct-reject.spec.ts`, parsed against one rich all-value-type schema so every guard is generated). Gated on `JSON_STRICT`, so the default lenient build is unchanged
7
+ - test: RFC 8259 accept-side coverage is now complete — all **95** `y_` (must-accept) cases parse via the dynamic `JSON.Value` type (`assembly/__tests__/rfc/accept.spec.ts`) and **31/35** `i_` (impl-defined) cases (`impl.spec.ts`), all green across NAIVE/SWAR/SIMD. The 4 deferred `i_` cases are uncatchable traps (raw UTF-16/BOM byte input, depth-500 nesting). RFC suite now 804 tests across 3 modes
8
+ - fix: tier-1 (the exact byte-template fast path) over-matched whitespace after a colon. For input like `{"k": v}` (a space after the colon but otherwise compact — e.g. Python's `json.dumps` default), tier-1 matched the minified `{"k":` prefix then read the value at a fixed offset that the space shifted, feeding a misaligned pointer to the value deserializer — string fields `abort`ed ("Expected leading quote") and float fields hit `unreachable`. Tier-1 now checks for whitespace after the colon and bails to the (whitespace-tolerant) tier-2 instead of mis-reading. Surfaced via the RFC 8259 conformance work
9
+ - fix: add `bs.reset()` to `lib/as-bs.ts` — restores the shared serialization buffer to a clean state (offset + pause stacks) after a throw aborts a (de)serialize mid-flight, so the next op isn't corrupted by a dangling offset
10
+ - test: begin RFC 8259 conformance coverage from nst/JSONTestSuite as typed specs (`assembly/__tests__/rfc-object.spec.ts`, accept/round-trip cases; see `RFC-DEFERRED.md` for status and the reject-case blocker)
11
+
12
+ - perf: pretty-printed JSON now stays on the fast path instead of collapsing to the naive scalar deserializer (the "canada 10×"). The transform's `__DESERIALIZE_FAST` gained a whitespace-tolerant **tier 2**: when the exact packed-`u64` byte-template (tier 1) misses on inter-token whitespace, instead of dumping the whole object to `__DESERIALIZE_SLOW` (~5× slower) it re-matches per field using the same packed key constants but skips whitespace between every structural token (`{`, key, `:`, value, `,`, `}`). Minified input never reaches tier 2, so tier 1 keeps peak speed. canada *pretty* deserialize **~139 → ~1,180 MB/s (SWAR)** / **~1,150 MB/s (SIMD)** — on par with minified; on a value-heavy struct tier 2 costs ~1% over tier 1 (worst case ~2× on a 16-field all-primitive struct). The SWAR array bodies (`deserializeArrayArrayBody`, `deserializeFloatArrayBody`, the inline string/object array element loops) were made whitespace-tolerant to match
13
+ - fix: `JSON.parse` / `JSON.__deserialize` accepted a fast-path result only when it consumed *exactly* to `srcEnd`. Pretty files end in a newline, so tier 2 stopped just past the closing `}` and the **entire object silently fell back to the slow path despite a successful parse** — this was the real cause of the pretty penalty (tier 2 alone only moved canada 139 → 220 MB/s; this fix took 220 → ~1,180). The fast path is now accepted when only trailing whitespace remains (`skipWhitespace(fastEnd, srcEnd) == srcEnd`)
14
+ - perf: optional-field structs (`@omitnull` / `@omitif`) get a whitespace-tolerant tier 2 as well — a probe-and-commit variant that matches each field's key with a lookahead cursor and only commits past the separator + key + `:` when it matches, so omitted fields are skipped without consuming input. Previously these structs fell to slow on *any* whitespace. ~1.18× of tier-1 on a pretty 7-field optional struct, vs the ~5× slow path it used to hit
15
+ - fix: enforce a uniform whitespace contract across **every** deserialize handler in all three modes (field and non-field): the entry points (`JSON.parse` / `JSON.__deserialize`) skip leading whitespace once, and every handler now assumes `srcStart` points at the first non-whitespace char and never re-skips it. Removed ~17 now-redundant leading-whitespace trims from composite handlers (naive `object`/`map`/`set` + array/staticarray composites; SWAR array bodies); composites still skip whitespace *internally* since they are the caller for their child values/keys
16
+ - fix: Map and Set struct **fields** did not handle internal whitespace — `deserializeMapBody` had no inter-token skips at all, and `deserializeSetDirect`'s skips were gated behind an `allowWhitespace` flag the field path left `false`. Concrete structs with `Map`/`Set` fields parsed from pretty input silently misparsed (only the generic-box slow path that existing tests exercised handled it). Both now skip whitespace unconditionally
17
+ - fix: `scanValueEnd` (both `util/scanValueEnd.ts` and `swar/array/shared.ts`) now stops a scalar value at trailing whitespace, not just `,`/`]`/`}`. The returned range previously included trailing spaces (e.g. `"1 "`), which the SWAR/SIMD scalar number parsers — assuming `[srcStart,srcEnd)` is the exact value — misparsed (a whitespaced map value `{ "p" : 1 }` deserialized to `-6`). Now matches the already-correct `JSON.Util.scanValueEnd`
18
+ - bench: add `assembly/__benches__/custom/tier-h2h.bench.ts` — tier-1 (minified) vs tier-2 (one leading space, isolating pure path overhead) vs tier-2 (fully pretty), across a value-heavy `medium` struct, a 16-field all-primitive worst case, and an optional-field struct (with a non-optional twin to isolate the `seenAny` tier-1 overhead)
19
+ - test: add concrete-struct whitespace coverage to `whitespace.spec.ts` — tier-2 fast-path structs (nested objects, float/string/object arrays), the optional-field probe path, top-level leading whitespace for every scalar/array type, and one struct touching every field-handler family (scalar/string/nested-object/array/Map/Set) parsed from heavily-whitespaced (leading + internal + trailing) input
20
+ - perf: fully vectorize the SIMD string **field** deserializer. `deserializeStringField_SIMD` previously bailed to `deserializeStringField_SWAR` on the first backslash, so SIMD mode got zero vectorization on escaped struct fields. It now runs a HYBRID escaped scanner — v128 scan for `"`/`\`; escape-bearing blocks use a single whole-block v128 store to copy the plain prefix for free, then decode the escape; clean runs stream the first block then bulk-`memcpy` the remainder (bandwidth-optimal on long sparse runs, no large-input cliff). Validated against run-copy and pure-stream variants across escape densities; ~+10–20% on common small/moderate/sparse cases
21
+ - perf: replace the SWAR string **field** scanner (formerly the run-copy `deserializeEscapedStringScan_SWAR_SplitTuned`) with the same HYBRID strategy, renamed `deserializeEscapedStringField_SWAR`. On the `swar-string-deser-hybrid-h2h` bench: +50–70% dense, +17–22% moderate, +37–48% sparse — the prior run-copy scanner read each plain run twice (scan + `memcpy`)
22
+ - perf/fix: the standalone whole-value scanners (`deserializeString_SWAR` / `deserializeString_SIMD`) also move to the HYBRID strategy, replacing the older "overflow-pattern" SIMD code. Also fixes a latent out-of-bounds read on short escaped strings — the unguarded `srcEnd - 16` underflow is now guarded with `srcEnd >= 16`
23
+ - refactor: route string-field deserialization through the `deserialize/index/string.ts` dispatcher (`deserializeStringField`), matching the integer/unsigned/float field helpers, instead of the transform hard-coding `deserializeStringField_SWAR` / `_SIMD` by `codegenMode`. Adds a real `deserializeStringField_NAIVE` (NAIVE builds previously deserialized struct string fields with the SWAR field scanner) and unifies all three mode variants on the 3-param `(srcStart, srcEnd, dstFieldPtr)` signature
24
+ - refactor: rename the `simple/` (de)serialize implementation directories to `naive/` and carry the mode suffix on the source functions (`*_NAIVE` / `*_SWAR` / `*_SIMD`), eliminating the `as X_NAIVE` import aliases at call sites
25
+ - fix(build): drop `@inline` from `deserializeStringField_SIMD`. As an `@inline` entry it let binaryen inline the loop-bearing escaped scanner into every struct string-field call site, exploding `large` SIMD compile ~24× under `--converge` (4 s → 99 s, 221 KB → 555 KB wasm). Kept as a single shared function (matching the already-non-inline `deserializeStringField_SWAR`); `large` SIMD build is back to ~4 s / 138 KB with unchanged runtime
26
+ - bench: add string-perf benches under `assembly/__benches__/custom/` — `simd-string-deser-scratch-h2h`, `simd-string-deser-variants-h2h`, `simd-string-deser-standalone-h2h`, and `swar-string-deser-hybrid-h2h` (escape-density head-to-heads for the HYBRID scanners); `serialize-string-modes-h2h` (NAIVE/SWAR/SIMD landscape, confirms streaming serialize already beats run-copy 2–6× so no HYBRID change is warranted); and `serialize-string-safety-h2h`, which checks SWAR/SIMD serialize byte-for-byte against NAIVE across 430 adversarial escape/surrogate/boundary-length inputs — confirming the SIMD `store<v128>` overflow stores stay within buffer slack
27
+
28
+ ## 2026-05-20
29
+
30
+ - perf: unify the SWAR 4-digit kernel across array entry points. `swar/array/integer.ts` and `swar/array/float.ts` now import `parse4Digits_PairMul` from `util/swar-int.ts` (Lemire-style PairMul) instead of defining a local Baseline variant. `JSON.parse<u32[]>` ~27.4 ms/op @ 64 MiB (up ~17% from ~33 ms) and `JSON.parse<Int32Array>` ~29.5 ms/op @ 64 MiB (up ~17% from ~35.6 ms). Float wins are smaller (~2-4%) since dragonbox dominates
31
+ - perf: `deserializeIntegerArrayInto` (the `Array<int>` struct-field path) inlines `parseSignedIntegerSWAR` / `parseUnsignedIntegerSWAR` directly instead of routing through the per-element `deserializeIntegerField` / `deserializeUnsignedField` dispatchers. The element parser is now identical across all three call sites (top-level `JSON.parse<T[]>`, `swar/typedarray.ts`, struct field). Tuning note: array path uses parse4 + scalar even for unsigned — parse8 wins for the single-token struct field where the digit run is aligned, but in arrays the `,` separator lands mid-load and forces a wasted validate, costing ~23% on `u32-64mib`
32
+ - refactor: fold byte-identical `deserializeObjectArrayInto` and `deserializeStructArrayInto` into one shared helper in `swar/array/shared.ts`. The two outer wrappers retain their type-specific names and now delegate; net ~130 lines removed
33
+ - bench: add `assembly/__benches__/custom/u32-array-field.bench.ts` covering the unsigned struct-field path (no existing bench targeted `deserializeIntegerArrayInto` for `Array<u32>`); add `uint8array-512mib.bench.ts` for round-trip throughput at the largest payload AS's GC allows (BLOCK_MAXSIZE = (1<<30)-16, so a managed UTF-16 string maxes out near 1 GiB = 512 MiB of UTF-8). Built via a single `__new` + `memory.copy` to avoid concat/slice peaking past the wasm cap; the deserialize bench drops the source string and runs `__collect()` before the serialize bench so the per-op ~1 GiB output fits. SWAR ~1,350 MB/s deserialize, ~1,130 MB/s serialize at 512 MiB
34
+
35
+ ## 2026-05-19 - 1.3.7
36
+
37
+ - fix: anchor the json-as transform's import-path rewrite on the *trailing* `json-as` directory via `lastIndexOf`. Under pnpm/yarn-pnp the install path is `.pnpm/json-as@<ver>_<hash>/node_modules/json-as`; the previous `indexOf` matched the version-qualified store directory and leaked it into every emitted runtime import, crashing AS during `program.initialize` (AssertionError at `program.ts:1183`). Flat-npm consumers were unaffected. Test coverage added in `transform/__tests__/normalize-base-rel.test.mjs` (8 layout cases, including pnpm with version+hash). Runnable via `npm run test:transform`
38
+ - perf: `JSON.parse<u8[]>` SWAR / SIMD throughput at ~64 MiB up to ~2.8 GB/s (SWAR) and ~4.6 GB/s (SIMD), up from ~750 MB/s in both modes. SWAR cascades three u64 fast paths (3-/2-/1-digit single-element); SIMD batch-decodes 2 elements per v128 load via `i32x4.dot_i16x8_s` across six (a, b)-width patterns (0x88 / 0x48 / 0x44 / 0x24 / 0x28 / 0x22), falling back to the SWAR cascade for the remainder
39
+ - perf: `JSON.parse<u32[]>` (and other wider integer arrays) now use worst-case `srcLen >> 2` pre-allocation + unchecked direct stores via a `writePtr`, eliminating `Array.push`'s per-element capacity check. ~2.2 GB/s SWAR, ~2.7 GB/s SIMD on the u32-64mib bench (up from ~1.2 GB/s); the same pattern applies to all integer widths. Parse helpers (`parseSignedIntegerSWAR`, `parseUnsignedIntegerSWAR`, signed/unsigned scalar and SIMD variants) now take a `slot: usize` instead of `out: T` and store via `storeSignedInteger` / `storeUnsignedInteger`
40
+ - perf: `JSON.parse<bool[]>` ~10 GB/s across all modes (up from ~1.5 GB/s). Same pre-allocation + unchecked-store pattern; the per-element u64 magic-constant token match stays
41
+ - perf: `JSON.parse<f64[]>` / `JSON.parse<f32[]>` ~870 MB/s, up from ~735 MB/s, via pre-allocation + unchecked stores. The remaining cost is in `f64.parse` itself (Grisu)
42
+ - perf: `JSON.stringify<u8[]>` ~5.1 GB/s, up from ~680 MB/s (7.4×), via a 256-entry UTF-16 LUT that packs the `"DDD,"` representation of each `u8` value into a single u64 plus a parallel byte-count LUT. Trailing comma is overwritten by `]` after the loop. `JSON.stringify<i8[]>` follows the same path with a peeled `-`
43
+ - perf: `JSON.stringify<bool[]>` ~4.6 GB/s, up from ~1.1 GB/s (4.2×), by folding the per-element comma into the element write (`store<u64>(TRUE_COMMA_LO) + store<u16>(0x002c, 8)` for `"true,"`; `store<u64>(FALSE_COMMA_LO) + store<u32>(0x002c_0065, 8)` for `"false,"`)
44
+ - perf: replace AS std's `itoa_buffered` in the integer serialize path with a jeaiii-style forward-writing itoa (`assembly/util/itoa-fast.ts`). The new path uses a 100-entry UTF-16 digit-pair LUT, computes digit count from a width-ladder so `decimalCount32` is no longer a separate call, and `@inline`s into the array loop. `JSON.stringify<i32[]>` ~1.8 GB/s, up from ~1.35 GB/s. Per-width gains range from 1.0× (1-digit) to 3.4× (2-digit) in the head-to-head bench
45
+ - bench: new throughput benches at `assembly/__benches__/custom/{u8,u32,f64,bool}-64mib.bench.ts` for deserialize and `serialize-primitive-arrays.bench.ts` for stringify; head-to-head benches `itoa-h2h.bench.ts` (`itoa_buffered` vs jeaiii) and `parsefloat-h2h.bench.ts` (existing parser vs Lemire-lite at `util/parsefloat-fast.ts` — research, not wired in due to test expectations encoding the existing parser's rounding behaviour)
46
+ - deps: pin `as-test` to `^1.1.10` and `try-as` to `^1.0.1`
47
+
3
48
  ## 2026-05-14 - 1.3.6
4
49
 
5
50
  - fix: prevent cross-lane borrow propagation in `detect_escapable_u64_swar_safe` and `detect_escapable_u64_swar_unsafe` by ORing `0x0100_0100_0100_0100` into each 16-bit lane before the SWAR subtraction steps, eliminating corrupted output (`\u00\0\0`) when a NUL or other control character precedes a printable character in SWAR string serialization
package/README.md CHANGED
@@ -493,7 +493,7 @@ The `json-as` library is engineered for **multi-GB/s processing speeds**, levera
493
493
 
494
494
  ### Comparison to JavaScript
495
495
 
496
- The following charts compare JSON-AS (both SWAR and SIMD variants) against JavaScript's native `JSON` implementation. The published charts are generated locally and pushed to the `docs` branch.
496
+ The following charts compare JSON-AS against JavaScript's native `JSON` implementation. It's as fair as possible and runs on V8's turboshaft optimizer. The published charts are generated locally and pushed to the `docs` branch.
497
497
 
498
498
  > Note: Benchmarks reflect the **latest version**. Older versions may show different performance.
499
499
  >
@@ -1,4 +1,7 @@
1
- export function deserializeUintScan<T extends number>(src: usize, dst: usize): usize {
1
+ export function deserializeUintScan<T extends number>(
2
+ src: usize,
3
+ dst: usize,
4
+ ): usize {
2
5
  let digit = <T>load<u16>(src) - 48;
3
6
  if (digit > 9) abort("Found invalid digit");
4
7
  let val = digit;
@@ -1,19 +1,23 @@
1
1
  import { JSON } from "../..";
2
2
  import { deserializeArray } from "./array";
3
3
  import { deserializeBoolean } from "./bool";
4
- import { deserializeFloat } from "../simple/float";
4
+ import { deserializeFloat_NAIVE } from "../naive/float";
5
5
  import { deserializeObject } from "./object";
6
6
  import { deserializeString } from "./string";
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
17
  } else if (firstChar == BRACE_LEFT) {
14
18
  return JSON.Value.from(deserializeObject(srcStart, srcEnd, 0));
15
19
  } else if (firstChar - 48 <= 9 || firstChar == 45) {
16
- return JSON.Value.from(deserializeFloat<f64>(srcStart, srcEnd));
20
+ return JSON.Value.from(deserializeFloat_NAIVE<f64>(srcStart, srcEnd));
17
21
  } else if (firstChar == BRACKET_LEFT) {
18
22
  return JSON.Value.from(deserializeArray<JSON.Value[]>(srcStart, srcEnd, 0));
19
23
  } else if (firstChar == 116 || firstChar == 102) {
@@ -1,24 +1,42 @@
1
1
  import { JSON, JSONMode } from "../..";
2
- import { deserializeArbitraryArray } from "../simple/array/arbitrary";
3
- import { deserializeArrayArray } from "../simple/array/array";
4
- import { deserializeBooleanArray } from "../simple/array/bool";
5
- import { deserializeBoxArray } from "../simple/array/box";
6
- import { deserializeFloatArray } from "../simple/array/float";
7
- import { deserializeGenericArray } from "../simple/array/generic";
8
- import { deserializeIntegerArray as deserializeIntegerArray_NAIVE } from "../simple/array/integer";
9
- import { deserializeMapArray } from "../simple/array/map";
10
- import { deserializeObjectArray } from "../simple/array/object";
11
- import { deserializeRawArray } from "../simple/array/raw";
12
- import { deserializeStringArray } from "../simple/array/string";
13
- import { deserializeStructArray } from "../simple/array/struct";
2
+ import { deserializeArbitraryArray } from "../naive/array/arbitrary";
3
+ import { deserializeArrayArray } from "../naive/array/array";
4
+ import { deserializeBooleanArray } from "../naive/array/bool";
5
+ import { deserializeBoxArray } from "../naive/array/box";
6
+ import { deserializeFloatArray_NAIVE } from "../naive/array/float";
7
+ import { deserializeGenericArray } from "../naive/array/generic";
8
+ import { deserializeIntegerArray_NAIVE } from "../naive/array/integer";
9
+ import { deserializeMapArray } from "../naive/array/map";
10
+ import { deserializeObjectArray } from "../naive/array/object";
11
+ import { deserializeRawArray } from "../naive/array/raw";
12
+ import { deserializeStringArray_NAIVE } from "../naive/array/string";
13
+ import { deserializeStructArray } from "../naive/array/struct";
14
14
  import { deserializeIntegerArray_SIMD } from "../simd/array/integer";
15
15
  import { deserializeIntegerArray_SWAR } from "../swar/array/integer";
16
+ import { deserializeFloatArray_SWAR } from "../swar/array/float";
17
+ import { deserializeStringArray_SWAR } from "../swar/array/string";
16
18
 
17
- export { deserializeArrayField, deserializeArrayField as deserializeArrayField_SWAR } from "../swar/array";
19
+ export {
20
+ deserializeArrayField,
21
+ deserializeArrayField as deserializeArrayField_SWAR,
22
+ } from "../swar/array";
18
23
 
19
- export function deserializeArray<T extends unknown[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
24
+ export function deserializeArray<T extends unknown[]>(
25
+ srcStart: usize,
26
+ srcEnd: usize,
27
+ dst: usize,
28
+ ): T {
20
29
  if (isString<valueof<T>>()) {
21
- return <T>deserializeStringArray(srcStart, srcEnd, dst);
30
+ // SWAR/SIMD routes through the same `Into` helper used by the
31
+ // struct-field path; that helper carries the `null` token fast path
32
+ // for `(string | null)[]`. NAIVE keeps the naive scanner, which
33
+ // currently only handles non-nullable string arrays. The naive
34
+ // variant's static `string[]` return type is bit-identical to
35
+ // `(string | null)[]` so `changetype<T>` is a runtime no-op.
36
+ if (JSON_MODE == JSONMode.SWAR || JSON_MODE == JSONMode.SIMD) {
37
+ return deserializeStringArray_SWAR<T>(srcStart, srcEnd, dst);
38
+ }
39
+ return changetype<T>(deserializeStringArray_NAIVE(srcStart, srcEnd, dst));
22
40
  } else if (isBoolean<valueof<T>>()) {
23
41
  return deserializeBooleanArray<T>(srcStart, srcEnd, dst);
24
42
  } else if (isInteger<valueof<T>>()) {
@@ -33,7 +51,11 @@ export function deserializeArray<T extends unknown[]>(srcStart: usize, srcEnd: u
33
51
  return deserializeIntegerArray_NAIVE<T>(srcStart, srcEnd, dst);
34
52
  }
35
53
  } else if (isFloat<valueof<T>>()) {
36
- return deserializeFloatArray<T>(srcStart, srcEnd, dst);
54
+ if (JSON_MODE == JSONMode.SWAR || JSON_MODE == JSONMode.SIMD) {
55
+ // @ts-ignore: float array branch
56
+ return deserializeFloatArray_SWAR<T>(srcStart, srcEnd, dst);
57
+ }
58
+ return deserializeFloatArray_NAIVE<T>(srcStart, srcEnd, dst);
37
59
  } else if (isArray<valueof<T>>()) {
38
60
  return deserializeArrayArray<T>(srcStart, srcEnd, dst);
39
61
  } else if (isManaged<valueof<T>>() || isReference<valueof<T>>()) {
@@ -56,7 +78,10 @@ export function deserializeArray<T extends unknown[]>(srcStart: usize, srcEnd: u
56
78
  } else if (isDefined(type.__DESERIALIZE_CUSTOM)) {
57
79
  return deserializeStructArray<T>(srcStart, srcEnd, dst);
58
80
  // @ts-ignore: defined by transform
59
- } else if (isDefined(type.__DESERIALIZE_SLOW) || isDefined(type.__DESERIALIZE_FAST)) {
81
+ } else if (
82
+ isDefined(type.__DESERIALIZE_SLOW) ||
83
+ isDefined(type.__DESERIALIZE_FAST)
84
+ ) {
60
85
  return deserializeStructArray<T>(srcStart, srcEnd, dst);
61
86
  }
62
87
  throw new Error("Could not parse array of type " + nameof<T>() + "!");
@@ -1 +1 @@
1
- export { deserializeBoolean } from "../simple/bool";
1
+ export { deserializeBoolean } from "../naive/bool";
@@ -1 +1 @@
1
- export { deserializeDate } from "../simple/date";
1
+ export { deserializeDate } from "../naive/date";
@@ -1 +1,40 @@
1
- export { deserializeFloat } from "../simple/float";
1
+ import { JSONMode } from "../..";
2
+ import {
3
+ deserializeFloat_NAIVE,
4
+ deserializeFloatField_NAIVE,
5
+ } from "../naive/float";
6
+ import {
7
+ deserializeFloat_SWAR,
8
+ deserializeFloatField_SWAR,
9
+ } from "../swar/float";
10
+ import {
11
+ deserializeFloat_SIMD,
12
+ deserializeFloatField_SIMD,
13
+ } from "../simd/float";
14
+
15
+ // @ts-ignore: inline
16
+ @inline export function deserializeFloat<T>(srcStart: usize, srcEnd: usize): T {
17
+ if (JSON_MODE == JSONMode.SIMD) {
18
+ return deserializeFloat_SIMD<T>(srcStart, srcEnd);
19
+ } else if (JSON_MODE == JSONMode.NAIVE) {
20
+ return deserializeFloat_NAIVE<T>(srcStart, srcEnd);
21
+ } else {
22
+ return deserializeFloat_SWAR<T>(srcStart, srcEnd);
23
+ }
24
+ }
25
+
26
+ // @ts-ignore: inline
27
+ @inline export function deserializeFloatField<T extends number>(
28
+ srcStart: usize,
29
+ srcEnd: usize,
30
+ dstObj: usize,
31
+ dstOffset: usize = 0,
32
+ ): usize {
33
+ if (JSON_MODE == JSONMode.SIMD) {
34
+ return deserializeFloatField_SIMD<T>(srcStart, srcEnd, dstObj, dstOffset);
35
+ } else if (JSON_MODE == JSONMode.NAIVE) {
36
+ return deserializeFloatField_NAIVE<T>(srcStart, srcEnd, dstObj, dstOffset);
37
+ } else {
38
+ return deserializeFloatField_SWAR<T>(srcStart, srcEnd, dstObj, dstOffset);
39
+ }
40
+ }
@@ -1 +1,68 @@
1
- export { deserializeInteger } from "../simple/integer";
1
+ import { JSONMode } from "../..";
2
+ import {
3
+ deserializeInteger_NAIVE,
4
+ deserializeIntegerField_NAIVE,
5
+ } from "../naive/integer";
6
+ import {
7
+ deserializeInteger_SWAR,
8
+ deserializeIntegerField_SWAR,
9
+ } from "../swar/integer";
10
+ import {
11
+ deserializeInteger_SIMD,
12
+ deserializeIntegerField_SIMD,
13
+ } from "../simd/integer";
14
+
15
+ /**
16
+ * Compile-time dispatch for {@link deserializeInteger_NAIVE},
17
+ * {@link deserializeInteger_SWAR}, and {@link deserializeInteger_SIMD}
18
+ * based on `JSON_MODE`.
19
+ *
20
+ * @param srcStart Pointer to the first UTF-16 code unit.
21
+ * @param srcEnd Pointer just past the last code unit.
22
+ * @returns The parsed value, truncated to `T`.
23
+ */
24
+ // @ts-expect-error: @inline is a valid decorator
25
+ @inline export function deserializeInteger<T extends number>(
26
+ srcStart: usize,
27
+ srcEnd: usize,
28
+ ): T {
29
+ if (JSON_MODE == JSONMode.SIMD) {
30
+ return deserializeInteger_SIMD<T>(srcStart, srcEnd);
31
+ } else if (JSON_MODE == JSONMode.NAIVE) {
32
+ return deserializeInteger_NAIVE<T>(srcStart, srcEnd);
33
+ } else {
34
+ return deserializeInteger_SWAR<T>(srcStart, srcEnd);
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Compile-time dispatch for {@link deserializeIntegerField_NAIVE},
40
+ * {@link deserializeIntegerField_SWAR}, and
41
+ * {@link deserializeIntegerField_SIMD} based on `JSON_MODE`.
42
+ *
43
+ * @param srcStart Pointer to the first UTF-16 code unit.
44
+ * @param srcEnd Pointer just past the last code unit.
45
+ * @param dstObj Destination object pointer.
46
+ * @param dstOffset Byte offset of the field within `dstObj`.
47
+ * @returns The source position immediately after the last digit consumed.
48
+ */
49
+ // @ts-expect-error: @inline is a valid decorator
50
+ @inline export function deserializeIntegerField<T extends number>(
51
+ srcStart: usize,
52
+ srcEnd: usize,
53
+ dstObj: usize,
54
+ dstOffset: usize = 0,
55
+ ): usize {
56
+ if (JSON_MODE == JSONMode.SIMD) {
57
+ return deserializeIntegerField_SIMD<T>(srcStart, srcEnd, dstObj, dstOffset);
58
+ } else if (JSON_MODE == JSONMode.NAIVE) {
59
+ return deserializeIntegerField_NAIVE<T>(
60
+ srcStart,
61
+ srcEnd,
62
+ dstObj,
63
+ dstOffset,
64
+ );
65
+ } else {
66
+ return deserializeIntegerField_SWAR<T>(srcStart, srcEnd, dstObj, dstOffset);
67
+ }
68
+ }
@@ -1 +1 @@
1
- export { deserializeMap } from "../simple/map";
1
+ export { deserializeMap, deserializeMapField } from "../naive/map";
@@ -1 +1 @@
1
- export { deserializeObject } from "../simple/object";
1
+ export { deserializeObject } from "../naive/object";
@@ -1 +1 @@
1
- export { deserializeRaw } from "../simple/raw";
1
+ export { deserializeRaw } from "../naive/raw";
@@ -1 +1 @@
1
- export { deserializeSet } from "../simple/set";
1
+ export { deserializeSet, deserializeSetField } from "../naive/set";
@@ -1 +1,4 @@
1
- export { deserializeStaticArray } from "../simple/staticarray";
1
+ export {
2
+ deserializeStaticArray,
3
+ deserializeStaticArrayField,
4
+ } from "../naive/staticarray";
@@ -1,10 +1,22 @@
1
1
  import { JSONMode } from "../..";
2
- import { deserializeString as deserializeString_NAIVE } from "../simple/string";
3
- import { deserializeString_SIMD } from "../simd/string";
4
- import { deserializeString_SWAR } from "../swar/string";
2
+ import {
3
+ deserializeString_NAIVE,
4
+ deserializeStringField_NAIVE,
5
+ } from "../naive/string";
6
+ import {
7
+ deserializeString_SIMD,
8
+ deserializeStringField_SIMD,
9
+ } from "../simd/string";
10
+ import {
11
+ deserializeString_SWAR,
12
+ deserializeStringField_SWAR,
13
+ } from "../swar/string";
5
14
 
6
15
 
7
- @inline export function deserializeString(srcStart: usize, srcEnd: usize): string {
16
+ @inline export function deserializeString(
17
+ srcStart: usize,
18
+ srcEnd: usize,
19
+ ): string {
8
20
  if (JSON_MODE == JSONMode.SIMD) {
9
21
  return deserializeString_SIMD(srcStart, srcEnd);
10
22
  } else if (JSON_MODE == JSONMode.NAIVE) {
@@ -13,3 +25,19 @@ import { deserializeString_SWAR } from "../swar/string";
13
25
  return deserializeString_SWAR(srcStart, srcEnd);
14
26
  }
15
27
  }
28
+
29
+
30
+ @inline export function deserializeStringField<T extends string | null>(
31
+ srcStart: usize,
32
+ srcEnd: usize,
33
+ dstObj: usize,
34
+ dstOffset: usize = 0,
35
+ ): usize {
36
+ if (JSON_MODE == JSONMode.SIMD) {
37
+ return deserializeStringField_SIMD<T>(srcStart, srcEnd, dstObj, dstOffset);
38
+ } else if (JSON_MODE == JSONMode.NAIVE) {
39
+ return deserializeStringField_NAIVE<T>(srcStart, srcEnd, dstObj, dstOffset);
40
+ } else {
41
+ return deserializeStringField_SWAR<T>(srcStart, srcEnd, dstObj, dstOffset);
42
+ }
43
+ }
@@ -1 +1 @@
1
- export { deserializeStruct } from "../simple/struct";
1
+ export { deserializeStruct } from "../naive/struct";
@@ -1,15 +1,35 @@
1
- import { bytes } from "../../util";
2
- export { deserializeArrayBuffer, deserializeTypedArray } from "../simple/typedarray";
3
- import { deserializeArrayBuffer, deserializeTypedArray } from "../simple/typedarray";
1
+ import { JSONMode } from "../..";
2
+ import {
3
+ deserializeArrayBuffer_NAIVE,
4
+ deserializeTypedArray_NAIVE,
5
+ } from "../naive/typedarray";
6
+ import {
7
+ deserializeArrayBuffer_SWAR,
8
+ deserializeTypedArray_SWAR,
9
+ } from "../swar/typedarray";
4
10
 
11
+ // SWAR/SIMD modes share the same SWAR-only fast path: a single
12
+ // comma-count pass plus inline parsing. The NAIVE path keeps the
13
+ // scalar double-pass implementation for compat.
5
14
 
6
- @inline export function parseArrayBuffer(data: string): ArrayBuffer {
7
- const dataSize = bytes(data);
8
- const dataPtr = changetype<usize>(data);
9
- return deserializeArrayBuffer(dataPtr, dataPtr + dataSize, 0);
15
+ export function deserializeTypedArray<T extends ArrayLike<number>>(
16
+ srcStart: usize,
17
+ srcEnd: usize,
18
+ dst: usize = 0,
19
+ ): T {
20
+ if (JSON_MODE == JSONMode.SWAR || JSON_MODE == JSONMode.SIMD) {
21
+ return deserializeTypedArray_SWAR<T>(srcStart, srcEnd, dst);
22
+ }
23
+ return deserializeTypedArray_NAIVE<T>(srcStart, srcEnd, dst);
10
24
  }
11
25
 
12
-
13
- @inline export function __deserializeArrayBuffer(srcStart: usize, srcEnd: usize, dst: usize = 0): ArrayBuffer {
14
- return deserializeArrayBuffer(srcStart, srcEnd, dst);
26
+ export function deserializeArrayBuffer(
27
+ srcStart: usize,
28
+ srcEnd: usize,
29
+ dst: usize = 0,
30
+ ): ArrayBuffer {
31
+ if (JSON_MODE == JSONMode.SWAR || JSON_MODE == JSONMode.SIMD) {
32
+ return deserializeArrayBuffer_SWAR(srcStart, srcEnd, dst);
33
+ }
34
+ return deserializeArrayBuffer_NAIVE(srcStart, srcEnd, dst);
15
35
  }
@@ -1 +1,78 @@
1
- export { deserializeUnsigned } from "../simple/unsigned";
1
+ import { JSONMode } from "../..";
2
+ import {
3
+ deserializeUnsigned_NAIVE,
4
+ deserializeUnsignedField_NAIVE,
5
+ } from "../naive/unsigned";
6
+ import {
7
+ deserializeUnsigned_SWAR,
8
+ deserializeUnsignedField_SWAR,
9
+ } from "../swar/integer";
10
+ import {
11
+ deserializeUnsigned_SIMD,
12
+ deserializeUnsignedField_SIMD,
13
+ } from "../simd/integer";
14
+
15
+ /**
16
+ * Compile-time dispatch for {@link deserializeUnsigned_NAIVE},
17
+ * {@link deserializeUnsigned_SWAR}, and {@link deserializeUnsigned_SIMD}
18
+ * based on `JSON_MODE`.
19
+ *
20
+ * @param srcStart Pointer to the first UTF-16 code unit.
21
+ * @param srcEnd Pointer just past the last code unit.
22
+ * @returns The parsed value, truncated to `T`.
23
+ */
24
+ // @ts-expect-error: @inline is a valid decorator
25
+ @inline export function deserializeUnsigned<T extends number>(
26
+ srcStart: usize,
27
+ srcEnd: usize,
28
+ ): T {
29
+ if (JSON_MODE == JSONMode.SIMD) {
30
+ return deserializeUnsigned_SIMD<T>(srcStart, srcEnd);
31
+ } else if (JSON_MODE == JSONMode.NAIVE) {
32
+ return deserializeUnsigned_NAIVE<T>(srcStart, srcEnd);
33
+ } else {
34
+ return deserializeUnsigned_SWAR<T>(srcStart, srcEnd);
35
+ }
36
+ }
37
+
38
+ /**
39
+ * Compile-time dispatch for {@link deserializeUnsignedField_NAIVE},
40
+ * {@link deserializeUnsignedField_SWAR}, and
41
+ * {@link deserializeUnsignedField_SIMD} based on `JSON_MODE`.
42
+ *
43
+ * @param srcStart Pointer to the first UTF-16 code unit.
44
+ * @param srcEnd Pointer just past the last code unit.
45
+ * @param dstObj Destination object pointer.
46
+ * @param dstOffset Byte offset of the field within `dstObj`.
47
+ * @returns The source position immediately after the last digit consumed.
48
+ */
49
+ // @ts-expect-error: @inline is a valid decorator
50
+ @inline export function deserializeUnsignedField<T extends number>(
51
+ srcStart: usize,
52
+ srcEnd: usize,
53
+ dstObj: usize,
54
+ dstOffset: usize = 0,
55
+ ): usize {
56
+ if (JSON_MODE == JSONMode.SIMD) {
57
+ return deserializeUnsignedField_SIMD<T>(
58
+ srcStart,
59
+ srcEnd,
60
+ dstObj,
61
+ dstOffset,
62
+ );
63
+ } else if (JSON_MODE == JSONMode.NAIVE) {
64
+ return deserializeUnsignedField_NAIVE<T>(
65
+ srcStart,
66
+ srcEnd,
67
+ dstObj,
68
+ dstOffset,
69
+ );
70
+ } else {
71
+ return deserializeUnsignedField_SWAR<T>(
72
+ srcStart,
73
+ srcEnd,
74
+ dstObj,
75
+ dstOffset,
76
+ );
77
+ }
78
+ }
@@ -11,4 +11,5 @@ export * from "./index/set";
11
11
  export * from "./index/staticarray";
12
12
  export * from "./index/string";
13
13
  export * from "./index/struct";
14
+ export * from "./index/typedarray";
14
15
  export * from "./index/unsigned";
@@ -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;