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.
Files changed (155) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +1 -1
  3. package/assembly/deserialize/helpers/uint.ts +4 -1
  4. package/assembly/deserialize/index/arbitrary.ts +7 -3
  5. package/assembly/deserialize/index/array.ts +42 -17
  6. package/assembly/deserialize/index/bool.ts +1 -1
  7. package/assembly/deserialize/index/date.ts +1 -1
  8. package/assembly/deserialize/index/float.ts +40 -1
  9. package/assembly/deserialize/index/integer.ts +68 -1
  10. package/assembly/deserialize/index/map.ts +1 -1
  11. package/assembly/deserialize/index/object.ts +1 -1
  12. package/assembly/deserialize/index/raw.ts +1 -1
  13. package/assembly/deserialize/index/set.ts +1 -1
  14. package/assembly/deserialize/index/staticarray.ts +4 -1
  15. package/assembly/deserialize/index/string.ts +32 -4
  16. package/assembly/deserialize/index/struct.ts +1 -1
  17. package/assembly/deserialize/index/typedarray.ts +30 -10
  18. package/assembly/deserialize/index/unsigned.ts +78 -1
  19. package/assembly/deserialize/index.ts +1 -0
  20. package/assembly/deserialize/{simple → naive}/array/arbitrary.ts +24 -5
  21. package/assembly/deserialize/{simple → naive}/array/array.ts +8 -2
  22. package/assembly/deserialize/naive/array/bool.ts +68 -0
  23. package/assembly/deserialize/{simple → naive}/array/box.ts +8 -2
  24. package/assembly/deserialize/naive/array/float.ts +63 -0
  25. package/assembly/deserialize/{simple → naive}/array/generic.ts +14 -7
  26. package/assembly/deserialize/naive/array/integer.ts +86 -0
  27. package/assembly/deserialize/naive/array/map.ts +47 -0
  28. package/assembly/deserialize/naive/array/object.ts +47 -0
  29. package/assembly/deserialize/{simple → naive}/array/raw.ts +34 -7
  30. package/assembly/deserialize/naive/array/string.ts +69 -0
  31. package/assembly/deserialize/naive/array/struct.ts +47 -0
  32. package/assembly/deserialize/{simple → naive}/array.ts +15 -10
  33. package/assembly/deserialize/{simple → naive}/bool.ts +6 -2
  34. package/assembly/deserialize/naive/float.ts +135 -0
  35. package/assembly/deserialize/{simple → naive}/integer.ts +10 -2
  36. package/assembly/deserialize/{simple → naive}/map.ts +106 -27
  37. package/assembly/deserialize/{simple → naive}/object.ts +65 -19
  38. package/assembly/deserialize/{simple → naive}/raw.ts +4 -1
  39. package/assembly/deserialize/{simple → naive}/set.ts +49 -19
  40. package/assembly/deserialize/{simple → naive}/staticarray/array.ts +1 -1
  41. package/assembly/deserialize/{simple → naive}/staticarray/bool.ts +1 -1
  42. package/assembly/deserialize/{simple → naive}/staticarray/float.ts +1 -1
  43. package/assembly/deserialize/{simple → naive}/staticarray/integer.ts +1 -1
  44. package/assembly/deserialize/{simple → naive}/staticarray/string.ts +11 -3
  45. package/assembly/deserialize/{simple → naive}/staticarray/struct.ts +1 -2
  46. package/assembly/deserialize/{simple → naive}/staticarray.ts +68 -18
  47. package/assembly/deserialize/naive/string.ts +199 -0
  48. package/assembly/deserialize/{simple → naive}/struct.ts +5 -1
  49. package/assembly/deserialize/{simple → naive}/typedarray.ts +17 -4
  50. package/assembly/deserialize/{simple → naive}/unsigned.ts +10 -15
  51. package/assembly/deserialize/simd/array/integer.ts +339 -62
  52. package/assembly/deserialize/simd/float.ts +303 -0
  53. package/assembly/deserialize/simd/integer.ts +233 -0
  54. package/assembly/deserialize/simd/string.ts +266 -107
  55. package/assembly/deserialize/swar/array/arbitrary.ts +11 -3
  56. package/assembly/deserialize/swar/array/array.ts +40 -9
  57. package/assembly/deserialize/swar/array/bool.ts +28 -5
  58. package/assembly/deserialize/swar/array/box.ts +11 -3
  59. package/assembly/deserialize/swar/array/float.ts +295 -7
  60. package/assembly/deserialize/swar/array/generic.ts +28 -7
  61. package/assembly/deserialize/swar/array/integer.ts +363 -112
  62. package/assembly/deserialize/swar/array/map.ts +11 -3
  63. package/assembly/deserialize/swar/array/object.ts +37 -25
  64. package/assembly/deserialize/swar/array/raw.ts +11 -3
  65. package/assembly/deserialize/swar/array/shared.ts +63 -14
  66. package/assembly/deserialize/swar/array/string.ts +140 -7
  67. package/assembly/deserialize/swar/array/struct.ts +66 -12
  68. package/assembly/deserialize/swar/array.ts +12 -51
  69. package/assembly/deserialize/swar/float.ts +304 -0
  70. package/assembly/deserialize/swar/integer.ts +246 -0
  71. package/assembly/deserialize/swar/string.ts +213 -294
  72. package/assembly/deserialize/swar/typedarray.ts +224 -0
  73. package/assembly/index.d.ts +3 -1
  74. package/assembly/index.ts +402 -261
  75. package/assembly/serialize/index/array.ts +1 -1
  76. package/assembly/serialize/index/bool.ts +1 -1
  77. package/assembly/serialize/index/date.ts +1 -1
  78. package/assembly/serialize/index/float.ts +5 -1
  79. package/assembly/serialize/index/integer.ts +1 -1
  80. package/assembly/serialize/index/map.ts +1 -1
  81. package/assembly/serialize/index/raw.ts +1 -1
  82. package/assembly/serialize/index/set.ts +1 -1
  83. package/assembly/serialize/index/staticarray.ts +1 -1
  84. package/assembly/serialize/index/string.ts +1 -1
  85. package/assembly/serialize/index/struct.ts +1 -1
  86. package/assembly/serialize/index/typedarray.ts +21 -12
  87. package/assembly/serialize/index.ts +1 -0
  88. package/assembly/serialize/naive/array.ts +351 -0
  89. package/assembly/serialize/{simple → naive}/float.ts +4 -1
  90. package/assembly/serialize/naive/integer.ts +19 -0
  91. package/assembly/serialize/{simple → naive}/map.ts +6 -2
  92. package/assembly/serialize/{simple → naive}/raw.ts +5 -1
  93. package/assembly/serialize/{simple → naive}/set.ts +6 -1
  94. package/assembly/serialize/{simple → naive}/staticarray.ts +6 -1
  95. package/assembly/serialize/{simple → naive}/string.ts +1 -2
  96. package/assembly/serialize/{simple → naive}/typedarray.ts +10 -3
  97. package/assembly/serialize/simd/string.ts +6 -2
  98. package/assembly/serialize/swar/string.ts +15 -141
  99. package/assembly/util/atoi-fast.ts +81 -0
  100. package/assembly/util/concat.ts +5 -1
  101. package/assembly/util/dragonbox-cache.ts +443 -2
  102. package/assembly/util/dragonbox.ts +53 -17
  103. package/assembly/util/itoa-fast.ts +241 -0
  104. package/assembly/util/masks.ts +18 -1
  105. package/assembly/util/parsefloat-fast.ts +167 -0
  106. package/assembly/util/scanValueEnd.ts +78 -0
  107. package/assembly/util/scientific.ts +132 -0
  108. package/assembly/util/simd-int.ts +191 -0
  109. package/assembly/util/snp.ts +4 -1
  110. package/assembly/util/swar-int.ts +248 -0
  111. package/assembly/util/swar.ts +13 -3
  112. package/lib/as-bs.ts +27 -6
  113. package/package.json +15 -11
  114. package/transform/lib/builder.d.ts.map +1 -1
  115. package/transform/lib/builder.js +13 -5
  116. package/transform/lib/builder.js.map +1 -1
  117. package/transform/lib/index.d.ts +5 -0
  118. package/transform/lib/index.d.ts.map +1 -1
  119. package/transform/lib/index.js +1046 -340
  120. package/transform/lib/index.js.map +1 -1
  121. package/transform/lib/linkers/alias.d.ts.map +1 -1
  122. package/transform/lib/linkers/alias.js.map +1 -1
  123. package/transform/lib/linkers/custom.d.ts.map +1 -1
  124. package/transform/lib/linkers/custom.js +3 -2
  125. package/transform/lib/linkers/custom.js.map +1 -1
  126. package/transform/lib/linkers/imports.d.ts.map +1 -1
  127. package/transform/lib/linkers/imports.js.map +1 -1
  128. package/transform/lib/types.d.ts.map +1 -1
  129. package/transform/lib/types.js +54 -16
  130. package/transform/lib/types.js.map +1 -1
  131. package/transform/lib/util.d.ts.map +1 -1
  132. package/transform/lib/util.js +1 -1
  133. package/transform/lib/util.js.map +1 -1
  134. package/transform/lib/visitor.d.ts.map +1 -1
  135. package/transform/lib/visitor.js +2 -1
  136. package/transform/lib/visitor.js.map +1 -1
  137. package/assembly/custom/util.ts +0 -310
  138. package/assembly/deserialize/simple/arbitrary.ts +0 -23
  139. package/assembly/deserialize/simple/array/bool.ts +0 -17
  140. package/assembly/deserialize/simple/array/float.ts +0 -28
  141. package/assembly/deserialize/simple/array/integer.ts +0 -27
  142. package/assembly/deserialize/simple/array/map.ts +0 -28
  143. package/assembly/deserialize/simple/array/object.ts +0 -28
  144. package/assembly/deserialize/simple/array/string.ts +0 -23
  145. package/assembly/deserialize/simple/array/struct.ts +0 -28
  146. package/assembly/deserialize/simple/float.ts +0 -201
  147. package/assembly/deserialize/simple/string.ts +0 -132
  148. package/assembly/serialize/simple/arbitrary.ts +0 -79
  149. package/assembly/serialize/simple/array.ts +0 -86
  150. package/assembly/serialize/simple/integer.ts +0 -20
  151. package/assembly/serialize/simple/object.ts +0 -42
  152. /package/assembly/deserialize/{simple → naive}/date.ts +0 -0
  153. /package/assembly/serialize/{simple → naive}/bool.ts +0 -0
  154. /package/assembly/serialize/{simple → naive}/date.ts +0 -0
  155. /package/assembly/serialize/{simple → naive}/struct.ts +0 -0
