json-as 1.3.1 → 1.3.3

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 (55) hide show
  1. package/CHANGELOG.md +9 -26
  2. package/README.md +43 -19
  3. package/assembly/deserialize/index/arbitrary.ts +1 -1
  4. package/assembly/deserialize/index/array.ts +6 -1
  5. package/assembly/deserialize/index/float.ts +1 -1
  6. package/assembly/deserialize/index/integer.ts +1 -1
  7. package/assembly/deserialize/index/unsigned.ts +1 -1
  8. package/assembly/deserialize/simd/string.ts +17 -13
  9. package/assembly/deserialize/simple/arbitrary.ts +1 -1
  10. package/assembly/deserialize/simple/array/generic.ts +42 -0
  11. package/assembly/deserialize/simple/array.ts +8 -1
  12. package/assembly/deserialize/{float.ts → simple/float.ts} +22 -2
  13. package/assembly/deserialize/{integer.ts → simple/integer.ts} +3 -2
  14. package/assembly/deserialize/simple/map.ts +62 -12
  15. package/assembly/deserialize/simple/object.ts +1 -1
  16. package/assembly/deserialize/simple/set.ts +119 -134
  17. package/assembly/deserialize/simple/staticarray.ts +13 -1
  18. package/assembly/deserialize/simple/string.ts +15 -10
  19. package/assembly/deserialize/simple/struct.ts +9 -157
  20. package/assembly/deserialize/simple/typedarray.ts +1 -1
  21. package/assembly/deserialize/{unsigned.ts → simple/unsigned.ts} +3 -2
  22. package/assembly/deserialize/swar/array/array.ts +42 -7
  23. package/assembly/deserialize/swar/array/bool.ts +6 -2
  24. package/assembly/deserialize/swar/array/float.ts +7 -3
  25. package/assembly/deserialize/swar/array/generic.ts +41 -0
  26. package/assembly/deserialize/swar/array/integer.ts +8 -4
  27. package/assembly/deserialize/swar/array/object.ts +21 -4
  28. package/assembly/deserialize/swar/array/shared.ts +19 -4
  29. package/assembly/deserialize/swar/array/string.ts +6 -2
  30. package/assembly/deserialize/swar/array/struct.ts +21 -4
  31. package/assembly/deserialize/swar/array.ts +57 -2
  32. package/assembly/deserialize/swar/string.ts +248 -372
  33. package/assembly/index.d.ts +1 -0
  34. package/assembly/index.ts +77 -19
  35. package/assembly/serialize/index/arbitrary.ts +3 -3
  36. package/assembly/serialize/index/float.ts +1 -1
  37. package/assembly/serialize/index/object.ts +1 -5
  38. package/assembly/serialize/simd/string.ts +4 -5
  39. package/assembly/serialize/simple/arbitrary.ts +3 -3
  40. package/assembly/serialize/simple/array.ts +18 -6
  41. package/assembly/serialize/simple/float.ts +20 -4
  42. package/assembly/serialize/simple/map.ts +10 -27
  43. package/assembly/serialize/simple/object.ts +1 -5
  44. package/assembly/serialize/simple/set.ts +3 -4
  45. package/assembly/serialize/simple/staticarray.ts +4 -3
  46. package/assembly/serialize/simple/typedarray.ts +9 -7
  47. package/assembly/serialize/swar/string.ts +0 -1
  48. package/assembly/tsconfig.json +1 -0
  49. package/assembly/util/dragonbox-cache.ts +4 -0
  50. package/assembly/util/dragonbox.ts +624 -0
  51. package/lib/as-bs.ts +35 -24
  52. package/package.json +26 -18
  53. package/transform/lib/index.d.ts.map +1 -1
  54. package/transform/lib/index.js +508 -148
  55. package/transform/lib/index.js.map +1 -1
