json-as 1.0.7 → 1.0.9
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 +15 -0
- package/README.md +18 -2
- package/asconfig.json +1 -2
- package/assembly/__benches__/abc.bench.ts +5 -5
- package/assembly/__benches__/large.bench.ts +5 -51
- package/assembly/{custom → __benches__/lib}/bench.ts +4 -0
- package/assembly/__benches__/medium.bench.ts +3 -3
- package/assembly/__benches__/small.bench.ts +1 -1
- package/assembly/__benches__/vec3.bench.ts +3 -49
- package/assembly/__tests__/custom.spec.ts +4 -3
- package/assembly/__tests__/hierarchy.spec.ts +55 -0
- package/assembly/__tests__/lib/index.ts +1 -0
- package/assembly/custom/util.ts +0 -4
- package/assembly/deserialize/simple/array/box.ts +45 -0
- package/assembly/deserialize/simple/array/object.ts +28 -0
- package/assembly/deserialize/simple/array.ts +8 -0
- package/assembly/index.ts +39 -41
- package/assembly/serialize/simd/string.ts +12 -12
- package/assembly/test.ts +46 -133
- package/assembly/util/idofd.ts +6 -0
- package/bench/abc.bench.ts +1 -1
- package/bench/large.bench.ts +3 -3
- package/bench/lib/bench.ts +12 -0
- package/bench/medium.bench.ts +1 -1
- package/bench/runners/assemblyscript.js +28 -0
- package/bench/small.bench.ts +1 -1
- package/bench/tsconfig.json +12 -0
- package/bench/vec3.bench.ts +1 -1
- package/package.json +3 -4
- package/run-bench.as.sh +44 -18
- package/run-bench.js.sh +32 -6
- package/run-tests.sh +1 -1
- package/transform/lib/index.js +381 -122
- package/transform/lib/index.js.map +1 -1
- package/transform/lib/types.js +1 -0
- package/transform/lib/types.js.map +1 -1
- package/transform/src/index.ts +430 -117
- package/transform/src/types.ts +1 -0
- package/assembly/__benches__/lib/index.ts +0 -26
- package/assembly/custom/memory.ts +0 -25
- package/assembly/custom/sink.ts +0 -231
- package/assembly/custom/types.ts +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,20 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 2025-05-22 - 1.0.9
|
|
4
|
+
|
|
5
|
+
- fix: [#132](https://github.com/JairusSW/json-as/issues/132)
|
|
6
|
+
- feat: allow base classes to use their child classes if the signatures match
|
|
7
|
+
- perf: rewrite struct deserialization to be significantly faster
|
|
8
|
+
- fix: [#131](https://github.com/JairusSW/json-as/issues/131) Generic classes with custom deserializer crashing
|
|
9
|
+
- fix: [#66](https://github.com/JairusSW/json-as/issues/66) Throw error when additional keys are in JSON
|
|
10
|
+
|
|
11
|
+
## 2025-05-21 - 1.0.8
|
|
12
|
+
|
|
13
|
+
- fix: inline warnings on layer-2 serialize and deserialize functions
|
|
14
|
+
- feat: fully support `JSON.Obj` and `JSON.Box` everywhere
|
|
15
|
+
- fix: temp disable SIMD
|
|
16
|
+
- feat: write fair benchmarks with `v8` using `jsvu`
|
|
17
|
+
|
|
3
18
|
## 2025-05-14 - 1.0.7
|
|
4
19
|
|
|
5
20
|
- merge: pull request [#128](https://github.com/JairusSW/json-as/pull/128) from [loredanacirstea/nested-custom-serializer-fix](https://github.com/loredanacirstea/nested-custom-serializer-fix)
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
7
7
|
█████ ███████ ██████ ██ ████ ██ ██ ███████
|
|
8
8
|
</span>
|
|
9
|
-
AssemblyScript - v1.0.
|
|
9
|
+
AssemblyScript - v1.0.9
|
|
10
10
|
</pre>
|
|
11
11
|
</h6>
|
|
12
12
|
|
|
@@ -57,6 +57,7 @@ If you'd like to see the code that the transform generates, run the build step w
|
|
|
57
57
|
```typescript
|
|
58
58
|
import { JSON } from "json-as";
|
|
59
59
|
|
|
60
|
+
|
|
60
61
|
@json
|
|
61
62
|
class Vec3 {
|
|
62
63
|
x: f32 = 0.0;
|
|
@@ -64,8 +65,10 @@ class Vec3 {
|
|
|
64
65
|
z: f32 = 0.0;
|
|
65
66
|
}
|
|
66
67
|
|
|
68
|
+
|
|
67
69
|
@json
|
|
68
70
|
class Player {
|
|
71
|
+
|
|
69
72
|
@alias("first name")
|
|
70
73
|
firstName!: string;
|
|
71
74
|
lastName!: string;
|
|
@@ -73,6 +76,7 @@ class Player {
|
|
|
73
76
|
// Drop in a code block, function, or expression that evaluates to a boolean
|
|
74
77
|
@omitif((self: Player) => self.age < 18)
|
|
75
78
|
age!: i32;
|
|
79
|
+
|
|
76
80
|
@omitnull()
|
|
77
81
|
pos!: Vec3 | null;
|
|
78
82
|
isVerified!: boolean;
|
|
@@ -109,9 +113,11 @@ This library allows selective omission of fields during serialization using the
|
|
|
109
113
|
This decorator excludes a field from serialization entirely.
|
|
110
114
|
|
|
111
115
|
```typescript
|
|
116
|
+
|
|
112
117
|
@json
|
|
113
118
|
class Example {
|
|
114
119
|
name!: string;
|
|
120
|
+
|
|
115
121
|
@omit
|
|
116
122
|
SSN!: string;
|
|
117
123
|
}
|
|
@@ -128,9 +134,11 @@ console.log(JSON.stringify(obj)); // { "name": "Jairus" }
|
|
|
128
134
|
This decorator omits a field only if its value is null.
|
|
129
135
|
|
|
130
136
|
```typescript
|
|
137
|
+
|
|
131
138
|
@json
|
|
132
139
|
class Example {
|
|
133
140
|
name!: string;
|
|
141
|
+
|
|
134
142
|
@omitnull()
|
|
135
143
|
optionalField!: string | null;
|
|
136
144
|
}
|
|
@@ -147,9 +155,11 @@ console.log(JSON.stringify(obj)); // { "name": "Jairus" }
|
|
|
147
155
|
This decorator omits a field based on a custom predicate function.
|
|
148
156
|
|
|
149
157
|
```typescript
|
|
158
|
+
|
|
150
159
|
@json
|
|
151
160
|
class Example {
|
|
152
161
|
name!: string;
|
|
162
|
+
|
|
153
163
|
@omitif((self: Example) => self.age <= 18)
|
|
154
164
|
age!: number;
|
|
155
165
|
}
|
|
@@ -174,6 +184,7 @@ AssemblyScript doesn't support using nullable primitive types, so instead, json-
|
|
|
174
184
|
For example, this schema won't compile in AssemblyScript:
|
|
175
185
|
|
|
176
186
|
```typescript
|
|
187
|
+
|
|
177
188
|
@json
|
|
178
189
|
class Person {
|
|
179
190
|
name!: string;
|
|
@@ -184,6 +195,7 @@ class Person {
|
|
|
184
195
|
Instead, use `JSON.Box` to allow nullable primitives:
|
|
185
196
|
|
|
186
197
|
```typescript
|
|
198
|
+
|
|
187
199
|
@json
|
|
188
200
|
class Person {
|
|
189
201
|
name: string;
|
|
@@ -246,6 +258,7 @@ More often, objects will be completely statically typed except for one or two va
|
|
|
246
258
|
In such cases, `JSON.Value` can be used to handle fields that may hold different types at runtime.
|
|
247
259
|
|
|
248
260
|
```typescript
|
|
261
|
+
|
|
249
262
|
@json
|
|
250
263
|
class DynamicObj {
|
|
251
264
|
id: i32 = 0;
|
|
@@ -302,6 +315,7 @@ Here's an example of creating a custom data type called `Point` which serializes
|
|
|
302
315
|
```typescript
|
|
303
316
|
import { bytes } from "json-as/assembly/util";
|
|
304
317
|
|
|
318
|
+
|
|
305
319
|
@json
|
|
306
320
|
class Point {
|
|
307
321
|
x: f64 = 0.0;
|
|
@@ -311,11 +325,13 @@ class Point {
|
|
|
311
325
|
this.y = y;
|
|
312
326
|
}
|
|
313
327
|
|
|
328
|
+
|
|
314
329
|
@serializer
|
|
315
330
|
serializer(self: Point): string {
|
|
316
331
|
return `(${self.x},${self.y})`;
|
|
317
332
|
}
|
|
318
333
|
|
|
334
|
+
|
|
319
335
|
@deserializer
|
|
320
336
|
deserializer(data: string): Point {
|
|
321
337
|
const dataSize = bytes(data);
|
|
@@ -371,7 +387,7 @@ These benchmarks compare this library to JavaScript's native `JSON.stringify` an
|
|
|
371
387
|
|
|
372
388
|
| Test Case | Size | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
373
389
|
| --------------- | ---------- | --------------------- | ----------------------- | -------------------- | ---------------------- |
|
|
374
|
-
| Vector3 Object | 38 bytes |
|
|
390
|
+
| Vector3 Object | 38 bytes | 26,611,226 ops/s | 32,160,804 ops/s | 1,357 MB/s | 1,348 MB/s |
|
|
375
391
|
| Alphabet String | 104 bytes | 13,617,021 ops/s | 18,390,804 ops/s | 1,416 MB/s | 1,986 MB/s |
|
|
376
392
|
| Small Object | 88 bytes | 24,242,424 ops/s | 12,307,692 ops/s | 2,133 MB/s | 1,083 MB/s |
|
|
377
393
|
| Medium Object | 494 bytes | 4,060,913 ops/s | 1,396,160 ops/s | 2,006 MB/s | 689.7 MB/s |
|
package/asconfig.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JSON } from "..";
|
|
2
|
-
import { bench } from "
|
|
2
|
+
import { bench } from "./lib/bench";
|
|
3
3
|
|
|
4
4
|
const v1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
5
5
|
const v2 = '"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"';
|
|
@@ -7,15 +7,15 @@ const v2 = '"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"';
|
|
|
7
7
|
bench(
|
|
8
8
|
"Serialize Alphabet",
|
|
9
9
|
() => {
|
|
10
|
-
JSON.stringify(v1);
|
|
10
|
+
inline.always(JSON.stringify(v1));
|
|
11
11
|
},
|
|
12
|
-
|
|
12
|
+
64_000_00,
|
|
13
13
|
);
|
|
14
14
|
|
|
15
15
|
bench(
|
|
16
16
|
"Deserialize Alphabet",
|
|
17
17
|
() => {
|
|
18
|
-
JSON.parse<string>(v2);
|
|
18
|
+
inline.always(JSON.parse<string>(v2));
|
|
19
19
|
},
|
|
20
|
-
|
|
20
|
+
64_000_00,
|
|
21
21
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JSON } from "..";
|
|
2
|
-
import { bench } from "
|
|
2
|
+
import { bench } from "./lib/bench";
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
@json
|
|
@@ -7,52 +7,6 @@ class Vec3 {
|
|
|
7
7
|
public x!: i32;
|
|
8
8
|
public y!: i32;
|
|
9
9
|
public z!: i32;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@inline __SERIALIZE(ptr: usize): void {
|
|
13
|
-
bs.proposeSize(98);
|
|
14
|
-
store<u64>(bs.offset, 9570664606466171, 0); // {"x"
|
|
15
|
-
store<u16>(bs.offset, 58, 8); // :
|
|
16
|
-
bs.offset += 10;
|
|
17
|
-
JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("x")));
|
|
18
|
-
store<u64>(bs.offset, 9570668901433388, 0); // ,"y"
|
|
19
|
-
store<u16>(bs.offset, 58, 8); // :
|
|
20
|
-
bs.offset += 10;
|
|
21
|
-
JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("y")));
|
|
22
|
-
store<u64>(bs.offset, 9570673196400684, 0); // ,"z"
|
|
23
|
-
store<u16>(bs.offset, 58, 8); // :
|
|
24
|
-
bs.offset += 10;
|
|
25
|
-
JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("z")));
|
|
26
|
-
store<u16>(bs.offset, 125, 0); // }
|
|
27
|
-
bs.offset += 2;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@inline __INITIALIZE(): this {
|
|
32
|
-
return this;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@inline __DESERIALIZE(keyStart: usize, keyEnd: usize, valStart: usize, valEnd: usize, ptr: usize): void {
|
|
37
|
-
switch (load<u16>(keyStart)) {
|
|
38
|
-
case 120: {
|
|
39
|
-
// x
|
|
40
|
-
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("x"));
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
case 121: {
|
|
44
|
-
// y
|
|
45
|
-
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("y"));
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
case 122: {
|
|
49
|
-
// z
|
|
50
|
-
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("z"));
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
10
|
}
|
|
57
11
|
|
|
58
12
|
|
|
@@ -161,17 +115,17 @@ const v1: LargeJSON = {
|
|
|
161
115
|
const v2 = `{"id":2,"name":"Medium Object","age":18,"email":"me@jairus.dev","street":"I don't want to say my street","city":"I don't want to say this either","state":"It really depends","zip":"I forget what it is","tags":["me","dogs","mountains","bar","foo"],"theme":"Hyper Term Black","notifications":true,"language":"en-US","movement":[{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3}]}`;
|
|
162
116
|
|
|
163
117
|
bench(
|
|
164
|
-
"Serialize
|
|
118
|
+
"Serialize Large Object",
|
|
165
119
|
() => {
|
|
166
120
|
JSON.stringify(v1);
|
|
167
121
|
},
|
|
168
|
-
|
|
122
|
+
2_000_00,
|
|
169
123
|
);
|
|
170
124
|
|
|
171
125
|
bench(
|
|
172
|
-
"Deserialize
|
|
126
|
+
"Deserialize Large Object",
|
|
173
127
|
() => {
|
|
174
128
|
JSON.parse<LargeJSON>(v2);
|
|
175
129
|
},
|
|
176
|
-
|
|
130
|
+
2_000_00,
|
|
177
131
|
);
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
export function bench(description: string, routine: () => void, ops: u64 = 1_000_000): void {
|
|
2
2
|
console.log(" - Benchmarking " + description);
|
|
3
|
+
let warmup = ops / 10;
|
|
4
|
+
while (--warmup) {
|
|
5
|
+
routine();
|
|
6
|
+
}
|
|
3
7
|
const start = Date.now();
|
|
4
8
|
let count = ops;
|
|
5
9
|
while (count != 0) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JSON } from "..";
|
|
2
|
-
import { bench } from "
|
|
2
|
+
import { bench } from "./lib/bench";
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
@json
|
|
@@ -34,7 +34,7 @@ bench(
|
|
|
34
34
|
() => {
|
|
35
35
|
JSON.stringify(v1);
|
|
36
36
|
},
|
|
37
|
-
|
|
37
|
+
6_000_00,
|
|
38
38
|
);
|
|
39
39
|
|
|
40
40
|
bench(
|
|
@@ -42,5 +42,5 @@ bench(
|
|
|
42
42
|
() => {
|
|
43
43
|
JSON.parse<MediumJSON>(v2);
|
|
44
44
|
},
|
|
45
|
-
|
|
45
|
+
6_000_00,
|
|
46
46
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JSON } from "..";
|
|
2
|
-
import { bench } from "
|
|
2
|
+
import { bench } from "./lib/bench";
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
@json
|
|
@@ -7,52 +7,6 @@ class Vec3 {
|
|
|
7
7
|
public x!: i32;
|
|
8
8
|
public y!: i32;
|
|
9
9
|
public z!: i32;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
@inline __SERIALIZE(ptr: usize): void {
|
|
13
|
-
bs.proposeSize(98);
|
|
14
|
-
store<u64>(bs.offset, 9570664606466171, 0); // {"x"
|
|
15
|
-
store<u16>(bs.offset, 58, 8); // :
|
|
16
|
-
bs.offset += 10;
|
|
17
|
-
JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("x")));
|
|
18
|
-
store<u64>(bs.offset, 9570668901433388, 0); // ,"y"
|
|
19
|
-
store<u16>(bs.offset, 58, 8); // :
|
|
20
|
-
bs.offset += 10;
|
|
21
|
-
JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("y")));
|
|
22
|
-
store<u64>(bs.offset, 9570673196400684, 0); // ,"z"
|
|
23
|
-
store<u16>(bs.offset, 58, 8); // :
|
|
24
|
-
bs.offset += 10;
|
|
25
|
-
JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("z")));
|
|
26
|
-
store<u16>(bs.offset, 125, 0); // }
|
|
27
|
-
bs.offset += 2;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
@inline __INITIALIZE(): this {
|
|
32
|
-
return this;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@inline __DESERIALIZE(keyStart: usize, keyEnd: usize, valStart: usize, valEnd: usize, ptr: usize): void {
|
|
37
|
-
switch (load<u16>(keyStart)) {
|
|
38
|
-
case 120: {
|
|
39
|
-
// x
|
|
40
|
-
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("x"));
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
case 121: {
|
|
44
|
-
// y
|
|
45
|
-
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("y"));
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
case 122: {
|
|
49
|
-
// z
|
|
50
|
-
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("z"));
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
10
|
}
|
|
57
11
|
|
|
58
12
|
const v1: Vec3 = { x: 1, y: 2, z: 3 };
|
|
@@ -63,7 +17,7 @@ bench(
|
|
|
63
17
|
() => {
|
|
64
18
|
JSON.stringify(v1);
|
|
65
19
|
},
|
|
66
|
-
|
|
20
|
+
128_000_00,
|
|
67
21
|
);
|
|
68
22
|
|
|
69
23
|
bench(
|
|
@@ -71,5 +25,5 @@ bench(
|
|
|
71
25
|
() => {
|
|
72
26
|
JSON.parse<Vec3>(v2);
|
|
73
27
|
},
|
|
74
|
-
|
|
28
|
+
128_000_00,
|
|
75
29
|
);
|
|
@@ -32,11 +32,12 @@ class Point {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
|
|
35
36
|
@json
|
|
36
|
-
|
|
37
|
-
value: Point = new Point(0, 0)
|
|
37
|
+
class ObjectWithCustom {
|
|
38
|
+
value: Point = new Point(0, 0);
|
|
38
39
|
constructor(value: Point) {
|
|
39
|
-
this.value = value
|
|
40
|
+
this.value = value;
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { OBJECT, TOTAL_OVERHEAD } from "rt/common";
|
|
2
|
+
import { JSON } from "..";
|
|
3
|
+
import { describe, expect } from "./lib";
|
|
4
|
+
|
|
5
|
+
@json
|
|
6
|
+
class Foo {
|
|
7
|
+
a: i32 = 0;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
@json
|
|
11
|
+
class Bar extends Foo {
|
|
12
|
+
b: i32 = 0;
|
|
13
|
+
|
|
14
|
+
@serializer
|
|
15
|
+
serialize(self: Bar): string {
|
|
16
|
+
return `"bar"`;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@deserializer
|
|
20
|
+
deserialize(data: string): Bar {
|
|
21
|
+
return data == "\"bar\"" ? {
|
|
22
|
+
a: 1,
|
|
23
|
+
b: 2,
|
|
24
|
+
} : new Bar();
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
describe("should use custom serializer for subclasses", () => {
|
|
29
|
+
const bar = new Bar();
|
|
30
|
+
bar.a = 1;
|
|
31
|
+
bar.b = 2;
|
|
32
|
+
const data = JSON.stringify(bar);
|
|
33
|
+
expect(data).toBe('"bar"');
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
describe("should use custom serializer for subclasses when type is the parent", () => {
|
|
37
|
+
const bar = new Bar();
|
|
38
|
+
bar.a = 1;
|
|
39
|
+
bar.b = 2;
|
|
40
|
+
const data = JSON.stringify<Foo>(bar);
|
|
41
|
+
expect(data).toBe('"bar"');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
describe("should use custom deserializer for subclass", () => {
|
|
45
|
+
const json = '"bar"';
|
|
46
|
+
const bar = JSON.parse<Bar>(json);
|
|
47
|
+
expect(bar.a.toString()).toBe("1");
|
|
48
|
+
expect(bar.b.toString()).toBe("2");
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
describe("should use custom deserializer even when type is the parent", () => {
|
|
52
|
+
const json = '"bar"';
|
|
53
|
+
const foo = JSON.parse<Bar>(json);
|
|
54
|
+
expect(foo.a.toString()).toBe("1");
|
|
55
|
+
});
|
package/assembly/custom/util.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { isSpace } from "util/string";
|
|
2
|
-
import { BACK_SLASH, QUOTE } from "./chars";
|
|
3
|
-
import { Sink } from "./sink";
|
|
4
|
-
|
|
5
1
|
/** Scientific Notation Integer Parsing - SNIP
|
|
6
2
|
* This is absolutely the fastest algorithm I could think of while adding full support for Scientific Notation
|
|
7
3
|
* Loads 32 bits and retrieves the high/low bits.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { isSpace } from "../../../util";
|
|
2
|
+
import { COMMA, BRACKET_RIGHT } from "../../../custom/chars";
|
|
3
|
+
import { JSON } from "../../..";
|
|
4
|
+
|
|
5
|
+
export function deserializeBoxArray<T extends JSON.Box<any>[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
|
|
6
|
+
const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
|
|
7
|
+
if (isBoolean<valueof<T>>()) {
|
|
8
|
+
srcStart += 2; // skip [
|
|
9
|
+
while (srcStart < srcEnd) {
|
|
10
|
+
const block = load<u64>(srcStart);
|
|
11
|
+
if (block == 28429475166421108) {
|
|
12
|
+
out.push(JSON.Box.from(true));
|
|
13
|
+
srcStart += 10;
|
|
14
|
+
} else if (block == 32370086184550502 && load<u16>(srcStart, 8) == 101) {
|
|
15
|
+
out.push(JSON.Box.from(false));
|
|
16
|
+
srcStart += 12;
|
|
17
|
+
} else {
|
|
18
|
+
srcStart += 2;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
} else {
|
|
23
|
+
let lastIndex: usize = 0;
|
|
24
|
+
while (srcStart < srcEnd) {
|
|
25
|
+
const code = load<u16>(srcStart);
|
|
26
|
+
if (code - 48 <= 9 || code == 45) {
|
|
27
|
+
lastIndex = srcStart;
|
|
28
|
+
srcStart += 2;
|
|
29
|
+
while (srcStart < srcEnd) {
|
|
30
|
+
const code = load<u16>(srcStart);
|
|
31
|
+
if (code == COMMA || code == BRACKET_RIGHT || isSpace(code)) {
|
|
32
|
+
out.push(JSON.__deserialize<valueof<T>>(lastIndex, srcStart));
|
|
33
|
+
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
34
|
+
// /* empty */
|
|
35
|
+
// }
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
srcStart += 2;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
srcStart += 2;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT } from "../../../custom/chars";
|
|
2
|
+
import { JSON } from "../../..";
|
|
3
|
+
import { isSpace } from "util/string";
|
|
4
|
+
|
|
5
|
+
export function deserializeObjectArray<T extends unknown[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
|
|
6
|
+
const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
|
|
7
|
+
let lastIndex: usize = 0;
|
|
8
|
+
let depth: u32 = 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 == 0) throw new Error("Input string had zero length or was all whitespace");
|
|
14
|
+
|
|
15
|
+
if (load<u16>(srcStart) != BRACKET_LEFT) throw new Error("Expected '[' at start of object at position " + (srcEnd - srcStart).toString());
|
|
16
|
+
if (load<u16>(srcEnd - 2) != BRACKET_RIGHT) throw new Error("Expected ']' at end of object at position " + (srcEnd - srcStart).toString());
|
|
17
|
+
|
|
18
|
+
while (srcStart < srcEnd) {
|
|
19
|
+
const code = load<u16>(srcStart);
|
|
20
|
+
if (code == BRACE_LEFT && depth++ == 0) {
|
|
21
|
+
lastIndex = srcStart;
|
|
22
|
+
} else if (code == BRACE_RIGHT && --depth == 0) {
|
|
23
|
+
out.push(JSON.__deserialize<valueof<T>>(lastIndex, (srcStart += 2)));
|
|
24
|
+
}
|
|
25
|
+
srcStart += 2;
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
@@ -7,6 +7,8 @@ import { deserializeIntegerArray } from "./array/integer";
|
|
|
7
7
|
import { deserializeMapArray } from "./array/map";
|
|
8
8
|
import { deserializeStructArray } from "./array/struct";
|
|
9
9
|
import { deserializeStringArray } from "./array/string";
|
|
10
|
+
import { deserializeObjectArray } from "./array/object";
|
|
11
|
+
import { deserializeBoxArray } from "./array/box";
|
|
10
12
|
|
|
11
13
|
// @ts-ignore: Decorator valid here
|
|
12
14
|
export function deserializeArray<T extends unknown[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
|
|
@@ -29,6 +31,12 @@ export function deserializeArray<T extends unknown[]>(srcStart: usize, srcEnd: u
|
|
|
29
31
|
if (type instanceof JSON.Value) {
|
|
30
32
|
// @ts-ignore: type
|
|
31
33
|
return deserializeArbitraryArray(srcStart, srcEnd, dst);
|
|
34
|
+
} else if (type instanceof JSON.Box) {
|
|
35
|
+
// @ts-ignore: type
|
|
36
|
+
return deserializeBoxArray<T>(srcStart, srcEnd, dst);
|
|
37
|
+
} else if (type instanceof JSON.Obj) {
|
|
38
|
+
// @ts-ignore: type
|
|
39
|
+
return deserializeObjectArray<T>(srcStart, srcEnd, dst);
|
|
32
40
|
} else if (type instanceof Map) {
|
|
33
41
|
// @ts-ignore: type
|
|
34
42
|
return deserializeMapArray<T>(srcStart, srcEnd, dst);
|