Skip to content

Commit 9f154ea

Browse files
committed
feat: add SWAR string serialization
1 parent f6a2cdf commit 9f154ea

9 files changed

Lines changed: 123 additions & 327 deletions

File tree

assembly/index.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/// <reference path="./index.d.ts" />
22

33
import { bs } from "../lib/as-bs";
4-
import { serializeString } from "./serialize/simple/string";
4+
// import { serializeString } from "./serialize/simple/string";
55
import { serializeArray } from "./serialize/simple/array";
66
import { serializeMap } from "./serialize/simple/map";
77
import { serializeDate } from "./serialize/simple/date";
@@ -28,6 +28,7 @@ import { deserializeObject } from "./deserialize/simple/object";
2828
import { serializeRaw } from "./serialize/simple/raw";
2929
import { deserializeRaw } from "./deserialize/simple/raw";
3030
import { serializeString_SIMD } from "./serialize/simd/string";
31+
import { serializeString_SWAR } from "./serialize/swar/string";
3132
// import { deserializeString_SIMD } from "./deserialize/simd/string";
3233

3334
/**
@@ -110,14 +111,14 @@ export namespace JSON {
110111
// const newSize = bytes(data) + 4;
111112
// const newBuf = __new(newSize, idof<string>());
112113
// bs.setBuffer(newBuf);
113-
// serializeString(changetype<string>(data));
114+
// serializeString_SWAR(changetype<string>(data));
114115
// bs.setBuffer(oldBuf);
115116
// return changetype<string>(newBuf);
116117
// }
117118
if (ASC_FEATURE_SIMD) {
118119
serializeString_SIMD(data as string);
119120
} else {
120-
serializeString(data as string);
121+
serializeString_SWAR(data as string);
121122
}
122123
return bs.out<string>();
123124
// @ts-ignore: Supplied by transform
@@ -261,6 +262,7 @@ export namespace JSON {
261262
}
262263

263264

265+
// @ts-ignore: decorators allowed here
264266
@final
265267
export class Value {
266268
static METHODS: Map<u32, u32> = new Map<u32, u32>();
@@ -553,7 +555,7 @@ export namespace JSON {
553555
if (ASC_FEATURE_SIMD) {
554556
serializeString_SIMD(src as string);
555557
} else {
556-
serializeString(src as string);
558+
serializeString_SWAR(src as string);
557559
}
558560
// @ts-ignore: Supplied by transform
559561
} else if (isDefined(src.__SERIALIZE_CUSTOM)) {
@@ -722,15 +724,15 @@ export namespace JSON {
722724
// const newSize = bytes(data) + 4;
723725
// const newBuf = __new(newSize, idof<string>());
724726
// bs.setBuffer(newBuf);
725-
// serializeString(changetype<string>(data));
727+
// serializeString_SWAR(changetype<string>(data));
726728
// bs.setBuffer(oldBuf);
727729
// return changetype<string>(newBuf);
728730
// }
729731
if (ASC_FEATURE_SIMD) {
730732
serializeString_SIMD(data as string);
731733
} else {
732734
bs.saveState();
733-
serializeString(data as string);
735+
serializeString_SWAR(data as string);
734736
}
735737
return bs.cpyOut<string>();
736738
// @ts-ignore: Supplied by transform

assembly/serialize/simd/string.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,6 @@ import { bytes } from "../../util";
55

66
/**
77
* Serializes strings into their JSON counterparts using SIMD operations
8-
* @param srcStart pointer to begin serializing at
9-
* @param srcEnd pointer to end serialization at
108
*/
119
export function serializeString_SIMD(src: string): void {
1210
const U00_MARKER = 13511005048209500;
@@ -22,7 +20,7 @@ export function serializeString_SIMD(src: string): void {
2220

2321
bs.proposeSize(srcSize + 4);
2422

25-
store<u8>(changetype<usize>(bs.offset), 34); /* " */
23+
store<u8>(bs.offset, 34); // "
2624
bs.offset += 2;
2725

2826
while (srcStart <= srcEnd16) {
@@ -39,6 +37,7 @@ export function serializeString_SIMD(src: string): void {
3937

4038
while (mask != 0) {
4139
const lane_index = ctz(mask) << 1;
40+
// console.log("lane index " + lane_index.toString());
4241
const src_offset = srcStart + lane_index;
4342
const code = load<u16>(src_offset) << 2;
4443
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + code);

assembly/serialize/simple/arbitrary.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { JSON } from "../..";
2+
import { serializeString_SWAR } from "../swar/string";
23
import { serializeArray } from "./array";
34
import { serializeBool } from "./bool";
45
import { serializeFloat } from "./float";
@@ -27,7 +28,7 @@ export function serializeArbitrary(src: JSON.Value): void {
2728
serializeFloat<f64>(src.get<f64>());
2829
break;
2930
case JSON.Types.String:
30-
serializeString(src.get<string>());
31+
serializeString_SWAR(src.get<string>());
3132
break;
3233
case JSON.Types.Bool:
3334
serializeBool(src.get<bool>());

assembly/serialize/simple/object.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { bs } from "../../../lib/as-bs";
22
import { JSON } from "../..";
33
import { BRACE_LEFT, BRACE_RIGHT, COLON, COMMA } from "../../custom/chars";
44
import { serializeArbitrary } from "./arbitrary";
5-
import { serializeString } from "./string";
5+
import { serializeString_SWAR } from "../swar/string";
66

77
export function serializeObject(src: JSON.Obj): void {
88
const srcSize = src.size;
@@ -23,7 +23,7 @@ export function serializeObject(src: JSON.Obj): void {
2323
bs.offset += 2;
2424

2525
for (let i = 0; i < srcEnd; i++) {
26-
serializeString(unchecked(keys[i]));
26+
serializeString_SWAR(unchecked(keys[i]));
2727
bs.growSize(2);
2828
store<u16>(bs.offset, COLON);
2929
bs.offset += 2;
@@ -34,7 +34,7 @@ export function serializeObject(src: JSON.Obj): void {
3434
bs.offset += 2;
3535
}
3636

37-
serializeString(unchecked(keys[srcEnd]));
37+
serializeString_SWAR(unchecked(keys[srcEnd]));
3838
bs.growSize(2);
3939
store<u16>(bs.offset, COLON);
4040
bs.offset += 2;

assembly/serialize/swar/string.ts

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
import { bs } from "../../../lib/as-bs";
2+
import { BACK_SLASH } from "../../custom/chars";
3+
import { SERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
4+
import { bytes } from "../../util/bytes";
5+
6+
// @ts-ignore: decorator allowed
7+
@lazy const QUOTE_MASK = 0x0022_0022_0022_0022;
8+
// @ts-ignore: decorator allowed
9+
@lazy const U00_MARKER = 13511005048209500;
10+
11+
export function serializeString_SWAR(src: string): void {
12+
const srcSize = bytes(src);
13+
let srcStart = changetype<usize>(src);
14+
const srcEnd = srcStart + srcSize;
15+
const srcEnd8 = srcEnd - 8;
16+
17+
bs.proposeSize(srcSize + 4);
18+
store<u16>(bs.offset, 34); // "
19+
bs.offset += 2;
20+
21+
while (srcStart <= srcEnd8) {
22+
const block = load<u64>(srcStart);
23+
store<u64>(bs.offset, block);
24+
25+
const quotes = COMP(block, QUOTE_MASK);
26+
let mask = quotes;
27+
28+
29+
while (mask != 0) {
30+
const lane_index = usize(ctz(mask) >> 3);
31+
// console.log("lane index " + lane_index.toString());
32+
const src_offset = srcStart + lane_index;
33+
const code = load<u16>(src_offset) << 2;
34+
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + code);
35+
36+
if ((escaped & 0xffff) != BACK_SLASH) {
37+
bs.growSize(10);
38+
const dst_offset = bs.offset + lane_index;
39+
store<u64>(dst_offset, U00_MARKER);
40+
store<u32>(dst_offset, escaped, 8);
41+
store<u64>(dst_offset, load<u64>(src_offset, 2), 12); // unsafe. can overflow here
42+
bs.offset += 10;
43+
} else {
44+
bs.growSize(2);
45+
const dst_offset = bs.offset + lane_index;
46+
store<u32>(dst_offset, escaped);
47+
store<u64>(dst_offset, load<u64>(src_offset, 2), 4);
48+
bs.offset += 2;
49+
}
50+
51+
mask &= mask - 1;
52+
}
53+
54+
srcStart += 8;
55+
bs.offset += 8;
56+
}
57+
58+
while (srcStart <= srcEnd - 2) {
59+
const code = load<u16>(srcStart);
60+
if (code == 92 || code == 34 || code < 32) {
61+
const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + (code << 2));
62+
if ((escaped & 0xffff) != BACK_SLASH) {
63+
bs.growSize(10);
64+
store<u64>(bs.offset, U00_MARKER);
65+
store<u32>(bs.offset, escaped, 8);
66+
bs.offset += 12;
67+
} else {
68+
bs.growSize(2);
69+
store<u32>(bs.offset, escaped);
70+
bs.offset += 4;
71+
}
72+
} else {
73+
store<u16>(bs.offset, code);
74+
bs.offset += 2;
75+
}
76+
srcStart += 2;
77+
}
78+
79+
store<u16>(bs.offset, 34); // "
80+
bs.offset += 2;
81+
}
82+
83+
// @ts-ignore: decorators allowed
84+
@inline function COMP(x: u64, y: u64): u64 {
85+
const LANE_MASK = 0xFF00_FF00_FF00_FF00;
86+
const xored = (x ^ LANE_MASK) ^ y;
87+
const mask = (((xored >> 1) | 0x8080808080808080) - xored) & 0x8080808080808080;
88+
return (mask << 1) - (mask >> 7) & 0x8080808080808080;
89+
}

0 commit comments

Comments
 (0)