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
|
@@ -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
|
+
}
|