json-as 1.3.6 → 1.3.8
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 +45 -0
- package/README.md +1 -1
- package/assembly/deserialize/helpers/uint.ts +4 -1
- package/assembly/deserialize/index/arbitrary.ts +7 -3
- package/assembly/deserialize/index/array.ts +42 -17
- package/assembly/deserialize/index/bool.ts +1 -1
- package/assembly/deserialize/index/date.ts +1 -1
- package/assembly/deserialize/index/float.ts +40 -1
- package/assembly/deserialize/index/integer.ts +68 -1
- package/assembly/deserialize/index/map.ts +1 -1
- package/assembly/deserialize/index/object.ts +1 -1
- package/assembly/deserialize/index/raw.ts +1 -1
- package/assembly/deserialize/index/set.ts +1 -1
- package/assembly/deserialize/index/staticarray.ts +4 -1
- package/assembly/deserialize/index/string.ts +32 -4
- package/assembly/deserialize/index/struct.ts +1 -1
- package/assembly/deserialize/index/typedarray.ts +30 -10
- package/assembly/deserialize/index/unsigned.ts +78 -1
- package/assembly/deserialize/index.ts +1 -0
- package/assembly/deserialize/{simple → naive}/array/arbitrary.ts +24 -5
- package/assembly/deserialize/{simple → naive}/array/array.ts +8 -2
- package/assembly/deserialize/naive/array/bool.ts +68 -0
- package/assembly/deserialize/{simple → naive}/array/box.ts +8 -2
- package/assembly/deserialize/naive/array/float.ts +63 -0
- package/assembly/deserialize/{simple → naive}/array/generic.ts +14 -7
- package/assembly/deserialize/naive/array/integer.ts +86 -0
- package/assembly/deserialize/naive/array/map.ts +47 -0
- package/assembly/deserialize/naive/array/object.ts +47 -0
- package/assembly/deserialize/{simple → naive}/array/raw.ts +34 -7
- package/assembly/deserialize/naive/array/string.ts +69 -0
- package/assembly/deserialize/naive/array/struct.ts +47 -0
- package/assembly/deserialize/{simple → naive}/array.ts +15 -10
- package/assembly/deserialize/{simple → naive}/bool.ts +6 -2
- package/assembly/deserialize/naive/float.ts +135 -0
- package/assembly/deserialize/{simple → naive}/integer.ts +10 -2
- package/assembly/deserialize/{simple → naive}/map.ts +106 -27
- package/assembly/deserialize/{simple → naive}/object.ts +65 -19
- package/assembly/deserialize/{simple → naive}/raw.ts +4 -1
- package/assembly/deserialize/{simple → naive}/set.ts +49 -19
- package/assembly/deserialize/{simple → naive}/staticarray/array.ts +1 -1
- package/assembly/deserialize/{simple → naive}/staticarray/bool.ts +1 -1
- package/assembly/deserialize/{simple → naive}/staticarray/float.ts +1 -1
- package/assembly/deserialize/{simple → naive}/staticarray/integer.ts +1 -1
- package/assembly/deserialize/{simple → naive}/staticarray/string.ts +11 -3
- package/assembly/deserialize/{simple → naive}/staticarray/struct.ts +1 -2
- package/assembly/deserialize/{simple → naive}/staticarray.ts +68 -18
- package/assembly/deserialize/naive/string.ts +199 -0
- package/assembly/deserialize/{simple → naive}/struct.ts +5 -1
- package/assembly/deserialize/{simple → naive}/typedarray.ts +17 -4
- package/assembly/deserialize/{simple → naive}/unsigned.ts +10 -15
- package/assembly/deserialize/simd/array/integer.ts +339 -62
- package/assembly/deserialize/simd/float.ts +303 -0
- package/assembly/deserialize/simd/integer.ts +233 -0
- package/assembly/deserialize/simd/string.ts +266 -107
- package/assembly/deserialize/swar/array/arbitrary.ts +11 -3
- package/assembly/deserialize/swar/array/array.ts +40 -9
- package/assembly/deserialize/swar/array/bool.ts +28 -5
- package/assembly/deserialize/swar/array/box.ts +11 -3
- package/assembly/deserialize/swar/array/float.ts +295 -7
- package/assembly/deserialize/swar/array/generic.ts +28 -7
- package/assembly/deserialize/swar/array/integer.ts +363 -112
- package/assembly/deserialize/swar/array/map.ts +11 -3
- package/assembly/deserialize/swar/array/object.ts +37 -25
- package/assembly/deserialize/swar/array/raw.ts +11 -3
- package/assembly/deserialize/swar/array/shared.ts +63 -14
- package/assembly/deserialize/swar/array/string.ts +140 -7
- package/assembly/deserialize/swar/array/struct.ts +66 -12
- package/assembly/deserialize/swar/array.ts +12 -51
- package/assembly/deserialize/swar/float.ts +304 -0
- package/assembly/deserialize/swar/integer.ts +246 -0
- package/assembly/deserialize/swar/string.ts +213 -294
- package/assembly/deserialize/swar/typedarray.ts +224 -0
- package/assembly/index.d.ts +3 -1
- package/assembly/index.ts +402 -261
- package/assembly/serialize/index/array.ts +1 -1
- package/assembly/serialize/index/bool.ts +1 -1
- package/assembly/serialize/index/date.ts +1 -1
- package/assembly/serialize/index/float.ts +5 -1
- package/assembly/serialize/index/integer.ts +1 -1
- package/assembly/serialize/index/map.ts +1 -1
- package/assembly/serialize/index/raw.ts +1 -1
- package/assembly/serialize/index/set.ts +1 -1
- package/assembly/serialize/index/staticarray.ts +1 -1
- package/assembly/serialize/index/string.ts +1 -1
- package/assembly/serialize/index/struct.ts +1 -1
- package/assembly/serialize/index/typedarray.ts +21 -12
- package/assembly/serialize/index.ts +1 -0
- package/assembly/serialize/naive/array.ts +351 -0
- package/assembly/serialize/{simple → naive}/float.ts +4 -1
- package/assembly/serialize/naive/integer.ts +19 -0
- package/assembly/serialize/{simple → naive}/map.ts +6 -2
- package/assembly/serialize/{simple → naive}/raw.ts +5 -1
- package/assembly/serialize/{simple → naive}/set.ts +6 -1
- package/assembly/serialize/{simple → naive}/staticarray.ts +6 -1
- package/assembly/serialize/{simple → naive}/string.ts +1 -2
- package/assembly/serialize/{simple → naive}/typedarray.ts +10 -3
- package/assembly/serialize/simd/string.ts +6 -2
- package/assembly/serialize/swar/string.ts +15 -141
- package/assembly/util/atoi-fast.ts +81 -0
- package/assembly/util/concat.ts +5 -1
- package/assembly/util/dragonbox-cache.ts +443 -2
- package/assembly/util/dragonbox.ts +53 -17
- package/assembly/util/itoa-fast.ts +241 -0
- package/assembly/util/masks.ts +18 -1
- package/assembly/util/parsefloat-fast.ts +167 -0
- package/assembly/util/scanValueEnd.ts +78 -0
- package/assembly/util/scientific.ts +132 -0
- package/assembly/util/simd-int.ts +191 -0
- package/assembly/util/snp.ts +4 -1
- package/assembly/util/swar-int.ts +248 -0
- package/assembly/util/swar.ts +13 -3
- package/lib/as-bs.ts +27 -6
- package/package.json +15 -11
- package/transform/lib/builder.d.ts.map +1 -1
- package/transform/lib/builder.js +13 -5
- package/transform/lib/builder.js.map +1 -1
- package/transform/lib/index.d.ts +5 -0
- package/transform/lib/index.d.ts.map +1 -1
- package/transform/lib/index.js +1046 -340
- package/transform/lib/index.js.map +1 -1
- package/transform/lib/linkers/alias.d.ts.map +1 -1
- package/transform/lib/linkers/alias.js.map +1 -1
- package/transform/lib/linkers/custom.d.ts.map +1 -1
- package/transform/lib/linkers/custom.js +3 -2
- package/transform/lib/linkers/custom.js.map +1 -1
- package/transform/lib/linkers/imports.d.ts.map +1 -1
- package/transform/lib/linkers/imports.js.map +1 -1
- package/transform/lib/types.d.ts.map +1 -1
- package/transform/lib/types.js +54 -16
- package/transform/lib/types.js.map +1 -1
- package/transform/lib/util.d.ts.map +1 -1
- package/transform/lib/util.js +1 -1
- package/transform/lib/util.js.map +1 -1
- package/transform/lib/visitor.d.ts.map +1 -1
- package/transform/lib/visitor.js +2 -1
- package/transform/lib/visitor.js.map +1 -1
- package/assembly/custom/util.ts +0 -310
- package/assembly/deserialize/simple/arbitrary.ts +0 -23
- package/assembly/deserialize/simple/array/bool.ts +0 -17
- package/assembly/deserialize/simple/array/float.ts +0 -28
- package/assembly/deserialize/simple/array/integer.ts +0 -27
- package/assembly/deserialize/simple/array/map.ts +0 -28
- package/assembly/deserialize/simple/array/object.ts +0 -28
- package/assembly/deserialize/simple/array/string.ts +0 -23
- package/assembly/deserialize/simple/array/struct.ts +0 -28
- package/assembly/deserialize/simple/float.ts +0 -201
- package/assembly/deserialize/simple/string.ts +0 -132
- package/assembly/serialize/simple/arbitrary.ts +0 -79
- package/assembly/serialize/simple/array.ts +0 -86
- package/assembly/serialize/simple/integer.ts +0 -20
- package/assembly/serialize/simple/object.ts +0 -42
- /package/assembly/deserialize/{simple → naive}/date.ts +0 -0
- /package/assembly/serialize/{simple → naive}/bool.ts +0 -0
- /package/assembly/serialize/{simple → naive}/date.ts +0 -0
- /package/assembly/serialize/{simple → naive}/struct.ts +0 -0
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { DRAGONBOX_F32_CACHE, DRAGONBOX_F64_CACHE } from "./dragonbox-cache";
|
|
2
|
-
import { decimalCount32,
|
|
2
|
+
import { decimalCount32, utoa32_dec_core } from "util/number";
|
|
3
|
+
import { ensureItoaPairs, itoaU32, itoaU64 } from "./itoa-fast";
|
|
3
4
|
|
|
4
5
|
const CHAR_MINUS: u16 = 45;
|
|
5
6
|
const CHAR_DOT: u16 = 46;
|
|
@@ -188,7 +189,12 @@ function computeMul64(u: u64, cacheHigh: u64, cacheLow: u64): void {
|
|
|
188
189
|
|
|
189
190
|
|
|
190
191
|
@inline
|
|
191
|
-
function computeMulParity64(
|
|
192
|
+
function computeMulParity64(
|
|
193
|
+
twoF: u64,
|
|
194
|
+
cacheHigh: u64,
|
|
195
|
+
cacheLow: u64,
|
|
196
|
+
beta: i32,
|
|
197
|
+
): void {
|
|
192
198
|
const high = twoF * cacheHigh;
|
|
193
199
|
|
|
194
200
|
const a = <u32>(twoF >>> 32);
|
|
@@ -205,7 +211,8 @@ function computeMulParity64(twoF: u64, cacheHigh: u64, cacheLow: u64, beta: i32)
|
|
|
205
211
|
|
|
206
212
|
const rHigh = high + lowHigh;
|
|
207
213
|
_dbParity = ((rHigh >>> (64 - beta)) & 1) != 0;
|
|
208
|
-
_dbMulIsInteger =
|
|
214
|
+
_dbMulIsInteger =
|
|
215
|
+
(((rHigh << beta) & 0xffffffffffffffff) | (lowLow >>> (64 - beta))) == 0;
|
|
209
216
|
}
|
|
210
217
|
|
|
211
218
|
|
|
@@ -298,7 +305,8 @@ function prettify(buffer: usize, length: i32, k: i32): i32 {
|
|
|
298
305
|
|
|
299
306
|
const kk = length + k;
|
|
300
307
|
if (length <= kk && kk <= 21) {
|
|
301
|
-
for (let i = length; i < kk; ++i)
|
|
308
|
+
for (let i = length; i < kk; ++i)
|
|
309
|
+
store<u16>(buffer + ((<usize>i) << 1), CHAR_0);
|
|
302
310
|
const tail = buffer + ((<usize>kk) << 1);
|
|
303
311
|
store<u16>(tail, CHAR_DOT);
|
|
304
312
|
store<u16>(tail, CHAR_0, 2);
|
|
@@ -313,7 +321,8 @@ function prettify(buffer: usize, length: i32, k: i32): i32 {
|
|
|
313
321
|
memory.copy(buffer + ((<usize>offset) << 1), buffer, (<usize>length) << 1);
|
|
314
322
|
store<u16>(buffer, CHAR_0);
|
|
315
323
|
store<u16>(buffer, CHAR_DOT, 2);
|
|
316
|
-
for (let i = 2; i < offset; ++i)
|
|
324
|
+
for (let i = 2; i < offset; ++i)
|
|
325
|
+
store<u16>(buffer + ((<usize>i) << 1), CHAR_0);
|
|
317
326
|
return length + offset;
|
|
318
327
|
} else if (length == 1) {
|
|
319
328
|
store<u16>(buffer, CHAR_E, 2);
|
|
@@ -365,7 +374,10 @@ function prettifyFast(buffer: usize, length: i32, k: i32): i32 {
|
|
|
365
374
|
return -1;
|
|
366
375
|
}
|
|
367
376
|
|
|
368
|
-
function dragonboxToDecimalF32(
|
|
377
|
+
function dragonboxToDecimalF32(
|
|
378
|
+
binarySignificand: u32,
|
|
379
|
+
binaryExponent: i32,
|
|
380
|
+
): u32 {
|
|
369
381
|
const isEven = (binarySignificand & 1) == 0;
|
|
370
382
|
let twoFc = binarySignificand << 1;
|
|
371
383
|
|
|
@@ -374,7 +386,9 @@ function dragonboxToDecimalF32(binarySignificand: u32, binaryExponent: i32): u32
|
|
|
374
386
|
if (twoFc == 0) {
|
|
375
387
|
const minusK = floor_log10_pow2_minus_log10_4_over_3(binaryExponent);
|
|
376
388
|
const beta = binaryExponent + floor_log2_pow10(-minusK);
|
|
377
|
-
const cache = load<u64>(
|
|
389
|
+
const cache = load<u64>(
|
|
390
|
+
DRAGONBOX_F32_CACHE + ((<usize>(31 - minusK)) << 3),
|
|
391
|
+
);
|
|
378
392
|
let xi = computeLeftEndpointShorter32(cache, beta);
|
|
379
393
|
const zi = computeRightEndpointShorter32(cache, beta);
|
|
380
394
|
if (!(binaryExponent >= 2 && binaryExponent <= 3)) ++xi;
|
|
@@ -387,7 +401,8 @@ function dragonboxToDecimalF32(binarySignificand: u32, binaryExponent: i32): u32
|
|
|
387
401
|
return decimalSignificand;
|
|
388
402
|
}
|
|
389
403
|
decimalSignificand = computeRoundUpShorter32(cache, beta);
|
|
390
|
-
if ((decimalSignificand & 1) != 0 && binaryExponent == -35)
|
|
404
|
+
if ((decimalSignificand & 1) != 0 && binaryExponent == -35)
|
|
405
|
+
--decimalSignificand;
|
|
391
406
|
else if (decimalSignificand < xi) ++decimalSignificand;
|
|
392
407
|
_dbK = minusK;
|
|
393
408
|
return decimalSignificand;
|
|
@@ -444,14 +459,18 @@ function dragonboxToDecimalF32(binarySignificand: u32, binaryExponent: i32): u32
|
|
|
444
459
|
if ((packedDiv & 1) != 0) {
|
|
445
460
|
computeMulParity32(twoFc, cache, beta);
|
|
446
461
|
if (_dbParity != approxYParity) --decimalSignificand;
|
|
447
|
-
else if ((decimalSignificand & 1) != 0 && _dbMulIsInteger)
|
|
462
|
+
else if ((decimalSignificand & 1) != 0 && _dbMulIsInteger)
|
|
463
|
+
--decimalSignificand;
|
|
448
464
|
}
|
|
449
465
|
|
|
450
466
|
_dbK = minusK + 1;
|
|
451
467
|
return decimalSignificand;
|
|
452
468
|
}
|
|
453
469
|
|
|
454
|
-
function dragonboxToDecimalF64(
|
|
470
|
+
function dragonboxToDecimalF64(
|
|
471
|
+
binarySignificand: u64,
|
|
472
|
+
binaryExponent: i32,
|
|
473
|
+
): u64 {
|
|
455
474
|
const isEven = (binarySignificand & 1) == 0;
|
|
456
475
|
let twoFc = binarySignificand << 1;
|
|
457
476
|
|
|
@@ -475,7 +494,8 @@ function dragonboxToDecimalF64(binarySignificand: u64, binaryExponent: i32): u64
|
|
|
475
494
|
return decimalSignificand;
|
|
476
495
|
}
|
|
477
496
|
decimalSignificand = computeRoundUpShorter64(cacheHigh, beta);
|
|
478
|
-
if ((decimalSignificand & 1) != 0 && binaryExponent == -77)
|
|
497
|
+
if ((decimalSignificand & 1) != 0 && binaryExponent == -77)
|
|
498
|
+
--decimalSignificand;
|
|
479
499
|
else if (decimalSignificand < xi) ++decimalSignificand;
|
|
480
500
|
_dbK = minusK;
|
|
481
501
|
return decimalSignificand;
|
|
@@ -534,7 +554,8 @@ function dragonboxToDecimalF64(binarySignificand: u64, binaryExponent: i32): u64
|
|
|
534
554
|
if ((packedDiv & 1) != 0) {
|
|
535
555
|
computeMulParity64(twoFc, cacheHigh, cacheLow, beta);
|
|
536
556
|
if (_dbParity != approxYParity) --decimalSignificand;
|
|
537
|
-
else if ((decimalSignificand & 1) != 0 && _dbMulIsInteger)
|
|
557
|
+
else if ((decimalSignificand & 1) != 0 && _dbMulIsInteger)
|
|
558
|
+
--decimalSignificand;
|
|
538
559
|
}
|
|
539
560
|
|
|
540
561
|
_dbK = minusK + 2;
|
|
@@ -548,8 +569,15 @@ function dragonboxCoreF32(buffer: usize, value: f32): u32 {
|
|
|
548
569
|
value = -value;
|
|
549
570
|
store<u16>(buffer, CHAR_MINUS);
|
|
550
571
|
}
|
|
551
|
-
const digits = dragonboxToDecimalF32(
|
|
552
|
-
|
|
572
|
+
const digits = dragonboxToDecimalF32(
|
|
573
|
+
reinterpret<u32>(value) & 0x7fffff,
|
|
574
|
+
(reinterpret<u32>(value) >>> 23) & 0xff,
|
|
575
|
+
);
|
|
576
|
+
// Use the jeaiii-style itoa (forward write + 2-digit-pair LUT) instead
|
|
577
|
+
// of AS stdlib's `itoa_buffered`, which runs a separate width classifier
|
|
578
|
+
// + backward div-by-10000 loop. Saves a function call and ~half the
|
|
579
|
+
// per-digit work for the typical 7-17 digit dragonbox output.
|
|
580
|
+
let len = itoaU32(buffer + ((<usize>sign) << 1), digits);
|
|
553
581
|
return <u32>(prettify(buffer + ((<usize>sign) << 1), len, _dbK) + sign);
|
|
554
582
|
}
|
|
555
583
|
|
|
@@ -561,8 +589,11 @@ function dragonboxCoreF64(buffer: usize, value: f64): u32 {
|
|
|
561
589
|
store<u16>(buffer, CHAR_MINUS);
|
|
562
590
|
}
|
|
563
591
|
const bits = reinterpret<u64>(value);
|
|
564
|
-
const digits = dragonboxToDecimalF64(
|
|
565
|
-
|
|
592
|
+
const digits = dragonboxToDecimalF64(
|
|
593
|
+
bits & 0x000fffffffffffff,
|
|
594
|
+
<i32>((bits >>> 52) & 0x7ff),
|
|
595
|
+
);
|
|
596
|
+
let len = itoaU64(buffer + ((<usize>sign) << 1), digits);
|
|
566
597
|
return <u32>(prettify(buffer + ((<usize>sign) << 1), len, _dbK) + sign);
|
|
567
598
|
}
|
|
568
599
|
|
|
@@ -589,6 +620,7 @@ export function dragonbox_f32_buffered(buffer: usize, value: f32): u32 {
|
|
|
589
620
|
store<u64>(buffer + 8, 0x7900740069006e);
|
|
590
621
|
return 8 + (sign ? 1 : 0);
|
|
591
622
|
}
|
|
623
|
+
ensureItoaPairs();
|
|
592
624
|
return dragonboxCoreF32(buffer, value);
|
|
593
625
|
}
|
|
594
626
|
|
|
@@ -615,10 +647,14 @@ export function dragonbox_f64_buffered(buffer: usize, value: f64): u32 {
|
|
|
615
647
|
store<u64>(buffer + 8, 0x7900740069006e);
|
|
616
648
|
return 8 + (sign ? 1 : 0);
|
|
617
649
|
}
|
|
650
|
+
ensureItoaPairs();
|
|
618
651
|
return dragonboxCoreF64(buffer, value);
|
|
619
652
|
}
|
|
620
653
|
|
|
621
|
-
export function dragonbox_buffered<T extends number>(
|
|
654
|
+
export function dragonbox_buffered<T extends number>(
|
|
655
|
+
buffer: usize,
|
|
656
|
+
value: T,
|
|
657
|
+
): u32 {
|
|
622
658
|
if (sizeof<T>() == 4) return dragonbox_f32_buffered(buffer, <f32>value);
|
|
623
659
|
return dragonbox_f64_buffered(buffer, <f64>value);
|
|
624
660
|
}
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
// Fast integer -> UTF-16 stringification.
|
|
2
|
+
//
|
|
3
|
+
// We tried the "real" jeaiii algorithm (fixed-point magic-multiplication
|
|
4
|
+
// per bucket; `f0 -> f2 -> f4 -> f6` chained fractional-part extractions)
|
|
5
|
+
// and it ran ~5-7% slower on V8/wasm than the div-by-constant variant
|
|
6
|
+
// below. Two reasons:
|
|
7
|
+
//
|
|
8
|
+
// 1. V8/wasm lowers `v / 100` (and other `/ <const>`s) to a single
|
|
9
|
+
// multiply-shift, so jeaiii's main selling point — avoiding division
|
|
10
|
+
// hardware — gives no win on this target. The op counts come out
|
|
11
|
+
// roughly equal.
|
|
12
|
+
//
|
|
13
|
+
// 2. The div-by-const variant computes each digit pair independently
|
|
14
|
+
// from `v` (`h = v / 100`, `l = v - h*100`, etc), so V8 schedules
|
|
15
|
+
// the LUT loads + stores for all pairs in parallel. The jeaiii
|
|
16
|
+
// chain forces them serial.
|
|
17
|
+
//
|
|
18
|
+
// What we keep from jeaiii here:
|
|
19
|
+
//
|
|
20
|
+
// - Width-ladder dispatch (`if v < 100 / 10_000 / 1_000_000 / ...`) so
|
|
21
|
+
// the same comparisons that would drive a separate `decimalCount`
|
|
22
|
+
// pass become the bucket pick.
|
|
23
|
+
//
|
|
24
|
+
// - A 100-entry digit-pair LUT keyed on `value % 100`. One `store<u32>`
|
|
25
|
+
// emits a UTF-16 pair.
|
|
26
|
+
//
|
|
27
|
+
// - Forward write in one pass — no `decimalCount32` precomputation, no
|
|
28
|
+
// backward write.
|
|
29
|
+
//
|
|
30
|
+
// Reference H2H bench: `__benches__/custom/itoa-h2h.bench.ts`.
|
|
31
|
+
|
|
32
|
+
// 100-entry pair LUT: index `i` -> u32 holding UTF-16 chars for the
|
|
33
|
+
// zero-padded two-digit string "DD". One `store<u32>` writes the pair.
|
|
34
|
+
const DIGIT_PAIRS_UTF16: usize = memory.data(100 * 4);
|
|
35
|
+
let _pairsInited: bool = false;
|
|
36
|
+
|
|
37
|
+
function initPairs(): void {
|
|
38
|
+
for (let i: i32 = 0; i < 100; i++) {
|
|
39
|
+
const tens = u32(0x30 + i / 10);
|
|
40
|
+
const units = u32(0x30 + (i % 10));
|
|
41
|
+
store<u32>(DIGIT_PAIRS_UTF16 + ((<usize>i) << 2), tens | (units << 16));
|
|
42
|
+
}
|
|
43
|
+
_pairsInited = true;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@inline export function ensureItoaPairs(): void {
|
|
48
|
+
if (!_pairsInited) initPairs();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// @ts-expect-error: @inline is a valid decorator
|
|
52
|
+
@inline function pair(i: u32): u32 {
|
|
53
|
+
return load<u32>(DIGIT_PAIRS_UTF16 + ((<usize>i) << 2));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* u32 -> UTF-16 stringification, forward write.
|
|
58
|
+
* Returns the number of UTF-16 chars written (caller multiplies by 2 for
|
|
59
|
+
* a byte offset). Caller must ensure the buffer has at least 20 bytes
|
|
60
|
+
* available (max 10 chars).
|
|
61
|
+
*/
|
|
62
|
+
// @ts-expect-error: @inline is a valid decorator
|
|
63
|
+
@inline export function itoaU32(buf: usize, v: u32): u32 {
|
|
64
|
+
if (v < 10) {
|
|
65
|
+
store<u16>(buf, <u16>(v + 0x30));
|
|
66
|
+
return 1;
|
|
67
|
+
}
|
|
68
|
+
if (v < 100) {
|
|
69
|
+
store<u32>(buf, pair(v));
|
|
70
|
+
return 2;
|
|
71
|
+
}
|
|
72
|
+
if (v < 1_000_000) {
|
|
73
|
+
if (v < 10_000) {
|
|
74
|
+
if (v < 1_000) {
|
|
75
|
+
const h = v / 100;
|
|
76
|
+
const l = v - h * 100;
|
|
77
|
+
store<u16>(buf, <u16>(h + 0x30));
|
|
78
|
+
store<u32>(buf, pair(l), 2);
|
|
79
|
+
return 3;
|
|
80
|
+
}
|
|
81
|
+
const h = v / 100;
|
|
82
|
+
const l = v - h * 100;
|
|
83
|
+
store<u32>(buf, pair(h));
|
|
84
|
+
store<u32>(buf, pair(l), 4);
|
|
85
|
+
return 4;
|
|
86
|
+
}
|
|
87
|
+
if (v < 100_000) {
|
|
88
|
+
const hi = v / 10_000;
|
|
89
|
+
const rest = v - hi * 10_000;
|
|
90
|
+
const m = rest / 100;
|
|
91
|
+
const l = rest - m * 100;
|
|
92
|
+
store<u16>(buf, <u16>(hi + 0x30));
|
|
93
|
+
store<u32>(buf, pair(m), 2);
|
|
94
|
+
store<u32>(buf, pair(l), 6);
|
|
95
|
+
return 5;
|
|
96
|
+
}
|
|
97
|
+
const hi = v / 10_000;
|
|
98
|
+
const rest = v - hi * 10_000;
|
|
99
|
+
const m = rest / 100;
|
|
100
|
+
const l = rest - m * 100;
|
|
101
|
+
store<u32>(buf, pair(hi));
|
|
102
|
+
store<u32>(buf, pair(m), 4);
|
|
103
|
+
store<u32>(buf, pair(l), 8);
|
|
104
|
+
return 6;
|
|
105
|
+
}
|
|
106
|
+
if (v < 100_000_000) {
|
|
107
|
+
if (v < 10_000_000) {
|
|
108
|
+
const top = v / 1_000_000;
|
|
109
|
+
let rest = v - top * 1_000_000;
|
|
110
|
+
const m = rest / 10_000;
|
|
111
|
+
rest = rest - m * 10_000;
|
|
112
|
+
const n = rest / 100;
|
|
113
|
+
const l = rest - n * 100;
|
|
114
|
+
store<u16>(buf, <u16>(top + 0x30));
|
|
115
|
+
store<u32>(buf, pair(m), 2);
|
|
116
|
+
store<u32>(buf, pair(n), 6);
|
|
117
|
+
store<u32>(buf, pair(l), 10);
|
|
118
|
+
return 7;
|
|
119
|
+
}
|
|
120
|
+
const top = v / 1_000_000;
|
|
121
|
+
let rest = v - top * 1_000_000;
|
|
122
|
+
const m = rest / 10_000;
|
|
123
|
+
rest = rest - m * 10_000;
|
|
124
|
+
const n = rest / 100;
|
|
125
|
+
const l = rest - n * 100;
|
|
126
|
+
store<u32>(buf, pair(top));
|
|
127
|
+
store<u32>(buf, pair(m), 4);
|
|
128
|
+
store<u32>(buf, pair(n), 8);
|
|
129
|
+
store<u32>(buf, pair(l), 12);
|
|
130
|
+
return 8;
|
|
131
|
+
}
|
|
132
|
+
if (v < 1_000_000_000) {
|
|
133
|
+
const top = v / 100_000_000;
|
|
134
|
+
let rest = v - top * 100_000_000;
|
|
135
|
+
const a = rest / 1_000_000;
|
|
136
|
+
rest = rest - a * 1_000_000;
|
|
137
|
+
const b = rest / 10_000;
|
|
138
|
+
rest = rest - b * 10_000;
|
|
139
|
+
const c = rest / 100;
|
|
140
|
+
const d = rest - c * 100;
|
|
141
|
+
store<u16>(buf, <u16>(top + 0x30));
|
|
142
|
+
store<u32>(buf, pair(a), 2);
|
|
143
|
+
store<u32>(buf, pair(b), 6);
|
|
144
|
+
store<u32>(buf, pair(c), 10);
|
|
145
|
+
store<u32>(buf, pair(d), 14);
|
|
146
|
+
return 9;
|
|
147
|
+
}
|
|
148
|
+
const top = v / 100_000_000;
|
|
149
|
+
let rest = v - top * 100_000_000;
|
|
150
|
+
const a = rest / 1_000_000;
|
|
151
|
+
rest = rest - a * 1_000_000;
|
|
152
|
+
const b = rest / 10_000;
|
|
153
|
+
rest = rest - b * 10_000;
|
|
154
|
+
const c = rest / 100;
|
|
155
|
+
const d = rest - c * 100;
|
|
156
|
+
store<u32>(buf, pair(top));
|
|
157
|
+
store<u32>(buf, pair(a), 4);
|
|
158
|
+
store<u32>(buf, pair(b), 8);
|
|
159
|
+
store<u32>(buf, pair(c), 12);
|
|
160
|
+
store<u32>(buf, pair(d), 16);
|
|
161
|
+
return 10;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Writes a u32 in the range 0..99_999_999 as exactly 8 UTF-16 chars with
|
|
166
|
+
* leading zeros. Used by the u64 path to emit trailing groups of 8 digits.
|
|
167
|
+
*/
|
|
168
|
+
// @ts-expect-error: @inline is a valid decorator
|
|
169
|
+
@inline function writeU32Padded8(buf: usize, v: u32): void {
|
|
170
|
+
const a = v / 1_000_000;
|
|
171
|
+
let rest = v - a * 1_000_000;
|
|
172
|
+
const b = rest / 10_000;
|
|
173
|
+
rest = rest - b * 10_000;
|
|
174
|
+
const c = rest / 100;
|
|
175
|
+
const d = rest - c * 100;
|
|
176
|
+
store<u32>(buf, pair(a));
|
|
177
|
+
store<u32>(buf, pair(b), 4);
|
|
178
|
+
store<u32>(buf, pair(c), 8);
|
|
179
|
+
store<u32>(buf, pair(d), 12);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* u64 -> UTF-16 stringification.
|
|
184
|
+
* Small values delegate to `itoaU32`. For 11+ digit values, peel 8 digits
|
|
185
|
+
* from the bottom (always fits in u32), emit the remaining top via the
|
|
186
|
+
* u32 path, then emit the 8 trailing digits with leading-zero padding.
|
|
187
|
+
* For 17+ digit values (which still fit in u64 < 1.8e19), repeat.
|
|
188
|
+
* Caller must ensure the buffer has at least 40 bytes available.
|
|
189
|
+
*/
|
|
190
|
+
// @ts-expect-error: @inline is a valid decorator
|
|
191
|
+
@inline export function itoaU64(buf: usize, v: u64): u32 {
|
|
192
|
+
if (v <= <u64>u32.MAX_VALUE) {
|
|
193
|
+
return itoaU32(buf, <u32>v);
|
|
194
|
+
}
|
|
195
|
+
const lo8 = <u32>(v % 100_000_000);
|
|
196
|
+
const hi = v / 100_000_000;
|
|
197
|
+
if (hi <= <u64>u32.MAX_VALUE) {
|
|
198
|
+
const written = itoaU32(buf, <u32>hi);
|
|
199
|
+
writeU32Padded8(buf + ((<usize>written) << 1), lo8);
|
|
200
|
+
return written + 8;
|
|
201
|
+
}
|
|
202
|
+
// 17-20 digit case: peel a second group of 8.
|
|
203
|
+
const mid8 = <u32>(hi % 100_000_000);
|
|
204
|
+
const top = <u32>(hi / 100_000_000);
|
|
205
|
+
const written = itoaU32(buf, top);
|
|
206
|
+
writeU32Padded8(buf + ((<usize>written) << 1), mid8);
|
|
207
|
+
writeU32Padded8(buf + ((<usize>written) << 1) + 16, lo8);
|
|
208
|
+
return written + 16;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Generic integer -> UTF-16 entry point. Signed types peel `-` and pass
|
|
213
|
+
* the absolute value (via two's complement negation, which works for the
|
|
214
|
+
* minimum-value edge case because `u32(-i32.MIN_VALUE) == 2147483648`
|
|
215
|
+
* and likewise for i64).
|
|
216
|
+
*
|
|
217
|
+
* Returns the number of UTF-16 chars written.
|
|
218
|
+
*/
|
|
219
|
+
// @ts-expect-error: @inline is a valid decorator
|
|
220
|
+
@inline export function itoaFast<T extends number>(buf: usize, value: T): u32 {
|
|
221
|
+
if (sizeof<T>() <= 4) {
|
|
222
|
+
if (isSigned<T>()) {
|
|
223
|
+
let v = <i32>value;
|
|
224
|
+
if (v < 0) {
|
|
225
|
+
store<u16>(buf, 0x2d); // '-'
|
|
226
|
+
return 1 + itoaU32(buf + 2, <u32>-v);
|
|
227
|
+
}
|
|
228
|
+
return itoaU32(buf, <u32>v);
|
|
229
|
+
}
|
|
230
|
+
return itoaU32(buf, <u32>value);
|
|
231
|
+
}
|
|
232
|
+
if (isSigned<T>()) {
|
|
233
|
+
let v = <i64>value;
|
|
234
|
+
if (v < 0) {
|
|
235
|
+
store<u16>(buf, 0x2d); // '-'
|
|
236
|
+
return 1 + itoaU64(buf + 2, <u64>-v);
|
|
237
|
+
}
|
|
238
|
+
return itoaU64(buf, <u64>value);
|
|
239
|
+
}
|
|
240
|
+
return itoaU64(buf, <u64>value);
|
|
241
|
+
}
|
package/assembly/util/masks.ts
CHANGED
|
@@ -25,7 +25,24 @@ export function block_to_string(block: u64): string {
|
|
|
25
25
|
export function mask_to_string_v128(vec: v128): string {
|
|
26
26
|
let result = "0x";
|
|
27
27
|
|
|
28
|
-
const lanes: i8[] = [
|
|
28
|
+
const lanes: i8[] = [
|
|
29
|
+
i8x16.extract_lane_s(vec, 0),
|
|
30
|
+
i8x16.extract_lane_s(vec, 1),
|
|
31
|
+
i8x16.extract_lane_s(vec, 2),
|
|
32
|
+
i8x16.extract_lane_s(vec, 3),
|
|
33
|
+
i8x16.extract_lane_s(vec, 4),
|
|
34
|
+
i8x16.extract_lane_s(vec, 5),
|
|
35
|
+
i8x16.extract_lane_s(vec, 6),
|
|
36
|
+
i8x16.extract_lane_s(vec, 7),
|
|
37
|
+
i8x16.extract_lane_s(vec, 8),
|
|
38
|
+
i8x16.extract_lane_s(vec, 9),
|
|
39
|
+
i8x16.extract_lane_s(vec, 10),
|
|
40
|
+
i8x16.extract_lane_s(vec, 11),
|
|
41
|
+
i8x16.extract_lane_s(vec, 12),
|
|
42
|
+
i8x16.extract_lane_s(vec, 13),
|
|
43
|
+
i8x16.extract_lane_s(vec, 14),
|
|
44
|
+
i8x16.extract_lane_s(vec, 15),
|
|
45
|
+
];
|
|
29
46
|
|
|
30
47
|
for (let i = 15; i >= 0; i--) {
|
|
31
48
|
const byte = lanes[i];
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { ptrToStr } from "./ptrToStr";
|
|
2
|
+
|
|
3
|
+
// Lemire-style fast float parser.
|
|
4
|
+
//
|
|
5
|
+
// Reference: Daniel Lemire, "Number parsing at a gigabyte per second"
|
|
6
|
+
// (2021). https://arxiv.org/abs/2101.11408 — implemented in
|
|
7
|
+
// https://github.com/fastfloat/fast_float.
|
|
8
|
+
//
|
|
9
|
+
// The "fast path" applies when:
|
|
10
|
+
// - the mantissa fits in a u64 (<=19 decimal digits), and
|
|
11
|
+
// - the total decimal exponent is in [-22, 22], so the matching
|
|
12
|
+
// `1e<exp>` power-of-ten is representable exactly in f64.
|
|
13
|
+
//
|
|
14
|
+
// In that regime `value = mantissa * 10^exp` rounds correctly under
|
|
15
|
+
// IEEE-754: both operands are exact in f64 and the single fmul is
|
|
16
|
+
// correctly rounded, so the result is the same as the strictly-rounded
|
|
17
|
+
// reference. This covers the overwhelming majority of JSON float
|
|
18
|
+
// payloads (most fields are <20 significant digits and modest
|
|
19
|
+
// exponents). Out-of-range inputs delegate to AS std's `f64.parse`
|
|
20
|
+
// (Grisu-based; correctly rounded for all f64).
|
|
21
|
+
//
|
|
22
|
+
// Compared to the original digit-by-digit accumulator (`value = value *
|
|
23
|
+
// 10.0 + digit`) this saves both wall-time (fewer fmul/fdiv) and
|
|
24
|
+
// precision (one rounding instead of N).
|
|
25
|
+
|
|
26
|
+
// 23-entry table: 10^0 .. 10^22, all exact in f64. f32 fast-paths can
|
|
27
|
+
// reuse the same table (since 10^k for k <= 22 fits in f32 only up to
|
|
28
|
+
// 10^7, but the multiplication is done in f64 and narrowed at the end).
|
|
29
|
+
const POW10_F64_POS: usize = memory.data<f64>([
|
|
30
|
+
1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14,
|
|
31
|
+
1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22,
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
const MAX_EXACT_POW10: i32 = 22;
|
|
35
|
+
// 2^53 = 9_007_199_254_740_992. Any u64 <= this is exact in f64.
|
|
36
|
+
const MAX_EXACT_MANTISSA: u64 = 1 << 53;
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@inline function loadPow10(exp: u32): f64 {
|
|
40
|
+
return load<f64>(POW10_F64_POS + ((<usize>exp) << 3));
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@inline function fallback<T>(srcStart: usize, srcEnd: usize): T {
|
|
45
|
+
const s = ptrToStr(srcStart, srcEnd);
|
|
46
|
+
// @ts-ignore: type
|
|
47
|
+
const type: T = 0;
|
|
48
|
+
// @ts-ignore: type
|
|
49
|
+
if (type instanceof f64) return <T>f64.parse(s);
|
|
50
|
+
// @ts-ignore: cast
|
|
51
|
+
return <T>(<f32>f32.parse(s));
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Fast path for `deserializeFloat`. `srcStart..srcEnd` must contain only
|
|
56
|
+
* the float content (no surrounding whitespace, no `null`). Returns the
|
|
57
|
+
* parsed value; on the slow path falls back to `f64.parse` /
|
|
58
|
+
* `f32.parse` over the same range so behavior is preserved for every
|
|
59
|
+
* input the previous parser accepted.
|
|
60
|
+
*
|
|
61
|
+
* Structure mirrors the existing parser's split integer/fraction loops
|
|
62
|
+
* (TurboFan schedules these tighter than a single fused loop) but uses
|
|
63
|
+
* u64 accumulators throughout so a 17-digit "3.141592653589793" stays
|
|
64
|
+
* exact through accumulation and only loses precision at the final
|
|
65
|
+
* `<f64>` cast.
|
|
66
|
+
*/
|
|
67
|
+
// @ts-expect-error: @inline is a valid decorator
|
|
68
|
+
@inline export function parseFloatFast<T>(srcStart: usize, srcEnd: usize): T {
|
|
69
|
+
const origStart = srcStart;
|
|
70
|
+
let p = srcStart;
|
|
71
|
+
let negative = false;
|
|
72
|
+
if (p < srcEnd && load<u16>(p) == 45) {
|
|
73
|
+
negative = true;
|
|
74
|
+
p += 2;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Integer part.
|
|
78
|
+
let mantissa: u64 = 0;
|
|
79
|
+
let intDigits: i32 = 0;
|
|
80
|
+
while (p < srcEnd) {
|
|
81
|
+
const d = <u32>load<u16>(p) - 48;
|
|
82
|
+
if (d > 9) break;
|
|
83
|
+
mantissa = mantissa * 10 + <u64>d;
|
|
84
|
+
intDigits++;
|
|
85
|
+
p += 2;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Optional fractional part.
|
|
89
|
+
let fracDigits: i32 = 0;
|
|
90
|
+
if (p < srcEnd && load<u16>(p) == 46) {
|
|
91
|
+
p += 2;
|
|
92
|
+
while (p < srcEnd) {
|
|
93
|
+
const d = <u32>load<u16>(p) - 48;
|
|
94
|
+
if (d > 9) break;
|
|
95
|
+
mantissa = mantissa * 10 + <u64>d;
|
|
96
|
+
fracDigits++;
|
|
97
|
+
p += 2;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const mantDigits = intDigits + fracDigits;
|
|
102
|
+
if (mantDigits == 0) {
|
|
103
|
+
// No digits seen (e.g. `.5`, `NaN`, `Infinity`) - defer to AS std.
|
|
104
|
+
return fallback<T>(origStart, srcEnd);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
let exponent: i32 = -fracDigits;
|
|
108
|
+
|
|
109
|
+
// Optional `e[+-]NNN` suffix.
|
|
110
|
+
if (p < srcEnd) {
|
|
111
|
+
const c = load<u16>(p);
|
|
112
|
+
if (c == 101 || c == 69) {
|
|
113
|
+
p += 2;
|
|
114
|
+
if (p >= srcEnd) return fallback<T>(origStart, srcEnd);
|
|
115
|
+
let expNeg = false;
|
|
116
|
+
const sc = load<u16>(p);
|
|
117
|
+
if (sc == 45) {
|
|
118
|
+
expNeg = true;
|
|
119
|
+
p += 2;
|
|
120
|
+
} else if (sc == 43) {
|
|
121
|
+
p += 2;
|
|
122
|
+
}
|
|
123
|
+
if (p >= srcEnd) return fallback<T>(origStart, srcEnd);
|
|
124
|
+
let exp: i32 = 0;
|
|
125
|
+
let expDigits: i32 = 0;
|
|
126
|
+
while (p < srcEnd) {
|
|
127
|
+
const d = <u32>load<u16>(p) - 48;
|
|
128
|
+
if (d > 9) break;
|
|
129
|
+
exp = exp * 10 + <i32>d;
|
|
130
|
+
expDigits++;
|
|
131
|
+
if (expDigits > 4) {
|
|
132
|
+
// Pathological exponent - fall back for safety.
|
|
133
|
+
return fallback<T>(origStart, srcEnd);
|
|
134
|
+
}
|
|
135
|
+
p += 2;
|
|
136
|
+
}
|
|
137
|
+
if (expDigits == 0) return fallback<T>(origStart, srcEnd);
|
|
138
|
+
exponent += expNeg ? -exp : exp;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Fast path eligibility: mantissa fits exactly in an f64 and exponent
|
|
143
|
+
// is in the exactly-representable pow10 range. Both halves are needed
|
|
144
|
+
// for the result to be correctly rounded. Capping `mantDigits` at 19
|
|
145
|
+
// is a cheaper proxy for "didn't overflow u64".
|
|
146
|
+
if (mantDigits > 19 || mantissa > MAX_EXACT_MANTISSA) {
|
|
147
|
+
return fallback<T>(origStart, srcEnd);
|
|
148
|
+
}
|
|
149
|
+
if (exponent > MAX_EXACT_POW10 || exponent < -MAX_EXACT_POW10) {
|
|
150
|
+
return fallback<T>(origStart, srcEnd);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
let result = <f64>mantissa;
|
|
154
|
+
if (exponent > 0) {
|
|
155
|
+
result *= loadPow10(<u32>exponent);
|
|
156
|
+
} else if (exponent < 0) {
|
|
157
|
+
result /= loadPow10(<u32>-exponent);
|
|
158
|
+
}
|
|
159
|
+
if (negative) result = -result;
|
|
160
|
+
|
|
161
|
+
// @ts-ignore: type
|
|
162
|
+
const type: T = 0;
|
|
163
|
+
// @ts-ignore: type
|
|
164
|
+
if (type instanceof f64) return <T>result;
|
|
165
|
+
// @ts-ignore: cast
|
|
166
|
+
return <T>(<f32>result);
|
|
167
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BRACE_LEFT,
|
|
3
|
+
BRACE_RIGHT,
|
|
4
|
+
BRACKET_LEFT,
|
|
5
|
+
BRACKET_RIGHT,
|
|
6
|
+
COMMA,
|
|
7
|
+
QUOTE,
|
|
8
|
+
} from "../custom/chars";
|
|
9
|
+
import { isSpace } from "./isSpace";
|
|
10
|
+
import { scanStringEnd } from "./stringScan";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Pure-scalar value-end scanner used by the NAIVE container deserializers.
|
|
14
|
+
*
|
|
15
|
+
* Returns the position immediately after the value that begins at `srcStart`:
|
|
16
|
+
*
|
|
17
|
+
* - For a quoted string: position past the closing `"` (uses scalar
|
|
18
|
+
* {@link scanStringEnd}).
|
|
19
|
+
* - For an object/array: position past the matching `}`/`]`, tracking depth
|
|
20
|
+
* and skipping nested quoted strings.
|
|
21
|
+
* - For anything else: position of the first `,`, `]`, or `}` (the value's
|
|
22
|
+
* structural terminator).
|
|
23
|
+
*
|
|
24
|
+
* Returns `0` when the input is empty or no terminator is found.
|
|
25
|
+
*
|
|
26
|
+
* Mirrors the semantics of `deserialize/swar/array/shared.ts:scanValueEnd`
|
|
27
|
+
* but stays scalar so `naive/` callers don't pull SWAR into the correctness
|
|
28
|
+
* baseline.
|
|
29
|
+
*/
|
|
30
|
+
// @ts-ignore: inline
|
|
31
|
+
@inline export function scanValueEnd(srcStart: usize, srcEnd: usize): usize {
|
|
32
|
+
if (srcStart >= srcEnd) return 0;
|
|
33
|
+
const first = load<u16>(srcStart);
|
|
34
|
+
|
|
35
|
+
if (first == QUOTE) {
|
|
36
|
+
const end = scanStringEnd(srcStart, srcEnd);
|
|
37
|
+
return end >= srcEnd ? 0 : end + 2;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (first == BRACE_LEFT || first == BRACKET_LEFT) {
|
|
41
|
+
let depth: i32 = 1;
|
|
42
|
+
let ptr = srcStart + 2;
|
|
43
|
+
while (ptr < srcEnd) {
|
|
44
|
+
const code = load<u16>(ptr);
|
|
45
|
+
if (code == QUOTE) {
|
|
46
|
+
const end = scanStringEnd(ptr, srcEnd);
|
|
47
|
+
if (end >= srcEnd) return 0;
|
|
48
|
+
ptr = end + 2;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (code == BRACE_LEFT || code == BRACKET_LEFT) {
|
|
52
|
+
depth++;
|
|
53
|
+
} else if (code == BRACE_RIGHT || code == BRACKET_RIGHT) {
|
|
54
|
+
if (--depth == 0) return ptr + 2;
|
|
55
|
+
}
|
|
56
|
+
ptr += 2;
|
|
57
|
+
}
|
|
58
|
+
return 0;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
while (srcStart < srcEnd) {
|
|
62
|
+
const code = load<u16>(srcStart);
|
|
63
|
+
// Stop at the structural terminator OR trailing whitespace, so the returned
|
|
64
|
+
// range is the exact value (scalar parsers assume [srcStart,srcEnd) is the
|
|
65
|
+
// value with no trailing whitespace). Callers skip whitespace to reach the
|
|
66
|
+
// following `,`/`]`/`}`.
|
|
67
|
+
if (
|
|
68
|
+
code == COMMA ||
|
|
69
|
+
code == BRACKET_RIGHT ||
|
|
70
|
+
code == BRACE_RIGHT ||
|
|
71
|
+
isSpace(code)
|
|
72
|
+
)
|
|
73
|
+
return srcStart;
|
|
74
|
+
srcStart += 2;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return 0;
|
|
78
|
+
}
|