json-as 1.0.0 → 1.0.2
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/.github/workflows/{nodejs.yml → tests.yml} +1 -1
- package/CHANGELOG.md +8 -0
- package/README.md +60 -12
- package/assembly/__benches__/abc.bench.ts +21 -0
- package/assembly/__benches__/large.bench.ts +174 -0
- package/assembly/__benches__/lib/index.ts +26 -0
- package/assembly/__benches__/medium.bench.ts +46 -0
- package/assembly/__benches__/small.bench.ts +33 -0
- package/assembly/__benches__/vec3.bench.ts +72 -0
- package/assembly/__tests__/struct.spec.ts +20 -22
- package/assembly/deserialize/simple/bool.ts +2 -1
- package/assembly/deserialize/simple/date.ts +2 -1
- package/assembly/deserialize/simple/float.ts +2 -1
- package/assembly/deserialize/simple/raw.ts +2 -1
- package/assembly/deserialize/simple/string.ts +2 -1
- package/assembly/deserialize/simple/struct.ts +0 -1
- package/assembly/index.ts +65 -58
- package/assembly/serialize/simple/float.ts +2 -1
- package/assembly/serialize/simple/integer.ts +2 -1
- package/assembly/serialize/simple/raw.ts +2 -1
- package/assembly/serialize/simple/string.ts +2 -1
- package/assembly/test.ts +12 -1
- package/bench/abc.bench.ts +20 -0
- package/bench/large.bench.ts +126 -0
- package/bench/medium.bench.ts +43 -0
- package/bench/small.bench.ts +30 -0
- package/bench/vec3.bench.ts +26 -0
- package/package.json +28 -31
- package/run-bench.as.sh +27 -0
- package/run-bench.js.sh +12 -0
- package/run-tests.sh +14 -2
- package/transform/lib/index.js +2 -2
- package/transform/lib/index.js.map +1 -1
- package/transform/src/index.ts +2 -2
- package/assembly/__benches__/misc.bench.ts +0 -47
- package/assembly/__benches__/schemas.ts +0 -25
- package/assembly/__benches__/string.bench.ts +0 -23
- package/assembly/__benches__/struct.bench.ts +0 -21
- package/bench/schemas.ts +0 -5
- package/bench/string.bench.ts +0 -16
- /package/bench/{bench.ts → lib/bench.ts} +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 2025-03-19 - 1.0.2
|
|
4
|
+
|
|
5
|
+
- fix: include check for nullable types for properties when deserialization is called internally [#118](https://github.com/JairusSW/json-as/pull/118)
|
|
6
|
+
|
|
7
|
+
## 2025-03-10 - 1.0.1
|
|
8
|
+
|
|
9
|
+
- docs: add comprehensive performance metrics
|
|
10
|
+
|
|
3
11
|
## 2025-03-09 - 1.0.0
|
|
4
12
|
|
|
5
13
|
- fix: relative paths pointing through node_modules would create a second Source
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
7
7
|
█████ ███████ ██████ ██ ████ ██ ██ ███████
|
|
8
8
|
</span>
|
|
9
|
-
AssemblyScript - v1.0.
|
|
9
|
+
AssemblyScript - v1.0.2
|
|
10
10
|
</pre>
|
|
11
11
|
</h5>
|
|
12
12
|
|
|
@@ -14,19 +14,17 @@
|
|
|
14
14
|
|
|
15
15
|
JSON is the de-facto serialization format of modern web applications, but its serialization and deserialization remain a significant performance bottleneck, especially at scale. Traditional parsing approaches are computationally expensive, adding unnecessary overhead to both clients and servers. This library is designed to mitigate this by leveraging SIMD acceleration and highly optimized transformations.
|
|
16
16
|
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
🔹Breaking changes to the way custom serializers/deserializers function (See Custom Serializers below)
|
|
17
|
+
## 🔭 What's new
|
|
20
18
|
|
|
21
19
|
🔹Major performance improvements and addition of SIMD
|
|
22
20
|
|
|
23
|
-
🔹
|
|
21
|
+
🔹Near zero-growth allocation design and low overhead
|
|
24
22
|
|
|
25
|
-
🔹
|
|
23
|
+
🔹Support for custom serializer and deserializers
|
|
26
24
|
|
|
27
|
-
🔹
|
|
25
|
+
🔹Fixes to many, many, bugs and edge cases
|
|
28
26
|
|
|
29
|
-
🔹
|
|
27
|
+
🔹Support for dynamic objects, arrays, arbitrary values, and raw types
|
|
30
28
|
|
|
31
29
|
## 📚 Contents
|
|
32
30
|
|
|
@@ -374,11 +372,13 @@ This allows custom serialization while maintaining a generic interface for the l
|
|
|
374
372
|
|
|
375
373
|
## ⚡ Performance
|
|
376
374
|
|
|
377
|
-
The `json-as` library has been optimized to achieve near-gigabyte-per-second JSON processing speeds through SIMD acceleration and highly efficient transformations. Below are
|
|
375
|
+
The `json-as` library has been optimized to achieve near-gigabyte-per-second JSON processing speeds through SIMD acceleration and highly efficient transformations. Below are detailed statistics comparing performance metrics such as build time, operations-per-second, and throughput.
|
|
376
|
+
|
|
377
|
+
### 🔍 Comparison to JavaScript
|
|
378
378
|
|
|
379
|
-
|
|
379
|
+
These benchmarks compare this library to JavaScript's native `JSON.stringify` and `JSON.parse` functions.
|
|
380
380
|
|
|
381
|
-
**Table 1** -
|
|
381
|
+
**Table 1** - _AssemblyScript (LLVM)_
|
|
382
382
|
|
|
383
383
|
| Test Case | Size | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
384
384
|
| --------------- | ---------- | --------------------- | ----------------------- | -------------------- | ---------------------- |
|
|
@@ -388,7 +388,7 @@ Note: the AssemblyScript benches are run using a _bump allocator_ so that Garbag
|
|
|
388
388
|
| Medium Object | 494 bytes | 4,060,913 ops/s | 1,396,160 ops/s | 2,006 MB/s | 689.7 MB/s |
|
|
389
389
|
| Large Object | 3374 bytes | 614,754 ops/s | 132,802 ops/s | 2,074 MB/s | 448.0 MB/s |
|
|
390
390
|
|
|
391
|
-
**Table 2** -
|
|
391
|
+
**Table 2** - _JavaScript (V8)_
|
|
392
392
|
|
|
393
393
|
| Test Case | Size | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
394
394
|
| --------------- | ---------- | --------------------- | ----------------------- | -------------------- | ---------------------- |
|
|
@@ -398,6 +398,54 @@ Note: the AssemblyScript benches are run using a _bump allocator_ so that Garbag
|
|
|
398
398
|
| Medium Object | 494 bytes | 2,395,210 ops/s | 1,381,693 ops/s | 1,183 MB/s | 682.5 MB/s |
|
|
399
399
|
| Large Object | 3374 bytes | 222,222 ops/s | 117,233 ops/s | 749.7 MB/s | 395.5 MB/s |
|
|
400
400
|
|
|
401
|
+
**📌 Insights**
|
|
402
|
+
|
|
403
|
+
- JSON-AS consistently outperforms JavaScript's native implementation.
|
|
404
|
+
|
|
405
|
+
- **Serialization Speed:**
|
|
406
|
+
- JSON-AS achieves speeds up to `2,133 MB/s`, significantly faster than JavaScript's peak of `1,416 MB/s`.
|
|
407
|
+
- Large objects see the biggest improvement, with JSON-AS at `2,074 MB/s` vs. JavaScript’s `749.7 MB/s`.
|
|
408
|
+
|
|
409
|
+
- **Deserialization Speed:**
|
|
410
|
+
- JSON-AS reaches `1,986 MB/s`, while JavaScript caps at `1,592 MB/s`.
|
|
411
|
+
- Small and medium objects see the most significant performance boost overall.
|
|
412
|
+
|
|
413
|
+
### 📈 Comparison to v0.9.x version
|
|
414
|
+
|
|
415
|
+
**Table 1** - _v1.0.0_
|
|
416
|
+
|
|
417
|
+
| Test Case | Size | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
418
|
+
| --------------- | ---------- | --------------------- | ----------------------- | -------------------- | ---------------------- |
|
|
419
|
+
| Vector3 Object | 38 bytes | 35,714,285 ops/s | 35,435,552 ops/s | 1,357 MB/s | 1,348 MB/s |
|
|
420
|
+
| Alphabet String | 104 bytes | 13,617,021 ops/s | 18,390,804 ops/s | 1,416 MB/s | 1,986 MB/s |
|
|
421
|
+
| Small Object | 88 bytes | 24,242,424 ops/s | 12,307,692 ops/s | 2,133 MB/s | 1,083 MB/s |
|
|
422
|
+
| Medium Object | 494 bytes | 4,060,913 ops/s | 1,396,160 ops/s | 2,006 MB/s | 689.7 MB/s |
|
|
423
|
+
| Large Object | 3374 bytes | 614,754 ops/s | 132,802 ops/s | 2,074 MB/s | 448.0 MB/s |
|
|
424
|
+
|
|
425
|
+
**Table 2** - _v0.9.29_
|
|
426
|
+
|
|
427
|
+
| Test Case | Size | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
428
|
+
| --------------- | ---------- | --------------------- | ----------------------- | -------------------- | ---------------------- |
|
|
429
|
+
| Vector3 Object | 38 bytes | 6,896,551 ops/s | 10,958,904 ops/s | 262.1 MB/s | 416.4 MB/s |
|
|
430
|
+
| Alphabet String | 104 bytes | 5,128,205 ops/s | 8,695,652 ops/s | 533.3 MB/s | 939.1 MB/s |
|
|
431
|
+
| Small Object | 88 bytes | 4,953,560 ops/s | 3,678,160 ops/s | 435.9 MB/s | 323.7 MB/s |
|
|
432
|
+
| Medium Object | 494 bytes | 522,193 ops/s | 508,582 ops/s | 258.0 MB/s | 251.2 MB/s |
|
|
433
|
+
| Large Object | 3374 bytes | 51,229 ops/s | 65,585 ops/s | 172.8 MB/s | 221.3 MB/s |
|
|
434
|
+
|
|
435
|
+
**📌 Insights:**
|
|
436
|
+
|
|
437
|
+
- Massive performance improvements in JSON-AS `v1.0.0`:
|
|
438
|
+
- Serialization is **2-12x faster** (e.g., Large Object: `2,074 MB/s` vs. `172.8 MB/s`).
|
|
439
|
+
- Deserialization is **2-3x faster** (e.g., Large Object: `1,348 MB/s` vs. `221.3 MB/s`).
|
|
440
|
+
- Vector3 Object serialization improved from `416 MB/s` to `1,357 MB/s`--a **3x benefit** through new code generation techniques.
|
|
441
|
+
|
|
442
|
+
## 🔭 What's Next
|
|
443
|
+
|
|
444
|
+
- Theorize plans to keep key-order in generated schemas
|
|
445
|
+
- Generate optimized deserialization methods
|
|
446
|
+
- Inline specific hot code paths
|
|
447
|
+
- Implement error handling implementation
|
|
448
|
+
|
|
401
449
|
## 📃 License
|
|
402
450
|
|
|
403
451
|
This project is distributed under an open source license. You can view the full license using the following link: [License](./LICENSE)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { JSON } from "..";
|
|
2
|
+
import { bench } from "../custom/bench";
|
|
3
|
+
|
|
4
|
+
const v1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
5
|
+
const v2 = '"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"';
|
|
6
|
+
|
|
7
|
+
bench(
|
|
8
|
+
"Serialize Alphabet",
|
|
9
|
+
() => {
|
|
10
|
+
JSON.stringify(v1);
|
|
11
|
+
},
|
|
12
|
+
1_000_00,
|
|
13
|
+
);
|
|
14
|
+
|
|
15
|
+
bench(
|
|
16
|
+
"Deserialize Alphabet",
|
|
17
|
+
() => {
|
|
18
|
+
JSON.parse<string>(v2);
|
|
19
|
+
},
|
|
20
|
+
1_000_00,
|
|
21
|
+
);
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { JSON } from "..";
|
|
2
|
+
import { bench } from "../custom/bench";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@json
|
|
6
|
+
class Vec3 {
|
|
7
|
+
public x!: i32;
|
|
8
|
+
public y!: i32;
|
|
9
|
+
public z!: i32;
|
|
10
|
+
|
|
11
|
+
@inline __SERIALIZE(ptr: usize): void {
|
|
12
|
+
bs.proposeSize(98);
|
|
13
|
+
store<u64>(bs.offset, 9570664606466171, 0); // {"x"
|
|
14
|
+
store<u16>(bs.offset, 58, 8); // :
|
|
15
|
+
bs.offset += 10;
|
|
16
|
+
JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("x")));
|
|
17
|
+
store<u64>(bs.offset, 9570668901433388, 0); // ,"y"
|
|
18
|
+
store<u16>(bs.offset, 58, 8); // :
|
|
19
|
+
bs.offset += 10;
|
|
20
|
+
JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("y")));
|
|
21
|
+
store<u64>(bs.offset, 9570673196400684, 0); // ,"z"
|
|
22
|
+
store<u16>(bs.offset, 58, 8); // :
|
|
23
|
+
bs.offset += 10;
|
|
24
|
+
JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("z")));
|
|
25
|
+
store<u16>(bs.offset, 125, 0); // }
|
|
26
|
+
bs.offset += 2;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@inline __INITIALIZE(): this {
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@inline __DESERIALIZE(keyStart: usize, keyEnd: usize, valStart: usize, valEnd: usize, ptr: usize): void {
|
|
34
|
+
switch (load<u16>(keyStart)) {
|
|
35
|
+
case 120: {
|
|
36
|
+
// x
|
|
37
|
+
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("x"));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
case 121: {
|
|
41
|
+
// y
|
|
42
|
+
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("y"));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
case 122: {
|
|
46
|
+
// z
|
|
47
|
+
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("z"));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
@json
|
|
57
|
+
class LargeJSON {
|
|
58
|
+
public id!: i32;
|
|
59
|
+
public name!: string;
|
|
60
|
+
public age!: i32;
|
|
61
|
+
public email!: string;
|
|
62
|
+
public street!: string;
|
|
63
|
+
public city!: string;
|
|
64
|
+
public state!: string;
|
|
65
|
+
public zip!: string;
|
|
66
|
+
public tags!: string[];
|
|
67
|
+
public theme!: string;
|
|
68
|
+
public notifications!: boolean;
|
|
69
|
+
public language!: string;
|
|
70
|
+
public movement!: Vec3[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const v1: LargeJSON = {
|
|
74
|
+
id: 2,
|
|
75
|
+
name: "Medium Object",
|
|
76
|
+
age: 18,
|
|
77
|
+
email: "me@jairus.dev",
|
|
78
|
+
street: "I don't want to say my street",
|
|
79
|
+
city: "I don't want to say this either",
|
|
80
|
+
state: "It really depends",
|
|
81
|
+
zip: "I forget what it is",
|
|
82
|
+
tags: ["me", "dogs", "mountains", "bar", "foo"],
|
|
83
|
+
theme: "Hyper Term Black",
|
|
84
|
+
notifications: true,
|
|
85
|
+
language: "en-US",
|
|
86
|
+
movement: [
|
|
87
|
+
{ x: 1, y: 2, z: 3 },
|
|
88
|
+
{ x: 1, y: 2, z: 3 },
|
|
89
|
+
{ x: 1, y: 2, z: 3 },
|
|
90
|
+
{ x: 1, y: 2, z: 3 },
|
|
91
|
+
{ x: 1, y: 2, z: 3 },
|
|
92
|
+
{ x: 1, y: 2, z: 3 },
|
|
93
|
+
{ x: 1, y: 2, z: 3 },
|
|
94
|
+
{ x: 1, y: 2, z: 3 },
|
|
95
|
+
{ x: 1, y: 2, z: 3 },
|
|
96
|
+
{ x: 1, y: 2, z: 3 },
|
|
97
|
+
{ x: 1, y: 2, z: 3 },
|
|
98
|
+
{ x: 1, y: 2, z: 3 },
|
|
99
|
+
{ x: 1, y: 2, z: 3 },
|
|
100
|
+
{ x: 1, y: 2, z: 3 },
|
|
101
|
+
{ x: 1, y: 2, z: 3 },
|
|
102
|
+
{ x: 1, y: 2, z: 3 },
|
|
103
|
+
{ x: 1, y: 2, z: 3 },
|
|
104
|
+
{ x: 1, y: 2, z: 3 },
|
|
105
|
+
{ x: 1, y: 2, z: 3 },
|
|
106
|
+
{ x: 1, y: 2, z: 3 },
|
|
107
|
+
{ x: 1, y: 2, z: 3 },
|
|
108
|
+
{ x: 1, y: 2, z: 3 },
|
|
109
|
+
{ x: 1, y: 2, z: 3 },
|
|
110
|
+
{ x: 1, y: 2, z: 3 },
|
|
111
|
+
{ x: 1, y: 2, z: 3 },
|
|
112
|
+
{ x: 1, y: 2, z: 3 },
|
|
113
|
+
{ x: 1, y: 2, z: 3 },
|
|
114
|
+
{ x: 1, y: 2, z: 3 },
|
|
115
|
+
{ x: 1, y: 2, z: 3 },
|
|
116
|
+
{ x: 1, y: 2, z: 3 },
|
|
117
|
+
{ x: 1, y: 2, z: 3 },
|
|
118
|
+
{ x: 1, y: 2, z: 3 },
|
|
119
|
+
{ x: 1, y: 2, z: 3 },
|
|
120
|
+
{ x: 1, y: 2, z: 3 },
|
|
121
|
+
{ x: 1, y: 2, z: 3 },
|
|
122
|
+
{ x: 1, y: 2, z: 3 },
|
|
123
|
+
{ x: 1, y: 2, z: 3 },
|
|
124
|
+
{ x: 1, y: 2, z: 3 },
|
|
125
|
+
{ x: 1, y: 2, z: 3 },
|
|
126
|
+
{ x: 1, y: 2, z: 3 },
|
|
127
|
+
{ x: 1, y: 2, z: 3 },
|
|
128
|
+
{ x: 1, y: 2, z: 3 },
|
|
129
|
+
{ x: 1, y: 2, z: 3 },
|
|
130
|
+
{ x: 1, y: 2, z: 3 },
|
|
131
|
+
{ x: 1, y: 2, z: 3 },
|
|
132
|
+
{ x: 1, y: 2, z: 3 },
|
|
133
|
+
{ x: 1, y: 2, z: 3 },
|
|
134
|
+
{ x: 1, y: 2, z: 3 },
|
|
135
|
+
{ x: 1, y: 2, z: 3 },
|
|
136
|
+
{ x: 1, y: 2, z: 3 },
|
|
137
|
+
{ x: 1, y: 2, z: 3 },
|
|
138
|
+
{ x: 1, y: 2, z: 3 },
|
|
139
|
+
{ x: 1, y: 2, z: 3 },
|
|
140
|
+
{ x: 1, y: 2, z: 3 },
|
|
141
|
+
{ x: 1, y: 2, z: 3 },
|
|
142
|
+
{ x: 1, y: 2, z: 3 },
|
|
143
|
+
{ x: 1, y: 2, z: 3 },
|
|
144
|
+
{ x: 1, y: 2, z: 3 },
|
|
145
|
+
{ x: 1, y: 2, z: 3 },
|
|
146
|
+
{ x: 1, y: 2, z: 3 },
|
|
147
|
+
{ x: 1, y: 2, z: 3 },
|
|
148
|
+
{ x: 1, y: 2, z: 3 },
|
|
149
|
+
{ x: 1, y: 2, z: 3 },
|
|
150
|
+
{ x: 1, y: 2, z: 3 },
|
|
151
|
+
{ x: 1, y: 2, z: 3 },
|
|
152
|
+
{ x: 1, y: 2, z: 3 },
|
|
153
|
+
{ x: 1, y: 2, z: 3 },
|
|
154
|
+
{ x: 1, y: 2, z: 3 },
|
|
155
|
+
],
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
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}]}`;
|
|
159
|
+
|
|
160
|
+
bench(
|
|
161
|
+
"Serialize Medium Object",
|
|
162
|
+
() => {
|
|
163
|
+
JSON.stringify(v1);
|
|
164
|
+
},
|
|
165
|
+
3_000_00,
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
bench(
|
|
169
|
+
"Deserialize Medium Object",
|
|
170
|
+
() => {
|
|
171
|
+
JSON.parse<LargeJSON>(v2);
|
|
172
|
+
},
|
|
173
|
+
3_000_00,
|
|
174
|
+
);
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function describe(description: string, routine: () => void): void {
|
|
2
|
+
routine();
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
export function it(description: string, routine: () => void): void {
|
|
6
|
+
routine();
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function expect(left: string): Expectation {
|
|
10
|
+
return new Expectation(left);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
class Expectation {
|
|
14
|
+
public left: string;
|
|
15
|
+
|
|
16
|
+
constructor(left: string) {
|
|
17
|
+
this.left = left;
|
|
18
|
+
}
|
|
19
|
+
toBe(right: string): void {
|
|
20
|
+
if (this.left != right) {
|
|
21
|
+
console.log(" (expected) -> " + right);
|
|
22
|
+
console.log(" (received) -> " + this.left);
|
|
23
|
+
process.exit(1);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { JSON } from "..";
|
|
2
|
+
import { bench } from "../custom/bench";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@json
|
|
6
|
+
class MediumJSON {
|
|
7
|
+
public id!: i32;
|
|
8
|
+
public name!: string;
|
|
9
|
+
public age!: i32;
|
|
10
|
+
public email!: string;
|
|
11
|
+
public street!: string;
|
|
12
|
+
public city!: string;
|
|
13
|
+
public state!: string;
|
|
14
|
+
public zip!: string;
|
|
15
|
+
public tags!: string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const v1: MediumJSON = {
|
|
19
|
+
id: 2,
|
|
20
|
+
name: "Medium Object",
|
|
21
|
+
age: 18,
|
|
22
|
+
email: "me@jairus.dev",
|
|
23
|
+
street: "I don't want to say my street",
|
|
24
|
+
city: "I don't want to say this either",
|
|
25
|
+
state: "It really depends",
|
|
26
|
+
zip: "I forget what it is",
|
|
27
|
+
tags: ["me", "dogs", "mountains", "bar", "foo"],
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
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"]}`;
|
|
31
|
+
|
|
32
|
+
bench(
|
|
33
|
+
"Serialize Medium Object",
|
|
34
|
+
() => {
|
|
35
|
+
JSON.stringify(v1);
|
|
36
|
+
},
|
|
37
|
+
8_000_00,
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
bench(
|
|
41
|
+
"Deserialize Medium Object",
|
|
42
|
+
() => {
|
|
43
|
+
JSON.parse<MediumJSON>(v2);
|
|
44
|
+
},
|
|
45
|
+
8_000_00,
|
|
46
|
+
);
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { JSON } from "..";
|
|
2
|
+
import { bench } from "../custom/bench";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@json
|
|
6
|
+
class SmallJSON {
|
|
7
|
+
public id!: i32;
|
|
8
|
+
public name!: string;
|
|
9
|
+
public active!: boolean;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const v1: SmallJSON = {
|
|
13
|
+
id: 1,
|
|
14
|
+
name: "Small Object",
|
|
15
|
+
active: true,
|
|
16
|
+
};
|
|
17
|
+
const v2 = '{"id":1,"name":"Small Object","active":true}';
|
|
18
|
+
|
|
19
|
+
bench(
|
|
20
|
+
"Serialize Small Object",
|
|
21
|
+
() => {
|
|
22
|
+
JSON.stringify(v1);
|
|
23
|
+
},
|
|
24
|
+
16_000_00,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
bench(
|
|
28
|
+
"Deserialize Small Object",
|
|
29
|
+
() => {
|
|
30
|
+
JSON.parse<SmallJSON>(v2);
|
|
31
|
+
},
|
|
32
|
+
16_000_00,
|
|
33
|
+
);
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { JSON } from "..";
|
|
2
|
+
import { bench } from "../custom/bench";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
@json
|
|
6
|
+
class Vec3 {
|
|
7
|
+
public x!: i32;
|
|
8
|
+
public y!: i32;
|
|
9
|
+
public z!: i32;
|
|
10
|
+
|
|
11
|
+
@inline __SERIALIZE(ptr: usize): void {
|
|
12
|
+
bs.proposeSize(98);
|
|
13
|
+
store<u64>(bs.offset, 9570664606466171, 0); // {"x"
|
|
14
|
+
store<u16>(bs.offset, 58, 8); // :
|
|
15
|
+
bs.offset += 10;
|
|
16
|
+
JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("x")));
|
|
17
|
+
store<u64>(bs.offset, 9570668901433388, 0); // ,"y"
|
|
18
|
+
store<u16>(bs.offset, 58, 8); // :
|
|
19
|
+
bs.offset += 10;
|
|
20
|
+
JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("y")));
|
|
21
|
+
store<u64>(bs.offset, 9570673196400684, 0); // ,"z"
|
|
22
|
+
store<u16>(bs.offset, 58, 8); // :
|
|
23
|
+
bs.offset += 10;
|
|
24
|
+
JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("z")));
|
|
25
|
+
store<u16>(bs.offset, 125, 0); // }
|
|
26
|
+
bs.offset += 2;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@inline __INITIALIZE(): this {
|
|
30
|
+
return this;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
@inline __DESERIALIZE(keyStart: usize, keyEnd: usize, valStart: usize, valEnd: usize, ptr: usize): void {
|
|
34
|
+
switch (load<u16>(keyStart)) {
|
|
35
|
+
case 120: {
|
|
36
|
+
// x
|
|
37
|
+
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("x"));
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
case 121: {
|
|
41
|
+
// y
|
|
42
|
+
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("y"));
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
case 122: {
|
|
46
|
+
// z
|
|
47
|
+
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("z"));
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const v1: Vec3 = { x: 1, y: 2, z: 3 };
|
|
56
|
+
const v2 = '{"x":1,"y":2,"z":3}';
|
|
57
|
+
|
|
58
|
+
bench(
|
|
59
|
+
"Serialize Vec3",
|
|
60
|
+
() => {
|
|
61
|
+
JSON.stringify(v1);
|
|
62
|
+
},
|
|
63
|
+
16_000_00,
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
bench(
|
|
67
|
+
"Deserialize Vec3",
|
|
68
|
+
() => {
|
|
69
|
+
JSON.parse<Vec3>(v2);
|
|
70
|
+
},
|
|
71
|
+
16_000_00,
|
|
72
|
+
);
|
|
@@ -64,6 +64,16 @@ describe("Should deserialize structs with whitespace", () => {
|
|
|
64
64
|
).toBe('{"x":3.4,"y":1.2,"z":8.3}');
|
|
65
65
|
});
|
|
66
66
|
|
|
67
|
+
describe("Should deserialize structs with nullable properties", () => {
|
|
68
|
+
expect(
|
|
69
|
+
JSON.stringify(JSON.parse<NullableObj>('{"bar":{"value":"test"}}'))
|
|
70
|
+
).toBe('{"bar":{"value":"test"}}');
|
|
71
|
+
|
|
72
|
+
expect(
|
|
73
|
+
JSON.stringify(JSON.parse<NullableObj>('{"bar":null}'))
|
|
74
|
+
).toBe('{"bar":null}');
|
|
75
|
+
})
|
|
76
|
+
|
|
67
77
|
// describe("Should serialize Suite struct", () => {
|
|
68
78
|
|
|
69
79
|
// });
|
|
@@ -106,47 +116,35 @@ class Player {
|
|
|
106
116
|
}
|
|
107
117
|
|
|
108
118
|
|
|
109
|
-
@json
|
|
110
|
-
class ObjWithString {
|
|
111
|
-
s!: string;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
|
|
115
119
|
@json
|
|
116
120
|
class ObjWithStrangeKey<T> {
|
|
117
121
|
@alias('a\\\t"\x02b`c')
|
|
118
122
|
data!: T;
|
|
119
123
|
}
|
|
120
124
|
|
|
121
|
-
|
|
122
|
-
@json
|
|
123
|
-
class ObjectWithStringArray {
|
|
124
|
-
sa!: string[];
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
|
|
128
125
|
@json
|
|
129
126
|
class ObjectWithFloat {
|
|
130
127
|
f!: f64;
|
|
131
128
|
}
|
|
132
129
|
|
|
133
|
-
|
|
134
|
-
@json
|
|
135
|
-
class ObjectWithFloatArray {
|
|
136
|
-
fa!: f64[];
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
|
|
140
130
|
@json
|
|
141
131
|
class OmitIf {
|
|
142
132
|
x: i32 = 1;
|
|
143
133
|
|
|
144
|
-
|
|
145
134
|
@omitif("this.y == -1")
|
|
146
135
|
y: i32 = -1;
|
|
147
136
|
z: i32 = 1;
|
|
148
137
|
|
|
149
|
-
|
|
150
138
|
@omitnull()
|
|
151
139
|
foo: string | null = null;
|
|
152
140
|
}
|
|
141
|
+
|
|
142
|
+
@json
|
|
143
|
+
class NullableObj {
|
|
144
|
+
bar: Bar | null = null;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
@json
|
|
148
|
+
class Bar {
|
|
149
|
+
value: string = "";
|
|
150
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
1
|
+
// @ts-ignore: inline
|
|
2
|
+
@inline export function deserializeBoolean(srcStart: usize, srcEnd: usize): boolean {
|
|
2
3
|
const block = load<u64>(srcStart);
|
|
3
4
|
if (block == 28429475166421108) return true;
|
|
4
5
|
else if (block == 32370086184550502 && load<u16>(srcStart, 8) == 101) return false;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ptrToStr } from "../../util/ptrToStr";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// @ts-ignore: inline
|
|
4
|
+
@inline export function deserializeDate(srcStart: usize, srcEnd: usize): Date {
|
|
4
5
|
// Use AssemblyScript's date parser
|
|
5
6
|
const d = Date.fromString(ptrToStr(srcStart + 2, srcEnd - 2));
|
|
6
7
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ptrToStr } from "../../util/ptrToStr";
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
// @ts-ignore: inline
|
|
4
|
+
@inline export function deserializeFloat<T>(srcStart: usize, srcEnd: usize): T {
|
|
4
5
|
// @ts-ignore
|
|
5
6
|
const type: T = 0;
|
|
6
7
|
// @ts-ignore
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { JSON } from "../..";
|
|
2
2
|
import { ptrToStr } from "../../util/ptrToStr";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// @ts-ignore: inline
|
|
5
|
+
@inline export function deserializeRaw(srcStart: usize, srcEnd: usize): JSON.Raw {
|
|
5
6
|
return JSON.Raw.from(ptrToStr(srcStart, srcEnd));
|
|
6
7
|
}
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { BACK_SLASH } from "../../custom/chars";
|
|
2
2
|
import { DESERIALIZE_ESCAPE_TABLE, ESCAPE_HEX_TABLE } from "../../globals/tables";
|
|
3
3
|
|
|
4
|
-
|
|
4
|
+
// @ts-ignore: inline
|
|
5
|
+
@inline export function deserializeString(srcStart: usize, srcEnd: usize, dst: usize): string {
|
|
5
6
|
srcStart += 2;
|
|
6
7
|
srcEnd -= 2;
|
|
7
8
|
const startPtr = srcStart;
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { BACK_SLASH, COMMA, CHAR_F, BRACE_LEFT, BRACKET_LEFT, CHAR_N, QUOTE, BRACE_RIGHT, BRACKET_RIGHT, CHAR_T, COLON } from "../../custom/chars";
|
|
2
2
|
import { isSpace } from "../../util";
|
|
3
|
-
import { ptrToStr } from "../../util/ptrToStr";
|
|
4
3
|
|
|
5
4
|
export function deserializeStruct<T>(srcStart: usize, srcEnd: usize, dst: usize): T {
|
|
6
5
|
const out = changetype<nonnull<T>>(dst || __new(offsetof<T>(), idof<T>()));
|