package/CHANGELOG.md CHANGED
@@ -2,32 +2,15 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
- - feat: allow `JSON.Value` to store and re-serialize built-in typed arrays and `ArrayBuffer`
6
- - feat: support `JSON.stringify<ArrayBuffer>(...)` directly without a dedicated helper
7
- - feat: let explicit `__SERIALIZE_CUSTOM` / `__DESERIALIZE_CUSTOM` hooks override built-in typed-array and `ArrayBuffer` handling while keeping generated hooks last
8
- - feat: support optional JSON shape hints on `@serializer(...)` / `@deserializer(...)` decorators, defaulting to `any` and allowing nullable forms like `string | null`
9
- - fix: preserve `bs` state across `JSON.internal.stringify(...)` and `JSON.internal.parse(...)`
10
- - fix: make escaped string deserializers use local `bs` slices so nested parse/custom flows do not clobber active buffer state
11
- - fix: allow nested `@json` fields that use decorator-based custom deserializers to deserialize correctly from string-valued object properties
12
- - fix: restore correct object-value end pointers in generated field deserializers so nested maps and custom string-backed fields both deserialize correctly
13
- - fix: make `JSON.Value` follow built-in subclass rules consistently for typed-array subclasses and custom `@json` subclasses
14
- - fix: make generated custom serializer wrappers use the provided `ptr` so indirect-call sites like `JSON.Value` serialize correctly
15
- - fix: resolve stdlib and `--lib` base classes during transform inheritance so `@json` subclasses of built-ins like `Uint8Array` include inherited fields instead of collapsing to empty objects
16
- - fix: stop preloading transform imports through `parser.parseFile(...)`, so repeated `asc()` calls in the same process no longer poison parser state or trip `lookupForeignFile` assertions
17
- - fix: rewrite nested `JSON.parse(...)` calls inside custom serializers and deserializers to `JSON.internal.parse(...)`, matching the existing internal stringify rewrite and documented behavior
18
- - docs: clarify that custom serializers and deserializers must always produce and consume valid JSON
19
- - docs: document how subclassing built-in container types behaves, including when `@json` custom overrides take effect
20
- - fix: stop naive string deserialization scratch allocations from growing across repeated large payload parses by using local `ensureSize(...)` scratch capacity instead of serialization-style proposed growth
21
- - tests: expand custom serializer/deserializer coverage for nullable fields, multiple custom fields, escaped content, whitespace, and repeated round-trips
22
- - tests: add a repeated large string-heavy struct parse regression that exercises the naive deserialization scratch path
23
- - tests: add override coverage for plain and custom subclasses of `Array`, `Map`, `Set`, and typed arrays
24
- - tests: add `JSON.Value` regressions for undecorated and decorated typed-array subclasses
25
- - tests: add regression coverage for generated `@json` subclasses inheriting stdlib typed-array fields
26
- - chore: restrict published package contents to the runtime, declarations, built transform output, and top-level metadata files
27
- - perf: raise the serialization buffer minimum size to 1024 bytes and add adaptive `bs.shrink()`
28
- - perf: add a packed SWAR `u16_to_hex4_swar` helper for `\uXXXX` emission and use it across simple, SWAR, and SIMD string serializers
29
- - tests: add dedicated SWAR hex helper coverage, including exhaustive full-range round-trip validation
30
- - bench: add SWAR hex and SWAR string serializer head-to-head microbenchmarks
5
+ ## 2026-04-13 - 1.3.2
6
+
7
+ - fix: remove the fast double parser dependency and return float deserialization to the local legacy parser path
8
+ - fix: restrict string field destination reuse/renewal to heap-backed strings only and avoid writing into static literal storage
9
+ - perf: reduce branching in string field write paths while preserving heap-backed reuse (`simple`, `swar`, `simd`, and shared `bs.toField`)
10
+ - tests: add string-field regression coverage for literal defaults and heap-backed output pointers
11
+ - tooling: fix d8 bench runner lint issues (`print` global and unused buffer id vars)
12
+ - tooling: align `bench` script to use `charts:build`
13
+ - docs: streamline README benchmark/docs sections and update benchmark chart command references
31
14
 
32
15
  ## 2026-03-19 - 1.3.0
33
16
 
package/README.md CHANGED
@@ -6,6 +6,7 @@
6
6
  <summary>Table of Contents</summary>
7
7
 
