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.
- package/CHANGELOG.md +9 -26
- package/README.md +43 -19
- package/assembly/deserialize/index/arbitrary.ts +1 -1
- package/assembly/deserialize/index/array.ts +6 -1
- package/assembly/deserialize/index/float.ts +1 -1
- package/assembly/deserialize/index/integer.ts +1 -1
- package/assembly/deserialize/index/unsigned.ts +1 -1
- package/assembly/deserialize/simd/string.ts +17 -13
- package/assembly/deserialize/simple/arbitrary.ts +1 -1
- package/assembly/deserialize/simple/array/generic.ts +42 -0
- package/assembly/deserialize/simple/array.ts +8 -1
- package/assembly/deserialize/{float.ts → simple/float.ts} +22 -2
- package/assembly/deserialize/{integer.ts → simple/integer.ts} +3 -2
- package/assembly/deserialize/simple/map.ts +62 -12
- package/assembly/deserialize/simple/object.ts +1 -1
- package/assembly/deserialize/simple/set.ts +119 -134
- package/assembly/deserialize/simple/staticarray.ts +13 -1
- package/assembly/deserialize/simple/string.ts +15 -10
- package/assembly/deserialize/simple/struct.ts +9 -157
- package/assembly/deserialize/simple/typedarray.ts +1 -1
- package/assembly/deserialize/{unsigned.ts → simple/unsigned.ts} +3 -2
- package/assembly/deserialize/swar/array/array.ts +42 -7
- package/assembly/deserialize/swar/array/bool.ts +6 -2
- package/assembly/deserialize/swar/array/float.ts +7 -3
- package/assembly/deserialize/swar/array/generic.ts +41 -0
- package/assembly/deserialize/swar/array/integer.ts +8 -4
- package/assembly/deserialize/swar/array/object.ts +21 -4
- package/assembly/deserialize/swar/array/shared.ts +19 -4
- package/assembly/deserialize/swar/array/string.ts +6 -2
- package/assembly/deserialize/swar/array/struct.ts +21 -4
- package/assembly/deserialize/swar/array.ts +57 -2
- package/assembly/deserialize/swar/string.ts +248 -372
- package/assembly/index.d.ts +1 -0
- package/assembly/index.ts +77 -19
- package/assembly/serialize/index/arbitrary.ts +3 -3
- package/assembly/serialize/index/float.ts +1 -1
- package/assembly/serialize/index/object.ts +1 -5
- package/assembly/serialize/simd/string.ts +4 -5
- package/assembly/serialize/simple/arbitrary.ts +3 -3
- package/assembly/serialize/simple/array.ts +18 -6
- package/assembly/serialize/simple/float.ts +20 -4
- package/assembly/serialize/simple/map.ts +10 -27
- package/assembly/serialize/simple/object.ts +1 -5
- package/assembly/serialize/simple/set.ts +3 -4
- package/assembly/serialize/simple/staticarray.ts +4 -3
- package/assembly/serialize/simple/typedarray.ts +9 -7
- package/assembly/serialize/swar/string.ts +0 -1
- package/assembly/tsconfig.json +1 -0
- package/assembly/util/dragonbox-cache.ts +4 -0
- package/assembly/util/dragonbox.ts +624 -0
- package/lib/as-bs.ts +35 -24
- package/package.json +26 -18
- package/transform/lib/index.d.ts.map +1 -1
- package/transform/lib/index.js +508 -148
- package/transform/lib/index.js.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,32 +2,15 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
550
|
-
|
|
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
|
-
|
|
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
|
-
|
|
592
|
+
Or run the full local benchmark flow in one step:
|
|
566
593
|
|
|
567
594
|
```bash
|
|
568
|
-
|
|
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 "
|
|
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.
|
|
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
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
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
|
|
213
|
+
const mask = i16x8.bitmask(i16x8.eq(block, SPLAT_5C));
|
|
211
214
|
|
|
212
|
-
if (
|
|
215
|
+
if (mask == 0) {
|
|
213
216
|
srcStart += 16;
|
|
214
217
|
continue;
|
|
215
218
|
}
|
|
216
219
|
|
|
217
|
-
const laneIdx = usize(ctz(
|
|
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,
|
|
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 "
|
|
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.
|
|
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 "
|
|
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,
|
|
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 "
|
|
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,
|
|
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
|
|
7
|
-
|
|
8
|
-
return
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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(
|
|
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 "
|
|
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 {
|