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
|
@@ -5,6 +5,9 @@ import { QUOTE } from "../../custom/chars";
|
|
|
5
5
|
import { BACK_SLASH } from "../../custom/chars";
|
|
6
6
|
import { DESERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
7
7
|
import { hex4_to_u16_swar } from "../../util/swar";
|
|
8
|
+
import { markProductionParseError } from "../error";
|
|
9
|
+
import { isValidStringEscape } from "../string-validation";
|
|
10
|
+
import { probeStringFieldTrace, recordStringFieldTrace } from "../parseMode";
|
|
8
11
|
|
|
9
12
|
// @ts-expect-error: @lazy is a valid decorator
|
|
10
13
|
@lazy const SPLAT_5C = i16x8.splat(0x5c); // \
|
|
@@ -63,13 +66,8 @@ import { hex4_to_u16_swar } from "../../util/swar";
|
|
|
63
66
|
* @param dst buffer to write to
|
|
64
67
|
* @returns number of bytes written
|
|
65
68
|
*/
|
|
66
|
-
|
|
67
|
-
@inline function copyStringFromSource_SIMD(
|
|
68
|
-
srcStart: usize,
|
|
69
|
-
byteLength: usize,
|
|
70
|
-
): string {
|
|
69
|
+
function copyStringFromSource_SIMD(srcStart: usize, byteLength: usize): string {
|
|
71
70
|
if (byteLength == 0) return changetype<string>("");
|
|
72
|
-
// @ts-expect-error: __new is a runtime builtin
|
|
73
71
|
const out = __new(byteLength, idof<string>());
|
|
74
72
|
memory.copy(out, srcStart, byteLength);
|
|
75
73
|
return changetype<string>(out);
|
|
@@ -106,11 +104,7 @@ function writeStringToField_SIMD(
|
|
|
106
104
|
// handling (no closing-quote search). Same HYBRID strategy as the field path
|
|
107
105
|
// (see deserializeEscapedStringField_SIMD): escape blocks use a free
|
|
108
106
|
// whole-block v128 store for the plain prefix; clean runs stream the first
|
|
109
|
-
|
|
110
|
-
//
|
|
111
|
-
// Deliberately NOT @inline: cold escape path. Inlining the nested-loop v128
|
|
112
|
-
// body at every call site explodes `--converge` compile time on large schemas
|
|
113
|
-
// for no runtime gain (one call per escaped string).
|
|
107
|
+
|
|
114
108
|
function deserializeEscapedString_SIMD(
|
|
115
109
|
payloadStart: usize,
|
|
116
110
|
escapeStart: usize,
|
|
@@ -125,7 +119,7 @@ function deserializeEscapedString_SIMD(
|
|
|
125
119
|
}
|
|
126
120
|
|
|
127
121
|
let srcStart = escapeStart;
|
|
128
|
-
const srcEnd16 = srcEnd
|
|
122
|
+
const srcEnd16 = srcEnd - 16;
|
|
129
123
|
|
|
130
124
|
while (srcStart <= srcEnd16) {
|
|
131
125
|
const block = load<v128>(srcStart);
|
|
@@ -159,6 +153,11 @@ function deserializeEscapedString_SIMD(
|
|
|
159
153
|
const laneIdx = usize(ctz(mask) << 1);
|
|
160
154
|
bs.offset += laneIdx;
|
|
161
155
|
const srcIdx = srcStart + laneIdx;
|
|
156
|
+
if (!isValidStringEscape(srcIdx, srcEnd)) {
|
|
157
|
+
bs.offset = bs.buffer + outStart;
|
|
158
|
+
markProductionParseError();
|
|
159
|
+
return changetype<string>(0);
|
|
160
|
+
}
|
|
162
161
|
const code = load<u16>(srcIdx, 2);
|
|
163
162
|
if (code !== 0x75) {
|
|
164
163
|
store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
|
|
@@ -180,6 +179,11 @@ function deserializeEscapedString_SIMD(
|
|
|
180
179
|
srcStart += 2;
|
|
181
180
|
continue;
|
|
182
181
|
}
|
|
182
|
+
if (!isValidStringEscape(srcStart, srcEnd)) {
|
|
183
|
+
bs.offset = bs.buffer + outStart;
|
|
184
|
+
markProductionParseError();
|
|
185
|
+
return changetype<string>(0);
|
|
186
|
+
}
|
|
183
187
|
const code = load<u16>(srcStart, 2);
|
|
184
188
|
if (code !== 0x75) {
|
|
185
189
|
store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
|
|
@@ -254,18 +258,14 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
|
|
|
254
258
|
// reused `bs` scratch buffer, then written once via writeStringToField_SIMD.
|
|
255
259
|
//
|
|
256
260
|
// Strategy (validated against run-copy and pure-stream variants across escape
|
|
257
|
-
// densities
|
|
261
|
+
// densities - see __benches__/custom/simd-string-deser-variants-h2h):
|
|
258
262
|
// * Escape-bearing block: a single whole-block v128 store copies the plain
|
|
259
263
|
// prefix for free; then the escape is decoded scalar.
|
|
260
264
|
// * Clean block: stream the first one cheaply, but if the clean run keeps
|
|
261
|
-
// going, switch to one bulk memory.copy for the remainder
|
|
265
|
+
// going, switch to one bulk memory.copy for the remainder - bandwidth-
|
|
262
266
|
// optimal on long sparse runs, avoiding a per-block-store cliff on large
|
|
263
267
|
// inputs. This dominates both alternatives: stream-cheap on dense escapes,
|
|
264
|
-
|
|
265
|
-
//
|
|
266
|
-
// Deliberately NOT @inline: cold escape path. As an @inline it was inlined into
|
|
267
|
-
// deserializeStringField_SIMD at every struct string-field site, exploding
|
|
268
|
-
// `--converge` compile time ~24x on large schemas (4s → 99s) for no runtime
|
|
268
|
+
|
|
269
269
|
// gain. The hot no-escape scan + writeStringToField stay inline in the caller.
|
|
270
270
|
function deserializeEscapedStringField_SIMD(
|
|
271
271
|
payloadStart: usize,
|
|
@@ -275,16 +275,23 @@ function deserializeEscapedStringField_SIMD(
|
|
|
275
275
|
): usize {
|
|
276
276
|
const prefixLen = <u32>(escapeStart - payloadStart);
|
|
277
277
|
bs.offset = bs.buffer;
|
|
278
|
-
|
|
278
|
+
// The containing parser may pass document-end bounds. Reserve according to
|
|
279
|
+
// decoded output rather than retaining the entire remaining document.
|
|
280
|
+
bs.ensureSize(prefixLen + 1024);
|
|
279
281
|
if (prefixLen != 0) {
|
|
280
282
|
memory.copy(bs.buffer, payloadStart, prefixLen);
|
|
281
283
|
bs.offset += prefixLen;
|
|
282
284
|
}
|
|
283
285
|
|
|
284
286
|
let srcStart = escapeStart;
|
|
285
|
-
const srcEnd16 = srcEnd
|
|
287
|
+
const srcEnd16 = srcEnd - 16;
|
|
288
|
+
let scratchEnd = bs.buffer + bs.bufferSize - 16;
|
|
286
289
|
|
|
287
290
|
while (srcStart <= srcEnd16) {
|
|
291
|
+
if (bs.offset > scratchEnd) {
|
|
292
|
+
bs.ensureSize(1024);
|
|
293
|
+
scratchEnd = bs.buffer + bs.bufferSize - 16;
|
|
294
|
+
}
|
|
288
295
|
const block = load<v128>(srcStart);
|
|
289
296
|
const mask = i16x8.bitmask(
|
|
290
297
|
v128.or(i16x8.eq(block, SPLAT_5C), i16x8.eq(block, SPLAT_22)),
|
|
@@ -315,6 +322,10 @@ function deserializeEscapedStringField_SIMD(
|
|
|
315
322
|
srcStart += 16;
|
|
316
323
|
}
|
|
317
324
|
const runLen = <u32>(srcStart - runStart);
|
|
325
|
+
if (bs.offset + runLen > scratchEnd) {
|
|
326
|
+
bs.ensureSize(runLen + 1024);
|
|
327
|
+
scratchEnd = bs.buffer + bs.bufferSize - 16;
|
|
328
|
+
}
|
|
318
329
|
memory.copy(bs.offset, runStart, runLen);
|
|
319
330
|
bs.offset += runLen;
|
|
320
331
|
}
|
|
@@ -338,6 +349,11 @@ function deserializeEscapedStringField_SIMD(
|
|
|
338
349
|
return srcIdx + 2;
|
|
339
350
|
}
|
|
340
351
|
|
|
352
|
+
if (!isValidStringEscape(srcIdx, srcEnd)) {
|
|
353
|
+
bs.offset = bs.buffer;
|
|
354
|
+
markProductionParseError();
|
|
355
|
+
return 0;
|
|
356
|
+
}
|
|
341
357
|
const code = load<u16>(srcIdx, 2);
|
|
342
358
|
if (code !== 0x75) {
|
|
343
359
|
store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
|
|
@@ -351,6 +367,7 @@ function deserializeEscapedStringField_SIMD(
|
|
|
351
367
|
}
|
|
352
368
|
|
|
353
369
|
// scalar tail (< 16 bytes remaining): emit chars directly.
|
|
370
|
+
bs.ensureSize(<u32>(srcEnd - srcStart) + 2);
|
|
354
371
|
while (srcStart < srcEnd) {
|
|
355
372
|
const char = load<u16>(srcStart);
|
|
356
373
|
if (char == QUOTE) {
|
|
@@ -369,6 +386,11 @@ function deserializeEscapedStringField_SIMD(
|
|
|
369
386
|
continue;
|
|
370
387
|
}
|
|
371
388
|
|
|
389
|
+
if (!isValidStringEscape(srcStart, srcEnd)) {
|
|
390
|
+
bs.offset = bs.buffer;
|
|
391
|
+
markProductionParseError();
|
|
392
|
+
return 0;
|
|
393
|
+
}
|
|
372
394
|
const code = load<u16>(srcStart, 2);
|
|
373
395
|
if (code !== 0x75) {
|
|
374
396
|
store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
|
|
@@ -382,27 +404,42 @@ function deserializeEscapedStringField_SIMD(
|
|
|
382
404
|
}
|
|
383
405
|
|
|
384
406
|
bs.offset = bs.buffer;
|
|
385
|
-
|
|
386
|
-
return
|
|
407
|
+
markProductionParseError();
|
|
408
|
+
return 0;
|
|
387
409
|
}
|
|
388
410
|
|
|
389
|
-
// NOT @inline: as an @inline entry, binaryen inlined the (loop-bearing) escaped
|
|
390
|
-
// scanner into every per-field copy, exploding `large` SIMD compile ~24x
|
|
391
|
-
// (4s→99s, 221KB→555KB wasm). Kept as a single shared function — matches
|
|
392
|
-
// deserializeStringField_SWAR (also non-inline) and costs only one call/field.
|
|
393
411
|
export function deserializeStringField_SIMD<T extends string | null>(
|
|
394
412
|
srcStart: usize,
|
|
395
413
|
srcEnd: usize,
|
|
396
414
|
dstObj: usize,
|
|
397
415
|
dstOffset: usize = 0,
|
|
398
416
|
): usize {
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
417
|
+
if (srcStart + 2 > srcEnd || load<u16>(srcStart) != QUOTE) {
|
|
418
|
+
markProductionParseError();
|
|
419
|
+
return 0;
|
|
420
|
+
}
|
|
421
|
+
return deserializeStringFieldTrusted_SIMD(
|
|
422
|
+
srcStart + 2,
|
|
423
|
+
srcEnd,
|
|
424
|
+
dstObj,
|
|
425
|
+
dstOffset,
|
|
426
|
+
);
|
|
427
|
+
}
|
|
402
428
|
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
429
|
+
// Generated compact-object paths have already matched the key and validated
|
|
430
|
+
// the opening quote. Enter at the payload so that proof replaces (rather than
|
|
431
|
+
// duplicates) the public field decoder's framing load.
|
|
432
|
+
export function deserializeStringFieldTrusted_SIMD(
|
|
433
|
+
payloadStart: usize,
|
|
434
|
+
srcEnd: usize,
|
|
435
|
+
dstObj: usize,
|
|
436
|
+
dstOffset: usize = 0,
|
|
437
|
+
): usize {
|
|
438
|
+
const dstFieldPtr = dstObj + dstOffset;
|
|
439
|
+
const cachedEnd = probeStringFieldTrace(dstFieldPtr, payloadStart);
|
|
440
|
+
if (cachedEnd != 0) return cachedEnd;
|
|
441
|
+
const srcEnd16 = srcEnd - 16;
|
|
442
|
+
let srcStart = payloadStart;
|
|
406
443
|
|
|
407
444
|
while (srcStart <= srcEnd16) {
|
|
408
445
|
const block = load<v128>(srcStart);
|
|
@@ -423,15 +460,19 @@ export function deserializeStringField_SIMD<T extends string | null>(
|
|
|
423
460
|
payloadStart,
|
|
424
461
|
<u32>(srcIdx - payloadStart),
|
|
425
462
|
);
|
|
426
|
-
|
|
463
|
+
const next = srcIdx + 2;
|
|
464
|
+
recordStringFieldTrace(dstFieldPtr, payloadStart, next);
|
|
465
|
+
return next;
|
|
427
466
|
}
|
|
428
467
|
// backslash → vectorized escaped scan (no more SWAR fallback)
|
|
429
|
-
|
|
468
|
+
const next = deserializeEscapedStringField_SIMD(
|
|
430
469
|
payloadStart,
|
|
431
470
|
srcIdx,
|
|
432
471
|
srcEnd,
|
|
433
472
|
dstFieldPtr,
|
|
434
473
|
);
|
|
474
|
+
if (next != 0) recordStringFieldTrace(dstFieldPtr, payloadStart, next);
|
|
475
|
+
return next;
|
|
435
476
|
}
|
|
436
477
|
|
|
437
478
|
while (srcStart < srcEnd) {
|
|
@@ -442,19 +483,23 @@ export function deserializeStringField_SIMD<T extends string | null>(
|
|
|
442
483
|
payloadStart,
|
|
443
484
|
<u32>(srcStart - payloadStart),
|
|
444
485
|
);
|
|
445
|
-
|
|
486
|
+
const next = srcStart + 2;
|
|
487
|
+
recordStringFieldTrace(dstFieldPtr, payloadStart, next);
|
|
488
|
+
return next;
|
|
446
489
|
}
|
|
447
490
|
if (char == BACK_SLASH) {
|
|
448
|
-
|
|
491
|
+
const next = deserializeEscapedStringField_SIMD(
|
|
449
492
|
payloadStart,
|
|
450
493
|
srcStart,
|
|
451
494
|
srcEnd,
|
|
452
495
|
dstFieldPtr,
|
|
453
496
|
);
|
|
497
|
+
if (next != 0) recordStringFieldTrace(dstFieldPtr, payloadStart, next);
|
|
498
|
+
return next;
|
|
454
499
|
}
|
|
455
500
|
srcStart += 2;
|
|
456
501
|
}
|
|
457
502
|
|
|
458
|
-
|
|
459
|
-
return
|
|
503
|
+
markProductionParseError();
|
|
504
|
+
return 0;
|
|
460
505
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
|
|
2
|
+
@inline
|
|
3
|
+
function isHexDigit(c: u16): bool {
|
|
4
|
+
return (
|
|
5
|
+
(c >= 0x30 && c <= 0x39) ||
|
|
6
|
+
(c >= 0x41 && c <= 0x46) ||
|
|
7
|
+
(c >= 0x61 && c <= 0x66)
|
|
8
|
+
);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Validate one escape beginning at its backslash without scanning plain text. */
|
|
12
|
+
export function isValidStringEscape(srcStart: usize, srcEnd: usize): bool {
|
|
13
|
+
if (srcStart + 4 > srcEnd) return false;
|
|
14
|
+
const code = load<u16>(srcStart, 2);
|
|
15
|
+
if (code == 0x75) {
|
|
16
|
+
return (
|
|
17
|
+
srcStart + 12 <= srcEnd &&
|
|
18
|
+
isHexDigit(load<u16>(srcStart, 4)) &&
|
|
19
|
+
isHexDigit(load<u16>(srcStart, 6)) &&
|
|
20
|
+
isHexDigit(load<u16>(srcStart, 8)) &&
|
|
21
|
+
isHexDigit(load<u16>(srcStart, 10))
|
|
22
|
+
);
|
|
23
|
+
}
|
|
24
|
+
return (
|
|
25
|
+
code == 0x22 ||
|
|
26
|
+
code == 0x5c ||
|
|
27
|
+
code == 0x2f ||
|
|
28
|
+
code == 0x62 ||
|
|
29
|
+
code == 0x66 ||
|
|
30
|
+
code == 0x6e ||
|
|
31
|
+
code == 0x72 ||
|
|
32
|
+
code == 0x74
|
|
33
|
+
);
|
|
34
|
+
}
|
|
@@ -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,
|
|
@@ -2,6 +2,27 @@ import { JSON } from "../../..";
|
|
|
2
2
|
import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
|
|
3
3
|
import { deserializeFloatArrayBody } from "./float";
|
|
4
4
|
import { ensureArrayField, scanValueEnd, skipWhitespace } from "./shared";
|
|
5
|
+
import { skipPrettyWhitespace_SIMD } from "../../../util/prettyWhitespaceSimd";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@inline
|
|
9
|
+
function skipNestedArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
|
|
10
|
+
const code = load<u16>(srcStart);
|
|
11
|
+
if (ASC_FEATURE_SIMD && code == 10) {
|
|
12
|
+
// Common JSON.stringify(..., null, 2) shape at Canada's coordinate depth:
|
|
13
|
+
// LF + twelve spaces + the next nested array. Two fixed compares avoid a
|
|
14
|
+
// general whitespace mask, bitmask, and ctz on every coordinate pair.
|
|
15
|
+
if (
|
|
16
|
+
srcStart + 28 <= srcEnd &&
|
|
17
|
+
!v128.any_true(v128.xor(load<v128>(srcStart, 2), i16x8.splat(0x20))) &&
|
|
18
|
+
load<u64>(srcStart, 18) == 0x0020_0020_0020_0020 &&
|
|
19
|
+
load<u16>(srcStart, 26) == BRACKET_LEFT
|
|
20
|
+
)
|
|
21
|
+
return srcStart + 26;
|
|
22
|
+
return skipPrettyWhitespace_SIMD(srcStart, srcEnd);
|
|
23
|
+
}
|
|
24
|
+
return skipWhitespace(srcStart, srcEnd);
|
|
25
|
+
}
|
|
5
26
|
|
|
6
27
|
export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
7
28
|
srcStart: usize,
|
|
@@ -9,11 +30,21 @@ export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
|
9
30
|
out: T,
|
|
10
31
|
): usize {
|
|
11
32
|
let index = 0;
|
|
33
|
+
const reusableLength = load<i32>(
|
|
34
|
+
changetype<usize>(out),
|
|
35
|
+
offsetof<T>("length_"),
|
|
36
|
+
);
|
|
37
|
+
const reusableDataStart = out.dataStart;
|
|
38
|
+
const elementSize = sizeof<valueof<T>>();
|
|
12
39
|
|
|
13
40
|
do {
|
|
14
41
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
|
|
15
42
|
srcStart += 2;
|
|
16
|
-
|
|
43
|
+
if (srcStart < srcEnd) {
|
|
44
|
+
const code = load<u16>(srcStart);
|
|
45
|
+
if (code != BRACKET_LEFT)
|
|
46
|
+
srcStart = skipNestedArrayWhitespace(srcStart, srcEnd);
|
|
47
|
+
}
|
|
17
48
|
if (srcStart >= srcEnd) break;
|
|
18
49
|
if (load<u16>(srcStart) == BRACKET_RIGHT) {
|
|
19
50
|
out.length = 0;
|
|
@@ -23,8 +54,10 @@ export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
|
23
54
|
while (srcStart < srcEnd) {
|
|
24
55
|
if (isFloat<valueof<valueof<T>>>()) {
|
|
25
56
|
let value: valueof<T>;
|
|
26
|
-
if (index <
|
|
27
|
-
value =
|
|
57
|
+
if (index < reusableLength) {
|
|
58
|
+
value = load<valueof<T>>(
|
|
59
|
+
reusableDataStart + <usize>index * elementSize,
|
|
60
|
+
);
|
|
28
61
|
} else {
|
|
29
62
|
value = changetype<valueof<T>>(instantiate<valueof<T>>());
|
|
30
63
|
out.push(value);
|
|
@@ -37,8 +70,10 @@ export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
|
37
70
|
if (!srcStart || srcStart >= srcEnd) break;
|
|
38
71
|
} else if (isArray<valueof<valueof<T>>>()) {
|
|
39
72
|
let value: valueof<T>;
|
|
40
|
-
if (index <
|
|
41
|
-
value =
|
|
73
|
+
if (index < reusableLength) {
|
|
74
|
+
value = load<valueof<T>>(
|
|
75
|
+
reusableDataStart + <usize>index * elementSize,
|
|
76
|
+
);
|
|
42
77
|
} else {
|
|
43
78
|
value = changetype<valueof<T>>(instantiate<valueof<T>>());
|
|
44
79
|
out.push(value);
|
|
@@ -54,24 +89,39 @@ export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
|
54
89
|
if (!valueEnd || valueEnd <= srcStart) break;
|
|
55
90
|
|
|
56
91
|
let valuePtr: usize = 0;
|
|
57
|
-
if (index <
|
|
58
|
-
valuePtr = changetype<usize>(
|
|
92
|
+
if (index < reusableLength) {
|
|
93
|
+
valuePtr = changetype<usize>(
|
|
94
|
+
load<valueof<T>>(reusableDataStart + <usize>index * elementSize),
|
|
95
|
+
);
|
|
59
96
|
}
|
|
60
97
|
const value = JSON.__deserialize<valueof<T>>(
|
|
61
98
|
srcStart,
|
|
62
99
|
valueEnd,
|
|
63
100
|
valuePtr,
|
|
64
101
|
);
|
|
65
|
-
if (index <
|
|
66
|
-
|
|
102
|
+
if (index < reusableLength) {
|
|
103
|
+
store<valueof<T>>(
|
|
104
|
+
reusableDataStart + <usize>index * elementSize,
|
|
105
|
+
value,
|
|
106
|
+
);
|
|
107
|
+
} else {
|
|
108
|
+
out.push(value);
|
|
109
|
+
}
|
|
67
110
|
srcStart = valueEnd;
|
|
68
111
|
}
|
|
69
112
|
|
|
70
|
-
|
|
71
|
-
|
|
113
|
+
let code = load<u16>(srcStart);
|
|
114
|
+
if (code != COMMA && code != BRACKET_RIGHT) {
|
|
115
|
+
srcStart = skipWhitespace(srcStart, srcEnd);
|
|
116
|
+
code = load<u16>(srcStart);
|
|
117
|
+
}
|
|
72
118
|
if (code == COMMA) {
|
|
73
119
|
srcStart += 2;
|
|
74
|
-
|
|
120
|
+
if (srcStart < srcEnd) {
|
|
121
|
+
code = load<u16>(srcStart);
|
|
122
|
+
if (code != BRACKET_LEFT)
|
|
123
|
+
srcStart = skipNestedArrayWhitespace(srcStart, srcEnd);
|
|
124
|
+
}
|
|
75
125
|
index++;
|
|
76
126
|
continue;
|
|
77
127
|
}
|
|
@@ -80,7 +130,7 @@ export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
|
80
130
|
// correct (the array is being reused with the same shape, e.g.
|
|
81
131
|
// canada's geometry rings across repeated parses).
|
|
82
132
|
const nextLen = index + 1;
|
|
83
|
-
if (
|
|
133
|
+
if (reusableLength != nextLen) out.length = nextLen;
|
|
84
134
|
return srcStart + 2;
|
|
85
135
|
}
|
|
86
136
|
break;
|
|
@@ -90,8 +140,7 @@ export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
|
90
140
|
throw new Error("Failed to parse JSON!");
|
|
91
141
|
}
|
|
92
142
|
|
|
93
|
-
|
|
94
|
-
@inline export function deserializeArrayArrayField<T extends unknown[][]>(
|
|
143
|
+
export function deserializeArrayArrayField<T extends unknown[][]>(
|
|
95
144
|
srcStart: usize,
|
|
96
145
|
srcEnd: usize,
|
|
97
146
|
fieldPtr: usize,
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "../../../custom/chars";
|
|
8
8
|
import { isSpace } from "../../../util";
|
|
9
9
|
import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
10
|
+
import { markProductionParseError } from "../../error";
|
|
10
11
|
|
|
11
12
|
function deserializeBooleanArrayBody<T extends boolean[]>(
|
|
12
13
|
srcStart: usize,
|
|
@@ -14,6 +15,12 @@ function deserializeBooleanArrayBody<T extends boolean[]>(
|
|
|
14
15
|
out: T,
|
|
15
16
|
): usize {
|
|
16
17
|
let index = 0;
|
|
18
|
+
const reusableLength = load<i32>(
|
|
19
|
+
changetype<usize>(out),
|
|
20
|
+
offsetof<T>("length_"),
|
|
21
|
+
);
|
|
22
|
+
const reusableDataStart = out.dataStart;
|
|
23
|
+
const elementSize = sizeof<valueof<T>>();
|
|
17
24
|
|
|
18
25
|
do {
|
|
19
26
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
|
|
@@ -26,7 +33,10 @@ function deserializeBooleanArrayBody<T extends boolean[]>(
|
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
while (srcStart < srcEnd) {
|
|
29
|
-
const slot =
|
|
36
|
+
const slot =
|
|
37
|
+
index < reusableLength
|
|
38
|
+
? reusableDataStart + <usize>index * elementSize
|
|
39
|
+
: ensureArrayElementSlot<T>(out, index);
|
|
30
40
|
const block = load<u64>(srcStart);
|
|
31
41
|
if (block == TRUE_WORD_U64) {
|
|
32
42
|
store<valueof<T>>(slot, true);
|
|
@@ -49,18 +59,18 @@ function deserializeBooleanArrayBody<T extends boolean[]>(
|
|
|
49
59
|
}
|
|
50
60
|
if (code == BRACKET_RIGHT) {
|
|
51
61
|
const nextLen = index + 1;
|
|
52
|
-
if (
|
|
62
|
+
if (reusableLength != nextLen) out.length = nextLen;
|
|
53
63
|
return srcStart + 2;
|
|
54
64
|
}
|
|
55
65
|
break;
|
|
56
66
|
}
|
|
57
67
|
} while (false);
|
|
58
68
|
|
|
59
|
-
|
|
69
|
+
markProductionParseError();
|
|
70
|
+
return 0;
|
|
60
71
|
}
|
|
61
72
|
|
|
62
|
-
|
|
63
|
-
@inline export function deserializeBooleanArrayField<T extends boolean[]>(
|
|
73
|
+
export function deserializeBooleanArrayField<T extends boolean[]>(
|
|
64
74
|
srcStart: usize,
|
|
65
75
|
srcEnd: usize,
|
|
66
76
|
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,
|