json-as 1.3.9 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/CHANGELOG.md +60 -19
  2. package/README.md +120 -21
  3. package/assembly/custom/chars.ts +39 -78
  4. package/assembly/deserialize/index/arbitrary.ts +28 -10
  5. package/assembly/deserialize/index/float.ts +2 -4
  6. package/assembly/deserialize/index/integer.ts +2 -4
  7. package/assembly/deserialize/index/object.ts +6 -1
  8. package/assembly/deserialize/index/string.ts +2 -7
  9. package/assembly/deserialize/index/unsigned.ts +2 -4
  10. package/assembly/deserialize/naive/array/arbitrary.ts +3 -136
  11. package/assembly/deserialize/naive/array/array.ts +30 -1
  12. package/assembly/deserialize/naive/array/integer.ts +2 -7
  13. package/assembly/deserialize/naive/array/map.ts +10 -14
  14. package/assembly/deserialize/naive/array/object.ts +10 -14
  15. package/assembly/deserialize/naive/array/struct.ts +19 -1
  16. package/assembly/deserialize/naive/bool.ts +1 -5
  17. package/assembly/deserialize/naive/date.ts +1 -2
  18. package/assembly/deserialize/naive/float.ts +4 -11
  19. package/assembly/deserialize/naive/integer.ts +2 -4
  20. package/assembly/deserialize/naive/map.ts +42 -205
  21. package/assembly/deserialize/naive/object.ts +291 -174
  22. package/assembly/deserialize/naive/raw.ts +1 -5
  23. package/assembly/deserialize/naive/set.ts +3 -6
  24. package/assembly/deserialize/naive/staticarray.ts +2 -4
  25. package/assembly/deserialize/naive/string.ts +68 -24
  26. package/assembly/deserialize/naive/typedarray.ts +1 -2
  27. package/assembly/deserialize/naive/unsigned.ts +2 -4
  28. package/assembly/deserialize/simd/array/integer.ts +5 -13
  29. package/assembly/deserialize/simd/float.ts +5 -12
  30. package/assembly/deserialize/simd/integer.ts +6 -15
  31. package/assembly/deserialize/simd/string.ts +21 -43
  32. package/assembly/deserialize/swar/array/arbitrary.ts +1 -2
  33. package/assembly/deserialize/swar/array/array.ts +2 -4
  34. package/assembly/deserialize/swar/array/bool.ts +2 -4
  35. package/assembly/deserialize/swar/array/box.ts +1 -2
  36. package/assembly/deserialize/swar/array/float.ts +8 -21
  37. package/assembly/deserialize/swar/array/generic.ts +2 -4
  38. package/assembly/deserialize/swar/array/integer.ts +13 -27
  39. package/assembly/deserialize/swar/array/map.ts +1 -2
  40. package/assembly/deserialize/swar/array/object.ts +2 -4
  41. package/assembly/deserialize/swar/array/raw.ts +1 -2
  42. package/assembly/deserialize/swar/array/shared.ts +9 -21
  43. package/assembly/deserialize/swar/array/string.ts +4 -10
  44. package/assembly/deserialize/swar/array/struct.ts +3 -9
  45. package/assembly/deserialize/swar/array.ts +1 -3
  46. package/assembly/deserialize/swar/float.ts +7 -17
  47. package/assembly/deserialize/swar/integer.ts +6 -15
  48. package/assembly/deserialize/swar/string.ts +40 -54
  49. package/assembly/deserialize/swar/typedarray.ts +4 -4
  50. package/assembly/index.d.ts +259 -21
  51. package/assembly/index.ts +1704 -266
  52. package/assembly/serialize/index/arbitrary.ts +70 -4
  53. package/assembly/serialize/index/jsonarray.ts +51 -0
  54. package/assembly/serialize/index/object.ts +39 -14
  55. package/assembly/serialize/index/string.ts +1 -2
  56. package/assembly/serialize/index/typedarray.ts +1 -2
  57. package/assembly/serialize/index.ts +1 -0
  58. package/assembly/serialize/naive/array.ts +23 -34
  59. package/assembly/serialize/naive/bool.ts +0 -1
  60. package/assembly/serialize/naive/float.ts +16 -25
  61. package/assembly/serialize/naive/integer.ts +1 -5
  62. package/assembly/serialize/naive/raw.ts +1 -2
  63. package/assembly/serialize/naive/set.ts +0 -4
  64. package/assembly/serialize/naive/staticarray.ts +0 -5
  65. package/assembly/serialize/naive/string.ts +11 -7
  66. package/assembly/serialize/naive/typedarray.ts +0 -6
  67. package/assembly/serialize/simd/string.ts +1 -3
  68. package/assembly/serialize/swar/string.ts +2 -4
  69. package/assembly/util/atoi-fast.ts +4 -14
  70. package/assembly/util/atoi.ts +1 -2
  71. package/assembly/util/bytes.ts +1 -2
  72. package/assembly/util/idofd.ts +1 -2
  73. package/assembly/util/isSpace.ts +1 -2
  74. package/assembly/util/itoa-fast.ts +9 -15
  75. package/assembly/util/nextPowerOf2.ts +1 -2
  76. package/assembly/util/parsefloat-fast.ts +4 -7
  77. package/assembly/util/ptrToStr.ts +1 -2
  78. package/assembly/util/scanValueEnd.ts +1 -2
  79. package/assembly/util/scanValueEndSimd.ts +198 -0
  80. package/assembly/util/scanValueEndSwar.ts +184 -0
  81. package/assembly/util/scientific.ts +8 -14
  82. package/assembly/util/simd-int.ts +4 -8
  83. package/assembly/util/snp.ts +2 -7
  84. package/assembly/util/stringScan.ts +2 -4
  85. package/assembly/util/swar-int.ts +8 -16
  86. package/assembly/util/swar.ts +2 -4
  87. package/lib/as-bs.ts +57 -42
  88. package/package.json +27 -10
  89. package/transform/lib/builder.d.ts +0 -1
  90. package/transform/lib/builder.js +0 -1
  91. package/transform/lib/index.d.ts +0 -1
  92. package/transform/lib/index.js +617 -326
  93. package/transform/lib/linkers/alias.d.ts +0 -1
  94. package/transform/lib/linkers/alias.js +0 -1
  95. package/transform/lib/linkers/custom.d.ts +0 -1
  96. package/transform/lib/linkers/custom.js +0 -1
  97. package/transform/lib/linkers/imports.d.ts +0 -1
  98. package/transform/lib/linkers/imports.js +0 -1
  99. package/transform/lib/types.d.ts +4 -2
  100. package/transform/lib/types.js +5 -1
  101. package/transform/lib/util.d.ts +0 -1
  102. package/transform/lib/util.js +0 -1
  103. package/transform/lib/visitor.d.ts +0 -1
  104. package/transform/lib/visitor.js +0 -1
  105. package/assembly/util/dragonbox-cache.ts +0 -445
  106. package/assembly/util/dragonbox.ts +0 -660
  107. package/transform/lib/builder.d.ts.map +0 -1
  108. package/transform/lib/builder.js.map +0 -1
  109. package/transform/lib/index.d.ts.map +0 -1
  110. package/transform/lib/index.js.map +0 -1
  111. package/transform/lib/linkers/alias.d.ts.map +0 -1
  112. package/transform/lib/linkers/alias.js.map +0 -1
  113. package/transform/lib/linkers/custom.d.ts.map +0 -1
  114. package/transform/lib/linkers/custom.js.map +0 -1
  115. package/transform/lib/linkers/imports.d.ts.map +0 -1
  116. package/transform/lib/linkers/imports.js.map +0 -1
  117. package/transform/lib/types.d.ts.map +0 -1
  118. package/transform/lib/types.js.map +0 -1
  119. package/transform/lib/util.d.ts.map +0 -1
  120. package/transform/lib/util.js.map +0 -1
  121. package/transform/lib/visitor.d.ts.map +0 -1
  122. package/transform/lib/visitor.js.map +0 -1
