json-as 1.5.0 → 1.6.1
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 +10 -1
- package/README.md +42 -0
- package/assembly/deserialize/error.ts +21 -0
- package/assembly/deserialize/index/arbitrary.ts +29 -8
- package/assembly/deserialize/index/array.ts +2 -1
- package/assembly/deserialize/index/bool.ts +1 -1
- package/assembly/deserialize/index/object.ts +3 -0
- package/assembly/deserialize/index/string.ts +37 -0
- package/assembly/deserialize/index.ts +0 -1
- package/assembly/deserialize/naive/array/arbitrary.ts +3 -2
- package/assembly/deserialize/naive/array/array.ts +1 -0
- package/assembly/deserialize/naive/array/bool.ts +21 -11
- package/assembly/deserialize/naive/array/box.ts +13 -33
- package/assembly/deserialize/naive/array/float.ts +23 -13
- package/assembly/deserialize/naive/array/integer.ts +33 -21
- package/assembly/deserialize/naive/array/object.ts +1 -0
- package/assembly/deserialize/naive/array/string.ts +29 -16
- package/assembly/deserialize/naive/bool.ts +17 -5
- package/assembly/deserialize/naive/float.ts +1 -2
- package/assembly/deserialize/naive/map.ts +339 -39
- package/assembly/deserialize/naive/object.ts +122 -65
- package/assembly/deserialize/naive/raw.ts +13 -1
- package/assembly/deserialize/naive/set.ts +0 -1
- package/assembly/deserialize/naive/staticarray.ts +3 -2
- package/assembly/deserialize/naive/string.ts +41 -52
- package/assembly/deserialize/parseMode.ts +216 -0
- package/assembly/deserialize/simd/array/integer.ts +6 -3
- package/assembly/deserialize/simd/float.ts +35 -29
- package/assembly/deserialize/simd/string.ts +79 -17
- package/assembly/deserialize/string-validation.ts +34 -0
- package/assembly/deserialize/swar/array/array.ts +63 -13
- package/assembly/deserialize/swar/array/bool.ts +14 -3
- package/assembly/deserialize/swar/array/float.ts +394 -13
- package/assembly/deserialize/swar/array/generic.ts +12 -2
- package/assembly/deserialize/swar/array/integer.ts +22 -62
- package/assembly/deserialize/swar/array/object.ts +11 -2
- package/assembly/deserialize/swar/array/shared.ts +1 -1
- package/assembly/deserialize/swar/array/string.ts +34 -5
- package/assembly/deserialize/swar/array/struct.ts +109 -15
- package/assembly/deserialize/swar/float.ts +19 -12
- package/assembly/deserialize/swar/string.ts +70 -111
- package/assembly/index.d.ts +6 -0
- package/assembly/index.ts +411 -33
- package/assembly/serialize/index/bool.ts +1 -1
- package/assembly/serialize/index/float.ts +1 -5
- package/assembly/serialize/index/integer.ts +1 -1
- package/assembly/serialize/index/object.ts +17 -0
- package/assembly/serialize/naive/array.ts +64 -0
- package/assembly/serialize/naive/float.ts +0 -5
- package/assembly/serialize/naive/map.ts +37 -0
- package/assembly/serialize/naive/set.ts +0 -1
- package/assembly/serialize/naive/staticarray.ts +0 -1
- package/assembly/serialize/naive/string.ts +0 -6
- package/assembly/serialize/naive/typedarray.ts +0 -1
- package/assembly/util/eisel-lemire.ts +179 -0
- package/assembly/util/prettyWhitespaceSimd.ts +69 -0
- package/assembly/util/scanValueEndSimd.ts +59 -36
- package/assembly/util/validateJson.ts +338 -0
- package/lib/as-bs.ts +28 -6
- package/package.json +17 -7
- package/transform/lib/index.d.ts +6 -0
- package/transform/lib/index.js +773 -112
- package/transform/lib/types.d.ts +1 -0
- package/transform/lib/types.js +3 -0
- package/assembly/deserialize/index/struct.ts +0 -1
- package/assembly/deserialize/naive/struct.ts +0 -21
|
@@ -20,12 +20,22 @@ export function deserializeGenericArrayBody<T extends unknown[]>(
|
|
|
20
20
|
if (load<u16>(srcStart) == BRACKET_RIGHT) return srcStart + 2;
|
|
21
21
|
|
|
22
22
|
let index = 0;
|
|
23
|
+
const reusableLength = load<i32>(
|
|
24
|
+
changetype<usize>(out),
|
|
25
|
+
offsetof<T>("length_"),
|
|
26
|
+
);
|
|
27
|
+
const reusableDataStart = out.dataStart;
|
|
28
|
+
const elementSize = sizeof<valueof<T>>();
|
|
23
29
|
|
|
24
30
|
while (srcStart < srcEnd) {
|
|
25
31
|
const valueEnd = scanValueEnd(srcStart, srcEnd);
|
|
26
32
|
if (!valueEnd || valueEnd <= srcStart) break;
|
|
27
33
|
|
|
28
|
-
const slot =
|
|
34
|
+
const slot =
|
|
35
|
+
index < reusableLength
|
|
36
|
+
? reusableDataStart + <usize>index * elementSize
|
|
37
|
+
: ensureArrayElementSlot<T>(out, index);
|
|
38
|
+
index++;
|
|
29
39
|
store<valueof<T>>(slot, JSON.__deserialize<valueof<T>>(srcStart, valueEnd));
|
|
30
40
|
srcStart = valueEnd;
|
|
31
41
|
|
|
@@ -38,7 +48,7 @@ export function deserializeGenericArrayBody<T extends unknown[]>(
|
|
|
38
48
|
continue;
|
|
39
49
|
}
|
|
40
50
|
if (code == BRACKET_RIGHT) {
|
|
41
|
-
out.length = index;
|
|
51
|
+
if (reusableLength != index) out.length = index;
|
|
42
52
|
return srcStart + 2;
|
|
43
53
|
}
|
|
44
54
|
break;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { deserializeIntegerArray_NAIVE } from "../../naive/array/integer";
|
|
2
2
|
import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
|
|
3
3
|
import { isSpace } from "../../../util";
|
|
4
|
+
import { markProductionParseError } from "../../error";
|
|
4
5
|
import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
5
6
|
import { parse4Digits_PairMul } from "../../../util/swar-int";
|
|
6
7
|
|
|
@@ -40,7 +41,7 @@ function storeUnsignedIntegerE<E extends number>(
|
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
// The
|
|
44
|
+
// The parse helpers below take a `slot` pointer (`writePtr`) and store
|
|
44
45
|
// the value directly via `store<valueof<T>>(slot, ...)`. The outer dispatcher
|
|
45
46
|
// owns the array's `out.length = maxElements` pre-allocation and the
|
|
46
47
|
// `writePtr` advance, so the per-element `Array.push` capacity check and
|
|
@@ -49,57 +50,6 @@ function storeUnsignedIntegerE<E extends number>(
|
|
|
49
50
|
// Parsers are also E-parameterised so they're shareable with
|
|
50
51
|
// `swar/typedarray.ts`. The body is byte-identical to the prior version
|
|
51
52
|
// modulo s/valueof<T>/E/.
|
|
52
|
-
export function parseSignedIntegerScalar<E extends number>(
|
|
53
|
-
srcStart: usize,
|
|
54
|
-
srcEnd: usize,
|
|
55
|
-
slot: usize,
|
|
56
|
-
): usize {
|
|
57
|
-
let negative = false;
|
|
58
|
-
let code = load<u16>(srcStart);
|
|
59
|
-
if (code == 45) {
|
|
60
|
-
negative = true;
|
|
61
|
-
srcStart += 2;
|
|
62
|
-
if (srcStart >= srcEnd) return 0;
|
|
63
|
-
code = load<u16>(srcStart);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
let digit = <u32>code - 48;
|
|
67
|
-
if (digit > 9) return 0;
|
|
68
|
-
|
|
69
|
-
let value: u64 = digit;
|
|
70
|
-
srcStart += 2;
|
|
71
|
-
while (srcStart < srcEnd) {
|
|
72
|
-
digit = <u32>load<u16>(srcStart) - 48;
|
|
73
|
-
if (digit > 9) break;
|
|
74
|
-
value = value * 10 + digit;
|
|
75
|
-
srcStart += 2;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
storeSignedIntegerE<E>(slot, negative ? -(<i64>value) : <i64>value);
|
|
79
|
-
return srcStart;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function parseUnsignedIntegerScalar<E extends number>(
|
|
83
|
-
srcStart: usize,
|
|
84
|
-
srcEnd: usize,
|
|
85
|
-
slot: usize,
|
|
86
|
-
): usize {
|
|
87
|
-
let digit = <u32>load<u16>(srcStart) - 48;
|
|
88
|
-
if (digit > 9) return 0;
|
|
89
|
-
|
|
90
|
-
let value: u64 = digit;
|
|
91
|
-
srcStart += 2;
|
|
92
|
-
while (srcStart < srcEnd) {
|
|
93
|
-
digit = <u32>load<u16>(srcStart) - 48;
|
|
94
|
-
if (digit > 9) break;
|
|
95
|
-
value = value * 10 + digit;
|
|
96
|
-
srcStart += 2;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
storeUnsignedIntegerE<E>(slot, value);
|
|
100
|
-
return srcStart;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
53
|
export function parseSignedIntegerSWAR<E extends number>(
|
|
104
54
|
srcStart: usize,
|
|
105
55
|
srcEnd: usize,
|
|
@@ -227,7 +177,8 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(
|
|
|
227
177
|
|
|
228
178
|
out.length = 0;
|
|
229
179
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) {
|
|
230
|
-
|
|
180
|
+
markProductionParseError();
|
|
181
|
+
return changetype<T>(0);
|
|
231
182
|
}
|
|
232
183
|
|
|
233
184
|
srcStart += 2;
|
|
@@ -295,7 +246,8 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(
|
|
|
295
246
|
break;
|
|
296
247
|
}
|
|
297
248
|
|
|
298
|
-
|
|
249
|
+
markProductionParseError();
|
|
250
|
+
return changetype<T>(0);
|
|
299
251
|
}
|
|
300
252
|
|
|
301
253
|
function deserializeIntegerArrayImpl<T extends number[]>(
|
|
@@ -308,7 +260,10 @@ function deserializeIntegerArrayImpl<T extends number[]>(
|
|
|
308
260
|
dst || changetype<usize>(instantiate<T>()),
|
|
309
261
|
);
|
|
310
262
|
const originalSrcStart = srcStart;
|
|
311
|
-
const reusableLength =
|
|
263
|
+
const reusableLength = load<i32>(
|
|
264
|
+
changetype<usize>(out),
|
|
265
|
+
offsetof<T>("length_"),
|
|
266
|
+
);
|
|
312
267
|
|
|
313
268
|
if (useSWAR && reusableLength != 0) {
|
|
314
269
|
const dataStart = out.dataStart;
|
|
@@ -448,9 +403,11 @@ function deserializeIntegerArrayImpl<T extends number[]>(
|
|
|
448
403
|
|
|
449
404
|
if (isSigned<valueof<T>>()) {
|
|
450
405
|
while (srcStart < srcEnd) {
|
|
451
|
-
const next =
|
|
452
|
-
|
|
453
|
-
|
|
406
|
+
const next = parseSignedIntegerSWAR<valueof<T>>(
|
|
407
|
+
srcStart,
|
|
408
|
+
srcEnd,
|
|
409
|
+
writePtr,
|
|
410
|
+
);
|
|
454
411
|
if (!next) break;
|
|
455
412
|
writePtr += elementSize;
|
|
456
413
|
srcStart = next;
|
|
@@ -469,9 +426,11 @@ function deserializeIntegerArrayImpl<T extends number[]>(
|
|
|
469
426
|
}
|
|
470
427
|
} else {
|
|
471
428
|
while (srcStart < srcEnd) {
|
|
472
|
-
const next =
|
|
473
|
-
|
|
474
|
-
|
|
429
|
+
const next = parseUnsignedIntegerSWAR<valueof<T>>(
|
|
430
|
+
srcStart,
|
|
431
|
+
srcEnd,
|
|
432
|
+
writePtr,
|
|
433
|
+
);
|
|
475
434
|
if (!next) break;
|
|
476
435
|
writePtr += elementSize;
|
|
477
436
|
srcStart = next;
|
|
@@ -686,7 +645,8 @@ function deserializeIntegerArrayBody<T extends number[]>(
|
|
|
686
645
|
}
|
|
687
646
|
} while (false);
|
|
688
647
|
|
|
689
|
-
|
|
648
|
+
markProductionParseError();
|
|
649
|
+
return 0;
|
|
690
650
|
}
|
|
691
651
|
|
|
692
652
|
export function deserializeIntegerArrayField<T extends number[]>(
|
|
@@ -13,6 +13,12 @@ function deserializeObjectArrayBody<T extends unknown[]>(
|
|
|
13
13
|
out: T,
|
|
14
14
|
): usize {
|
|
15
15
|
let index = 0;
|
|
16
|
+
const reusableLength = load<i32>(
|
|
17
|
+
changetype<usize>(out),
|
|
18
|
+
offsetof<T>("length_"),
|
|
19
|
+
);
|
|
20
|
+
const reusableDataStart = out.dataStart;
|
|
21
|
+
const elementSize = sizeof<valueof<T>>();
|
|
16
22
|
|
|
17
23
|
do {
|
|
18
24
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
|
|
@@ -25,7 +31,10 @@ function deserializeObjectArrayBody<T extends unknown[]>(
|
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
while (srcStart < srcEnd) {
|
|
28
|
-
const slot =
|
|
34
|
+
const slot =
|
|
35
|
+
index < reusableLength
|
|
36
|
+
? reusableDataStart + <usize>index * elementSize
|
|
37
|
+
: ensureArrayElementSlot<T>(out, index);
|
|
29
38
|
const valueStart = srcStart;
|
|
30
39
|
const valueEnd = scanValueEnd(valueStart, srcEnd);
|
|
31
40
|
if (!valueEnd) break;
|
|
@@ -48,7 +57,7 @@ function deserializeObjectArrayBody<T extends unknown[]>(
|
|
|
48
57
|
}
|
|
49
58
|
if (code == BRACKET_RIGHT) {
|
|
50
59
|
const nextLen = index + 1;
|
|
51
|
-
if (
|
|
60
|
+
if (reusableLength != nextLen) out.length = nextLen;
|
|
52
61
|
return srcStart + 2;
|
|
53
62
|
}
|
|
54
63
|
break;
|
|
@@ -72,7 +72,7 @@ export function ensureArrayElementSlot<T extends Array<any>>(
|
|
|
72
72
|
|
|
73
73
|
export function scanQuotedValueEnd_SWAR(srcStart: usize, srcEnd: usize): usize {
|
|
74
74
|
srcStart += 2;
|
|
75
|
-
const srcEnd8 = srcEnd
|
|
75
|
+
const srcEnd8 = srcEnd - 8;
|
|
76
76
|
|
|
77
77
|
while (srcStart <= srcEnd8) {
|
|
78
78
|
let mask = backslashOrQuoteMask(load<u64>(srcStart));
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
import { isSpace } from "../../../util";
|
|
8
8
|
import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
9
9
|
import { deserializeStringField_SWAR } from "../string";
|
|
10
|
+
import { markProductionParseError } from "../../error";
|
|
10
11
|
|
|
11
12
|
function skipStringArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
|
|
12
13
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
@@ -19,6 +20,15 @@ function deserializeStringArrayBody<T extends string[]>(
|
|
|
19
20
|
out: T,
|
|
20
21
|
): usize {
|
|
21
22
|
let index = 0;
|
|
23
|
+
// Reused struct fields normally retain their array backing store. Cache the
|
|
24
|
+
// original slot range so the common path avoids an Array.length load and
|
|
25
|
+
// growth branch for every string.
|
|
26
|
+
const reusableLength = load<i32>(
|
|
27
|
+
changetype<usize>(out),
|
|
28
|
+
offsetof<T>("length_"),
|
|
29
|
+
);
|
|
30
|
+
const reusableDataStart = out.dataStart;
|
|
31
|
+
const elementSize = sizeof<valueof<T>>();
|
|
22
32
|
|
|
23
33
|
do {
|
|
24
34
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
|
|
@@ -31,7 +41,10 @@ function deserializeStringArrayBody<T extends string[]>(
|
|
|
31
41
|
}
|
|
32
42
|
|
|
33
43
|
while (srcStart < srcEnd) {
|
|
34
|
-
const slot =
|
|
44
|
+
const slot =
|
|
45
|
+
index < reusableLength
|
|
46
|
+
? reusableDataStart + <usize>index * elementSize
|
|
47
|
+
: ensureArrayElementSlot<T>(out, index);
|
|
35
48
|
// Null fast path for `(string | null)[]`. "null" is 4 UTF-16 chars
|
|
36
49
|
// = 8 bytes, exactly one u64 compare. Store 0 (the AS null reference)
|
|
37
50
|
// and skip 8 bytes.
|
|
@@ -66,14 +79,15 @@ function deserializeStringArrayBody<T extends string[]>(
|
|
|
66
79
|
}
|
|
67
80
|
if (code == BRACKET_RIGHT) {
|
|
68
81
|
const nextLen = index + 1;
|
|
69
|
-
if (
|
|
82
|
+
if (reusableLength != nextLen) out.length = nextLen;
|
|
70
83
|
return srcStart + 2;
|
|
71
84
|
}
|
|
72
85
|
break;
|
|
73
86
|
}
|
|
74
87
|
} while (false);
|
|
75
88
|
|
|
76
|
-
|
|
89
|
+
markProductionParseError();
|
|
90
|
+
return 0;
|
|
77
91
|
}
|
|
78
92
|
|
|
79
93
|
export function deserializeStringArrayField<T extends string[]>(
|
|
@@ -106,6 +120,11 @@ export function deserializeStringArray_SWAR<T extends string[]>(
|
|
|
106
120
|
dst || changetype<usize>(instantiate<T>()),
|
|
107
121
|
);
|
|
108
122
|
|
|
123
|
+
// Top-level exact-range entry. Field arrays use the cursor-returning body
|
|
124
|
+
// above, so trimming here preserves valid document whitespace without
|
|
125
|
+
// touching the hot generated-struct path.
|
|
126
|
+
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
127
|
+
|
|
109
128
|
// Worst-case sizing: shortest possible element is `""` followed by `,` =
|
|
110
129
|
// 6 UTF-16 bytes, so `(srcLen + 5) / 6` upper-bounds the count. Clamp to
|
|
111
130
|
// AS's BLOCK_MAXSIZE / sizeof<string ref> = (1<<28) - 4 elements; payloads
|
|
@@ -126,11 +145,16 @@ export function deserializeStringArray_SWAR<T extends string[]>(
|
|
|
126
145
|
// Caller guarantees srcStart is at the opening `[`.
|
|
127
146
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) {
|
|
128
147
|
out.length = 0;
|
|
129
|
-
|
|
148
|
+
markProductionParseError();
|
|
149
|
+
return changetype<T>(0);
|
|
130
150
|
}
|
|
131
151
|
srcStart += 2;
|
|
132
152
|
srcStart = skipStringArrayWhitespace(srcStart, srcEnd);
|
|
133
153
|
if (srcStart < srcEnd && load<u16>(srcStart) == BRACKET_RIGHT) {
|
|
154
|
+
if (srcStart + 2 != srcEnd) {
|
|
155
|
+
markProductionParseError();
|
|
156
|
+
return changetype<T>(0);
|
|
157
|
+
}
|
|
134
158
|
out.length = 0;
|
|
135
159
|
return out;
|
|
136
160
|
}
|
|
@@ -159,6 +183,10 @@ export function deserializeStringArray_SWAR<T extends string[]>(
|
|
|
159
183
|
continue;
|
|
160
184
|
}
|
|
161
185
|
if (code == BRACKET_RIGHT) {
|
|
186
|
+
if (srcStart + 2 != srcEnd) {
|
|
187
|
+
markProductionParseError();
|
|
188
|
+
return changetype<T>(0);
|
|
189
|
+
}
|
|
162
190
|
const finalLen = i32(<usize>(writePtr - dataStart) / elementSize);
|
|
163
191
|
if (out.length != finalLen) out.length = finalLen;
|
|
164
192
|
return out;
|
|
@@ -166,5 +194,6 @@ export function deserializeStringArray_SWAR<T extends string[]>(
|
|
|
166
194
|
break;
|
|
167
195
|
}
|
|
168
196
|
|
|
169
|
-
|
|
197
|
+
markProductionParseError();
|
|
198
|
+
return changetype<T>(0);
|
|
170
199
|
}
|
|
@@ -1,8 +1,33 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
BRACE_LEFT,
|
|
3
|
+
BRACKET_LEFT,
|
|
4
|
+
BRACKET_RIGHT,
|
|
5
|
+
COMMA,
|
|
6
|
+
} from "../../../custom/chars";
|
|
2
7
|
import { isSpace } from "../../../util";
|
|
3
|
-
import {
|
|
8
|
+
import {
|
|
9
|
+
ensureArrayElementSlot,
|
|
10
|
+
ensureArrayField,
|
|
11
|
+
scanValueEnd,
|
|
12
|
+
} from "./shared";
|
|
13
|
+
import { markProductionParseError } from "../../error";
|
|
4
14
|
|
|
15
|
+
|
|
16
|
+
@inline
|
|
5
17
|
function skipStructArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
|
|
18
|
+
// JSON.stringify(..., null, 2) at a root struct-array boundary emits exactly
|
|
19
|
+
// LF + two spaces + the next object (or LF + closing bracket). Keep that
|
|
20
|
+
// overwhelmingly common shape to two scalar loads; nested/foreign layouts
|
|
21
|
+
// retain the fully general fallback below.
|
|
22
|
+
if (
|
|
23
|
+
ASC_FEATURE_SIMD &&
|
|
24
|
+
srcStart + 8 <= srcEnd &&
|
|
25
|
+
load<u16>(srcStart) == 10 &&
|
|
26
|
+
load<u32>(srcStart, 2) == 0x0020_0020
|
|
27
|
+
) {
|
|
28
|
+
const next = load<u16>(srcStart, 6);
|
|
29
|
+
if (next == BRACE_LEFT || next == BRACKET_RIGHT) return srcStart + 6;
|
|
30
|
+
}
|
|
6
31
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
7
32
|
return srcStart;
|
|
8
33
|
}
|
|
@@ -28,11 +53,22 @@ function deserializeStructArrayBody<T extends unknown[]>(
|
|
|
28
53
|
out: T,
|
|
29
54
|
): usize {
|
|
30
55
|
let index = 0;
|
|
56
|
+
const reusableLength = load<i32>(
|
|
57
|
+
changetype<usize>(out),
|
|
58
|
+
offsetof<T>("length_"),
|
|
59
|
+
);
|
|
60
|
+
const reusableDataStart = out.dataStart;
|
|
61
|
+
const elementSize = sizeof<valueof<T>>();
|
|
62
|
+
let pretty = false;
|
|
31
63
|
|
|
32
64
|
do {
|
|
33
65
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
|
|
34
66
|
srcStart += 2;
|
|
35
|
-
srcStart
|
|
67
|
+
if (srcStart < srcEnd && load<u16>(srcStart) != BRACE_LEFT) {
|
|
68
|
+
const code = load<u16>(srcStart);
|
|
69
|
+
pretty = ASC_FEATURE_SIMD && (code == 10 || code == 13);
|
|
70
|
+
srcStart = skipStructArrayWhitespace(srcStart, srcEnd);
|
|
71
|
+
}
|
|
36
72
|
if (srcStart >= srcEnd) break;
|
|
37
73
|
if (load<u16>(srcStart) == BRACKET_RIGHT) {
|
|
38
74
|
out.length = 0;
|
|
@@ -40,7 +76,10 @@ function deserializeStructArrayBody<T extends unknown[]>(
|
|
|
40
76
|
}
|
|
41
77
|
|
|
42
78
|
while (srcStart < srcEnd) {
|
|
43
|
-
const slot =
|
|
79
|
+
const slot =
|
|
80
|
+
index < reusableLength
|
|
81
|
+
? reusableDataStart + <usize>index * elementSize
|
|
82
|
+
: ensureArrayElementSlot<T>(out, index);
|
|
44
83
|
let value = load<valueof<T>>(slot);
|
|
45
84
|
if (changetype<usize>(value) == 0) {
|
|
46
85
|
value = changetype<valueof<T>>(
|
|
@@ -54,31 +93,70 @@ function deserializeStructArrayBody<T extends unknown[]>(
|
|
|
54
93
|
store<valueof<T>>(slot, value);
|
|
55
94
|
}
|
|
56
95
|
|
|
96
|
+
const valueStart = srcStart;
|
|
57
97
|
let next: usize = 0;
|
|
58
98
|
if (
|
|
59
99
|
// @ts-ignore: supplied by transform
|
|
60
100
|
isDefined(changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST)
|
|
61
101
|
) {
|
|
102
|
+
// Omit the explicit type arg so AS infers it from `value` — passing
|
|
103
|
+
// `<valueof<T>>` explicitly collides when the element type is itself
|
|
104
|
+
// generic (e.g. GenericTest<string>) because AS resolves `T` against
|
|
105
|
+
// the element class's own generic rather than this function's `T`.
|
|
62
106
|
// @ts-ignore: supplied by transform
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
107
|
+
// @ts-ignore: supplied by transform in SIMD builds
|
|
108
|
+
if (
|
|
109
|
+
pretty &&
|
|
110
|
+
isDefined(
|
|
111
|
+
changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST_PRETTY,
|
|
112
|
+
)
|
|
113
|
+
) {
|
|
114
|
+
// @ts-ignore: supplied by transform
|
|
115
|
+
next = changetype<nonnull<valueof<T>>>(
|
|
116
|
+
value,
|
|
117
|
+
).__DESERIALIZE_FAST_PRETTY(srcStart, srcEnd, value);
|
|
118
|
+
} else {
|
|
119
|
+
next = changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST(
|
|
120
|
+
srcStart,
|
|
121
|
+
srcEnd,
|
|
122
|
+
value,
|
|
123
|
+
);
|
|
124
|
+
}
|
|
66
125
|
}
|
|
67
126
|
if (!next) {
|
|
127
|
+
// __DESERIALIZE_SLOW requires srcEnd to point just past the closing
|
|
128
|
+
// `}` of *this* element (it asserts srcEnd-2 == `}`). Scan the value
|
|
129
|
+
// boundary first, then re-initialize the object so any partial state
|
|
130
|
+
// from the failed FAST attempt is cleared.
|
|
131
|
+
const valueEnd = scanValueEnd(valueStart, srcEnd);
|
|
132
|
+
if (!valueEnd) break;
|
|
133
|
+
// @ts-ignore: supplied by transform
|
|
134
|
+
if (isDefined(changetype<nonnull<valueof<T>>>(value).__INITIALIZE)) {
|
|
135
|
+
// @ts-ignore: supplied by transform
|
|
136
|
+
changetype<nonnull<valueof<T>>>(value).__INITIALIZE();
|
|
137
|
+
}
|
|
68
138
|
// @ts-ignore: supplied by transform
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
139
|
+
changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_SLOW(
|
|
140
|
+
valueStart,
|
|
141
|
+
valueEnd,
|
|
142
|
+
value,
|
|
143
|
+
);
|
|
144
|
+
next = valueEnd;
|
|
72
145
|
}
|
|
73
146
|
if (!next) break;
|
|
74
147
|
srcStart = next;
|
|
75
|
-
srcStart = skipStructArrayWhitespace(srcStart, srcEnd);
|
|
76
148
|
if (srcStart >= srcEnd) break;
|
|
77
149
|
|
|
78
|
-
|
|
150
|
+
let code = load<u16>(srcStart);
|
|
151
|
+
if (code != COMMA && code != BRACKET_RIGHT) {
|
|
152
|
+
srcStart = skipStructArrayWhitespace(srcStart, srcEnd);
|
|
153
|
+
if (srcStart >= srcEnd) break;
|
|
154
|
+
code = load<u16>(srcStart);
|
|
155
|
+
}
|
|
79
156
|
if (code == COMMA) {
|
|
80
157
|
srcStart += 2;
|
|
81
|
-
srcStart
|
|
158
|
+
if (srcStart < srcEnd && load<u16>(srcStart) != BRACE_LEFT)
|
|
159
|
+
srcStart = skipStructArrayWhitespace(srcStart, srcEnd);
|
|
82
160
|
index++;
|
|
83
161
|
continue;
|
|
84
162
|
}
|
|
@@ -87,15 +165,31 @@ function deserializeStructArrayBody<T extends unknown[]>(
|
|
|
87
165
|
// right length (e.g. canada-style geometry rings whose count
|
|
88
166
|
// matches a previous parse).
|
|
89
167
|
const nextLen = index + 1;
|
|
90
|
-
if (
|
|
168
|
+
if (reusableLength != nextLen) out.length = nextLen;
|
|
91
169
|
return srcStart + 2;
|
|
92
170
|
}
|
|
93
171
|
break;
|
|
94
172
|
}
|
|
95
173
|
} while (false);
|
|
96
174
|
|
|
97
|
-
|
|
175
|
+
markProductionParseError();
|
|
176
|
+
return 0;
|
|
98
177
|
}
|
|
178
|
+
export function deserializeStructArray_SWAR<T extends unknown[]>(
|
|
179
|
+
srcStart: usize,
|
|
180
|
+
srcEnd: usize,
|
|
181
|
+
dst: usize,
|
|
182
|
+
): T {
|
|
183
|
+
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
184
|
+
const out = changetype<nonnull<T>>(
|
|
185
|
+
dst || changetype<usize>(instantiate<T>()),
|
|
186
|
+
);
|
|
187
|
+
const end = deserializeStructArrayBody<T>(srcStart, srcEnd, out);
|
|
188
|
+
if (end == srcEnd) return out;
|
|
189
|
+
markProductionParseError();
|
|
190
|
+
return changetype<T>(0);
|
|
191
|
+
}
|
|
192
|
+
|
|
99
193
|
export function deserializeStructArrayField<T extends unknown[]>(
|
|
100
194
|
srcStart: usize,
|
|
101
195
|
srcEnd: usize,
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
import { ptrToStr } from "../../util/ptrToStr";
|
|
20
20
|
import { parse4Digits_PairMul } from "../../util/swar-int";
|
|
21
21
|
import { scientific } from "../../util/scientific";
|
|
22
|
+
import { eiselLemire22 } from "../../util/eisel-lemire";
|
|
22
23
|
|
|
23
24
|
export const POW10_F64_POS: usize = memory.data<f64>([
|
|
24
25
|
1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14,
|
|
@@ -72,37 +73,37 @@ export function deserializeFloat_SWAR<T>(srcStart: usize, srcEnd: usize): T {
|
|
|
72
73
|
// Integer part: scalar. Most JSON integers are 1-3 digits, so parse4 would
|
|
73
74
|
// waste a validate per call.
|
|
74
75
|
let mantissa: u64 = 0;
|
|
75
|
-
|
|
76
|
+
const intStart = p;
|
|
76
77
|
while (p < srcEnd) {
|
|
77
78
|
const d = <u32>load<u16>(p) - ASCII_ZERO;
|
|
78
79
|
if (d > 9) break;
|
|
79
80
|
mantissa = mantissa * 10 + <u64>d;
|
|
80
|
-
intDigits++;
|
|
81
81
|
p += 2;
|
|
82
82
|
}
|
|
83
|
+
const intDigits = <i32>((p - intStart) >> 1);
|
|
83
84
|
|
|
84
85
|
// Fractional part: parse4 stride (8 bytes / 4 digits) → scalar tail.
|
|
85
86
|
// parse8 was tried and benchmarked even/worse; the saved mantissa mul
|
|
86
87
|
// didn't outweigh the extra load and combined-validation latency, and the
|
|
87
88
|
// dependency chain is the same length either way.
|
|
88
|
-
let
|
|
89
|
+
let fracStart = p;
|
|
89
90
|
if (p < srcEnd && load<u16>(p) == ASCII_DOT) {
|
|
90
91
|
p += 2;
|
|
92
|
+
fracStart = p;
|
|
91
93
|
while (p + 6 < srcEnd) {
|
|
92
94
|
const parsed = parse4Digits_PairMul(load<u64>(p));
|
|
93
95
|
if (parsed == U32.MAX_VALUE) break;
|
|
94
96
|
mantissa = mantissa * 10_000 + <u64>parsed;
|
|
95
|
-
fracDigits += 4;
|
|
96
97
|
p += 8;
|
|
97
98
|
}
|
|
98
99
|
while (p < srcEnd) {
|
|
99
100
|
const d = <u32>load<u16>(p) - ASCII_ZERO;
|
|
100
101
|
if (d > 9) break;
|
|
101
102
|
mantissa = mantissa * 10 + <u64>d;
|
|
102
|
-
fracDigits++;
|
|
103
103
|
p += 2;
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
+
const fracDigits = <i32>((p - fracStart) >> 1);
|
|
106
107
|
|
|
107
108
|
const mantDigits = intDigits + fracDigits;
|
|
108
109
|
if (mantDigits == 0) return fallback<T>(origStart, srcEnd);
|
|
@@ -158,7 +159,10 @@ export function deserializeFloat_SWAR<T>(srcStart: usize, srcEnd: usize): T {
|
|
|
158
159
|
result /= loadPow10(<u32>-exponent);
|
|
159
160
|
}
|
|
160
161
|
} else if (mantDigits <= 19) {
|
|
161
|
-
result =
|
|
162
|
+
result =
|
|
163
|
+
exponent >= -22 && exponent <= 22
|
|
164
|
+
? eiselLemire22(mantissa, exponent)
|
|
165
|
+
: scientific(mantissa, exponent);
|
|
162
166
|
} else {
|
|
163
167
|
return fallback<T>(origStart, srcEnd);
|
|
164
168
|
}
|
|
@@ -188,33 +192,33 @@ export function deserializeFloatField_SWAR<T extends number>(
|
|
|
188
192
|
}
|
|
189
193
|
|
|
190
194
|
let mantissa: u64 = 0;
|
|
191
|
-
|
|
195
|
+
const intStart = p;
|
|
192
196
|
while (p < srcEnd) {
|
|
193
197
|
const d = <u32>load<u16>(p) - ASCII_ZERO;
|
|
194
198
|
if (d > 9) break;
|
|
195
199
|
mantissa = mantissa * 10 + <u64>d;
|
|
196
|
-
intDigits++;
|
|
197
200
|
p += 2;
|
|
198
201
|
}
|
|
202
|
+
const intDigits = <i32>((p - intStart) >> 1);
|
|
199
203
|
|
|
200
|
-
let
|
|
204
|
+
let fracStart = p;
|
|
201
205
|
if (p < srcEnd && load<u16>(p) == ASCII_DOT) {
|
|
202
206
|
p += 2;
|
|
207
|
+
fracStart = p;
|
|
203
208
|
while (p + 6 < srcEnd) {
|
|
204
209
|
const parsed = parse4Digits_PairMul(load<u64>(p));
|
|
205
210
|
if (parsed == U32.MAX_VALUE) break;
|
|
206
211
|
mantissa = mantissa * 10_000 + <u64>parsed;
|
|
207
|
-
fracDigits += 4;
|
|
208
212
|
p += 8;
|
|
209
213
|
}
|
|
210
214
|
while (p < srcEnd) {
|
|
211
215
|
const d = <u32>load<u16>(p) - ASCII_ZERO;
|
|
212
216
|
if (d > 9) break;
|
|
213
217
|
mantissa = mantissa * 10 + <u64>d;
|
|
214
|
-
fracDigits++;
|
|
215
218
|
p += 2;
|
|
216
219
|
}
|
|
217
220
|
}
|
|
221
|
+
const fracDigits = <i32>((p - fracStart) >> 1);
|
|
218
222
|
|
|
219
223
|
const mantDigits = intDigits + fracDigits;
|
|
220
224
|
if (mantDigits == 0) unreachable();
|
|
@@ -277,7 +281,10 @@ export function deserializeFloatField_SWAR<T extends number>(
|
|
|
277
281
|
result /= loadPow10(<u32>-exponent);
|
|
278
282
|
}
|
|
279
283
|
} else if (mantDigits <= 19) {
|
|
280
|
-
result =
|
|
284
|
+
result =
|
|
285
|
+
exponent >= -22 && exponent <= 22
|
|
286
|
+
? eiselLemire22(mantissa, exponent)
|
|
287
|
+
: scientific(mantissa, exponent);
|
|
281
288
|
} else {
|
|
282
289
|
fallbackField<T>(origStart, p, fieldPtr);
|
|
283
290
|
return p;
|