json-as 1.3.8 → 1.4.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 (84) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/README.md +49 -1
  3. package/assembly/deserialize/index/arbitrary.ts +2 -2
  4. package/assembly/deserialize/naive/array/arbitrary.ts +3 -136
  5. package/assembly/deserialize/naive/array/array.ts +30 -1
  6. package/assembly/deserialize/naive/array/integer.ts +1 -6
  7. package/assembly/deserialize/naive/array/map.ts +10 -14
  8. package/assembly/deserialize/naive/array/object.ts +10 -14
  9. package/assembly/deserialize/naive/float.ts +2 -4
  10. package/assembly/deserialize/naive/integer.ts +1 -2
  11. package/assembly/deserialize/naive/map.ts +40 -202
  12. package/assembly/deserialize/naive/object.ts +153 -174
  13. package/assembly/deserialize/naive/set.ts +1 -2
  14. package/assembly/deserialize/naive/staticarray.ts +1 -2
  15. package/assembly/deserialize/naive/string.ts +65 -18
  16. package/assembly/deserialize/naive/typedarray.ts +1 -2
  17. package/assembly/deserialize/naive/unsigned.ts +1 -2
  18. package/assembly/deserialize/simd/array/integer.ts +3 -6
  19. package/assembly/deserialize/simd/float.ts +2 -7
  20. package/assembly/deserialize/simd/integer.ts +4 -8
  21. package/assembly/deserialize/simd/string.ts +16 -21
  22. package/assembly/deserialize/swar/array/array.ts +1 -2
  23. package/assembly/deserialize/swar/array/bool.ts +1 -2
  24. package/assembly/deserialize/swar/array/float.ts +2 -3
  25. package/assembly/deserialize/swar/array/generic.ts +1 -2
  26. package/assembly/deserialize/swar/array/integer.ts +6 -11
  27. package/assembly/deserialize/swar/array/object.ts +1 -2
  28. package/assembly/deserialize/swar/array/shared.ts +3 -8
  29. package/assembly/deserialize/swar/array/string.ts +1 -2
  30. package/assembly/deserialize/swar/array/struct.ts +1 -1
  31. package/assembly/deserialize/swar/float.ts +3 -8
  32. package/assembly/deserialize/swar/integer.ts +4 -8
  33. package/assembly/deserialize/swar/string.ts +29 -41
  34. package/assembly/index.d.ts +248 -15
  35. package/assembly/index.ts +468 -146
  36. package/assembly/serialize/index/object.ts +18 -15
  37. package/assembly/serialize/naive/string.ts +9 -2
  38. package/assembly/serialize/swar/string.ts +1 -2
  39. package/assembly/util/atoi.ts +1 -2
  40. package/assembly/util/dragonbox.ts +0 -8
  41. package/assembly/util/itoa-fast.ts +3 -6
  42. package/assembly/util/parsefloat-fast.ts +1 -2
  43. package/assembly/util/scanValueEnd.ts +1 -2
  44. package/assembly/util/scanValueEndSimd.ts +160 -0
  45. package/assembly/util/scanValueEndSwar.ts +142 -0
  46. package/assembly/util/scientific.ts +3 -6
  47. package/assembly/util/simd-int.ts +4 -8
  48. package/assembly/util/snp.ts +1 -5
  49. package/assembly/util/stringScan.ts +2 -4
  50. package/assembly/util/swar-int.ts +3 -6
  51. package/lib/as-bs.ts +37 -0
  52. package/package.json +14 -4
  53. package/transform/lib/builder.d.ts +0 -1
  54. package/transform/lib/builder.js +0 -1
  55. package/transform/lib/index.d.ts +0 -1
  56. package/transform/lib/index.js +537 -290
  57. package/transform/lib/linkers/alias.d.ts +0 -1
  58. package/transform/lib/linkers/alias.js +0 -1
  59. package/transform/lib/linkers/custom.d.ts +0 -1
  60. package/transform/lib/linkers/custom.js +0 -1
  61. package/transform/lib/linkers/imports.d.ts +0 -1
  62. package/transform/lib/linkers/imports.js +0 -1
  63. package/transform/lib/types.d.ts +3 -2
  64. package/transform/lib/types.js +2 -1
  65. package/transform/lib/util.d.ts +0 -1
  66. package/transform/lib/util.js +0 -1
  67. package/transform/lib/visitor.d.ts +0 -1
  68. package/transform/lib/visitor.js +0 -1
  69. package/transform/lib/builder.d.ts.map +0 -1
  70. package/transform/lib/builder.js.map +0 -1
  71. package/transform/lib/index.d.ts.map +0 -1
  72. package/transform/lib/index.js.map +0 -1
  73. package/transform/lib/linkers/alias.d.ts.map +0 -1
  74. package/transform/lib/linkers/alias.js.map +0 -1
  75. package/transform/lib/linkers/custom.d.ts.map +0 -1
  76. package/transform/lib/linkers/custom.js.map +0 -1
  77. package/transform/lib/linkers/imports.d.ts.map +0 -1
  78. package/transform/lib/linkers/imports.js.map +0 -1
  79. package/transform/lib/types.d.ts.map +0 -1
  80. package/transform/lib/types.js.map +0 -1
  81. package/transform/lib/util.d.ts.map +0 -1
  82. package/transform/lib/util.js.map +0 -1
  83. package/transform/lib/visitor.d.ts.map +0 -1
  84. package/transform/lib/visitor.js.map +0 -1
