json-as 1.1.20 → 1.1.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +42 -50
- package/assembly/__tests__/arbitrary.spec.ts +16 -0
- package/assembly/__tests__/enum.spec.ts +35 -0
- package/assembly/deserialize/simd/string.ts +18 -19
- package/assembly/deserialize/simple/object.ts +15 -8
- package/assembly/index.ts +15 -9
- package/assembly/serialize/simd/string.ts +2 -3
- package/assembly/serialize/simple/arbitrary.ts +1 -0
- package/assembly/serialize/simple/array.ts +1 -1
- package/assembly/serialize/simple/bool.ts +1 -1
- package/assembly/serialize/simple/float.ts +3 -1
- package/assembly/serialize/simple/integer.ts +1 -1
- package/assembly/serialize/simple/map.ts +2 -2
- package/assembly/serialize/simple/object.ts +28 -26
- package/assembly/test.tmp.ts +91 -402
- package/assembly/test.ts +20 -37
- package/lib/as-bs.ts +13 -12
- package/package.json +7 -7
- package/run-bench.as.sh +3 -3
- package/run-bench.js.sh +1 -1
- package/run-tests.sh +1 -1
- package/transform/lib/index.js +52 -18
- package/transform/lib/index.js.map +1 -1
- package/transform/lib/types.js +22 -1
- package/transform/lib/types.js.map +1 -1
- package/transform/src/index.ts +61 -19
- package/transform/src/types.ts +36 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 2025-09-01 - 1.1.22
|
|
4
|
+
|
|
5
|
+
- fix: Type aliases should work across files [#154](https://github.com/JairusSW/json-as/issues/154)
|
|
6
|
+
|
|
7
|
+
## 2025-08-14 - 1.1.21
|
|
8
|
+
|
|
9
|
+
- fix: JSON.parse on classes with enums [#155](https://github.com/JairusSW/json-as/issues/155)
|
|
10
|
+
- fix: Resolve memory OOB issue within `serializeFloat` function [#153](https://github.com/JairusSW/json-as/issues/153)
|
|
11
|
+
|
|
3
12
|
## 2025-07-14 - 1.1.20
|
|
4
13
|
|
|
5
14
|
- feat: enable SIMD string serialization
|
package/README.md
CHANGED
|
@@ -1,34 +1,6 @@
|
|
|
1
|
-
<
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
██ ██ ██ ██ ████ ██ ██ ██ ██
|
|
5
|
-
██ ███████ ██ ██ ██ ██ ██ █████ ███████ ███████
|
|
6
|
-
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
7
|
-
█████ ███████ ██████ ██ ████ ██ ██ ███████
|
|
8
|
-
</span>
|
|
9
|
-
AssemblyScript - v1.1.20
|
|
10
|
-
</pre>
|
|
11
|
-
</h6>
|
|
12
|
-
|
|
13
|
-
## 📝 About
|
|
14
|
-
|
|
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
|
-
|
|
17
|
-
## 📚 Contents
|
|
18
|
-
|
|
19
|
-
- [Installation](#-installation)
|
|
20
|
-
- [Usage](#-usage)
|
|
21
|
-
- [Examples](#-examples)
|
|
22
|
-
- [Omitting Fields](#️-omitting-fields)
|
|
23
|
-
- [Nullable Primitives](#️-using-nullable-primitives)
|
|
24
|
-
- [Unknown or Dynamic Data](#-working-with-unknown-or-dynamic-data)
|
|
25
|
-
- [Using Raw JSON Strings](#️-using-raw-json-strings)
|
|
26
|
-
- [Using Enums](#️-working-with-enums)
|
|
27
|
-
- [Custom Serializers](#️-using-custom-serializers-or-deserializers)
|
|
28
|
-
- [Performance](#-performance)
|
|
29
|
-
- [Debugging](#-debugging)
|
|
30
|
-
- [License](#-license)
|
|
31
|
-
- [Contact](#-contact)
|
|
1
|
+
<h1 align="center"><pre> ╦╔═╗╔═╗╔╗╔ ╔═╗╔═╗
|
|
2
|
+
║╚═╗║ ║║║║══╠═╣╚═╗
|
|
3
|
+
╚╝╚═╝╚═╝╝╚╝ ╩ ╩╚═╝</pre></h1>
|
|
32
4
|
|
|
33
5
|
## 💾 Installation
|
|
34
6
|
|
|
@@ -297,7 +269,7 @@ console.log(JSON.stringify(map));
|
|
|
297
269
|
|
|
298
270
|
### 📝 Working with enums
|
|
299
271
|
|
|
300
|
-
By default, enums arn't supported by
|
|
272
|
+
By default, enums with values other than `i32` arn't supported by AssemblyScript. However, you can use a workaround:
|
|
301
273
|
|
|
302
274
|
```typescript
|
|
303
275
|
namespace Foo {
|
|
@@ -406,19 +378,6 @@ These benchmarks compare this library to JavaScript's native `JSON.stringify` an
|
|
|
406
378
|
| Medium Object | 494 bytes | 2,395,210 ops/s | 1,381,693 ops/s | 1,183 MB/s | 682.5 MB/s |
|
|
407
379
|
| Large Object | 3374 bytes | 222,222 ops/s | 117,233 ops/s | 749.7 MB/s | 395.5 MB/s |
|
|
408
380
|
|
|
409
|
-
**📌 Insights**
|
|
410
|
-
|
|
411
|
-
- JSON-AS consistently outperforms JavaScript's native implementation.
|
|
412
|
-
|
|
413
|
-
- **Serialization Speed:**
|
|
414
|
-
|
|
415
|
-
- JSON-AS achieves speeds up to `2,133 MB/s`, significantly faster than JavaScript's peak of `1,416 MB/s`.
|
|
416
|
-
- Large objects see the biggest improvement, with JSON-AS at `2,074 MB/s` vs. JavaScript’s `749.7 MB/s`.
|
|
417
|
-
|
|
418
|
-
- **Deserialization Speed:**
|
|
419
|
-
- JSON-AS reaches `1,986 MB/s`, while JavaScript caps at `1,592 MB/s`.
|
|
420
|
-
- Small and medium objects see the most significant performance boost overall.
|
|
421
|
-
|
|
422
381
|
### 📈 Comparison to v0.9.x version
|
|
423
382
|
|
|
424
383
|
**Table 1** - _v1.0.0_
|
|
@@ -441,12 +400,45 @@ These benchmarks compare this library to JavaScript's native `JSON.stringify` an
|
|
|
441
400
|
| Medium Object | 494 bytes | 522,193 ops/s | 508,582 ops/s | 258.0 MB/s | 251.2 MB/s |
|
|
442
401
|
| Large Object | 3374 bytes | 51,229 ops/s | 65,585 ops/s | 172.8 MB/s | 221.3 MB/s |
|
|
443
402
|
|
|
444
|
-
|
|
403
|
+
### Running benchmarks locally
|
|
404
|
+
|
|
405
|
+
Benchmarks are run directly on top of v8 for more tailored control
|
|
406
|
+
|
|
407
|
+
1. Download JSVU off of npm
|
|
408
|
+
|
|
409
|
+
```bash
|
|
410
|
+
npm install jsvu -g
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
2. Modify your dotfiles to add `~/.jsvu/bin` to `PATH`
|
|
414
|
+
|
|
415
|
+
```bash
|
|
416
|
+
export PATH="${HOME}/.jsvu/bin:${PATH}"
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
3. Clone the repository
|
|
445
420
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
421
|
+
```bash
|
|
422
|
+
git clone https://github.com/JairusSW/json-as
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
4. Install dependencies
|
|
426
|
+
|
|
427
|
+
```bash
|
|
428
|
+
npm i
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
5. Run benchmarks for either AssemblyScript or JavaScript
|
|
432
|
+
|
|
433
|
+
```bash
|
|
434
|
+
./run-bench.as.sh
|
|
435
|
+
```
|
|
436
|
+
|
|
437
|
+
or
|
|
438
|
+
|
|
439
|
+
```bash
|
|
440
|
+
./run-bench.js.sh
|
|
441
|
+
```
|
|
450
442
|
|
|
451
443
|
## 🔭 What's Next
|
|
452
444
|
|
|
@@ -8,6 +8,22 @@ describe("Should serialize arbitrary types", () => {
|
|
|
8
8
|
expect(JSON.stringify(JSON.Value.from(true))).toBe("true");
|
|
9
9
|
expect(JSON.stringify(JSON.Value.from(new Vec3()))).toBe('{"x":1.0,"y":2.0,"z":3.0}');
|
|
10
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
|
+
const o = new JSON.Obj();
|
|
13
|
+
o.set("schema", "http://json-schema.org/draft-07/schema#");
|
|
14
|
+
o.set("additionalProperties", false);
|
|
15
|
+
o.set("properties", new JSON.Obj());
|
|
16
|
+
o.get("properties")!.as<JSON.Obj>().set("duration", new JSON.Obj());
|
|
17
|
+
o.get("properties")!.as<JSON.Obj>().get("duration")!.as<JSON.Obj>().set("default", 10.0);
|
|
18
|
+
o.get("properties")!.as<JSON.Obj>().get("duration")!.as<JSON.Obj>().set("description", "Duration of the operation in seconds");
|
|
19
|
+
o.get("properties")!.as<JSON.Obj>().get("duration")!.as<JSON.Obj>().set("type", "number");
|
|
20
|
+
o.get("properties")!.as<JSON.Obj>().set("steps", new JSON.Obj());
|
|
21
|
+
o.get("properties")!.as<JSON.Obj>().get("steps")!.as<JSON.Obj>().set("default", 5.0);
|
|
22
|
+
o.get("properties")!.as<JSON.Obj>().get("steps")!.as<JSON.Obj>().set("description", "Number of steps in the operation");
|
|
23
|
+
o.get("properties")!.as<JSON.Obj>().get("steps")!.as<JSON.Obj>().set("type", "number");
|
|
24
|
+
o.set("type", "object");
|
|
25
|
+
|
|
26
|
+
expect(o.toString()).toBe('{"schema":"http://json-schema.org/draft-07/schema#","additionalProperties":false,"properties":{"duration":{"default":10.0,"description":"Duration of the operation in seconds","type":"number"},"steps":{"default":5.0,"description":"Number of steps in the operation","type":"number"}},"type":"object"}');
|
|
11
27
|
});
|
|
12
28
|
|
|
13
29
|
describe("Should deserialize arbitrary types", () => {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { JSON } from "..";
|
|
2
|
+
import { describe, expect } from "./lib";
|
|
3
|
+
|
|
4
|
+
enum Enum1 {
|
|
5
|
+
Zero = 0,
|
|
6
|
+
One = 1,
|
|
7
|
+
Two = 2,
|
|
8
|
+
Three = 3,
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@json
|
|
13
|
+
class DataWithEnum {
|
|
14
|
+
v: Enum1 = Enum1.One;
|
|
15
|
+
constructor(v: Enum1) {
|
|
16
|
+
this.v = v;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
describe("Should serialize enums", () => {
|
|
21
|
+
expect(JSON.stringify<Enum1>(Enum1.One)).toBe("1");
|
|
22
|
+
expect(JSON.stringify<Enum1>(Enum1.Zero)).toBe("0");
|
|
23
|
+
expect(JSON.stringify<DataWithEnum>(new DataWithEnum(Enum1.Two))).toBe('{"v":2}');
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe("Should deserialize enums", () => {
|
|
27
|
+
const date1 = JSON.parse<Enum1>("2");
|
|
28
|
+
expect(date1).toBe(Enum1.Two);
|
|
29
|
+
|
|
30
|
+
const date2 = JSON.parse<Enum1>("0");
|
|
31
|
+
expect(date2).toBe(Enum1.Zero);
|
|
32
|
+
|
|
33
|
+
const date3 = JSON.parse<DataWithEnum>('{"v":3}');
|
|
34
|
+
expect(date3.v).toBe(Enum1.Three);
|
|
35
|
+
});
|
|
@@ -10,14 +10,13 @@ import { DESERIALIZE_ESCAPE_TABLE, ESCAPE_HEX_TABLE } from "../../globals/tables
|
|
|
10
10
|
// todo: optimize and stuff. it works, its not pretty. ideally, i'd like this to be (nearly) branchless
|
|
11
11
|
export function deserializeString_SIMD(srcStart: usize, srcEnd: usize, dst: usize): usize {
|
|
12
12
|
const SPLAT_92 = i16x8.splat(92); /* \ */
|
|
13
|
-
|
|
13
|
+
srcStart += 2;
|
|
14
|
+
srcEnd -= 2;
|
|
14
15
|
let dst_ptr = changetype<usize>(dst);
|
|
16
|
+
const src_end_15 = srcEnd - 15;
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
while (src_ptr < src_end_15) {
|
|
20
|
-
const block = v128.load(src_ptr);
|
|
18
|
+
while (srcStart < src_end_15) {
|
|
19
|
+
const block = v128.load(srcStart);
|
|
21
20
|
v128.store(dst_ptr, block);
|
|
22
21
|
|
|
23
22
|
const backslash_indices = i16x8.eq(block, SPLAT_92);
|
|
@@ -26,7 +25,7 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize, dst: usiz
|
|
|
26
25
|
while (mask != 0) {
|
|
27
26
|
const lane_index = ctz(mask) << 1;
|
|
28
27
|
const dst_offset = dst_ptr + lane_index;
|
|
29
|
-
const src_offset =
|
|
28
|
+
const src_offset = srcStart + lane_index;
|
|
30
29
|
const code = load<u16>(src_offset, 2);
|
|
31
30
|
|
|
32
31
|
mask &= mask - 1;
|
|
@@ -48,7 +47,7 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize, dst: usiz
|
|
|
48
47
|
v128.store(dst_offset, v128.load(src_offset, 4), 2);
|
|
49
48
|
if (lane_index >= 6) {
|
|
50
49
|
const bytes_left = lane_index - 4;
|
|
51
|
-
|
|
50
|
+
srcStart += bytes_left;
|
|
52
51
|
dst_ptr += bytes_left;
|
|
53
52
|
// console.log(" e: " + (bytes_left).toString())
|
|
54
53
|
}
|
|
@@ -59,25 +58,25 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize, dst: usiz
|
|
|
59
58
|
v128.store(dst_offset, v128.load(src_offset, 4), 2);
|
|
60
59
|
// console.log("Escaped:");
|
|
61
60
|
if (lane_index == 14) {
|
|
62
|
-
|
|
61
|
+
srcStart += 2;
|
|
63
62
|
} else {
|
|
64
63
|
dst_ptr -= 2;
|
|
65
64
|
}
|
|
66
65
|
}
|
|
67
66
|
}
|
|
68
67
|
|
|
69
|
-
|
|
68
|
+
srcStart += 16;
|
|
70
69
|
dst_ptr += 16;
|
|
71
70
|
|
|
72
|
-
// console.log("src: " + (
|
|
71
|
+
// console.log("src: " + (srcStart - changetype<usize>(src)).toString());
|
|
73
72
|
// console.log("dst: " + (dst_ptr - dst).toString());
|
|
74
73
|
}
|
|
75
|
-
while (
|
|
76
|
-
let code = load<u16>(
|
|
74
|
+
while (srcStart < srcEnd) {
|
|
75
|
+
let code = load<u16>(srcStart);
|
|
77
76
|
if (code == BACK_SLASH) {
|
|
78
|
-
code = load<u16>(DESERIALIZE_ESCAPE_TABLE + load<u8>(
|
|
79
|
-
if (code == 117 && load<u32>(
|
|
80
|
-
const block = load<u32>(
|
|
77
|
+
code = load<u16>(DESERIALIZE_ESCAPE_TABLE + load<u8>(srcStart, 2));
|
|
78
|
+
if (code == 117 && load<u32>(srcStart, 4) == 3145776) {
|
|
79
|
+
const block = load<u32>(srcStart, 8);
|
|
81
80
|
const codeA = block & 0xffff;
|
|
82
81
|
const codeB = (block >> 16) & 0xffff;
|
|
83
82
|
const escapedA = load<u8>(ESCAPE_HEX_TABLE + codeA);
|
|
@@ -85,16 +84,16 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize, dst: usiz
|
|
|
85
84
|
const escaped = (escapedA << 4) + escapedB;
|
|
86
85
|
store<u16>(dst_ptr, escaped);
|
|
87
86
|
dst_ptr += 2;
|
|
88
|
-
|
|
87
|
+
srcStart += 12;
|
|
89
88
|
} else {
|
|
90
89
|
store<u16>(dst_ptr, code);
|
|
91
90
|
dst_ptr += 2;
|
|
92
|
-
|
|
91
|
+
srcStart += 4;
|
|
93
92
|
}
|
|
94
93
|
} else {
|
|
95
94
|
store<u16>(dst_ptr, code);
|
|
96
95
|
dst_ptr += 2;
|
|
97
|
-
|
|
96
|
+
srcStart += 2;
|
|
98
97
|
}
|
|
99
98
|
}
|
|
100
99
|
|
|
@@ -3,6 +3,10 @@ import { BACK_SLASH, COMMA, CHAR_F, BRACE_LEFT, BRACKET_LEFT, CHAR_N, QUOTE, BRA
|
|
|
3
3
|
import { isSpace } from "../../util";
|
|
4
4
|
import { ptrToStr } from "../../util/ptrToStr";
|
|
5
5
|
import { deserializeArbitrary } from "./arbitrary";
|
|
6
|
+
import { deserializeArray } from "./array";
|
|
7
|
+
import { deserializeBoolean } from "./bool";
|
|
8
|
+
import { deserializeFloat } from "./float";
|
|
9
|
+
import { deserializeString } from "./string";
|
|
6
10
|
|
|
7
11
|
export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): JSON.Obj {
|
|
8
12
|
const out = changetype<JSON.Obj>(dst || changetype<usize>(new JSON.Obj()));
|
|
@@ -48,8 +52,8 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
|
|
|
48
52
|
while (srcStart < srcEnd) {
|
|
49
53
|
const code = load<u16>(srcStart);
|
|
50
54
|
if (code == QUOTE && load<u16>(srcStart - 2) !== BACK_SLASH) {
|
|
51
|
-
// console.log("Value (string)
|
|
52
|
-
out.set(ptrToStr(keyStart, keyEnd),
|
|
55
|
+
// console.log("Value (string):-" + deserializeString(lastIndex, srcStart + 2, 0) + "-");
|
|
56
|
+
out.set(ptrToStr(keyStart, keyEnd), deserializeString(lastIndex, srcStart + 2, 0));
|
|
53
57
|
// while (isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
54
58
|
srcStart += 4;
|
|
55
59
|
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
@@ -65,7 +69,7 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
|
|
|
65
69
|
const code = load<u16>(srcStart);
|
|
66
70
|
if (code == COMMA || code == BRACE_RIGHT || isSpace(code)) {
|
|
67
71
|
// console.log("Value (number): " + ptrToStr(lastIndex, srcStart));
|
|
68
|
-
out.set(ptrToStr(keyStart, keyEnd),
|
|
72
|
+
out.set(ptrToStr(keyStart, keyEnd), deserializeFloat<f64>(lastIndex, srcStart));
|
|
69
73
|
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
70
74
|
// /* empty */
|
|
71
75
|
// }
|
|
@@ -88,7 +92,7 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
|
|
|
88
92
|
} else if (code == BRACE_RIGHT) {
|
|
89
93
|
if (--depth == 0) {
|
|
90
94
|
// console.log("Value (object): " + ptrToStr(lastIndex, srcStart + 2));
|
|
91
|
-
out.set(ptrToStr(keyStart, keyEnd),
|
|
95
|
+
out.set(ptrToStr(keyStart, keyEnd), deserializeObject(lastIndex, (srcStart += 2), 0));
|
|
92
96
|
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
93
97
|
keyStart = 0;
|
|
94
98
|
// while (isSpace(load<u16>(srcStart))) {
|
|
@@ -108,7 +112,7 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
|
|
|
108
112
|
if (code == BRACKET_RIGHT) {
|
|
109
113
|
if (--depth == 0) {
|
|
110
114
|
// console.log("Value (array): " + ptrToStr(lastIndex, srcStart + 2));
|
|
111
|
-
out.set(ptrToStr(keyStart, keyEnd),
|
|
115
|
+
out.set(ptrToStr(keyStart, keyEnd), deserializeArray<JSON.Value[]>(lastIndex, (srcStart += 2), 0));
|
|
112
116
|
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
113
117
|
keyStart = 0;
|
|
114
118
|
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
@@ -122,7 +126,8 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
|
|
|
122
126
|
} else if (code == CHAR_T) {
|
|
123
127
|
if (load<u64>(srcStart) == 28429475166421108) {
|
|
124
128
|
// console.log("Value (bool): " + ptrToStr(srcStart, srcStart + 8));
|
|
125
|
-
out.set(ptrToStr(keyStart, keyEnd),
|
|
129
|
+
out.set(ptrToStr(keyStart, keyEnd), true);
|
|
130
|
+
srcStart += 8;
|
|
126
131
|
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
127
132
|
// /* empty */
|
|
128
133
|
// }
|
|
@@ -133,7 +138,8 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
|
|
|
133
138
|
} else if (code == CHAR_F) {
|
|
134
139
|
if (load<u64>(srcStart, 2) == 28429466576093281) {
|
|
135
140
|
// console.log("Value (bool): " + ptrToStr(srcStart, srcStart + 10));
|
|
136
|
-
out.set(ptrToStr(keyStart, keyEnd),
|
|
141
|
+
out.set(ptrToStr(keyStart, keyEnd), false);
|
|
142
|
+
srcStart += 10;
|
|
137
143
|
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
138
144
|
// /* empty */
|
|
139
145
|
// }
|
|
@@ -144,7 +150,8 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
|
|
|
144
150
|
} else if (code == CHAR_N) {
|
|
145
151
|
if (load<u64>(srcStart) == 30399761348886638) {
|
|
146
152
|
// console.log("Value (null): " + ptrToStr(srcStart, srcStart + 8));
|
|
147
|
-
out.set(ptrToStr(keyStart, keyEnd),
|
|
153
|
+
out.set(ptrToStr(keyStart, keyEnd), JSON.Value.from<usize>(0));
|
|
154
|
+
srcStart += 8;
|
|
148
155
|
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
149
156
|
/* empty */
|
|
150
157
|
// }
|
package/assembly/index.ts
CHANGED
|
@@ -260,6 +260,8 @@ export namespace JSON {
|
|
|
260
260
|
}
|
|
261
261
|
}
|
|
262
262
|
|
|
263
|
+
|
|
264
|
+
@final
|
|
263
265
|
export class Value {
|
|
264
266
|
static METHODS: Map<u32, u32> = new Map<u32, u32>();
|
|
265
267
|
public type: i32;
|
|
@@ -334,7 +336,7 @@ export namespace JSON {
|
|
|
334
336
|
this.type = JSON.Types.Struct;
|
|
335
337
|
store<T>(changetype<usize>(this), value, STORAGE);
|
|
336
338
|
// @ts-ignore: supplied by transform
|
|
337
|
-
} else if (isDefined(value.__SERIALIZE)) {
|
|
339
|
+
} else if (isDefined(value.__SERIALIZE) && isManaged<T>(value)) {
|
|
338
340
|
this.type = idof<T>() + JSON.Types.Struct;
|
|
339
341
|
// @ts-ignore
|
|
340
342
|
if (!JSON.Value.METHODS.has(idof<T>())) JSON.Value.METHODS.set(idof<T>(), value.__SERIALIZE.index);
|
|
@@ -419,16 +421,20 @@ export namespace JSON {
|
|
|
419
421
|
}
|
|
420
422
|
}
|
|
421
423
|
}
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
@unsafe private __visit(cookie: u32): void {
|
|
427
|
+
if (this.type >= JSON.Types.String) {
|
|
428
|
+
__visit(load<usize>(changetype<usize>(this), STORAGE), cookie);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
422
431
|
}
|
|
423
432
|
|
|
424
433
|
export class Obj {
|
|
425
|
-
// When accessing stackSize, subtract 2
|
|
426
|
-
// @ts-ignore: type
|
|
427
|
-
private stackSize: u32 = 6;
|
|
428
434
|
// @ts-ignore: type
|
|
429
|
-
|
|
435
|
+
storage: Map<string, JSON.Value> = new Map<string, JSON.Value>();
|
|
430
436
|
|
|
431
|
-
constructor() {
|
|
437
|
+
constructor() {}
|
|
432
438
|
|
|
433
439
|
// @ts-ignore: decorator
|
|
434
440
|
@inline get size(): i32 {
|
|
@@ -437,7 +443,7 @@ export namespace JSON {
|
|
|
437
443
|
|
|
438
444
|
// @ts-ignore: decorator
|
|
439
445
|
@inline set<T>(key: string, value: T): void {
|
|
440
|
-
if (!this.storage.has(key)) this.stackSize += bytes(key) + 8;
|
|
446
|
+
// if (!this.storage.has(key)) this.stackSize += bytes(key) + 8;
|
|
441
447
|
this.storage.set(key, JSON.Value.from<T>(value));
|
|
442
448
|
}
|
|
443
449
|
|
|
@@ -475,7 +481,7 @@ export namespace JSON {
|
|
|
475
481
|
// @ts-ignore: decorator
|
|
476
482
|
@inline static from<T>(value: T): JSON.Obj {
|
|
477
483
|
if (value instanceof JSON.Obj) return value;
|
|
478
|
-
const out =
|
|
484
|
+
const out = new JSON.Obj();
|
|
479
485
|
|
|
480
486
|
if (value instanceof Map) {
|
|
481
487
|
}
|
|
@@ -611,7 +617,7 @@ export namespace JSON {
|
|
|
611
617
|
if (isDefined(type.__DESERIALIZE)) {
|
|
612
618
|
const out = changetype<nonnull<T>>(dst || __new(offsetof<nonnull<T>>(), idof<nonnull<T>>()));
|
|
613
619
|
// @ts-ignore: Defined by transform
|
|
614
|
-
if (
|
|
620
|
+
if (isDefined(type.__INITIALIZE)) out.__INITIALIZE();
|
|
615
621
|
// @ts-ignore: Defined by transform
|
|
616
622
|
return out.__DESERIALIZE(srcStart, srcEnd, out);
|
|
617
623
|
} else if (type instanceof Map) {
|
|
@@ -3,14 +3,13 @@ import { BACK_SLASH } from "../../custom/chars";
|
|
|
3
3
|
import { SERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
4
4
|
import { bytes } from "../../util";
|
|
5
5
|
|
|
6
|
-
const U00_MARKER = 13511005048209500;
|
|
7
|
-
|
|
8
6
|
/**
|
|
9
7
|
* Serializes strings into their JSON counterparts using SIMD operations
|
|
10
8
|
* @param srcStart pointer to begin serializing at
|
|
11
9
|
* @param srcEnd pointer to end serialization at
|
|
12
10
|
*/
|
|
13
11
|
export function serializeString_SIMD(src: string): void {
|
|
12
|
+
const U00_MARKER = 13511005048209500;
|
|
14
13
|
const SPLAT_34 = i16x8.splat(34); /* " */
|
|
15
14
|
const SPLAT_92 = i16x8.splat(92); /* \ */
|
|
16
15
|
|
|
@@ -21,7 +20,7 @@ export function serializeString_SIMD(src: string): void {
|
|
|
21
20
|
const srcEnd = srcStart + srcSize;
|
|
22
21
|
const srcEnd16 = srcEnd - 16;
|
|
23
22
|
|
|
24
|
-
bs.proposeSize(srcSize +
|
|
23
|
+
bs.proposeSize(srcSize + 4);
|
|
25
24
|
|
|
26
25
|
store<u8>(changetype<usize>(bs.offset), 34); /* " */
|
|
27
26
|
bs.offset += 2;
|
|
@@ -4,5 +4,7 @@ import { dtoa_buffered } from "util/number";
|
|
|
4
4
|
// @ts-ignore: inline
|
|
5
5
|
@inline export function serializeFloat<T extends number>(data: T): void {
|
|
6
6
|
bs.ensureSize(64);
|
|
7
|
-
|
|
7
|
+
const size = dtoa_buffered(bs.offset, data) << 1;
|
|
8
|
+
bs.stackSize += size;
|
|
9
|
+
bs.offset += size;
|
|
8
10
|
}
|
|
@@ -5,8 +5,8 @@ import { itoa_buffered } from "util/number";
|
|
|
5
5
|
@inline export function serializeInteger<T extends number>(data: T): void {
|
|
6
6
|
bs.ensureSize(sizeof<T>() << 3);
|
|
7
7
|
const bytesWritten = itoa_buffered(bs.offset, data) << 1;
|
|
8
|
-
bs.offset += bytesWritten;
|
|
9
8
|
bs.growSize(bytesWritten);
|
|
9
|
+
bs.offset += bytesWritten;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
// 32 {"x":,"y":,"z"}
|
|
@@ -16,7 +16,7 @@ export function serializeMap<T extends Map<any, any>>(src: T): void {
|
|
|
16
16
|
let keys = src.keys();
|
|
17
17
|
let values = src.values();
|
|
18
18
|
|
|
19
|
-
bs.proposeSize(
|
|
19
|
+
bs.proposeSize(4);
|
|
20
20
|
|
|
21
21
|
store<u16>(bs.offset, BRACE_LEFT);
|
|
22
22
|
bs.offset += 2;
|
|
@@ -37,7 +37,7 @@ export function serializeMap<T extends Map<any, any>>(src: T): void {
|
|
|
37
37
|
store<u16>(bs.offset, COLON);
|
|
38
38
|
bs.offset += 2;
|
|
39
39
|
JSON.__serialize(unchecked(values[srcEnd]));
|
|
40
|
-
bs.growSize(2);
|
|
40
|
+
// bs.growSize(2);
|
|
41
41
|
store<u16>(bs.offset, BRACE_RIGHT);
|
|
42
42
|
bs.offset += 2;
|
|
43
43
|
}
|
|
@@ -1,44 +1,46 @@
|
|
|
1
1
|
import { bs } from "../../../lib/as-bs";
|
|
2
2
|
import { JSON } from "../..";
|
|
3
|
-
import { BRACE_LEFT, BRACE_RIGHT,
|
|
4
|
-
import {
|
|
3
|
+
import { BRACE_LEFT, BRACE_RIGHT, COLON, COMMA } from "../../custom/chars";
|
|
4
|
+
import { serializeArbitrary } from "./arbitrary";
|
|
5
|
+
import { serializeString } from "./string";
|
|
5
6
|
|
|
6
|
-
export function serializeObject(
|
|
7
|
-
|
|
7
|
+
export function serializeObject(src: JSON.Obj): void {
|
|
8
|
+
const srcSize = src.size;
|
|
9
|
+
const srcEnd = srcSize - 1;
|
|
10
|
+
|
|
11
|
+
if (srcSize == 0) {
|
|
8
12
|
bs.proposeSize(4);
|
|
9
13
|
store<u32>(bs.offset, 8192123);
|
|
10
14
|
bs.offset += 4;
|
|
11
15
|
return;
|
|
12
16
|
}
|
|
13
17
|
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const values = data.values();
|
|
17
|
-
|
|
18
|
-
// console.log(" Keys " + keys.join(" "));
|
|
19
|
-
// console.log(" Values " + values.map<string>(v => v.toString()).join(" "))
|
|
18
|
+
const keys = src.keys();
|
|
19
|
+
const values = src.values();
|
|
20
20
|
|
|
21
|
+
bs.growSize(2);
|
|
21
22
|
store<u16>(bs.offset, BRACE_LEFT);
|
|
22
23
|
bs.offset += 2;
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
const keySize = bytes(key);
|
|
35
|
-
store<u32>(bs.offset, 2228268); // ,"
|
|
36
|
-
memory.copy(bs.offset + 4, changetype<usize>(key), keySize);
|
|
37
|
-
store<u32>((bs.offset += keySize + 4), 3801122); // ":
|
|
38
|
-
bs.offset += 4;
|
|
39
|
-
JSON.__serialize(unchecked(values[i]));
|
|
25
|
+
for (let i = 0; i < srcEnd; i++) {
|
|
26
|
+
serializeString(unchecked(keys[i]));
|
|
27
|
+
bs.growSize(2);
|
|
28
|
+
store<u16>(bs.offset, COLON);
|
|
29
|
+
bs.offset += 2;
|
|
30
|
+
|
|
31
|
+
serializeArbitrary(unchecked(values[i]));
|
|
32
|
+
bs.growSize(2);
|
|
33
|
+
store<u16>(bs.offset, COMMA);
|
|
34
|
+
bs.offset += 2;
|
|
40
35
|
}
|
|
41
36
|
|
|
37
|
+
serializeString(unchecked(keys[srcEnd]));
|
|
38
|
+
bs.growSize(2);
|
|
39
|
+
store<u16>(bs.offset, COLON);
|
|
40
|
+
bs.offset += 2;
|
|
41
|
+
serializeArbitrary(unchecked(values[srcEnd]));
|
|
42
|
+
|
|
43
|
+
bs.growSize(2);
|
|
42
44
|
store<u16>(bs.offset, BRACE_RIGHT);
|
|
43
45
|
bs.offset += 2;
|
|
44
46
|
}
|