json-as 1.1.19 → 1.1.21
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 +6 -34
- package/assembly/__benches__/abc.bench.ts +12 -4
- package/assembly/__benches__/lib/bench.ts +19 -7
- 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 +32 -20
- package/assembly/serialize/simd/string.ts +8 -95
- 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 +16 -609
- package/assembly/test.ts +2 -0
- package/bench/abc.bench.ts +9 -2
- package/bench/lib/bench.ts +33 -18
- package/bench/runners/assemblyscript.js +1 -0
- package/bench.ts +18 -0
- package/lib/as-bs.ts +13 -12
- package/package.json +6 -6
- package/run-bench.as.sh +5 -6
- package/run-bench.js.sh +2 -6
- package/run-tests.sh +1 -1
- package/transform/lib/index.js +4 -1
- package/transform/lib/index.js.map +1 -1
- package/transform/lib/types.js +21 -0
- package/transform/lib/types.js.map +1 -1
- package/transform/src/index.ts +5 -1
- package/transform/src/types.ts +35 -1
- package/bench.js +0 -83
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 2025-08-14 - 1.1.21
|
|
4
|
+
|
|
5
|
+
- fix: JSON.parse on classes with enums [#155](https://github.com/JairusSW/json-as/issues/155)
|
|
6
|
+
- fix: Resolve memory OOB issue within `serializeFloat` function [#153](https://github.com/JairusSW/json-as/issues/153)
|
|
7
|
+
|
|
8
|
+
## 2025-07-14 - 1.1.20
|
|
9
|
+
|
|
10
|
+
- feat: enable SIMD string serialization
|
|
11
|
+
|
|
3
12
|
## 2025-06-30 - 1.1.19
|
|
4
13
|
|
|
5
14
|
- fix: wrong path used in `readFileSync` when importing from a library
|
package/README.md
CHANGED
|
@@ -1,34 +1,6 @@
|
|
|
1
|
-
<
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
██ ██ ██ ██ ████ ██ ██ ██ ██
|
|
5
|
-
██ ███████ ██ ██ ██ ██ ██ █████ ███████ ███████
|
|
6
|
-
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
7
|
-
█████ ███████ ██████ ██ ████ ██ ██ ███████
|
|
8
|
-
</span>
|
|
9
|
-
AssemblyScript - v1.1.19
|
|
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 {
|
|
@@ -391,7 +363,7 @@ These benchmarks compare this library to JavaScript's native `JSON.stringify` an
|
|
|
391
363
|
| Test Case | Size | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
392
364
|
| --------------- | ---------- | --------------------- | ----------------------- | -------------------- | ---------------------- |
|
|
393
365
|
| Vector3 Object | 38 bytes | 26,611,226 ops/s | 32,160,804 ops/s | 1,357 MB/s | 1,348 MB/s |
|
|
394
|
-
| Alphabet String | 104 bytes |
|
|
366
|
+
| Alphabet String | 104 bytes | 16,916,886 ops/s | 18,390,804 ops/s | 1,759 MB/s | 1,986 MB/s |
|
|
395
367
|
| Small Object | 88 bytes | 24,242,424 ops/s | 12,307,692 ops/s | 2,133 MB/s | 1,083 MB/s |
|
|
396
368
|
| Medium Object | 494 bytes | 4,060,913 ops/s | 1,396,160 ops/s | 2,006 MB/s | 689.7 MB/s |
|
|
397
369
|
| Large Object | 3374 bytes | 479,616 ops/s | 132,802 ops/s | 2,074 MB/s | 448.0 MB/s |
|
|
@@ -401,7 +373,7 @@ These benchmarks compare this library to JavaScript's native `JSON.stringify` an
|
|
|
401
373
|
| Test Case | Size | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
|
|
402
374
|
| --------------- | ---------- | --------------------- | ----------------------- | -------------------- | ---------------------- |
|
|
403
375
|
| Vector3 Object | 38 bytes | 8,791,209 ops/s | 5,369,12 ops/s | 357.4 MB/s | 204.3 MB/s |
|
|
404
|
-
| Alphabet String | 104 bytes |
|
|
376
|
+
| Alphabet String | 104 bytes | 12,830,228 ops/s | 12,140,296 ops/s | 1,334 MB/s | 1,311 MB/s |
|
|
405
377
|
| Small Object | 88 bytes | 8,376,963 ops/s | 4,968,944 ops/s | 737.1 MB/s | 437.2 MB/s |
|
|
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 |
|
|
@@ -5,20 +5,28 @@ import { bench } from "./lib/bench";
|
|
|
5
5
|
const v1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
6
6
|
const v2 = '"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"';
|
|
7
7
|
|
|
8
|
+
const blackBoxArea = memory.data(64);
|
|
8
9
|
expect(JSON.stringify(v1)).toBe(v2);
|
|
9
10
|
|
|
10
11
|
bench(
|
|
11
12
|
"Serialize Alphabet",
|
|
12
13
|
() => {
|
|
13
|
-
inline.always(JSON.stringify(v1));
|
|
14
|
+
blackbox(inline.always(JSON.stringify(blackbox(v1))));
|
|
14
15
|
},
|
|
15
|
-
|
|
16
|
+
24_000_00,
|
|
17
|
+
v1.length << 1,
|
|
16
18
|
);
|
|
17
19
|
|
|
18
20
|
bench(
|
|
19
21
|
"Deserialize Alphabet",
|
|
20
22
|
() => {
|
|
21
|
-
inline.always(JSON.parse<string>(v2));
|
|
23
|
+
blackbox(inline.always(JSON.parse<string>(blackbox(v2))));
|
|
22
24
|
},
|
|
23
|
-
|
|
25
|
+
24_000_00,
|
|
26
|
+
v2.length << 1,
|
|
24
27
|
);
|
|
28
|
+
|
|
29
|
+
function blackbox<T>(value: T): T {
|
|
30
|
+
store<T>(blackBoxArea, value);
|
|
31
|
+
return load<T>(blackBoxArea);
|
|
32
|
+
}
|
|
@@ -1,20 +1,32 @@
|
|
|
1
|
-
export function bench(description: string, routine: () => void, ops: u64 = 1_000_000): void {
|
|
1
|
+
export function bench(description: string, routine: () => void, ops: u64 = 1_000_000, bytesPerOp: u64 = 0): void {
|
|
2
2
|
console.log(" - Benchmarking " + description);
|
|
3
|
+
|
|
3
4
|
let warmup = ops / 10;
|
|
4
5
|
while (--warmup) {
|
|
5
6
|
routine();
|
|
6
7
|
}
|
|
7
|
-
|
|
8
|
+
|
|
9
|
+
const start = performance.now();
|
|
10
|
+
|
|
8
11
|
let count = ops;
|
|
9
|
-
while (count
|
|
12
|
+
while (count--) {
|
|
10
13
|
routine();
|
|
11
|
-
count--;
|
|
12
14
|
}
|
|
13
|
-
const elapsed = Date.now() - start;
|
|
14
15
|
|
|
15
|
-
|
|
16
|
+
const end = performance.now();
|
|
17
|
+
const elapsed = Math.max(1, end - start);
|
|
18
|
+
|
|
19
|
+
const opsPerSecond = f64(ops * 1000) / elapsed;
|
|
20
|
+
|
|
21
|
+
let log = ` Completed benchmark in ${formatNumber(u64(Math.round(elapsed)))}ms at ${formatNumber(u64(Math.round(opsPerSecond)))} ops/s`;
|
|
22
|
+
|
|
23
|
+
if (bytesPerOp > 0) {
|
|
24
|
+
const totalBytes = bytesPerOp * ops;
|
|
25
|
+
const mbPerSec = f64(totalBytes) / (elapsed / 1000) / (1000 * 1000);
|
|
26
|
+
log += ` @ ${formatNumber(u64(Math.round(mbPerSec)))}MB/s`;
|
|
27
|
+
}
|
|
16
28
|
|
|
17
|
-
console.log(
|
|
29
|
+
console.log(log + "\n");
|
|
18
30
|
}
|
|
19
31
|
|
|
20
32
|
function formatNumber(n: u64): string {
|
|
@@ -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
|
@@ -27,6 +27,8 @@ import { serializeObject } from "./serialize/simple/object";
|
|
|
27
27
|
import { deserializeObject } from "./deserialize/simple/object";
|
|
28
28
|
import { serializeRaw } from "./serialize/simple/raw";
|
|
29
29
|
import { deserializeRaw } from "./deserialize/simple/raw";
|
|
30
|
+
import { serializeString_SIMD } from "./serialize/simd/string";
|
|
31
|
+
// import { deserializeString_SIMD } from "./deserialize/simd/string";
|
|
30
32
|
|
|
31
33
|
/**
|
|
32
34
|
* Offset of the 'storage' property in the JSON.Value class.
|
|
@@ -112,11 +114,11 @@ export namespace JSON {
|
|
|
112
114
|
// bs.setBuffer(oldBuf);
|
|
113
115
|
// return changetype<string>(newBuf);
|
|
114
116
|
// }
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
117
|
+
if (ASC_FEATURE_SIMD) {
|
|
118
|
+
serializeString_SIMD(data as string);
|
|
119
|
+
} else {
|
|
120
|
+
serializeString(data as string);
|
|
121
|
+
}
|
|
120
122
|
return bs.out<string>();
|
|
121
123
|
// @ts-ignore: Supplied by transform
|
|
122
124
|
} else if (isDefined(data.__SERIALIZE)) {
|
|
@@ -179,7 +181,7 @@ export namespace JSON {
|
|
|
179
181
|
} else if (isString<T>()) {
|
|
180
182
|
if (dataSize < 4) throw new Error("Cannot parse data as string because it was formatted incorrectly!");
|
|
181
183
|
// if (ASC_FEATURE_SIMD) {
|
|
182
|
-
//
|
|
184
|
+
// // @ts-ignore
|
|
183
185
|
// return changetype<string>(deserializeString_SIMD(dataPtr, dataPtr + dataSize, __new(dataSize - 4, idof<string>())));
|
|
184
186
|
// } else {
|
|
185
187
|
// @ts-ignore
|
|
@@ -258,6 +260,8 @@ export namespace JSON {
|
|
|
258
260
|
}
|
|
259
261
|
}
|
|
260
262
|
|
|
263
|
+
|
|
264
|
+
@final
|
|
261
265
|
export class Value {
|
|
262
266
|
static METHODS: Map<u32, u32> = new Map<u32, u32>();
|
|
263
267
|
public type: i32;
|
|
@@ -332,7 +336,7 @@ export namespace JSON {
|
|
|
332
336
|
this.type = JSON.Types.Struct;
|
|
333
337
|
store<T>(changetype<usize>(this), value, STORAGE);
|
|
334
338
|
// @ts-ignore: supplied by transform
|
|
335
|
-
} else if (isDefined(value.__SERIALIZE)) {
|
|
339
|
+
} else if (isDefined(value.__SERIALIZE) && isManaged<T>(value)) {
|
|
336
340
|
this.type = idof<T>() + JSON.Types.Struct;
|
|
337
341
|
// @ts-ignore
|
|
338
342
|
if (!JSON.Value.METHODS.has(idof<T>())) JSON.Value.METHODS.set(idof<T>(), value.__SERIALIZE.index);
|
|
@@ -417,14 +421,18 @@ export namespace JSON {
|
|
|
417
421
|
}
|
|
418
422
|
}
|
|
419
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
|
+
}
|
|
420
431
|
}
|
|
421
432
|
|
|
422
433
|
export class Obj {
|
|
423
|
-
// When accessing stackSize, subtract 2
|
|
424
|
-
// @ts-ignore: type
|
|
425
|
-
private stackSize: u32 = 6;
|
|
426
434
|
// @ts-ignore: type
|
|
427
|
-
|
|
435
|
+
storage: Map<string, JSON.Value> = new Map<string, JSON.Value>();
|
|
428
436
|
|
|
429
437
|
constructor() {}
|
|
430
438
|
|
|
@@ -435,7 +443,7 @@ export namespace JSON {
|
|
|
435
443
|
|
|
436
444
|
// @ts-ignore: decorator
|
|
437
445
|
@inline set<T>(key: string, value: T): void {
|
|
438
|
-
if (!this.storage.has(key)) this.stackSize += bytes(key) + 8;
|
|
446
|
+
// if (!this.storage.has(key)) this.stackSize += bytes(key) + 8;
|
|
439
447
|
this.storage.set(key, JSON.Value.from<T>(value));
|
|
440
448
|
}
|
|
441
449
|
|
|
@@ -473,7 +481,7 @@ export namespace JSON {
|
|
|
473
481
|
// @ts-ignore: decorator
|
|
474
482
|
@inline static from<T>(value: T): JSON.Obj {
|
|
475
483
|
if (value instanceof JSON.Obj) return value;
|
|
476
|
-
const out =
|
|
484
|
+
const out = new JSON.Obj();
|
|
477
485
|
|
|
478
486
|
if (value instanceof Map) {
|
|
479
487
|
}
|
|
@@ -542,7 +550,11 @@ export namespace JSON {
|
|
|
542
550
|
store<u64>(bs.offset, 30399761348886638);
|
|
543
551
|
bs.offset += 8;
|
|
544
552
|
} else if (isString<nonnull<T>>()) {
|
|
545
|
-
|
|
553
|
+
if (ASC_FEATURE_SIMD) {
|
|
554
|
+
serializeString_SIMD(src as string);
|
|
555
|
+
} else {
|
|
556
|
+
serializeString(src as string);
|
|
557
|
+
}
|
|
546
558
|
// @ts-ignore: Supplied by transform
|
|
547
559
|
} else if (isDefined(src.__SERIALIZE_CUSTOM)) {
|
|
548
560
|
// @ts-ignore
|
|
@@ -714,12 +726,12 @@ export namespace JSON {
|
|
|
714
726
|
// bs.setBuffer(oldBuf);
|
|
715
727
|
// return changetype<string>(newBuf);
|
|
716
728
|
// }
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
729
|
+
if (ASC_FEATURE_SIMD) {
|
|
730
|
+
serializeString_SIMD(data as string);
|
|
731
|
+
} else {
|
|
732
|
+
bs.saveState();
|
|
733
|
+
serializeString(data as string);
|
|
734
|
+
}
|
|
723
735
|
return bs.cpyOut<string>();
|
|
724
736
|
// @ts-ignore: Supplied by transform
|
|
725
737
|
} else if (isDefined(data.__SERIALIZE)) {
|
|
@@ -9,11 +9,11 @@ import { bytes } from "../../util";
|
|
|
9
9
|
* @param srcEnd pointer to end serialization at
|
|
10
10
|
*/
|
|
11
11
|
export function serializeString_SIMD(src: string): void {
|
|
12
|
+
const U00_MARKER = 13511005048209500;
|
|
12
13
|
const SPLAT_34 = i16x8.splat(34); /* " */
|
|
13
14
|
const SPLAT_92 = i16x8.splat(92); /* \ */
|
|
14
15
|
|
|
15
16
|
const SPLAT_32 = i16x8.splat(32); /* [ESC] */
|
|
16
|
-
const SPLAT_0 = i16x8.splat(0); /* 0 */
|
|
17
17
|
|
|
18
18
|
const srcSize = bytes(src);
|
|
19
19
|
let srcStart = changetype<usize>(src);
|
|
@@ -27,6 +27,7 @@ export function serializeString_SIMD(src: string): void {
|
|
|
27
27
|
|
|
28
28
|
while (srcStart <= srcEnd16) {
|
|
29
29
|
const block = v128.load(srcStart);
|
|
30
|
+
|
|
30
31
|
v128.store(bs.offset, block);
|
|
31
32
|
|
|
32
33
|
const backslash_indices = i16x8.eq(block, SPLAT_92);
|
|
@@ -38,21 +39,20 @@ export function serializeString_SIMD(src: string): void {
|
|
|
38
39
|
|
|
39
40
|
while (mask != 0) {
|
|
40
41
|
const lane_index = ctz(mask) << 1;
|
|
41
|
-
const dst_offset = bs.offset + lane_index;
|
|
42
42
|
const src_offset = srcStart + lane_index;
|
|
43
43
|
const code = load<u16>(src_offset) << 2;
|
|
44
44
|
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + code);
|
|
45
|
-
|
|
46
45
|
mask &= mask - 1;
|
|
47
|
-
|
|
48
46
|
if ((escaped & 0xffff) != BACK_SLASH) {
|
|
49
47
|
bs.growSize(10);
|
|
50
|
-
|
|
48
|
+
const dst_offset = bs.offset + lane_index;
|
|
49
|
+
store<u64>(dst_offset, U00_MARKER);
|
|
51
50
|
store<u32>(dst_offset, escaped, 8);
|
|
52
51
|
v128.store(dst_offset, v128.load(src_offset, 2), 12);
|
|
53
52
|
bs.offset += 10;
|
|
54
53
|
} else {
|
|
55
54
|
bs.growSize(2);
|
|
55
|
+
const dst_offset = bs.offset + lane_index;
|
|
56
56
|
store<u32>(dst_offset, escaped);
|
|
57
57
|
v128.store(dst_offset, v128.load(src_offset, 2), 4);
|
|
58
58
|
bs.offset += 2;
|
|
@@ -63,101 +63,13 @@ export function serializeString_SIMD(src: string): void {
|
|
|
63
63
|
bs.offset += 16;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
if (rem & 8) {
|
|
68
|
-
const block = v128.load64_zero(srcStart);
|
|
69
|
-
v128.store64_lane(bs.offset, block, 0);
|
|
70
|
-
|
|
71
|
-
const backslash_indices = i16x8.eq(block, SPLAT_92);
|
|
72
|
-
const quote_indices = i16x8.eq(block, SPLAT_34);
|
|
73
|
-
const escape_indices = i16x8.lt_u(block, SPLAT_32);
|
|
74
|
-
const zero_indices = i16x8.eq(block, SPLAT_0);
|
|
75
|
-
const sieve = v128.and(v128.or(v128.or(backslash_indices, quote_indices), escape_indices), v128.not(zero_indices));
|
|
76
|
-
|
|
77
|
-
let mask = i16x8.bitmask(sieve);
|
|
78
|
-
while (mask != 0) {
|
|
79
|
-
let lane_index = ctz(mask) << 1;
|
|
80
|
-
const dst_offset = bs.offset + lane_index;
|
|
81
|
-
const src_offset = srcStart + lane_index;
|
|
82
|
-
const code = load<u16>(src_offset) << 2;
|
|
83
|
-
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + code);
|
|
84
|
-
mask &= mask - 1;
|
|
85
|
-
|
|
86
|
-
if ((escaped & 0xffff) != BACK_SLASH) {
|
|
87
|
-
bs.growSize(10);
|
|
88
|
-
store<u64>(dst_offset, 13511005048209500);
|
|
89
|
-
store<u32>(dst_offset, escaped, 8);
|
|
90
|
-
while (lane_index < 6) {
|
|
91
|
-
store<u8>(bs.offset + lane_index, load<u8>(srcStart + lane_index, 2), 12);
|
|
92
|
-
lane_index += 2;
|
|
93
|
-
}
|
|
94
|
-
bs.offset += 10;
|
|
95
|
-
} else {
|
|
96
|
-
bs.growSize(2);
|
|
97
|
-
store<u32>(dst_offset, escaped);
|
|
98
|
-
|
|
99
|
-
while (lane_index < 6) {
|
|
100
|
-
store<u8>(bs.offset + lane_index, load<u8>(srcStart + lane_index, 2), 4);
|
|
101
|
-
lane_index += 2;
|
|
102
|
-
}
|
|
103
|
-
bs.offset += 2;
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
bs.offset += 8;
|
|
108
|
-
srcStart += 8;
|
|
109
|
-
}
|
|
110
|
-
if (rem & 4) {
|
|
111
|
-
const block = load<u32>(srcStart);
|
|
112
|
-
const codeA = block & 0xffff;
|
|
113
|
-
const codeB = (block >> 16) & 0xffff;
|
|
114
|
-
|
|
115
|
-
if (codeA == 92 || codeA == 34 || codeA < 32) {
|
|
116
|
-
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + (codeA << 2));
|
|
117
|
-
|
|
118
|
-
if ((escaped & 0xffff) != BACK_SLASH) {
|
|
119
|
-
bs.growSize(10);
|
|
120
|
-
store<u64>(bs.offset, 13511005048209500);
|
|
121
|
-
store<u32>(bs.offset, escaped, 8);
|
|
122
|
-
bs.offset += 12;
|
|
123
|
-
} else {
|
|
124
|
-
bs.growSize(2);
|
|
125
|
-
store<u32>(bs.offset, escaped);
|
|
126
|
-
bs.offset += 4;
|
|
127
|
-
}
|
|
128
|
-
} else {
|
|
129
|
-
store<u16>(bs.offset, codeA);
|
|
130
|
-
bs.offset += 2;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (codeB == 92 || codeB == 34 || codeB < 32) {
|
|
134
|
-
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + (codeB << 2));
|
|
135
|
-
|
|
136
|
-
if ((escaped & 0xffff) != BACK_SLASH) {
|
|
137
|
-
bs.growSize(10);
|
|
138
|
-
store<u64>(bs.offset, 13511005048209500);
|
|
139
|
-
store<u32>(bs.offset, escaped, 8);
|
|
140
|
-
bs.offset += 12;
|
|
141
|
-
} else {
|
|
142
|
-
bs.growSize(2);
|
|
143
|
-
store<u32>(bs.offset, escaped);
|
|
144
|
-
bs.offset += 4;
|
|
145
|
-
}
|
|
146
|
-
} else {
|
|
147
|
-
store<u16>(bs.offset, codeB);
|
|
148
|
-
bs.offset += 2;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
srcStart += 4;
|
|
152
|
-
}
|
|
153
|
-
if (rem & 2) {
|
|
66
|
+
while (srcStart <= srcEnd - 2) {
|
|
154
67
|
const code = load<u16>(srcStart);
|
|
155
68
|
if (code == 92 || code == 34 || code < 32) {
|
|
156
69
|
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + (code << 2));
|
|
157
|
-
|
|
158
70
|
if ((escaped & 0xffff) != BACK_SLASH) {
|
|
159
71
|
bs.growSize(10);
|
|
160
|
-
store<u64>(bs.offset,
|
|
72
|
+
store<u64>(bs.offset, U00_MARKER);
|
|
161
73
|
store<u32>(bs.offset, escaped, 8);
|
|
162
74
|
bs.offset += 12;
|
|
163
75
|
} else {
|
|
@@ -169,6 +81,7 @@ export function serializeString_SIMD(src: string): void {
|
|
|
169
81
|
store<u16>(bs.offset, code);
|
|
170
82
|
bs.offset += 2;
|
|
171
83
|
}
|
|
84
|
+
srcStart += 2;
|
|
172
85
|
}
|
|
173
86
|
|
|
174
87
|
store<u8>(bs.offset, 34); /* " */
|