@@ -0,0 +1,304 @@
1
+ // SWAR float deserializers. Lemire-style fast path with parse4 SWAR digit
2
+ // folding on the fractional accumulator. Output is bit-identical to
3
+ // `f64.parse` / `f32.parse` (the NAIVE baseline) on the fast path because:
4
+ //
5
+ // - the u64 mantissa accumulator is exact (no rounding in the digit loop),
6
+ // - `1e0 .. 1e22` are exactly representable in f64, and
7
+ // - a single fmul/fdiv on two exact operands is correctly rounded.
8
+ //
9
+ // Pathological inputs (>19 mantissa digits, mantissa > 2^53, or |exp| > 22)
10
+ // fall through to `f64.parse` / `f32.parse` over the float's own range so the
11
+ // SWAR result matches the NAIVE result for every input.
12
+ //
13
+ // Inspired by Daniel Lemire, "Number parsing at a gigabyte per second" (2021)
14
+ // and the simdjson `fast_float` implementation. The integer-part loop stays
15
+ // scalar — most JSON float payloads have 1-3 digit integer parts, so a parse4
16
+ // stride there pays the wasted-validate cost on every call without saving
17
+ // enough scalar iterations.
18
+
19
+ import { ptrToStr } from "../../util/ptrToStr";
20
+ import { parse4Digits_PairMul } from "../../util/swar-int";
21
+ import { scientific } from "../../util/scientific";
22
+
23
+ export const POW10_F64_POS: usize = memory.data<f64>([
24
+ 1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14,
25
+ 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22,
26
+ ]);
27
+ export const MAX_EXACT_POW10: i32 = 22;
28
+ // 2^53. Any u64 mantissa <= this is exact in f64.
29
+ export const MAX_EXACT_MANTISSA: u64 = 1 << 53;
30
+
31
+ const ASCII_PLUS: u16 = 43;
32
+ const ASCII_MINUS: u16 = 45;
33
+ const ASCII_DOT: u16 = 46;
34
+ const ASCII_ZERO: u16 = 48;
35
+ const ASCII_E_UP: u16 = 69;
36
+ const ASCII_E_LO: u16 = 101;
37
+
38
+ // @ts-ignore: inline
39
+ @inline export function loadPow10(exp: u32): f64 {
40
+ return load<f64>(POW10_F64_POS + ((<usize>exp) << 3));
41
+ }
42
+
43
+ // @ts-ignore: inline
44
+ @inline function fallback<T>(srcStart: usize, srcEnd: usize): T {
45
+ const s = ptrToStr(srcStart, srcEnd);
46
+ // @ts-ignore
47
+ const type: T = 0;
48
+ // @ts-ignore
49
+ if (type instanceof f64) return <T>f64.parse(s);
50
+ // @ts-ignore
51
+ return <T>(<f32>f32.parse(s));
52
+ }
53
+
54
+ // @ts-ignore: inline
55
+ @inline function fallbackField<T extends number>(
56
+ origStart: usize,
57
+ end: usize,
58
+ fieldPtr: usize,
59
+ ): void {
60
+ const s = ptrToStr(origStart, end);
61
+ if (sizeof<T>() == sizeof<f32>()) {
62
+ store<f32>(fieldPtr, f32.parse(s));
63
+ } else {
64
+ store<f64>(fieldPtr, f64.parse(s));
65
+ }
66
+ }
67
+
68
+ // @ts-ignore: inline
69
+ @inline export function deserializeFloat_SWAR<T>(
70
+ srcStart: usize,
71
+ srcEnd: usize,
72
+ ): T {
73
+ const origStart = srcStart;
74
+ let p = srcStart;
75
+ let negative = false;
76
+ if (p < srcEnd && load<u16>(p) == ASCII_MINUS) {
77
+ negative = true;
78
+ p += 2;
79
+ }
80
+
81
+ // Integer part: scalar. Most JSON integers are 1-3 digits, so parse4 would
82
+ // waste a validate per call.
83
+ let mantissa: u64 = 0;
84
+ let intDigits: i32 = 0;
85
+ while (p < srcEnd) {
86
+ const d = <u32>load<u16>(p) - ASCII_ZERO;
87
+ if (d > 9) break;
88
+ mantissa = mantissa * 10 + <u64>d;
89
+ intDigits++;
90
+ p += 2;
91
+ }
92
+
93
+ // Fractional part: parse4 stride (8 bytes / 4 digits) → scalar tail.
94
+ // parse8 was tried and benchmarked even/worse; the saved mantissa mul
95
+ // didn't outweigh the extra load and combined-validation latency, and the
96
+ // dependency chain is the same length either way.
97
+ let fracDigits: i32 = 0;
98
+ if (p < srcEnd && load<u16>(p) == ASCII_DOT) {
99
+ p += 2;
100
+ while (p + 6 < srcEnd) {
101
+ const parsed = inline.always(parse4Digits_PairMul(load<u64>(p)));
102
+ if (parsed == U32.MAX_VALUE) break;
103
+ mantissa = mantissa * 10_000 + <u64>parsed;
104
+ fracDigits += 4;
105
+ p += 8;
106
+ }
107
+ while (p < srcEnd) {
108
+ const d = <u32>load<u16>(p) - ASCII_ZERO;
109
+ if (d > 9) break;
110
+ mantissa = mantissa * 10 + <u64>d;
111
+ fracDigits++;
112
+ p += 2;
113
+ }
114
+ }
115
+
116
+ const mantDigits = intDigits + fracDigits;
117
+ if (mantDigits == 0) return fallback<T>(origStart, srcEnd);
118
+
119
+ let exponent: i32 = -fracDigits;
120
+
121
+ // Optional `e[+-]NNN` suffix.
122
+ if (p < srcEnd) {
123
+ const c = load<u16>(p);
124
+ if (c == ASCII_E_LO || c == ASCII_E_UP) {
125
+ const expStart = p;
126
+ p += 2;
127
+ if (p >= srcEnd) return fallback<T>(origStart, expStart);
128
+ let expNeg = false;
129
+ const sc = load<u16>(p);
130
+ if (sc == ASCII_MINUS) {
131
+ expNeg = true;
132
+ p += 2;
133
+ } else if (sc == ASCII_PLUS) {
134
+ p += 2;
135
+ }
136
+ if (p >= srcEnd) return fallback<T>(origStart, expStart);
137
+ let exp: i32 = 0;
138
+ let expDigits: i32 = 0;
139
+ while (p < srcEnd) {
140
+ const d = <u32>load<u16>(p) - ASCII_ZERO;
141
+ if (d > 9) break;
142
+ exp = exp * 10 + <i32>d;
143
+ expDigits++;
144
+ if (expDigits > 4) return fallback<T>(origStart, srcEnd);
145
+ p += 2;
146
+ }
147
+ if (expDigits == 0) return fallback<T>(origStart, expStart);
148
+ exponent += expNeg ? -exp : exp;
149
+ }
150
+ }
151
+
152
+ // Lemire fast path when fully in range; `scientific` for u64-fitting
153
+ // mantissas that exceed 2^53 or |exp| > 22 (still correctly rounded, but
154
+ // via the scaledown/scaleup path); `f*.parse` fallback only for >19
155
+ // mantissa digits where the sticky-bit handling matters.
156
+ let result: f64;
157
+ if (
158
+ mantDigits <= 19 &&
159
+ mantissa <= MAX_EXACT_MANTISSA &&
160
+ exponent <= MAX_EXACT_POW10 &&
161
+ exponent >= -MAX_EXACT_POW10
162
+ ) {
163
+ result = <f64>mantissa;
164
+ if (exponent > 0) {
165
+ result *= loadPow10(<u32>exponent);
166
+ } else if (exponent < 0) {
167
+ result /= loadPow10(<u32>-exponent);
168
+ }
169
+ } else if (mantDigits <= 19) {
170
+ result = scientific(mantissa, exponent);
171
+ } else {
172
+ return fallback<T>(origStart, srcEnd);
173
+ }
174
+ if (negative) result = -result;
175
+
176
+ // @ts-ignore
177
+ const type: T = 0;
178
+ // @ts-ignore
179
+ if (type instanceof f64) return <T>result;
180
+ // @ts-ignore
181
+ return <T>(<f32>result);
182
+ }
183
+
184
+ // @ts-ignore: inline
185
+ @inline export function deserializeFloatField_SWAR<T extends number>(
186
+ srcStart: usize,
187
+ srcEnd: usize,
188
+ dstObj: usize,
189
+ dstOffset: usize = 0,
190
+ ): usize {
191
+ const fieldPtr = dstObj + dstOffset;
192
+ const origStart = srcStart;
193
+ let p = srcStart;
194
+ let negative = false;
195
+ if (p < srcEnd && load<u16>(p) == ASCII_MINUS) {
196
+ negative = true;
197
+ p += 2;
198
+ }
199
+
200
+ let mantissa: u64 = 0;
201
+ let intDigits: i32 = 0;
202
+ while (p < srcEnd) {
203
+ const d = <u32>load<u16>(p) - ASCII_ZERO;
204
+ if (d > 9) break;
205
+ mantissa = mantissa * 10 + <u64>d;
206
+ intDigits++;
207
+ p += 2;
208
+ }
209
+
210
+ let fracDigits: i32 = 0;
211
+ if (p < srcEnd && load<u16>(p) == ASCII_DOT) {
212
+ p += 2;
213
+ while (p + 6 < srcEnd) {
214
+ const parsed = parse4Digits_PairMul(load<u64>(p));
215
+ if (parsed == U32.MAX_VALUE) break;
216
+ mantissa = mantissa * 10_000 + <u64>parsed;
217
+ fracDigits += 4;
218
+ p += 8;
219
+ }
220
+ while (p < srcEnd) {
221
+ const d = <u32>load<u16>(p) - ASCII_ZERO;
222
+ if (d > 9) break;
223
+ mantissa = mantissa * 10 + <u64>d;
224
+ fracDigits++;
225
+ p += 2;
226
+ }
227
+ }
228
+
229
+ const mantDigits = intDigits + fracDigits;
230
+ if (mantDigits == 0) unreachable();
231
+
232
+ let exponent: i32 = -fracDigits;
233
+
234
+ if (p < srcEnd) {
235
+ const c = load<u16>(p);
236
+ if (c == ASCII_E_LO || c == ASCII_E_UP) {
237
+ const expStart = p;
238
+ p += 2;
239
+ if (p >= srcEnd) {
240
+ fallbackField<T>(origStart, expStart, fieldPtr);
241
+ return expStart;
242
+ }
243
+ let expNeg = false;
244
+ const sc = load<u16>(p);
245
+ if (sc == ASCII_MINUS) {
246
+ expNeg = true;
247
+ p += 2;
248
+ } else if (sc == ASCII_PLUS) {
249
+ p += 2;
250
+ }
251
+ if (p >= srcEnd) {
252
+ fallbackField<T>(origStart, expStart, fieldPtr);
253
+ return expStart;
254
+ }
255
+ let exp: i32 = 0;
256
+ let expDigits: i32 = 0;
257
+ while (p < srcEnd) {
258
+ const d = <u32>load<u16>(p) - ASCII_ZERO;
259
+ if (d > 9) break;
260
+ exp = exp * 10 + <i32>d;
261
+ expDigits++;
262
+ if (expDigits > 4) {
263
+ fallbackField<T>(origStart, p, fieldPtr);
264
+ return p;
265
+ }
266
+ p += 2;
267
+ }
268
+ if (expDigits == 0) {
269
+ fallbackField<T>(origStart, expStart, fieldPtr);
270
+ return expStart;
271
+ }
272
+ exponent += expNeg ? -exp : exp;
273
+ }
274
+ }
275
+
276
+ let result: f64;
277
+ if (
278
+ mantDigits <= 19 &&
279
+ mantissa <= MAX_EXACT_MANTISSA &&
280
+ exponent <= MAX_EXACT_POW10 &&
281
+ exponent >= -MAX_EXACT_POW10
282
+ ) {
283
+ result = <f64>mantissa;
284
+ if (exponent > 0) {
285
+ result *= loadPow10(<u32>exponent);
286
+ } else if (exponent < 0) {
287
+ result /= loadPow10(<u32>-exponent);
288
+ }
289
+ } else if (mantDigits <= 19) {
290
+ result = scientific(mantissa, exponent);
291
+ } else {
292
+ fallbackField<T>(origStart, p, fieldPtr);
293
+ return p;
294
+ }
295
+ if (negative) result = -result;
296
+
297
+ if (sizeof<T>() == sizeof<f32>()) {
298
+ store<f32>(fieldPtr, <f32>result);
299
+ } else {
300
+ store<f64>(fieldPtr, result);
301
+ }
302
+
303
+ return p;
304
+ }
@@ -0,0 +1,246 @@
1
+ // SWAR integer deserializers (signed + unsigned) over UTF-16 sources.
2
+ //
3
+ // Consume-to-end paths use the full tiered stride hierarchy:
4
+ //
5
+ // - parse16 (32 bytes / 16 digits): best for long inputs
6
+ // - parse8 (16 bytes / 8 digits)
7
+ // - parse4 ( 8 bytes / 4 digits)
8
+ // - scalar ( 2 bytes / 1 digit )
9
+ //
10
+ // Scan paths use asymmetric tuning based on empirical h2h benches:
11
+ //
12
+ // - Unsigned scan: parse8 + scalar (no parse4). For unsigned inputs the
13
+ // digit run is aligned, so parse8 either succeeds cleanly or terminates
14
+ // early. The intermediate parse4 stride costs more in wasted-validate
15
+ // than it saves in successful work.
16
+ // - Signed scan: parse4 + scalar (no parse8). The leading minus consumes
17
+ // 2 bytes, shifting the digit run into parse8's "terminator-in-load"
18
+ // zone where validate-fail is common. parse4 is a smaller failure unit.
19
+ //
20
+ // parse16 is omitted from scan entirely: a 16-character digit run plus
21
+ // terminator fits in its 32-byte load, so the terminator triggers a wasted
22
+ // validate-fail at the boundary.
23
+
24
+ import {
25
+ parse4Digits_PairMul,
26
+ parse4Digits_PairMul_Unsafe,
27
+ parse8Digits_PairMul,
28
+ parse8Digits_PairMul_Unsafe,
29
+ parse16Digits_SWAR,
30
+ parse16Digits_SWAR_Unsafe,
31
+ } from "../../util/swar-int";
32
+
33
+ const ASCII_MINUS: u16 = 45;
34
+ const ASCII_ZERO: u16 = 48;
35
+
36
+ /**
37
+ * Store a signed value into a typed integer field, truncating to `T`'s width.
38
+ *
39
+ * @param dstPtr Destination pointer (already includes any field offset).
40
+ * @param value The `u64` accumulator, interpreted as a two's-complement
41
+ * signed integer for narrower types.
42
+ */
43
+ // @ts-expect-error: @inline is a valid decorator
44
+ @inline function storeSignedToField<T extends number>(
45
+ dstPtr: usize,
46
+ value: u64,
47
+ ): void {
48
+ if (sizeof<T>() == 1) {
49
+ store<i8>(dstPtr, <i8>value);
50
+ } else if (sizeof<T>() == 2) {
51
+ store<i16>(dstPtr, <i16>value);
52
+ } else if (sizeof<T>() == 4) {
53
+ store<i32>(dstPtr, <i32>value);
54
+ } else {
55
+ store<i64>(dstPtr, <i64>value);
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Store an unsigned value into a typed integer field, truncating to `T`'s
61
+ * width.
62
+ *
63
+ * @param dstPtr Destination pointer (already includes any field offset).
64
+ * @param value The `u64` accumulator.
65
+ */
66
+ // @ts-expect-error: @inline is a valid decorator
67
+ @inline function storeUnsignedToField<T extends number>(
68
+ dstPtr: usize,
69
+ value: u64,
70
+ ): void {
71
+ if (sizeof<T>() == 1) {
72
+ store<u8>(dstPtr, <u8>value);
73
+ } else if (sizeof<T>() == 2) {
74
+ store<u16>(dstPtr, <u16>value);
75
+ } else if (sizeof<T>() == 4) {
76
+ store<u32>(dstPtr, <u32>value);
77
+ } else {
78
+ store<u64>(dstPtr, value);
79
+ }
80
+ }
81
+
82
+ /**
83
+ * Parse a signed integer by consuming the entire `[srcStart, srcEnd)` range
84
+ * as a digit run, with an optional leading `-`.
85
+ *
86
+ * Caller guarantees the range is well-formed (optional minus followed by
87
+ * digits only). Uses the unsafe SWAR kernels with no per-stride validation.
88
+ *
89
+ * @param srcStart Pointer to the first UTF-16 code unit.
90
+ * @param srcEnd Pointer just past the last code unit.
91
+ * @returns The parsed value, two's-complement truncated to `T`.
92
+ */
93
+ // @ts-expect-error: @inline is a valid decorator
94
+ @inline export function deserializeInteger_SWAR<T extends number>(
95
+ srcStart: usize,
96
+ srcEnd: usize,
97
+ ): T {
98
+ let negative = false;
99
+ if (srcStart < srcEnd && load<u16>(srcStart) == ASCII_MINUS) {
100
+ negative = true;
101
+ srcStart += 2;
102
+ }
103
+ let value: u64 = 0;
104
+ while (srcStart + 30 < srcEnd) {
105
+ value =
106
+ value * 10_000_000_000_000_000 + parse16Digits_SWAR_Unsafe(srcStart);
107
+ srcStart += 32;
108
+ }
109
+ while (srcStart + 14 < srcEnd) {
110
+ value =
111
+ value * 100_000_000 +
112
+ parse8Digits_PairMul_Unsafe(load<u64>(srcStart), load<u64>(srcStart, 8));
113
+ srcStart += 16;
114
+ }
115
+ while (srcStart + 6 < srcEnd) {
116
+ value = value * 10_000 + parse4Digits_PairMul_Unsafe(load<u64>(srcStart));
117
+ srcStart += 8;
118
+ }
119
+ while (srcStart < srcEnd) {
120
+ value = value * 10 + (<u32>load<u16>(srcStart) - ASCII_ZERO);
121
+ srcStart += 2;
122
+ }
123
+ return <T>(negative ? 0 - value : value);
124
+ }
125
+
126
+ /**
127
+ * Scan for a signed integer field, stopping at the first non-digit
128
+ * character. Writes the parsed value through `dstObj + dstOffset` and
129
+ * returns the source position immediately after the last digit.
130
+ *
131
+ * Used by struct field deserializers, where the digit run is followed by
132
+ * a `,`, `}`, `]`, whitespace, etc.
133
+ *
134
+ * @param srcStart Pointer to the first UTF-16 code unit.
135
+ * @param srcEnd Pointer just past the last code unit.
136
+ * @param dstObj Destination object pointer.
137
+ * @param dstOffset Byte offset of the field within `dstObj`.
138
+ * @returns The source position immediately after the last digit consumed.
139
+ */
140
+ // @ts-expect-error: @inline is a valid decorator
141
+ @inline export function deserializeIntegerField_SWAR<T extends number>(
142
+ srcStart: usize,
143
+ srcEnd: usize,
144
+ dstObj: usize,
145
+ dstOffset: usize = 0,
146
+ ): usize {
147
+ let negative = false;
148
+ if (srcStart < srcEnd && load<u16>(srcStart) == ASCII_MINUS) {
149
+ negative = true;
150
+ srcStart += 2;
151
+ }
152
+ let value: u64 = 0;
153
+ // Signed scan uses parse4 + scalar only (see file header).
154
+ while (srcStart + 6 < srcEnd) {
155
+ const parsed = parse4Digits_PairMul(load<u64>(srcStart));
156
+ if (parsed == U32.MAX_VALUE) break;
157
+ value = value * 10_000 + parsed;
158
+ srcStart += 8;
159
+ }
160
+ while (srcStart < srcEnd) {
161
+ const digit = <u32>load<u16>(srcStart) - ASCII_ZERO;
162
+ if (digit > 9) break;
163
+ value = value * 10 + digit;
164
+ srcStart += 2;
165
+ }
166
+ storeSignedToField<T>(dstObj + dstOffset, negative ? 0 - value : value);
167
+ return srcStart;
168
+ }
169
+
170
+ /**
171
+ * Parse an unsigned integer by consuming the entire `[srcStart, srcEnd)`
172
+ * range as a digit run.
173
+ *
174
+ * Caller guarantees the range is digits only. Uses the unsafe SWAR kernels
175
+ * with no per-stride validation.
176
+ *
177
+ * @param srcStart Pointer to the first UTF-16 code unit.
178
+ * @param srcEnd Pointer just past the last code unit.
179
+ * @returns The parsed value, truncated to `T`.
180
+ */
181
+ // @ts-expect-error: @inline is a valid decorator
182
+ @inline export function deserializeUnsigned_SWAR<T extends number>(
183
+ srcStart: usize,
184
+ srcEnd: usize,
185
+ ): T {
186
+ let value: u64 = 0;
187
+ while (srcStart + 30 < srcEnd) {
188
+ value =
189
+ value * 10_000_000_000_000_000 + parse16Digits_SWAR_Unsafe(srcStart);
190
+ srcStart += 32;
191
+ }
192
+ while (srcStart + 14 < srcEnd) {
193
+ value =
194
+ value * 100_000_000 +
195
+ parse8Digits_PairMul_Unsafe(load<u64>(srcStart), load<u64>(srcStart, 8));
196
+ srcStart += 16;
197
+ }
198
+ while (srcStart + 6 < srcEnd) {
199
+ value = value * 10_000 + parse4Digits_PairMul_Unsafe(load<u64>(srcStart));
200
+ srcStart += 8;
201
+ }
202
+ while (srcStart < srcEnd) {
203
+ value = value * 10 + (<u32>load<u16>(srcStart) - ASCII_ZERO);
204
+ srcStart += 2;
205
+ }
206
+ return <T>value;
207
+ }
208
+
209
+ /**
210
+ * Scan for an unsigned integer field, stopping at the first non-digit
211
+ * character. Writes the parsed value through `dstObj + dstOffset` and
212
+ * returns the source position immediately after the last digit.
213
+ *
214
+ * @param srcStart Pointer to the first UTF-16 code unit.
215
+ * @param srcEnd Pointer just past the last code unit.
216
+ * @param dstObj Destination object pointer.
217
+ * @param dstOffset Byte offset of the field within `dstObj`.
218
+ * @returns The source position immediately after the last digit consumed.
219
+ */
220
+ // @ts-expect-error: @inline is a valid decorator
221
+ @inline export function deserializeUnsignedField_SWAR<T extends number>(
222
+ srcStart: usize,
223
+ srcEnd: usize,
224
+ dstObj: usize,
225
+ dstOffset: usize = 0,
226
+ ): usize {
227
+ let value: u64 = 0;
228
+ // Unsigned scan uses parse8 + scalar only (see file header).
229
+ while (srcStart + 14 < srcEnd) {
230
+ const parsed = parse8Digits_PairMul(
231
+ load<u64>(srcStart),
232
+ load<u64>(srcStart, 8),
233
+ );
234
+ if (parsed == U32.MAX_VALUE) break;
235
+ value = value * 100_000_000 + parsed;
236
+ srcStart += 16;
237
+ }
238
+ while (srcStart < srcEnd) {
239
+ const digit = <u32>load<u16>(srcStart) - ASCII_ZERO;
240
+ if (digit > 9) break;
241
+ value = value * 10 + digit;
242
+ srcStart += 2;
243
+ }
244
+ storeUnsignedToField<T>(dstObj + dstOffset, value);
245
+ return srcStart;
246
+ }