json-as 1.0.6 → 1.0.8
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 +16 -0
- package/README.md +4 -24
- package/asconfig.json +1 -2
- package/assembly/__benches__/abc.bench.ts +5 -5
- package/assembly/__benches__/large.bench.ts +5 -51
- package/assembly/{custom → __benches__/lib}/bench.ts +4 -0
- package/assembly/__benches__/medium.bench.ts +3 -3
- package/assembly/__benches__/small.bench.ts +1 -1
- package/assembly/__benches__/vec3.bench.ts +3 -3
- package/assembly/__tests__/custom.spec.ts +12 -0
- package/assembly/custom/util.ts +0 -4
- package/assembly/deserialize/simple/array/box.ts +45 -0
- package/assembly/deserialize/simple/array/object.ts +28 -0
- package/assembly/deserialize/simple/array.ts +9 -1
- package/assembly/index.ts +24 -19
- package/assembly/serialize/simd/string.ts +12 -12
- package/assembly/test.ts +179 -107
- package/bench/abc.bench.ts +1 -1
- package/bench/large.bench.ts +3 -3
- package/bench/lib/bench.ts +13 -1
- package/bench/medium.bench.ts +1 -1
- package/bench/runners/assemblyscript.js +30 -0
- package/bench/small.bench.ts +1 -1
- package/bench/tsconfig.json +12 -0
- package/bench/vec3.bench.ts +1 -1
- package/package.json +3 -4
- package/run-bench.as.sh +44 -18
- package/run-bench.js.sh +32 -6
- package/run-tests.sh +1 -1
- package/transform/lib/index.js +2 -4
- package/transform/lib/index.js.map +1 -1
- package/transform/src/index.ts +1 -1
- package/assembly/__benches__/lib/index.ts +0 -26
- package/assembly/custom/memory.ts +0 -25
- package/assembly/custom/sink.ts +0 -231
- package/assembly/custom/types.ts +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 2025-05-21 - 1.0.8
|
|
4
|
+
|
|
5
|
+
- fix: inline warnings on layer-2 serialize and deserialize functions
|
|
6
|
+
- feat: fully support `JSON.Obj` and `JSON.Box` everywhere
|
|
7
|
+
- fix: temp disable SIMD
|
|
8
|
+
- feat: write fair benchmarks with `v8` using `jsvu`
|
|
9
|
+
|
|
10
|
+
## 2025-05-14 - 1.0.7
|
|
11
|
+
|
|
12
|
+
- merge: pull request [#128](https://github.com/JairusSW/json-as/pull/128) from [loredanacirstea/nested-custom-serializer-fix](https://github.com/loredanacirstea/nested-custom-serializer-fix)
|
|
13
|
+
|
|
14
|
+
## 2025-05-12 - 1.0.6
|
|
15
|
+
|
|
16
|
+
- fix: support zero-param serialization and make sure types are consistent
|
|
17
|
+
- fix: [#124](https://github.com/JairusSW/json-as/issues/124)
|
|
18
|
+
|
|
3
19
|
## 2025-05-11 - 1.0.5
|
|
4
20
|
|
|
5
21
|
- feat: add sanity checks for badly formatted strings
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
7
7
|
█████ ███████ ██████ ██ ████ ██ ██ ███████
|
|
8
8
|
</span>
|
|
9
|
-
AssemblyScript - v1.0.
|
|
9
|
+
AssemblyScript - v1.0.8
|
|
10
10
|
</pre>
|
|
11
11
|
</h6>
|
|
12
12
|
|
|
@@ -57,7 +57,6 @@ If you'd like to see the code that the transform generates, run the build step w
|
|
|
57
57
|
```typescript
|
|
58
58
|
import { JSON } from "json-as";
|
|
59
59
|
|
|
60
|
-
|
|
61
60
|
@json
|
|
62
61
|
class Vec3 {
|
|
63
62
|
x: f32 = 0.0;
|
|
@@ -65,10 +64,8 @@ class Vec3 {
|
|
|
65
64
|
z: f32 = 0.0;
|
|
66
65
|
}
|
|
67
66
|
|
|
68
|
-
|
|
69
67
|
@json
|
|
70
68
|
class Player {
|
|
71
|
-
|
|
72
69
|
@alias("first name")
|
|
73
70
|
firstName!: string;
|
|
74
71
|
lastName!: string;
|
|
@@ -76,8 +73,6 @@ class Player {
|
|
|
76
73
|
// Drop in a code block, function, or expression that evaluates to a boolean
|
|
77
74
|
@omitif((self: Player) => self.age < 18)
|
|
78
75
|
age!: i32;
|
|
79
|
-
|
|
80
|
-
|
|
81
76
|
@omitnull()
|
|
82
77
|
pos!: Vec3 | null;
|
|
83
78
|
isVerified!: boolean;
|
|
@@ -114,12 +109,9 @@ This library allows selective omission of fields during serialization using the
|
|
|
114
109
|
This decorator excludes a field from serialization entirely.
|
|
115
110
|
|
|
116
111
|
```typescript
|
|
117
|
-
|
|
118
112
|
@json
|
|
119
113
|
class Example {
|
|
120
114
|
name!: string;
|
|
121
|
-
|
|
122
|
-
|
|
123
115
|
@omit
|
|
124
116
|
SSN!: string;
|
|
125
117
|
}
|
|
@@ -136,12 +128,9 @@ console.log(JSON.stringify(obj)); // { "name": "Jairus" }
|
|
|
136
128
|
This decorator omits a field only if its value is null.
|
|
137
129
|
|
|
138
130
|
```typescript
|
|
139
|
-
|
|
140
131
|
@json
|
|
141
132
|
class Example {
|
|
142
133
|
name!: string;
|
|
143
|
-
|
|
144
|
-
|
|
145
134
|
@omitnull()
|
|
146
135
|
optionalField!: string | null;
|
|
147
136
|
}
|
|
@@ -158,12 +147,9 @@ console.log(JSON.stringify(obj)); // { "name": "Jairus" }
|
|
|
158
147
|
This decorator omits a field based on a custom predicate function.
|
|
159
148
|
|
|
160
149
|
```typescript
|
|
161
|
-
|
|
162
150
|
@json
|
|
163
151
|
class Example {
|
|
164
152
|
name!: string;
|
|
165
|
-
|
|
166
|
-
|
|
167
153
|
@omitif((self: Example) => self.age <= 18)
|
|
168
154
|
age!: number;
|
|
169
155
|
}
|
|
@@ -188,7 +174,6 @@ AssemblyScript doesn't support using nullable primitive types, so instead, json-
|
|
|
188
174
|
For example, this schema won't compile in AssemblyScript:
|
|
189
175
|
|
|
190
176
|
```typescript
|
|
191
|
-
|
|
192
177
|
@json
|
|
193
178
|
class Person {
|
|
194
179
|
name!: string;
|
|
@@ -199,7 +184,6 @@ class Person {
|
|
|
199
184
|
Instead, use `JSON.Box` to allow nullable primitives:
|
|
200
185
|
|
|
201
186
|
```typescript
|
|
202
|
-
|
|
203
187
|
@json
|
|
204
188
|
class Person {
|
|
205
189
|
name: string;
|
|
@@ -262,7 +246,6 @@ More often, objects will be completely statically typed except for one or two va
|
|
|
262
246
|
In such cases, `JSON.Value` can be used to handle fields that may hold different types at runtime.
|
|
263
247
|
|
|
264
248
|
```typescript
|
|
265
|
-
|
|
266
249
|
@json
|
|
267
250
|
class DynamicObj {
|
|
268
251
|
id: i32 = 0;
|
|
@@ -319,7 +302,6 @@ Here's an example of creating a custom data type called `Point` which serializes
|
|
|
319
302
|
```typescript
|
|
320
303
|
import { bytes } from "json-as/assembly/util";
|
|
321
304
|
|
|
322
|
-
|
|
323
305
|
@json
|
|
324
306
|
class Point {
|
|
325
307
|
x: f64 = 0.0;
|
|
@@ -329,13 +311,11 @@ class Point {
|
|
|
329
311
|
this.y = y;
|
|
330
312
|
}
|
|
331
313
|
|
|
332
|
-
|
|
333
314
|
@serializer
|
|
334
315
|
serializer(self: Point): string {
|
|
335
316
|
return `(${self.x},${self.y})`;
|
|
336
317
|
}
|
|
337
318
|
|
|
338
|
-
|
|
339
319
|
@deserializer
|
|
340
320
|
deserializer(data: string): Point {
|
|
341
321
|
const dataSize = bytes(data);
|
|
@@ -365,8 +345,8 @@ The deserializer function parses the string `(x,y)` back into a `Point` instance
|
|
|
365
345
|
These functions are then wrapped before being consumed by the json-as library:
|
|
366
346
|
|
|
367
347
|
```typescript
|
|
368
|
-
@inline __SERIALIZE_CUSTOM(
|
|
369
|
-
const data = this.serializer(
|
|
348
|
+
@inline __SERIALIZE_CUSTOM(): void {
|
|
349
|
+
const data = this.serializer(this);
|
|
370
350
|
const dataSize = data.length << 1;
|
|
371
351
|
memory.copy(bs.offset, changetype<usize>(data), dataSize);
|
|
372
352
|
bs.offset += dataSize;
|
|
@@ -391,7 +371,7 @@ These benchmarks compare this library to JavaScript's native `JSON.stringify` an
|
|
|
391
371
|
|
|
392
372
|
| Test Case | Size | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
393
373
|
| --------------- | ---------- | --------------------- | ----------------------- | -------------------- | ---------------------- |
|
|
394
|
-
| Vector3 Object | 38 bytes |
|
|
374
|
+
| Vector3 Object | 38 bytes | 26,611,226 ops/s | 32,160,804 ops/s | 1,357 MB/s | 1,348 MB/s |
|
|
395
375
|
| Alphabet String | 104 bytes | 13,617,021 ops/s | 18,390,804 ops/s | 1,416 MB/s | 1,986 MB/s |
|
|
396
376
|
| Small Object | 88 bytes | 24,242,424 ops/s | 12,307,692 ops/s | 2,133 MB/s | 1,083 MB/s |
|
|
397
377
|
| Medium Object | 494 bytes | 4,060,913 ops/s | 1,396,160 ops/s | 2,006 MB/s | 689.7 MB/s |
|
package/asconfig.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JSON } from "..";
|
|
2
|
-
import { bench } from "
|
|
2
|
+
import { bench } from "./lib/bench";
|
|
3
3
|
|
|
4
4
|
const v1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
5
5
|
const v2 = '"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"';
|
|
@@ -7,15 +7,15 @@ const v2 = '"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"';
|
|
|
7
7
|
bench(
|
|
8
8
|
"Serialize Alphabet",
|
|
9
9
|
() => {
|
|
10
|
-
JSON.stringify(v1);
|
|
10
|
+
inline.always(JSON.stringify(v1));
|
|
11
11
|
},
|
|
12
|
-
|
|
12
|
+
64_000_00,
|
|
13
13
|
);
|
|
14
14
|
|
|
15
15
|
bench(
|
|
16
16
|
"Deserialize Alphabet",
|
|
17
17
|
() => {
|
|
18
|
-
JSON.parse<string>(v2);
|
|
18
|
+
inline.always(JSON.parse<string>(v2));
|
|
19
19
|
},
|
|
20
|
-
|
|
20
|
+
64_000_00,
|
|
21
21
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JSON } from "..";
|
|
2
|
-
import { bench } from "
|
|
2
|
+
import { bench } from "./lib/bench";
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
@json
|
|
@@ -7,52 +7,6 @@ class Vec3 {
|
|
|
7
7
|
public x!: i32;
|
|
8
8
|
public y!: i32;
|
|
9
9
|
public z!: i32;
|
|
10
|
-
|
|
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
|
-
|
|
31
|
-
@inline __INITIALIZE(): this {
|
|
32
|
-
return this;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@inline __DESERIALIZE(keyStart: usize, keyEnd: usize, valStart: usize, valEnd: usize, ptr: usize): void {
|
|
37
|
-
switch (load<u16>(keyStart)) {
|
|
38
|
-
case 120: {
|
|
39
|
-
// x
|
|
40
|
-
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("x"));
|
|
41
|
-
return;
|
|
42
|
-
}
|
|
43
|
-
case 121: {
|
|
44
|
-
// y
|
|
45
|
-
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("y"));
|
|
46
|
-
return;
|
|
47
|
-
}
|
|
48
|
-
case 122: {
|
|
49
|
-
// z
|
|
50
|
-
store<i32>(ptr, JSON.__deserialize<i32>(valStart, valEnd, 0), offsetof<this>("z"));
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
10
|
}
|
|
57
11
|
|
|
58
12
|
|
|
@@ -161,17 +115,17 @@ const v1: LargeJSON = {
|
|
|
161
115
|
const v2 = `{"id":2,"name":"Medium Object","age":18,"email":"me@jairus.dev","street":"I don't want to say my street","city":"I don't want to say this either","state":"It really depends","zip":"I forget what it is","tags":["me","dogs","mountains","bar","foo"],"theme":"Hyper Term Black","notifications":true,"language":"en-US","movement":[{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3},{"x":1,"y":2,"z":3}]}`;
|
|
162
116
|
|
|
163
117
|
bench(
|
|
164
|
-
"Serialize
|
|
118
|
+
"Serialize Large Object",
|
|
165
119
|
() => {
|
|
166
120
|
JSON.stringify(v1);
|
|
167
121
|
},
|
|
168
|
-
|
|
122
|
+
2_000_00,
|
|
169
123
|
);
|
|
170
124
|
|
|
171
125
|
bench(
|
|
172
|
-
"Deserialize
|
|
126
|
+
"Deserialize Large Object",
|
|
173
127
|
() => {
|
|
174
128
|
JSON.parse<LargeJSON>(v2);
|
|
175
129
|
},
|
|
176
|
-
|
|
130
|
+
2_000_00,
|
|
177
131
|
);
|
|
@@ -1,5 +1,9 @@
|
|
|
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/10;
|
|
4
|
+
while (--warmup) {
|
|
5
|
+
routine();
|
|
6
|
+
}
|
|
3
7
|
const start = Date.now();
|
|
4
8
|
let count = ops;
|
|
5
9
|
while (count != 0) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JSON } from "..";
|
|
2
|
-
import { bench } from "
|
|
2
|
+
import { bench } from "./lib/bench";
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
@json
|
|
@@ -34,7 +34,7 @@ bench(
|
|
|
34
34
|
() => {
|
|
35
35
|
JSON.stringify(v1);
|
|
36
36
|
},
|
|
37
|
-
|
|
37
|
+
6_000_00,
|
|
38
38
|
);
|
|
39
39
|
|
|
40
40
|
bench(
|
|
@@ -42,5 +42,5 @@ bench(
|
|
|
42
42
|
() => {
|
|
43
43
|
JSON.parse<MediumJSON>(v2);
|
|
44
44
|
},
|
|
45
|
-
|
|
45
|
+
6_000_00,
|
|
46
46
|
);
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { JSON } from "..";
|
|
2
|
-
import { bench } from "
|
|
2
|
+
import { bench } from "./lib/bench";
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
@json
|
|
@@ -63,7 +63,7 @@ bench(
|
|
|
63
63
|
() => {
|
|
64
64
|
JSON.stringify(v1);
|
|
65
65
|
},
|
|
66
|
-
|
|
66
|
+
128_000_00,
|
|
67
67
|
);
|
|
68
68
|
|
|
69
69
|
bench(
|
|
@@ -71,5 +71,5 @@ bench(
|
|
|
71
71
|
() => {
|
|
72
72
|
JSON.parse<Vec3>(v2);
|
|
73
73
|
},
|
|
74
|
-
|
|
74
|
+
128_000_00,
|
|
75
75
|
);
|
|
@@ -32,6 +32,14 @@ class Point {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
+
@json
|
|
36
|
+
export class ObjectWithCustom {
|
|
37
|
+
value: Point = new Point(0, 0)
|
|
38
|
+
constructor(value: Point) {
|
|
39
|
+
this.value = value
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
35
43
|
describe("Should serialize using custom serializers", () => {
|
|
36
44
|
expect(JSON.stringify<Point>(new Point(1, 2))).toBe("(1.0,2.0)");
|
|
37
45
|
});
|
|
@@ -41,3 +49,7 @@ describe("Should deserialize using custom deserializers", () => {
|
|
|
41
49
|
expect(p1.x.toString()).toBe("1.0");
|
|
42
50
|
expect(p1.y.toString()).toBe("2.0");
|
|
43
51
|
});
|
|
52
|
+
|
|
53
|
+
describe("Should serialize and deserialize using nested custom serializers", () => {
|
|
54
|
+
expect(JSON.stringify<ObjectWithCustom>(new ObjectWithCustom(new Point(1, 2)))).toBe(`{"value":(1.0,2.0)}`);
|
|
55
|
+
});
|
package/assembly/custom/util.ts
CHANGED
|
@@ -1,7 +1,3 @@
|
|
|
1
|
-
import { isSpace } from "util/string";
|
|
2
|
-
import { BACK_SLASH, QUOTE } from "./chars";
|
|
3
|
-
import { Sink } from "./sink";
|
|
4
|
-
|
|
5
1
|
/** Scientific Notation Integer Parsing - SNIP
|
|
6
2
|
* This is absolutely the fastest algorithm I could think of while adding full support for Scientific Notation
|
|
7
3
|
* Loads 32 bits and retrieves the high/low bits.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { isSpace } from "../../../util";
|
|
2
|
+
import { COMMA, BRACKET_RIGHT } from "../../../custom/chars";
|
|
3
|
+
import { JSON } from "../../..";
|
|
4
|
+
|
|
5
|
+
export function deserializeBoxArray<T extends JSON.Box<any>[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
|
|
6
|
+
const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
|
|
7
|
+
if (isBoolean<valueof<T>>()) {
|
|
8
|
+
srcStart += 2; // skip [
|
|
9
|
+
while (srcStart < srcEnd) {
|
|
10
|
+
const block = load<u64>(srcStart);
|
|
11
|
+
if (block == 28429475166421108) {
|
|
12
|
+
out.push(JSON.Box.from(true));
|
|
13
|
+
srcStart += 10;
|
|
14
|
+
} else if (block == 32370086184550502 && load<u16>(srcStart, 8) == 101) {
|
|
15
|
+
out.push(JSON.Box.from(false));
|
|
16
|
+
srcStart += 12;
|
|
17
|
+
} else {
|
|
18
|
+
srcStart += 2;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
return out;
|
|
22
|
+
} else {
|
|
23
|
+
let lastIndex: usize = 0;
|
|
24
|
+
while (srcStart < srcEnd) {
|
|
25
|
+
const code = load<u16>(srcStart);
|
|
26
|
+
if (code - 48 <= 9 || code == 45) {
|
|
27
|
+
lastIndex = srcStart;
|
|
28
|
+
srcStart += 2;
|
|
29
|
+
while (srcStart < srcEnd) {
|
|
30
|
+
const code = load<u16>(srcStart);
|
|
31
|
+
if (code == COMMA || code == BRACKET_RIGHT || isSpace(code)) {
|
|
32
|
+
out.push(JSON.__deserialize<valueof<T>>(lastIndex, srcStart));
|
|
33
|
+
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
34
|
+
// /* empty */
|
|
35
|
+
// }
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
srcStart += 2;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
srcStart += 2;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return out;
|
|
45
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT } from "../../../custom/chars";
|
|
2
|
+
import { JSON } from "../../..";
|
|
3
|
+
import { isSpace } from "util/string";
|
|
4
|
+
|
|
5
|
+
export function deserializeObjectArray<T extends unknown[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
|
|
6
|
+
const out = changetype<nonnull<T>>(dst || changetype<usize>(instantiate<T>()));
|
|
7
|
+
let lastIndex: usize = 0;
|
|
8
|
+
let depth: u32 = 0;
|
|
9
|
+
|
|
10
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
11
|
+
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
12
|
+
|
|
13
|
+
if (srcStart - srcEnd == 0) throw new Error("Input string had zero length or was all whitespace");
|
|
14
|
+
|
|
15
|
+
if (load<u16>(srcStart) != BRACKET_LEFT) throw new Error("Expected '[' at start of object at position " + (srcEnd - srcStart).toString());
|
|
16
|
+
if (load<u16>(srcEnd - 2) != BRACKET_RIGHT) throw new Error("Expected ']' at end of object at position " + (srcEnd - srcStart).toString());
|
|
17
|
+
|
|
18
|
+
while (srcStart < srcEnd) {
|
|
19
|
+
const code = load<u16>(srcStart);
|
|
20
|
+
if (code == BRACE_LEFT && depth++ == 0) {
|
|
21
|
+
lastIndex = srcStart;
|
|
22
|
+
} else if (code == BRACE_RIGHT && --depth == 0) {
|
|
23
|
+
out.push(JSON.__deserialize<valueof<T>>(lastIndex, (srcStart += 2)));
|
|
24
|
+
}
|
|
25
|
+
srcStart += 2;
|
|
26
|
+
}
|
|
27
|
+
return out;
|
|
28
|
+
}
|
|
@@ -7,6 +7,8 @@ import { deserializeIntegerArray } from "./array/integer";
|
|
|
7
7
|
import { deserializeMapArray } from "./array/map";
|
|
8
8
|
import { deserializeStructArray } from "./array/struct";
|
|
9
9
|
import { deserializeStringArray } from "./array/string";
|
|
10
|
+
import { deserializeObjectArray } from "./array/object";
|
|
11
|
+
import { deserializeBoxArray } from "./array/box";
|
|
10
12
|
|
|
11
13
|
// @ts-ignore: Decorator valid here
|
|
12
14
|
export function deserializeArray<T extends unknown[]>(srcStart: usize, srcEnd: usize, dst: usize): T {
|
|
@@ -29,7 +31,13 @@ export function deserializeArray<T extends unknown[]>(srcStart: usize, srcEnd: u
|
|
|
29
31
|
if (type instanceof JSON.Value) {
|
|
30
32
|
// @ts-ignore: type
|
|
31
33
|
return deserializeArbitraryArray(srcStart, srcEnd, dst);
|
|
32
|
-
} else if (type instanceof
|
|
34
|
+
} else if (type instanceof JSON.Box) {
|
|
35
|
+
// @ts-ignore: type
|
|
36
|
+
return deserializeBoxArray<T>(srcStart, srcEnd, dst);
|
|
37
|
+
} else if (type instanceof JSON.Obj) {
|
|
38
|
+
// @ts-ignore: type
|
|
39
|
+
return deserializeObjectArray<T>(srcStart, srcEnd, dst);
|
|
40
|
+
}else if (type instanceof Map) {
|
|
33
41
|
// @ts-ignore: type
|
|
34
42
|
return deserializeMapArray<T>(srcStart, srcEnd, dst);
|
|
35
43
|
// @ts-ignore: defined by transform
|
package/assembly/index.ts
CHANGED
|
@@ -15,7 +15,6 @@ import { deserializeInteger } from "./deserialize/simple/integer";
|
|
|
15
15
|
import { deserializeString } from "./deserialize/simple/string";
|
|
16
16
|
import { serializeArbitrary } from "./serialize/simple/arbitrary";
|
|
17
17
|
|
|
18
|
-
import { Sink } from "./custom/sink";
|
|
19
18
|
import { NULL_WORD, QUOTE } from "./custom/chars";
|
|
20
19
|
import { dtoa_buffered, itoa_buffered } from "util/number";
|
|
21
20
|
import { serializeBool } from "./serialize/simple/bool";
|
|
@@ -31,6 +30,7 @@ import { serializeRaw } from "./serialize/simple/raw";
|
|
|
31
30
|
import { deserializeRaw } from "./deserialize/simple/raw";
|
|
32
31
|
import { isSpace } from "util/string";
|
|
33
32
|
import { deserializeString_SIMD } from "./deserialize/simd/string";
|
|
33
|
+
import { serializeString_SIMD } from "./serialize/simd/string";
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
36
|
* Offset of the 'storage' property in the JSON.Value class.
|
|
@@ -116,7 +116,11 @@ export namespace JSON {
|
|
|
116
116
|
// bs.setBuffer(oldBuf);
|
|
117
117
|
// return changetype<string>(newBuf);
|
|
118
118
|
// }
|
|
119
|
-
|
|
119
|
+
// if (ASC_FEATURE_SIMD) {
|
|
120
|
+
// serializeString_SIMD(data as string);
|
|
121
|
+
// } else {
|
|
122
|
+
serializeString(data as string);
|
|
123
|
+
// }
|
|
120
124
|
return bs.out<string>();
|
|
121
125
|
// @ts-ignore: Supplied by transform
|
|
122
126
|
} else if (isDefined(data.__SERIALIZE_CUSTOM)) {
|
|
@@ -145,7 +149,7 @@ export namespace JSON {
|
|
|
145
149
|
inline.always(serializeMap(changetype<nonnull<T>>(data)));
|
|
146
150
|
return bs.out<string>();
|
|
147
151
|
} else if (data instanceof JSON.Raw) {
|
|
148
|
-
|
|
152
|
+
serializeRaw(data);
|
|
149
153
|
return bs.out<string>();
|
|
150
154
|
} else if (data instanceof JSON.Value) {
|
|
151
155
|
inline.always(serializeArbitrary(data));
|
|
@@ -404,18 +408,16 @@ export namespace JSON {
|
|
|
404
408
|
case JSON.Types.Array: {
|
|
405
409
|
const arr = this.get<JSON.Value[]>();
|
|
406
410
|
if (!arr.length) return "[]";
|
|
407
|
-
|
|
411
|
+
let out = "["
|
|
408
412
|
const end = arr.length - 1;
|
|
409
413
|
for (let i = 0; i < end; i++) {
|
|
410
414
|
const element = unchecked(arr[i]);
|
|
411
|
-
out
|
|
412
|
-
out.write(",");
|
|
415
|
+
out += element.toString() + ",";
|
|
413
416
|
}
|
|
414
417
|
|
|
415
418
|
const element = unchecked(arr[end]);
|
|
416
|
-
out
|
|
419
|
+
out += element.toString() + "]";
|
|
417
420
|
|
|
418
|
-
out.write("]");
|
|
419
421
|
return out.toString();
|
|
420
422
|
}
|
|
421
423
|
case JSON.Types.Object: {
|
|
@@ -438,7 +440,7 @@ export namespace JSON {
|
|
|
438
440
|
// @ts-ignore: type
|
|
439
441
|
private storage: Map<string, JSON.Value> = new Map<string, JSON.Value>();
|
|
440
442
|
|
|
441
|
-
constructor() {}
|
|
443
|
+
constructor() { }
|
|
442
444
|
|
|
443
445
|
// @ts-ignore: decorator
|
|
444
446
|
@inline get size(): i32 {
|
|
@@ -552,26 +554,26 @@ export namespace JSON {
|
|
|
552
554
|
// @ts-ignore: Supplied by transform
|
|
553
555
|
} else if (isDefined(src.__SERIALIZE_CUSTOM)) {
|
|
554
556
|
// @ts-ignore
|
|
555
|
-
return src.__SERIALIZE_CUSTOM(
|
|
557
|
+
return src.__SERIALIZE_CUSTOM();
|
|
556
558
|
// @ts-ignore: Supplied by transform
|
|
557
559
|
} else if (isDefined(src.__SERIALIZE)) {
|
|
558
560
|
// @ts-ignore
|
|
559
|
-
|
|
561
|
+
serializeStruct(changetype<nonnull<T>>(src));
|
|
560
562
|
} else if (src instanceof Date) {
|
|
561
563
|
// @ts-ignore
|
|
562
564
|
inline.always(serializeDate(changetype<nonnull<T>>(src)));
|
|
563
565
|
} else if (src instanceof Array) {
|
|
564
566
|
// @ts-ignore
|
|
565
|
-
|
|
567
|
+
serializeArray(changetype<nonnull<T>>(src));
|
|
566
568
|
} else if (src instanceof Map) {
|
|
567
569
|
// @ts-ignore
|
|
568
|
-
|
|
570
|
+
serializeMap(changetype<nonnull<T>>(src));
|
|
569
571
|
} else if (src instanceof JSON.Raw) {
|
|
570
572
|
serializeRaw(src);
|
|
571
573
|
} else if (src instanceof JSON.Value) {
|
|
572
|
-
|
|
574
|
+
serializeArbitrary(src);
|
|
573
575
|
} else if (src instanceof JSON.Obj) {
|
|
574
|
-
|
|
576
|
+
serializeObject(src);
|
|
575
577
|
} else if (src instanceof JSON.Box) {
|
|
576
578
|
__serialize(src.value);
|
|
577
579
|
} else {
|
|
@@ -596,7 +598,7 @@ export namespace JSON {
|
|
|
596
598
|
return null;
|
|
597
599
|
} else if (isArray<T>()) {
|
|
598
600
|
// @ts-ignore: type
|
|
599
|
-
return
|
|
601
|
+
return deserializeArray<T>(srcStart, srcEnd, dst);
|
|
600
602
|
} else {
|
|
601
603
|
let type: nonnull<T> = changetype<nonnull<T>>(0);
|
|
602
604
|
// @ts-ignore: Defined by transform
|
|
@@ -608,10 +610,10 @@ export namespace JSON {
|
|
|
608
610
|
return changetype<nonnull<T>>(out).__DESERIALIZE_CUSTOM(ptrToStr(srcStart, srcEnd));
|
|
609
611
|
// @ts-ignore: Defined by transform
|
|
610
612
|
} else if (isDefined(type.__DESERIALIZE)) {
|
|
611
|
-
return
|
|
613
|
+
return deserializeStruct<T>(srcStart, srcEnd, dst);
|
|
612
614
|
} else if (type instanceof Map) {
|
|
613
615
|
// @ts-ignore: type
|
|
614
|
-
return
|
|
616
|
+
return deserializeMap<T>(srcStart, srcEnd, dst);
|
|
615
617
|
} else if (type instanceof Date) {
|
|
616
618
|
// @ts-ignore: type
|
|
617
619
|
return deserializeDate(srcStart, srcEnd);
|
|
@@ -620,7 +622,10 @@ export namespace JSON {
|
|
|
620
622
|
return deserializeRaw(srcStart, srcEnd);
|
|
621
623
|
} else if (type instanceof JSON.Value) {
|
|
622
624
|
// @ts-ignore: type
|
|
623
|
-
return
|
|
625
|
+
return deserializeArbitrary(srcStart, srcEnd, 0);
|
|
626
|
+
}else if (type instanceof JSON.Obj) {
|
|
627
|
+
// @ts-ignore: type
|
|
628
|
+
return deserializeObject(srcStart, srcEnd, 0);
|
|
624
629
|
} else if (type instanceof JSON.Box) {
|
|
625
630
|
// @ts-ignore: type
|
|
626
631
|
return new JSON.Box(deserializeBox(srcStart, srcEnd, dst, changetype<nonnull<T>>(0).value));
|
|
@@ -1,13 +1,7 @@
|
|
|
1
1
|
import { bs } from "../../../lib/as-bs";
|
|
2
2
|
import { BACK_SLASH } from "../../custom/chars";
|
|
3
3
|
import { SERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
const SPLAT_34 = i16x8.splat(34); /* " */
|
|
7
|
-
const SPLAT_92 = i16x8.splat(92); /* \ */
|
|
8
|
-
|
|
9
|
-
const SPLAT_32 = i16x8.splat(32); /* [ESC] */
|
|
10
|
-
const SPLAT_0 = i16x8.splat(0); /* 0 */
|
|
4
|
+
import { bytes } from "../../util";
|
|
11
5
|
|
|
12
6
|
/**
|
|
13
7
|
* Serializes strings into their JSON counterparts using SIMD operations
|
|
@@ -15,16 +9,23 @@ const SPLAT_0 = i16x8.splat(0); /* 0 */
|
|
|
15
9
|
* @param srcEnd pointer to end serialization at
|
|
16
10
|
*/
|
|
17
11
|
export function serializeString_SIMD(src: string): void {
|
|
18
|
-
const
|
|
12
|
+
const SPLAT_34 = i16x8.splat(34); /* " */
|
|
13
|
+
const SPLAT_92 = i16x8.splat(92); /* \ */
|
|
14
|
+
|
|
15
|
+
const SPLAT_32 = i16x8.splat(32); /* [ESC] */
|
|
16
|
+
const SPLAT_0 = i16x8.splat(0); /* 0 */
|
|
17
|
+
|
|
18
|
+
const srcSize = bytes(src);
|
|
19
19
|
let srcStart = changetype<usize>(src);
|
|
20
20
|
const srcEnd = srcStart + srcSize;
|
|
21
|
+
const srcEnd16 = srcEnd - 16;
|
|
22
|
+
|
|
21
23
|
bs.proposeSize(srcSize + 4);
|
|
22
|
-
const srcEnd16 = srcEnd - 15;
|
|
23
24
|
|
|
24
25
|
store<u8>(changetype<usize>(bs.offset), 34); /* " */
|
|
25
26
|
bs.offset += 2;
|
|
26
27
|
|
|
27
|
-
while (srcStart
|
|
28
|
+
while (srcStart <= srcEnd16) {
|
|
28
29
|
const block = v128.load(srcStart);
|
|
29
30
|
v128.store(bs.offset, block);
|
|
30
31
|
|
|
@@ -62,8 +63,7 @@ export function serializeString_SIMD(src: string): void {
|
|
|
62
63
|
bs.offset += 16;
|
|
63
64
|
}
|
|
64
65
|
|
|
65
|
-
|
|
66
|
-
|
|
66
|
+
const rem = srcEnd - srcStart;
|
|
67
67
|
if (rem & 8) {
|
|
68
68
|
const block = v128.load64_zero(srcStart);
|
|
69
69
|
v128.store64_lane(bs.offset, block, 0);
|