json-as 1.0.0-beta.16 → 1.0.0-beta.18
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 → CHANGELOG.md} +11 -1
- package/README.md +80 -66
- package/assembly/__benches__/abc.bench.ts +22 -0
- package/assembly/__benches__/large.bench.ts +174 -0
- package/assembly/__benches__/medium.bench.ts +46 -0
- package/assembly/__benches__/small.bench.ts +33 -0
- package/assembly/__benches__/vec3.bench.ts +73 -0
- package/assembly/__tests__/arbitrary.spec.ts +3 -3
- package/assembly/__tests__/array.spec.ts +5 -8
- package/assembly/__tests__/box.spec.ts +10 -20
- package/assembly/__tests__/custom.spec.ts +7 -6
- package/assembly/__tests__/date.spec.ts +4 -6
- package/assembly/__tests__/float.spec.ts +3 -3
- package/assembly/__tests__/lib/index.ts +4 -0
- package/assembly/__tests__/map.spec.ts +1 -1
- package/assembly/__tests__/misc.spec.ts +90 -0
- package/assembly/__tests__/raw.spec.ts +3 -3
- package/assembly/__tests__/struct.spec.ts +3 -6
- package/assembly/custom/bench.ts +9 -2
- package/assembly/deserialize/simple/array/struct.ts +2 -3
- package/assembly/deserialize/simple/array.ts +1 -1
- package/assembly/deserialize/simple/bool.ts +1 -1
- package/assembly/deserialize/simple/map.ts +3 -4
- package/assembly/deserialize/simple/object.ts +2 -3
- package/assembly/deserialize/simple/raw.ts +1 -1
- package/assembly/deserialize/simple/struct.ts +9 -5
- package/assembly/index.ts +14 -5
- package/assembly/serialize/simple/arbitrary.ts +1 -0
- package/assembly/serialize/simple/array.ts +1 -0
- package/assembly/serialize/simple/bool.ts +1 -0
- package/assembly/serialize/simple/date.ts +1 -0
- package/assembly/serialize/simple/float.ts +1 -0
- package/assembly/serialize/simple/integer.ts +2 -1
- package/assembly/serialize/simple/map.ts +1 -0
- package/assembly/serialize/simple/object.ts +4 -3
- package/assembly/serialize/simple/raw.ts +1 -0
- package/assembly/serialize/simple/string.ts +1 -0
- package/assembly/test.ts +63 -39
- 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/index.ts +1 -1
- package/{lib → modules}/as-bs.ts +4 -13
- package/package.json +5 -3
- package/run-bench.as.sh +15 -0
- package/run-bench.js.sh +12 -0
- package/run-tests.sh +1 -1
- package/transform/lib/index.js +26 -46
- package/transform/lib/index.js.map +1 -1
- package/transform/src/index.ts +31 -72
- 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/assembly/as-bs.d.ts +0 -53
- package/bench/schemas.ts +0 -5
- package/bench/string.bench.ts +0 -16
- package/bench.js +0 -85
- /package/bench/{bench.ts → lib/bench.ts} +0 -0
- /package/{lib → modules}/tsconfig.json +0 -0
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { JSON } from "..";
|
|
2
|
+
import { bs } from "../../modules/as-bs";
|
|
3
|
+
import { bench } from "../custom/bench";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@json
|
|
7
|
+
class Vec3 {
|
|
8
|
+
public x!: i32;
|
|
9
|
+
public y!: i32;
|
|
10
|
+
public z!: i32;
|
|
11
|
+
|
|
12
|
+
@inline __SERIALIZE(ptr: usize): void {
|
|
13
|
+
bs.proposeSize(98);
|
|
14
|
+
store<u64>(bs.offset, 9570664606466171, 0); // {"x"
|
|
15
|
+
store<u16>(bs.offset, 58, 8); // :
|
|
16
|
+
bs.offset += 10;
|
|
17
|
+
JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("x")));
|
|
18
|
+
store<u64>(bs.offset, 9570668901433388, 0); // ,"y"
|
|
19
|
+
store<u16>(bs.offset, 58, 8); // :
|
|
20
|
+
bs.offset += 10;
|
|
21
|
+
JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("y")));
|
|
22
|
+
store<u64>(bs.offset, 9570673196400684, 0); // ,"z"
|
|
23
|
+
store<u16>(bs.offset, 58, 8); // :
|
|
24
|
+
bs.offset += 10;
|
|
25
|
+
JSON.__serialize<i32>(load<i32>(ptr, offsetof<this>("z")));
|
|
26
|
+
store<u16>(bs.offset, 125, 0); // }
|
|
27
|
+
bs.offset += 2;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@inline __INITIALIZE(): this {
|
|
31
|
+
return this;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
@inline __DESERIALIZE(keyStart: usize, keyEnd: usize, valStart: usize, valEnd: usize, ptr: usize): void {
|
|
35
|
+
switch (load<u16>(keyStart)) {
|
|
36
|
+
case 120: {
|
|
37
|
+
// x
|
|
38
|
+
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("x"));
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
case 121: {
|
|
42
|
+
// y
|
|
43
|
+
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("y"));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
case 122: {
|
|
47
|
+
// z
|
|
48
|
+
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("z"));
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const v1: Vec3 = { x: 1, y: 2, z: 3 };
|
|
57
|
+
const v2 = '{"x":1,"y":2,"z":3}';
|
|
58
|
+
|
|
59
|
+
bench(
|
|
60
|
+
"Serialize Vec3",
|
|
61
|
+
() => {
|
|
62
|
+
JSON.stringify(v1);
|
|
63
|
+
},
|
|
64
|
+
16_000_00,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
bench(
|
|
68
|
+
"Deserialize Vec3",
|
|
69
|
+
() => {
|
|
70
|
+
JSON.parse<Vec3>(v2);
|
|
71
|
+
},
|
|
72
|
+
16_000_00,
|
|
73
|
+
);
|
|
@@ -3,7 +3,7 @@ import { describe, expect } from "./lib";
|
|
|
3
3
|
import { Vec3 } from "./types";
|
|
4
4
|
|
|
5
5
|
describe("Should serialize arbitrary types", () => {
|
|
6
|
-
expect(JSON.stringify(JSON.Value.from("hello world"))).toBe("
|
|
6
|
+
expect(JSON.stringify(JSON.Value.from("hello world"))).toBe('"hello world"');
|
|
7
7
|
expect(JSON.stringify(JSON.Value.from(0))).toBe("0");
|
|
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}');
|
|
@@ -11,9 +11,9 @@ describe("Should serialize arbitrary types", () => {
|
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
describe("Should deserialize arbitrary types", () => {
|
|
14
|
-
expect(JSON.parse<JSON.Value>("
|
|
14
|
+
expect(JSON.parse<JSON.Value>('"hello world"').get<string>()).toBe("hello world");
|
|
15
15
|
expect(JSON.parse<JSON.Value>("0.0").toString()).toBe("0.0");
|
|
16
16
|
expect(JSON.parse<JSON.Value>("true").toString()).toBe("true");
|
|
17
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
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
|
-
});
|
|
19
|
+
});
|
|
@@ -59,8 +59,8 @@ describe("Should serialize object arrays", () => {
|
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
describe("Should deserialize integer arrays", () => {
|
|
62
|
-
expect(JSON.stringify(JSON.parse<u32[]>("[0,100,101]"))).toBe(
|
|
63
|
-
expect(JSON.stringify(JSON.parse<u64[]>("[0,100,101]"))).toBe(
|
|
62
|
+
expect(JSON.stringify(JSON.parse<u32[]>("[0,100,101]"))).toBe("[0,100,101]");
|
|
63
|
+
expect(JSON.stringify(JSON.parse<u64[]>("[0,100,101]"))).toBe("[0,100,101]");
|
|
64
64
|
expect(JSON.stringify(JSON.parse<i32[]>("[0,100,101,-100,-101]"))).toBe("[0,100,101,-100,-101]");
|
|
65
65
|
expect(JSON.stringify(JSON.parse<i64[]>("[0,100,101,-100,-101]"))).toBe("[0,100,101,-100,-101]");
|
|
66
66
|
});
|
|
@@ -76,7 +76,7 @@ describe("Should deserialize boolean arrays", () => {
|
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
describe("Should deserialize string arrays", () => {
|
|
79
|
-
expect(JSON.stringify(JSON.parse<string[]>(
|
|
79
|
+
expect(JSON.stringify(JSON.parse<string[]>('["string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"]'))).toBe('["string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"]');
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
describe("Should deserialize nested integer arrays", () => {
|
|
@@ -93,13 +93,10 @@ describe("Should deserialize nested boolean arrays", () => {
|
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
describe("Should deserialize object arrays", () => {
|
|
96
|
-
expect(
|
|
97
|
-
JSON.stringify(JSON.parse<Vec3[]>(
|
|
98
|
-
'[{"x":3.4,"y":1.2,"z":8.3},{"x":3.4,"y":-2.1,"z":9.3}]'
|
|
99
|
-
)
|
|
100
|
-
)).toBe('[{"x":3.4,"y":1.2,"z":8.3},{"x":3.4,"y":-2.1,"z":9.3}]');
|
|
96
|
+
expect(JSON.stringify(JSON.parse<Vec3[]>('[{"x":3.4,"y":1.2,"z":8.3},{"x":3.4,"y":-2.1,"z":9.3}]'))).toBe('[{"x":3.4,"y":1.2,"z":8.3},{"x":3.4,"y":-2.1,"z":9.3}]');
|
|
101
97
|
});
|
|
102
98
|
|
|
99
|
+
|
|
103
100
|
@json
|
|
104
101
|
class Vec3 {
|
|
105
102
|
x: f64 = 0.0;
|
|
@@ -2,36 +2,26 @@ import { JSON } from "..";
|
|
|
2
2
|
import { describe, expect } from "./lib";
|
|
3
3
|
|
|
4
4
|
describe("Should serialize JSON.Box<T>", () => {
|
|
5
|
-
expect(JSON.stringify<JSON.Box<i32> | null>(null))
|
|
6
|
-
.toBe("null");
|
|
5
|
+
expect(JSON.stringify<JSON.Box<i32> | null>(null)).toBe("null");
|
|
7
6
|
|
|
8
|
-
expect(JSON.stringify<JSON.Box<i32> | null>(new JSON.Box<i32>(0)))
|
|
9
|
-
.toBe("0");
|
|
7
|
+
expect(JSON.stringify<JSON.Box<i32> | null>(new JSON.Box<i32>(0))).toBe("0");
|
|
10
8
|
|
|
11
|
-
expect(JSON.stringify<JSON.Box<i32> | null>(new JSON.Box<i32>(1)))
|
|
12
|
-
.toBe("1");
|
|
9
|
+
expect(JSON.stringify<JSON.Box<i32> | null>(new JSON.Box<i32>(1))).toBe("1");
|
|
13
10
|
|
|
14
|
-
expect(JSON.stringify<JSON.Box<boolean> | null>(new JSON.Box<boolean>(false)))
|
|
15
|
-
.toBe("false");
|
|
11
|
+
expect(JSON.stringify<JSON.Box<boolean> | null>(new JSON.Box<boolean>(false))).toBe("false");
|
|
16
12
|
|
|
17
|
-
expect(JSON.stringify<JSON.Box<boolean> | null>(new JSON.Box<boolean>(true)))
|
|
18
|
-
.toBe("true");
|
|
13
|
+
expect(JSON.stringify<JSON.Box<boolean> | null>(new JSON.Box<boolean>(true))).toBe("true");
|
|
19
14
|
});
|
|
20
15
|
|
|
21
16
|
// This is somewhat clumsy to use. Perhaps I can redesign it or use some transform to make it more transparent.
|
|
22
17
|
describe("Should deserialize JSON.Box<T>", () => {
|
|
23
|
-
expect((JSON.parse<JSON.Box<i32> | null>("null") == null).toString())
|
|
24
|
-
.toBe("true");
|
|
18
|
+
expect((JSON.parse<JSON.Box<i32> | null>("null") == null).toString()).toBe("true");
|
|
25
19
|
|
|
26
|
-
expect(JSON.parse<JSON.Box<i32> | null>("0")!.value.toString())
|
|
27
|
-
.toBe("0");
|
|
20
|
+
expect(JSON.parse<JSON.Box<i32> | null>("0")!.value.toString()).toBe("0");
|
|
28
21
|
|
|
29
|
-
expect(JSON.parse<JSON.Box<i32> | null>("1")!.value.toString())
|
|
30
|
-
.toBe("1");
|
|
22
|
+
expect(JSON.parse<JSON.Box<i32> | null>("1")!.value.toString()).toBe("1");
|
|
31
23
|
|
|
32
|
-
expect(JSON.parse<JSON.Box<boolean> | null>("false")!.value.toString())
|
|
33
|
-
.toBe("false");
|
|
24
|
+
expect(JSON.parse<JSON.Box<boolean> | null>("false")!.value.toString()).toBe("false");
|
|
34
25
|
|
|
35
|
-
expect(JSON.parse<JSON.Box<boolean> | null>("true")!.value.toString())
|
|
36
|
-
.toBe("true");
|
|
26
|
+
expect(JSON.parse<JSON.Box<boolean> | null>("true")!.value.toString()).toBe("true");
|
|
37
27
|
});
|
|
@@ -2,6 +2,7 @@ import { JSON } from "..";
|
|
|
2
2
|
import { describe, expect } from "./lib";
|
|
3
3
|
import { bytes } from "../util";
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
@json
|
|
6
7
|
class Point {
|
|
7
8
|
x: f64 = 0.0;
|
|
@@ -10,10 +11,14 @@ class Point {
|
|
|
10
11
|
this.x = x;
|
|
11
12
|
this.y = y;
|
|
12
13
|
}
|
|
14
|
+
|
|
15
|
+
|
|
13
16
|
@serializer
|
|
14
17
|
serializer(self: Point): string {
|
|
15
18
|
return `(${self.x},${self.y})`;
|
|
16
19
|
}
|
|
20
|
+
|
|
21
|
+
|
|
17
22
|
@deserializer
|
|
18
23
|
deserializer(data: string): Point {
|
|
19
24
|
const dataSize = bytes(data);
|
|
@@ -23,16 +28,12 @@ class Point {
|
|
|
23
28
|
const x = data.slice(1, c);
|
|
24
29
|
const y = data.slice(c + 1, data.length - 1);
|
|
25
30
|
|
|
26
|
-
return new Point(
|
|
27
|
-
f64.parse(x),
|
|
28
|
-
f64.parse(y)
|
|
29
|
-
);
|
|
31
|
+
return new Point(f64.parse(x), f64.parse(y));
|
|
30
32
|
}
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
|
|
34
35
|
describe("Should serialize using custom serializers", () => {
|
|
35
|
-
expect(JSON.stringify<Point>(new Point(1,2))).toBe("(1.0,2.0)");
|
|
36
|
+
expect(JSON.stringify<Point>(new Point(1, 2))).toBe("(1.0,2.0)");
|
|
36
37
|
});
|
|
37
38
|
|
|
38
39
|
describe("Should deserialize using custom deserializers", () => {
|
|
@@ -2,10 +2,8 @@ import { JSON } from "..";
|
|
|
2
2
|
import { describe, expect } from "./lib";
|
|
3
3
|
|
|
4
4
|
describe("Should serialize Date", () => {
|
|
5
|
-
expect(JSON.stringify<Date>(new Date(0)))
|
|
6
|
-
|
|
7
|
-
expect(JSON.stringify<Date>(new Date(1738618120525)))
|
|
8
|
-
.toBe('"2025-02-03T21:28:40.525Z"');
|
|
5
|
+
expect(JSON.stringify<Date>(new Date(0))).toBe('"1970-01-01T00:00:00.000Z"');
|
|
6
|
+
expect(JSON.stringify<Date>(new Date(1738618120525))).toBe('"2025-02-03T21:28:40.525Z"');
|
|
9
7
|
});
|
|
10
8
|
|
|
11
9
|
describe("Should deserialize booleans", () => {
|
|
@@ -17,7 +15,7 @@ describe("Should deserialize booleans", () => {
|
|
|
17
15
|
// console.log("Minutes: " + date.getUTCMinutes().toString());
|
|
18
16
|
// console.log("Seconds: " + date.getUTCSeconds().toString());
|
|
19
17
|
// console.log("Milliseconds: " + date.getUTCMilliseconds().toString());
|
|
20
|
-
|
|
18
|
+
|
|
21
19
|
const date1 = JSON.parse<Date>('"1970-01-01T00:00:00.000Z"');
|
|
22
20
|
expect(date1.getUTCFullYear().toString()).toBe("1970");
|
|
23
21
|
expect(date1.getUTCMonth().toString()).toBe("0");
|
|
@@ -34,5 +32,5 @@ describe("Should deserialize booleans", () => {
|
|
|
34
32
|
expect(date2.getUTCHours().toString()).toBe("21");
|
|
35
33
|
expect(date2.getUTCMinutes().toString()).toBe("28");
|
|
36
34
|
expect(date2.getUTCSeconds().toString()).toBe("40");
|
|
37
|
-
expect(date2.getUTCMilliseconds().toString()).toBe("525");
|
|
35
|
+
expect(date2.getUTCMilliseconds().toString()).toBe("525");
|
|
38
36
|
});
|
|
@@ -34,9 +34,9 @@ describe("Should deserialize floats", () => {
|
|
|
34
34
|
|
|
35
35
|
expect(JSON.parse<f64>("0.000001").toString()).toBe("0.000001");
|
|
36
36
|
|
|
37
|
-
expect(JSON.parse<f64>("1e-7").toString()).toBe(1e-7.toString());
|
|
37
|
+
expect(JSON.parse<f64>("1e-7").toString()).toBe((1e-7).toString());
|
|
38
38
|
|
|
39
|
-
expect(JSON.parse<f64>("100000000000000000000.0").toString()).toBe(1e20.toString());
|
|
39
|
+
expect(JSON.parse<f64>("100000000000000000000.0").toString()).toBe((1e20).toString());
|
|
40
40
|
|
|
41
|
-
expect(JSON.parse<f64>("1e+21").toString()).toBe(1e21.toString());
|
|
41
|
+
expect(JSON.parse<f64>("1e+21").toString()).toBe((1e21).toString());
|
|
42
42
|
});
|
|
@@ -2,6 +2,10 @@ export function describe(description: string, routine: () => void): void {
|
|
|
2
2
|
routine();
|
|
3
3
|
}
|
|
4
4
|
|
|
5
|
+
export function it(description: string, routine: () => void): void {
|
|
6
|
+
routine();
|
|
7
|
+
}
|
|
8
|
+
|
|
5
9
|
export function expect(left: string): Expectation {
|
|
6
10
|
return new Expectation(left);
|
|
7
11
|
}
|
|
@@ -4,4 +4,4 @@ import { describe, expect } from "./lib";
|
|
|
4
4
|
describe("Should deserialize complex objects", () => {
|
|
5
5
|
const input = '{"a":{"b":{"c":[{"d":"random value 1"},{"e":["value 2","value 3"]}],"f":{"g":{"h":[1,2,3],"i":{"j":"nested value"}}}},"k":"simple value"},"l":[{"m":"another value","n":{"o":"deep nested","p":[{"q":"even deeper"},"final value"]}}],"r":null}';
|
|
6
6
|
expect(JSON.stringify(JSON.parse<Map<string, JSON.Raw>>(input))).toBe(input);
|
|
7
|
-
})
|
|
7
|
+
});
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import { JSON } from "..";
|
|
2
|
+
import { bytes } from "../util";
|
|
3
|
+
import { describe, expect } from "./lib";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
@json
|
|
7
|
+
class Obj {
|
|
8
|
+
public a: string = "hello";
|
|
9
|
+
public b: string = "world";
|
|
10
|
+
public c: string = '"\t\f\u0000\u0001';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@json
|
|
15
|
+
class Vec3 {
|
|
16
|
+
x: f32 = 0.0;
|
|
17
|
+
y: f32 = 0.0;
|
|
18
|
+
z: f32 = 0.0;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@json
|
|
23
|
+
class Player {
|
|
24
|
+
|
|
25
|
+
@alias("first name")
|
|
26
|
+
firstName!: string;
|
|
27
|
+
lastName!: string;
|
|
28
|
+
lastActive!: i32[];
|
|
29
|
+
// Drop in a code block, function, or expression that evaluates to a boolean
|
|
30
|
+
@omitif((self: Player) => self.age < 18)
|
|
31
|
+
age!: i32;
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@omitnull()
|
|
35
|
+
pos!: Vec3 | null;
|
|
36
|
+
isVerified!: boolean;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@json
|
|
41
|
+
class InnerObj<T> {
|
|
42
|
+
obj: T = instantiate<T>();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@json
|
|
47
|
+
class ObjWithBracketString {
|
|
48
|
+
data: string = "";
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
describe("Should serialize special characters", () => {
|
|
52
|
+
expect(JSON.stringify("\u0000\u0001\u0002\u0003\u0004\u0005\u0006\u0007\u0008\u0009\u000a\u000b\u000c\u000d\u000e\u000f\u000f\u0011\u0012\u0013\u0014\u0015\u0016\u0017\u0018\u0019\u001a\u001b\u001c\u001d\u001e\u001f")).toBe('"\\u0000\\u0001\\u0002\\u0003\\u0004\\u0005\\u0006\\u0007\\b\\t\\n\\u000b\\f\\r\\u000e\\u000f\\u000f\\u0011\\u0012\\u0013\\u0014\\u0015\\u0016\\u0017\\u0018\\u0019\\u001a\\u001b\\u001c\\u001d\\u001e\\u001f"');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe("Should serialize an object", () => {
|
|
56
|
+
const obj = new Obj();
|
|
57
|
+
expect(JSON.stringify(obj)).toBe('{"a":"hello","b":"world","c":"\\"\\t\\f\\u0000\\u0001"}');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("Should serialize a player instance", () => {
|
|
61
|
+
const player: Player = {
|
|
62
|
+
firstName: "Jairus",
|
|
63
|
+
lastName: "Tanaka",
|
|
64
|
+
lastActive: [2, 7, 2025],
|
|
65
|
+
age: 18,
|
|
66
|
+
pos: { x: 3.4, y: 1.2, z: 8.3 },
|
|
67
|
+
isVerified: true,
|
|
68
|
+
};
|
|
69
|
+
expect(JSON.stringify(player)).toBe('{"age":18,"pos":{"x":3.4,"y":1.2,"z":8.3},"first name":"Jairus","lastName":"Tanaka","lastActive":[2,7,2025],"isVerified":true}');
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
describe("Should parse JSON objects", () => {
|
|
73
|
+
expect(JSON.stringify(JSON.parse<JSON.Obj>('{"foo":"bar"}'))).toBe('{"foo":"bar"}');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe("Should parse nested JSON objects", () => {
|
|
77
|
+
expect(JSON.stringify(JSON.parse<JSON.Obj>('{"x":1.5,"y":5.4,"z":9.8,"obj":{"foo":"bar"}}'))).toBe('{"x":1.5,"y":5.4,"z":9.8,"obj":{"foo":"bar"}}');
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
describe("Should parse an array with mixed types", () => {
|
|
81
|
+
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.0,true]]'))).toBe('["string",true,3.14,{"x":1.0,"y":2.0,"z":3.0},[1.0,2.0,3.0,true]]');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
describe("Should parse and serialize a Vec3 instance", () => {
|
|
85
|
+
expect(JSON.stringify(JSON.parse<Vec3>(' { "x" : 3.4 , "y" : 1.2 , "z" : 8.3 } '))).toBe('{"x":3.4,"y":1.2,"z":8.3}');
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
describe("Should parse an InnerObj containing an ObjWithBracketString", () => {
|
|
89
|
+
expect(JSON.stringify(JSON.parse<InnerObj<ObjWithBracketString>>('{"obj":{"data":"hello} world"}}'))).toBe('{"obj":{"data":"hello} world"}}');
|
|
90
|
+
});
|
|
@@ -11,8 +11,8 @@ describe("Should deserialize JSON.Raw", () => {
|
|
|
11
11
|
|
|
12
12
|
describe("Should serialize Map<string, JSON.Raw>", () => {
|
|
13
13
|
const m1 = new Map<string, JSON.Raw>();
|
|
14
|
-
m1.set("hello", new JSON.Raw("
|
|
15
|
-
m1.set("pos", new JSON.Raw(
|
|
14
|
+
m1.set("hello", new JSON.Raw('"world"'));
|
|
15
|
+
m1.set("pos", new JSON.Raw('{"x":1.0,"y":2.0,"z":3.0}'));
|
|
16
16
|
|
|
17
17
|
expect(JSON.stringify(m1)).toBe('{"hello":"world","pos":{"x":1.0,"y":2.0,"z":3.0}}');
|
|
18
18
|
});
|
|
@@ -20,4 +20,4 @@ describe("Should serialize Map<string, JSON.Raw>", () => {
|
|
|
20
20
|
describe("Should deserialize Map<string, JSON.Raw>", () => {
|
|
21
21
|
const m1 = JSON.parse<Map<string, JSON.Raw>>('{"hello":"world","pos":{"x":1.0,"y":2.0,"z":3.0}}');
|
|
22
22
|
expect(JSON.stringify(m1)).toBe('{"hello":"world","pos":{"x":1.0,"y":2.0,"z":3.0}}');
|
|
23
|
-
});
|
|
23
|
+
});
|
|
@@ -53,15 +53,11 @@ describe("Should ignore properties decorated with @omit", () => {
|
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
describe("Should deserialize structs", () => {
|
|
56
|
-
expect(
|
|
57
|
-
JSON.stringify(JSON.parse<Vec3>('{"x":3.4,"y":1.2,"z":8.3}')),
|
|
58
|
-
).toBe('{"x":3.4,"y":1.2,"z":8.3}');
|
|
56
|
+
expect(JSON.stringify(JSON.parse<Vec3>('{"x":3.4,"y":1.2,"z":8.3}'))).toBe('{"x":3.4,"y":1.2,"z":8.3}');
|
|
59
57
|
});
|
|
60
58
|
|
|
61
59
|
describe("Should deserialize structs with whitespace", () => {
|
|
62
|
-
expect(
|
|
63
|
-
JSON.stringify(JSON.parse<Vec3>(' { "x" : 3.4 , "y" : 1.2 , "z" : 8.3 } ')),
|
|
64
|
-
).toBe('{"x":3.4,"y":1.2,"z":8.3}');
|
|
60
|
+
expect(JSON.stringify(JSON.parse<Vec3>(' { "x" : 3.4 , "y" : 1.2 , "z" : 8.3 } '))).toBe('{"x":3.4,"y":1.2,"z":8.3}');
|
|
65
61
|
});
|
|
66
62
|
|
|
67
63
|
// describe("Should serialize Suite struct", () => {
|
|
@@ -114,6 +110,7 @@ class ObjWithString {
|
|
|
114
110
|
|
|
115
111
|
@json
|
|
116
112
|
class ObjWithStrangeKey<T> {
|
|
113
|
+
|
|
117
114
|
@alias('a\\\t"\x02b`c')
|
|
118
115
|
data!: T;
|
|
119
116
|
}
|
package/assembly/custom/bench.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
1
|
export function bench(description: string, routine: () => void, ops: u64 = 1_000_000): void {
|
|
2
2
|
console.log(" - Benchmarking " + description);
|
|
3
|
+
let warmup = ops >> 1;
|
|
4
|
+
|
|
5
|
+
while (warmup--) {
|
|
6
|
+
routine();
|
|
7
|
+
}
|
|
8
|
+
|
|
3
9
|
const start = Date.now();
|
|
4
10
|
let count = ops;
|
|
5
|
-
|
|
11
|
+
|
|
12
|
+
while (count--) {
|
|
6
13
|
routine();
|
|
7
|
-
count--;
|
|
8
14
|
}
|
|
15
|
+
|
|
9
16
|
const elapsed = Date.now() - start;
|
|
10
17
|
|
|
11
18
|
let opsPerSecond = (ops * 1000) / elapsed;
|
|
@@ -10,8 +10,7 @@ export function deserializeStructArray<T extends unknown[]>(srcStart: usize, src
|
|
|
10
10
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
11
11
|
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
12
12
|
|
|
13
|
-
if (srcStart - srcEnd == 0)
|
|
14
|
-
throw new Error("Input string had zero length or was all whitespace");
|
|
13
|
+
if (srcStart - srcEnd == 0) throw new Error("Input string had zero length or was all whitespace");
|
|
15
14
|
|
|
16
15
|
if (load<u16>(srcStart) != BRACKET_LEFT) throw new Error("Expected '[' at start of object at position " + (srcEnd - srcStart).toString());
|
|
17
16
|
if (load<u16>(srcEnd - 2) != BRACKET_RIGHT) throw new Error("Expected ']' at end of object at position " + (srcEnd - srcStart).toString());
|
|
@@ -21,7 +20,7 @@ export function deserializeStructArray<T extends unknown[]>(srcStart: usize, src
|
|
|
21
20
|
if (code == BRACE_LEFT && depth++ == 0) {
|
|
22
21
|
lastIndex = srcStart;
|
|
23
22
|
} else if (code == BRACE_RIGHT && --depth == 0) {
|
|
24
|
-
out.push(JSON.__deserialize<valueof<T>>(lastIndex, srcStart += 2));
|
|
23
|
+
out.push(JSON.__deserialize<valueof<T>>(lastIndex, (srcStart += 2)));
|
|
25
24
|
}
|
|
26
25
|
srcStart += 2;
|
|
27
26
|
}
|
|
@@ -3,4 +3,4 @@ export function deserializeBoolean(srcStart: usize, srcEnd: usize): boolean {
|
|
|
3
3
|
if (block == 28429475166421108) return true;
|
|
4
4
|
else if (block == 32370086184550502 && load<u16>(srcStart, 8) == 101) return false;
|
|
5
5
|
return false; //throw new Error(`Expected to find boolean, but found "${data.slice(0, 100)}" instead!`);
|
|
6
|
-
}
|
|
6
|
+
}
|
|
@@ -17,8 +17,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
|
|
|
17
17
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
18
18
|
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2; // would like to optimize this later
|
|
19
19
|
|
|
20
|
-
if (srcStart - srcEnd == 0)
|
|
21
|
-
throw new Error("Input string had zero length or was all whitespace");
|
|
20
|
+
if (srcStart - srcEnd == 0) throw new Error("Input string had zero length or was all whitespace");
|
|
22
21
|
if (load<u16>(srcStart) != BRACE_LEFT) throw new Error("Expected '{' at start of object at position " + (srcEnd - srcStart).toString());
|
|
23
22
|
if (load<u16>(srcEnd - 2) != BRACE_RIGHT) throw new Error("Expected '}' at end of object at position " + (srcEnd - srcStart).toString());
|
|
24
23
|
|
|
@@ -32,7 +31,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
|
|
|
32
31
|
keyEnd = srcStart;
|
|
33
32
|
// console.log("Key: " + ptrToStr(lastIndex, srcStart));
|
|
34
33
|
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart + 2)));
|
|
35
|
-
while (isSpace((code = load<u16>((srcStart += 2))))) {
|
|
34
|
+
while (isSpace((code = load<u16>((srcStart += 2))))) {}
|
|
36
35
|
if (code !== COLON) throw new Error("Expected ':' after key at position " + (srcEnd - srcStart).toString());
|
|
37
36
|
isKey = false;
|
|
38
37
|
} else {
|
|
@@ -169,4 +168,4 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
|
|
|
169
168
|
}
|
|
170
169
|
}
|
|
171
170
|
return out;
|
|
172
|
-
}
|
|
171
|
+
}
|
|
@@ -16,8 +16,7 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
|
|
|
16
16
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
17
17
|
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2; // would like to optimize this later
|
|
18
18
|
|
|
19
|
-
if (srcStart - srcEnd == 0)
|
|
20
|
-
throw new Error("Input string had zero length or was all whitespace");
|
|
19
|
+
if (srcStart - srcEnd == 0) throw new Error("Input string had zero length or was all whitespace");
|
|
21
20
|
if (load<u16>(srcStart) != BRACE_LEFT) throw new Error("Expected '{' at start of object at position " + (srcEnd - srcStart).toString());
|
|
22
21
|
if (load<u16>(srcEnd - 2) != BRACE_RIGHT) throw new Error("Expected '}' at end of object at position " + (srcEnd - srcStart).toString());
|
|
23
22
|
|
|
@@ -31,7 +30,7 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
|
|
|
31
30
|
keyEnd = srcStart;
|
|
32
31
|
// console.log("Key: " + ptrToStr(lastIndex, srcStart));
|
|
33
32
|
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart + 2)));
|
|
34
|
-
while (isSpace((code = load<u16>((srcStart += 2))))) {
|
|
33
|
+
while (isSpace((code = load<u16>((srcStart += 2))))) {}
|
|
35
34
|
if (code !== COLON) throw new Error("Expected ':' after key at position " + (srcEnd - srcStart).toString());
|
|
36
35
|
isKey = false;
|
|
37
36
|
} else {
|
|
@@ -1,5 +1,6 @@
|
|
|
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";
|
|
3
4
|
|
|
4
5
|
export function deserializeStruct<T>(srcStart: usize, srcEnd: usize, dst: usize): T {
|
|
5
6
|
const out = changetype<nonnull<T>>(dst || __new(offsetof<T>(), idof<T>()));
|
|
@@ -16,8 +17,8 @@ export function deserializeStruct<T>(srcStart: usize, srcEnd: usize, dst: usize)
|
|
|
16
17
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
17
18
|
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2; // would like to optimize this later
|
|
18
19
|
|
|
19
|
-
if (srcStart - srcEnd == 0)
|
|
20
|
-
|
|
20
|
+
if (srcStart - srcEnd == 0) throw new Error("Input string had zero length or was all whitespace");
|
|
21
|
+
|
|
21
22
|
if (load<u16>(srcStart) != BRACE_LEFT) throw new Error("Expected '{' at start of object at position " + (srcEnd - srcStart).toString());
|
|
22
23
|
if (load<u16>(srcEnd - 2) != BRACE_RIGHT) throw new Error("Expected '}' at end of object at position " + (srcEnd - srcStart).toString());
|
|
23
24
|
|
|
@@ -31,7 +32,7 @@ export function deserializeStruct<T>(srcStart: usize, srcEnd: usize, dst: usize)
|
|
|
31
32
|
keyEnd = srcStart;
|
|
32
33
|
// console.log("Key: " + ptrToStr(lastIndex, srcStart));
|
|
33
34
|
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart + 2)));
|
|
34
|
-
while (isSpace((code = load<u16>((srcStart += 2))))) {
|
|
35
|
+
while (isSpace((code = load<u16>((srcStart += 2))))) {}
|
|
35
36
|
if (code !== COLON) throw new Error("Expected ':' after key at position " + (srcEnd - srcStart).toString());
|
|
36
37
|
isKey = false;
|
|
37
38
|
} else {
|
|
@@ -109,7 +110,10 @@ export function deserializeStruct<T>(srcStart: usize, srcEnd: usize, dst: usize)
|
|
|
109
110
|
srcStart += 2;
|
|
110
111
|
while (srcStart < srcEnd) {
|
|
111
112
|
const code = load<u16>(srcStart);
|
|
112
|
-
if (code ==
|
|
113
|
+
if (code == QUOTE) {
|
|
114
|
+
srcStart += 2;
|
|
115
|
+
while (!(load<u16>(srcStart) == QUOTE && load<u16>(srcStart - 2) != BACK_SLASH)) srcStart += 2;
|
|
116
|
+
} else if (code == BRACKET_RIGHT) {
|
|
113
117
|
if (--depth == 0) {
|
|
114
118
|
// console.log("Value (array): " + ptrToStr(lastIndex, srcStart + 2));
|
|
115
119
|
// @ts-ignore: exists
|
|
@@ -150,7 +154,7 @@ export function deserializeStruct<T>(srcStart: usize, srcEnd: usize, dst: usize)
|
|
|
150
154
|
}
|
|
151
155
|
} else if (code == CHAR_N) {
|
|
152
156
|
if (load<u64>(srcStart) == 30399761348886638) {
|
|
153
|
-
// console.log("Value (null): " + ptrToStr(srcStart, srcStart + 8));
|
|
157
|
+
// console.log("Value (null): " + ptrToStr(srcStart, srcStart + 8).replaceAll(" ", "-"));
|
|
154
158
|
// @ts-ignore: exists
|
|
155
159
|
out.__DESERIALIZE(keyStart, keyEnd, srcStart, (srcStart += 8), changetype<usize>(out));
|
|
156
160
|
// while (isSpace(load<u16>((srcStart += 2)))) {
|
package/assembly/index.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
/// <reference path="./index.d.ts" />
|
|
2
|
+
import { bs } from "../modules/as-bs";
|
|
2
3
|
|
|
3
4
|
import { serializeString } from "./serialize/simple/string";
|
|
4
5
|
import { serializeArray } from "./serialize/simple/array";
|
|
@@ -422,7 +423,7 @@ export namespace JSON {
|
|
|
422
423
|
// @ts-ignore: type
|
|
423
424
|
private storage: Map<string, JSON.Value> = new Map<string, JSON.Value>();
|
|
424
425
|
|
|
425
|
-
constructor() {
|
|
426
|
+
constructor() {}
|
|
426
427
|
|
|
427
428
|
// @ts-ignore: decorator
|
|
428
429
|
@inline get size(): i32 {
|
|
@@ -472,7 +473,6 @@ export namespace JSON {
|
|
|
472
473
|
const out = changetype<JSON.Obj>(__new(offsetof<JSON.Obj>(), idof<JSON.Obj>()));
|
|
473
474
|
|
|
474
475
|
if (value instanceof Map) {
|
|
475
|
-
|
|
476
476
|
}
|
|
477
477
|
return out;
|
|
478
478
|
}
|
|
@@ -574,6 +574,9 @@ export namespace JSON {
|
|
|
574
574
|
return atoi<T>(srcStart, srcEnd);
|
|
575
575
|
} else if (isFloat<T>()) {
|
|
576
576
|
return deserializeFloat<T>(srcStart, srcEnd);
|
|
577
|
+
} else if (isNullable<T>() && srcEnd - srcStart == 8 && load<u64>(srcStart) == 30399761348886638) {
|
|
578
|
+
// @ts-ignore
|
|
579
|
+
return null;
|
|
577
580
|
} else if (isString<T>()) {
|
|
578
581
|
// @ts-ignore: type
|
|
579
582
|
return deserializeString(srcStart, srcEnd, dst);
|
|
@@ -622,7 +625,13 @@ function deserializeBox<T>(srcStart: usize, srcEnd: usize, dst: usize, ty: T): T
|
|
|
622
625
|
return JSON.__deserialize<T>(srcStart, srcEnd, dst);
|
|
623
626
|
}
|
|
624
627
|
|
|
625
|
-
export function toRaw(data: string): JSON.Raw {
|
|
626
|
-
|
|
628
|
+
export function toRaw(data: string): JSON.Raw {
|
|
629
|
+
return new JSON.Raw(data);
|
|
630
|
+
}
|
|
631
|
+
export function fromRaw(data: JSON.Raw): string {
|
|
632
|
+
return data.data;
|
|
633
|
+
}
|
|
627
634
|
|
|
628
|
-
export function toBox<T>(data: T): JSON.Box<T> {
|
|
635
|
+
export function toBox<T>(data: T): JSON.Box<T> {
|
|
636
|
+
return new JSON.Box<T>(data);
|
|
637
|
+
}
|