json-as 1.0.0-alpha.4 → 1.0.0-beta.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/.trunk/configs/.markdownlint.yaml +2 -0
- package/.trunk/configs/.shellcheckrc +7 -0
- package/.trunk/configs/.yamllint.yaml +7 -0
- package/.trunk/trunk.yaml +36 -0
- package/CHANGELOG +23 -0
- package/README.md +59 -57
- package/assembly/__benches__/misc.bench.ts +48 -0
- package/assembly/__benches__/schemas.ts +25 -0
- package/assembly/__benches__/string.bench.ts +24 -0
- package/assembly/__benches__/struct.bench.ts +22 -0
- package/assembly/__tests__/arbitrary.spec.ts +19 -0
- package/assembly/__tests__/custom.spec.ts +42 -0
- package/assembly/__tests__/types.ts +3 -3
- package/assembly/custom/bench.ts +26 -0
- 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/integer.ts +2 -1
- package/assembly/deserialize/simple/map.ts +1 -1
- package/assembly/deserialize/simple/object.ts +11 -17
- package/assembly/deserialize/simple/struct.ts +158 -0
- package/assembly/index.d.ts +15 -1
- package/assembly/index.ts +144 -18
- package/assembly/serialize/simple/arbitrary.ts +15 -2
- package/assembly/serialize/simple/object.ts +43 -6
- package/assembly/serialize/simple/struct.ts +7 -0
- package/assembly/test.ts +61 -3
- package/assembly/util/atoi.ts +1 -1
- package/bench/bench.ts +15 -0
- package/bench/schemas.ts +5 -0
- package/bench/string.bench.ts +16 -0
- package/bench.js +11 -2
- package/modules/as-bs/assembly/index.ts +3 -3
- package/modules/as-bs/assembly/state.ts +8 -0
- package/package.json +3 -2
- package/transform/lib/index.js +59 -9
- package/transform/lib/index.js.map +1 -1
- package/transform/src/index.ts +75 -9
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# This file controls the behavior of Trunk: https://docs.trunk.io/cli
|
|
2
|
+
# To learn more about the format of this file, see https://docs.trunk.io/reference/trunk-yaml
|
|
3
|
+
version: 0.1
|
|
4
|
+
cli:
|
|
5
|
+
version: 1.22.10
|
|
6
|
+
# Trunk provides extensibility via plugins. (https://docs.trunk.io/plugins)
|
|
7
|
+
plugins:
|
|
8
|
+
sources:
|
|
9
|
+
- id: trunk
|
|
10
|
+
ref: v1.6.7
|
|
11
|
+
uri: https://github.com/trunk-io/plugins
|
|
12
|
+
# Many linters and tools depend on runtimes - configure them here. (https://docs.trunk.io/runtimes)
|
|
13
|
+
runtimes:
|
|
14
|
+
enabled:
|
|
15
|
+
- go@1.21.0
|
|
16
|
+
- node@18.20.5
|
|
17
|
+
- python@3.10.8
|
|
18
|
+
# This is the section where you manage your linters. (https://docs.trunk.io/check/configuration)
|
|
19
|
+
lint:
|
|
20
|
+
enabled:
|
|
21
|
+
- actionlint@1.7.7
|
|
22
|
+
- checkov@3.2.374
|
|
23
|
+
- git-diff-check
|
|
24
|
+
- markdownlint@0.44.0
|
|
25
|
+
- prettier@3.5.2
|
|
26
|
+
- shellcheck@0.10.0
|
|
27
|
+
- shfmt@3.6.0
|
|
28
|
+
- trufflehog@3.88.12
|
|
29
|
+
- yamllint@1.35.1
|
|
30
|
+
actions:
|
|
31
|
+
disabled:
|
|
32
|
+
- trunk-announce
|
|
33
|
+
- trunk-check-pre-push
|
|
34
|
+
- trunk-fmt-pre-commit
|
|
35
|
+
enabled:
|
|
36
|
+
- trunk-upgrade-available
|
package/CHANGELOG
CHANGED
|
@@ -1,5 +1,28 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 2025-02-25 - 1.0.0-beta.2
|
|
4
|
+
|
|
5
|
+
- feat: add support for custom serializers and deserializers [#110](https://github.com/JairusSW/as-json/pull/110)
|
|
6
|
+
|
|
7
|
+
## 2025-02-22 - 1.0.0-beta.1
|
|
8
|
+
|
|
9
|
+
- perf: add benchmarks for both AssemblyScript and JavaScript
|
|
10
|
+
- docs: publish preliminary benchmark results
|
|
11
|
+
- tests: ensure nested serialization works and add to tests
|
|
12
|
+
- feat: finish arbitrary type implementation
|
|
13
|
+
- feat: introduce `JSON.Obj` to handle objects effectively
|
|
14
|
+
- feat: reimplement arbitrary array deserialization
|
|
15
|
+
- fix: remove brace check on array deserialization
|
|
16
|
+
- feat: introduce native support for `JSON.Obj` transformations
|
|
17
|
+
- feat: implement arbitrary object serialization
|
|
18
|
+
- fix: deserialization of booleans panics on `false`
|
|
19
|
+
- fix: `bs.resize` should be type-safe
|
|
20
|
+
- impl: add `JSON.Obj` type as prototype to handle arbitrary object structures
|
|
21
|
+
- chore: rename static objects (schemas) to structs and name arbitrary objects as `obj`
|
|
22
|
+
- tests: add proper tests for arbitrary types
|
|
23
|
+
- fix: empty method generation using outdated function signature
|
|
24
|
+
- docs: update readme to be more concise
|
|
25
|
+
|
|
3
26
|
## 2025-02-13 - 1.0.0-alpha.4
|
|
4
27
|
|
|
5
28
|
- feat: reintroduce support for `Box<T>`-wrapped primitive types
|
package/README.md
CHANGED
|
@@ -6,47 +6,48 @@
|
|
|
6
6
|
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
7
7
|
█████ ███████ ██████ ██ ████ ██ ██ ███████
|
|
8
8
|
</span>
|
|
9
|
-
AssemblyScript - v1.0.0-
|
|
9
|
+
AssemblyScript - v1.0.0-beta.2
|
|
10
10
|
</pre>
|
|
11
11
|
</h5>
|
|
12
12
|
|
|
13
|
-
## Contents
|
|
14
|
-
- [About](#about)
|
|
15
|
-
- [Installation](#installation)
|
|
16
|
-
- [Usage](#usage)
|
|
17
|
-
- [Examples](#examples)
|
|
18
|
-
- [Performance](#performance)
|
|
19
|
-
- [License](#license)
|
|
20
|
-
- [Contact](#contact)
|
|
13
|
+
## 📚 Contents
|
|
21
14
|
|
|
22
|
-
|
|
15
|
+
- [About](#-about)
|
|
16
|
+
- [Installation](#-installation)
|
|
17
|
+
- [Usage](#-usage)
|
|
18
|
+
- [Examples](#examples)
|
|
19
|
+
- [Performance](#-performance)
|
|
20
|
+
- [License](#-license)
|
|
21
|
+
- [Contact](#-contact)
|
|
23
22
|
|
|
24
|
-
##
|
|
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
|
|
25
28
|
|
|
26
29
|
```bash
|
|
27
|
-
npm install json-as@1.0.0-
|
|
30
|
+
npm install json-as@1.0.0-beta.2
|
|
28
31
|
```
|
|
29
32
|
|
|
30
33
|
Add the `--transform` to your `asc` command (e.g. in package.json)
|
|
31
34
|
|
|
32
35
|
```bash
|
|
33
|
-
--transform json-as
|
|
36
|
+
--transform json-as/transform
|
|
34
37
|
```
|
|
35
38
|
|
|
36
39
|
Alternatively, add it to your `asconfig.json`
|
|
37
40
|
|
|
38
|
-
```
|
|
41
|
+
```json
|
|
39
42
|
{
|
|
40
43
|
// ...
|
|
41
|
-
"options": {
|
|
42
|
-
"transform": ["json-as/transform"]
|
|
43
|
-
}
|
|
44
|
+
"options": {"transform": ["json-as/transform"]}
|
|
44
45
|
}
|
|
45
46
|
```
|
|
46
47
|
|
|
47
48
|
If you'd like to see the code that the transform generates, run with `JSON_DEBUG=true`
|
|
48
49
|
|
|
49
|
-
## Usage
|
|
50
|
+
## 🪄 Usage
|
|
50
51
|
|
|
51
52
|
```js
|
|
52
53
|
import { JSON } from "json-as";
|
|
@@ -75,7 +76,7 @@ class Player {
|
|
|
75
76
|
const player: Player = {
|
|
76
77
|
firstName: "Jairus",
|
|
77
78
|
lastName: "Tanaka",
|
|
78
|
-
lastActive: [2,
|
|
79
|
+
lastActive: [2, 13, 2025],
|
|
79
80
|
age: 18,
|
|
80
81
|
pos: {
|
|
81
82
|
x: 3.4,
|
|
@@ -86,56 +87,57 @@ const player: Player = {
|
|
|
86
87
|
};
|
|
87
88
|
|
|
88
89
|
const serialized = JSON.stringify<Player>(player);
|
|
89
|
-
const
|
|
90
|
+
const deserialized = JSON.parse<Player>(serialized);
|
|
90
91
|
|
|
91
|
-
console.log("Serialized
|
|
92
|
-
console.log("
|
|
92
|
+
console.log("Serialized " + serialized);
|
|
93
|
+
console.log("Deserialized " + JSON.stringify(deserialized));
|
|
93
94
|
```
|
|
94
95
|
|
|
95
96
|
## Examples
|
|
96
97
|
|
|
97
|
-
|
|
98
|
+
## ⚡ Performance
|
|
98
99
|
|
|
99
|
-
|
|
100
|
-
@json
|
|
101
|
-
class Base {}
|
|
100
|
+
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
101
|
|
|
103
|
-
|
|
104
|
-
class Vec1 extends Base {
|
|
105
|
-
x: f32 = 1.0;
|
|
106
|
-
}
|
|
107
|
-
@json
|
|
108
|
-
class Vec2 extends Vec1 {
|
|
109
|
-
y: f32 = 2.0;
|
|
110
|
-
}
|
|
111
|
-
@json
|
|
112
|
-
class Vec3 extends Vec2 {
|
|
113
|
-
z: f32 = 3.0;
|
|
114
|
-
}
|
|
102
|
+
### Raw Performance
|
|
115
103
|
|
|
116
|
-
|
|
117
|
-
new Vec1(),
|
|
118
|
-
new Vec2(),
|
|
119
|
-
new Vec3()
|
|
120
|
-
];
|
|
104
|
+
Simple
|
|
121
105
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
106
|
+
| Test Case | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
107
|
+
| ------------------ | --------------------- | ----------------------- | -------------------- | ---------------------- |
|
|
108
|
+
| Vector3 Object | 32,642,320 ops/s | 9,736,272 ops/s | 1,240 MB/s | 369 MB/s |
|
|
109
|
+
| Alphabet String | 4,928,856 ops/s | 7,567,360 ops/s | 975 MB/s | 1,498 MB/s |
|
|
110
|
+
| Small JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
111
|
+
| Medium JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
112
|
+
| Large JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
126
113
|
|
|
127
|
-
|
|
114
|
+
SIMD
|
|
128
115
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
116
|
+
| Test Case | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
117
|
+
| ------------------ | --------------------- | ----------------------- | -------------------- | ---------------------- |
|
|
118
|
+
| Vector3 Object | 32,642,320 ops/s | 9,736,272 ops/s | 1,240 MB/s | 369 MB/s |
|
|
119
|
+
| Alphabet String | 20,368,584 ops/s | 28,467,424 ops/s | 3,910 MB/s | 5,636 MB/s |
|
|
120
|
+
| Small JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
121
|
+
| Medium JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
122
|
+
| Large JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
123
|
+
|
|
124
|
+
JavaScript
|
|
125
|
+
|
|
126
|
+
| Test Case | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
127
|
+
| ------------------ | --------------------- | ----------------------- | -------------------- | ---------------------- |
|
|
128
|
+
| Vector3 Object | 2,548,013 ops/s | 1,942,440 ops/s | 97 MB/s | 73 MB/s |
|
|
129
|
+
| Alphabet String | 3,221,556 ops/s | 2,716,617 ops/s | 624 MB/s | 537 MB/s |
|
|
130
|
+
| Small JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
131
|
+
| Medium JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
132
|
+
| Large JSON Object | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
133
|
+
|
|
134
|
+
### Real-World Usage
|
|
137
135
|
|
|
138
|
-
|
|
136
|
+
| Scenario | JSON Size (kb) | Serialization Time (ops/s) | Deserialization Time (ops/s) | Throughput (GB/s) |
|
|
137
|
+
| ---------------- | -------------- | -------------------------- | ---------------------------- | ----------------- |
|
|
138
|
+
| Web API Response | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
139
|
+
| Database Entry | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
140
|
+
| File Parsing | [Fill Value] | [Fill Value] | [Fill Value] | [Fill Value] |
|
|
139
141
|
|
|
140
142
|
## 📃 License
|
|
141
143
|
|
|
@@ -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,24 @@
|
|
|
1
|
+
import {JSON} from "..";
|
|
2
|
+
import {Vec3} from "./schemas";
|
|
3
|
+
import {bs} from "../../modules/as-bs/assembly";
|
|
4
|
+
import {bench} from "../custom/bench";
|
|
5
|
+
import {serializeString_SIMD} from "../serialize/simd/string";
|
|
6
|
+
import {deserializeString_SIMD} from "../deserialize/simd/string";
|
|
7
|
+
import {serializeString} from "../serialize/simple/string";
|
|
8
|
+
import {deserializeString} from "../deserialize/simple/string";
|
|
9
|
+
|
|
10
|
+
const vec: Vec3 = {x: 1, y: 2, z: 3};
|
|
11
|
+
|
|
12
|
+
bs.ensureSize(4096);
|
|
13
|
+
|
|
14
|
+
bench(
|
|
15
|
+
"Serialize String (Simple)",
|
|
16
|
+
() => {
|
|
17
|
+
serializeString("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789~`!@#$%^&*()-_=+{[}]|\\:;\"'?/>.<,'\"}");
|
|
18
|
+
},
|
|
19
|
+
25_000_000
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
// bench("Deserialize String Simple", () => {
|
|
23
|
+
// deserializeString_SIMD("\"hello world\"")
|
|
24
|
+
// });
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {JSON} from "..";
|
|
2
|
+
import {Vec3} from "./schemas";
|
|
3
|
+
import {bs} from "../../modules/as-bs/assembly";
|
|
4
|
+
import {bench} from "./bench";
|
|
5
|
+
|
|
6
|
+
const vec: Vec3 = {x: 1, y: 2, z: 3};
|
|
7
|
+
|
|
8
|
+
bs.ensureSize(4096);
|
|
9
|
+
bench(
|
|
10
|
+
"Serialize Vector3",
|
|
11
|
+
() => {
|
|
12
|
+
// JSON.__serialize(vec);
|
|
13
|
+
// bs.offset = changetype<usize>(bs.buffer);
|
|
14
|
+
// bs.stackSize = 0;
|
|
15
|
+
JSON.stringify(vec);
|
|
16
|
+
},
|
|
17
|
+
25_000_000
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
// bench("Deserialize Vector3", () => {
|
|
21
|
+
// JSON.parse<Vec3>('{"x":1,"y":2,"z":3}');
|
|
22
|
+
// });
|
|
@@ -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,42 @@
|
|
|
1
|
+
import { JSON } from "..";
|
|
2
|
+
import { describe, expect } from "../../modules/test/assembly";
|
|
3
|
+
import { bytes } from "../util";
|
|
4
|
+
|
|
5
|
+
@json
|
|
6
|
+
class Point {
|
|
7
|
+
x: f64 = 0.0;
|
|
8
|
+
y: f64 = 0.0;
|
|
9
|
+
constructor(x: f64, y: f64) {
|
|
10
|
+
this.x = x;
|
|
11
|
+
this.y = y;
|
|
12
|
+
}
|
|
13
|
+
@serializer
|
|
14
|
+
serializer(self: Point): string {
|
|
15
|
+
return `(${self.x},${self.y})`;
|
|
16
|
+
}
|
|
17
|
+
@deserializer
|
|
18
|
+
deserializer(data: string): Point {
|
|
19
|
+
const dataSize = bytes(data);
|
|
20
|
+
if (dataSize <= 2) throw new Error("Could not deserialize provided data as type Point");
|
|
21
|
+
|
|
22
|
+
const c = data.indexOf(",");
|
|
23
|
+
const x = data.slice(1, c);
|
|
24
|
+
const y = data.slice(c + 1, data.length - 1);
|
|
25
|
+
|
|
26
|
+
return new Point(
|
|
27
|
+
f64.parse(x),
|
|
28
|
+
f64.parse(y)
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
describe("Should serialize using custom serializers", () => {
|
|
35
|
+
expect(JSON.stringify<Point>(new Point(1,2))).toBe("(1.0,2.0)");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe("Should deserialize using custom deserializers", () => {
|
|
39
|
+
const p1 = JSON.parse<Point>("(1.0,2.0)");
|
|
40
|
+
expect(p1.x.toString()).toBe("1.0");
|
|
41
|
+
expect(p1.y.toString()).toBe("2.0");
|
|
42
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export function bench(description: string, routine: () => void, ops: u64 = 1_000_000): void {
|
|
2
|
+
console.log(" - Benchmarking " + description);
|
|
3
|
+
const start = Date.now();
|
|
4
|
+
let count = ops;
|
|
5
|
+
while (count != 0) {
|
|
6
|
+
routine();
|
|
7
|
+
count--;
|
|
8
|
+
}
|
|
9
|
+
const elapsed = Date.now() - start;
|
|
10
|
+
|
|
11
|
+
let opsPerSecond = (ops * 1000) / elapsed;
|
|
12
|
+
|
|
13
|
+
console.log(` Completed benchmark in ${formatNumber(elapsed)}ms at ${formatNumber(opsPerSecond)} ops/s\n`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function formatNumber(n: u64): string {
|
|
17
|
+
let str = n.toString();
|
|
18
|
+
let len = str.length;
|
|
19
|
+
let result = "";
|
|
20
|
+
let commaOffset = len % 3;
|
|
21
|
+
for (let i = 0; i < len; i++) {
|
|
22
|
+
if (i > 0 && (i - commaOffset) % 3 == 0) result += ",";
|
|
23
|
+
result += str.charAt(i);
|
|
24
|
+
}
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
@@ -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;
|