@@ -18,11 +18,7 @@ import {
18
18
  * @param srcEnd Pointer just past the last code unit.
19
19
  * @returns The parsed value, truncated to `T`.
20
20
  */
21
- // @ts-expect-error: @inline is a valid decorator
22
- @inline export function atou<T extends number>(
23
- srcStart: usize,
24
- srcEnd: usize,
25
- ): T {
21
+ export function atou<T extends number>(srcStart: usize, srcEnd: usize): T {
26
22
  return deserializeUnsigned_SWAR<T>(srcStart, srcEnd);
27
23
  }
28
24
 
@@ -34,11 +30,7 @@ import {
34
30
  * @param srcEnd Pointer just past the last code unit.
35
31
  * @returns The parsed value, truncated to `T`.
36
32
  */
37
- // @ts-expect-error: @inline is a valid decorator
38
- @inline export function atoi<T extends number>(
39
- srcStart: usize,
40
- srcEnd: usize,
41
- ): T {
33
+ export function atoi<T extends number>(srcStart: usize, srcEnd: usize): T {
42
34
  return deserializeInteger_SWAR<T>(srcStart, srcEnd);
43
35
  }
44
36
 
@@ -52,8 +44,7 @@ import {
52
44
  * @param dstPtr Destination pointer for the parsed value.
53
45
  * @returns The source position immediately after the last digit consumed.
54
46
  */
55
- // @ts-expect-error: @inline is a valid decorator
56
- @inline export function atouScan<T extends number>(
47
+ export function atouScan<T extends number>(
57
48
  srcStart: usize,
58
49
  srcEnd: usize,
59
50
  dstPtr: usize,
@@ -71,8 +62,7 @@ import {
71
62
  * @param dstPtr Destination pointer for the parsed value.
72
63
  * @returns The source position immediately after the last digit consumed.
73
64
  */
74
- // @ts-expect-error: @inline is a valid decorator
75
- @inline export function atoiScan<T extends number>(
65
+ export function atoiScan<T extends number>(
76
66
  srcStart: usize,
77
67
  srcEnd: usize,
78
68
  dstPtr: usize,
@@ -3,8 +3,7 @@
3
3
  * @param str
4
4
  * @returns
5
5
  */
6
- // @ts-ignore: inline
7
- @inline export function atoi<T>(srcStart: usize, srcEnd: usize): T {
6
+ export function atoi<T>(srcStart: usize, srcEnd: usize): T {
8
7
  // @ts-ignore: type
9
8
  let val: T = 0;
10
9
  if (isSigned<T>()) {
@@ -1,7 +1,6 @@
1
1
  import { OBJECT, TOTAL_OVERHEAD } from "rt/common";
2
2
 
3
- // @ts-ignore: Decorator valid here
4
- @inline export function bytes<T>(o: T): i32 {
3
+ export function bytes<T>(o: T): i32 {
5
4
  if (isInteger<T>() || isFloat<T>()) {
6
5
  return sizeof<T>();
7
6
  } else if (isManaged<T>() || isReference<T>()) {
@@ -1,6 +1,5 @@
1
1
  import { OBJECT, TOTAL_OVERHEAD } from "rt/common";
2
2
 
3
- // @ts-ignore: decorator
4
- @inline export function idofD<T>(value: T): usize {
3
+ export function idofD<T>(value: T): usize {
5
4
  return changetype<OBJECT>(changetype<usize>(value) - TOTAL_OVERHEAD).rtId;
6
5
  }
@@ -1,4 +1,3 @@
1
- // @ts-ignore
2
- @inline export function isSpace(code: u16): boolean {
1
+ export function isSpace(code: u16): boolean {
3
2
  return code == 0x20 || code - 9 <= 4;
4
3
  }
@@ -6,8 +6,8 @@
6
6
  // below. Two reasons:
7
7
  //
8
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
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
11
  // roughly equal.
12
12
  //
13
13
  // 2. The div-by-const variant computes each digit pair independently
@@ -24,7 +24,7 @@
24
24
  // - A 100-entry digit-pair LUT keyed on `value % 100`. One `store<u32>`
25
25
  // emits a UTF-16 pair.
26
26
  //
27
- // - Forward write in one pass no `decimalCount32` precomputation, no
27
+ // - Forward write in one pass - no `decimalCount32` precomputation, no
28
28
  // backward write.
29
29
  //
30
30
  // Reference H2H bench: `__benches__/custom/itoa-h2h.bench.ts`.
@@ -43,13 +43,11 @@ function initPairs(): void {
43
43
  _pairsInited = true;
44
44
  }
45
45
 
46
-
47
- @inline export function ensureItoaPairs(): void {
46
+ export function ensureItoaPairs(): void {
48
47
  if (!_pairsInited) initPairs();
49
48
  }
50
49
 
51
- // @ts-expect-error: @inline is a valid decorator
52
- @inline function pair(i: u32): u32 {
50
+ function pair(i: u32): u32 {
53
51
  return load<u32>(DIGIT_PAIRS_UTF16 + ((<usize>i) << 2));
54
52
  }
55
53
 
@@ -59,8 +57,7 @@ function initPairs(): void {
59
57
  * a byte offset). Caller must ensure the buffer has at least 20 bytes
60
58
  * available (max 10 chars).
61
59
  */
62
- // @ts-expect-error: @inline is a valid decorator
63
- @inline export function itoaU32(buf: usize, v: u32): u32 {
60
+ export function itoaU32(buf: usize, v: u32): u32 {
64
61
  if (v < 10) {
65
62
  store<u16>(buf, <u16>(v + 0x30));
66
63
  return 1;
@@ -165,8 +162,7 @@ function initPairs(): void {
165
162
  * Writes a u32 in the range 0..99_999_999 as exactly 8 UTF-16 chars with
166
163
  * leading zeros. Used by the u64 path to emit trailing groups of 8 digits.
167
164
  */
168
- // @ts-expect-error: @inline is a valid decorator
169
- @inline function writeU32Padded8(buf: usize, v: u32): void {
165
+ function writeU32Padded8(buf: usize, v: u32): void {
170
166
  const a = v / 1_000_000;
171
167
  let rest = v - a * 1_000_000;
172
168
  const b = rest / 10_000;
@@ -187,8 +183,7 @@ function initPairs(): void {
187
183
  * For 17+ digit values (which still fit in u64 < 1.8e19), repeat.
188
184
  * Caller must ensure the buffer has at least 40 bytes available.
189
185
  */
190
- // @ts-expect-error: @inline is a valid decorator
191
- @inline export function itoaU64(buf: usize, v: u64): u32 {
186
+ export function itoaU64(buf: usize, v: u64): u32 {
192
187
  if (v <= <u64>u32.MAX_VALUE) {
193
188
  return itoaU32(buf, <u32>v);
194
189
  }
@@ -216,8 +211,7 @@ function initPairs(): void {
216
211
  *
217
212
  * Returns the number of UTF-16 chars written.
218
213
  */
219
- // @ts-expect-error: @inline is a valid decorator
220
- @inline export function itoaFast<T extends number>(buf: usize, value: T): u32 {
214
+ export function itoaFast<T extends number>(buf: usize, value: T): u32 {
221
215
  if (sizeof<T>() <= 4) {
222
216
  if (isSigned<T>()) {
223
217
  let v = <i32>value;
@@ -1,4 +1,3 @@
1
- // @ts-ignore: Decorator valid here
2
- @inline export function nextPowerOf2(n: u32): u32 {
1
+ export function nextPowerOf2(n: u32): u32 {
3
2
  return 1 << (32 - clz(n - 1));
4
3
  }
@@ -3,7 +3,7 @@ import { ptrToStr } from "./ptrToStr";
3
3
  // Lemire-style fast float parser.
4
4
  //
5
5
  // Reference: Daniel Lemire, "Number parsing at a gigabyte per second"
6
- // (2021). https://arxiv.org/abs/2101.11408 implemented in
6
+ // (2021). https://arxiv.org/abs/2101.11408 - implemented in
7
7
  // https://github.com/fastfloat/fast_float.
8
8
  //
9
9
  // The "fast path" applies when:
@@ -35,13 +35,11 @@ const MAX_EXACT_POW10: i32 = 22;
35
35
  // 2^53 = 9_007_199_254_740_992. Any u64 <= this is exact in f64.
36
36
  const MAX_EXACT_MANTISSA: u64 = 1 << 53;
37
37
 
38
-
39
- @inline function loadPow10(exp: u32): f64 {
38
+ function loadPow10(exp: u32): f64 {
40
39
  return load<f64>(POW10_F64_POS + ((<usize>exp) << 3));
41
40
  }
42
41
 
43
-
44
- @inline function fallback<T>(srcStart: usize, srcEnd: usize): T {
42
+ function fallback<T>(srcStart: usize, srcEnd: usize): T {
45
43
  const s = ptrToStr(srcStart, srcEnd);
46
44
  // @ts-ignore: type
47
45
  const type: T = 0;
@@ -64,8 +62,7 @@ const MAX_EXACT_MANTISSA: u64 = 1 << 53;
64
62
  * exact through accumulation and only loses precision at the final
65
63
  * `<f64>` cast.
66
64
  */
67
- // @ts-expect-error: @inline is a valid decorator
68
- @inline export function parseFloatFast<T>(srcStart: usize, srcEnd: usize): T {
65
+ export function parseFloatFast<T>(srcStart: usize, srcEnd: usize): T {
69
66
  const origStart = srcStart;
70
67
  let p = srcStart;
71
68
  let negative = false;
@@ -1,5 +1,4 @@
1
- // @ts-ignore: decorator
2
- @inline export function ptrToStr(start: usize, end: usize): string {
1
+ export function ptrToStr(start: usize, end: usize): string {
3
2
  const size = end - start;
4
3
  const out = __new(size, idof<string>());
5
4
  memory.copy(out, start, size);
@@ -27,8 +27,7 @@ import { scanStringEnd } from "./stringScan";
27
27
  * but stays scalar so `naive/` callers don't pull SWAR into the correctness
28
28
  * baseline.
29
29
  */
30
- // @ts-ignore: inline
31
- @inline export function scanValueEnd(srcStart: usize, srcEnd: usize): usize {
30
+ export function scanValueEnd<T = usize>(srcStart: usize, srcEnd: usize): usize {
32
31
  if (srcStart >= srcEnd) return 0;
33
32
  const first = load<u16>(srcStart);
34
33
 
@@ -0,0 +1,198 @@
1
+ import {
2
+ BACK_SLASH,
3
+ BRACE_LEFT,
4
+ BRACE_RIGHT,
5
+ BRACKET_LEFT,
6
+ BRACKET_RIGHT,
7
+ COLON,
8
+ COMMA,
9
+ QUOTE,
10
+ } from "../custom/chars";
11
+ import { isSpace } from "./isSpace";
12
+
13
+ // @ts-expect-error: @lazy is a valid decorator
14
+ @lazy const SPLAT_QUOTE = i16x8.splat(<i16>QUOTE);
15
+ // @ts-expect-error: @lazy is a valid decorator
16
+ @lazy const SPLAT_BACK_SLASH = i16x8.splat(<i16>BACK_SLASH);
17
+ // @ts-expect-error: @lazy is a valid decorator
18
+ @lazy const SPLAT_BRACE_RIGHT = i16x8.splat(<i16>BRACE_RIGHT);
19
+ // @ts-expect-error: @lazy is a valid decorator
20
+ @lazy const SPLAT_BRACKET_RIGHT = i16x8.splat(<i16>BRACKET_RIGHT);
21
+ // @ts-expect-error: @lazy is a valid decorator
22
+ @lazy const SPLAT_COMMA = i16x8.splat(<i16>COMMA);
23
+ // @ts-expect-error: @lazy is a valid decorator
24
+ @lazy const SPLAT_SPACE = i16x8.splat(0x20);
25
+ // JSON whitespace besides space is the contiguous range 0x09..0x0d
26
+ // (tab/LF/VT/FF/CR), matched as `(c - 9) u<= 4` - one sub + one unsigned
27
+ // compare instead of five equality tests. Exact: matches `isSpace` with no
28
+ // false positives.
29
+ // @ts-expect-error: @lazy is a valid decorator
30
+ @lazy const SPLAT_WS_LO = i16x8.splat(0x09);
31
+ // @ts-expect-error: @lazy is a valid decorator
32
+ @lazy const SPLAT_WS_SPAN = i16x8.splat(0x04);
33
+ // @ts-expect-error: @lazy is a valid decorator
34
+ @lazy const SPLAT_BRACKET_LEFT = i16x8.splat(<i16>BRACKET_LEFT);
35
+ // Clears bit 5 (0x20), folding `{`/`}` onto `[`/`]` so one pair of compares
36
+ // matches either bracket flavor. No ASCII char besides `[{`/`]}` folds onto
37
+ // `[`/`]`, so the structural mask stays exact.
38
+ // @ts-expect-error: @lazy is a valid decorator
39
+ @lazy const SPLAT_BRACKET_FOLD = i16x8.splat(<i16>0xffdf);
40
+
41
+ function quoteOrBackslashMask(block: v128): i32 {
42
+ return i16x8.bitmask(
43
+ v128.or(i16x8.eq(block, SPLAT_QUOTE), i16x8.eq(block, SPLAT_BACK_SLASH)),
44
+ );
45
+ }
46
+
47
+ // Lanes equal to `"`, `{`, `}`, `[`, or `]` - the only bytes that, outside a
48
+ // string, change depth or open a string. Everything else (digits, `:`, `,`,
49
+ // whitespace, true/false/null) can be bulk-skipped between them.
50
+ function structuralOrQuoteMask(block: v128): i32 {
51
+ const folded = v128.and(block, SPLAT_BRACKET_FOLD);
52
+ const brackets = v128.or(
53
+ i16x8.eq(folded, SPLAT_BRACKET_LEFT),
54
+ i16x8.eq(folded, SPLAT_BRACKET_RIGHT),
55
+ );
56
+ return i16x8.bitmask(v128.or(brackets, i16x8.eq(block, SPLAT_QUOTE)));
57
+ }
58
+
59
+ function scalarTerminatorMask(block: v128): i32 {
60
+ const structural = v128.or(
61
+ v128.or(i16x8.eq(block, SPLAT_COMMA), i16x8.eq(block, SPLAT_BRACE_RIGHT)),
62
+ i16x8.eq(block, SPLAT_BRACKET_RIGHT),
63
+ );
64
+ // (c - 9) u<= 4 covers tab/LF/VT/FF/CR; space handled separately.
65
+ const whitespace = v128.or(
66
+ i16x8.le_u(i16x8.sub(block, SPLAT_WS_LO), SPLAT_WS_SPAN),
67
+ i16x8.eq(block, SPLAT_SPACE),
68
+ );
69
+ return i16x8.bitmask(v128.or(structural, whitespace));
70
+ }
71
+
72
+ function scanQuotedValueEnd_SIMD(srcStart: usize, srcEnd: usize): usize {
73
+ srcStart += 2;
74
+ const srcEnd16 = srcEnd >= 16 ? srcEnd - 16 : 0;
75
+
76
+ while (srcStart <= srcEnd16) {
77
+ const block = load<v128>(srcStart);
78
+ const mask = quoteOrBackslashMask(block);
79
+ if (mask == 0) {
80
+ srcStart += 16;
81
+ continue;
82
+ }
83
+
84
+ const laneIdx = usize(ctz(mask) << 1);
85
+ const srcIdx = srcStart + laneIdx;
86
+ const code = load<u16>(srcIdx);
87
+ if (code == QUOTE) return srcIdx + 2;
88
+ if (srcIdx + 2 >= srcEnd) return 0;
89
+ srcStart = srcIdx + 4;
90
+ }
91
+
92
+ while (srcStart < srcEnd) {
93
+ const code = load<u16>(srcStart);
94
+ if (code == QUOTE) return srcStart + 2;
95
+ if (code == BACK_SLASH) {
96
+ if (srcStart + 2 >= srcEnd) return 0;
97
+ srcStart += 4;
98
+ continue;
99
+ }
100
+ srcStart += 2;
101
+ }
102
+
103
+ return 0;
104
+ }
105
+
106
+ function scanCompositeValueEnd_SIMD(srcStart: usize, srcEnd: usize): usize {
107
+ // Process structural tokens scalar-side (cheap, and token-dense regions stay
108
+ // in a tight loop), but bulk-skip the bytes between them: nested string VALUES
109
+ // via the vectorized quoted scan (URLs, base64, prose), and runs of digits /
110
+ // punctuation / whitespace (numeric arrays like coordinate lists) via a
111
+ // vectorized hunt for the next `"`/`{`/`}`/`[`/`]`.
112
+ let depth: i32 = 1;
113
+ let ptr = srcStart + 2;
114
+ const srcEnd16 = srcEnd >= 16 ? srcEnd - 16 : 0;
115
+ while (ptr < srcEnd) {
116
+ const code = load<u16>(ptr);
117
+ if (code == QUOTE) {
118
+ ptr = scanQuotedValueEnd_SIMD(ptr, srcEnd);
119
+ if (!ptr) return 0;
120
+ continue;
121
+ }
122
+ const folded = code & 0xffdf;
123
+ if (folded == BRACKET_LEFT) {
124
+ // `[` or `{`
125
+ depth++;
126
+ ptr += 2;
127
+ continue;
128
+ }
129
+ if (folded == BRACKET_RIGHT) {
130
+ // `]` or `}`
131
+ if (--depth == 0) return ptr + 2;
132
+ ptr += 2;
133
+ continue;
134
+ }
135
+ ptr += 2;
136
+ // `,` and `:` sit one byte from the next token, so vectorizing them only
137
+ // adds SIMD setup on string-dense objects - stay scalar. Other fillers
138
+ // (number digits, whitespace, true/false/null) can run long; vectorize past
139
+ // them to the next `"`/`{`/`}`/`[`/`]`.
140
+ if (code == COMMA || code == COLON) continue;
141
+ while (ptr <= srcEnd16) {
142
+ const mask = structuralOrQuoteMask(load<v128>(ptr));
143
+ if (mask == 0) {
144
+ ptr += 16;
145
+ continue;
146
+ }
147
+ ptr += usize(ctz(mask) << 1);
148
+ break;
149
+ }
150
+ }
151
+ return 0;
152
+ }
153
+
154
+ function scanScalarValueEnd_SIMD(srcStart: usize, srcEnd: usize): usize {
155
+ const srcEnd16 = srcEnd >= 16 ? srcEnd - 16 : 0;
156
+ while (srcStart <= srcEnd16) {
157
+ const mask = scalarTerminatorMask(load<v128>(srcStart));
158
+ if (mask == 0) {
159
+ srcStart += 16;
160
+ continue;
161
+ }
162
+ return srcStart + usize(ctz(mask) << 1);
163
+ }
164
+
165
+ while (srcStart < srcEnd) {
166
+ const code = load<u16>(srcStart);
167
+ if (
168
+ code == COMMA ||
169
+ code == BRACKET_RIGHT ||
170
+ code == BRACE_RIGHT ||
171
+ isSpace(code)
172
+ )
173
+ return srcStart;
174
+ srcStart += 2;
175
+ }
176
+
177
+ return srcStart;
178
+ }
179
+
180
+ export function scanValueEnd_SIMD<T>(srcStart: usize, srcEnd: usize): usize {
181
+ if (srcStart >= srcEnd) return 0;
182
+ const first = load<u16>(srcStart);
183
+
184
+ if (isString<nonnull<T>>() && first == QUOTE)
185
+ return scanQuotedValueEnd_SIMD(srcStart, srcEnd);
186
+ if (isArray<nonnull<T>>() && first == BRACKET_LEFT)
187
+ return scanCompositeValueEnd_SIMD(srcStart, srcEnd);
188
+ if (
189
+ (isManaged<nonnull<T>>() || isReference<nonnull<T>>()) &&
190
+ first == BRACE_LEFT
191
+ )
192
+ return scanCompositeValueEnd_SIMD(srcStart, srcEnd);
193
+
194
+ if (first == QUOTE) return scanQuotedValueEnd_SIMD(srcStart, srcEnd);
195
+ if (first == BRACE_LEFT || first == BRACKET_LEFT)
196
+ return scanCompositeValueEnd_SIMD(srcStart, srcEnd);
197
+ return scanScalarValueEnd_SIMD(srcStart, srcEnd);
198
+ }
@@ -0,0 +1,184 @@
1
+ import {
2
+ BACK_SLASH,
3
+ BRACE_LEFT,
4
+ BRACE_RIGHT,
5
+ BRACKET_LEFT,
6
+ BRACKET_RIGHT,
7
+ COLON,
8
+ COMMA,
9
+ QUOTE,
10
+ } from "../custom/chars";
11
+ import { isSpace } from "./isSpace";
12
+
13
+ // SWAR analogue of `scanValueEndSimd.ts`, processing four UTF-16 lanes per
14
+ // 64-bit word for the SWAR build mode (no SIMD feature). Each mask is a fast
15
+ // FILTER - a matched lane is re-checked with a real `load<u16>` before acting -
16
+ // so the masks may over-match non-ASCII lanes whose low byte equals a target
17
+ // (the verify rejects them). Lane byte offset within a hit word is
18
+ // `ctz(mask) >> 3` (detection bit sits at lane*16 + 7).
19
+
20
+ const ONES: u64 = 0x0001_0001_0001_0001;
21
+ const HI: u64 = 0x0080_0080_0080_0080;
22
+
23
+ // 16-bit-lane "equals" partials (pre-`& HI`); OR several, then `& HI` once.
24
+ function eqPart(block: u64, splat: u64): u64 {
25
+ const t = block ^ splat;
26
+ return (t - ONES) & ~t;
27
+ }
28
+
29
+ const S_QUOTE: u64 = 0x0022_0022_0022_0022;
30
+ const S_BACK_SLASH: u64 = 0x005c_005c_005c_005c;
31
+ const S_BRACKET_LEFT: u64 = 0x005b_005b_005b_005b;
32
+ const S_BRACKET_RIGHT: u64 = 0x005d_005d_005d_005d;
33
+ // Clears bit 5 (0x20) of each lane, folding `{`/`}` onto `[`/`]`.
34
+ const FOLD: u64 = 0xffdf_ffdf_ffdf_ffdf;
35
+
36
+ function quoteOrBackslashMask(block: u64): u64 {
37
+ return (eqPart(block, S_QUOTE) | eqPart(block, S_BACK_SLASH)) & HI;
38
+ }
39
+
40
+ // Filter for lanes equal to `"`, `{`, `}`, `[`, or `]` - the only bytes that,
41
+ // outside a string, change depth or open a string. As with the other SWAR
42
+ // masks, a hit is a candidate to verify with a real load (it may over-match a
43
+ // non-ASCII lane whose low byte collides).
44
+ function structuralOrQuoteMask(block: u64): u64 {
45
+ const folded = block & FOLD;
46
+ return (
47
+ (eqPart(folded, S_BRACKET_LEFT) |
48
+ eqPart(folded, S_BRACKET_RIGHT) |
49
+ eqPart(block, S_QUOTE)) &
50
+ HI
51
+ );
52
+ }
53
+
54
+ function scanQuotedValueEnd_SWAR(srcStart: usize, srcEnd: usize): usize {
55
+ srcStart += 2;
56
+ const srcEnd8 = srcEnd >= 8 ? srcEnd - 8 : 0;
57
+
58
+ // Fast-skip 8-byte windows until a real quote (return) or a backslash, then
59
+ // hand off to the precise scalar tail (which resolves escape runs). The mask
60
+ // is a filter, so each candidate lane is verified with a real `load<u16>`;
61
+ // non-ASCII lanes that spuriously match are skipped (neither quote nor
62
+ // backslash), and `srcStart` is left at the window start for the tail.
63
+ while (srcStart <= srcEnd8) {
64
+ let mask = quoteOrBackslashMask(load<u64>(srcStart));
65
+ if (mask == 0) {
66
+ srcStart += 8;
67
+ continue;
68
+ }
69
+ do {
70
+ const srcIdx = srcStart + (usize(ctz(mask)) >> 3);
71
+ mask &= mask - 1;
72
+ const code = load<u16>(srcIdx);
73
+ if (code == QUOTE) return srcIdx + 2;
74
+ if (code == BACK_SLASH) break;
75
+ } while (mask != 0);
76
+ break;
77
+ }
78
+
79
+ // Resolve escapes by consuming a backslash *and the char it escapes* together,
80
+ // so escape parity is tracked exactly. A look-back `prev != BACK_SLASH` test is
81
+ // wrong for an escaped backslash: in `"x\\"` the closing quote follows a `\`
82
+ // (the second of the pair) yet still closes the string.
83
+ while (srcStart < srcEnd) {
84
+ const code = load<u16>(srcStart);
85
+ if (code == BACK_SLASH) {
86
+ srcStart += 4;
87
+ continue;
88
+ }
89
+ if (code == QUOTE) return srcStart + 2;
90
+ srcStart += 2;
91
+ }
92
+ return 0;
93
+ }
94
+
95
+ function scanCompositeValueEnd_SWAR(srcStart: usize, srcEnd: usize): usize {
96
+ // Process structural tokens scalar-side, but bulk-skip the bytes between them:
97
+ // nested string VALUES via the SWAR quoted scan, and runs of digits /
98
+ // punctuation / whitespace (numeric arrays) via a SWAR hunt for the next
99
+ // `"`/`{`/`}`/`[`/`]`.
100
+ let depth: i32 = 1;
101
+ let ptr = srcStart + 2;
102
+ const srcEnd8 = srcEnd >= 8 ? srcEnd - 8 : 0;
103
+ while (ptr < srcEnd) {
104
+ const code = load<u16>(ptr);
105
+ if (code == QUOTE) {
106
+ ptr = scanQuotedValueEnd_SWAR(ptr, srcEnd);
107
+ if (!ptr) return 0;
108
+ continue;
109
+ }
110
+ const folded = code & 0xffdf;
111
+ if (folded == BRACKET_LEFT) {
112
+ depth++;
113
+ ptr += 2;
114
+ continue;
115
+ }
116
+ if (folded == BRACKET_RIGHT) {
117
+ if (--depth == 0) return ptr + 2;
118
+ ptr += 2;
119
+ continue;
120
+ }
121
+ ptr += 2;
122
+ // `,`/`:` sit one byte from the next token - stay scalar (string-dense
123
+ // objects); other fillers can run long, so SWAR-skip past them.
124
+ if (code == COMMA || code == COLON) continue;
125
+ while (ptr <= srcEnd8) {
126
+ const mask = structuralOrQuoteMask(load<u64>(ptr));
127
+ if (mask == 0) {
128
+ ptr += 8;
129
+ continue;
130
+ }
131
+ const idx = ptr + (usize(ctz(mask)) >> 3);
132
+ const c = load<u16>(idx);
133
+ const f = c & 0xffdf;
134
+ if (c == QUOTE || f == BRACKET_LEFT || f == BRACKET_RIGHT) {
135
+ ptr = idx; // real token - the outer loop processes it
136
+ break;
137
+ }
138
+ ptr = idx + 2; // spurious lane match - keep scanning
139
+ }
140
+ }
141
+ return 0;
142
+ }
143
+
144
+ function scanScalarValueEnd_SWAR(srcStart: usize, srcEnd: usize): usize {
145
+ // Scalars (number/true/false/null) are short, so a plain scalar terminator
146
+ // scan beats setting up SWAR masks per word.
147
+ while (srcStart < srcEnd) {
148
+ const code = load<u16>(srcStart);
149
+ if (
150
+ code == COMMA ||
151
+ code == BRACKET_RIGHT ||
152
+ code == BRACE_RIGHT ||
153
+ isSpace(code)
154
+ )
155
+ return srcStart;
156
+ srcStart += 2;
157
+ }
158
+ return srcStart;
159
+ }
160
+
161
+ /**
162
+ * SWAR `scanValueEnd`: position just past the value at `srcStart`. Strings and
163
+ * objects/arrays use the SWAR token scans above; scalars use a short scalar
164
+ * loop. Returns 0 on empty input or an unterminated string/composite.
165
+ */
166
+ export function scanValueEnd_SWAR<T>(srcStart: usize, srcEnd: usize): usize {
167
+ if (srcStart >= srcEnd) return 0;
168
+ const first = load<u16>(srcStart);
169
+
170
+ if (isString<nonnull<T>>() && first == QUOTE)
171
+ return scanQuotedValueEnd_SWAR(srcStart, srcEnd);
172
+ if (isArray<nonnull<T>>() && first == BRACKET_LEFT)
173
+ return scanCompositeValueEnd_SWAR(srcStart, srcEnd);
174
+ if (
175
+ (isManaged<nonnull<T>>() || isReference<nonnull<T>>()) &&
176
+ first == BRACE_LEFT
177
+ )
178
+ return scanCompositeValueEnd_SWAR(srcStart, srcEnd);
179
+
180
+ if (first == QUOTE) return scanQuotedValueEnd_SWAR(srcStart, srcEnd);
181
+ if (first == BRACE_LEFT || first == BRACKET_LEFT)
182
+ return scanCompositeValueEnd_SWAR(srcStart, srcEnd);
183
+ return scanScalarValueEnd_SWAR(srcStart, srcEnd);
184
+ }
@@ -10,7 +10,7 @@
10
10
  // `scientific` directly skips both costs.
11
11
  //
12
12
  // scientific() is correctly rounded for all u64 mantissas and decimal
13
- // exponents that fit in IEEE-754 f64's range including the [2^53, 2^64)
13
+ // exponents that fit in IEEE-754 f64's range - including the [2^53, 2^64)
14
14
  // mantissa range that breaks Lemire's single-fmul fast path.
15
15
 
16
16
  const POWERS10: usize = memory.data<f64>([
@@ -25,13 +25,11 @@ const POWERS5: usize = memory.data<i32>([
25
25
  244140625, 1220703125,
26
26
  ]);
27
27
 
28
- // @ts-ignore: inline
29
- @inline function pow10(n: i32): f64 {
28
+ function pow10(n: i32): f64 {
30
29
  return load<f64>(POWERS10 + ((<usize>n) << alignof<f64>()));
31
30
  }
32
31
 
33
- // @ts-ignore: inline
34
- @inline function pow5_32(n: i32): i32 {
32
+ function pow5_32(n: i32): i32 {
35
33
  return load<i32>(POWERS5 + ((<usize>n) << alignof<i32>()));
36
34
  }
37
35
 
@@ -40,8 +38,7 @@ const POWERS5: usize = memory.data<i32>([
40
38
  // @ts-ignore: lazy decorator
41
39
  @lazy let __fixmulShift: u64 = 0;
42
40
 
43
- // @ts-ignore: inline
44
- @inline function fixmul(a: u64, b: u32): u64 {
41
+ function fixmul(a: u64, b: u32): u64 {
45
42
  const low = (a & 0xffffffff) * b;
46
43
  const high = (a >> 32) * b + (low >> 32);
47
44
  const overflow = <u32>(high >> 32);
@@ -54,8 +51,7 @@ const POWERS5: usize = memory.data<i32>([
54
51
  );
55
52
  }
56
53
 
57
- // @ts-ignore: inline
58
- @inline function scaledown(significand: u64, exp: i32): f64 {
54
+ function scaledown(significand: u64, exp: i32): f64 {
59
55
  const denom: u64 = 6103515625; // 1e14 * 0x1p-14
60
56
  const scale = reinterpret<f64>(0x3f06849b86a12b9b); // 1e-14 * 0x1p32
61
57
 
@@ -82,8 +78,7 @@ const POWERS5: usize = memory.data<i32>([
82
78
  return NativeMath.scalbn(<f64>significand, <i32>shift);
83
79
  }
84
80
 
85
- // @ts-ignore: inline
86
- @inline function scaleup(significand: u64, exp: i32): f64 {
81
+ function scaleup(significand: u64, exp: i32): f64 {
87
82
  const coeff: u32 = 1220703125; // 1e13 * 0x1p-13;
88
83
  let shift = ctz(significand);
89
84
  significand >>= shift;
@@ -100,7 +95,7 @@ const POWERS5: usize = memory.data<i32>([
100
95
 
101
96
  /**
102
97
  * Construct an f64 from a u64 mantissa and decimal exponent. Result is
103
- * correctly rounded bit-identical to `f64.parse` for any input the SWAR
98
+ * correctly rounded - bit-identical to `f64.parse` for any input the SWAR
104
99
  * float deserializer can pre-parse into this form.
105
100
  *
106
101
  * Caller guarantees the digit run that produced `significand` was already
@@ -111,8 +106,7 @@ const POWERS5: usize = memory.data<i32>([
111
106
  * @param exp Decimal exponent (e.g. for "12.34" pass 1234 and -2)
112
107
  * @returns The correctly rounded f64, or 0 / Infinity at the extremes.
113
108
  */
114
- // @ts-ignore: inline
115
- @inline export function scientific(significand: u64, exp: i32): f64 {
109
+ export function scientific(significand: u64, exp: i32): f64 {
116
110
  if (!significand || exp < -342) return 0;
117
111
  if (exp > 308) return Infinity;
118
112
  let significandf = <f64>significand;