json-as 1.2.1 → 1.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +31 -1
- package/assembly/deserialize/simd/string.ts +62 -63
- package/assembly/deserialize/simple/arbitrary.ts +4 -4
- package/assembly/deserialize/simple/array/arbitrary.ts +1 -1
- package/assembly/index.d.ts +9 -0
- package/assembly/index.ts +211 -112
- package/assembly/serialize/simd/string.ts +16 -3
- package/assembly/serialize/simple/arbitrary.ts +16 -2
- package/assembly/serialize/swar/string.ts +41 -15
- package/assembly/test.ts +38 -0
- package/lib/as-bs.ts +113 -22
- package/package.json +4 -3
- package/transform/lib/index.js +34 -1
- package/transform/lib/index.js.map +1 -1
- package/.github/FUNDING.yml +0 -1
- package/.github/dependabot.yml +0 -11
- package/.github/workflows/benchmark.yml +0 -72
- package/.github/workflows/release-package.yml +0 -47
- package/.github/workflows/tests.yml +0 -25
- package/.prettierignore +0 -9
- package/.prettierrc.json +0 -7
- package/.trunk/configs/.markdownlint.yaml +0 -2
- package/.trunk/configs/.shellcheckrc +0 -7
- package/.trunk/configs/.yamllint.yaml +0 -7
- package/.trunk/trunk.yaml +0 -37
- package/CHANGELOG.md +0 -334
- package/SECURITY.md +0 -32
- package/asconfig.json +0 -7
- package/assembly/__benches__/abc.bench.ts +0 -28
- package/assembly/__benches__/large.bench.ts +0 -238
- package/assembly/__benches__/lib/bench.ts +0 -85
- package/assembly/__benches__/medium.bench.ts +0 -128
- package/assembly/__benches__/small.bench.ts +0 -46
- package/assembly/__benches__/throughput.ts +0 -172
- package/assembly/__benches__/vec3.bench.ts +0 -37
- package/assembly/__tests__/arbitrary.spec.ts +0 -35
- package/assembly/__tests__/array.spec.ts +0 -145
- package/assembly/__tests__/bool.spec.ts +0 -12
- package/assembly/__tests__/box.spec.ts +0 -27
- package/assembly/__tests__/custom.spec.ts +0 -56
- package/assembly/__tests__/date.spec.ts +0 -36
- package/assembly/__tests__/enum.spec.ts +0 -35
- package/assembly/__tests__/float.spec.ts +0 -42
- package/assembly/__tests__/generics.spec.ts +0 -49
- package/assembly/__tests__/hierarchy.spec.ts +0 -61
- package/assembly/__tests__/integer.spec.ts +0 -26
- package/assembly/__tests__/lib/index.ts +0 -41
- package/assembly/__tests__/map.spec.ts +0 -7
- package/assembly/__tests__/namespace.spec.ts +0 -63
- package/assembly/__tests__/null.spec.ts +0 -12
- package/assembly/__tests__/raw.spec.ts +0 -23
- package/assembly/__tests__/resolving.spec.ts +0 -55
- package/assembly/__tests__/staticarray.spec.ts +0 -12
- package/assembly/__tests__/string.spec.ts +0 -30
- package/assembly/__tests__/struct.spec.ts +0 -163
- package/assembly/__tests__/test.spec.ts +0 -3
- package/assembly/__tests__/types.spec.ts +0 -27
- package/assembly/__tests__/types.ts +0 -98
- package/assembly/test.tmp.ts +0 -133
- package/bench/abc.bench.ts +0 -25
- package/bench/large.bench.ts +0 -127
- package/bench/lib/bench.d.ts +0 -27
- package/bench/lib/bench.js +0 -53
- package/bench/lib/chart.ts +0 -217
- package/bench/medium.bench.ts +0 -68
- package/bench/runners/assemblyscript.js +0 -34
- package/bench/small.bench.ts +0 -34
- package/bench/throughput.ts +0 -87
- package/bench/tsconfig.json +0 -13
- package/bench/vec3.bench.ts +0 -30
- package/bench.ts +0 -18
- package/ci/bench/lib/bench.ts +0 -42
- package/ci/bench/runners/assemblyscript.js +0 -29
- package/ci/run-bench.as.sh +0 -63
- package/publish.sh +0 -78
- package/run-bench.as.sh +0 -60
- package/run-bench.js.sh +0 -36
- package/run-tests.sh +0 -51
- package/scripts/build-chart01.ts +0 -38
- package/scripts/build-chart02.ts +0 -38
- package/scripts/build-chart03.ts +0 -139
- package/scripts/build-chart05.ts +0 -47
- package/scripts/generate-as-class.ts +0 -50
- package/scripts/lib/bench-utils.ts +0 -308
- package/transform/src/builder.ts +0 -1375
- package/transform/src/index.ts +0 -1486
- package/transform/src/linkers/alias.ts +0 -58
- package/transform/src/linkers/custom.ts +0 -32
- package/transform/src/linkers/imports.ts +0 -22
- package/transform/src/types.ts +0 -300
- package/transform/src/util.ts +0 -128
- package/transform/src/visitor.ts +0 -530
package/README.md
CHANGED
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
- [Using Custom Serializers or Deserializers](#using-custom-serializers-or-deserializers)
|
|
17
17
|
- [Performance](#performance)
|
|
18
18
|
- [Comparison to JavaScript](#comparison-to-javascript)
|
|
19
|
+
- [Performance Tuning](#performance-tuning)
|
|
19
20
|
- [Running Benchmarks Locally](#running-benchmarks-locally)
|
|
20
|
-
- [Re-run Benchmarks](#re-run-benchmarks)
|
|
21
21
|
- [Debugging](#debugging)
|
|
22
22
|
- [License](#license)
|
|
23
23
|
- [Contact](#contact)
|
|
@@ -269,6 +269,21 @@ obj.data = JSON.Value.from("a string"); // Changing to a string
|
|
|
269
269
|
console.log(JSON.stringify(obj)); // {"id":1,"name":"Example","data":"a string"}
|
|
270
270
|
```
|
|
271
271
|
|
|
272
|
+
**Working with nullable primitives and dynamic data**
|
|
273
|
+
|
|
274
|
+
```ts
|
|
275
|
+
const box = JSON.Box.from<i32>(123);
|
|
276
|
+
const value = JSON.Value.from<JSON.Box<i32> | null>(box);
|
|
277
|
+
const reboxed = JSON.Box.fromValue<i32>(value); // Box<i32> | null
|
|
278
|
+
console.log(reboxed !== null ? reboxed!.toString() : "null");
|
|
279
|
+
// 123
|
|
280
|
+
|
|
281
|
+
const value = JSON.parse<JSON.Value>("123");
|
|
282
|
+
const boxed = JSON.Box.fromValue<i32>(value);
|
|
283
|
+
console.log(boxed !== null ? boxed!.toString() : "null");
|
|
284
|
+
// 123
|
|
285
|
+
```
|
|
286
|
+
|
|
272
287
|
### Using Raw JSON strings
|
|
273
288
|
|
|
274
289
|
Sometimes its necessary to simply copy a string instead of serializing it.
|
|
@@ -396,8 +411,23 @@ The following charts compare JSON-AS (both SWAR and SIMD variants) against JavaS
|
|
|
396
411
|
|
|
397
412
|
<img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart02.svg" alt="Performance Chart 2">
|
|
398
413
|
|
|
414
|
+
<img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart03.png" alt="Performance Chart 3">
|
|
415
|
+
|
|
399
416
|
> Note: I have focused on extensively optimizing serialization. I used to have deserialization be highly unsafe and extremely fast, but I've since doubled down on safety for deserialization which has negatively affected performance. I will be optimizing soon.
|
|
400
417
|
|
|
418
|
+
### Performance Tuning
|
|
419
|
+
|
|
420
|
+
Instead of using flags for setting options, `json-as` is configured by environmental variables.
|
|
421
|
+
Here's a short list:
|
|
422
|
+
|
|
423
|
+
**JSON_CACHE** (default: 0) - Enables caching costly strings based on hit frequency. May boost string serialization in excess of 22 GB/s.
|
|
424
|
+
|
|
425
|
+
**JSON_DEBUG** (default: 0) - Sets the debug level. May be within range `0-3`
|
|
426
|
+
|
|
427
|
+
**JSON_MODE** (default: SWAR) - Selects which mode should be used. Can be `NAIVE,SWAR,SIMD`. Note that `--enable simd` may be required.
|
|
428
|
+
|
|
429
|
+
**JSON_WRITE** (default: "") - Select a series of files to output after transform and optimization passes have completed for easy inspection. Usage: `JSON_WRITE=.path-to-file-a.ts,./path-to-file-b.ts`
|
|
430
|
+
|
|
401
431
|
### Running benchmarks locally
|
|
402
432
|
|
|
403
433
|
Benchmarks are run directly on top of v8 for more tailored control
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { bs } from "../../../lib/as-bs";
|
|
1
2
|
import { BACK_SLASH } from "../../custom/chars";
|
|
2
3
|
import { DESERIALIZE_ESCAPE_TABLE, ESCAPE_HEX_TABLE } from "../../globals/tables";
|
|
3
4
|
|
|
@@ -8,94 +9,92 @@ import { DESERIALIZE_ESCAPE_TABLE, ESCAPE_HEX_TABLE } from "../../globals/tables
|
|
|
8
9
|
* @returns number of bytes written
|
|
9
10
|
*/
|
|
10
11
|
// todo: optimize and stuff. it works, its not pretty. ideally, i'd like this to be (nearly) branchless
|
|
11
|
-
export function deserializeString_SIMD(srcStart: usize, srcEnd: usize
|
|
12
|
+
export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): void {
|
|
12
13
|
const SPLAT_92 = i16x8.splat(92); /* \ */
|
|
13
14
|
srcStart += 2;
|
|
14
15
|
srcEnd -= 2;
|
|
15
|
-
|
|
16
|
+
bs.ensureSize(u32(srcEnd - srcStart));
|
|
16
17
|
const src_end_15 = srcEnd - 15;
|
|
17
18
|
|
|
18
|
-
while (srcStart < src_end_15) {
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
// while (srcStart < src_end_15) {
|
|
20
|
+
// const block = v128.load(srcStart);
|
|
21
|
+
// v128.store(bs.offset, block);
|
|
21
22
|
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
// const backslash_indices = i16x8.eq(block, SPLAT_92);
|
|
24
|
+
// let mask = i16x8.bitmask(backslash_indices);
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
26
|
+
// while (mask != 0) {
|
|
27
|
+
// const lane_index = ctz(mask) << 1;
|
|
28
|
+
// const dst_offset = bs.offset + lane_index;
|
|
29
|
+
// const src_offset = srcStart + lane_index;
|
|
30
|
+
// const code = load<u16>(src_offset, 2);
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
32
|
+
// mask &= mask - 1;
|
|
33
|
+
// if (code == 117 && load<u32>(src_offset, 4) == 3145776) {
|
|
34
|
+
// const block = load<u32>(src_offset, 8);
|
|
35
|
+
// const codeA = block & 0xffff;
|
|
36
|
+
// const codeB = (block >> 16) & 0xffff;
|
|
37
|
+
// const escapedA = load<u8>(ESCAPE_HEX_TABLE + codeA);
|
|
38
|
+
// const escapedB = load<u8>(ESCAPE_HEX_TABLE + codeB);
|
|
39
|
+
// const escaped = (escapedA << 4) + escapedB;
|
|
40
|
+
// // console.log("Escaped:");
|
|
41
|
+
// console.log(" a: " + escapedA.toString())
|
|
42
|
+
// console.log(" b: " + escapedB.toString());
|
|
43
|
+
// console.log(" c: " + escaped.toString());
|
|
44
|
+
// console.log(" o: " + (bs.offset - dst).toString());
|
|
45
|
+
// console.log(" d: " + (dst_offset - dst).toString())
|
|
46
|
+
// console.log(" l: " + (lane_index).toString())
|
|
47
|
+
// store<u16>(dst_offset, escaped);
|
|
48
|
+
// v128.store(dst_offset, v128.load(src_offset, 4), 2);
|
|
49
|
+
// if (lane_index >= 6) {
|
|
50
|
+
// const bytes_left = lane_index - 4;
|
|
51
|
+
// srcStart += bytes_left;
|
|
52
|
+
// bs.offset += bytes_left;
|
|
53
|
+
// // console.log(" e: " + (bytes_left).toString())
|
|
54
|
+
// }
|
|
55
|
+
// bs.offset -= 10;
|
|
56
|
+
// } else {
|
|
57
|
+
// const escaped = load<u8>(DESERIALIZE_ESCAPE_TABLE + code);
|
|
58
|
+
// store<u16>(dst_offset, escaped);
|
|
59
|
+
// v128.store(dst_offset, v128.load(src_offset, 4), 2);
|
|
60
|
+
// // console.log("Escaped:");
|
|
61
|
+
// if (lane_index == 14) {
|
|
62
|
+
// srcStart += 2;
|
|
63
|
+
// } else {
|
|
64
|
+
// bs.offset -= 2;
|
|
65
|
+
// }
|
|
66
|
+
// }
|
|
67
|
+
// }
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
// srcStart += 16;
|
|
70
|
+
// bs.offset += 16;
|
|
70
71
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
}
|
|
72
|
+
// // console.log("src: " + (srcStart - changetype<usize>(src)).toString());
|
|
73
|
+
// // console.log("dst: " + (dst_ptr - dst).toString());
|
|
74
|
+
// }
|
|
74
75
|
while (srcStart < srcEnd) {
|
|
75
76
|
let code = load<u16>(srcStart);
|
|
76
|
-
if (code
|
|
77
|
+
if (code === BACK_SLASH) {
|
|
77
78
|
code = load<u16>(DESERIALIZE_ESCAPE_TABLE + load<u8>(srcStart, 2));
|
|
78
|
-
if (code
|
|
79
|
+
if (code === 117 && load<u32>(srcStart, 4) === 3145776) {
|
|
79
80
|
const block = load<u32>(srcStart, 8);
|
|
80
81
|
const codeA = block & 0xffff;
|
|
81
82
|
const codeB = (block >> 16) & 0xffff;
|
|
82
83
|
const escapedA = load<u8>(ESCAPE_HEX_TABLE + codeA);
|
|
83
84
|
const escapedB = load<u8>(ESCAPE_HEX_TABLE + codeB);
|
|
84
85
|
const escaped = (escapedA << 4) + escapedB;
|
|
85
|
-
store<u16>(
|
|
86
|
-
|
|
86
|
+
store<u16>(bs.offset, escaped);
|
|
87
|
+
bs.offset += 2;
|
|
87
88
|
srcStart += 12;
|
|
88
89
|
} else {
|
|
89
|
-
store<u16>(
|
|
90
|
-
|
|
90
|
+
store<u16>(bs.offset, code);
|
|
91
|
+
bs.offset += 2;
|
|
91
92
|
srcStart += 4;
|
|
92
93
|
}
|
|
93
94
|
} else {
|
|
94
|
-
store<u16>(
|
|
95
|
-
|
|
95
|
+
store<u16>(bs.offset, code);
|
|
96
|
+
bs.offset += 2;
|
|
96
97
|
srcStart += 2;
|
|
97
98
|
}
|
|
98
99
|
}
|
|
99
|
-
|
|
100
|
-
return dst_ptr - dst;
|
|
101
100
|
}
|
|
@@ -5,8 +5,6 @@ import { deserializeFloat } from "./float";
|
|
|
5
5
|
import { deserializeString } from "./string";
|
|
6
6
|
import { deserializeObject } from "./object";
|
|
7
7
|
import { BRACE_LEFT, BRACKET_LEFT, CHAR_N, QUOTE } from "../../custom/chars";
|
|
8
|
-
import { bs } from "../../../lib/as-bs";
|
|
9
|
-
import { deserializeString_SWAR } from "../swar/string";
|
|
10
8
|
|
|
11
9
|
export function deserializeArbitrary(srcStart: usize, srcEnd: usize, dst: usize): JSON.Value {
|
|
12
10
|
const firstChar = load<u16>(srcStart);
|
|
@@ -18,7 +16,9 @@ export function deserializeArbitrary(srcStart: usize, srcEnd: usize, dst: usize)
|
|
|
18
16
|
return JSON.Value.from(deserializeArray<JSON.Value[]>(srcStart, srcEnd, 0));
|
|
19
17
|
} else if (firstChar == 116 || firstChar == 102) return JSON.Value.from(deserializeBoolean(srcStart, srcEnd));
|
|
20
18
|
else if (firstChar == CHAR_N) {
|
|
21
|
-
|
|
19
|
+
const value = JSON.Value.from<usize>(0);
|
|
20
|
+
value.isNull = true;
|
|
21
|
+
return value;
|
|
22
22
|
}
|
|
23
23
|
return unreachable();
|
|
24
|
-
}
|
|
24
|
+
}
|
|
@@ -121,7 +121,7 @@ export function deserializeArbitraryArray(srcStart: usize, srcEnd: usize, dst: u
|
|
|
121
121
|
}
|
|
122
122
|
} else if (code == CHAR_N) {
|
|
123
123
|
if (load<u64>(srcStart) == 30399761348886638) {
|
|
124
|
-
console.log("Value (null): " + ptrToStr(srcStart, srcStart + 8));
|
|
124
|
+
// console.log("Value (null): " + ptrToStr(srcStart, srcStart + 8));
|
|
125
125
|
// @ts-ignore: type
|
|
126
126
|
out.push(JSON.__deserialize<JSON.Value>(srcStart, (srcStart += 8)));
|
|
127
127
|
// while (isSpace(load<u16>((srcStart += 2)))) {
|
package/assembly/index.d.ts
CHANGED
|
@@ -43,3 +43,12 @@ declare function serializer(..._): any;
|
|
|
43
43
|
*/
|
|
44
44
|
// @ts-ignore: type
|
|
45
45
|
declare function deserializer(..._): any;
|
|
46
|
+
|
|
47
|
+
declare enum JSONMode {
|
|
48
|
+
SWAR = 0,
|
|
49
|
+
SIMD = 1,
|
|
50
|
+
NAIVE = 2
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
declare const JSON_MODE: JSONMode;
|
|
54
|
+
declare const JSON_CACHE: bool;
|