8
8
  - [Installation](#installation)
9
+ - [Docs](#docs)
9
10
  - [Usage](#usage)
10
11
  - [Examples](#examples)
11
12
  - [Omitting Fields](#omitting-fields)
@@ -18,6 +19,8 @@
18
19
  - [Performance](#performance)
19
20
  - [Comparison to JavaScript](#comparison-to-javascript)
20
21
  - [Performance Tuning](#performance-tuning)
22
+ - [Fast-Path Compatibility Matrix](#fast-path-compatibility-matrix)
23
+ - [Container Compatibility Matrix](#container-compatibility-matrix)
21
24
  - [Running Benchmarks Locally](#running-benchmarks-locally)
22
25
  - [Debugging](#debugging)
23
26
  - [Architecture](#architecture)
@@ -58,6 +61,12 @@ Alternatively, add it to your `asconfig.json`
58
61
 
59
62
  If you'd like to see the code that the transform generates, run the build step with `DEBUG=true`
60
63
 
64
+ ## Docs
65
+
66
+ Full documentation lives at:
67
+
68
+ <https://docs.jairus.dev/json-as>
69
+
61
70
  ## Usage
62
71
 
63
72
  ```typescript
@@ -496,26 +505,50 @@ The following charts compare JSON-AS (both SWAR and SIMD variants) against JavaS
496
505
 
497
506
  <img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart02.svg" alt="Performance Chart 2">
498
507
 
508
+ <details>
509
+ <summary>String serialize charts (click to expand)</summary>
510
+
499
511
  <img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart03.png" alt="Performance Chart 3">
500
512
 
513
+ <img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart07.png" alt="Performance Chart 7">
514
+ </details>
515
+
516
+ <details>
517
+ <summary>String deserialize charts (click to expand)</summary>
518
+
501
519
  <img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart04.png" alt="Performance Chart 4">
502
520
 
521
+ <img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart08.png" alt="Performance Chart 8">
522
+ </details>
523
+
524
+ <details>
525
+ <summary>Object serialize charts (click to expand)</summary>
526
+
503
527
  <img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart05.png" alt="Performance Chart 5">
504
528
 
529
+ <img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart09.png" alt="Performance Chart 9">
530
+ </details>
531
+
532
+ <details>
533
+ <summary>Object deserialize charts (click to expand)</summary>
534
+
505
535
  <img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart06.png" alt="Performance Chart 6">
506
536
 
537
+ <img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart10.png" alt="Performance Chart 10">
538
+ </details>
539
+
507
540
  ### Performance Tuning
508
541
 
509
542
  Instead of using flags for setting options, `json-as` is configured by environmental variables.
510
543
  Here's a short list:
511
544
 
512
- **JSON_CACHE** (default: 0) - Enables caching costly strings based on hit frequency. May boost string serialization in excess of 22 GB/s.
545
+ **JSON_CACHE** (default: 0) - Enables and sizes string cache. Supports `true|false`, raw bytes (`JSON_CACHE=1048576`), bits (`JSON_CACHE=512kb`, `2mb`, `1gb`), and bytes (`JSON_CACHE=64KB`, `2MB`, `1GB`). May boost string serialization in excess of 22 GB/s.
513
546
 
514
547
  **JSON_DEBUG** (default: 0) - Sets the debug level. May be within range `0-3`
515
548
 
516
549
  **JSON_MODE** (default: SWAR) - Selects which mode should be used. Can be `NAIVE,SWAR,SIMD`. Note that `--enable simd` may be required.
517
550
 
518
- **JSON_USE_FAST_PATH** (default: 0) - When set to `1`, the transform emits the fast `__DESERIALIZE` implementation for generated structs. When unset or `0`, it emits only the slow path.
551
+ **JSON_USE_FAST_PATH** (default: 1) - The transform emits the fast `__DESERIALIZE` implementation for generated structs by default. Set to `0`, `false`, `off`, or `no` to force slow-path-only output. See [FAST_PATH_DESERIALIZE.md](./FAST_PATH_DESERIALIZE.md) for the current support matrix, known gaps, and dedicated test command.
519
552
 
520
553
  **JSON_WRITE** (default: "") - Select a series of files to output after transform and optimization passes have completed for easy inspection. Usage: `JSON_WRITE=.path-to-file-a.ts,./path-to-file-b.ts`
521
554
 
@@ -546,32 +579,20 @@ npm install
546
579
  4. Run either benchmark suite directly:
547
580
 
548
581
  ```bash
549
- ./run-bench.as.sh
550
- ./run-bench.js.sh
582
+ npm run bench:as
583
+ npm run bench:js
551
584
  ```
552
585
 
553
586
  5. Build charts from the latest local logs:
554
587
 
555
588
  ```bash
556
- ./build-charts.sh
557
- ```
558
-
559
- 6. Publish benchmark charts to the `docs` branch:
560
-
561
- ```bash
562
- ./publish-benchmarks.sh
589
+ npm run charts:build
563
590
  ```
564
591
 
565
- If you already have fresh logs and only want to rebuild charts and push them:
592
+ Or run the full local benchmark flow in one step:
566
593
 
567
594
  ```bash
568
- ./publish-benchmarks.sh --no-run
569
- ```
570
-
571
- Or run the full local publish flow in one step:
572
-
573
- ```bash
574
- npm run bench:publish
595
+ npm run bench
575
596
  ```
576
597
 
577
598
  ## Debugging
@@ -584,6 +605,8 @@ npm run bench:publish
584
605
 
585
606
  For a deep dive into how json-as works internally, including the transform system, optimization modes (NAIVE, SWAR, SIMD), and buffer management, see [ARCHITECTURE.md](./ARCHITECTURE.md).
586
607
 
608
+ For a code-oriented repository walkthrough that maps the runtime, transform, tests, and benchmark files to their concrete responsibilities, see [PROJECT_WALKTHROUGH.md](./PROJECT_WALKTHROUGH.md).
609
+
587
610
  ## Contributing
588
611
 
589
612
  We welcome contributions! Please see [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines on:
@@ -606,6 +629,7 @@ A few companies and open-source projects use json-as!
606
629
  | [Klave](https://klave.com) | Privacy-first platform |
607
630
  | [Bifrost](https://github.com/maximhq/bifrost) | Open source project by Maxim HQ |
608
631
  | [Massa Labs](https://github.com/massalabs) | Massa blockchain tooling |
632
+ | [Seda Protocol](https://github.com/sedaprotocl) | Wasm-based blockchain VM |
609
633
 
610
634
  ## License
611
635
 
@@ -1,7 +1,7 @@
1
1
  import { JSON } from "../..";
2
2
  import { deserializeArray } from "./array";
3
3
  import { deserializeBoolean } from "./bool";
4
- import { deserializeFloat } from "./float";
4
+ import { deserializeFloat } from "../simple/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";
@@ -4,6 +4,7 @@ import { deserializeArrayArray } from "../simple/array/array";
4
4
  import { deserializeBooleanArray } from "../simple/array/bool";
5
5
  import { deserializeBoxArray } from "../simple/array/box";
6
6
  import { deserializeFloatArray } from "../simple/array/float";
7
+ import { deserializeGenericArray } from "../simple/array/generic";
7
8
  import { deserializeIntegerArray as deserializeIntegerArray_NAIVE } from "../simple/array/integer";
8
9
  import { deserializeMapArray } from "../simple/array/map";
9
10
  import { deserializeObjectArray } from "../simple/array/object";
@@ -45,13 +46,17 @@ export function deserializeArray<T extends unknown[]>(srcStart: usize, srcEnd: u
45
46
  return deserializeObjectArray<T>(srcStart, srcEnd, dst);
46
47
  } else if (type instanceof JSON.Raw) {
47
48
  return deserializeRawArray(srcStart, srcEnd, dst) as T;
49
+ } else if (type instanceof Date) {
50
+ return deserializeGenericArray<T>(srcStart, srcEnd, dst);
51
+ } else if (type instanceof Set) {
52
+ return deserializeGenericArray<T>(srcStart, srcEnd, dst);
48
53
  } else if (type instanceof Map) {
49
54
  return deserializeMapArray<T>(srcStart, srcEnd, dst);
50
55
  // @ts-ignore: defined by transform
51
56
  } else if (isDefined(type.__DESERIALIZE_CUSTOM)) {
52
57
  return deserializeStructArray<T>(srcStart, srcEnd, dst);
53
58
  // @ts-ignore: defined by transform
54
- } else if (isDefined(type.__DESERIALIZE)) {
59
+ } else if (isDefined(type.__DESERIALIZE_SLOW) || isDefined(type.__DESERIALIZE_FAST)) {
55
60
  return deserializeStructArray<T>(srcStart, srcEnd, dst);
56
61
  }
57
62
  throw new Error("Could not parse array of type " + nameof<T>() + "!");
@@ -1 +1 @@
1
- export { deserializeFloat } from "../float";
1
+ export { deserializeFloat } from "../simple/float";
@@ -1 +1 @@
1
- export { deserializeInteger } from "../integer";
1
+ export { deserializeInteger } from "../simple/integer";
@@ -1 +1 @@
1
- export { deserializeUnsigned } from "../unsigned";
1
+ export { deserializeUnsigned } from "../simple/unsigned";
@@ -1,5 +1,6 @@
1
1
  import { bs } from "../../../lib/as-bs";
2
2
  import { OBJECT, TOTAL_OVERHEAD } from "rt/common";
3
+ import { __heap_base } from "memory";
3
4
  import { QUOTE } from "../../custom/chars";
4
5
  import { BACK_SLASH } from "../../custom/chars";
5
6
  import { DESERIALIZE_ESCAPE_TABLE, ESCAPE_HEX_TABLE } from "../../globals/tables";
@@ -81,11 +82,13 @@ import { deserializeStringField_SWAR } from "../swar/string";
81
82
 
82
83
  const current = load<usize>(dstFieldPtr);
83
84
  let stringPtr: usize;
84
- if (current != 0 && changetype<OBJECT>(current - TOTAL_OVERHEAD).rtSize == byteLength) {
85
- stringPtr = current;
86
- } else if (current != 0 && current != changetype<usize>("")) {
87
- stringPtr = __renew(current, byteLength);
88
- store<usize>(dstFieldPtr, stringPtr);
85
+ if (current >= __heap_base) {
86
+ if (changetype<OBJECT>(current - TOTAL_OVERHEAD).rtSize == byteLength) {
87
+ stringPtr = current;
88
+ } else {
89
+ stringPtr = __renew(current, byteLength);
90
+ store<usize>(dstFieldPtr, stringPtr);
91
+ }
89
92
  } else {
90
93
  stringPtr = __new(byteLength, idof<string>());
91
94
  store<usize>(dstFieldPtr, stringPtr);
@@ -93,6 +96,8 @@ import { deserializeStringField_SWAR } from "../swar/string";
93
96
  memory.copy(stringPtr, srcStart, byteLength);
94
97
  }
95
98
 
99
+ // @ts-expect-error: @inline is a valid decorator
100
+
96
101
  // todo: optimize and stuff. it works, its not pretty. ideally, i'd like this to be (nearly) branchless
97
102
  // @ts-expect-error: @inline is a valid decorator
98
103
  @inline function deserializeEscapedString_SIMD(payloadStart: usize, escapeStart: usize, srcEnd: usize): string {
@@ -111,16 +116,14 @@ import { deserializeStringField_SWAR } from "../swar/string";
111
116
  store<v128>(bs.offset, block);
112
117
 
113
118
  const eq5C = i16x8.eq(block, SPLAT_5C);
119
+ let mask = i16x8.bitmask(eq5C);
114
120
 
115
- // Early exit
116
- if (!v128.any_true(eq5C)) {
121
+ if (mask == 0) {
117
122
  srcStart += 16;
118
123
  bs.offset += 16;
119
124
  continue;
120
125
  }
121
126
 
122
- let mask = i16x8.bitmask(eq5C);
123
-
124
127
  let srcChg: usize = 0;
125
128
  let lastLane: usize = 0;
126
129
  do {
@@ -207,14 +210,14 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
207
210
 
208
211
  while (srcStart < srcEnd16) {
209
212
  const block = load<v128>(srcStart);
210
- const eq5C = i16x8.eq(block, SPLAT_5C);
213
+ const mask = i16x8.bitmask(i16x8.eq(block, SPLAT_5C));
211
214
 
212
- if (!v128.any_true(eq5C)) {
215
+ if (mask == 0) {
213
216
  srcStart += 16;
214
217
  continue;
215
218
  }
216
219
 
217
- const laneIdx = usize(ctz(i16x8.bitmask(eq5C)) << 1);
220
+ const laneIdx = usize(ctz(mask) << 1);
218
221
  return inline.always(deserializeEscapedString_SIMD(payloadStart, srcStart + laneIdx, srcEnd));
219
222
  }
220
223
 
@@ -229,7 +232,8 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
229
232
  }
230
233
 
231
234
  // @ts-expect-error: @inline is a valid decorator
232
- @inline export function deserializeStringField_SIMD<T extends string | null>(srcStart: usize, srcEnd: usize, dstFieldPtr: usize): usize {
235
+ @inline export function deserializeStringField_SIMD<T extends string | null>(srcStart: usize, srcEnd: usize, dstObj: usize, dstOffset: usize = 0): usize {
236
+ const dstFieldPtr = dstObj + dstOffset;
233
237
  if (srcStart + 2 > srcEnd || load<u16>(srcStart) != QUOTE) abort("Expected leading quote");
234
238
 
235
239
  const quotedStart = srcStart;
@@ -1,7 +1,7 @@
1
1
  import { JSON } from "../..";
2
2
  import { deserializeArray } from "./array";
3
3
  import { deserializeBoolean } from "./bool";
4
- import { deserializeFloat } from "../float";
4
+ import { deserializeFloat } from "./float";
5
5
  import { deserializeString } from "./string";
6
6
  import { deserializeObject } from "./object";
7
7
  import { BRACE_LEFT, BRACKET_LEFT, CHAR_N, QUOTE } from "../../custom/chars";
@@ -0,0 +1,42 @@
1
+ import { JSON } from "../../..";
2
+ import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
3
+ import { isSpace } from "../../../util";
4
+ import { scanValueEnd } from "../../swar/array/shared";
5
+
6
+ export function deserializeGenericArray<T extends unknown[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
7
+ const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
8
+ out.length = 0;
9
+
10
+ while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
11
+ while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
12
+
13
+ if (srcStart >= srcEnd) throw new Error("Input string had zero length or was all whitespace");
14
+ if (load<u16>(srcStart) != BRACKET_LEFT) throw new Error("Expected '[' at start of array");
15
+ srcStart += 2;
16
+
17
+ while (srcStart < srcEnd) {
18
+ while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
19
+ if (srcStart >= srcEnd) break;
20
+
21
+ if (load<u16>(srcStart) == BRACKET_RIGHT) return out;
22
+
23
+ const valueEnd = scanValueEnd(srcStart, srcEnd);
24
+ if (!valueEnd || valueEnd <= srcStart) break;
25
+
26
+ out.push(JSON.__deserialize<valueof<T>>(srcStart, valueEnd));
27
+ srcStart = valueEnd;
28
+
29
+ while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
30
+ if (srcStart >= srcEnd) break;
31
+
32
+ const code = load<u16>(srcStart);
33
+ if (code == COMMA) {
34
+ srcStart += 2;
35
+ continue;
36
+ }
37
+ if (code == BRACKET_RIGHT) return out;
38
+ break;
39
+ }
40
+
41
+ throw new Error("Failed to parse JSON!");
42
+ }
@@ -3,6 +3,7 @@ import { deserializeArbitraryArray } from "./array/arbitrary";
3
3
  import { deserializeArrayArray } from "./array/array";
4
4
  import { deserializeBooleanArray } from "./array/bool";
5
5
  import { deserializeFloatArray } from "./array/float";
6
+ import { deserializeGenericArray } from "./array/generic";
6
7
  import { deserializeIntegerArray } from "./array/integer";
7
8
  import { deserializeMapArray } from "./array/map";
8
9
  import { deserializeStructArray } from "./array/struct";
@@ -42,6 +43,12 @@ export function deserializeArray<T extends unknown[]>(srcStart: usize, srcEnd: u
42
43
  } else if (type instanceof JSON.Raw) {
43
44
  // @ts-ignore: type
44
45
  return deserializeRawArray(srcStart, srcEnd, dst);
46
+ } else if (type instanceof Date) {
47
+ // @ts-ignore: type
48
+ return deserializeGenericArray<T>(srcStart, srcEnd, dst);
49
+ } else if (type instanceof Set) {
50
+ // @ts-ignore: type
51
+ return deserializeGenericArray<T>(srcStart, srcEnd, dst);
45
52
  } else if (type instanceof Map) {
46
53
  // @ts-ignore: type
47
54
  return deserializeMapArray<T>(srcStart, srcEnd, dst);
@@ -49,7 +56,7 @@ export function deserializeArray<T extends unknown[]>(srcStart: usize, srcEnd: u
49
56
  } else if (isDefined(type.__DESERIALIZE_CUSTOM)) {
50
57
  return deserializeStructArray<T>(srcStart, srcEnd, dst);
51
58
  // @ts-ignore: defined by transform
52
- } else if (isDefined(type.__DESERIALIZE)) {
59
+ } else if (isDefined(type.__DESERIALIZE_SLOW) || isDefined(type.__DESERIALIZE_FAST)) {
53
60
  return deserializeStructArray<T>(srcStart, srcEnd, dst);
54
61
  }
55
62
  throw new Error("Could not parse array of type " + nameof<T>() + "!");
@@ -1,7 +1,26 @@
1
- import { ptrToStr } from "../util/ptrToStr";
1
+ import { ptrToStr } from "../../util/ptrToStr";
2
2
 
3
3
  // @ts-ignore: inline
4
4
  @inline function pow10Fast(exponent: u32): f64 {
5
+ if (exponent == 0) return 1.0;
6
+ if (exponent == 1) return 10.0;
7
+ if (exponent == 2) return 100.0;
8
+ if (exponent == 3) return 1e3;
9
+ if (exponent == 4) return 1e4;
10
+ if (exponent == 5) return 1e5;
11
+ if (exponent == 6) return 1e6;
12
+ if (exponent == 7) return 1e7;
13
+ if (exponent == 8) return 1e8;
14
+ if (exponent == 9) return 1e9;
15
+ if (exponent == 10) return 1e10;
16
+ if (exponent == 11) return 1e11;
17
+ if (exponent == 12) return 1e12;
18
+ if (exponent == 13) return 1e13;
19
+ if (exponent == 14) return 1e14;
20
+ if (exponent == 15) return 1e15;
21
+ if (exponent == 16) return 1e16;
22
+ if (exponent == 17) return 1e17;
23
+ if (exponent == 18) return 1e18;
5
24
  let result = 1.0;
6
25
  if (exponent & 1) result *= 1e1;
7
26
  if (exponent & 2) result *= 1e2;
@@ -100,7 +119,8 @@ import { ptrToStr } from "../util/ptrToStr";
100
119
  }
101
120
 
102
121
  // @ts-ignore: inline
103
- @inline export function deserializeFloatField<T extends number>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
122
+ @inline export function deserializeFloatField<T extends number>(srcStart: usize, srcEnd: usize, dstObj: usize, dstOffset: usize = 0): usize {
123
+ const fieldPtr = dstObj + dstOffset;
104
124
  let negative = false;
105
125
  if (load<u16>(srcStart) == 45) {
106
126
  negative = true;
@@ -1,4 +1,4 @@
1
- import { atoi } from "../util/atoi";
1
+ import { atoi } from "../../util/atoi";
2
2
 
3
3
  // @ts-ignore: inline
4
4
  @inline export function deserializeInteger<T>(srcStart: usize, srcEnd: usize): T {
@@ -6,7 +6,8 @@ import { atoi } from "../util/atoi";
6
6
  }
7
7
 
8
8
  // @ts-ignore: inline
9
- @inline export function deserializeIntegerField<T extends number>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
9
+ @inline export function deserializeIntegerField<T extends number>(srcStart: usize, srcEnd: usize, dstObj: usize, dstOffset: usize = 0): usize {
10
+ const fieldPtr = dstObj + dstOffset;
10
11
  let negative = false;
11
12
  if (load<u16>(srcStart) == 45) {
12
13
  negative = true;
@@ -1,17 +1,17 @@
1
1
  import { JSON } from "../..";
2
2
  import { BACK_SLASH, COMMA, CHAR_F, BRACE_LEFT, BRACKET_LEFT, CHAR_N, QUOTE, BRACE_RIGHT, BRACKET_RIGHT, CHAR_T, COLON } from "../../custom/chars";
3
3
  import { isSpace, isUnescapedQuote, scanStringEnd } from "../../util";
4
+ import { scanValueEnd } from "../swar/array/shared";
4
5
 
5
6
  // @ts-ignore: Decorator is valid here
6
- @inline function normalizeQuotes<T>(start: usize, end: usize): T {
7
- if (isString<T>()) return JSON.__deserialize<T>(start - 2, end + 2);
8
- return JSON.__deserialize<T>(start, end);
7
+ @inline function deserializeMapKey<T>(start: usize, end: usize): T {
8
+ const keyText = JSON.__deserialize<string>(start - 2, end + 2);
9
+ if (isString<T>()) return changetype<T>(keyText);
10
+ return JSON.parse<T>(keyText);
9
11
  }
10
12
 
11
13
  export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd: usize, dst: usize): T {
12
14
  const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
13
- // @ts-ignore: type
14
- if (!isString<indexof<T>>() && !isInteger<indexof<T>>() && !isFloat<indexof<T>>()) throw new Error("Map key must also be a valid JSON key!");
15
15
 
16
16
  let keyStart: usize = 0;
17
17
  let keyEnd: usize = 0;
@@ -53,7 +53,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
53
53
  srcStart = scanStringEnd(srcStart, srcEnd);
54
54
  if (srcStart >= srcEnd) throw new Error("Unterminated string in JSON object");
55
55
  // @ts-ignore: type
56
- out.set(normalizeQuotes<indexof<T>>(keyStart, keyEnd), JSON.__deserialize<valueof<T>>(lastIndex, srcStart + 2));
56
+ out.set(deserializeMapKey<indexof<T>>(keyStart, keyEnd), JSON.__deserialize<valueof<T>>(lastIndex, srcStart + 2));
57
57
  srcStart += 2;
58
58
  keyStart = 0;
59
59
  continue;
@@ -65,7 +65,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
65
65
  if (code == COMMA || code == BRACE_RIGHT || isSpace(code)) {
66
66
  // console.log("Value (number): " + ptrToStr(lastIndex, srcStart));
67
67
  // @ts-ignore: type
68
- out.set(normalizeQuotes<indexof<T>>(keyStart, keyEnd), JSON.__deserialize<valueof<T>>(lastIndex, srcStart));
68
+ out.set(deserializeMapKey<indexof<T>>(keyStart, keyEnd), JSON.__deserialize<valueof<T>>(lastIndex, srcStart));
69
69
  // while (isSpace(load<u16>((srcStart += 2)))) {
70
70
  // /* empty */
71
71
  // }
@@ -89,7 +89,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
89
89
  if (--depth == 0) {
90
90
  // console.log("Value (object): " + ptrToStr(lastIndex, srcStart + 2));
91
91
  // @ts-ignore: type
92
- out.set(normalizeQuotes<indexof<T>>(keyStart, keyEnd), JSON.__deserialize<valueof<T>>(lastIndex, (srcStart += 2)));
92
+ out.set(deserializeMapKey<indexof<T>>(keyStart, keyEnd), JSON.__deserialize<valueof<T>>(lastIndex, (srcStart += 2)));
93
93
  // console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
94
94
  keyStart = 0;
95
95
  // while (isSpace(load<u16>(srcStart))) {
@@ -113,7 +113,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
113
113
  if (--depth == 0) {
114
114
  // console.log("Value (array): " + ptrToStr(lastIndex, srcStart + 2));
115
115
  // @ts-ignore: type
116
- out.set(normalizeQuotes<indexof<T>>(keyStart, keyEnd), JSON.__deserialize<valueof<T>>(lastIndex, (srcStart += 2)));
116
+ out.set(deserializeMapKey<indexof<T>>(keyStart, keyEnd), JSON.__deserialize<valueof<T>>(lastIndex, (srcStart += 2)));
117
117
  // console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
118
118
  keyStart = 0;
119
119
  // while (isSpace(load<u16>((srcStart += 2)))) {
@@ -128,7 +128,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
128
128
  if (load<u64>(srcStart) == 28429475166421108) {
129
129
  // console.log("Value (bool): " + ptrToStr(srcStart, srcStart + 8));
130
130
  // @ts-ignore: type
131
- out.set(normalizeQuotes<indexof<T>>(keyStart, keyEnd), JSON.__deserialize<valueof<T>>(srcStart, (srcStart += 8)));
131
+ out.set(deserializeMapKey<indexof<T>>(keyStart, keyEnd), JSON.__deserialize<valueof<T>>(srcStart, (srcStart += 8)));
132
132
  // while (isSpace(load<u16>((srcStart += 2)))) {
133
133
  // /* empty */
134
134
  // }
@@ -140,7 +140,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
140
140
  if (load<u64>(srcStart, 2) == 28429466576093281) {
141
141
  // console.log("Value (bool): " + ptrToStr(srcStart, srcStart + 10));
142
142
  // @ts-ignore: type
143
- out.set(normalizeQuotes<indexof<T>>(keyStart, keyEnd), JSON.__deserialize<valueof<T>>(srcStart, (srcStart += 10)));
143
+ out.set(deserializeMapKey<indexof<T>>(keyStart, keyEnd), JSON.__deserialize<valueof<T>>(srcStart, (srcStart += 10)));
144
144
  // while (isSpace(load<u16>((srcStart += 2)))) {
145
145
  // /* empty */
146
146
  // }
@@ -152,7 +152,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
152
152
  if (load<u64>(srcStart) == 30399761348886638) {
153
153
  // console.log("Value (null): " + ptrToStr(srcStart, srcStart + 8));
154
154
  // @ts-ignore: type
155
- out.set(normalizeQuotes<indexof<T>>(keyStart, keyEnd), JSON.__deserialize<valueof<T>>(srcStart, (srcStart += 8)));
155
+ out.set(deserializeMapKey<indexof<T>>(keyStart, keyEnd), JSON.__deserialize<valueof<T>>(srcStart, (srcStart += 8)));
156
156
  // while (isSpace(load<u16>((srcStart += 2)))) {
157
157
  /* empty */
158
158
  // }
@@ -169,3 +169,53 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
169
169
  }
170
170
  return out;
171
171
  }
172
+
173
+
174
+ @inline export function deserializeMapInto<T extends Map<any, any>>(srcStart: usize, srcEnd: usize, out: T): usize {
175
+ changetype<nonnull<T>>(out).clear();
176
+
177
+ if (srcStart >= srcEnd || load<u16>(srcStart) != BRACE_LEFT) throw new Error("Failed to parse JSON!");
178
+ srcStart += 2;
179
+ if (srcStart >= srcEnd) throw new Error("Failed to parse JSON!");
180
+ if (load<u16>(srcStart) == BRACE_RIGHT) return srcStart + 2;
181
+
182
+ while (srcStart < srcEnd) {
183
+ if (load<u16>(srcStart) != QUOTE) break;
184
+
185
+ const keyStart = srcStart + 2;
186
+ const keyEnd = scanStringEnd(srcStart, srcEnd);
187
+ if (keyEnd >= srcEnd) break;
188
+
189
+ srcStart = keyEnd + 2;
190
+ if (srcStart >= srcEnd || load<u16>(srcStart) != COLON) break;
191
+ srcStart += 2;
192
+
193
+ const valueEnd = scanValueEnd(srcStart, srcEnd);
194
+ if (!valueEnd || valueEnd <= srcStart) break;
195
+
196
+ // @ts-ignore: type
197
+ changetype<nonnull<T>>(out).set(deserializeMapKey<indexof<T>>(keyStart, keyEnd), JSON.__deserialize<valueof<T>>(srcStart, valueEnd));
198
+ srcStart = valueEnd;
199
+
200
+ if (srcStart >= srcEnd) break;
201
+ const code = load<u16>(srcStart);
202
+ if (code == COMMA) {
203
+ srcStart += 2;
204
+ continue;
205
+ }
206
+ if (code == BRACE_RIGHT) return srcStart + 2;
207
+ break;
208
+ }
209
+
210
+ throw new Error("Failed to parse JSON!");
211
+ }
212
+
213
+
214
+ @inline export function deserializeMapField<T extends Map<any, any>>(srcStart: usize, srcEnd: usize, dstObj: usize, dstOffset: usize = 0): usize {
215
+ let out = load<T>(dstObj, dstOffset);
216
+ if (!changetype<usize>(out)) {
217
+ out = changetype<T>(instantiate<T>());
218
+ store<T>(dstObj, out, dstOffset);
219
+ }
220
+ return deserializeMapInto<T>(srcStart, srcEnd, out);
221
+ }
@@ -7,7 +7,7 @@ import { deserializeString_SWAR } from "../swar/string";
7
7
  import { deserializeArbitrary } from "./arbitrary";
8
8
  import { deserializeArray } from "./array";
9
9
  import { deserializeBoolean } from "./bool";
10
- import { deserializeFloat } from "../float";
10
+ import { deserializeFloat } from "./float";
11
11
  import { deserializeString } from "./string";
12
12
 
13
13
  export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): JSON.Obj {