json-as 1.1.26 → 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 +72 -0
- package/.prettierrc.json +1 -0
- package/CHANGELOG.md +4 -0
- package/README.md +44 -59
- package/assembly/__benches__/abc.bench.ts +5 -9
- package/assembly/__benches__/large.bench.ts +114 -120
- package/assembly/__benches__/lib/bench.ts +44 -1
- package/assembly/__benches__/medium.bench.ts +108 -29
- package/assembly/__benches__/small.bench.ts +28 -18
- package/assembly/__benches__/throughput.ts +172 -0
- package/assembly/__benches__/vec3.bench.ts +8 -3
- package/assembly/__tests__/string.spec.ts +4 -0
- package/assembly/deserialize/simple/arbitrary.ts +5 -2
- package/assembly/deserialize/simple/object.ts +3 -1
- package/assembly/deserialize/swar/string.ts +62 -62
- package/assembly/index.ts +6 -2
- package/assembly/serialize/simd/string.ts +1 -1
- package/assembly/serialize/swar/number.ts +0 -0
- package/assembly/serialize/swar/string.ts +25 -25
- package/assembly/test.tmp.ts +124 -2
- package/assembly/test.ts +0 -7
- package/bench/abc.bench.ts +6 -8
- package/bench/large.bench.ts +119 -118
- package/bench/lib/bench.d.ts +27 -0
- package/bench/lib/bench.js +53 -0
- package/bench/lib/chart.ts +217 -0
- package/bench/medium.bench.ts +55 -30
- package/bench/runners/assemblyscript.js +6 -1
- package/bench/small.bench.ts +7 -3
- package/bench/throughput.ts +87 -0
- package/bench/tsconfig.json +3 -2
- package/bench/vec3.bench.ts +7 -3
- package/ci/bench/runners/assemblyscript.js +29 -0
- package/ci/run-bench.as.sh +63 -0
- package/lib/as-bs.ts +13 -0
- package/package.json +3 -1
- package/run-bench.as.sh +21 -13
- package/run-bench.js.sh +9 -7
- package/run-tests.sh +34 -14
- package/scripts/build-chart01.ts +38 -0
- package/scripts/build-chart02.ts +38 -0
- package/scripts/build-chart03.ts +139 -0
- package/scripts/build-chart05.ts +47 -0
- package/scripts/generate-as-class.ts +50 -0
- package/scripts/lib/bench-utils.ts +308 -0
- package/transform/lib/index.js +2 -2
- package/transform/lib/index.js.map +1 -1
- package/transform/src/index.ts +2 -2
- package/assembly/__tests__/simd/string.spec.ts +0 -32
- /package/{bench → ci/bench}/lib/bench.ts +0 -0
|
@@ -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
|
|
|
@@ -23,68 +24,69 @@ import { DESERIALIZE_ESCAPE_TABLE, ESCAPE_HEX_TABLE } from "../../globals/tables
|
|
|
23
24
|
* @returns number of bytes written
|
|
24
25
|
*/
|
|
25
26
|
// todo: optimize and stuff. it works, its not pretty. ideally, i'd like this to be (nearly) branchless
|
|
26
|
-
export function deserializeString_SWAR(srcStart: usize, srcEnd: usize
|
|
27
|
+
export function deserializeString_SWAR(srcStart: usize, srcEnd: usize): void {
|
|
27
28
|
srcStart += 2;
|
|
28
29
|
srcEnd -= 2;
|
|
29
|
-
|
|
30
|
-
const src_end_15 = srcEnd - 15;
|
|
30
|
+
bs.proposeSize(u32(srcEnd - srcStart));
|
|
31
31
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
// const src_end_15 = srcEnd - 15;
|
|
33
|
+
// while (srcStart < src_end_15) {
|
|
34
|
+
// const block = load<u64>(srcStart);
|
|
35
|
+
// store<u64>(dst_ptr, block);
|
|
35
36
|
|
|
36
|
-
|
|
37
|
-
let mask = i16x8.bitmask(backslash_indices);
|
|
37
|
+
// let mask = v64x4_eq(block, BACKSLASH_MASK);
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
39
|
+
// while (mask != 0) {
|
|
40
|
+
// const lane_index = usize(ctz(mask) >> 3);
|
|
41
|
+
// const dst_offset = dst_ptr + lane_index;
|
|
42
|
+
// const src_offset = srcStart + lane_index;
|
|
43
|
+
// const code = load<u16>(src_offset, 2);
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
45
|
+
// mask &= mask - 1;
|
|
46
|
+
// if (code == 117 && load<u32>(src_offset, 4) == 3145776) {
|
|
47
|
+
// const block = load<u32>(src_offset, 8);
|
|
48
|
+
// const codeA = block & 0xffff;
|
|
49
|
+
// const codeB = (block >> 16) & 0xffff;
|
|
50
|
+
// const escapedA = load<u8>(ESCAPE_HEX_TABLE + codeA);
|
|
51
|
+
// const escapedB = load<u8>(ESCAPE_HEX_TABLE + codeB);
|
|
52
|
+
// const escaped = (escapedA << 4) + escapedB;
|
|
53
|
+
// // console.log("Escaped:");
|
|
54
|
+
// // console.log(" a: " + escapedA.toString())
|
|
55
|
+
// // console.log(" b: " + escapedB.toString());
|
|
56
|
+
// // console.log(" c: " + escaped.toString());
|
|
57
|
+
// // console.log(" o: " + (dst_ptr - dst).toString());
|
|
58
|
+
// // console.log(" d: " + (dst_offset - dst).toString())
|
|
59
|
+
// // console.log(" l: " + (lane_index).toString())
|
|
60
|
+
// store<u16>(dst_offset, escaped);
|
|
61
|
+
// memory.copy(dst_offset + 2, src_offset + 4, (4 - lane_index) << 1);
|
|
62
|
+
// // v128.store(dst_offset, v128.load(src_offset, 4), 2);
|
|
63
|
+
// if (lane_index >= 6) {
|
|
64
|
+
// const bytes_left = lane_index - 4;
|
|
65
|
+
// srcStart += bytes_left;
|
|
66
|
+
// dst_ptr += bytes_left;
|
|
67
|
+
// // console.log(" e: " + (bytes_left).toString())
|
|
68
|
+
// }
|
|
69
|
+
// dst_ptr -= 10;
|
|
70
|
+
// } else {
|
|
71
|
+
// const escaped = load<u8>(DESERIALIZE_ESCAPE_TABLE + code);
|
|
72
|
+
// store<u16>(dst_offset, escaped);
|
|
73
|
+
// memory.copy(dst_offset + 2, src_offset + 4, (4 - lane_index) << 1);
|
|
74
|
+
// // v128.store(dst_offset, v128.load(src_offset, 4), 2);
|
|
75
|
+
// // console.log("Escaped:");
|
|
76
|
+
// if (lane_index == 14) {
|
|
77
|
+
// srcStart += 2;
|
|
78
|
+
// } else {
|
|
79
|
+
// dst_ptr -= 2;
|
|
80
|
+
// }
|
|
81
|
+
// }
|
|
82
|
+
// }
|
|
81
83
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
// srcStart += 16;
|
|
85
|
+
// dst_ptr += 16;
|
|
84
86
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
87
|
+
// // console.log("src: " + (srcStart - changetype<usize>(src)).toString());
|
|
88
|
+
// // console.log("dst: " + (dst_ptr - dst).toString());
|
|
89
|
+
// }
|
|
88
90
|
while (srcStart < srcEnd) {
|
|
89
91
|
let code = load<u16>(srcStart);
|
|
90
92
|
if (code == BACK_SLASH) {
|
|
@@ -96,22 +98,20 @@ export function deserializeString_SWAR(srcStart: usize, srcEnd: usize, dst: usiz
|
|
|
96
98
|
const escapedA = load<u8>(ESCAPE_HEX_TABLE + codeA);
|
|
97
99
|
const escapedB = load<u8>(ESCAPE_HEX_TABLE + codeB);
|
|
98
100
|
const escaped = (escapedA << 4) + escapedB;
|
|
99
|
-
store<u16>(
|
|
100
|
-
|
|
101
|
+
store<u16>(bs.offset, escaped);
|
|
102
|
+
bs.offset += 2;
|
|
101
103
|
srcStart += 12;
|
|
102
104
|
} else {
|
|
103
|
-
store<u16>(
|
|
104
|
-
|
|
105
|
+
store<u16>(bs.offset, code);
|
|
106
|
+
bs.offset += 2;
|
|
105
107
|
srcStart += 4;
|
|
106
108
|
}
|
|
107
109
|
} else {
|
|
108
|
-
store<u16>(
|
|
109
|
-
|
|
110
|
+
store<u16>(bs.offset, code);
|
|
111
|
+
bs.offset += 2;
|
|
110
112
|
srcStart += 2;
|
|
111
113
|
}
|
|
112
114
|
}
|
|
113
|
-
|
|
114
|
-
return dst_ptr - dst;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
// @ts-ignore: decorators allowed
|
package/assembly/index.ts
CHANGED
|
@@ -11,7 +11,7 @@ import { deserializeFloat } from "./deserialize/simple/float";
|
|
|
11
11
|
import { deserializeMap } from "./deserialize/simple/map";
|
|
12
12
|
import { deserializeDate } from "./deserialize/simple/date";
|
|
13
13
|
import { deserializeInteger } from "./deserialize/simple/integer";
|
|
14
|
-
import { deserializeString } from "./deserialize/simple/string";
|
|
14
|
+
// import { deserializeString } from "./deserialize/simple/string";
|
|
15
15
|
import { serializeArbitrary } from "./serialize/simple/arbitrary";
|
|
16
16
|
|
|
17
17
|
import { NULL_WORD, QUOTE } from "./custom/chars";
|
|
@@ -29,6 +29,8 @@ import { serializeRaw } from "./serialize/simple/raw";
|
|
|
29
29
|
import { deserializeRaw } from "./deserialize/simple/raw";
|
|
30
30
|
import { serializeString_SIMD } from "./serialize/simd/string";
|
|
31
31
|
import { serializeString_SWAR } from "./serialize/swar/string";
|
|
32
|
+
import { deserializeString_SWAR } from "./deserialize/swar/string";
|
|
33
|
+
import { deserializeString } from "./deserialize/simple/string";
|
|
32
34
|
// import { deserializeString_SIMD } from "./deserialize/simd/string";
|
|
33
35
|
|
|
34
36
|
/**
|
|
@@ -185,8 +187,9 @@ export namespace JSON {
|
|
|
185
187
|
// // @ts-ignore
|
|
186
188
|
// return changetype<string>(deserializeString_SIMD(dataPtr, dataPtr + dataSize, __new(dataSize - 4, idof<string>())));
|
|
187
189
|
// } else {
|
|
190
|
+
|
|
188
191
|
// @ts-ignore
|
|
189
|
-
return deserializeString(dataPtr, dataPtr + dataSize,
|
|
192
|
+
return deserializeString(dataPtr, dataPtr + dataSize, 0);
|
|
190
193
|
// }
|
|
191
194
|
} else if (isArray<T>()) {
|
|
192
195
|
// @ts-ignore
|
|
@@ -605,6 +608,7 @@ export namespace JSON {
|
|
|
605
608
|
return deserializeFloat<T>(srcStart, srcEnd);
|
|
606
609
|
} else if (isString<T>()) {
|
|
607
610
|
if (srcEnd - srcStart < 4) throw new Error("Cannot parse data as string because it was formatted incorrectly!");
|
|
611
|
+
|
|
608
612
|
// @ts-ignore: type
|
|
609
613
|
return deserializeString(srcStart, srcEnd, dst);
|
|
610
614
|
} else if (isNullable<T>() && srcEnd - srcStart == 8 && load<u64>(srcStart) == 30399761348886638) {
|
|
@@ -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);
|
|
File without changes
|
|
@@ -7,9 +7,11 @@ import { bytes } from "../../util/bytes";
|
|
|
7
7
|
// @ts-ignore: decorator allowed
|
|
8
8
|
@lazy const LANE_MASK_HIGH = 0xFF00_FF00_FF00_FF00;
|
|
9
9
|
// @ts-ignore: decorator allowed
|
|
10
|
+
@lazy const ONES: u64 = 0x0101010101010101;
|
|
11
|
+
// @ts-ignore: decorator allowed
|
|
10
12
|
@lazy const LANE_MASK_LOW = 0x00FF_00FF_00FF_00FF;
|
|
11
13
|
// @ts-ignore: decorator allowed
|
|
12
|
-
@lazy const
|
|
14
|
+
@lazy const HIGHS = 0x8080808080808080;
|
|
13
15
|
// @ts-ignore: decorator allowed
|
|
14
16
|
@lazy const QUOTE_MASK = 0x0022_0022_0022_0022;
|
|
15
17
|
// @ts-ignore: decorator allowed
|
|
@@ -33,44 +35,40 @@ export function serializeString_SWAR(src: string): void {
|
|
|
33
35
|
const block = load<u64>(srcStart);
|
|
34
36
|
store<u64>(bs.offset, block);
|
|
35
37
|
|
|
36
|
-
let mask = (
|
|
37
|
-
v64x4_eq(block, QUOTE_MASK) |
|
|
38
|
-
v64x4_eq(block, BACKSLASH_MASK) |
|
|
39
|
-
v64x4_ltu(block & LANE_MASK_LOW, CONTROL_MASK)
|
|
40
|
-
) & UINT64_H8
|
|
38
|
+
let mask = v64x4_should_escape(block);
|
|
41
39
|
|
|
42
40
|
while (mask != 0) {
|
|
43
|
-
const lane_index = usize(ctz(mask) >> 3);
|
|
44
|
-
// console.log("lane index " + lane_index.toString());
|
|
41
|
+
const lane_index = usize(ctz(mask) >> 3); // 0 2 4 6
|
|
45
42
|
const src_offset = srcStart + lane_index;
|
|
43
|
+
// const dst_offset = bs.offset + lane_index;
|
|
46
44
|
const code = load<u16>(src_offset) << 2;
|
|
45
|
+
// console.log("lane: " + lane_index.toString())
|
|
47
46
|
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + code);
|
|
48
47
|
|
|
48
|
+
mask = mask & ~(0xFF << (lane_index << 3));
|
|
49
49
|
if ((escaped & 0xffff) != BACK_SLASH) {
|
|
50
50
|
bs.growSize(10);
|
|
51
51
|
const dst_offset = bs.offset + lane_index;
|
|
52
52
|
store<u64>(dst_offset, U00_MARKER);
|
|
53
53
|
store<u32>(dst_offset, escaped, 8);
|
|
54
|
-
|
|
55
|
-
memory.copy(dst_offset + 12, src_offset + 2, (4 - lane_index) << 1);
|
|
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
56
|
bs.offset += 10;
|
|
57
57
|
} else {
|
|
58
58
|
bs.growSize(2);
|
|
59
59
|
const dst_offset = bs.offset + lane_index;
|
|
60
60
|
store<u32>(dst_offset, escaped);
|
|
61
|
-
|
|
62
|
-
memory.copy(dst_offset + 4, src_offset + 2, (4 - lane_index) << 1);
|
|
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
63
|
bs.offset += 2;
|
|
64
64
|
}
|
|
65
|
-
|
|
66
|
-
mask &= mask - 1;
|
|
67
65
|
}
|
|
68
66
|
|
|
69
67
|
srcStart += 8;
|
|
70
68
|
bs.offset += 8;
|
|
71
69
|
}
|
|
72
70
|
|
|
73
|
-
|
|
71
|
+
while (srcStart <= srcEnd - 2) {
|
|
74
72
|
const code = load<u16>(srcStart);
|
|
75
73
|
if (code == 92 || code == 34 || code < 32) {
|
|
76
74
|
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + (code << 2));
|
|
@@ -96,14 +94,16 @@ export function serializeString_SWAR(src: string): void {
|
|
|
96
94
|
}
|
|
97
95
|
|
|
98
96
|
// @ts-ignore: decorators allowed
|
|
99
|
-
@inline function
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
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
|
|
109
109
|
}
|
package/assembly/test.tmp.ts
CHANGED
|
@@ -1,11 +1,133 @@
|
|
|
1
|
+
import {
|
|
2
|
+
JSON
|
|
3
|
+
} from ".";
|
|
1
4
|
import {
|
|
2
5
|
bs
|
|
3
6
|
} from "../lib/as-bs";
|
|
7
|
+
import {
|
|
8
|
+
expect
|
|
9
|
+
} from "./__tests__/lib";
|
|
4
10
|
import {
|
|
5
11
|
serializeString_SIMD
|
|
6
12
|
} from "./serialize/simd/string";
|
|
7
13
|
import {
|
|
8
14
|
serializeString_SWAR
|
|
9
15
|
} from "./serialize/swar/string";
|
|
10
|
-
|
|
11
|
-
|
|
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
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { bs } from "../lib/as-bs";
|
|
2
|
-
import { serializeString_SIMD } from "./serialize/simd/string";
|
|
3
|
-
import { serializeString_SWAR } from "./serialize/swar/string";
|
|
4
|
-
|
|
5
|
-
serializeString_SWAR("a\"cd");
|
|
6
|
-
// serializeString_SIMD("a\0b\"\nd");
|
|
7
|
-
console.log(bs.out<string>());
|
package/bench/abc.bench.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { dumpToFile } from "./lib/bench.js";
|
|
2
|
+
import { bench, blackbox } from "./lib/bench.js";
|
|
2
3
|
|
|
3
4
|
const v1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
|
|
4
5
|
const v2 = '"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"';
|
|
@@ -6,22 +7,19 @@ const v2 = '"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"';
|
|
|
6
7
|
bench(
|
|
7
8
|
"Serialize Alphabet",
|
|
8
9
|
() => {
|
|
9
|
-
blackbox(JSON.stringify(
|
|
10
|
+
blackbox(JSON.stringify(v1));
|
|
10
11
|
},
|
|
11
12
|
64_000_00,
|
|
12
13
|
v1.length << 1,
|
|
13
14
|
);
|
|
15
|
+
dumpToFile("abc", "serialize")
|
|
14
16
|
|
|
15
17
|
bench(
|
|
16
18
|
"Deserialize Alphabet",
|
|
17
19
|
() => {
|
|
18
|
-
blackbox(JSON.parse(
|
|
20
|
+
blackbox(JSON.parse(v2));
|
|
19
21
|
},
|
|
20
22
|
64_000_00,
|
|
21
23
|
v2.length << 1,
|
|
22
24
|
);
|
|
23
|
-
|
|
24
|
-
function blackbox<T>(value: T): T {
|
|
25
|
-
(globalThis as any).__blackhole = value;
|
|
26
|
-
return globalThis.__blackhole;
|
|
27
|
-
}
|
|
25
|
+
dumpToFile("abc", "deserialize")
|