json-as 1.4.0 → 1.6.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 +54 -29
- package/README.md +126 -33
- package/assembly/custom/chars.ts +39 -78
- package/assembly/deserialize/error.ts +21 -0
- package/assembly/deserialize/index/arbitrary.ts +50 -11
- package/assembly/deserialize/index/array.ts +2 -1
- package/assembly/deserialize/index/bool.ts +1 -1
- package/assembly/deserialize/index/float.ts +2 -4
- package/assembly/deserialize/index/integer.ts +2 -4
- package/assembly/deserialize/index/object.ts +9 -1
- package/assembly/deserialize/index/string.ts +39 -7
- package/assembly/deserialize/index/unsigned.ts +2 -4
- 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 +34 -22
- package/assembly/deserialize/naive/array/map.ts +1 -1
- package/assembly/deserialize/naive/array/object.ts +2 -1
- package/assembly/deserialize/naive/array/string.ts +29 -16
- package/assembly/deserialize/naive/array/struct.ts +19 -1
- package/assembly/deserialize/naive/bool.ts +18 -10
- package/assembly/deserialize/naive/date.ts +1 -2
- package/assembly/deserialize/naive/float.ts +3 -9
- package/assembly/deserialize/naive/integer.ts +1 -2
- package/assembly/deserialize/naive/map.ts +342 -43
- package/assembly/deserialize/naive/object.ts +250 -55
- package/assembly/deserialize/naive/raw.ts +10 -2
- package/assembly/deserialize/naive/set.ts +2 -5
- package/assembly/deserialize/naive/staticarray.ts +4 -4
- package/assembly/deserialize/naive/string.ts +46 -60
- package/assembly/deserialize/naive/unsigned.ts +1 -2
- package/assembly/deserialize/parseMode.ts +216 -0
- package/assembly/deserialize/simd/array/integer.ts +8 -10
- package/assembly/deserialize/simd/float.ts +38 -34
- package/assembly/deserialize/simd/integer.ts +2 -7
- package/assembly/deserialize/simd/string.ts +84 -39
- package/assembly/deserialize/string-validation.ts +34 -0
- package/assembly/deserialize/swar/array/arbitrary.ts +1 -2
- package/assembly/deserialize/swar/array/array.ts +64 -15
- package/assembly/deserialize/swar/array/bool.ts +15 -5
- package/assembly/deserialize/swar/array/box.ts +1 -2
- package/assembly/deserialize/swar/array/float.ts +399 -30
- package/assembly/deserialize/swar/array/generic.ts +13 -4
- package/assembly/deserialize/swar/array/integer.ts +29 -78
- package/assembly/deserialize/swar/array/map.ts +1 -2
- package/assembly/deserialize/swar/array/object.ts +12 -4
- package/assembly/deserialize/swar/array/raw.ts +1 -2
- package/assembly/deserialize/swar/array/shared.ts +7 -14
- package/assembly/deserialize/swar/array/string.ts +37 -13
- package/assembly/deserialize/swar/array/struct.ts +109 -21
- package/assembly/deserialize/swar/array.ts +1 -3
- package/assembly/deserialize/swar/float.ts +23 -21
- package/assembly/deserialize/swar/integer.ts +2 -7
- package/assembly/deserialize/swar/string.ts +81 -124
- package/assembly/deserialize/swar/typedarray.ts +4 -4
- package/assembly/index.d.ts +35 -24
- package/assembly/index.ts +1775 -281
- package/assembly/serialize/index/arbitrary.ts +70 -4
- 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/jsonarray.ts +51 -0
- package/assembly/serialize/index/object.ts +42 -3
- 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 +87 -34
- package/assembly/serialize/naive/bool.ts +0 -1
- package/assembly/serialize/naive/float.ts +15 -29
- package/assembly/serialize/naive/integer.ts +1 -5
- package/assembly/serialize/naive/map.ts +37 -0
- package/assembly/serialize/naive/raw.ts +1 -2
- package/assembly/serialize/naive/set.ts +0 -5
- package/assembly/serialize/naive/staticarray.ts +0 -6
- package/assembly/serialize/naive/string.ts +1 -10
- package/assembly/serialize/naive/typedarray.ts +0 -7
- package/assembly/serialize/simd/string.ts +1 -3
- package/assembly/serialize/swar/string.ts +1 -2
- package/assembly/util/atoi-fast.ts +4 -14
- package/assembly/util/bytes.ts +1 -2
- package/assembly/util/eisel-lemire.ts +179 -0
- package/assembly/util/idofd.ts +1 -2
- package/assembly/util/isSpace.ts +1 -2
- package/assembly/util/itoa-fast.ts +6 -9
- package/assembly/util/nextPowerOf2.ts +1 -2
- package/assembly/util/parsefloat-fast.ts +3 -5
- package/assembly/util/prettyWhitespaceSimd.ts +69 -0
- package/assembly/util/ptrToStr.ts +1 -2
- package/assembly/util/scanValueEndSimd.ts +83 -22
- package/assembly/util/scanValueEndSwar.ts +67 -25
- package/assembly/util/scientific.ts +5 -8
- package/assembly/util/snp.ts +1 -2
- package/assembly/util/swar-int.ts +5 -10
- package/assembly/util/swar.ts +2 -4
- package/assembly/util/validateJson.ts +338 -0
- package/lib/as-bs.ts +51 -51
- package/package.json +25 -8
- package/transform/lib/index.d.ts +6 -0
- package/transform/lib/index.js +874 -171
- package/transform/lib/types.d.ts +3 -1
- package/transform/lib/types.js +6 -0
- package/assembly/deserialize/index/struct.ts +0 -1
- package/assembly/deserialize/naive/struct.ts +0 -21
- package/assembly/util/dragonbox-cache.ts +0 -445
- package/assembly/util/dragonbox.ts +0 -652
|
@@ -1,18 +1,16 @@
|
|
|
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
|
|
|
7
8
|
// Store helpers parameterised on the element type `E` directly, so they
|
|
8
9
|
// serve both `Array<E>` and `TypedArray<E>` callers. The integer-array
|
|
9
10
|
// callers below all pass `valueof<T>` and AS folds the resulting tower of
|
|
10
|
-
// `sizeof<E>` comparisons at compile time
|
|
11
|
+
// `sizeof<E>` comparisons at compile time - same codegen as the prior
|
|
11
12
|
// `T extends number[]` version, but reusable from `swar/typedarray.ts`.
|
|
12
|
-
|
|
13
|
-
slot: usize,
|
|
14
|
-
value: i64,
|
|
15
|
-
): void {
|
|
13
|
+
function storeSignedIntegerE<E extends number>(slot: usize, value: i64): void {
|
|
16
14
|
if (sizeof<E>() == sizeof<i8>()) {
|
|
17
15
|
store<i8>(slot, <i8>value);
|
|
18
16
|
} else if (sizeof<E>() == sizeof<i16>()) {
|
|
@@ -26,8 +24,7 @@ import { parse4Digits_PairMul } from "../../../util/swar-int";
|
|
|
26
24
|
}
|
|
27
25
|
}
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
@inline function storeUnsignedIntegerE<E extends number>(
|
|
27
|
+
function storeUnsignedIntegerE<E extends number>(
|
|
31
28
|
slot: usize,
|
|
32
29
|
value: u64,
|
|
33
30
|
): void {
|
|
@@ -44,7 +41,7 @@ import { parse4Digits_PairMul } from "../../../util/swar-int";
|
|
|
44
41
|
}
|
|
45
42
|
}
|
|
46
43
|
|
|
47
|
-
// The
|
|
44
|
+
// The parse helpers below take a `slot` pointer (`writePtr`) and store
|
|
48
45
|
// the value directly via `store<valueof<T>>(slot, ...)`. The outer dispatcher
|
|
49
46
|
// owns the array's `out.length = maxElements` pre-allocation and the
|
|
50
47
|
// `writePtr` advance, so the per-element `Array.push` capacity check and
|
|
@@ -53,57 +50,6 @@ import { parse4Digits_PairMul } from "../../../util/swar-int";
|
|
|
53
50
|
// Parsers are also E-parameterised so they're shareable with
|
|
54
51
|
// `swar/typedarray.ts`. The body is byte-identical to the prior version
|
|
55
52
|
// modulo s/valueof<T>/E/.
|
|
56
|
-
export function parseSignedIntegerScalar<E extends number>(
|
|
57
|
-
srcStart: usize,
|
|
58
|
-
srcEnd: usize,
|
|
59
|
-
slot: usize,
|
|
60
|
-
): usize {
|
|
61
|
-
let negative = false;
|
|
62
|
-
let code = load<u16>(srcStart);
|
|
63
|
-
if (code == 45) {
|
|
64
|
-
negative = true;
|
|
65
|
-
srcStart += 2;
|
|
66
|
-
if (srcStart >= srcEnd) return 0;
|
|
67
|
-
code = load<u16>(srcStart);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
let digit = <u32>code - 48;
|
|
71
|
-
if (digit > 9) return 0;
|
|
72
|
-
|
|
73
|
-
let value: u64 = digit;
|
|
74
|
-
srcStart += 2;
|
|
75
|
-
while (srcStart < srcEnd) {
|
|
76
|
-
digit = <u32>load<u16>(srcStart) - 48;
|
|
77
|
-
if (digit > 9) break;
|
|
78
|
-
value = value * 10 + digit;
|
|
79
|
-
srcStart += 2;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
storeSignedIntegerE<E>(slot, negative ? -(<i64>value) : <i64>value);
|
|
83
|
-
return srcStart;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export function parseUnsignedIntegerScalar<E extends number>(
|
|
87
|
-
srcStart: usize,
|
|
88
|
-
srcEnd: usize,
|
|
89
|
-
slot: usize,
|
|
90
|
-
): usize {
|
|
91
|
-
let digit = <u32>load<u16>(srcStart) - 48;
|
|
92
|
-
if (digit > 9) return 0;
|
|
93
|
-
|
|
94
|
-
let value: u64 = digit;
|
|
95
|
-
srcStart += 2;
|
|
96
|
-
while (srcStart < srcEnd) {
|
|
97
|
-
digit = <u32>load<u16>(srcStart) - 48;
|
|
98
|
-
if (digit > 9) break;
|
|
99
|
-
value = value * 10 + digit;
|
|
100
|
-
srcStart += 2;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
storeUnsignedIntegerE<E>(slot, value);
|
|
104
|
-
return srcStart;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
53
|
export function parseSignedIntegerSWAR<E extends number>(
|
|
108
54
|
srcStart: usize,
|
|
109
55
|
srcEnd: usize,
|
|
@@ -211,11 +157,7 @@ export function parseUnsignedIntegerSWAR<E extends number>(
|
|
|
211
157
|
return srcStart;
|
|
212
158
|
}
|
|
213
159
|
|
|
214
|
-
|
|
215
|
-
@inline function skipIntegerArrayWhitespace(
|
|
216
|
-
srcStart: usize,
|
|
217
|
-
srcEnd: usize,
|
|
218
|
-
): usize {
|
|
160
|
+
function skipIntegerArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
|
|
219
161
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) {
|
|
220
162
|
srcStart += 2;
|
|
221
163
|
}
|
|
@@ -235,7 +177,8 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(
|
|
|
235
177
|
|
|
236
178
|
out.length = 0;
|
|
237
179
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) {
|
|
238
|
-
|
|
180
|
+
markProductionParseError();
|
|
181
|
+
return changetype<T>(0);
|
|
239
182
|
}
|
|
240
183
|
|
|
241
184
|
srcStart += 2;
|
|
@@ -303,7 +246,8 @@ export function deserializeIntegerArray_SLOW<T extends number[]>(
|
|
|
303
246
|
break;
|
|
304
247
|
}
|
|
305
248
|
|
|
306
|
-
|
|
249
|
+
markProductionParseError();
|
|
250
|
+
return changetype<T>(0);
|
|
307
251
|
}
|
|
308
252
|
|
|
309
253
|
function deserializeIntegerArrayImpl<T extends number[]>(
|
|
@@ -316,7 +260,10 @@ function deserializeIntegerArrayImpl<T extends number[]>(
|
|
|
316
260
|
dst || changetype<usize>(instantiate<T>()),
|
|
317
261
|
);
|
|
318
262
|
const originalSrcStart = srcStart;
|
|
319
|
-
const reusableLength =
|
|
263
|
+
const reusableLength = load<i32>(
|
|
264
|
+
changetype<usize>(out),
|
|
265
|
+
offsetof<T>("length_"),
|
|
266
|
+
);
|
|
320
267
|
|
|
321
268
|
if (useSWAR && reusableLength != 0) {
|
|
322
269
|
const dataStart = out.dataStart;
|
|
@@ -456,9 +403,11 @@ function deserializeIntegerArrayImpl<T extends number[]>(
|
|
|
456
403
|
|
|
457
404
|
if (isSigned<valueof<T>>()) {
|
|
458
405
|
while (srcStart < srcEnd) {
|
|
459
|
-
const next =
|
|
460
|
-
|
|
461
|
-
|
|
406
|
+
const next = parseSignedIntegerSWAR<valueof<T>>(
|
|
407
|
+
srcStart,
|
|
408
|
+
srcEnd,
|
|
409
|
+
writePtr,
|
|
410
|
+
);
|
|
462
411
|
if (!next) break;
|
|
463
412
|
writePtr += elementSize;
|
|
464
413
|
srcStart = next;
|
|
@@ -477,9 +426,11 @@ function deserializeIntegerArrayImpl<T extends number[]>(
|
|
|
477
426
|
}
|
|
478
427
|
} else {
|
|
479
428
|
while (srcStart < srcEnd) {
|
|
480
|
-
const next =
|
|
481
|
-
|
|
482
|
-
|
|
429
|
+
const next = parseUnsignedIntegerSWAR<valueof<T>>(
|
|
430
|
+
srcStart,
|
|
431
|
+
srcEnd,
|
|
432
|
+
writePtr,
|
|
433
|
+
);
|
|
483
434
|
if (!next) break;
|
|
484
435
|
writePtr += elementSize;
|
|
485
436
|
srcStart = next;
|
|
@@ -661,7 +612,7 @@ function deserializeIntegerArrayBody<T extends number[]>(
|
|
|
661
612
|
const slot = ensureArrayElementSlot<T>(out, index);
|
|
662
613
|
// Inline the array-optimized SWAR parser directly. The top-level
|
|
663
614
|
// (`deserializeIntegerArrayImpl`) and the typed-array path
|
|
664
|
-
// (`swar/typedarray.ts`) already call these
|
|
615
|
+
// (`swar/typedarray.ts`) already call these - having the field path
|
|
665
616
|
// call them too means the per-element parser is identical across all
|
|
666
617
|
// three call sites (parse4 + scalar fold for both signed and
|
|
667
618
|
// unsigned, narrow-lane special case for i8/u8/i16/u16).
|
|
@@ -669,7 +620,7 @@ function deserializeIntegerArrayBody<T extends number[]>(
|
|
|
669
620
|
// Why not `deserializeUnsignedField_SWAR` (which uses parse8 + scalar)?
|
|
670
621
|
// That tuning targets the struct-single-field path where the digit
|
|
671
622
|
// run is one aligned token. In an array, mixed element widths cause
|
|
672
|
-
// parse8 to fail-and-retry at element boundaries
|
|
623
|
+
// parse8 to fail-and-retry at element boundaries - see u32-64mib's
|
|
673
624
|
// 23% regression when parse8 was tried in the array path.
|
|
674
625
|
srcStart = isSigned<valueof<T>>()
|
|
675
626
|
? parseSignedIntegerSWAR<valueof<T>>(srcStart, srcEnd, slot)
|
|
@@ -694,11 +645,11 @@ function deserializeIntegerArrayBody<T extends number[]>(
|
|
|
694
645
|
}
|
|
695
646
|
} while (false);
|
|
696
647
|
|
|
697
|
-
|
|
648
|
+
markProductionParseError();
|
|
649
|
+
return 0;
|
|
698
650
|
}
|
|
699
651
|
|
|
700
|
-
|
|
701
|
-
@inline export function deserializeIntegerArrayField<T extends number[]>(
|
|
652
|
+
export function deserializeIntegerArrayField<T extends number[]>(
|
|
702
653
|
srcStart: usize,
|
|
703
654
|
srcEnd: usize,
|
|
704
655
|
fieldPtr: usize,
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { deserializeGenericArrayBody } from "./generic";
|
|
2
2
|
import { ensureArrayField } from "./shared";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
@inline export function deserializeMapArrayField<T extends Map<any, any>[]>(
|
|
4
|
+
export function deserializeMapArrayField<T extends Map<any, any>[]>(
|
|
6
5
|
srcStart: usize,
|
|
7
6
|
srcEnd: usize,
|
|
8
7
|
fieldPtr: usize,
|
|
@@ -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;
|
|
@@ -58,8 +67,7 @@ function deserializeObjectArrayBody<T extends unknown[]>(
|
|
|
58
67
|
throw new Error("Failed to parse JSON!");
|
|
59
68
|
}
|
|
60
69
|
|
|
61
|
-
|
|
62
|
-
@inline export function deserializeObjectArrayField<T extends unknown[]>(
|
|
70
|
+
export function deserializeObjectArrayField<T extends unknown[]>(
|
|
63
71
|
srcStart: usize,
|
|
64
72
|
srcEnd: usize,
|
|
65
73
|
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 deserializeRawArrayField(
|
|
5
|
+
export function deserializeRawArrayField(
|
|
7
6
|
srcStart: usize,
|
|
8
7
|
srcEnd: usize,
|
|
9
8
|
fieldPtr: usize,
|
|
@@ -10,16 +10,12 @@ import {
|
|
|
10
10
|
import { isSpace } from "../../../util";
|
|
11
11
|
|
|
12
12
|
/** Advance past JSON whitespace (space, tab, LF, CR). */
|
|
13
|
-
|
|
14
|
-
@inline export function skipWhitespace(srcStart: usize, srcEnd: usize): usize {
|
|
13
|
+
export function skipWhitespace(srcStart: usize, srcEnd: usize): usize {
|
|
15
14
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
16
15
|
return srcStart;
|
|
17
16
|
}
|
|
18
17
|
|
|
19
|
-
|
|
20
|
-
@inline export function ensureArrayField<T extends Array<any>>(
|
|
21
|
-
fieldPtr: usize,
|
|
22
|
-
): T {
|
|
18
|
+
export function ensureArrayField<T extends Array<any>>(fieldPtr: usize): T {
|
|
23
19
|
let out = load<T>(fieldPtr);
|
|
24
20
|
if (!changetype<usize>(out)) {
|
|
25
21
|
out = changetype<T>(instantiate<T>());
|
|
@@ -28,8 +24,7 @@ import { isSpace } from "../../../util";
|
|
|
28
24
|
return out;
|
|
29
25
|
}
|
|
30
26
|
|
|
31
|
-
|
|
32
|
-
@inline export function ensureArrayFieldAt<T extends Array<any>>(
|
|
27
|
+
export function ensureArrayFieldAt<T extends Array<any>>(
|
|
33
28
|
dstObj: usize,
|
|
34
29
|
dstOffset: usize,
|
|
35
30
|
): T {
|
|
@@ -41,8 +36,7 @@ import { isSpace } from "../../../util";
|
|
|
41
36
|
return out;
|
|
42
37
|
}
|
|
43
38
|
|
|
44
|
-
|
|
45
|
-
@inline function backslashOrQuoteMask(block: u64): u64 {
|
|
39
|
+
function backslashOrQuoteMask(block: u64): u64 {
|
|
46
40
|
const b = block ^ 0x005c_005c_005c_005c;
|
|
47
41
|
const q = block ^ 0x0022_0022_0022_0022;
|
|
48
42
|
return (
|
|
@@ -51,8 +45,7 @@ import { isSpace } from "../../../util";
|
|
|
51
45
|
);
|
|
52
46
|
}
|
|
53
47
|
|
|
54
|
-
|
|
55
|
-
@inline export function ensureArrayElementSlot<T extends Array<any>>(
|
|
48
|
+
export function ensureArrayElementSlot<T extends Array<any>>(
|
|
56
49
|
out: T,
|
|
57
50
|
index: i32,
|
|
58
51
|
): usize {
|
|
@@ -60,7 +53,7 @@ import { isSpace } from "../../../util";
|
|
|
60
53
|
if (out.length < nextLength) {
|
|
61
54
|
// Grow via `push`, not `out.length = nextLength`. AS's `length=`
|
|
62
55
|
// setter calls `ensureCapacity(canGrow=false)` which reallocates to
|
|
63
|
-
// *exactly* the requested size
|
|
56
|
+
// *exactly* the requested size - fine for a one-shot resize, but
|
|
64
57
|
// catastrophic in the per-element loop (every push triggers a full
|
|
65
58
|
// copy of the array, giving O(N²) growth cost). `push` goes through
|
|
66
59
|
// `canGrow=true`, doubling capacity geometrically as needed.
|
|
@@ -79,7 +72,7 @@ import { isSpace } from "../../../util";
|
|
|
79
72
|
|
|
80
73
|
export function scanQuotedValueEnd_SWAR(srcStart: usize, srcEnd: usize): usize {
|
|
81
74
|
srcStart += 2;
|
|
82
|
-
const srcEnd8 = srcEnd
|
|
75
|
+
const srcEnd8 = srcEnd - 8;
|
|
83
76
|
|
|
84
77
|
while (srcStart <= srcEnd8) {
|
|
85
78
|
let mask = backslashOrQuoteMask(load<u64>(srcStart));
|
|
@@ -7,12 +7,9 @@ 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
|
-
@inline function skipStringArrayWhitespace(
|
|
13
|
-
srcStart: usize,
|
|
14
|
-
srcEnd: usize,
|
|
15
|
-
): usize {
|
|
12
|
+
function skipStringArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
|
|
16
13
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
17
14
|
return srcStart;
|
|
18
15
|
}
|
|
@@ -23,6 +20,15 @@ function deserializeStringArrayBody<T extends string[]>(
|
|
|
23
20
|
out: T,
|
|
24
21
|
): usize {
|
|
25
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>>();
|
|
26
32
|
|
|
27
33
|
do {
|
|
28
34
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
|
|
@@ -35,13 +41,16 @@ function deserializeStringArrayBody<T extends string[]>(
|
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
while (srcStart < srcEnd) {
|
|
38
|
-
const slot =
|
|
44
|
+
const slot =
|
|
45
|
+
index < reusableLength
|
|
46
|
+
? reusableDataStart + <usize>index * elementSize
|
|
47
|
+
: ensureArrayElementSlot<T>(out, index);
|
|
39
48
|
// Null fast path for `(string | null)[]`. "null" is 4 UTF-16 chars
|
|
40
49
|
// = 8 bytes, exactly one u64 compare. Store 0 (the AS null reference)
|
|
41
50
|
// and skip 8 bytes.
|
|
42
51
|
//
|
|
43
52
|
// We accept null tokens unconditionally rather than gating on
|
|
44
|
-
// `isNullable<valueof<T>>()`
|
|
53
|
+
// `isNullable<valueof<T>>()` - AS's `valueof` of a nullable-array
|
|
45
54
|
// element type doesn't always preserve the nullable marker through
|
|
46
55
|
// the dispatcher's `<T>` cast, so the gate would mis-fire for the
|
|
47
56
|
// very case it's meant to handle. The runtime cost on plain
|
|
@@ -70,18 +79,18 @@ function deserializeStringArrayBody<T extends string[]>(
|
|
|
70
79
|
}
|
|
71
80
|
if (code == BRACKET_RIGHT) {
|
|
72
81
|
const nextLen = index + 1;
|
|
73
|
-
if (
|
|
82
|
+
if (reusableLength != nextLen) out.length = nextLen;
|
|
74
83
|
return srcStart + 2;
|
|
75
84
|
}
|
|
76
85
|
break;
|
|
77
86
|
}
|
|
78
87
|
} while (false);
|
|
79
88
|
|
|
80
|
-
|
|
89
|
+
markProductionParseError();
|
|
90
|
+
return 0;
|
|
81
91
|
}
|
|
82
92
|
|
|
83
|
-
|
|
84
|
-
@inline export function deserializeStringArrayField<T extends string[]>(
|
|
93
|
+
export function deserializeStringArrayField<T extends string[]>(
|
|
85
94
|
srcStart: usize,
|
|
86
95
|
srcEnd: usize,
|
|
87
96
|
fieldPtr: usize,
|
|
@@ -111,6 +120,11 @@ export function deserializeStringArray_SWAR<T extends string[]>(
|
|
|
111
120
|
dst || changetype<usize>(instantiate<T>()),
|
|
112
121
|
);
|
|
113
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
|
+
|
|
114
128
|
// Worst-case sizing: shortest possible element is `""` followed by `,` =
|
|
115
129
|
// 6 UTF-16 bytes, so `(srcLen + 5) / 6` upper-bounds the count. Clamp to
|
|
116
130
|
// AS's BLOCK_MAXSIZE / sizeof<string ref> = (1<<28) - 4 elements; payloads
|
|
@@ -131,11 +145,16 @@ export function deserializeStringArray_SWAR<T extends string[]>(
|
|
|
131
145
|
// Caller guarantees srcStart is at the opening `[`.
|
|
132
146
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) {
|
|
133
147
|
out.length = 0;
|
|
134
|
-
|
|
148
|
+
markProductionParseError();
|
|
149
|
+
return changetype<T>(0);
|
|
135
150
|
}
|
|
136
151
|
srcStart += 2;
|
|
137
152
|
srcStart = skipStringArrayWhitespace(srcStart, srcEnd);
|
|
138
153
|
if (srcStart < srcEnd && load<u16>(srcStart) == BRACKET_RIGHT) {
|
|
154
|
+
if (srcStart + 2 != srcEnd) {
|
|
155
|
+
markProductionParseError();
|
|
156
|
+
return changetype<T>(0);
|
|
157
|
+
}
|
|
139
158
|
out.length = 0;
|
|
140
159
|
return out;
|
|
141
160
|
}
|
|
@@ -164,6 +183,10 @@ export function deserializeStringArray_SWAR<T extends string[]>(
|
|
|
164
183
|
continue;
|
|
165
184
|
}
|
|
166
185
|
if (code == BRACKET_RIGHT) {
|
|
186
|
+
if (srcStart + 2 != srcEnd) {
|
|
187
|
+
markProductionParseError();
|
|
188
|
+
return changetype<T>(0);
|
|
189
|
+
}
|
|
167
190
|
const finalLen = i32(<usize>(writePtr - dataStart) / elementSize);
|
|
168
191
|
if (out.length != finalLen) out.length = finalLen;
|
|
169
192
|
return out;
|
|
@@ -171,5 +194,6 @@ export function deserializeStringArray_SWAR<T extends string[]>(
|
|
|
171
194
|
break;
|
|
172
195
|
}
|
|
173
196
|
|
|
174
|
-
|
|
197
|
+
markProductionParseError();
|
|
198
|
+
return changetype<T>(0);
|
|
175
199
|
}
|
|
@@ -1,12 +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
|
|
|
5
15
|
|
|
6
|
-
@inline
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
)
|
|
16
|
+
@inline
|
|
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
|
+
}
|
|
10
31
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
11
32
|
return srcStart;
|
|
12
33
|
}
|
|
@@ -32,11 +53,22 @@ function deserializeStructArrayBody<T extends unknown[]>(
|
|
|
32
53
|
out: T,
|
|
33
54
|
): usize {
|
|
34
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;
|
|
35
63
|
|
|
36
64
|
do {
|
|
37
65
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
|
|
38
66
|
srcStart += 2;
|
|
39
|
-
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
|
+
}
|
|
40
72
|
if (srcStart >= srcEnd) break;
|
|
41
73
|
if (load<u16>(srcStart) == BRACKET_RIGHT) {
|
|
42
74
|
out.length = 0;
|
|
@@ -44,7 +76,10 @@ function deserializeStructArrayBody<T extends unknown[]>(
|
|
|
44
76
|
}
|
|
45
77
|
|
|
46
78
|
while (srcStart < srcEnd) {
|
|
47
|
-
const slot =
|
|
79
|
+
const slot =
|
|
80
|
+
index < reusableLength
|
|
81
|
+
? reusableDataStart + <usize>index * elementSize
|
|
82
|
+
: ensureArrayElementSlot<T>(out, index);
|
|
48
83
|
let value = load<valueof<T>>(slot);
|
|
49
84
|
if (changetype<usize>(value) == 0) {
|
|
50
85
|
value = changetype<valueof<T>>(
|
|
@@ -58,31 +93,70 @@ function deserializeStructArrayBody<T extends unknown[]>(
|
|
|
58
93
|
store<valueof<T>>(slot, value);
|
|
59
94
|
}
|
|
60
95
|
|
|
96
|
+
const valueStart = srcStart;
|
|
61
97
|
let next: usize = 0;
|
|
62
98
|
if (
|
|
63
99
|
// @ts-ignore: supplied by transform
|
|
64
100
|
isDefined(changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST)
|
|
65
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`.
|
|
66
106
|
// @ts-ignore: supplied by transform
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
+
}
|
|
70
125
|
}
|
|
71
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;
|
|
72
133
|
// @ts-ignore: supplied by transform
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
134
|
+
if (isDefined(changetype<nonnull<valueof<T>>>(value).__INITIALIZE)) {
|
|
135
|
+
// @ts-ignore: supplied by transform
|
|
136
|
+
changetype<nonnull<valueof<T>>>(value).__INITIALIZE();
|
|
137
|
+
}
|
|
138
|
+
// @ts-ignore: supplied by transform
|
|
139
|
+
changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_SLOW(
|
|
140
|
+
valueStart,
|
|
141
|
+
valueEnd,
|
|
142
|
+
value,
|
|
143
|
+
);
|
|
144
|
+
next = valueEnd;
|
|
76
145
|
}
|
|
77
146
|
if (!next) break;
|
|
78
147
|
srcStart = next;
|
|
79
|
-
srcStart = skipStructArrayWhitespace(srcStart, srcEnd);
|
|
80
148
|
if (srcStart >= srcEnd) break;
|
|
81
149
|
|
|
82
|
-
|
|
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
|
+
}
|
|
83
156
|
if (code == COMMA) {
|
|
84
157
|
srcStart += 2;
|
|
85
|
-
srcStart
|
|
158
|
+
if (srcStart < srcEnd && load<u16>(srcStart) != BRACE_LEFT)
|
|
159
|
+
srcStart = skipStructArrayWhitespace(srcStart, srcEnd);
|
|
86
160
|
index++;
|
|
87
161
|
continue;
|
|
88
162
|
}
|
|
@@ -91,18 +165,32 @@ function deserializeStructArrayBody<T extends unknown[]>(
|
|
|
91
165
|
// right length (e.g. canada-style geometry rings whose count
|
|
92
166
|
// matches a previous parse).
|
|
93
167
|
const nextLen = index + 1;
|
|
94
|
-
if (
|
|
168
|
+
if (reusableLength != nextLen) out.length = nextLen;
|
|
95
169
|
return srcStart + 2;
|
|
96
170
|
}
|
|
97
171
|
break;
|
|
98
172
|
}
|
|
99
173
|
} while (false);
|
|
100
174
|
|
|
101
|
-
|
|
175
|
+
markProductionParseError();
|
|
176
|
+
return 0;
|
|
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);
|
|
102
191
|
}
|
|
103
192
|
|
|
104
|
-
|
|
105
|
-
@inline export function deserializeStructArrayField<T extends unknown[]>(
|
|
193
|
+
export function deserializeStructArrayField<T extends unknown[]>(
|
|
106
194
|
srcStart: usize,
|
|
107
195
|
srcEnd: usize,
|
|
108
196
|
fieldPtr: usize,
|
|
@@ -13,9 +13,7 @@ import { deserializeStringArrayField } from "./array/string";
|
|
|
13
13
|
import { deserializeStructArrayField } from "./array/struct";
|
|
14
14
|
|
|
15
15
|
export { deserializeArrayField as deserializeArrayField_SWAR };
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
@inline export function deserializeArrayField<T extends unknown[]>(
|
|
16
|
+
export function deserializeArrayField<T extends unknown[]>(
|
|
19
17
|
srcStart: usize,
|
|
20
18
|
srcEnd: usize,
|
|
21
19
|
dstObj: usize,
|