@@ -2,11 +2,10 @@ import { bs } from "../../../lib/as-bs";
2
2
  import { JSON } from "../..";
3
3
  import { BRACE_LEFT, BRACE_RIGHT, COLON, COMMA } from "../../custom/chars";
4
4
  import { serializeArbitrary } from "./arbitrary";
5
- import { serializeString } from "./string";
5
+ import { serializeStringRange } from "../naive/string";
6
6
 
7
7
  export function serializeObject(src: JSON.Obj): void {
8
8
  const srcSize = src.size;
9
- const srcEnd = srcSize - 1;
10
9
 
11
10
  if (srcSize == 0) {
12
11
  bs.proposeSize(4);
@@ -15,28 +14,32 @@ export function serializeObject(src: JSON.Obj): void {
15
14
  return;
16
15
  }
17
16
 
18
- const keys = src.keys();
19
- const values = src.values();
17
+ const vals = src._vals;
18
+ const kbuf = changetype<usize>(src._kbuf);
19
+ const kused = src._kused;
20
20
 
21
21
  bs.proposeSize(4 + <u32>(srcSize - 1) * 2 + <u32>srcSize * 2);
22
22
  store<u16>(bs.offset, BRACE_LEFT);
23
23
  bs.offset += 2;
24
24
 
25
- for (let i = 0; i < srcEnd; i++) {
26
- serializeString(unchecked(keys[i]));
25
+ // Walk the length-prefixed key buffer in lockstep with the values, emitting
26
+ // each key straight from its slice (no per-key string materialization).
27
+ let pos = 0;
28
+ let i = 0;
29
+ while (pos < kused) {
30
+ const len = <i32>load<u16>(kbuf + ((<usize>pos) << 1));
31
+ if (i != 0) {
32
+ store<u16>(bs.offset, COMMA);
33
+ bs.offset += 2;
34
+ }
35
+ serializeStringRange(kbuf + ((<usize>(pos + 1)) << 1), (<usize>len) << 1);
27
36
  store<u16>(bs.offset, COLON);
28
37
  bs.offset += 2;
29
-
30
- serializeArbitrary(unchecked(values[i]));
31
- store<u16>(bs.offset, COMMA);
32
- bs.offset += 2;
38
+ serializeArbitrary(unchecked(vals[i]));
39
+ pos += 1 + len;
40
+ i++;
33
41
  }
34
42
 
35
- serializeString(unchecked(keys[srcEnd]));
36
- store<u16>(bs.offset, COLON);
37
- bs.offset += 2;
38
- serializeArbitrary(unchecked(values[srcEnd]));
39
-
40
43
  store<u16>(bs.offset, BRACE_RIGHT);
41
44
  bs.offset += 2;
42
45
  }
@@ -17,9 +17,16 @@ import { serializeStruct } from "./struct";
17
17
  */
18
18
  // @ts-ignore: inline
19
19
  @inline export function serializeString_NAIVE(src: string): void {
20
- const srcSize = bytes(src);
20
+ serializeStringRange(changetype<usize>(src), bytes(src));
21
+ }
22
+
23
+ /**
24
+ * Serializes a raw UTF-16 range as a quoted, escaped JSON string. Lets callers
25
+ * (e.g. JSON.Obj key serialization) emit a string straight from a buffer slice
26
+ * without first materializing a heap `string`.
27
+ */
28
+ export function serializeStringRange(srcPtr: usize, srcSize: usize): void {
21
29
  bs.proposeSize(srcSize + 4);
22
- let srcPtr = changetype<usize>(src);
23
30
  const srcEnd = srcPtr + srcSize;
24
31
 
25
32
  store<u16>(bs.offset, QUOTE);
@@ -182,8 +182,7 @@ export function serializeString_SWAR(src: string): void {
182
182
  bs.offset += 12;
183
183
  }
184
184
 
185
- // @ts-expect-error: @inline is a valid decorator
186
- @inline export function detect_escapable_u64_swar_safe(block: u64): u64 {
185
+ export function detect_escapable_u64_swar_safe(block: u64): u64 {
187
186
  const hi = block & 0xff00_ff00_ff00_ff00;
188
187
  const lo = block & 0x00ff_00ff_00ff_00ff;
189
188
  // Setting bit 8 of each 16-bit lane (high byte LSB) prevents borrow from a
@@ -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>()) {
@@ -118,8 +118,6 @@ function computeRoundUpShorter32(cache: u64, beta: i32): u32 {
118
118
  return (<u32>(cache >>> (39 - beta)) + 1) >>> 1;
119
119
  }
120
120
 
121
-
122
- @inline
123
121
  function removeTrailingZeros32(significand: u32): u32 {
124
122
  let exponent = 0;
125
123
  let r = rotr32(significand * 184254097, 4);
@@ -161,8 +159,6 @@ function checkDivisibilityAndDivideByPow10_32_1(n: u32): u64 {
161
159
  return ((<u64>(prod >>> 16)) << 32) | ((prod & 0xffff) < 6554 ? 1 : 0);
162
160
  }
163
161
 
164
-
165
- @inline
166
162
  function computeMul64(u: u64, cacheHigh: u64, cacheLow: u64): void {
167
163
  const high = umul128_upper64(u, cacheHigh);
168
164
 
@@ -187,8 +183,6 @@ function computeMul64(u: u64, cacheHigh: u64, cacheLow: u64): void {
187
183
  _dbMulIsInteger = rLow == 0;
188
184
  }
189
185
 
190
-
191
- @inline
192
186
  function computeMulParity64(
193
187
  twoF: u64,
194
188
  cacheHigh: u64,
@@ -233,8 +227,6 @@ function computeRoundUpShorter64(cacheHigh: u64, beta: i32): u64 {
233
227
  return ((cacheHigh >>> (10 - beta)) + 1) >>> 1;
234
228
  }
235
229
 
236
-
237
- @inline
238
230
  function removeTrailingZeros64(significand: u64): u64 {
239
231
  let exponent = 0;
240
232
  let r = rotr64(significand * 28999941890838049, 8);
@@ -59,8 +59,7 @@ function initPairs(): void {
59
59
  * a byte offset). Caller must ensure the buffer has at least 20 bytes
60
60
  * available (max 10 chars).
61
61
  */
62
- // @ts-expect-error: @inline is a valid decorator
63
- @inline export function itoaU32(buf: usize, v: u32): u32 {
62
+ export function itoaU32(buf: usize, v: u32): u32 {
64
63
  if (v < 10) {
65
64
  store<u16>(buf, <u16>(v + 0x30));
66
65
  return 1;
@@ -165,8 +164,7 @@ function initPairs(): void {
165
164
  * Writes a u32 in the range 0..99_999_999 as exactly 8 UTF-16 chars with
166
165
  * leading zeros. Used by the u64 path to emit trailing groups of 8 digits.
167
166
  */
168
- // @ts-expect-error: @inline is a valid decorator
169
- @inline function writeU32Padded8(buf: usize, v: u32): void {
167
+ function writeU32Padded8(buf: usize, v: u32): void {
170
168
  const a = v / 1_000_000;
171
169
  let rest = v - a * 1_000_000;
172
170
  const b = rest / 10_000;
@@ -187,8 +185,7 @@ function initPairs(): void {
187
185
  * For 17+ digit values (which still fit in u64 < 1.8e19), repeat.
188
186
  * Caller must ensure the buffer has at least 40 bytes available.
189
187
  */
190
- // @ts-expect-error: @inline is a valid decorator
191
- @inline export function itoaU64(buf: usize, v: u64): u32 {
188
+ export function itoaU64(buf: usize, v: u64): u32 {
192
189
  if (v <= <u64>u32.MAX_VALUE) {
193
190
  return itoaU32(buf, <u32>v);
194
191
  }
@@ -64,8 +64,7 @@ const MAX_EXACT_MANTISSA: u64 = 1 << 53;
64
64
  * exact through accumulation and only loses precision at the final
65
65
  * `<f64>` cast.
66
66
  */
67
- // @ts-expect-error: @inline is a valid decorator
68
- @inline export function parseFloatFast<T>(srcStart: usize, srcEnd: usize): T {
67
+ export function parseFloatFast<T>(srcStart: usize, srcEnd: usize): T {
69
68
  const origStart = srcStart;
70
69
  let p = srcStart;
71
70
  let negative = false;
@@ -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,160 @@
1
+ import {
2
+ BACK_SLASH,
3
+ BRACE_LEFT,
4
+ BRACE_RIGHT,
5
+ BRACKET_LEFT,
6
+ BRACKET_RIGHT,
7
+ COMMA,
8
+ QUOTE,
9
+ } from "../custom/chars";
10
+ import { isSpace } from "./isSpace";
11
+
12
+ // @ts-expect-error: @lazy is a valid decorator
13
+ @lazy const SPLAT_QUOTE = i16x8.splat(<i16>QUOTE);
14
+ // @ts-expect-error: @lazy is a valid decorator
15
+ @lazy const SPLAT_BACK_SLASH = i16x8.splat(<i16>BACK_SLASH);
16
+ // @ts-expect-error: @lazy is a valid decorator
17
+ @lazy const SPLAT_BRACE_RIGHT = i16x8.splat(<i16>BRACE_RIGHT);
18
+ // @ts-expect-error: @lazy is a valid decorator
19
+ @lazy const SPLAT_BRACKET_RIGHT = i16x8.splat(<i16>BRACKET_RIGHT);
20
+ // @ts-expect-error: @lazy is a valid decorator
21
+ @lazy const SPLAT_COMMA = i16x8.splat(<i16>COMMA);
22
+ // @ts-expect-error: @lazy is a valid decorator
23
+ @lazy const SPLAT_SPACE = i16x8.splat(0x20);
24
+ // JSON whitespace besides space is the contiguous range 0x09..0x0d
25
+ // (tab/LF/VT/FF/CR), matched as `(c - 9) u<= 4` — one sub + one unsigned
26
+ // compare instead of five equality tests. Exact: matches `isSpace` with no
27
+ // false positives.
28
+ // @ts-expect-error: @lazy is a valid decorator
29
+ @lazy const SPLAT_WS_LO = i16x8.splat(0x09);
30
+ // @ts-expect-error: @lazy is a valid decorator
31
+ @lazy const SPLAT_WS_SPAN = i16x8.splat(0x04);
32
+
33
+ // @ts-expect-error: @inline is a valid decorator
34
+ @inline function quoteOrBackslashMask(block: v128): i32 {
35
+ return i16x8.bitmask(
36
+ v128.or(i16x8.eq(block, SPLAT_QUOTE), i16x8.eq(block, SPLAT_BACK_SLASH)),
37
+ );
38
+ }
39
+
40
+ // @ts-expect-error: @inline is a valid decorator
41
+ @inline function scalarTerminatorMask(block: v128): i32 {
42
+ const structural = v128.or(
43
+ v128.or(i16x8.eq(block, SPLAT_COMMA), i16x8.eq(block, SPLAT_BRACE_RIGHT)),
44
+ i16x8.eq(block, SPLAT_BRACKET_RIGHT),
45
+ );
46
+ // (c - 9) u<= 4 covers tab/LF/VT/FF/CR; space handled separately.
47
+ const whitespace = v128.or(
48
+ i16x8.le_u(i16x8.sub(block, SPLAT_WS_LO), SPLAT_WS_SPAN),
49
+ i16x8.eq(block, SPLAT_SPACE),
50
+ );
51
+ return i16x8.bitmask(v128.or(structural, whitespace));
52
+ }
53
+
54
+ function scanQuotedValueEnd_SIMD(srcStart: usize, srcEnd: usize): usize {
55
+ srcStart += 2;
56
+ const srcEnd16 = srcEnd >= 16 ? srcEnd - 16 : 0;
57
+
58
+ while (srcStart <= srcEnd16) {
59
+ const block = load<v128>(srcStart);
60
+ const mask = quoteOrBackslashMask(block);
61
+ if (mask == 0) {
62
+ srcStart += 16;
63
+ continue;
64
+ }
65
+
66
+ const laneIdx = usize(ctz(mask) << 1);
67
+ const srcIdx = srcStart + laneIdx;
68
+ const code = load<u16>(srcIdx);
69
+ if (code == QUOTE) return srcIdx + 2;
70
+ if (srcIdx + 2 >= srcEnd) return 0;
71
+ srcStart = srcIdx + 4;
72
+ }
73
+
74
+ while (srcStart < srcEnd) {
75
+ const code = load<u16>(srcStart);
76
+ if (code == QUOTE) return srcStart + 2;
77
+ if (code == BACK_SLASH) {
78
+ if (srcStart + 2 >= srcEnd) return 0;
79
+ srcStart += 4;
80
+ continue;
81
+ }
82
+ srcStart += 2;
83
+ }
84
+
85
+ return 0;
86
+ }
87
+
88
+ function scanCompositeValueEnd_SIMD(srcStart: usize, srcEnd: usize): usize {
89
+ // Track object/array depth scalar-side (structural tokens are sparse and a
90
+ // bulk token-mask scan loses to a tight loop on token-dense objects), but
91
+ // skip nested string VALUES with the vectorized quoted scan — that's where
92
+ // the long runs (URLs, base64, prose) actually are.
93
+ let depth: i32 = 1;
94
+ let ptr = srcStart + 2;
95
+ while (ptr < srcEnd) {
96
+ const code = load<u16>(ptr);
97
+ if (code == QUOTE) {
98
+ ptr = scanQuotedValueEnd_SIMD(ptr, srcEnd);
99
+ if (!ptr) return 0;
100
+ continue;
101
+ }
102
+ if (code == BRACE_LEFT || code == BRACKET_LEFT) {
103
+ depth++;
104
+ } else if (code == BRACE_RIGHT || code == BRACKET_RIGHT) {
105
+ if (--depth == 0) return ptr + 2;
106
+ }
107
+ ptr += 2;
108
+ }
109
+ return 0;
110
+ }
111
+
112
+ function scanScalarValueEnd_SIMD(srcStart: usize, srcEnd: usize): usize {
113
+ const srcEnd16 = srcEnd >= 16 ? srcEnd - 16 : 0;
114
+ while (srcStart <= srcEnd16) {
115
+ const mask = scalarTerminatorMask(load<v128>(srcStart));
116
+ if (mask == 0) {
117
+ srcStart += 16;
118
+ continue;
119
+ }
120
+ return srcStart + usize(ctz(mask) << 1);
121
+ }
122
+
123
+ while (srcStart < srcEnd) {
124
+ const code = load<u16>(srcStart);
125
+ if (
126
+ code == COMMA ||
127
+ code == BRACKET_RIGHT ||
128
+ code == BRACE_RIGHT ||
129
+ isSpace(code)
130
+ )
131
+ return srcStart;
132
+ srcStart += 2;
133
+ }
134
+
135
+ return srcStart;
136
+ }
137
+
138
+ // @ts-expect-error: @inline is a valid decorator
139
+ @inline export function scanValueEnd_SIMD<T>(
140
+ srcStart: usize,
141
+ srcEnd: usize,
142
+ ): usize {
143
+ if (srcStart >= srcEnd) return 0;
144
+ const first = load<u16>(srcStart);
145
+
146
+ if (isString<nonnull<T>>() && first == QUOTE)
147
+ return scanQuotedValueEnd_SIMD(srcStart, srcEnd);
148
+ if (isArray<nonnull<T>>() && first == BRACKET_LEFT)
149
+ return scanCompositeValueEnd_SIMD(srcStart, srcEnd);
150
+ if (
151
+ (isManaged<nonnull<T>>() || isReference<nonnull<T>>()) &&
152
+ first == BRACE_LEFT
153
+ )
154
+ return scanCompositeValueEnd_SIMD(srcStart, srcEnd);
155
+
156
+ if (first == QUOTE) return scanQuotedValueEnd_SIMD(srcStart, srcEnd);
157
+ if (first == BRACE_LEFT || first == BRACKET_LEFT)
158
+ return scanCompositeValueEnd_SIMD(srcStart, srcEnd);
159
+ return scanScalarValueEnd_SIMD(srcStart, srcEnd);
160
+ }
@@ -0,0 +1,142 @@
1
+ import {
2
+ BACK_SLASH,
3
+ BRACE_LEFT,
4
+ BRACE_RIGHT,
5
+ BRACKET_LEFT,
6
+ BRACKET_RIGHT,
7
+ COMMA,
8
+ QUOTE,
9
+ } from "../custom/chars";
10
+ import { isSpace } from "./isSpace";
11
+
12
+ // SWAR analogue of `scanValueEndSimd.ts`, processing four UTF-16 lanes per
13
+ // 64-bit word for the SWAR build mode (no SIMD feature). Each mask is a fast
14
+ // FILTER — a matched lane is re-checked with a real `load<u16>` before acting —
15
+ // so the masks may over-match non-ASCII lanes whose low byte equals a target
16
+ // (the verify rejects them). Lane byte offset within a hit word is
17
+ // `ctz(mask) >> 3` (detection bit sits at lane*16 + 7).
18
+
19
+ // @ts-expect-error: @inline is a valid decorator
20
+ @inline const ONES: u64 = 0x0001_0001_0001_0001;
21
+ // @ts-expect-error: @inline is a valid decorator
22
+ @inline const HI: u64 = 0x0080_0080_0080_0080;
23
+
24
+ // 16-bit-lane "equals" partials (pre-`& HI`); OR several, then `& HI` once.
25
+ // @ts-expect-error: @inline is a valid decorator
26
+ @inline function eqPart(block: u64, splat: u64): u64 {
27
+ const t = block ^ splat;
28
+ return (t - ONES) & ~t;
29
+ }
30
+
31
+ // @ts-expect-error: @inline is a valid decorator
32
+ @inline const S_QUOTE: u64 = 0x0022_0022_0022_0022;
33
+ // @ts-expect-error: @inline is a valid decorator
34
+ @inline const S_BACK_SLASH: u64 = 0x005c_005c_005c_005c;
35
+
36
+ // @ts-expect-error: @inline is a valid decorator
37
+ @inline function quoteOrBackslashMask(block: u64): u64 {
38
+ return (eqPart(block, S_QUOTE) | eqPart(block, S_BACK_SLASH)) & HI;
39
+ }
40
+
41
+ function scanQuotedValueEnd_SWAR(srcStart: usize, srcEnd: usize): usize {
42
+ srcStart += 2;
43
+ const srcEnd8 = srcEnd >= 8 ? srcEnd - 8 : 0;
44
+
45
+ // Fast-skip 8-byte windows until a real quote (return) or a backslash, then
46
+ // hand off to the precise scalar tail (which resolves escape runs). The mask
47
+ // is a filter, so each candidate lane is verified with a real `load<u16>`;
48
+ // non-ASCII lanes that spuriously match are skipped (neither quote nor
49
+ // backslash), and `srcStart` is left at the window start for the tail.
50
+ while (srcStart <= srcEnd8) {
51
+ let mask = quoteOrBackslashMask(load<u64>(srcStart));
52
+ if (mask == 0) {
53
+ srcStart += 8;
54
+ continue;
55
+ }
56
+ do {
57
+ const srcIdx = srcStart + (usize(ctz(mask)) >> 3);
58
+ mask &= mask - 1;
59
+ const code = load<u16>(srcIdx);
60
+ if (code == QUOTE) return srcIdx + 2;
61
+ if (code == BACK_SLASH) break;
62
+ } while (mask != 0);
63
+ break;
64
+ }
65
+
66
+ while (srcStart < srcEnd) {
67
+ const code = load<u16>(srcStart);
68
+ if (code == QUOTE && load<u16>(srcStart - 2) != BACK_SLASH)
69
+ return srcStart + 2;
70
+ srcStart += 2;
71
+ }
72
+ return 0;
73
+ }
74
+
75
+ function scanCompositeValueEnd_SWAR(srcStart: usize, srcEnd: usize): usize {
76
+ // Scalar depth tracking (structural tokens are sparse; a bulk token-mask scan
77
+ // loses to a tight loop on token-dense objects) with SWAR quoted-skip for
78
+ // nested string values — where the long runs are.
79
+ let depth: i32 = 1;
80
+ let ptr = srcStart + 2;
81
+ while (ptr < srcEnd) {
82
+ const code = load<u16>(ptr);
83
+ if (code == QUOTE) {
84
+ ptr = scanQuotedValueEnd_SWAR(ptr, srcEnd);
85
+ if (!ptr) return 0;
86
+ continue;
87
+ }
88
+ if (code == BRACE_LEFT || code == BRACKET_LEFT) {
89
+ depth++;
90
+ } else if (code == BRACE_RIGHT || code == BRACKET_RIGHT) {
91
+ if (--depth == 0) return ptr + 2;
92
+ }
93
+ ptr += 2;
94
+ }
95
+ return 0;
96
+ }
97
+
98
+ function scanScalarValueEnd_SWAR(srcStart: usize, srcEnd: usize): usize {
99
+ // Scalars (number/true/false/null) are short, so a plain scalar terminator
100
+ // scan beats setting up SWAR masks per word.
101
+ while (srcStart < srcEnd) {
102
+ const code = load<u16>(srcStart);
103
+ if (
104
+ code == COMMA ||
105
+ code == BRACKET_RIGHT ||
106
+ code == BRACE_RIGHT ||
107
+ isSpace(code)
108
+ )
109
+ return srcStart;
110
+ srcStart += 2;
111
+ }
112
+ return srcStart;
113
+ }
114
+
115
+ /**
116
+ * SWAR `scanValueEnd`: position just past the value at `srcStart`. Strings and
117
+ * objects/arrays use the SWAR token scans above; scalars use a short scalar
118
+ * loop. Returns 0 on empty input or an unterminated string/composite.
119
+ */
120
+ // @ts-expect-error: @inline is a valid decorator
121
+ @inline export function scanValueEnd_SWAR<T>(
122
+ srcStart: usize,
123
+ srcEnd: usize,
124
+ ): usize {
125
+ if (srcStart >= srcEnd) return 0;
126
+ const first = load<u16>(srcStart);
127
+
128
+ if (isString<nonnull<T>>() && first == QUOTE)
129
+ return scanQuotedValueEnd_SWAR(srcStart, srcEnd);
130
+ if (isArray<nonnull<T>>() && first == BRACKET_LEFT)
131
+ return scanCompositeValueEnd_SWAR(srcStart, srcEnd);
132
+ if (
133
+ (isManaged<nonnull<T>>() || isReference<nonnull<T>>()) &&
134
+ first == BRACE_LEFT
135
+ )
136
+ return scanCompositeValueEnd_SWAR(srcStart, srcEnd);
137
+
138
+ if (first == QUOTE) return scanQuotedValueEnd_SWAR(srcStart, srcEnd);
139
+ if (first == BRACE_LEFT || first == BRACKET_LEFT)
140
+ return scanCompositeValueEnd_SWAR(srcStart, srcEnd);
141
+ return scanScalarValueEnd_SWAR(srcStart, srcEnd);
142
+ }
@@ -54,8 +54,7 @@ const POWERS5: usize = memory.data<i32>([
54
54
  );
55
55
  }
56
56
 
57
- // @ts-ignore: inline
58
- @inline function scaledown(significand: u64, exp: i32): f64 {
57
+ function scaledown(significand: u64, exp: i32): f64 {
59
58
  const denom: u64 = 6103515625; // 1e14 * 0x1p-14
60
59
  const scale = reinterpret<f64>(0x3f06849b86a12b9b); // 1e-14 * 0x1p32
61
60
 
@@ -82,8 +81,7 @@ const POWERS5: usize = memory.data<i32>([
82
81
  return NativeMath.scalbn(<f64>significand, <i32>shift);
83
82
  }
84
83
 
85
- // @ts-ignore: inline
86
- @inline function scaleup(significand: u64, exp: i32): f64 {
84
+ function scaleup(significand: u64, exp: i32): f64 {
87
85
  const coeff: u32 = 1220703125; // 1e13 * 0x1p-13;
88
86
  let shift = ctz(significand);
89
87
  significand >>= shift;
@@ -111,8 +109,7 @@ const POWERS5: usize = memory.data<i32>([
111
109
  * @param exp Decimal exponent (e.g. for "12.34" pass 1234 and -2)
112
110
  * @returns The correctly rounded f64, or 0 / Infinity at the extremes.
113
111
  */
114
- // @ts-ignore: inline
115
- @inline export function scientific(significand: u64, exp: i32): f64 {
112
+ export function scientific(significand: u64, exp: i32): f64 {
116
113
  if (!significand || exp < -342) return 0;
117
114
  if (exp > 308) return Infinity;
118
115
  let significandf = <f64>significand;
@@ -82,8 +82,7 @@
82
82
  * @param srcStart Pointer to 16 source bytes (8 UTF-16 chars).
83
83
  * @returns The parsed 8-digit value, or `U32.MAX_VALUE` on invalid input.
84
84
  */
85
- // @ts-expect-error: @inline is a valid decorator
86
- @inline export function parse8Digits_SIMD(srcStart: usize): u32 {
85
+ export function parse8Digits_SIMD(srcStart: usize): u32 {
87
86
  const block = load<v128>(srcStart);
88
87
  const digits = i16x8.sub(block, SPLAT_30);
89
88
  if (v128.any_true(i16x8.gt_u(digits, SPLAT_09))) return U32.MAX_VALUE;
@@ -104,8 +103,7 @@
104
103
  * @param srcStart Pointer to 16 source bytes (8 UTF-16 chars).
105
104
  * @returns The parsed 8-digit value.
106
105
  */
107
- // @ts-expect-error: @inline is a valid decorator
108
- @inline export function parse8Digits_SIMD_Unsafe(srcStart: usize): u32 {
106
+ export function parse8Digits_SIMD_Unsafe(srcStart: usize): u32 {
109
107
  const block = load<v128>(srcStart);
110
108
  const digits = i16x8.sub(block, SPLAT_30);
111
109
  const packed = i8x16.narrow_i16x8_u(digits, ZERO_I16X8);
@@ -132,8 +130,7 @@
132
130
  * @param srcStart Pointer to 32 source bytes (16 UTF-16 chars).
133
131
  * @returns The parsed 16-digit value, or `U64.MAX_VALUE` on invalid input.
134
132
  */
135
- // @ts-expect-error: @inline is a valid decorator
136
- @inline export function parse16Digits_SIMD(srcStart: usize): u64 {
133
+ export function parse16Digits_SIMD(srcStart: usize): u64 {
137
134
  const block0 = load<v128>(srcStart);
138
135
  const block1 = load<v128>(srcStart, 16);
139
136
 
@@ -168,8 +165,7 @@
168
165
  * @param srcStart Pointer to 32 source bytes (16 UTF-16 chars).
169
166
  * @returns The parsed 16-digit value.
170
167
  */
171
- // @ts-expect-error: @inline is a valid decorator
172
- @inline export function parse16Digits_SIMD_Unsafe(srcStart: usize): u64 {
168
+ export function parse16Digits_SIMD_Unsafe(srcStart: usize): u64 {
173
169
  const block0 = load<v128>(srcStart);
174
170
  const block1 = load<v128>(srcStart, 16);
175
171
  const digits0 = i16x8.sub(block0, SPLAT_30);
@@ -6,11 +6,7 @@
6
6
  import { POW_TEN_TABLE_32, POW_TEN_TABLE_64 } from "../globals/tables";
7
7
  import { atoi } from "./atoi";
8
8
 
9
- // @ts-ignore: Decorator valid here
10
- @inline export function snp<T extends number>(
11
- srcStart: usize,
12
- srcEnd: usize,
13
- ): T {
9
+ export function snp<T extends number>(srcStart: usize, srcEnd: usize): T {
14
10
  // @ts-ignore: type
15
11
  let val: T = 0;
16
12
  let char = load<u16>(srcStart) - 48;
@@ -1,7 +1,6 @@
1
1
  import { BACK_SLASH, QUOTE } from "../custom/chars";
2
2
 
3
- // @ts-ignore
4
- @inline export function isUnescapedQuote(ptr: usize): bool {
3
+ export function isUnescapedQuote(ptr: usize): bool {
5
4
  if (load<u16>(ptr) != QUOTE) return false;
6
5
 
7
6
  let escaped = false;
@@ -13,8 +12,7 @@ import { BACK_SLASH, QUOTE } from "../custom/chars";
13
12
  return !escaped;
14
13
  }
15
14
 
16
- // @ts-ignore
17
- @inline export function scanStringEnd(ptr: usize, end: usize): usize {
15
+ export function scanStringEnd(ptr: usize, end: usize): usize {
18
16
  ptr += 2;
19
17
  while (ptr < end) {
20
18
  if (load<u16>(ptr) == QUOTE && isUnescapedQuote(ptr)) return ptr;
@@ -97,8 +97,7 @@ const FINAL_4_MAGIC: u64 = 0x0000_0064_0000_0001;
97
97
  * @param hi The second `u64`, four UTF-16 code units.
98
98
  * @returns The parsed 8-digit value, or `U32.MAX_VALUE` on invalid input.
99
99
  */
100
- // @ts-expect-error: @inline is a valid decorator
101
- @inline export function parse8Digits_PairMul(lo: u64, hi: u64): u32 {
100
+ export function parse8Digits_PairMul(lo: u64, hi: u64): u32 {
102
101
  const loDigits = lo - ZERO_4;
103
102
  const hiDigits = hi - ZERO_4;
104
103
  const bad =
@@ -174,8 +173,7 @@ const FINAL_4_MAGIC: u64 = 0x0000_0064_0000_0001;
174
173
  * @param srcStart Pointer to the start of 32 source bytes (16 UTF-16 chars).
175
174
  * @returns The parsed 16-digit value, or `U64.MAX_VALUE` on invalid input.
176
175
  */
177
- // @ts-expect-error: @inline is a valid decorator
178
- @inline export function parse16Digits_SWAR(srcStart: usize): u64 {
176
+ export function parse16Digits_SWAR(srcStart: usize): u64 {
179
177
  const b0 = load<u64>(srcStart);
180
178
  const b1 = load<u64>(srcStart, 8);
181
179
  const b2 = load<u64>(srcStart, 16);
@@ -220,8 +218,7 @@ const FINAL_4_MAGIC: u64 = 0x0000_0064_0000_0001;
220
218
  * @param srcStart Pointer to the start of 32 source bytes (16 UTF-16 chars).
221
219
  * @returns The parsed 16-digit value.
222
220
  */
223
- // @ts-expect-error: @inline is a valid decorator
224
- @inline export function parse16Digits_SWAR_Unsafe(srcStart: usize): u64 {
221
+ export function parse16Digits_SWAR_Unsafe(srcStart: usize): u64 {
225
222
  const b0 = load<u64>(srcStart);
226
223
  const b1 = load<u64>(srcStart, 8);
227
224
  const b2 = load<u64>(srcStart, 16);
package/lib/as-bs.ts CHANGED
@@ -287,6 +287,43 @@ export namespace bs {
287
287
  }
288
288
  return changetype<T>(out);
289
289
  }
290
+
291
+ /**
292
+ * Like `out<T>()`, but writes the finished bytes into the existing object
293
+ * `target`, reusing its allocation: an in-place overwrite when the size
294
+ * already matches, `__renew` when it differs. Falls back to a fresh
295
+ * `out<T>()` when `target` is null or a static (`< __heap_base`), so callers
296
+ * can pass an empty/uninitialized target on the first call. Mirrors the reuse
297
+ * policy of `toField`, but for the top-level return value.
298
+ */
299
+ // @ts-expect-error: @inline is a valid decorator
300
+ @inline export function outTo<T>(target: usize): T {
301
+ if (target < __heap_base) return out<T>();
302
+ let len: usize;
303
+ let src: usize;
304
+ if (cacheOutput === 0) {
305
+ len = offset - buffer;
306
+ src = buffer;
307
+ } else {
308
+ len = cacheOutputLen;
309
+ src = cacheOutput;
310
+ }
311
+ let dst = target;
312
+ if (changetype<OBJECT>(target - TOTAL_OVERHEAD).rtSize != len) {
313
+ // @ts-expect-error: __renew is a runtime builtin
314
+ dst = __renew(target, len);
315
+ }
316
+ memory.copy(dst, src, len);
317
+ if (cacheOutput === 0) {
318
+ finalizeDynamicOutput(len);
319
+ } else {
320
+ cacheOutput = 0;
321
+ cacheOutputLen = 0;
322
+ offset = buffer;
323
+ stackSize = 0;
324
+ }
325
+ return changetype<T>(dst);
326
+ }
290
327
  }
291
328
 
292
329
  /**