json-as 1.3.7 → 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.
- package/CHANGELOG.md +32 -0
- package/README.md +1 -1
- package/assembly/deserialize/index/arbitrary.ts +2 -2
- package/assembly/deserialize/index/array.ts +29 -14
- package/assembly/deserialize/index/bool.ts +1 -1
- package/assembly/deserialize/index/date.ts +1 -1
- package/assembly/deserialize/index/float.ts +40 -1
- package/assembly/deserialize/index/integer.ts +3 -3
- package/assembly/deserialize/index/map.ts +1 -1
- package/assembly/deserialize/index/object.ts +1 -1
- package/assembly/deserialize/index/raw.ts +1 -1
- package/assembly/deserialize/index/set.ts +1 -1
- package/assembly/deserialize/index/staticarray.ts +4 -1
- package/assembly/deserialize/index/string.ts +28 -3
- package/assembly/deserialize/index/struct.ts +1 -1
- package/assembly/deserialize/index/typedarray.ts +25 -15
- package/assembly/deserialize/index/unsigned.ts +3 -3
- package/assembly/deserialize/index.ts +1 -0
- package/assembly/deserialize/naive/array/bool.ts +68 -0
- package/assembly/deserialize/naive/array/float.ts +63 -0
- package/assembly/deserialize/{simple → naive}/array/generic.ts +1 -2
- package/assembly/deserialize/naive/array/integer.ts +86 -0
- package/assembly/deserialize/{simple → naive}/array/map.ts +0 -1
- package/assembly/deserialize/{simple → naive}/array/object.ts +0 -1
- package/assembly/deserialize/naive/array/string.ts +69 -0
- package/assembly/deserialize/{simple → naive}/array/struct.ts +0 -1
- package/assembly/deserialize/{simple → naive}/array.ts +6 -11
- package/assembly/deserialize/naive/float.ts +135 -0
- package/assembly/deserialize/{simple → naive}/integer.ts +2 -2
- package/assembly/deserialize/{simple → naive}/map.ts +12 -6
- package/assembly/deserialize/{simple → naive}/object.ts +4 -7
- package/assembly/deserialize/{simple → naive}/set.ts +12 -27
- package/assembly/deserialize/{simple → naive}/staticarray/array.ts +1 -1
- package/assembly/deserialize/{simple → naive}/staticarray/bool.ts +1 -1
- package/assembly/deserialize/{simple → naive}/staticarray/float.ts +1 -1
- package/assembly/deserialize/{simple → naive}/staticarray/integer.ts +1 -1
- package/assembly/deserialize/{simple → naive}/staticarray/struct.ts +1 -2
- package/assembly/deserialize/{simple → naive}/staticarray.ts +4 -4
- package/assembly/deserialize/naive/string.ts +199 -0
- package/assembly/deserialize/{simple → naive}/typedarray.ts +4 -4
- package/assembly/deserialize/{simple → naive}/unsigned.ts +2 -2
- package/assembly/deserialize/simd/array/integer.ts +19 -19
- package/assembly/deserialize/simd/float.ts +303 -0
- package/assembly/deserialize/simd/string.ts +233 -108
- package/assembly/deserialize/swar/array/arbitrary.ts +6 -2
- package/assembly/deserialize/swar/array/array.ts +14 -7
- package/assembly/deserialize/swar/array/bool.ts +8 -3
- package/assembly/deserialize/swar/array/box.ts +6 -2
- package/assembly/deserialize/swar/array/float.ts +282 -6
- package/assembly/deserialize/swar/array/generic.ts +6 -2
- package/assembly/deserialize/swar/array/integer.ts +81 -74
- package/assembly/deserialize/swar/array/map.ts +6 -2
- package/assembly/deserialize/swar/array/object.ts +24 -32
- package/assembly/deserialize/swar/array/raw.ts +6 -2
- package/assembly/deserialize/swar/array/shared.ts +32 -8
- package/assembly/deserialize/swar/array/string.ts +127 -10
- package/assembly/deserialize/swar/array/struct.ts +45 -11
- package/assembly/deserialize/swar/array.ts +2 -56
- package/assembly/deserialize/swar/float.ts +304 -0
- package/assembly/deserialize/swar/string.ts +119 -104
- package/assembly/deserialize/swar/typedarray.ts +224 -0
- package/assembly/index.ts +203 -293
- package/assembly/serialize/index/array.ts +1 -1
- package/assembly/serialize/index/bool.ts +1 -1
- package/assembly/serialize/index/date.ts +1 -1
- package/assembly/serialize/index/float.ts +1 -1
- package/assembly/serialize/index/integer.ts +1 -1
- package/assembly/serialize/index/map.ts +1 -1
- package/assembly/serialize/index/raw.ts +1 -1
- package/assembly/serialize/index/set.ts +1 -1
- package/assembly/serialize/index/staticarray.ts +1 -1
- package/assembly/serialize/index/string.ts +1 -1
- package/assembly/serialize/index/struct.ts +1 -1
- package/assembly/serialize/index/typedarray.ts +2 -11
- package/assembly/serialize/index.ts +1 -0
- package/assembly/serialize/{simple → naive}/array.ts +87 -0
- package/assembly/serialize/{simple → naive}/string.ts +1 -1
- package/assembly/serialize/swar/string.ts +0 -139
- package/assembly/util/dragonbox.ts +10 -3
- package/assembly/util/itoa-fast.ts +29 -18
- package/assembly/util/scanValueEnd.ts +78 -0
- package/assembly/util/scientific.ts +132 -0
- package/lib/as-bs.ts +14 -1
- package/package.json +14 -13
- package/transform/lib/index.d.ts +4 -0
- package/transform/lib/index.d.ts.map +1 -1
- package/transform/lib/index.js +153 -236
- package/transform/lib/index.js.map +1 -1
- package/assembly/deserialize/simple/arbitrary.ts +0 -30
- package/assembly/deserialize/simple/array/bool.ts +0 -48
- package/assembly/deserialize/simple/array/float.ts +0 -55
- package/assembly/deserialize/simple/array/integer.ts +0 -33
- package/assembly/deserialize/simple/array/string.ts +0 -29
- package/assembly/deserialize/simple/float.ts +0 -206
- package/assembly/deserialize/simple/string.ts +0 -45
- package/assembly/serialize/simple/arbitrary.ts +0 -79
- package/assembly/serialize/simple/object.ts +0 -42
- /package/assembly/deserialize/{simple → naive}/array/arbitrary.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/array/array.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/array/box.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/array/raw.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/bool.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/date.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/raw.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/staticarray/string.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/struct.ts +0 -0
- /package/assembly/serialize/{simple → naive}/bool.ts +0 -0
- /package/assembly/serialize/{simple → naive}/date.ts +0 -0
- /package/assembly/serialize/{simple → naive}/float.ts +0 -0
- /package/assembly/serialize/{simple → naive}/integer.ts +0 -0
- /package/assembly/serialize/{simple → naive}/map.ts +0 -0
- /package/assembly/serialize/{simple → naive}/raw.ts +0 -0
- /package/assembly/serialize/{simple → naive}/set.ts +0 -0
- /package/assembly/serialize/{simple → naive}/staticarray.ts +0 -0
- /package/assembly/serialize/{simple → naive}/struct.ts +0 -0
- /package/assembly/serialize/{simple → naive}/typedarray.ts +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,37 @@
|
|
|
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
|
+
|
|
3
35
|
## 2026-05-19 - 1.3.7
|
|
4
36
|
|
|
5
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`
|
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
|
|
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,7 +1,7 @@
|
|
|
1
1
|
import { JSON } from "../..";
|
|
2
2
|
import { deserializeArray } from "./array";
|
|
3
3
|
import { deserializeBoolean } from "./bool";
|
|
4
|
-
import {
|
|
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";
|
|
@@ -17,7 +17,7 @@ export function deserializeArbitrary(
|
|
|
17
17
|
} else if (firstChar == BRACE_LEFT) {
|
|
18
18
|
return JSON.Value.from(deserializeObject(srcStart, srcEnd, 0));
|
|
19
19
|
} else if (firstChar - 48 <= 9 || firstChar == 45) {
|
|
20
|
-
return JSON.Value.from(
|
|
20
|
+
return JSON.Value.from(deserializeFloat_NAIVE<f64>(srcStart, srcEnd));
|
|
21
21
|
} else if (firstChar == BRACKET_LEFT) {
|
|
22
22
|
return JSON.Value.from(deserializeArray<JSON.Value[]>(srcStart, srcEnd, 0));
|
|
23
23
|
} else if (firstChar == 116 || firstChar == 102) {
|
|
@@ -1,18 +1,20 @@
|
|
|
1
1
|
import { JSON, JSONMode } from "../..";
|
|
2
|
-
import { deserializeArbitraryArray } from "../
|
|
3
|
-
import { deserializeArrayArray } from "../
|
|
4
|
-
import { deserializeBooleanArray } from "../
|
|
5
|
-
import { deserializeBoxArray } from "../
|
|
6
|
-
import {
|
|
7
|
-
import { deserializeGenericArray } from "../
|
|
8
|
-
import {
|
|
9
|
-
import { deserializeMapArray } from "../
|
|
10
|
-
import { deserializeObjectArray } from "../
|
|
11
|
-
import { deserializeRawArray } from "../
|
|
12
|
-
import {
|
|
13
|
-
import { deserializeStructArray } from "../
|
|
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
19
|
export {
|
|
18
20
|
deserializeArrayField,
|
|
@@ -25,7 +27,16 @@ export function deserializeArray<T extends unknown[]>(
|
|
|
25
27
|
dst: usize,
|
|
26
28
|
): T {
|
|
27
29
|
if (isString<valueof<T>>()) {
|
|
28
|
-
|
|
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));
|
|
29
40
|
} else if (isBoolean<valueof<T>>()) {
|
|
30
41
|
return deserializeBooleanArray<T>(srcStart, srcEnd, dst);
|
|
31
42
|
} else if (isInteger<valueof<T>>()) {
|
|
@@ -40,7 +51,11 @@ export function deserializeArray<T extends unknown[]>(
|
|
|
40
51
|
return deserializeIntegerArray_NAIVE<T>(srcStart, srcEnd, dst);
|
|
41
52
|
}
|
|
42
53
|
} else if (isFloat<valueof<T>>()) {
|
|
43
|
-
|
|
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);
|
|
44
59
|
} else if (isArray<valueof<T>>()) {
|
|
45
60
|
return deserializeArrayArray<T>(srcStart, srcEnd, dst);
|
|
46
61
|
} else if (isManaged<valueof<T>>() || isReference<valueof<T>>()) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { deserializeBoolean } from "../
|
|
1
|
+
export { deserializeBoolean } from "../naive/bool";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { deserializeDate } from "../
|
|
1
|
+
export { deserializeDate } from "../naive/date";
|
|
@@ -1 +1,40 @@
|
|
|
1
|
-
|
|
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,8 +1,8 @@
|
|
|
1
1
|
import { JSONMode } from "../..";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from "../
|
|
3
|
+
deserializeInteger_NAIVE,
|
|
4
|
+
deserializeIntegerField_NAIVE,
|
|
5
|
+
} from "../naive/integer";
|
|
6
6
|
import {
|
|
7
7
|
deserializeInteger_SWAR,
|
|
8
8
|
deserializeIntegerField_SWAR,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { deserializeMap } from "../
|
|
1
|
+
export { deserializeMap, deserializeMapField } from "../naive/map";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { deserializeObject } from "../
|
|
1
|
+
export { deserializeObject } from "../naive/object";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { deserializeRaw } from "../
|
|
1
|
+
export { deserializeRaw } from "../naive/raw";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export { deserializeSet } from "../
|
|
1
|
+
export { deserializeSet, deserializeSetField } from "../naive/set";
|
|
@@ -1,7 +1,16 @@
|
|
|
1
1
|
import { JSONMode } from "../..";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
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
16
|
@inline export function deserializeString(
|
|
@@ -16,3 +25,19 @@ import { deserializeString_SWAR } from "../swar/string";
|
|
|
16
25
|
return deserializeString_SWAR(srcStart, srcEnd);
|
|
17
26
|
}
|
|
18
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 "../
|
|
1
|
+
export { deserializeStruct } from "../naive/struct";
|
|
@@ -1,25 +1,35 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export {
|
|
3
|
-
deserializeArrayBuffer,
|
|
4
|
-
deserializeTypedArray,
|
|
5
|
-
} from "../simple/typedarray";
|
|
1
|
+
import { JSONMode } from "../..";
|
|
6
2
|
import {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
} from "../
|
|
3
|
+
deserializeArrayBuffer_NAIVE,
|
|
4
|
+
deserializeTypedArray_NAIVE,
|
|
5
|
+
} from "../naive/typedarray";
|
|
6
|
+
import {
|
|
7
|
+
deserializeArrayBuffer_SWAR,
|
|
8
|
+
deserializeTypedArray_SWAR,
|
|
9
|
+
} from "../swar/typedarray";
|
|
10
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.
|
|
11
14
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
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);
|
|
16
24
|
}
|
|
17
25
|
|
|
18
|
-
|
|
19
|
-
@inline export function __deserializeArrayBuffer(
|
|
26
|
+
export function deserializeArrayBuffer(
|
|
20
27
|
srcStart: usize,
|
|
21
28
|
srcEnd: usize,
|
|
22
29
|
dst: usize = 0,
|
|
23
30
|
): ArrayBuffer {
|
|
24
|
-
|
|
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);
|
|
25
35
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { JSONMode } from "../..";
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from "../
|
|
3
|
+
deserializeUnsigned_NAIVE,
|
|
4
|
+
deserializeUnsignedField_NAIVE,
|
|
5
|
+
} from "../naive/unsigned";
|
|
6
6
|
import {
|
|
7
7
|
deserializeUnsigned_SWAR,
|
|
8
8
|
deserializeUnsignedField_SWAR,
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { isSpace } from "../../../util";
|
|
2
|
+
import {
|
|
3
|
+
COMMA,
|
|
4
|
+
BRACKET_LEFT,
|
|
5
|
+
BRACKET_RIGHT,
|
|
6
|
+
FALSE_WORD_U64,
|
|
7
|
+
TRUE_WORD_U64,
|
|
8
|
+
} from "../../../custom/chars";
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Strict boolean-array deserializer (`bool[]`, every JSON_MODE).
|
|
12
|
+
*
|
|
13
|
+
* Enforces RFC 8259 array structure: `[`-framed, single-comma separated, no
|
|
14
|
+
* leading / trailing / doubled commas, and each element must be exactly the
|
|
15
|
+
* literal `true` or `false`. Throws on any deviation.
|
|
16
|
+
*
|
|
17
|
+
* The token check is SWAR-shaped: one `u64` load matches all four chars of
|
|
18
|
+
* `true`; `false` adds one `u16` load to confirm the trailing `e`.
|
|
19
|
+
*/
|
|
20
|
+
export function deserializeBooleanArray<T extends boolean[]>(
|
|
21
|
+
srcStart: usize,
|
|
22
|
+
srcEnd: usize,
|
|
23
|
+
dst: usize,
|
|
24
|
+
): T {
|
|
25
|
+
const out = changetype<nonnull<T>>(
|
|
26
|
+
dst || changetype<usize>(instantiate<T>()),
|
|
27
|
+
);
|
|
28
|
+
out.length = 0; // dst may arrive pre-sized; re-parse from empty via push
|
|
29
|
+
|
|
30
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
31
|
+
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
32
|
+
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT)
|
|
33
|
+
throw new Error("Invalid JSON array: expected '['");
|
|
34
|
+
if (load<u16>(srcEnd - 2) != BRACKET_RIGHT)
|
|
35
|
+
throw new Error("Invalid JSON array: expected ']'");
|
|
36
|
+
srcStart += 2; // past '['
|
|
37
|
+
srcEnd -= 2; // before ']'
|
|
38
|
+
|
|
39
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
40
|
+
if (srcStart >= srcEnd) return out;
|
|
41
|
+
|
|
42
|
+
while (true) {
|
|
43
|
+
if (srcStart + 8 <= srcEnd && load<u64>(srcStart) == TRUE_WORD_U64) {
|
|
44
|
+
out.push(true);
|
|
45
|
+
srcStart += 8;
|
|
46
|
+
} else if (
|
|
47
|
+
srcStart + 10 <= srcEnd &&
|
|
48
|
+
load<u64>(srcStart) == FALSE_WORD_U64 &&
|
|
49
|
+
load<u16>(srcStart, 8) == 101
|
|
50
|
+
) {
|
|
51
|
+
out.push(false);
|
|
52
|
+
srcStart += 10;
|
|
53
|
+
} else {
|
|
54
|
+
throw new Error("Invalid JSON array: expected 'true' or 'false'");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
58
|
+
if (srcStart >= srcEnd) break;
|
|
59
|
+
if (load<u16>(srcStart) != COMMA)
|
|
60
|
+
throw new Error("Invalid JSON array: expected ',' or ']'");
|
|
61
|
+
srcStart += 2;
|
|
62
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
63
|
+
if (srcStart >= srcEnd)
|
|
64
|
+
throw new Error("Invalid JSON array: trailing comma");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { isSpace } from "../../../util";
|
|
2
|
+
import { COMMA, BRACKET_LEFT, BRACKET_RIGHT } from "../../../custom/chars";
|
|
3
|
+
import { JSON } from "../../..";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Strict float-array deserializer (`f64[]` / `f32[]`).
|
|
7
|
+
*
|
|
8
|
+
* Enforces RFC 8259 array structure: `[`-framed, single-comma separated, no
|
|
9
|
+
* leading / trailing / doubled commas, and no garbage between values. Each
|
|
10
|
+
* element token is validated by `deserializeFloat_NAIVE` (via `JSON.__deserialize`),
|
|
11
|
+
* so malformed numbers like `0e` / `-01` / `1.` are rejected here too. Throws on
|
|
12
|
+
* any deviation.
|
|
13
|
+
*/
|
|
14
|
+
export function deserializeFloatArray_NAIVE<T extends number[]>(
|
|
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
|
+
// `dst` may arrive pre-sized (e.g. the SWAR fast path presizes then falls
|
|
23
|
+
// back here); we re-parse from scratch via push, so start from empty.
|
|
24
|
+
out.length = 0;
|
|
25
|
+
|
|
26
|
+
// Trim surrounding whitespace and require the enclosing brackets.
|
|
27
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
28
|
+
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
29
|
+
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT)
|
|
30
|
+
throw new Error("Invalid JSON array: expected '['");
|
|
31
|
+
if (load<u16>(srcEnd - 2) != BRACKET_RIGHT)
|
|
32
|
+
throw new Error("Invalid JSON array: expected ']'");
|
|
33
|
+
srcStart += 2; // past '['
|
|
34
|
+
srcEnd -= 2; // before ']'
|
|
35
|
+
|
|
36
|
+
// skip whitespace; an empty body is a valid empty array
|
|
37
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
38
|
+
if (srcStart >= srcEnd) return out;
|
|
39
|
+
|
|
40
|
+
while (true) {
|
|
41
|
+
// value token: runs until whitespace or a comma
|
|
42
|
+
const tokenStart = srcStart;
|
|
43
|
+
while (srcStart < srcEnd) {
|
|
44
|
+
const c = load<u16>(srcStart);
|
|
45
|
+
if (c == COMMA || isSpace(c)) break;
|
|
46
|
+
srcStart += 2;
|
|
47
|
+
}
|
|
48
|
+
if (srcStart == tokenStart)
|
|
49
|
+
throw new Error("Invalid JSON array: missing value");
|
|
50
|
+
out.push(JSON.__deserialize<valueof<T>>(tokenStart, srcStart));
|
|
51
|
+
|
|
52
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
53
|
+
if (srcStart >= srcEnd) break; // end of array body
|
|
54
|
+
if (load<u16>(srcStart) != COMMA)
|
|
55
|
+
throw new Error("Invalid JSON array: expected ',' or ']'");
|
|
56
|
+
srcStart += 2; // past ','
|
|
57
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
58
|
+
if (srcStart >= srcEnd)
|
|
59
|
+
throw new Error("Invalid JSON array: trailing comma");
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return out;
|
|
63
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { JSON } from "../../..";
|
|
2
2
|
import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
|
|
3
3
|
import { isSpace } from "../../../util";
|
|
4
|
-
import { scanValueEnd } from "
|
|
4
|
+
import { scanValueEnd } from "../../../util/scanValueEnd";
|
|
5
5
|
|
|
6
6
|
export function deserializeGenericArray<T extends unknown[]>(
|
|
7
7
|
srcStart: usize,
|
|
@@ -13,7 +13,6 @@ export function deserializeGenericArray<T extends unknown[]>(
|
|
|
13
13
|
);
|
|
14
14
|
out.length = 0;
|
|
15
15
|
|
|
16
|
-
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
17
16
|
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
18
17
|
|
|
19
18
|
if (srcStart >= srcEnd)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { atoi, isSpace } from "../../../util";
|
|
2
|
+
import { COMMA, BRACKET_LEFT, BRACKET_RIGHT } from "../../../custom/chars";
|
|
3
|
+
|
|
4
|
+
// Strict RFC 8259 integer-token check over [start, end): optional minus (signed
|
|
5
|
+
// types only), then a lone `0` or [1-9] digits — no leading zeros, fraction,
|
|
6
|
+
// exponent, or trailing garbage. Throws otherwise.
|
|
7
|
+
// @ts-ignore: inline
|
|
8
|
+
@inline function validateJSONInteger(
|
|
9
|
+
start: usize,
|
|
10
|
+
end: usize,
|
|
11
|
+
signed: bool,
|
|
12
|
+
): void {
|
|
13
|
+
let ptr = start;
|
|
14
|
+
if (ptr < end && load<u16>(ptr) == 45) {
|
|
15
|
+
if (!signed) throw new Error("Invalid JSON number: minus on unsigned");
|
|
16
|
+
ptr += 2;
|
|
17
|
+
}
|
|
18
|
+
if (ptr >= end) throw new Error("Invalid JSON number: expected digit");
|
|
19
|
+
const first = load<u16>(ptr);
|
|
20
|
+
if (first == 48) {
|
|
21
|
+
ptr += 2;
|
|
22
|
+
if (ptr < end && <u32>(load<u16>(ptr) - 48) <= 9)
|
|
23
|
+
throw new Error("Invalid JSON number: leading zero");
|
|
24
|
+
} else if (<u32>(first - 48) <= 9) {
|
|
25
|
+
ptr += 2;
|
|
26
|
+
while (ptr < end && <u32>(load<u16>(ptr) - 48) <= 9) ptr += 2;
|
|
27
|
+
} else {
|
|
28
|
+
throw new Error("Invalid JSON number: expected digit");
|
|
29
|
+
}
|
|
30
|
+
if (ptr != end) throw new Error("Invalid JSON number: not an integer");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Strict integer-array deserializer (`i8[]`..`i64[]`, `u8[]`..`u64[]`).
|
|
35
|
+
*
|
|
36
|
+
* Enforces RFC 8259 array structure: `[`-framed, single-comma separated, no
|
|
37
|
+
* leading / trailing / doubled commas, and each element must be a valid JSON
|
|
38
|
+
* integer (no leading zeros, fraction, or exponent). Throws on any deviation.
|
|
39
|
+
*/
|
|
40
|
+
export function deserializeIntegerArray_NAIVE<T extends number[]>(
|
|
41
|
+
srcStart: usize,
|
|
42
|
+
srcEnd: usize,
|
|
43
|
+
dst: usize,
|
|
44
|
+
): T {
|
|
45
|
+
const out = changetype<nonnull<T>>(
|
|
46
|
+
dst || changetype<usize>(instantiate<T>()),
|
|
47
|
+
);
|
|
48
|
+
out.length = 0; // dst may arrive pre-sized; re-parse from empty via push
|
|
49
|
+
|
|
50
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
51
|
+
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
52
|
+
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT)
|
|
53
|
+
throw new Error("Invalid JSON array: expected '['");
|
|
54
|
+
if (load<u16>(srcEnd - 2) != BRACKET_RIGHT)
|
|
55
|
+
throw new Error("Invalid JSON array: expected ']'");
|
|
56
|
+
srcStart += 2; // past '['
|
|
57
|
+
srcEnd -= 2; // before ']'
|
|
58
|
+
|
|
59
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
60
|
+
if (srcStart >= srcEnd) return out;
|
|
61
|
+
|
|
62
|
+
const signed = isSigned<valueof<T>>();
|
|
63
|
+
while (true) {
|
|
64
|
+
const tokenStart = srcStart;
|
|
65
|
+
while (srcStart < srcEnd) {
|
|
66
|
+
const c = load<u16>(srcStart);
|
|
67
|
+
if (c == COMMA || isSpace(c)) break;
|
|
68
|
+
srcStart += 2;
|
|
69
|
+
}
|
|
70
|
+
if (srcStart == tokenStart)
|
|
71
|
+
throw new Error("Invalid JSON array: missing value");
|
|
72
|
+
validateJSONInteger(tokenStart, srcStart, signed);
|
|
73
|
+
out.push(atoi<valueof<T>>(tokenStart, srcStart));
|
|
74
|
+
|
|
75
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
76
|
+
if (srcStart >= srcEnd) break;
|
|
77
|
+
if (load<u16>(srcStart) != COMMA)
|
|
78
|
+
throw new Error("Invalid JSON array: expected ',' or ']'");
|
|
79
|
+
srcStart += 2;
|
|
80
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
81
|
+
if (srcStart >= srcEnd)
|
|
82
|
+
throw new Error("Invalid JSON array: trailing comma");
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
@@ -18,7 +18,6 @@ export function deserializeMapArray<T extends Map<any, any>[]>(
|
|
|
18
18
|
let lastIndex: usize = 0;
|
|
19
19
|
let depth: u32 = 0;
|
|
20
20
|
|
|
21
|
-
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
22
21
|
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
23
22
|
|
|
24
23
|
if (srcStart - srcEnd == 0)
|
|
@@ -18,7 +18,6 @@ export function deserializeObjectArray<T extends unknown[]>(
|
|
|
18
18
|
let lastIndex: usize = 0;
|
|
19
19
|
let depth: u32 = 0;
|
|
20
20
|
|
|
21
|
-
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
22
21
|
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
23
22
|
|
|
24
23
|
if (srcStart - srcEnd == 0)
|