json-as 1.1.27 → 1.2.0
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/.github/workflows/benchmark.yml +34 -26
- package/CHANGELOG.md +4 -0
- package/README.md +12 -4
- package/assembly/serialize/simd/string.ts +1 -1
- package/assembly/serialize/swar/string.ts +74 -76
- package/assembly/test.tmp.ts +127 -1
- package/assembly/test.ts +0 -4
- package/lib/as-bs.ts +13 -0
- package/package.json +2 -2
- package/assembly/__tests__/simd/string.spec.ts +0 -32
- package/data/chart01.svg +0 -258
- package/data/chart02.svg +0 -246
- package/data/chart03.svg +0 -193
- package/data/chart05.svg +0 -142
- package/dump.txt +0 -41590
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
name: Benchmark
|
|
1
|
+
name: Benchmark
|
|
2
2
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
@@ -6,6 +6,7 @@ on:
|
|
|
6
6
|
pull_request:
|
|
7
7
|
branches: [ main ]
|
|
8
8
|
workflow_dispatch:
|
|
9
|
+
repository_dispatch:
|
|
9
10
|
|
|
10
11
|
jobs:
|
|
11
12
|
benchmark:
|
|
@@ -13,52 +14,59 @@ jobs:
|
|
|
13
14
|
steps:
|
|
14
15
|
- name: Checkout Repository
|
|
15
16
|
uses: actions/checkout@v3
|
|
17
|
+
continue-on-error: false
|
|
16
18
|
|
|
17
19
|
- name: Setup Bun
|
|
18
20
|
uses: oven-sh/setup-bun@v1
|
|
21
|
+
continue-on-error: true
|
|
19
22
|
|
|
20
23
|
- name: Install Dependencies
|
|
21
|
-
run: bun install
|
|
24
|
+
run: bun install || true
|
|
25
|
+
continue-on-error: true
|
|
22
26
|
|
|
23
27
|
- name: Install JSVU and V8
|
|
24
28
|
run: |
|
|
25
|
-
bunx jsvu --os=linux64 --engines=v8
|
|
29
|
+
bunx jsvu --os=linux64 --engines=v8 || true
|
|
26
30
|
echo "${HOME}/.jsvu/bin" >> $GITHUB_PATH
|
|
27
|
-
|
|
31
|
+
continue-on-error: true
|
|
28
32
|
|
|
29
33
|
- name: Install Binaryen
|
|
30
|
-
run:
|
|
31
|
-
|
|
32
|
-
sudo apt-get install -y binaryen
|
|
33
|
-
echo "Binaryen installed."
|
|
34
|
+
run: sudo apt-get update && sudo apt-get install -y binaryen || true
|
|
35
|
+
continue-on-error: true
|
|
34
36
|
|
|
35
37
|
- name: Run AssemblyScript Benchmarks
|
|
36
|
-
run:
|
|
37
|
-
|
|
38
|
-
./run-bench.as.sh
|
|
38
|
+
run: ./run-bench.as.sh || true
|
|
39
|
+
continue-on-error: true
|
|
39
40
|
|
|
40
41
|
- name: Run JavaScript Benchmarks
|
|
41
|
-
run:
|
|
42
|
-
|
|
43
|
-
./run-bench.js.sh
|
|
42
|
+
run: ./run-bench.js.sh || true
|
|
43
|
+
continue-on-error: true
|
|
44
44
|
|
|
45
|
-
- name: Build
|
|
45
|
+
- name: Build Charts
|
|
46
46
|
run: |
|
|
47
|
-
mkdir -p
|
|
48
|
-
bun ./scripts/build-chart01.ts
|
|
49
|
-
bun ./scripts/build-chart02.ts
|
|
47
|
+
mkdir -p data
|
|
48
|
+
bun ./scripts/build-chart01.ts || true
|
|
49
|
+
bun ./scripts/build-chart02.ts || true
|
|
50
|
+
continue-on-error: true
|
|
50
51
|
|
|
51
|
-
- name:
|
|
52
|
+
- name: Commit Charts to docs branch
|
|
53
|
+
continue-on-error: true
|
|
52
54
|
run: |
|
|
55
|
+
set -e
|
|
53
56
|
git config user.name "github-actions"
|
|
54
57
|
git config user.email "actions@github.com"
|
|
55
58
|
|
|
56
|
-
|
|
57
|
-
git
|
|
58
|
-
git
|
|
59
|
+
# Ensure the docs branch exists locally
|
|
60
|
+
git fetch origin docs || true
|
|
61
|
+
if git rev-parse --verify docs >/dev/null 2>&1; then
|
|
62
|
+
git branch -D docs || true
|
|
63
|
+
fi
|
|
64
|
+
git checkout -b docs origin/docs || git checkout --orphan docs || true
|
|
65
|
+
|
|
66
|
+
# Copy generated charts
|
|
59
67
|
mkdir -p charts
|
|
60
|
-
|
|
68
|
+
cp data/*.svg charts/ || true
|
|
61
69
|
|
|
62
|
-
git add charts
|
|
63
|
-
git commit -m "Update benchmark
|
|
64
|
-
git push origin docs
|
|
70
|
+
git add charts/ || true
|
|
71
|
+
git commit -m "Update benchmark charts [skip ci]" || echo "No changes to commit"
|
|
72
|
+
git push origin docs --force || true
|
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -380,15 +380,23 @@ This allows custom serialization while maintaining a generic interface for the l
|
|
|
380
380
|
|
|
381
381
|
## Performance
|
|
382
382
|
|
|
383
|
-
The `json-as` library
|
|
383
|
+
The `json-as` library is engineered for **multi-GB/s processing speeds**, leveraging SIMD and SWAR optimizations along with highly efficient transformations. The charts below highlight key performance metrics such as build time, operations-per-second, and throughput.
|
|
384
|
+
|
|
385
|
+
You can **re-run the benchmarks** at any time by clicking the button below. After the workflow completes, refresh this page to view updated results.
|
|
386
|
+
|
|
387
|
+
[](https://github.com/JairusSW/json-as/actions/workflows/benchmark.yml)
|
|
384
388
|
|
|
385
389
|
### Comparison to JavaScript
|
|
386
390
|
|
|
387
|
-
|
|
391
|
+
The following charts compare JSON-AS (both SWAR and SIMD variants) against JavaScript's native `JSON` implementation. Benchmarks were conducted in a GitHub Actions environment. On modern hardware, you may see even higher throughput.
|
|
392
|
+
|
|
393
|
+
> Note: Benchmarks reflect the **latest version**. Older versions may show different performance.
|
|
394
|
+
|
|
395
|
+
<img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/data/chart01.svg" alt="Performance Chart 1">
|
|
388
396
|
|
|
389
|
-
<img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/data/
|
|
397
|
+
<img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/data/chart02.svg" alt="Performance Chart 2">
|
|
390
398
|
|
|
391
|
-
|
|
399
|
+
> 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.
|
|
392
400
|
|
|
393
401
|
### Running benchmarks locally
|
|
394
402
|
|
|
@@ -37,7 +37,7 @@ export function serializeString_SIMD(src: string): void {
|
|
|
37
37
|
|
|
38
38
|
while (mask != 0) {
|
|
39
39
|
const lane_index = ctz(mask) << 1;
|
|
40
|
-
// console.log("lane
|
|
40
|
+
// console.log("lane: " + (lane_index >= 8 ? (lane_index - 8).toString():lane_index.toString()));
|
|
41
41
|
const src_offset = srcStart + lane_index;
|
|
42
42
|
const code = load<u16>(src_offset) << 2;
|
|
43
43
|
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + code);
|
|
@@ -2,7 +2,6 @@ import { bs } from "../../../lib/as-bs";
|
|
|
2
2
|
import { BACK_SLASH } from "../../custom/chars";
|
|
3
3
|
import { SERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
4
4
|
import { bytes } from "../../util/bytes";
|
|
5
|
-
import { serializeString } from "../simple/string";
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
// @ts-ignore: decorator allowed
|
|
@@ -22,90 +21,89 @@ import { serializeString } from "../simple/string";
|
|
|
22
21
|
// @ts-ignore: decorator allowed
|
|
23
22
|
@lazy const U00_MARKER = 13511005048209500;
|
|
24
23
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
24
|
+
export function serializeString_SWAR(src: string): void {
|
|
25
|
+
const srcSize = bytes(src);
|
|
26
|
+
let srcStart = changetype<usize>(src);
|
|
27
|
+
const srcEnd = srcStart + srcSize;
|
|
28
|
+
const srcEnd8 = srcEnd - 8;
|
|
28
29
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
// const srcEnd8 = srcEnd - 8;
|
|
30
|
+
bs.proposeSize(srcSize + 4);
|
|
31
|
+
store<u16>(bs.offset, 34); // "
|
|
32
|
+
bs.offset += 2;
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
while (srcStart <= srcEnd8) {
|
|
35
|
+
const block = load<u64>(srcStart);
|
|
36
|
+
store<u64>(bs.offset, block);
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
// const block = load<u64>(srcStart);
|
|
40
|
-
// store<u64>(bs.offset, block);
|
|
38
|
+
let mask = v64x4_should_escape(block);
|
|
41
39
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
while (mask != 0) {
|
|
41
|
+
const lane_index = usize(ctz(mask) >> 3); // 0 2 4 6
|
|
42
|
+
const src_offset = srcStart + lane_index;
|
|
43
|
+
// const dst_offset = bs.offset + lane_index;
|
|
44
|
+
const code = load<u16>(src_offset) << 2;
|
|
45
|
+
// console.log("lane: " + lane_index.toString())
|
|
46
|
+
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + code);
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
48
|
+
mask = mask & ~(0xFF << (lane_index << 3));
|
|
49
|
+
if ((escaped & 0xffff) != BACK_SLASH) {
|
|
50
|
+
bs.growSize(10);
|
|
51
|
+
const dst_offset = bs.offset + lane_index;
|
|
52
|
+
store<u64>(dst_offset, U00_MARKER);
|
|
53
|
+
store<u32>(dst_offset, escaped, 8);
|
|
54
|
+
store<u64>(dst_offset, load<u64>(src_offset, 2), 12); // unsafe. can overflow here
|
|
55
|
+
// memory.copy(dst_offset + 12, src_offset + 2, (4 - lane_index) << 1);
|
|
56
|
+
bs.offset += 10;
|
|
57
|
+
} else {
|
|
58
|
+
bs.growSize(2);
|
|
59
|
+
const dst_offset = bs.offset + lane_index;
|
|
60
|
+
store<u32>(dst_offset, escaped);
|
|
61
|
+
store<u64>(dst_offset, load<u64>(src_offset, 2), 4);
|
|
62
|
+
// memory.copy(dst_offset + 4, src_offset + 2, (4 - lane_index) << 1);
|
|
63
|
+
bs.offset += 2;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
54
66
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
// store<u32>(dst_offset, escaped, 8);
|
|
59
|
-
// // store<u64>(dst_offset, load<u64>(src_offset, 2), 12); // unsafe. can overflow here
|
|
60
|
-
// memory.copy(dst_offset + 12, src_offset + 2, (4 - lane_index) << 1);
|
|
61
|
-
// bs.offset += 10;
|
|
62
|
-
// } else {
|
|
63
|
-
// bs.growSize(2);
|
|
64
|
-
// store<u32>(dst_offset, escaped);
|
|
65
|
-
// // store<u64>(dst_offset, load<u64>(src_offset, 2), 4);
|
|
66
|
-
// memory.copy(dst_offset + 4, src_offset + 2, (4 - lane_index) << 1);
|
|
67
|
-
// bs.offset += 2;
|
|
68
|
-
// }
|
|
67
|
+
srcStart += 8;
|
|
68
|
+
bs.offset += 8;
|
|
69
|
+
}
|
|
69
70
|
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
while (srcStart <= srcEnd - 2) {
|
|
72
|
+
const code = load<u16>(srcStart);
|
|
73
|
+
if (code == 92 || code == 34 || code < 32) {
|
|
74
|
+
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + (code << 2));
|
|
75
|
+
if ((escaped & 0xffff) != BACK_SLASH) {
|
|
76
|
+
bs.growSize(10);
|
|
77
|
+
store<u64>(bs.offset, U00_MARKER);
|
|
78
|
+
store<u32>(bs.offset, escaped, 8);
|
|
79
|
+
bs.offset += 12;
|
|
80
|
+
} else {
|
|
81
|
+
bs.growSize(2);
|
|
82
|
+
store<u32>(bs.offset, escaped);
|
|
83
|
+
bs.offset += 4;
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
store<u16>(bs.offset, code);
|
|
87
|
+
bs.offset += 2;
|
|
88
|
+
}
|
|
89
|
+
srcStart += 2;
|
|
90
|
+
}
|
|
72
91
|
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
// }
|
|
76
|
-
|
|
77
|
-
// while (srcStart <= srcEnd - 2) {
|
|
78
|
-
// const code = load<u16>(srcStart);
|
|
79
|
-
// if (code == 92 || code == 34 || code < 32) {
|
|
80
|
-
// const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + (code << 2));
|
|
81
|
-
// if ((escaped & 0xffff) != BACK_SLASH) {
|
|
82
|
-
// bs.growSize(10);
|
|
83
|
-
// store<u64>(bs.offset, U00_MARKER);
|
|
84
|
-
// store<u32>(bs.offset, escaped, 8);
|
|
85
|
-
// bs.offset += 12;
|
|
86
|
-
// } else {
|
|
87
|
-
// bs.growSize(2);
|
|
88
|
-
// store<u32>(bs.offset, escaped);
|
|
89
|
-
// bs.offset += 4;
|
|
90
|
-
// }
|
|
91
|
-
// } else {
|
|
92
|
-
// store<u16>(bs.offset, code);
|
|
93
|
-
// bs.offset += 2;
|
|
94
|
-
// }
|
|
95
|
-
// srcStart += 2;
|
|
96
|
-
// }
|
|
97
|
-
|
|
98
|
-
// store<u16>(bs.offset, 34); // "
|
|
99
|
-
// bs.offset += 2;
|
|
92
|
+
store<u16>(bs.offset, 34); // "
|
|
93
|
+
bs.offset += 2;
|
|
100
94
|
}
|
|
101
95
|
|
|
102
96
|
// @ts-ignore: decorators allowed
|
|
103
|
-
@inline function
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
97
|
+
@inline function v64x4_should_escape(x: u64): u64 {
|
|
98
|
+
x = x ^ 0xFF00FF00FF00FF00;
|
|
99
|
+
|
|
100
|
+
const is_ascii: u64 = HIGHS & ~x;
|
|
101
|
+
const xor2: u64 = x ^ 0x0202020202020202;
|
|
102
|
+
const lt32_or_eq34: u64 = xor2 - 0x2121212121212121;
|
|
103
|
+
const sub92: u64 = x ^ 0x5C5C5C5C5C5C5C5C;
|
|
104
|
+
const eq92: u64 = sub92 - ONES;
|
|
105
|
+
|
|
106
|
+
const high_bits: u64 = (lt32_or_eq34 | eq92) & is_ascii;
|
|
107
|
+
|
|
108
|
+
return high_bits
|
|
111
109
|
}
|
package/assembly/test.tmp.ts
CHANGED
|
@@ -1,7 +1,133 @@
|
|
|
1
1
|
import {
|
|
2
2
|
JSON
|
|
3
3
|
} from ".";
|
|
4
|
+
import {
|
|
5
|
+
bs
|
|
6
|
+
} from "../lib/as-bs";
|
|
4
7
|
import {
|
|
5
8
|
expect
|
|
6
9
|
} from "./__tests__/lib";
|
|
7
|
-
|
|
10
|
+
import {
|
|
11
|
+
serializeString_SIMD
|
|
12
|
+
} from "./serialize/simd/string";
|
|
13
|
+
import {
|
|
14
|
+
serializeString_SWAR
|
|
15
|
+
} from "./serialize/swar/string";
|
|
16
|
+
@lazy
|
|
17
|
+
const ONES: u64 = 72340172838076673;
|
|
18
|
+
@lazy
|
|
19
|
+
const HIGHS: u64 = -9187201950435737472;
|
|
20
|
+
@lazy
|
|
21
|
+
const LANE_MASK_LOW: u64 = 71777214294589695;
|
|
22
|
+
@inline
|
|
23
|
+
function json_escapable_byte_mask(x: u64): u64 {
|
|
24
|
+
x = x ^ -71777214294589696;
|
|
25
|
+
const is_ascii: u64 = HIGHS & ~x;
|
|
26
|
+
const xor2: u64 = x ^ 144680345676153346;
|
|
27
|
+
const lt32_or_eq34: u64 = xor2 - 2387225703656530209;
|
|
28
|
+
const sub92: u64 = x ^ 6655295901103053916;
|
|
29
|
+
const eq92: u64 = sub92 - ONES;
|
|
30
|
+
const high_bits: u64 = (lt32_or_eq34 | eq92) & is_ascii;
|
|
31
|
+
return high_bits;
|
|
32
|
+
}
|
|
33
|
+
function to_u64(s: string): u64 {
|
|
34
|
+
return load<u64>(changetype<usize>(s));
|
|
35
|
+
}
|
|
36
|
+
export function mask_to_string(mask: u64): string {
|
|
37
|
+
let result = "0x";
|
|
38
|
+
for (let i = 7; i >= 0; i--) {
|
|
39
|
+
const byte = u8((mask >> (i * 8)) & 255);
|
|
40
|
+
const hi = (byte >> 4) & 15;
|
|
41
|
+
const lo = byte & 15;
|
|
42
|
+
result += hi < 10 ? String.fromCharCode(48 + hi) : String.fromCharCode(55 + hi);
|
|
43
|
+
result += lo < 10 ? String.fromCharCode(48 + lo) : String.fromCharCode(55 + lo);
|
|
44
|
+
result += " ";
|
|
45
|
+
}
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
function test_mask(input: string, expected_mask: u64, description: string): void {
|
|
49
|
+
const mask = json_escapable_byte_mask(to_u64(input));
|
|
50
|
+
const pass = mask == expected_mask;
|
|
51
|
+
console.log((pass ? "✓ " : "✗ ") + description);
|
|
52
|
+
if (!pass) {
|
|
53
|
+
console.log(" Input: \"" + input + "\"");
|
|
54
|
+
console.log(" Expected: " + mask_to_string(expected_mask));
|
|
55
|
+
console.log(" Got: " + mask_to_string(mask));
|
|
56
|
+
process.exit(1);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
console.log("=== No Escapes Needed ===");
|
|
60
|
+
test_mask("abcd", 0, "Regular ASCII letters");
|
|
61
|
+
test_mask("ABCD", 0, "Uppercase letters");
|
|
62
|
+
test_mask("1234", 0, "Numbers");
|
|
63
|
+
test_mask("test", 0, "Common word");
|
|
64
|
+
test_mask("____", 0, "Underscores");
|
|
65
|
+
test_mask("....", 0, "Periods");
|
|
66
|
+
test_mask("!#$%", 0, "Safe symbols");
|
|
67
|
+
test_mask("&*()", 0, "More symbols");
|
|
68
|
+
test_mask("+,-.", 0, "Math symbols");
|
|
69
|
+
console.log();
|
|
70
|
+
console.log("=== Quote Character (34 / 0x22) ===");
|
|
71
|
+
test_mask("\"abc", 128, "Quote at position 0");
|
|
72
|
+
test_mask("a\"bc", 8388608, "Quote at position 1");
|
|
73
|
+
test_mask("ab\"c", 549755813888, "Quote at position 2");
|
|
74
|
+
test_mask("abc\"", 36028797018963968, "Quote at position 3");
|
|
75
|
+
test_mask("\"\"\"\"", 36029346783166592, "All quotes");
|
|
76
|
+
console.log();
|
|
77
|
+
console.log("=== Backslash Character (92 / 0x5C) ===");
|
|
78
|
+
test_mask("\babc", 128, "Backslash at position 0");
|
|
79
|
+
test_mask("a\bbc", 8388608, "Backslash at position 1");
|
|
80
|
+
test_mask("ab\bc", 549755813888, "Backslash at position 2");
|
|
81
|
+
test_mask("abc\b", 36028797018963968, "Backslash at position 3");
|
|
82
|
+
test_mask("\b\b\b\b", 36029346783166592, "All backslashes");
|
|
83
|
+
console.log();
|
|
84
|
+
console.log("=== Control Characters (< 32) ===");
|
|
85
|
+
test_mask("\0abc", 128, "Null character at position 0");
|
|
86
|
+
test_mask("\nabc", 128, "Newline at position 0");
|
|
87
|
+
test_mask("\rabc", 128, "Carriage return at position 0");
|
|
88
|
+
test_mask("\tabc", 128, "Tab at position 0");
|
|
89
|
+
test_mask("a\nbc", 8388608, "Newline at position 1");
|
|
90
|
+
test_mask("ab\nc", 549755813888, "Newline at position 2");
|
|
91
|
+
test_mask("abc\n", 36028797018963968, "Newline at position 3");
|
|
92
|
+
console.log();
|
|
93
|
+
console.log("=== Boundary Cases ===");
|
|
94
|
+
test_mask("abc", 128, "Character 31 (should escape)");
|
|
95
|
+
test_mask(" abc", 0, "Space (32, should NOT escape)");
|
|
96
|
+
test_mask("!abc", 0, "Character 33 (should NOT escape)");
|
|
97
|
+
console.log();
|
|
98
|
+
console.log("=== Mixed Scenarios ===");
|
|
99
|
+
test_mask("\"\nabc", 128 | 8388608, "Quote + newline");
|
|
100
|
+
test_mask("\"\babc", 128 | 8388608, "Quote + backslash");
|
|
101
|
+
test_mask("\"\b\nabc", 128 | 8388608 | 549755813888, "Quote + backslash + newline");
|
|
102
|
+
test_mask("a\"\tb", 8388608 | 549755813888, "Quote + tab at pos 1-2");
|
|
103
|
+
console.log();
|
|
104
|
+
console.log("=== Multiple Escapes ===");
|
|
105
|
+
test_mask("\"\"\b\b", 8388736 | 36029346774777856, "All 4 positions need escape");
|
|
106
|
+
test_mask("a\"b\b", 8388608 | 36028797018963968, "Quote at pos 1, backslash at pos 3");
|
|
107
|
+
test_mask("\"a\bb", 128 | 549755813888, "Quote at pos 0, backslash at pos 2");
|
|
108
|
+
console.log();
|
|
109
|
+
console.log("=== Edge Cases ===");
|
|
110
|
+
test_mask("", 36029346783166592, "All control characters (1-4)");
|
|
111
|
+
test_mask(" ", 128 | 8388608, "Characters 30-31, then two spaces");
|
|
112
|
+
test_mask("]}~", 0, "High safe ASCII (125-127)");
|
|
113
|
+
console.log();
|
|
114
|
+
console.log("=== Specific JSON Escape Sequences ===");
|
|
115
|
+
test_mask("\f\n\r", 36029346783166592, "Backspace, form feed, newline, carriage return");
|
|
116
|
+
test_mask("\"\b\f", 36029346783166592, "Quote, backslash, backspace, form feed");
|
|
117
|
+
console.log();
|
|
118
|
+
function get_lane(mask: u64): u64 {
|
|
119
|
+
return ctz(mask) >> 3;
|
|
120
|
+
}
|
|
121
|
+
function clearLane(mask: u64, lane_index: u32): u64 {
|
|
122
|
+
return mask & ~(255 << (lane_index << 3));
|
|
123
|
+
}
|
|
124
|
+
expect(get_lane(128)).toBe(0);
|
|
125
|
+
expect(get_lane(8388608)).toBe(2);
|
|
126
|
+
expect(get_lane(549755813888)).toBe(4);
|
|
127
|
+
expect(get_lane(36028797018963968)).toBe(6);
|
|
128
|
+
let x: u64 = 36029346783166592;
|
|
129
|
+
expect(get_lane(x = clearLane(x, 0))).toBe(2);
|
|
130
|
+
expect(get_lane(x = clearLane(x, 2))).toBe(4);
|
|
131
|
+
expect(get_lane(x = clearLane(x, 4))).toBe(6);
|
|
132
|
+
serializeString_SWAR("\0\t\n\v\f\rabcdefg");
|
|
133
|
+
expect(bs.out<string>()).toBe("\"\bu0000\bu0001\bu0002\bu0003\bu0004\bu0005\bu0006\bu0007\bb\bt\bn\bu000b\bf\br\bu000eabcdefg\"");
|
package/assembly/test.ts
CHANGED
package/lib/as-bs.ts
CHANGED
|
@@ -167,6 +167,19 @@ export namespace bs {
|
|
|
167
167
|
return changetype<T>(_out);
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
/**
|
|
171
|
+
* Copies the buffer's content to a new object of a specified type.
|
|
172
|
+
* @returns The new object containing the buffer's content.
|
|
173
|
+
*/
|
|
174
|
+
// @ts-ignore: Decorator valid here
|
|
175
|
+
@inline export function view<T>(): T {
|
|
176
|
+
const len = offset - changetype<usize>(buffer);
|
|
177
|
+
// @ts-ignore: exists
|
|
178
|
+
const _out = __new(len, idof<T>());
|
|
179
|
+
memory.copy(_out, changetype<usize>(buffer), len);
|
|
180
|
+
return changetype<T>(_out);
|
|
181
|
+
}
|
|
182
|
+
|
|
170
183
|
/**
|
|
171
184
|
* Copies the buffer's content to a given destination pointer.
|
|
172
185
|
* Optionally shrinks the buffer after copying.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "json-as",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"author": "Jairus Tanaka",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -59,7 +59,7 @@
|
|
|
59
59
|
"test": "bash ./run-tests.sh",
|
|
60
60
|
"bench:as": "bash ./run-bench.as.sh",
|
|
61
61
|
"bench:js": "bash ./run-bench.js.sh",
|
|
62
|
-
"build:test": "rm -rf ./build/ && JSON_DEBUG=2 JSON_WRITE=assembly/test.ts asc assembly/test.ts --transform ./transform -o ./build/test.wasm --textFile ./build/test.wat --debug --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json",
|
|
62
|
+
"build:test": "rm -rf ./build/ && JSON_DEBUG=2 JSON_WRITE=assembly/test.ts asc assembly/test.ts --transform ./transform -o ./build/test.wasm --textFile ./build/test.wat --enable simd --debug --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json",
|
|
63
63
|
"build:tmp:test": "rm -rf ./build/ && JSON_DEBUG=1 asc assembly/test.tmp.ts -o ./build/test.wasm --textFile ./build/test.wat --enable simd --debug --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json",
|
|
64
64
|
"build:test:wine": "JSON_DEBUG=1 JSON_WRITE=assembly/test.ts NODE_SKIP_PLATFORM_CHECK=1 wine ~/.win-bin/node/node.exe ./node_modules/assemblyscript/bin/asc.js assembly/test.ts --transform ./transform -o ./build/test.wasm --textFile ./build/test.wat --debug --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json",
|
|
65
65
|
"test:wasmtime": "wasmtime ./build/test.wasm",
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
// import { describe, expect, run } from "as-test/assembly";
|
|
2
|
-
// import { serializeString_SIMD } from "../../serialize/simd/string";
|
|
3
|
-
// import { deserializeString_SIMD } from "../../deserialize/simd/string";
|
|
4
|
-
|
|
5
|
-
// const out = changetype<usize>(new ArrayBuffer(512));
|
|
6
|
-
|
|
7
|
-
// const serialize_simd = (data: string): string => String.UTF16.decodeUnsafe(out, serializeString_SIMD(data, out));
|
|
8
|
-
// const deserialize_simd = (data: string): string => String.UTF16.decodeUnsafe(out, deserializeString_SIMD(data, out));
|
|
9
|
-
// describe("Should serialize strings", () => {
|
|
10
|
-
// expect(serialize_simd("abcdefg")).toBe('"abcdefg"');
|
|
11
|
-
|
|
12
|
-
// expect(serialize_simd('st"ring" w""ith quotes"')).toBe('"st\\"ring\\" w\\"\\"ith quotes\\""');
|
|
13
|
-
|
|
14
|
-
// expect(serialize_simd('string "with random spa\nces and \nnewlines\n\n\n')).toBe('"string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"');
|
|
15
|
-
|
|
16
|
-
// expect(serialize_simd('string with colon : comma , brace [ ] bracket { } and quote " and other quote "')).toBe('"string with colon : comma , brace [ ] bracket { } and quote \\" and other quote \\""');
|
|
17
|
-
|
|
18
|
-
// expect(serialize_simd("\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"');
|
|
19
|
-
// });
|
|
20
|
-
|
|
21
|
-
// describe("Should deserialize strings", () => {
|
|
22
|
-
// expect(deserialize_simd('"abcdefg"')).toBe("abcdefg");
|
|
23
|
-
// expect(deserialize_simd('"st\\"ring\\" w\\"\\"ith quotes\\""')).toBe('st"ring" w""ith quotes"');
|
|
24
|
-
|
|
25
|
-
// expect(deserialize_simd('"string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"')).toBe('string "with random spa\nces and \nnewlines\n\n\n');
|
|
26
|
-
|
|
27
|
-
// expect(deserialize_simd('"string with colon : comma , brace [ ] bracket { } and quote \\" and other quote \\""')).toBe('string with colon : comma , brace [ ] bracket { } and quote " and other quote "');
|
|
28
|
-
|
|
29
|
-
// expect(deserialize_simd('"\\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"')).toBe("\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");
|
|
30
|
-
// });
|
|
31
|
-
|
|
32
|
-
// run();
|