json-as 1.0.0-alpha.3 → 1.0.0-beta.1
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 +14 -0
- package/README.md +76 -59
- package/assembly/__benches__/misc.bench.ts +48 -0
- package/assembly/__benches__/schemas.ts +25 -0
- package/assembly/__tests__/arbitrary.spec.ts +19 -0
- package/assembly/__tests__/box.spec.ts +37 -0
- package/assembly/__tests__/date.spec.ts +38 -0
- package/assembly/__tests__/types.ts +3 -3
- package/assembly/deserialize/simd/string.ts +1 -1
- package/assembly/deserialize/simple/arbitrary.ts +1 -1
- package/assembly/deserialize/simple/array/arbitrary.ts +47 -24
- package/assembly/deserialize/simple/array/{object.ts → struct.ts} +1 -1
- package/assembly/deserialize/simple/array.ts +4 -9
- package/assembly/deserialize/simple/bool.ts +4 -7
- package/assembly/deserialize/simple/date.ts +2 -2
- package/assembly/deserialize/simple/integer.ts +2 -1
- package/assembly/deserialize/simple/map.ts +1 -1
- package/assembly/deserialize/simple/object.ts +13 -17
- package/assembly/deserialize/simple/struct.ts +158 -0
- package/assembly/index.ts +143 -24
- package/assembly/serialize/simd/string.ts +11 -11
- package/assembly/serialize/simple/arbitrary.ts +15 -2
- package/assembly/serialize/simple/array.ts +4 -3
- package/assembly/serialize/simple/float.ts +1 -1
- package/assembly/serialize/simple/integer.ts +7 -2
- package/assembly/serialize/simple/object.ts +43 -6
- package/assembly/serialize/simple/struct.ts +7 -0
- package/assembly/test.ts +84 -4
- package/assembly/util/atoi.ts +1 -1
- package/assembly/util/snp.ts +2 -2
- package/bench.js +11 -2
- package/modules/as-bs/assembly/index.ts +63 -32
- package/modules/as-bs/assembly/state.ts +8 -0
- package/package.json +7 -6
- package/transform/lib/builder.js +1340 -1262
- package/transform/lib/index.js +577 -512
- package/transform/lib/index.js.map +1 -1
- package/transform/lib/linker.js +12 -10
- package/transform/lib/types.js +19 -19
- package/transform/lib/util.js +34 -34
- package/transform/lib/visitor.js +529 -526
- package/transform/src/index.ts +25 -23
package/CHANGELOG
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 2025-02-13 - 1.0.0-alpha.4
|
|
4
|
+
|
|
5
|
+
- feat: reintroduce support for `Box<T>`-wrapped primitive types
|
|
6
|
+
- tests: add extensive tests to all supported types
|
|
7
|
+
- fix: 6-byte keys being recognized on deserialize
|
|
8
|
+
- perf: take advantage of aligned memory to use a single 64-bit load on 6-byte keys
|
|
9
|
+
- fix: `bs.proposeSize()` should increment `stackSize` by `size` instead of setting it
|
|
10
|
+
- fix: allow runtime to manage `bs.buffer`
|
|
11
|
+
- fix: memory leaks in `bs` module
|
|
12
|
+
- fix: add (possibly temporary) `JSON.Memory.shrink()` to shrink memory in `bs`
|
|
13
|
+
- perf: prefer growing memory by `nextPowerOf2(size + 64)` for less reallocations
|
|
14
|
+
- tests: add boolean tests to `Box<T>`
|
|
15
|
+
- fix: serialization of non-growable data types should grow `bs.stackSize`
|
|
16
|
+
|
|
3
17
|
## 2025-01-31 - 1.0.0-alpha.3
|
|
4
18
|
|
|
5
19
|
- fix: write to proper offset when deserializing string with \u0000-type escapes
|
package/README.md
CHANGED
|
@@ -1,30 +1,44 @@
|
|
|
1
1
|
<h5 align="center">
|
|
2
2
|
<pre>
|
|
3
3
|
<span style="font-size: 0.8em;"> ██ ███████ ██████ ███ ██ █████ ███████
|
|
4
|
-
██ ██ ██ ██ ████ ██ ██ ██ ██
|
|
4
|
+
██ ██ ██ ██ ████ ██ ██ ██ ██
|
|
5
5
|
██ ███████ ██ ██ ██ ██ ██ █████ ███████ ███████
|
|
6
6
|
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
7
7
|
█████ ███████ ██████ ██ ████ ██ ██ ███████
|
|
8
8
|
</span>
|
|
9
|
-
AssemblyScript - v1.0.0-
|
|
9
|
+
AssemblyScript - v1.0.0-beta.1
|
|
10
10
|
</pre>
|
|
11
11
|
</h5>
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## 📚 Contents
|
|
14
|
+
|
|
15
|
+
- [About](#-about)
|
|
16
|
+
- [Installation](#-installation)
|
|
17
|
+
- [Usage](#-usage)
|
|
18
|
+
- [Examples](#examples)
|
|
19
|
+
- [Performance](#-performance)
|
|
20
|
+
- [License](#-license)
|
|
21
|
+
- [Contact](#-contact)
|
|
22
|
+
|
|
23
|
+
## 📝 About
|
|
24
|
+
|
|
25
|
+
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.
|
|
26
|
+
|
|
27
|
+
## 💾 Installation
|
|
14
28
|
|
|
15
29
|
```bash
|
|
16
|
-
npm install json-as@1.0.0-
|
|
30
|
+
npm install json-as@1.0.0-beta.1
|
|
17
31
|
```
|
|
18
32
|
|
|
19
33
|
Add the `--transform` to your `asc` command (e.g. in package.json)
|
|
20
34
|
|
|
21
35
|
```bash
|
|
22
|
-
--transform json-as
|
|
36
|
+
--transform json-as/transform
|
|
23
37
|
```
|
|
24
38
|
|
|
25
39
|
Alternatively, add it to your `asconfig.json`
|
|
26
40
|
|
|
27
|
-
```
|
|
41
|
+
```json
|
|
28
42
|
{
|
|
29
43
|
// ...
|
|
30
44
|
"options": {
|
|
@@ -35,12 +49,11 @@ Alternatively, add it to your `asconfig.json`
|
|
|
35
49
|
|
|
36
50
|
If you'd like to see the code that the transform generates, run with `JSON_DEBUG=true`
|
|
37
51
|
|
|
38
|
-
## Usage
|
|
52
|
+
## 🪄 Usage
|
|
39
53
|
|
|
40
54
|
```js
|
|
41
55
|
import { JSON } from "json-as";
|
|
42
56
|
|
|
43
|
-
// @json or @serializable work here
|
|
44
57
|
@json
|
|
45
58
|
class Vec3 {
|
|
46
59
|
x: f32 = 0.0;
|
|
@@ -55,8 +68,7 @@ class Player {
|
|
|
55
68
|
lastName!: string;
|
|
56
69
|
lastActive!: i32[];
|
|
57
70
|
// Drop in a code block, function, or expression that evaluates to a boolean
|
|
58
|
-
@omitif((
|
|
59
|
-
@omitif('this.age <= 0')
|
|
71
|
+
@omitif((self: Player) => self.age < 18)
|
|
60
72
|
age!: i32;
|
|
61
73
|
@omitnull()
|
|
62
74
|
pos!: Vec3 | null;
|
|
@@ -64,10 +76,10 @@ class Player {
|
|
|
64
76
|
}
|
|
65
77
|
|
|
66
78
|
const player: Player = {
|
|
67
|
-
firstName: "
|
|
68
|
-
lastName: "
|
|
69
|
-
lastActive: [
|
|
70
|
-
age:
|
|
79
|
+
firstName: "Jairus",
|
|
80
|
+
lastName: "Tanaka",
|
|
81
|
+
lastActive: [2, 13, 2025],
|
|
82
|
+
age: 18,
|
|
71
83
|
pos: {
|
|
72
84
|
x: 3.4,
|
|
73
85
|
y: 1.2,
|
|
@@ -76,64 +88,69 @@ const player: Player = {
|
|
|
76
88
|
isVerified: true
|
|
77
89
|
};
|
|
78
90
|
|
|
79
|
-
const
|
|
91
|
+
const serialized = JSON.stringify<Player>(player);
|
|
92
|
+
const deserialized = JSON.parse<Player>(serialized);
|
|
80
93
|
|
|
81
|
-
|
|
94
|
+
console.log("Serialized " + serialized);
|
|
95
|
+
console.log("Deserialized " + JSON.stringify(deserialized));
|
|
82
96
|
```
|
|
83
97
|
|
|
84
|
-
|
|
98
|
+
## Examples
|
|
85
99
|
|
|
86
|
-
|
|
87
|
-
@json
|
|
88
|
-
class Base {}
|
|
100
|
+
## ⚡ Performance
|
|
89
101
|
|
|
90
|
-
|
|
91
|
-
class Vec1 extends Base {
|
|
92
|
-
x: f32 = 1.0;
|
|
93
|
-
}
|
|
94
|
-
@json
|
|
95
|
-
class Vec2 extends Vec1 {
|
|
96
|
-
y: f32 = 2.0;
|
|
97
|
-
}
|
|
98
|
-
@json
|
|
99
|
-
class Vec3 extends Vec2 {
|
|
100
|
-
z: f32 = 3.0;
|
|
101
|
-
}
|
|
102
|
+
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 some key performance benchmarks to give you an idea of how it performs.
|
|
102
103
|
|
|
103
|
-
|
|
104
|
-
new Vec1(),
|
|
105
|
-
new Vec2(),
|
|
106
|
-
new Vec3()
|
|
107
|
-
];
|
|
104
|
+
### Raw Performance
|
|
108
105
|
|
|
109
|
-
|
|
110
|
-
// [{"x":1.0},{"x":1.0,"y":2.0},{"y":2.0,"x":1.0,"z":3.0}]
|
|
111
|
-
const parsed = JSON.parse<Base[]>(serialized);
|
|
112
|
-
```
|
|
106
|
+
Simple
|
|
113
107
|
|
|
114
|
-
|
|
108
|
+
| Test Case | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
109
|
+
|--------------------|-----------------------|-------------------------|----------------------|------------------------|
|
|
110
|
+
| Vector3 Object | 32,642,320 ops/s | 9,736,272 ops/s | 1,240 MB/s | 369 MB/s |
|
|
111
|
+
| Alphabet String | 4,928,856 ops/s | 7,567,360 ops/s | 975 MB/s | 1,498 MB/s |
|
|
112
|
+
| Small JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
113
|
+
| Medium JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
114
|
+
| Large JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
115
115
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
116
|
+
SIMD
|
|
117
|
+
|
|
118
|
+
| Test Case | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
119
|
+
|--------------------|-----------------------|-------------------------|----------------------|------------------------|
|
|
120
|
+
| Vector3 Object | 32,642,320 ops/s | 9,736,272 ops/s | 1,240 MB/s | 369 MB/s |
|
|
121
|
+
| Alphabet String | 20,368,584 ops/s | 28,467,424 ops/s | 3,910 MB/s | 5,636 MB/s |
|
|
122
|
+
| Small JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
123
|
+
| Medium JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
124
|
+
| Large JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
125
|
+
|
|
126
|
+
JavaScript
|
|
127
|
+
|
|
128
|
+
| Test Case | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
129
|
+
|--------------------|-----------------------|-------------------------|----------------------|------------------------|
|
|
130
|
+
| Vector3 Object | 2,548,013 ops/s | 1,942,440 ops/s | 97 MB/s | 73 MB/s |
|
|
131
|
+
| Alphabet String | 3,221,556 ops/s | 2,716,617 ops/s | 624 MB/s | 537 MB/s |
|
|
132
|
+
| Small JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
133
|
+
| Medium JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
134
|
+
| Large JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
135
|
+
|
|
136
|
+
### Real-World Usage
|
|
124
137
|
|
|
125
|
-
|
|
138
|
+
| Scenario | JSON Size (kb) | Serialization Time (ops/s) | Deserialization Time (ops/s) | Throughput (GB/s) |
|
|
139
|
+
|--------------------|----------------|----------------------------|------------------------------|-------------------|
|
|
140
|
+
| Web API Response | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
141
|
+
| Database Entry | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
142
|
+
| File Parsing | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
126
143
|
|
|
127
|
-
## Notes
|
|
128
144
|
|
|
129
|
-
|
|
145
|
+
## 📃 License
|
|
130
146
|
|
|
131
|
-
|
|
147
|
+
This project is distributed under an open source license. You can view the full license using the following link: [License](./LICENSE)
|
|
132
148
|
|
|
133
|
-
|
|
134
|
-
- [GitHub](https://github.com/JairusSW)
|
|
135
|
-
- [Discord](discord.com/users/600700584038760448)
|
|
149
|
+
## 📫 Contact
|
|
136
150
|
|
|
137
|
-
|
|
151
|
+
Please send all issues to [GitHub Issues](https://github.com/JairusSW/as-json/issues) and to converse, please send me an email at [me@jairus.dev](mailto:me@jairus.dev)
|
|
138
152
|
|
|
139
|
-
|
|
153
|
+
- **Email:** Send me inquiries, questions, or requests at [me@jairus.dev](mailto:me@jairus.dev)
|
|
154
|
+
- **GitHub:** Visit the official GitHub repository [Here](https://github.com/JairusSW/as-json)
|
|
155
|
+
- **Website:** Visit my official website at [jairus.dev](https://jairus.dev/)
|
|
156
|
+
- **Discord:** Converse with me on [My Discord](discord.com/users/600700584038760448) or on the [AssemblyScript Discord Server](https://discord.gg/assemblyscript/)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { bench } from "as-bench/assembly";
|
|
2
|
+
import { JSON } from "..";
|
|
3
|
+
import { Vec3 } from "./schemas";
|
|
4
|
+
import { bs } from "../../modules/as-bs/assembly";
|
|
5
|
+
import { serializeString_SIMD } from "../serialize/simd/string";
|
|
6
|
+
import { serializeString } from "../serialize/simple/string";
|
|
7
|
+
import { deserializeString } from "../deserialize/simple/string";
|
|
8
|
+
import { bytes } from "../util";
|
|
9
|
+
import { deserializeString_SIMD } from "../deserialize/simd/string";
|
|
10
|
+
|
|
11
|
+
const vec: Vec3 = {
|
|
12
|
+
x: 1,
|
|
13
|
+
y: 2,
|
|
14
|
+
z: 3
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
bs.ensureSize(4096);
|
|
18
|
+
// bench("Serialize Vector3", () => {
|
|
19
|
+
// JSON.stringify(vec);
|
|
20
|
+
// });
|
|
21
|
+
|
|
22
|
+
// bench("Deserialize Vector3", () => {
|
|
23
|
+
// JSON.parse<Vec3>('{"x":1,"y":2,"z":3}');
|
|
24
|
+
// });
|
|
25
|
+
|
|
26
|
+
// bench("Serialize String SIMD", () => {
|
|
27
|
+
// serializeString_SIMD("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~`!@#$%^&*()-_=+{[}]|\\:;\"'?/>.<,'\"}");
|
|
28
|
+
// bs.offset = changetype<usize>(bs.buffer);
|
|
29
|
+
// bs.stackSize = 0;
|
|
30
|
+
// });
|
|
31
|
+
|
|
32
|
+
// bench("Serialize String", () => {
|
|
33
|
+
// serializeString("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~`!@#$%^&*()-_=+{[}]|\\:;\"'?/>.<,'\"}");
|
|
34
|
+
// bs.offset = changetype<usize>(bs.buffer);
|
|
35
|
+
// bs.stackSize = 0;
|
|
36
|
+
// });
|
|
37
|
+
|
|
38
|
+
const src = '"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~`!@#$%^&*()-_=+{[}]|\\\\:;\\"\'?/>.<,\'\\"}"';
|
|
39
|
+
const srcStart = changetype<usize>(src);
|
|
40
|
+
const srcEnd = srcStart + bytes(src);
|
|
41
|
+
const out = changetype<usize>(new ArrayBuffer(256));
|
|
42
|
+
bench("Deserialize String", () => {
|
|
43
|
+
deserializeString(srcStart, srcEnd, out);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
bench("Deserialize String SIMD", () => {
|
|
47
|
+
deserializeString_SIMD(srcStart, srcEnd, out);
|
|
48
|
+
})
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { JSON } from "..";
|
|
2
|
+
|
|
3
|
+
@json
|
|
4
|
+
export class Vec3 {
|
|
5
|
+
public x!: i32;
|
|
6
|
+
public y!: i32;
|
|
7
|
+
public z!: i32;
|
|
8
|
+
__DESERIALIZE(keyStart: usize, keyEnd: usize, valStart: usize, valEnd: usize, ptr: usize): void {
|
|
9
|
+
switch (load<u16>(keyStart)) {
|
|
10
|
+
case 120: { // x
|
|
11
|
+
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("x"));
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
case 121: { // y
|
|
15
|
+
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("y"));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
case 122: { // z
|
|
19
|
+
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("z"));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { JSON } from "..";
|
|
2
|
+
import { describe, expect } from "../../modules/test/assembly";
|
|
3
|
+
import { Vec3 } from "./types";
|
|
4
|
+
|
|
5
|
+
describe("Should serialize arbitrary types", () => {
|
|
6
|
+
expect(JSON.stringify(JSON.Value.from("hello world"))).toBe("\"hello world\"");
|
|
7
|
+
expect(JSON.stringify(JSON.Value.from(0))).toBe("0");
|
|
8
|
+
expect(JSON.stringify(JSON.Value.from(true))).toBe("true");
|
|
9
|
+
expect(JSON.stringify(JSON.Value.from(new Vec3()))).toBe('{"x":1.0,"y":2.0,"z":3.0}');
|
|
10
|
+
expect(JSON.stringify([JSON.Value.from("string"), JSON.Value.from(true), JSON.Value.from(3.14), JSON.Value.from(new Vec3())])).toBe('["string",true,3.14,{"x":1.0,"y":2.0,"z":3.0}]');
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
describe("Should deserialize arbitrary types", () => {
|
|
14
|
+
expect(JSON.parse<JSON.Value>("\"hello world\"").get<string>()).toBe("hello world");
|
|
15
|
+
expect(JSON.parse<JSON.Value>("0.0").toString()).toBe("0.0");
|
|
16
|
+
expect(JSON.parse<JSON.Value>("true").toString()).toBe("true");
|
|
17
|
+
expect(JSON.stringify(JSON.parse<JSON.Value>('{"x":1.0,"y":2.0,"z":3.0}'))).toBe('{"x":1.0,"y":2.0,"z":3.0}');
|
|
18
|
+
expect(JSON.stringify(JSON.parse<JSON.Value[]>('["string",true,3.14,{"x":1.0,"y":2.0,"z":3.0},[1.0,2.0,3,true]]'))).toBe('["string",true,3.14,{"x":1.0,"y":2.0,"z":3.0},[1.0,2.0,3.0,true]]');
|
|
19
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { JSON } from "..";
|
|
2
|
+
import { describe, expect } from "../../modules/test/assembly";
|
|
3
|
+
|
|
4
|
+
describe("Should serialize JSON.Box<T>", () => {
|
|
5
|
+
expect(JSON.stringify<JSON.Box<i32> | null>(null))
|
|
6
|
+
.toBe("null");
|
|
7
|
+
|
|
8
|
+
expect(JSON.stringify<JSON.Box<i32> | null>(new JSON.Box<i32>(0)))
|
|
9
|
+
.toBe("0");
|
|
10
|
+
|
|
11
|
+
expect(JSON.stringify<JSON.Box<i32> | null>(new JSON.Box<i32>(1)))
|
|
12
|
+
.toBe("1");
|
|
13
|
+
|
|
14
|
+
expect(JSON.stringify<JSON.Box<boolean> | null>(new JSON.Box<boolean>(false)))
|
|
15
|
+
.toBe("false");
|
|
16
|
+
|
|
17
|
+
expect(JSON.stringify<JSON.Box<boolean> | null>(new JSON.Box<boolean>(true)))
|
|
18
|
+
.toBe("true");
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// This is somewhat clumsy to use. Perhaps I can redesign it or use some transform to make it more transparent.
|
|
22
|
+
describe("Should deserialize JSON.Box<T>", () => {
|
|
23
|
+
expect((JSON.parse<JSON.Box<i32> | null>("null") == null).toString())
|
|
24
|
+
.toBe("true");
|
|
25
|
+
|
|
26
|
+
expect(JSON.parse<JSON.Box<i32> | null>("0")!.value.toString())
|
|
27
|
+
.toBe("0");
|
|
28
|
+
|
|
29
|
+
expect(JSON.parse<JSON.Box<i32> | null>("1")!.value.toString())
|
|
30
|
+
.toBe("1");
|
|
31
|
+
|
|
32
|
+
expect(JSON.parse<JSON.Box<boolean> | null>("false")!.value.toString())
|
|
33
|
+
.toBe("false");
|
|
34
|
+
|
|
35
|
+
expect(JSON.parse<JSON.Box<boolean> | null>("true")!.value.toString())
|
|
36
|
+
.toBe("true");
|
|
37
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { JSON } from "..";
|
|
2
|
+
import { describe, expect } from "../../modules/test/assembly";
|
|
3
|
+
|
|
4
|
+
describe("Should serialize Date", () => {
|
|
5
|
+
expect(JSON.stringify<Date>(new Date(0)))
|
|
6
|
+
.toBe('"1970-01-01T00:00:00.000Z"');
|
|
7
|
+
expect(JSON.stringify<Date>(new Date(1738618120525)))
|
|
8
|
+
.toBe('"2025-02-03T21:28:40.525Z"');
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
describe("Should deserialize booleans", () => {
|
|
12
|
+
// const date = JSON.parse<Date>('"2025-02-03T21:28:40.525Z"');
|
|
13
|
+
// console.log("Year: " + date.getUTCFullYear().toString());
|
|
14
|
+
// console.log("Month: " + date.getUTCMonth().toString());
|
|
15
|
+
// console.log("Day: " + date.getUTCDay().toString());
|
|
16
|
+
// console.log("Hours: " + date.getUTCHours().toString());
|
|
17
|
+
// console.log("Minutes: " + date.getUTCMinutes().toString());
|
|
18
|
+
// console.log("Seconds: " + date.getUTCSeconds().toString());
|
|
19
|
+
// console.log("Milliseconds: " + date.getUTCMilliseconds().toString());
|
|
20
|
+
|
|
21
|
+
const date1 = JSON.parse<Date>('"1970-01-01T00:00:00.000Z"');
|
|
22
|
+
expect(date1.getUTCFullYear().toString()).toBe("1970");
|
|
23
|
+
expect(date1.getUTCMonth().toString()).toBe("0");
|
|
24
|
+
expect(date1.getUTCDay().toString()).toBe("4");
|
|
25
|
+
expect(date1.getUTCHours().toString()).toBe("0");
|
|
26
|
+
expect(date1.getUTCMinutes().toString()).toBe("0");
|
|
27
|
+
expect(date1.getUTCSeconds().toString()).toBe("0");
|
|
28
|
+
expect(date1.getUTCMilliseconds().toString()).toBe("0");
|
|
29
|
+
|
|
30
|
+
const date2 = JSON.parse<Date>('"2025-02-03T21:28:40.525Z"');
|
|
31
|
+
expect(date2.getUTCFullYear().toString()).toBe("2025");
|
|
32
|
+
expect(date2.getUTCMonth().toString()).toBe("1");
|
|
33
|
+
expect(date2.getUTCDay().toString()).toBe("1");
|
|
34
|
+
expect(date2.getUTCHours().toString()).toBe("21");
|
|
35
|
+
expect(date2.getUTCMinutes().toString()).toBe("28");
|
|
36
|
+
expect(date2.getUTCSeconds().toString()).toBe("40");
|
|
37
|
+
expect(date2.getUTCMilliseconds().toString()).toBe("525");
|
|
38
|
+
});
|
|
@@ -10,7 +10,7 @@ const SPLAT_92 = i16x8.splat(92); /* \ */
|
|
|
10
10
|
* @returns number of bytes written
|
|
11
11
|
*/
|
|
12
12
|
// todo: optimize and stuff. it works, its not pretty. ideally, i'd like this to be (nearly) branchless
|
|
13
|
-
export function deserializeString_SIMD(
|
|
13
|
+
export function deserializeString_SIMD(srcStart: usize, srcEnd: usize, dst: usize): usize {
|
|
14
14
|
let src_ptr = srcStart + 2;
|
|
15
15
|
let dst_ptr = changetype<usize>(dst);
|
|
16
16
|
|
|
@@ -2,8 +2,8 @@ import { JSON } from "../..";
|
|
|
2
2
|
import { deserializeArray } from "./array";
|
|
3
3
|
import { deserializeBoolean } from "./bool";
|
|
4
4
|
import { deserializeFloat } from "./float";
|
|
5
|
-
import { deserializeObject } from "./object";
|
|
6
5
|
import { deserializeString } from "./string";
|
|
6
|
+
import { deserializeObject } from "./object";
|
|
7
7
|
|
|
8
8
|
export function deserializeArbitrary(srcStart: usize, srcEnd: usize, dst: usize): JSON.Value {
|
|
9
9
|
const firstChar = load<u16>(srcStart);
|
|
@@ -1,45 +1,66 @@
|
|
|
1
1
|
import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, CHAR_F, CHAR_N, CHAR_T, COMMA, QUOTE } from "../../../custom/chars";
|
|
2
2
|
import { JSON } from "../../../";
|
|
3
3
|
import { isSpace } from "util/string";
|
|
4
|
+
import { ptrToStr } from "../../../util/ptrToStr";
|
|
4
5
|
|
|
5
|
-
export function deserializeArbitraryArray
|
|
6
|
-
const out = dst ? changetype<
|
|
6
|
+
export function deserializeArbitraryArray(srcStart: usize, srcEnd: usize, dst: usize): JSON.Value[] {
|
|
7
|
+
const out = dst ? changetype<JSON.Value[]>(dst) : instantiate<JSON.Value[]>();
|
|
7
8
|
let lastIndex: usize = 0;
|
|
8
9
|
let depth: u32 = 0;
|
|
10
|
+
// if (load<u16>(srcStart) != BRACKET_LEFT)
|
|
11
|
+
srcStart += 2;
|
|
9
12
|
while (srcStart < srcEnd) {
|
|
10
13
|
const code = load<u16>(srcStart);
|
|
11
|
-
|
|
14
|
+
// console.log("code: " + String.fromCharCode(code));
|
|
15
|
+
if (code == BRACE_LEFT) {
|
|
12
16
|
lastIndex = srcStart;
|
|
17
|
+
depth++;
|
|
13
18
|
srcStart += 2;
|
|
14
19
|
while (srcStart < srcEnd) {
|
|
15
20
|
const code = load<u16>(srcStart);
|
|
16
|
-
if (code ==
|
|
17
|
-
|
|
18
|
-
|
|
21
|
+
if (code == BRACE_RIGHT) {
|
|
22
|
+
if (--depth == 0) {
|
|
23
|
+
// @ts-ignore: type
|
|
24
|
+
out.push(JSON.__deserialize<JSON.Value>(lastIndex, srcStart + 2));
|
|
25
|
+
// console.log("Value (object): " + ptrToStr(lastIndex, srcStart + 2));
|
|
26
|
+
break;
|
|
19
27
|
}
|
|
20
|
-
|
|
28
|
+
} else if (code == BRACE_LEFT) depth++;
|
|
29
|
+
srcStart += 2;
|
|
30
|
+
}
|
|
31
|
+
} else if (code == QUOTE) {
|
|
32
|
+
lastIndex = srcStart;
|
|
33
|
+
srcStart += 2;
|
|
34
|
+
while (srcStart < srcEnd) {
|
|
35
|
+
const code = load<u16>(srcStart);
|
|
36
|
+
if (code == QUOTE && load<u16>(srcStart - 2) !== BACK_SLASH) {
|
|
37
|
+
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
38
|
+
// /* empty */
|
|
39
|
+
// }
|
|
40
|
+
// console.log("Value (string): " + ptrToStr(lastIndex, srcStart + 2));
|
|
21
41
|
// @ts-ignore: exists
|
|
22
|
-
out.push(JSON.__deserialize<
|
|
42
|
+
out.push(JSON.__deserialize<JSON.Value>(lastIndex, srcStart + 2));
|
|
43
|
+
srcStart += 2;
|
|
23
44
|
break;
|
|
24
45
|
}
|
|
25
46
|
srcStart += 2;
|
|
26
47
|
}
|
|
48
|
+
// console.log("next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
27
49
|
} else if (code - 48 <= 9 || code == 45) {
|
|
50
|
+
// console.log("trigger int")
|
|
28
51
|
lastIndex = srcStart;
|
|
29
52
|
srcStart += 2;
|
|
30
53
|
while (srcStart < srcEnd) {
|
|
31
54
|
const code = load<u16>(srcStart);
|
|
32
|
-
if (code == COMMA || code ==
|
|
55
|
+
if (code == COMMA || code == BRACKET_RIGHT || isSpace(code)) {
|
|
33
56
|
// @ts-ignore: type
|
|
34
|
-
out.push(JSON.__deserialize<
|
|
57
|
+
out.push(JSON.__deserialize<JSON.Value>(lastIndex, srcStart));
|
|
35
58
|
// console.log("Value (number): " + ptrToStr(lastIndex, srcStart));
|
|
36
|
-
while (isSpace(load<u16>((srcStart += 2)))) {
|
|
37
|
-
/* empty */
|
|
38
|
-
}
|
|
39
59
|
break;
|
|
40
60
|
}
|
|
41
61
|
srcStart += 2;
|
|
42
62
|
}
|
|
63
|
+
// console.log("next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
43
64
|
} else if (code == BRACE_LEFT) {
|
|
44
65
|
lastIndex = srcStart;
|
|
45
66
|
depth++;
|
|
@@ -49,7 +70,7 @@ export function deserializeArbitraryArray<T extends JSON.Value>(srcStart: usize,
|
|
|
49
70
|
if (code == BRACE_RIGHT) {
|
|
50
71
|
if (--depth == 0) {
|
|
51
72
|
// @ts-ignore: type
|
|
52
|
-
out.push(JSON.__deserialize<
|
|
73
|
+
out.push(JSON.__deserialize<JSON.Value>(lastIndex, srcStart));
|
|
53
74
|
// console.log("Value (object): " + ptrToStr(lastIndex, srcStart));
|
|
54
75
|
while (isSpace(load<u16>((srcStart += 2)))) {
|
|
55
76
|
/* empty */
|
|
@@ -68,7 +89,7 @@ export function deserializeArbitraryArray<T extends JSON.Value>(srcStart: usize,
|
|
|
68
89
|
if (code == BRACKET_RIGHT) {
|
|
69
90
|
if (--depth == 0) {
|
|
70
91
|
// @ts-ignore: type
|
|
71
|
-
out.push(JSON.__deserialize<
|
|
92
|
+
out.push(JSON.__deserialize<JSON.Value>(lastIndex, srcStart));
|
|
72
93
|
// console.log("Value (array): " + ptrToStr(lastIndex, srcStart));
|
|
73
94
|
while (isSpace(load<u16>((srcStart += 2)))) {
|
|
74
95
|
/* empty */
|
|
@@ -81,25 +102,27 @@ export function deserializeArbitraryArray<T extends JSON.Value>(srcStart: usize,
|
|
|
81
102
|
} else if (code == CHAR_T) {
|
|
82
103
|
if (load<u64>(srcStart) == 28429475166421108) {
|
|
83
104
|
// @ts-ignore: type
|
|
84
|
-
out.push(JSON.__deserialize<
|
|
105
|
+
out.push(JSON.__deserialize<JSON.Value>(srcStart, (srcStart += 8)));
|
|
85
106
|
// console.log("Value (bool): " + ptrToStr(srcStart - 8, srcStart));
|
|
86
|
-
while (isSpace(load<u16>((srcStart += 2)))) {
|
|
87
|
-
|
|
88
|
-
}
|
|
107
|
+
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
108
|
+
// /* empty */
|
|
109
|
+
// }
|
|
110
|
+
// console.log("next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
89
111
|
}
|
|
90
112
|
} else if (code == CHAR_F) {
|
|
91
113
|
if (load<u64>(srcStart, 2) == 28429466576093281) {
|
|
92
114
|
// @ts-ignore: type
|
|
93
|
-
out.push(JSON.__deserialize<
|
|
115
|
+
out.push(JSON.__deserialize<JSON.Value>(srcStart, (srcStart += 10)));
|
|
94
116
|
// console.log("Value (bool): " + ptrToStr(srcStart - 10, srcStart));
|
|
95
|
-
while (isSpace(load<u16>((srcStart += 2)))) {
|
|
96
|
-
|
|
97
|
-
}
|
|
117
|
+
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
118
|
+
// /* empty */
|
|
119
|
+
// }
|
|
120
|
+
// console.log("next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
98
121
|
}
|
|
99
122
|
} else if (code == CHAR_N) {
|
|
100
123
|
if (load<u64>(srcStart) == 30399761348886638) {
|
|
101
124
|
// @ts-ignore: type
|
|
102
|
-
out.push(JSON.__deserialize<
|
|
125
|
+
out.push(JSON.__deserialize<JSON.Value>(lastIndex, (srcStart += 8)));
|
|
103
126
|
// console.log("Value (null): " + ptrToStr(srcStart - 8, srcStart));
|
|
104
127
|
while (isSpace(load<u16>((srcStart += 2)))) {
|
|
105
128
|
/* empty */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { BRACE_LEFT, BRACE_RIGHT } from "../../../custom/chars";
|
|
2
2
|
import { JSON } from "../../..";
|
|
3
3
|
|
|
4
|
-
export function
|
|
4
|
+
export function deserializeStructArray<T extends unknown[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
|
|
5
5
|
const out = dst ? changetype<T>(dst) : instantiate<T>();
|
|
6
6
|
let lastIndex: usize = 0;
|
|
7
7
|
let depth: u32 = 0;
|
|
@@ -5,7 +5,7 @@ import { deserializeBooleanArray } from "./array/bool";
|
|
|
5
5
|
import { deserializeFloatArray } from "./array/float";
|
|
6
6
|
import { deserializeIntegerArray } from "./array/integer";
|
|
7
7
|
import { deserializeMapArray } from "./array/map";
|
|
8
|
-
import {
|
|
8
|
+
import { deserializeStructArray } from "./array/struct";
|
|
9
9
|
import { deserializeStringArray } from "./array/string";
|
|
10
10
|
|
|
11
11
|
// @ts-ignore: Decorator valid here
|
|
@@ -28,21 +28,16 @@ export function deserializeArray<T extends unknown[]>(srcStart: usize, srcEnd: u
|
|
|
28
28
|
const type = changetype<nonnull<valueof<T>>>(0);
|
|
29
29
|
if (type instanceof JSON.Value) {
|
|
30
30
|
// @ts-ignore: type
|
|
31
|
-
return deserializeArbitraryArray
|
|
31
|
+
return deserializeArbitraryArray(srcStart, srcEnd, dst);
|
|
32
32
|
} else if (type instanceof Map) {
|
|
33
33
|
// @ts-ignore: type
|
|
34
34
|
return deserializeMapArray<T>(srcStart, srcEnd, dst);
|
|
35
35
|
// @ts-ignore: defined by transform
|
|
36
36
|
} else if (isDefined(type.__DESERIALIZE)) {
|
|
37
|
-
return
|
|
37
|
+
return deserializeStructArray<T>(srcStart, srcEnd, dst);
|
|
38
38
|
}
|
|
39
39
|
throw new Error("Could not parse array of type " + nameof<T>() + "!");
|
|
40
40
|
} else {
|
|
41
41
|
throw new Error("Could not parse array of type " + nameof<T>() + "!");
|
|
42
42
|
}
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
function isMap<T>(): boolean {
|
|
46
|
-
let type: T = changetype<T>(0);
|
|
47
|
-
return type instanceof Map;
|
|
48
|
-
}
|
|
43
|
+
}
|
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
import { CHAR_F, CHAR_T } from "../../custom/chars";
|
|
2
|
-
|
|
3
1
|
export function deserializeBoolean(srcStart: usize, srcEnd: usize): boolean {
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
if (
|
|
7
|
-
else if (firstChar == CHAR_F && load<u64>(srcSize, 2) == 28429466576093281) return false;
|
|
2
|
+
const block = load<u64>(srcStart);
|
|
3
|
+
if (block == 28429475166421108) return true;
|
|
4
|
+
else if (block == 32370086184550502 && load<u16>(srcStart, 8) == 101) return false;
|
|
8
5
|
return false; //throw new Error(`Expected to find boolean, but found "${data.slice(0, 100)}" instead!`);
|
|
9
|
-
}
|
|
6
|
+
}
|
|
@@ -2,10 +2,10 @@ import { ptrToStr } from "../../util/ptrToStr";
|
|
|
2
2
|
|
|
3
3
|
export function deserializeDate(srcStart: usize, srcEnd: usize): Date {
|
|
4
4
|
// Use AssemblyScript's date parser
|
|
5
|
-
const d = Date.fromString(ptrToStr(srcStart, srcEnd));
|
|
5
|
+
const d = Date.fromString(ptrToStr(srcStart + 2, srcEnd - 2));
|
|
6
6
|
|
|
7
7
|
// Return a new object instead of the one that the parser returned.
|
|
8
|
-
// This may seem redundant, but
|
|
8
|
+
// This may seem redundant, but it addresses the issue when Date
|
|
9
9
|
// is globally aliased to wasi_Date (or some other superclass).
|
|
10
10
|
return new Date(d.getTime());
|
|
11
11
|
}
|