json-as 1.3.9 → 1.5.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/CHANGELOG.md +60 -19
- package/README.md +120 -21
- package/assembly/custom/chars.ts +39 -78
- package/assembly/deserialize/index/arbitrary.ts +28 -10
- package/assembly/deserialize/index/float.ts +2 -4
- package/assembly/deserialize/index/integer.ts +2 -4
- package/assembly/deserialize/index/object.ts +6 -1
- package/assembly/deserialize/index/string.ts +2 -7
- package/assembly/deserialize/index/unsigned.ts +2 -4
- package/assembly/deserialize/naive/array/arbitrary.ts +3 -136
- package/assembly/deserialize/naive/array/array.ts +30 -1
- package/assembly/deserialize/naive/array/integer.ts +2 -7
- package/assembly/deserialize/naive/array/map.ts +10 -14
- package/assembly/deserialize/naive/array/object.ts +10 -14
- package/assembly/deserialize/naive/array/struct.ts +19 -1
- package/assembly/deserialize/naive/bool.ts +1 -5
- package/assembly/deserialize/naive/date.ts +1 -2
- package/assembly/deserialize/naive/float.ts +4 -11
- package/assembly/deserialize/naive/integer.ts +2 -4
- package/assembly/deserialize/naive/map.ts +42 -205
- package/assembly/deserialize/naive/object.ts +291 -174
- package/assembly/deserialize/naive/raw.ts +1 -5
- package/assembly/deserialize/naive/set.ts +3 -6
- package/assembly/deserialize/naive/staticarray.ts +2 -4
- package/assembly/deserialize/naive/string.ts +68 -24
- package/assembly/deserialize/naive/typedarray.ts +1 -2
- package/assembly/deserialize/naive/unsigned.ts +2 -4
- package/assembly/deserialize/simd/array/integer.ts +5 -13
- package/assembly/deserialize/simd/float.ts +5 -12
- package/assembly/deserialize/simd/integer.ts +6 -15
- package/assembly/deserialize/simd/string.ts +21 -43
- package/assembly/deserialize/swar/array/arbitrary.ts +1 -2
- package/assembly/deserialize/swar/array/array.ts +2 -4
- package/assembly/deserialize/swar/array/bool.ts +2 -4
- package/assembly/deserialize/swar/array/box.ts +1 -2
- package/assembly/deserialize/swar/array/float.ts +8 -21
- package/assembly/deserialize/swar/array/generic.ts +2 -4
- package/assembly/deserialize/swar/array/integer.ts +13 -27
- package/assembly/deserialize/swar/array/map.ts +1 -2
- package/assembly/deserialize/swar/array/object.ts +2 -4
- package/assembly/deserialize/swar/array/raw.ts +1 -2
- package/assembly/deserialize/swar/array/shared.ts +9 -21
- package/assembly/deserialize/swar/array/string.ts +4 -10
- package/assembly/deserialize/swar/array/struct.ts +3 -9
- package/assembly/deserialize/swar/array.ts +1 -3
- package/assembly/deserialize/swar/float.ts +7 -17
- package/assembly/deserialize/swar/integer.ts +6 -15
- package/assembly/deserialize/swar/string.ts +40 -54
- package/assembly/deserialize/swar/typedarray.ts +4 -4
- package/assembly/index.d.ts +259 -21
- package/assembly/index.ts +1704 -266
- package/assembly/serialize/index/arbitrary.ts +70 -4
- package/assembly/serialize/index/jsonarray.ts +51 -0
- package/assembly/serialize/index/object.ts +39 -14
- package/assembly/serialize/index/string.ts +1 -2
- package/assembly/serialize/index/typedarray.ts +1 -2
- package/assembly/serialize/index.ts +1 -0
- package/assembly/serialize/naive/array.ts +23 -34
- package/assembly/serialize/naive/bool.ts +0 -1
- package/assembly/serialize/naive/float.ts +16 -25
- package/assembly/serialize/naive/integer.ts +1 -5
- package/assembly/serialize/naive/raw.ts +1 -2
- package/assembly/serialize/naive/set.ts +0 -4
- package/assembly/serialize/naive/staticarray.ts +0 -5
- package/assembly/serialize/naive/string.ts +11 -7
- package/assembly/serialize/naive/typedarray.ts +0 -6
- package/assembly/serialize/simd/string.ts +1 -3
- package/assembly/serialize/swar/string.ts +2 -4
- package/assembly/util/atoi-fast.ts +4 -14
- package/assembly/util/atoi.ts +1 -2
- package/assembly/util/bytes.ts +1 -2
- package/assembly/util/idofd.ts +1 -2
- package/assembly/util/isSpace.ts +1 -2
- package/assembly/util/itoa-fast.ts +9 -15
- package/assembly/util/nextPowerOf2.ts +1 -2
- package/assembly/util/parsefloat-fast.ts +4 -7
- package/assembly/util/ptrToStr.ts +1 -2
- package/assembly/util/scanValueEnd.ts +1 -2
- package/assembly/util/scanValueEndSimd.ts +198 -0
- package/assembly/util/scanValueEndSwar.ts +184 -0
- package/assembly/util/scientific.ts +8 -14
- package/assembly/util/simd-int.ts +4 -8
- package/assembly/util/snp.ts +2 -7
- package/assembly/util/stringScan.ts +2 -4
- package/assembly/util/swar-int.ts +8 -16
- package/assembly/util/swar.ts +2 -4
- package/lib/as-bs.ts +57 -42
- package/package.json +27 -10
- package/transform/lib/builder.d.ts +0 -1
- package/transform/lib/builder.js +0 -1
- package/transform/lib/index.d.ts +0 -1
- package/transform/lib/index.js +617 -326
- package/transform/lib/linkers/alias.d.ts +0 -1
- package/transform/lib/linkers/alias.js +0 -1
- package/transform/lib/linkers/custom.d.ts +0 -1
- package/transform/lib/linkers/custom.js +0 -1
- package/transform/lib/linkers/imports.d.ts +0 -1
- package/transform/lib/linkers/imports.js +0 -1
- package/transform/lib/types.d.ts +4 -2
- package/transform/lib/types.js +5 -1
- package/transform/lib/util.d.ts +0 -1
- package/transform/lib/util.js +0 -1
- package/transform/lib/visitor.d.ts +0 -1
- package/transform/lib/visitor.js +0 -1
- package/assembly/util/dragonbox-cache.ts +0 -445
- package/assembly/util/dragonbox.ts +0 -660
- package/transform/lib/builder.d.ts.map +0 -1
- package/transform/lib/builder.js.map +0 -1
- package/transform/lib/index.d.ts.map +0 -1
- package/transform/lib/index.js.map +0 -1
- package/transform/lib/linkers/alias.d.ts.map +0 -1
- package/transform/lib/linkers/alias.js.map +0 -1
- package/transform/lib/linkers/custom.d.ts.map +0 -1
- package/transform/lib/linkers/custom.js.map +0 -1
- package/transform/lib/linkers/imports.d.ts.map +0 -1
- package/transform/lib/linkers/imports.js.map +0 -1
- package/transform/lib/types.d.ts.map +0 -1
- package/transform/lib/types.js.map +0 -1
- package/transform/lib/util.d.ts.map +0 -1
- package/transform/lib/util.js.map +0 -1
- package/transform/lib/visitor.d.ts.map +0 -1
- package/transform/lib/visitor.js.map +0 -1
|
@@ -4,15 +4,13 @@ import { __heap_base } from "memory";
|
|
|
4
4
|
import { BACK_SLASH, QUOTE } from "../../custom/chars";
|
|
5
5
|
import { DESERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
@inline function hexDigit(c: u16): u32 {
|
|
7
|
+
function hexDigit(c: u16): u32 {
|
|
9
8
|
if (c <= 0x39) return c - 0x30; // '0'-'9'
|
|
10
9
|
if (c <= 0x46) return c - 0x37; // 'A'-'F'
|
|
11
10
|
return c - 0x57; // 'a'-'f'
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
@inline function hex4ToU16(srcStart: usize): u16 {
|
|
13
|
+
function hex4ToU16(srcStart: usize): u16 {
|
|
16
14
|
return <u16>(
|
|
17
15
|
((hexDigit(load<u16>(srcStart)) << 12) |
|
|
18
16
|
(hexDigit(load<u16>(srcStart, 2)) << 8) |
|
|
@@ -21,8 +19,7 @@ import { DESERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
|
21
19
|
);
|
|
22
20
|
}
|
|
23
21
|
|
|
24
|
-
|
|
25
|
-
@inline function isHexDigit(c: u16): bool {
|
|
22
|
+
function isHexDigit(c: u16): bool {
|
|
26
23
|
return (
|
|
27
24
|
(c >= 0x30 && c <= 0x39) ||
|
|
28
25
|
(c >= 0x41 && c <= 0x46) ||
|
|
@@ -33,8 +30,7 @@ import { DESERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
|
33
30
|
// Strict RFC 8259 check for the char following a backslash, at [escPtr, srcEnd).
|
|
34
31
|
// Legal escapes: " \ / b f n r t and \uXXXX (4 hex digits). Throws otherwise:
|
|
35
32
|
// unknown escape letter, a trailing backslash, or a short / non-hex \u.
|
|
36
|
-
|
|
37
|
-
@inline function validateEscape(escPtr: usize, srcEnd: usize): void {
|
|
33
|
+
function validateEscape(escPtr: usize, srcEnd: usize): void {
|
|
38
34
|
if (escPtr >= srcEnd)
|
|
39
35
|
throw new Error("Invalid JSON string: incomplete escape");
|
|
40
36
|
const code = load<u16>(escPtr);
|
|
@@ -65,8 +61,7 @@ import { DESERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
|
65
61
|
throw new Error("Invalid JSON string: illegal escape");
|
|
66
62
|
}
|
|
67
63
|
|
|
68
|
-
|
|
69
|
-
@inline export function deserializeString_NAIVE(
|
|
64
|
+
export function deserializeString_NAIVE(
|
|
70
65
|
srcStart: usize,
|
|
71
66
|
srcEnd: usize,
|
|
72
67
|
): string {
|
|
@@ -121,8 +116,7 @@ import { DESERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
|
121
116
|
|
|
122
117
|
// Writes into the destination field, reusing or resizing the backing string.
|
|
123
118
|
// Mirrors `writeStringToField` in ../swar/string.ts.
|
|
124
|
-
|
|
125
|
-
@inline function writeStringToField(
|
|
119
|
+
function writeStringToField(
|
|
126
120
|
dstFieldPtr: usize,
|
|
127
121
|
srcStart: usize,
|
|
128
122
|
byteLength: u32,
|
|
@@ -148,23 +142,26 @@ import { DESERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
|
148
142
|
memory.copy(stringPtr, srcStart, byteLength);
|
|
149
143
|
}
|
|
150
144
|
|
|
151
|
-
//
|
|
152
|
-
|
|
153
|
-
|
|
145
|
+
// Escape-bearing tail of the field parse: the clean prefix [payloadStart,
|
|
146
|
+
// escPos) is bulk-copied into the scratch buffer, then escapes are decoded into
|
|
147
|
+
// it, and the result is written to the field. Only reached when a backslash is
|
|
148
|
+
// actually present - the common escape-free case never touches `bs`.
|
|
149
|
+
function deserializeEscapedStringField_NAIVE(
|
|
150
|
+
payloadStart: usize,
|
|
151
|
+
escPos: usize,
|
|
154
152
|
srcEnd: usize,
|
|
155
|
-
|
|
156
|
-
dstOffset: usize = 0,
|
|
153
|
+
dstFieldPtr: usize,
|
|
157
154
|
): usize {
|
|
158
|
-
const dstFieldPtr = dstObj + dstOffset;
|
|
159
|
-
if (srcStart + 2 > srcEnd || load<u16>(srcStart) != QUOTE)
|
|
160
|
-
abort("Expected leading quote");
|
|
161
|
-
|
|
162
|
-
const payloadStart = srcStart + 2;
|
|
163
|
-
srcStart = payloadStart;
|
|
164
|
-
|
|
165
155
|
bs.offset = bs.buffer;
|
|
166
156
|
bs.ensureSize(<u32>(srcEnd - payloadStart));
|
|
167
157
|
|
|
158
|
+
const prefixLen = escPos - payloadStart;
|
|
159
|
+
if (prefixLen) {
|
|
160
|
+
memory.copy(bs.offset, payloadStart, prefixLen);
|
|
161
|
+
bs.offset += prefixLen;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
let srcStart = escPos;
|
|
168
165
|
while (srcStart < srcEnd) {
|
|
169
166
|
const block = load<u16>(srcStart);
|
|
170
167
|
|
|
@@ -197,3 +194,50 @@ import { DESERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
|
197
194
|
abort("Expected closing quote");
|
|
198
195
|
return 0;
|
|
199
196
|
}
|
|
197
|
+
|
|
198
|
+
// NOT @inline: this is a loop-bearing scanner called per string field. As an
|
|
199
|
+
// always-inline entry it gets inlined into every field call site inside the
|
|
200
|
+
// @inline __DESERIALIZE_FAST, exploding binaryen's optimize phase on large
|
|
201
|
+
// schemas (~118s on the `large` bench). Kept as a single shared function - one
|
|
202
|
+
// call per field - matching the non-inline SWAR/SIMD field deserializers.
|
|
203
|
+
export function deserializeStringField_NAIVE<T extends string | null>(
|
|
204
|
+
srcStart: usize,
|
|
205
|
+
srcEnd: usize,
|
|
206
|
+
dstObj: usize,
|
|
207
|
+
dstOffset: usize = 0,
|
|
208
|
+
): usize {
|
|
209
|
+
const dstFieldPtr = dstObj + dstOffset;
|
|
210
|
+
if (srcStart + 2 > srcEnd || load<u16>(srcStart) != QUOTE)
|
|
211
|
+
abort("Expected leading quote");
|
|
212
|
+
|
|
213
|
+
const payloadStart = srcStart + 2;
|
|
214
|
+
srcStart = payloadStart;
|
|
215
|
+
|
|
216
|
+
// Scan for the closing quote without touching the scratch buffer. For the
|
|
217
|
+
// common escape-free case the bytes are a verbatim slice of the source, so we
|
|
218
|
+
// copy source -> field directly (mirrors the SWAR/SIMD field paths). Only a
|
|
219
|
+
// backslash diverts to the escape-decoding tail above.
|
|
220
|
+
while (srcStart < srcEnd) {
|
|
221
|
+
const block = load<u16>(srcStart);
|
|
222
|
+
if (block == QUOTE) {
|
|
223
|
+
writeStringToField(
|
|
224
|
+
dstFieldPtr,
|
|
225
|
+
payloadStart,
|
|
226
|
+
<u32>(srcStart - payloadStart),
|
|
227
|
+
);
|
|
228
|
+
return srcStart + 2;
|
|
229
|
+
}
|
|
230
|
+
if (block == BACK_SLASH) {
|
|
231
|
+
return deserializeEscapedStringField_NAIVE(
|
|
232
|
+
payloadStart,
|
|
233
|
+
srcStart,
|
|
234
|
+
srcEnd,
|
|
235
|
+
dstFieldPtr,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
srcStart += 2;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
abort("Expected closing quote");
|
|
242
|
+
return 0;
|
|
243
|
+
}
|
|
@@ -2,8 +2,7 @@ import { COMMA, BRACKET_RIGHT } from "../../custom/chars";
|
|
|
2
2
|
import { deserializeFloat_NAIVE } from "./float";
|
|
3
3
|
import { atoi, isSpace } from "../../util";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
@inline function countTypedArrayElements(srcStart: usize, srcEnd: usize): i32 {
|
|
5
|
+
function countTypedArrayElements(srcStart: usize, srcEnd: usize): i32 {
|
|
7
6
|
let count = 0;
|
|
8
7
|
|
|
9
8
|
while (srcStart < srcEnd) {
|
|
@@ -1,15 +1,13 @@
|
|
|
1
1
|
import { atoi } from "../../util/atoi";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
@inline export function deserializeUnsigned_NAIVE<T extends number>(
|
|
3
|
+
export function deserializeUnsigned_NAIVE<T extends number>(
|
|
5
4
|
srcStart: usize,
|
|
6
5
|
srcEnd: usize,
|
|
7
6
|
): T {
|
|
8
7
|
return atoi<T>(srcStart, srcEnd);
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
|
|
12
|
-
@inline export function deserializeUnsignedField_NAIVE<T extends number>(
|
|
10
|
+
export function deserializeUnsignedField_NAIVE<T extends number>(
|
|
13
11
|
srcStart: usize,
|
|
14
12
|
srcEnd: usize,
|
|
15
13
|
dstObj: usize,
|
|
@@ -40,11 +40,7 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
|
|
|
40
40
|
// @ts-expect-error: decorators valid here
|
|
41
41
|
@lazy const PAIR_WEIGHTS_100_1 = i16x8(100, 1, 100, 1, 0, 0, 0, 0);
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
@inline function storeSignedInteger<T extends number[]>(
|
|
45
|
-
slot: usize,
|
|
46
|
-
value: i64,
|
|
47
|
-
): void {
|
|
43
|
+
function storeSignedInteger<T extends number[]>(slot: usize, value: i64): void {
|
|
48
44
|
if (sizeof<valueof<T>>() == sizeof<i8>()) {
|
|
49
45
|
store<i8>(slot, <i8>value);
|
|
50
46
|
} else if (sizeof<valueof<T>>() == sizeof<i16>()) {
|
|
@@ -58,8 +54,7 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
|
|
|
58
54
|
}
|
|
59
55
|
}
|
|
60
56
|
|
|
61
|
-
|
|
62
|
-
@inline function storeUnsignedInteger<T extends number[]>(
|
|
57
|
+
function storeUnsignedInteger<T extends number[]>(
|
|
63
58
|
slot: usize,
|
|
64
59
|
value: u64,
|
|
65
60
|
): void {
|
|
@@ -76,8 +71,7 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
|
|
|
76
71
|
}
|
|
77
72
|
}
|
|
78
73
|
|
|
79
|
-
|
|
80
|
-
@inline function tryParseEightDigitsSIMD(srcStart: usize, value: u64): u64 {
|
|
74
|
+
function tryParseEightDigitsSIMD(srcStart: usize, value: u64): u64 {
|
|
81
75
|
const block = load<v128>(srcStart);
|
|
82
76
|
const digits = i16x8.sub(block, SPLAT_30);
|
|
83
77
|
if (v128.any_true(i16x8.gt_u(digits, SPLAT_09))) return 0;
|
|
@@ -96,8 +90,7 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
|
|
|
96
90
|
// As in the SWAR variant: the parse helpers take a `slot` (`writePtr`) and
|
|
97
91
|
// store directly. The dispatcher owns `out.length = maxElements` and the
|
|
98
92
|
// per-element `writePtr` advance so `Array.push` is removed for every
|
|
99
|
-
|
|
100
|
-
@inline function parseSignedIntegerSIMD<T extends number[]>(
|
|
93
|
+
function parseSignedIntegerSIMD<T extends number[]>(
|
|
101
94
|
srcStart: usize,
|
|
102
95
|
srcEnd: usize,
|
|
103
96
|
slot: usize,
|
|
@@ -135,8 +128,7 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
|
|
|
135
128
|
return srcStart;
|
|
136
129
|
}
|
|
137
130
|
|
|
138
|
-
|
|
139
|
-
@inline function parseUnsignedIntegerSIMD<T extends number[]>(
|
|
131
|
+
function parseUnsignedIntegerSIMD<T extends number[]>(
|
|
140
132
|
srcStart: usize,
|
|
141
133
|
srcEnd: usize,
|
|
142
134
|
slot: usize,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// `parse8Digits_SIMD` (16 bytes / 8 digits) before falling through to
|
|
4
4
|
// `parse4Digits_PairMul` (8 bytes / 4 digits) and finally the scalar tail.
|
|
5
5
|
//
|
|
6
|
-
// Output is bit-identical to `f64.parse` / `f32.parse` for every input
|
|
6
|
+
// Output is bit-identical to `f64.parse` / `f32.parse` for every input - the
|
|
7
7
|
// SIMD strides only change how the u64 mantissa is accumulated, not what it
|
|
8
8
|
// becomes.
|
|
9
9
|
//
|
|
@@ -23,8 +23,7 @@ const ASCII_ZERO: u16 = 48;
|
|
|
23
23
|
const ASCII_E_UP: u16 = 69;
|
|
24
24
|
const ASCII_E_LO: u16 = 101;
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
@inline function fallback<T>(srcStart: usize, srcEnd: usize): T {
|
|
26
|
+
function fallback<T>(srcStart: usize, srcEnd: usize): T {
|
|
28
27
|
const s = ptrToStr(srcStart, srcEnd);
|
|
29
28
|
// @ts-ignore
|
|
30
29
|
const type: T = 0;
|
|
@@ -34,8 +33,7 @@ const ASCII_E_LO: u16 = 101;
|
|
|
34
33
|
return <T>(<f32>f32.parse(s));
|
|
35
34
|
}
|
|
36
35
|
|
|
37
|
-
|
|
38
|
-
@inline function fallbackField<T extends number>(
|
|
36
|
+
function fallbackField<T extends number>(
|
|
39
37
|
origStart: usize,
|
|
40
38
|
end: usize,
|
|
41
39
|
fieldPtr: usize,
|
|
@@ -48,11 +46,7 @@ const ASCII_E_LO: u16 = 101;
|
|
|
48
46
|
}
|
|
49
47
|
}
|
|
50
48
|
|
|
51
|
-
|
|
52
|
-
@inline export function deserializeFloat_SIMD<T>(
|
|
53
|
-
srcStart: usize,
|
|
54
|
-
srcEnd: usize,
|
|
55
|
-
): T {
|
|
49
|
+
export function deserializeFloat_SIMD<T>(srcStart: usize, srcEnd: usize): T {
|
|
56
50
|
const origStart = srcStart;
|
|
57
51
|
let p = srcStart;
|
|
58
52
|
let negative = false;
|
|
@@ -168,8 +162,7 @@ const ASCII_E_LO: u16 = 101;
|
|
|
168
162
|
return <T>(<f32>result);
|
|
169
163
|
}
|
|
170
164
|
|
|
171
|
-
|
|
172
|
-
@inline export function deserializeFloatField_SIMD<T extends number>(
|
|
165
|
+
export function deserializeFloatField_SIMD<T extends number>(
|
|
173
166
|
srcStart: usize,
|
|
174
167
|
srcEnd: usize,
|
|
175
168
|
dstObj: usize,
|
|
@@ -38,11 +38,7 @@ const ASCII_ZERO: u16 = 48;
|
|
|
38
38
|
* @param value The `u64` accumulator, interpreted as a two's-complement
|
|
39
39
|
* signed integer for narrower types.
|
|
40
40
|
*/
|
|
41
|
-
|
|
42
|
-
@inline function storeSignedToField<T extends number>(
|
|
43
|
-
dstPtr: usize,
|
|
44
|
-
value: u64,
|
|
45
|
-
): void {
|
|
41
|
+
function storeSignedToField<T extends number>(dstPtr: usize, value: u64): void {
|
|
46
42
|
if (sizeof<T>() == 1) {
|
|
47
43
|
store<i8>(dstPtr, <i8>value);
|
|
48
44
|
} else if (sizeof<T>() == 2) {
|
|
@@ -61,8 +57,7 @@ const ASCII_ZERO: u16 = 48;
|
|
|
61
57
|
* @param dstPtr Destination pointer (already includes any field offset).
|
|
62
58
|
* @param value The `u64` accumulator.
|
|
63
59
|
*/
|
|
64
|
-
|
|
65
|
-
@inline function storeUnsignedToField<T extends number>(
|
|
60
|
+
function storeUnsignedToField<T extends number>(
|
|
66
61
|
dstPtr: usize,
|
|
67
62
|
value: u64,
|
|
68
63
|
): void {
|
|
@@ -88,8 +83,7 @@ const ASCII_ZERO: u16 = 48;
|
|
|
88
83
|
* @param srcEnd Pointer just past the last code unit.
|
|
89
84
|
* @returns The parsed value, two's-complement truncated to `T`.
|
|
90
85
|
*/
|
|
91
|
-
|
|
92
|
-
@inline export function deserializeInteger_SIMD<T extends number>(
|
|
86
|
+
export function deserializeInteger_SIMD<T extends number>(
|
|
93
87
|
srcStart: usize,
|
|
94
88
|
srcEnd: usize,
|
|
95
89
|
): T {
|
|
@@ -130,8 +124,7 @@ const ASCII_ZERO: u16 = 48;
|
|
|
130
124
|
* @param dstOffset Byte offset of the field within `dstObj`.
|
|
131
125
|
* @returns The source position immediately after the last digit consumed.
|
|
132
126
|
*/
|
|
133
|
-
|
|
134
|
-
@inline export function deserializeIntegerField_SIMD<T extends number>(
|
|
127
|
+
export function deserializeIntegerField_SIMD<T extends number>(
|
|
135
128
|
srcStart: usize,
|
|
136
129
|
srcEnd: usize,
|
|
137
130
|
dstObj: usize,
|
|
@@ -170,8 +163,7 @@ const ASCII_ZERO: u16 = 48;
|
|
|
170
163
|
* @param srcEnd Pointer just past the last code unit.
|
|
171
164
|
* @returns The parsed value, truncated to `T`.
|
|
172
165
|
*/
|
|
173
|
-
|
|
174
|
-
@inline export function deserializeUnsigned_SIMD<T extends number>(
|
|
166
|
+
export function deserializeUnsigned_SIMD<T extends number>(
|
|
175
167
|
srcStart: usize,
|
|
176
168
|
srcEnd: usize,
|
|
177
169
|
): T {
|
|
@@ -207,8 +199,7 @@ const ASCII_ZERO: u16 = 48;
|
|
|
207
199
|
* @param dstOffset Byte offset of the field within `dstObj`.
|
|
208
200
|
* @returns The source position immediately after the last digit consumed.
|
|
209
201
|
*/
|
|
210
|
-
|
|
211
|
-
@inline export function deserializeUnsignedField_SIMD<T extends number>(
|
|
202
|
+
export function deserializeUnsignedField_SIMD<T extends number>(
|
|
212
203
|
srcStart: usize,
|
|
213
204
|
srcEnd: usize,
|
|
214
205
|
dstObj: usize,
|
|
@@ -63,20 +63,14 @@ import { hex4_to_u16_swar } from "../../util/swar";
|
|
|
63
63
|
* @param dst buffer to write to
|
|
64
64
|
* @returns number of bytes written
|
|
65
65
|
*/
|
|
66
|
-
|
|
67
|
-
@inline function copyStringFromSource_SIMD(
|
|
68
|
-
srcStart: usize,
|
|
69
|
-
byteLength: usize,
|
|
70
|
-
): string {
|
|
66
|
+
function copyStringFromSource_SIMD(srcStart: usize, byteLength: usize): string {
|
|
71
67
|
if (byteLength == 0) return changetype<string>("");
|
|
72
|
-
// @ts-expect-error: __new is a runtime builtin
|
|
73
68
|
const out = __new(byteLength, idof<string>());
|
|
74
69
|
memory.copy(out, srcStart, byteLength);
|
|
75
70
|
return changetype<string>(out);
|
|
76
71
|
}
|
|
77
72
|
|
|
78
|
-
|
|
79
|
-
@inline function writeStringToField_SIMD(
|
|
73
|
+
function writeStringToField_SIMD(
|
|
80
74
|
dstFieldPtr: usize,
|
|
81
75
|
srcStart: usize,
|
|
82
76
|
byteLength: u32,
|
|
@@ -107,11 +101,7 @@ import { hex4_to_u16_swar } from "../../util/swar";
|
|
|
107
101
|
// handling (no closing-quote search). Same HYBRID strategy as the field path
|
|
108
102
|
// (see deserializeEscapedStringField_SIMD): escape blocks use a free
|
|
109
103
|
// whole-block v128 store for the plain prefix; clean runs stream the first
|
|
110
|
-
|
|
111
|
-
//
|
|
112
|
-
// Deliberately NOT @inline: cold escape path. Inlining the nested-loop v128
|
|
113
|
-
// body at every call site explodes `--converge` compile time on large schemas
|
|
114
|
-
// for no runtime gain (one call per escaped string).
|
|
104
|
+
|
|
115
105
|
function deserializeEscapedString_SIMD(
|
|
116
106
|
payloadStart: usize,
|
|
117
107
|
escapeStart: usize,
|
|
@@ -233,16 +223,16 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
|
|
|
233
223
|
}
|
|
234
224
|
|
|
235
225
|
const laneIdx = usize(ctz(mask) << 1);
|
|
236
|
-
return
|
|
237
|
-
|
|
226
|
+
return deserializeEscapedString_SIMD(
|
|
227
|
+
payloadStart,
|
|
228
|
+
srcStart + laneIdx,
|
|
229
|
+
srcEnd,
|
|
238
230
|
);
|
|
239
231
|
}
|
|
240
232
|
|
|
241
233
|
while (srcStart < srcEnd) {
|
|
242
234
|
if (load<u16>(srcStart) == BACK_SLASH) {
|
|
243
|
-
return
|
|
244
|
-
deserializeEscapedString_SIMD(payloadStart, srcStart, srcEnd),
|
|
245
|
-
);
|
|
235
|
+
return deserializeEscapedString_SIMD(payloadStart, srcStart, srcEnd);
|
|
246
236
|
}
|
|
247
237
|
srcStart += 2;
|
|
248
238
|
}
|
|
@@ -255,18 +245,14 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
|
|
|
255
245
|
// reused `bs` scratch buffer, then written once via writeStringToField_SIMD.
|
|
256
246
|
//
|
|
257
247
|
// Strategy (validated against run-copy and pure-stream variants across escape
|
|
258
|
-
// densities
|
|
248
|
+
// densities - see __benches__/custom/simd-string-deser-variants-h2h):
|
|
259
249
|
// * Escape-bearing block: a single whole-block v128 store copies the plain
|
|
260
250
|
// prefix for free; then the escape is decoded scalar.
|
|
261
251
|
// * Clean block: stream the first one cheaply, but if the clean run keeps
|
|
262
|
-
// going, switch to one bulk memory.copy for the remainder
|
|
252
|
+
// going, switch to one bulk memory.copy for the remainder - bandwidth-
|
|
263
253
|
// optimal on long sparse runs, avoiding a per-block-store cliff on large
|
|
264
254
|
// inputs. This dominates both alternatives: stream-cheap on dense escapes,
|
|
265
|
-
|
|
266
|
-
//
|
|
267
|
-
// Deliberately NOT @inline: cold escape path. As an @inline it was inlined into
|
|
268
|
-
// deserializeStringField_SIMD at every struct string-field site, exploding
|
|
269
|
-
// `--converge` compile time ~24x on large schemas (4s → 99s) for no runtime
|
|
255
|
+
|
|
270
256
|
// gain. The hot no-escape scan + writeStringToField stay inline in the caller.
|
|
271
257
|
function deserializeEscapedStringField_SIMD(
|
|
272
258
|
payloadStart: usize,
|
|
@@ -387,10 +373,6 @@ function deserializeEscapedStringField_SIMD(
|
|
|
387
373
|
return srcStart;
|
|
388
374
|
}
|
|
389
375
|
|
|
390
|
-
// NOT @inline: as an @inline entry, binaryen inlined the (loop-bearing) escaped
|
|
391
|
-
// scanner into every per-field copy, exploding `large` SIMD compile ~24x
|
|
392
|
-
// (4s→99s, 221KB→555KB wasm). Kept as a single shared function — matches
|
|
393
|
-
// deserializeStringField_SWAR (also non-inline) and costs only one call/field.
|
|
394
376
|
export function deserializeStringField_SIMD<T extends string | null>(
|
|
395
377
|
srcStart: usize,
|
|
396
378
|
srcEnd: usize,
|
|
@@ -427,13 +409,11 @@ export function deserializeStringField_SIMD<T extends string | null>(
|
|
|
427
409
|
return srcIdx + 2;
|
|
428
410
|
}
|
|
429
411
|
// backslash → vectorized escaped scan (no more SWAR fallback)
|
|
430
|
-
return
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
dstFieldPtr,
|
|
436
|
-
),
|
|
412
|
+
return deserializeEscapedStringField_SIMD(
|
|
413
|
+
payloadStart,
|
|
414
|
+
srcIdx,
|
|
415
|
+
srcEnd,
|
|
416
|
+
dstFieldPtr,
|
|
437
417
|
);
|
|
438
418
|
}
|
|
439
419
|
|
|
@@ -448,13 +428,11 @@ export function deserializeStringField_SIMD<T extends string | null>(
|
|
|
448
428
|
return srcStart + 2;
|
|
449
429
|
}
|
|
450
430
|
if (char == BACK_SLASH) {
|
|
451
|
-
return
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
dstFieldPtr,
|
|
457
|
-
),
|
|
431
|
+
return deserializeEscapedStringField_SIMD(
|
|
432
|
+
payloadStart,
|
|
433
|
+
srcStart,
|
|
434
|
+
srcEnd,
|
|
435
|
+
dstFieldPtr,
|
|
458
436
|
);
|
|
459
437
|
}
|
|
460
438
|
srcStart += 2;
|
|
@@ -2,8 +2,7 @@ import { JSON } from "../../..";
|
|
|
2
2
|
import { deserializeGenericArrayBody } from "./generic";
|
|
3
3
|
import { ensureArrayField } from "./shared";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
@inline export function deserializeArbitraryArrayField(
|
|
5
|
+
export function deserializeArbitraryArrayField(
|
|
7
6
|
srcStart: usize,
|
|
8
7
|
srcEnd: usize,
|
|
9
8
|
fieldPtr: usize,
|
|
@@ -3,8 +3,7 @@ import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
|
|
|
3
3
|
import { deserializeFloatArrayBody } from "./float";
|
|
4
4
|
import { ensureArrayField, scanValueEnd, skipWhitespace } from "./shared";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
@inline export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
6
|
+
export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
8
7
|
srcStart: usize,
|
|
9
8
|
srcEnd: usize,
|
|
10
9
|
out: T,
|
|
@@ -91,8 +90,7 @@ import { ensureArrayField, scanValueEnd, skipWhitespace } from "./shared";
|
|
|
91
90
|
throw new Error("Failed to parse JSON!");
|
|
92
91
|
}
|
|
93
92
|
|
|
94
|
-
|
|
95
|
-
@inline export function deserializeArrayArrayField<T extends unknown[][]>(
|
|
93
|
+
export function deserializeArrayArrayField<T extends unknown[][]>(
|
|
96
94
|
srcStart: usize,
|
|
97
95
|
srcEnd: usize,
|
|
98
96
|
fieldPtr: usize,
|
|
@@ -8,8 +8,7 @@ import {
|
|
|
8
8
|
import { isSpace } from "../../../util";
|
|
9
9
|
import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
@inline function deserializeBooleanArrayBody<T extends boolean[]>(
|
|
11
|
+
function deserializeBooleanArrayBody<T extends boolean[]>(
|
|
13
12
|
srcStart: usize,
|
|
14
13
|
srcEnd: usize,
|
|
15
14
|
out: T,
|
|
@@ -60,8 +59,7 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
|
60
59
|
throw new Error("Failed to parse JSON!");
|
|
61
60
|
}
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
@inline export function deserializeBooleanArrayField<T extends boolean[]>(
|
|
62
|
+
export function deserializeBooleanArrayField<T extends boolean[]>(
|
|
65
63
|
srcStart: usize,
|
|
66
64
|
srcEnd: usize,
|
|
67
65
|
fieldPtr: usize,
|
|
@@ -2,8 +2,7 @@ import { JSON } from "../../..";
|
|
|
2
2
|
import { deserializeGenericArrayBody } from "./generic";
|
|
3
3
|
import { ensureArrayField } from "./shared";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
@inline export function deserializeBoxArrayField<T extends JSON.Box<any>[]>(
|
|
5
|
+
export function deserializeBoxArrayField<T extends JSON.Box<any>[]>(
|
|
7
6
|
srcStart: usize,
|
|
8
7
|
srcEnd: usize,
|
|
9
8
|
fieldPtr: usize,
|
|
@@ -8,21 +8,11 @@ import { parse4Digits_PairMul } from "../../../util/swar-int";
|
|
|
8
8
|
import { loadPow10, MAX_EXACT_MANTISSA, MAX_EXACT_POW10 } from "../float";
|
|
9
9
|
import { isSpace } from "../../../util";
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
@inline function skipFloatArrayWhitespace(
|
|
13
|
-
srcStart: usize,
|
|
14
|
-
srcEnd: usize,
|
|
15
|
-
): usize {
|
|
11
|
+
function skipFloatArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
|
|
16
12
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
17
13
|
return srcStart;
|
|
18
14
|
}
|
|
19
|
-
|
|
20
|
-
// @ts-ignore: inline
|
|
21
|
-
@inline function fallbackStore<E>(
|
|
22
|
-
origStart: usize,
|
|
23
|
-
end: usize,
|
|
24
|
-
slot: usize,
|
|
25
|
-
): void {
|
|
15
|
+
function fallbackStore<E>(origStart: usize, end: usize, slot: usize): void {
|
|
26
16
|
const s = ptrToStr(origStart, end);
|
|
27
17
|
if (sizeof<E>() == sizeof<f32>()) {
|
|
28
18
|
store<f32>(slot, f32.parse(s));
|
|
@@ -46,8 +36,7 @@ import { isSpace } from "../../../util";
|
|
|
46
36
|
* digit, lone minus, malformed exponent suffix). Valid-but-out-of-range
|
|
47
37
|
* numbers are handled inline.
|
|
48
38
|
*/
|
|
49
|
-
|
|
50
|
-
@inline export function parseFloatElementSWAR<E>(
|
|
39
|
+
export function parseFloatElementSWAR<E>(
|
|
51
40
|
srcStart: usize,
|
|
52
41
|
srcEnd: usize,
|
|
53
42
|
slot: usize,
|
|
@@ -78,7 +67,7 @@ import { isSpace } from "../../../util";
|
|
|
78
67
|
}
|
|
79
68
|
|
|
80
69
|
// Fractional mantissa: parse4 SWAR stride + scalar tail. Same u64
|
|
81
|
-
// accumulator as the integer part
|
|
70
|
+
// accumulator as the integer part - exponent compensates for fracDigits.
|
|
82
71
|
let fracDigits: i32 = 0;
|
|
83
72
|
if (p < srcEnd && load<u16>(p) == 46) {
|
|
84
73
|
p += 2;
|
|
@@ -165,7 +154,7 @@ import { isSpace } from "../../../util";
|
|
|
165
154
|
// the `ptrToStr` allocation + strtod re-parse.
|
|
166
155
|
result = scientific(mantissa, exponent);
|
|
167
156
|
} else {
|
|
168
|
-
// >19 mantissa digits
|
|
157
|
+
// >19 mantissa digits - beyond u64 capacity, may need strtod's sticky-bit
|
|
169
158
|
// pattern. Hand off to f*.parse on the float's substring.
|
|
170
159
|
fallbackStore<E>(origStart, p, slot);
|
|
171
160
|
return p;
|
|
@@ -256,7 +245,7 @@ export function deserializeFloatArray_SWAR<T extends number[]>(
|
|
|
256
245
|
}
|
|
257
246
|
|
|
258
247
|
/**
|
|
259
|
-
* Field/into variant
|
|
248
|
+
* Field/into variant - parses `[..]` into the existing `out` array and
|
|
260
249
|
* returns the cursor past the closing `]`.
|
|
261
250
|
*
|
|
262
251
|
* Worst-case pre-sizing (`(srcEnd - srcStart) >> 2`) used by the top-level
|
|
@@ -265,7 +254,7 @@ export function deserializeFloatArray_SWAR<T extends number[]>(
|
|
|
265
254
|
* `[lon,lat]` would over-allocate megabytes of f64 capacity. Instead we
|
|
266
255
|
* use `ensureArrayElementSlot`'s grow-or-reuse strategy.
|
|
267
256
|
*/
|
|
268
|
-
|
|
257
|
+
export function deserializeFloatArrayBody<T extends number[]>(
|
|
269
258
|
srcStart: usize,
|
|
270
259
|
srcEnd: usize,
|
|
271
260
|
out: T,
|
|
@@ -316,9 +305,7 @@ export function deserializeFloatArray_SWAR<T extends number[]>(
|
|
|
316
305
|
|
|
317
306
|
throw new Error("Failed to parse JSON!");
|
|
318
307
|
}
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
@inline export function deserializeFloatArrayField<T extends number[]>(
|
|
308
|
+
export function deserializeFloatArrayField<T extends number[]>(
|
|
322
309
|
srcStart: usize,
|
|
323
310
|
srcEnd: usize,
|
|
324
311
|
fieldPtr: usize,
|
|
@@ -7,8 +7,7 @@ import {
|
|
|
7
7
|
skipWhitespace,
|
|
8
8
|
} from "./shared";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
@inline export function deserializeGenericArrayBody<T extends unknown[]>(
|
|
10
|
+
export function deserializeGenericArrayBody<T extends unknown[]>(
|
|
12
11
|
srcStart: usize,
|
|
13
12
|
srcEnd: usize,
|
|
14
13
|
out: T,
|
|
@@ -48,8 +47,7 @@ import {
|
|
|
48
47
|
throw new Error("Failed to parse JSON!");
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
@inline export function deserializeGenericArrayField<T extends unknown[]>(
|
|
50
|
+
export function deserializeGenericArrayField<T extends unknown[]>(
|
|
53
51
|
srcStart: usize,
|
|
54
52
|
srcEnd: usize,
|
|
55
53
|
fieldPtr: usize,
|