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,303 @@
|
|
|
1
|
+
// SIMD float deserializers. Same Lemire-style fast path / `scientific()` /
|
|
2
|
+
// `f*.parse` cascade as `swar/float.ts`, but the fractional digit loop uses
|
|
3
|
+
// `parse8Digits_SIMD` (16 bytes / 8 digits) before falling through to
|
|
4
|
+
// `parse4Digits_PairMul` (8 bytes / 4 digits) and finally the scalar tail.
|
|
5
|
+
//
|
|
6
|
+
// Output is bit-identical to `f64.parse` / `f32.parse` for every input — the
|
|
7
|
+
// SIMD strides only change how the u64 mantissa is accumulated, not what it
|
|
8
|
+
// becomes.
|
|
9
|
+
//
|
|
10
|
+
// Requires `--enable simd` at compile time. Dead-code-eliminated when
|
|
11
|
+
// JSON_MODE != SIMD.
|
|
12
|
+
|
|
13
|
+
import { ptrToStr } from "../../util/ptrToStr";
|
|
14
|
+
import { parse4Digits_PairMul } from "../../util/swar-int";
|
|
15
|
+
import { parse16Digits_SIMD } from "../../util/simd-int";
|
|
16
|
+
import { scientific } from "../../util/scientific";
|
|
17
|
+
import { loadPow10, MAX_EXACT_MANTISSA, MAX_EXACT_POW10 } from "../swar/float";
|
|
18
|
+
|
|
19
|
+
const ASCII_PLUS: u16 = 43;
|
|
20
|
+
const ASCII_MINUS: u16 = 45;
|
|
21
|
+
const ASCII_DOT: u16 = 46;
|
|
22
|
+
const ASCII_ZERO: u16 = 48;
|
|
23
|
+
const ASCII_E_UP: u16 = 69;
|
|
24
|
+
const ASCII_E_LO: u16 = 101;
|
|
25
|
+
|
|
26
|
+
// @ts-ignore: inline
|
|
27
|
+
@inline function fallback<T>(srcStart: usize, srcEnd: usize): T {
|
|
28
|
+
const s = ptrToStr(srcStart, srcEnd);
|
|
29
|
+
// @ts-ignore
|
|
30
|
+
const type: T = 0;
|
|
31
|
+
// @ts-ignore
|
|
32
|
+
if (type instanceof f64) return <T>f64.parse(s);
|
|
33
|
+
// @ts-ignore
|
|
34
|
+
return <T>(<f32>f32.parse(s));
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// @ts-ignore: inline
|
|
38
|
+
@inline function fallbackField<T extends number>(
|
|
39
|
+
origStart: usize,
|
|
40
|
+
end: usize,
|
|
41
|
+
fieldPtr: usize,
|
|
42
|
+
): void {
|
|
43
|
+
const s = ptrToStr(origStart, end);
|
|
44
|
+
if (sizeof<T>() == sizeof<f32>()) {
|
|
45
|
+
store<f32>(fieldPtr, f32.parse(s));
|
|
46
|
+
} else {
|
|
47
|
+
store<f64>(fieldPtr, f64.parse(s));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// @ts-ignore: inline
|
|
52
|
+
@inline export function deserializeFloat_SIMD<T>(
|
|
53
|
+
srcStart: usize,
|
|
54
|
+
srcEnd: usize,
|
|
55
|
+
): T {
|
|
56
|
+
const origStart = srcStart;
|
|
57
|
+
let p = srcStart;
|
|
58
|
+
let negative = false;
|
|
59
|
+
if (p < srcEnd && load<u16>(p) == ASCII_MINUS) {
|
|
60
|
+
negative = true;
|
|
61
|
+
p += 2;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
let mantissa: u64 = 0;
|
|
65
|
+
let intDigits: i32 = 0;
|
|
66
|
+
while (p < srcEnd) {
|
|
67
|
+
const d = <u32>load<u16>(p) - ASCII_ZERO;
|
|
68
|
+
if (d > 9) break;
|
|
69
|
+
mantissa = mantissa * 10 + <u64>d;
|
|
70
|
+
intDigits++;
|
|
71
|
+
p += 2;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Fractional part: parse8 SIMD stride → parse4 SWAR stride → scalar tail.
|
|
75
|
+
// `intDigits + fracDigits <= 11` gate keeps `mantissa * 10^8 + parsed8`
|
|
76
|
+
// under u64 max even when the integer part is large.
|
|
77
|
+
let fracDigits: i32 = 0;
|
|
78
|
+
if (p < srcEnd && load<u16>(p) == ASCII_DOT) {
|
|
79
|
+
p += 2;
|
|
80
|
+
// parse16 SIMD only fires on long fractions (>=16 digits ahead, with
|
|
81
|
+
// <=3 integer digits to keep mantissa * 1e16 under u64 max). Rare in
|
|
82
|
+
// typical JSON but a big win when it does fire (8 digits per stride in
|
|
83
|
+
// SWAR's parse8 was benched even/worse than parse4, so we skip parse8
|
|
84
|
+
// entirely and let parse4 handle the 4-15 char tail).
|
|
85
|
+
while (p + 30 < srcEnd && intDigits + fracDigits <= 3) {
|
|
86
|
+
const parsed = parse16Digits_SIMD(p);
|
|
87
|
+
if (parsed == U64.MAX_VALUE) break;
|
|
88
|
+
mantissa = mantissa * 10_000_000_000_000_000 + parsed;
|
|
89
|
+
fracDigits += 16;
|
|
90
|
+
p += 32;
|
|
91
|
+
}
|
|
92
|
+
while (p + 6 < srcEnd) {
|
|
93
|
+
const parsed = parse4Digits_PairMul(load<u64>(p));
|
|
94
|
+
if (parsed == U32.MAX_VALUE) break;
|
|
95
|
+
mantissa = mantissa * 10_000 + <u64>parsed;
|
|
96
|
+
fracDigits += 4;
|
|
97
|
+
p += 8;
|
|
98
|
+
}
|
|
99
|
+
while (p < srcEnd) {
|
|
100
|
+
const d = <u32>load<u16>(p) - ASCII_ZERO;
|
|
101
|
+
if (d > 9) break;
|
|
102
|
+
mantissa = mantissa * 10 + <u64>d;
|
|
103
|
+
fracDigits++;
|
|
104
|
+
p += 2;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const mantDigits = intDigits + fracDigits;
|
|
109
|
+
if (mantDigits == 0) return fallback<T>(origStart, srcEnd);
|
|
110
|
+
|
|
111
|
+
let exponent: i32 = -fracDigits;
|
|
112
|
+
|
|
113
|
+
if (p < srcEnd) {
|
|
114
|
+
const c = load<u16>(p);
|
|
115
|
+
if (c == ASCII_E_LO || c == ASCII_E_UP) {
|
|
116
|
+
const expStart = p;
|
|
117
|
+
p += 2;
|
|
118
|
+
if (p >= srcEnd) return fallback<T>(origStart, expStart);
|
|
119
|
+
let expNeg = false;
|
|
120
|
+
const sc = load<u16>(p);
|
|
121
|
+
if (sc == ASCII_MINUS) {
|
|
122
|
+
expNeg = true;
|
|
123
|
+
p += 2;
|
|
124
|
+
} else if (sc == ASCII_PLUS) {
|
|
125
|
+
p += 2;
|
|
126
|
+
}
|
|
127
|
+
if (p >= srcEnd) return fallback<T>(origStart, expStart);
|
|
128
|
+
let exp: i32 = 0;
|
|
129
|
+
let expDigits: i32 = 0;
|
|
130
|
+
while (p < srcEnd) {
|
|
131
|
+
const d = <u32>load<u16>(p) - ASCII_ZERO;
|
|
132
|
+
if (d > 9) break;
|
|
133
|
+
exp = exp * 10 + <i32>d;
|
|
134
|
+
expDigits++;
|
|
135
|
+
if (expDigits > 4) return fallback<T>(origStart, srcEnd);
|
|
136
|
+
p += 2;
|
|
137
|
+
}
|
|
138
|
+
if (expDigits == 0) return fallback<T>(origStart, expStart);
|
|
139
|
+
exponent += expNeg ? -exp : exp;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
let result: f64;
|
|
144
|
+
if (
|
|
145
|
+
mantDigits <= 19 &&
|
|
146
|
+
mantissa <= MAX_EXACT_MANTISSA &&
|
|
147
|
+
exponent <= MAX_EXACT_POW10 &&
|
|
148
|
+
exponent >= -MAX_EXACT_POW10
|
|
149
|
+
) {
|
|
150
|
+
result = <f64>mantissa;
|
|
151
|
+
if (exponent > 0) {
|
|
152
|
+
result *= loadPow10(<u32>exponent);
|
|
153
|
+
} else if (exponent < 0) {
|
|
154
|
+
result /= loadPow10(<u32>-exponent);
|
|
155
|
+
}
|
|
156
|
+
} else if (mantDigits <= 19) {
|
|
157
|
+
result = scientific(mantissa, exponent);
|
|
158
|
+
} else {
|
|
159
|
+
return fallback<T>(origStart, srcEnd);
|
|
160
|
+
}
|
|
161
|
+
if (negative) result = -result;
|
|
162
|
+
|
|
163
|
+
// @ts-ignore
|
|
164
|
+
const type: T = 0;
|
|
165
|
+
// @ts-ignore
|
|
166
|
+
if (type instanceof f64) return <T>result;
|
|
167
|
+
// @ts-ignore
|
|
168
|
+
return <T>(<f32>result);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// @ts-ignore: inline
|
|
172
|
+
@inline export function deserializeFloatField_SIMD<T extends number>(
|
|
173
|
+
srcStart: usize,
|
|
174
|
+
srcEnd: usize,
|
|
175
|
+
dstObj: usize,
|
|
176
|
+
dstOffset: usize = 0,
|
|
177
|
+
): usize {
|
|
178
|
+
const fieldPtr = dstObj + dstOffset;
|
|
179
|
+
const origStart = srcStart;
|
|
180
|
+
let p = srcStart;
|
|
181
|
+
let negative = false;
|
|
182
|
+
if (p < srcEnd && load<u16>(p) == ASCII_MINUS) {
|
|
183
|
+
negative = true;
|
|
184
|
+
p += 2;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
let mantissa: u64 = 0;
|
|
188
|
+
let intDigits: i32 = 0;
|
|
189
|
+
while (p < srcEnd) {
|
|
190
|
+
const d = <u32>load<u16>(p) - ASCII_ZERO;
|
|
191
|
+
if (d > 9) break;
|
|
192
|
+
mantissa = mantissa * 10 + <u64>d;
|
|
193
|
+
intDigits++;
|
|
194
|
+
p += 2;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
let fracDigits: i32 = 0;
|
|
198
|
+
if (p < srcEnd && load<u16>(p) == ASCII_DOT) {
|
|
199
|
+
p += 2;
|
|
200
|
+
// parse16 SIMD only fires on long fractions (>=16 digits ahead, with
|
|
201
|
+
// <=3 integer digits to keep mantissa * 1e16 under u64 max). Rare in
|
|
202
|
+
// typical JSON but a big win when it does fire (8 digits per stride in
|
|
203
|
+
// SWAR's parse8 was benched even/worse than parse4, so we skip parse8
|
|
204
|
+
// entirely and let parse4 handle the 4-15 char tail).
|
|
205
|
+
while (p + 30 < srcEnd && intDigits + fracDigits <= 3) {
|
|
206
|
+
const parsed = parse16Digits_SIMD(p);
|
|
207
|
+
if (parsed == U64.MAX_VALUE) break;
|
|
208
|
+
mantissa = mantissa * 10_000_000_000_000_000 + parsed;
|
|
209
|
+
fracDigits += 16;
|
|
210
|
+
p += 32;
|
|
211
|
+
}
|
|
212
|
+
while (p + 6 < srcEnd) {
|
|
213
|
+
const parsed = parse4Digits_PairMul(load<u64>(p));
|
|
214
|
+
if (parsed == U32.MAX_VALUE) break;
|
|
215
|
+
mantissa = mantissa * 10_000 + <u64>parsed;
|
|
216
|
+
fracDigits += 4;
|
|
217
|
+
p += 8;
|
|
218
|
+
}
|
|
219
|
+
while (p < srcEnd) {
|
|
220
|
+
const d = <u32>load<u16>(p) - ASCII_ZERO;
|
|
221
|
+
if (d > 9) break;
|
|
222
|
+
mantissa = mantissa * 10 + <u64>d;
|
|
223
|
+
fracDigits++;
|
|
224
|
+
p += 2;
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const mantDigits = intDigits + fracDigits;
|
|
229
|
+
if (mantDigits == 0) unreachable();
|
|
230
|
+
|
|
231
|
+
let exponent: i32 = -fracDigits;
|
|
232
|
+
|
|
233
|
+
if (p < srcEnd) {
|
|
234
|
+
const c = load<u16>(p);
|
|
235
|
+
if (c == ASCII_E_LO || c == ASCII_E_UP) {
|
|
236
|
+
const expStart = p;
|
|
237
|
+
p += 2;
|
|
238
|
+
if (p >= srcEnd) {
|
|
239
|
+
fallbackField<T>(origStart, expStart, fieldPtr);
|
|
240
|
+
return expStart;
|
|
241
|
+
}
|
|
242
|
+
let expNeg = false;
|
|
243
|
+
const sc = load<u16>(p);
|
|
244
|
+
if (sc == ASCII_MINUS) {
|
|
245
|
+
expNeg = true;
|
|
246
|
+
p += 2;
|
|
247
|
+
} else if (sc == ASCII_PLUS) {
|
|
248
|
+
p += 2;
|
|
249
|
+
}
|
|
250
|
+
if (p >= srcEnd) {
|
|
251
|
+
fallbackField<T>(origStart, expStart, fieldPtr);
|
|
252
|
+
return expStart;
|
|
253
|
+
}
|
|
254
|
+
let exp: i32 = 0;
|
|
255
|
+
let expDigits: i32 = 0;
|
|
256
|
+
while (p < srcEnd) {
|
|
257
|
+
const d = <u32>load<u16>(p) - ASCII_ZERO;
|
|
258
|
+
if (d > 9) break;
|
|
259
|
+
exp = exp * 10 + <i32>d;
|
|
260
|
+
expDigits++;
|
|
261
|
+
if (expDigits > 4) {
|
|
262
|
+
fallbackField<T>(origStart, p, fieldPtr);
|
|
263
|
+
return p;
|
|
264
|
+
}
|
|
265
|
+
p += 2;
|
|
266
|
+
}
|
|
267
|
+
if (expDigits == 0) {
|
|
268
|
+
fallbackField<T>(origStart, expStart, fieldPtr);
|
|
269
|
+
return expStart;
|
|
270
|
+
}
|
|
271
|
+
exponent += expNeg ? -exp : exp;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
let result: f64;
|
|
276
|
+
if (
|
|
277
|
+
mantDigits <= 19 &&
|
|
278
|
+
mantissa <= MAX_EXACT_MANTISSA &&
|
|
279
|
+
exponent <= MAX_EXACT_POW10 &&
|
|
280
|
+
exponent >= -MAX_EXACT_POW10
|
|
281
|
+
) {
|
|
282
|
+
result = <f64>mantissa;
|
|
283
|
+
if (exponent > 0) {
|
|
284
|
+
result *= loadPow10(<u32>exponent);
|
|
285
|
+
} else if (exponent < 0) {
|
|
286
|
+
result /= loadPow10(<u32>-exponent);
|
|
287
|
+
}
|
|
288
|
+
} else if (mantDigits <= 19) {
|
|
289
|
+
result = scientific(mantissa, exponent);
|
|
290
|
+
} else {
|
|
291
|
+
fallbackField<T>(origStart, p, fieldPtr);
|
|
292
|
+
return p;
|
|
293
|
+
}
|
|
294
|
+
if (negative) result = -result;
|
|
295
|
+
|
|
296
|
+
if (sizeof<T>() == sizeof<f32>()) {
|
|
297
|
+
store<f32>(fieldPtr, <f32>result);
|
|
298
|
+
} else {
|
|
299
|
+
store<f64>(fieldPtr, result);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return p;
|
|
303
|
+
}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// SIMD integer deserializers (signed + unsigned) over UTF-16 sources.
|
|
2
|
+
//
|
|
3
|
+
// Consume-to-end paths use the full tiered hierarchy:
|
|
4
|
+
//
|
|
5
|
+
// - parse16_SIMD (32 bytes / 16 digits): best for long inputs
|
|
6
|
+
// - parse8_SIMD (16 bytes / 8 digits)
|
|
7
|
+
// - parse4_SWAR ( 8 bytes / 4 digits): SWAR is fine for short tails
|
|
8
|
+
// - scalar ( 2 bytes / 1 digit )
|
|
9
|
+
//
|
|
10
|
+
// Scan paths use the same asymmetric tuning as the SWAR version:
|
|
11
|
+
//
|
|
12
|
+
// - Unsigned scan: parse8_SIMD + scalar (no parse4).
|
|
13
|
+
// - Signed scan: parse4_SWAR + scalar (no parse8). After the leading
|
|
14
|
+
// minus, the digit run lands in parse8's "terminator-in-load" boundary
|
|
15
|
+
// zone.
|
|
16
|
+
//
|
|
17
|
+
// Requires `--enable simd` at compile time. Dead-code-eliminated when
|
|
18
|
+
// JSON_MODE != SIMD.
|
|
19
|
+
|
|
20
|
+
import {
|
|
21
|
+
parse4Digits_PairMul,
|
|
22
|
+
parse4Digits_PairMul_Unsafe,
|
|
23
|
+
} from "../../util/swar-int";
|
|
24
|
+
import {
|
|
25
|
+
parse8Digits_SIMD,
|
|
26
|
+
parse8Digits_SIMD_Unsafe,
|
|
27
|
+
parse16Digits_SIMD,
|
|
28
|
+
parse16Digits_SIMD_Unsafe,
|
|
29
|
+
} from "../../util/simd-int";
|
|
30
|
+
|
|
31
|
+
const ASCII_MINUS: u16 = 45;
|
|
32
|
+
const ASCII_ZERO: u16 = 48;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Store a signed value into a typed integer field, truncating to `T`'s width.
|
|
36
|
+
*
|
|
37
|
+
* @param dstPtr Destination pointer (already includes any field offset).
|
|
38
|
+
* @param value The `u64` accumulator, interpreted as a two's-complement
|
|
39
|
+
* signed integer for narrower types.
|
|
40
|
+
*/
|
|
41
|
+
// @ts-expect-error: @inline is a valid decorator
|
|
42
|
+
@inline function storeSignedToField<T extends number>(
|
|
43
|
+
dstPtr: usize,
|
|
44
|
+
value: u64,
|
|
45
|
+
): void {
|
|
46
|
+
if (sizeof<T>() == 1) {
|
|
47
|
+
store<i8>(dstPtr, <i8>value);
|
|
48
|
+
} else if (sizeof<T>() == 2) {
|
|
49
|
+
store<i16>(dstPtr, <i16>value);
|
|
50
|
+
} else if (sizeof<T>() == 4) {
|
|
51
|
+
store<i32>(dstPtr, <i32>value);
|
|
52
|
+
} else {
|
|
53
|
+
store<i64>(dstPtr, <i64>value);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Store an unsigned value into a typed integer field, truncating to `T`'s
|
|
59
|
+
* width.
|
|
60
|
+
*
|
|
61
|
+
* @param dstPtr Destination pointer (already includes any field offset).
|
|
62
|
+
* @param value The `u64` accumulator.
|
|
63
|
+
*/
|
|
64
|
+
// @ts-expect-error: decorator valid here
|
|
65
|
+
@inline function storeUnsignedToField<T extends number>(
|
|
66
|
+
dstPtr: usize,
|
|
67
|
+
value: u64,
|
|
68
|
+
): void {
|
|
69
|
+
if (sizeof<T>() == 1) {
|
|
70
|
+
store<u8>(dstPtr, <u8>value);
|
|
71
|
+
} else if (sizeof<T>() == 2) {
|
|
72
|
+
store<u16>(dstPtr, <u16>value);
|
|
73
|
+
} else if (sizeof<T>() == 4) {
|
|
74
|
+
store<u32>(dstPtr, <u32>value);
|
|
75
|
+
} else {
|
|
76
|
+
store<u64>(dstPtr, value);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Parse a signed integer by consuming the entire `[srcStart, srcEnd)` range
|
|
82
|
+
* as a digit run, with an optional leading `-`.
|
|
83
|
+
*
|
|
84
|
+
* Caller guarantees the range is well-formed. Uses the unsafe SIMD kernels
|
|
85
|
+
* with no per-stride validation.
|
|
86
|
+
*
|
|
87
|
+
* @param srcStart Pointer to the first UTF-16 code unit.
|
|
88
|
+
* @param srcEnd Pointer just past the last code unit.
|
|
89
|
+
* @returns The parsed value, two's-complement truncated to `T`.
|
|
90
|
+
*/
|
|
91
|
+
// @ts-expect-error: decorator valid here
|
|
92
|
+
@inline export function deserializeInteger_SIMD<T extends number>(
|
|
93
|
+
srcStart: usize,
|
|
94
|
+
srcEnd: usize,
|
|
95
|
+
): T {
|
|
96
|
+
let negative = false;
|
|
97
|
+
if (srcStart < srcEnd && load<u16>(srcStart) == ASCII_MINUS) {
|
|
98
|
+
negative = true;
|
|
99
|
+
srcStart += 2;
|
|
100
|
+
}
|
|
101
|
+
let value: u64 = 0;
|
|
102
|
+
while (srcStart + 30 < srcEnd) {
|
|
103
|
+
value =
|
|
104
|
+
value * 10_000_000_000_000_000 + parse16Digits_SIMD_Unsafe(srcStart);
|
|
105
|
+
srcStart += 32;
|
|
106
|
+
}
|
|
107
|
+
while (srcStart + 14 < srcEnd) {
|
|
108
|
+
value = value * 100_000_000 + parse8Digits_SIMD_Unsafe(srcStart);
|
|
109
|
+
srcStart += 16;
|
|
110
|
+
}
|
|
111
|
+
while (srcStart + 6 < srcEnd) {
|
|
112
|
+
value = value * 10_000 + parse4Digits_PairMul_Unsafe(load<u64>(srcStart));
|
|
113
|
+
srcStart += 8;
|
|
114
|
+
}
|
|
115
|
+
while (srcStart < srcEnd) {
|
|
116
|
+
value = value * 10 + (<u32>load<u16>(srcStart) - ASCII_ZERO);
|
|
117
|
+
srcStart += 2;
|
|
118
|
+
}
|
|
119
|
+
return <T>(negative ? 0 - value : value);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Scan for a signed integer field, stopping at the first non-digit
|
|
124
|
+
* character. Writes the parsed value through `dstObj + dstOffset` and
|
|
125
|
+
* returns the source position immediately after the last digit.
|
|
126
|
+
*
|
|
127
|
+
* @param srcStart Pointer to the first UTF-16 code unit.
|
|
128
|
+
* @param srcEnd Pointer just past the last code unit.
|
|
129
|
+
* @param dstObj Destination object pointer.
|
|
130
|
+
* @param dstOffset Byte offset of the field within `dstObj`.
|
|
131
|
+
* @returns The source position immediately after the last digit consumed.
|
|
132
|
+
*/
|
|
133
|
+
// @ts-expect-error: @inline is a valid decorator
|
|
134
|
+
@inline export function deserializeIntegerField_SIMD<T extends number>(
|
|
135
|
+
srcStart: usize,
|
|
136
|
+
srcEnd: usize,
|
|
137
|
+
dstObj: usize,
|
|
138
|
+
dstOffset: usize = 0,
|
|
139
|
+
): usize {
|
|
140
|
+
let negative = false;
|
|
141
|
+
if (srcStart < srcEnd && load<u16>(srcStart) == ASCII_MINUS) {
|
|
142
|
+
negative = true;
|
|
143
|
+
srcStart += 2;
|
|
144
|
+
}
|
|
145
|
+
let value: u64 = 0;
|
|
146
|
+
// Signed scan uses parse4 + scalar only (see file header).
|
|
147
|
+
while (srcStart + 6 < srcEnd) {
|
|
148
|
+
const parsed = parse4Digits_PairMul(load<u64>(srcStart));
|
|
149
|
+
if (parsed == U32.MAX_VALUE) break;
|
|
150
|
+
value = value * 10_000 + parsed;
|
|
151
|
+
srcStart += 8;
|
|
152
|
+
}
|
|
153
|
+
while (srcStart < srcEnd) {
|
|
154
|
+
const digit = <u32>load<u16>(srcStart) - ASCII_ZERO;
|
|
155
|
+
if (digit > 9) break;
|
|
156
|
+
value = value * 10 + digit;
|
|
157
|
+
srcStart += 2;
|
|
158
|
+
}
|
|
159
|
+
storeSignedToField<T>(dstObj + dstOffset, negative ? 0 - value : value);
|
|
160
|
+
return srcStart;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Parse an unsigned integer by consuming the entire `[srcStart, srcEnd)`
|
|
165
|
+
* range as a digit run.
|
|
166
|
+
*
|
|
167
|
+
* Caller guarantees the range is digits only. Uses the unsafe SIMD kernels.
|
|
168
|
+
*
|
|
169
|
+
* @param srcStart Pointer to the first UTF-16 code unit.
|
|
170
|
+
* @param srcEnd Pointer just past the last code unit.
|
|
171
|
+
* @returns The parsed value, truncated to `T`.
|
|
172
|
+
*/
|
|
173
|
+
// @ts-expect-error: @inline is a valid decorator
|
|
174
|
+
@inline export function deserializeUnsigned_SIMD<T extends number>(
|
|
175
|
+
srcStart: usize,
|
|
176
|
+
srcEnd: usize,
|
|
177
|
+
): T {
|
|
178
|
+
let value: u64 = 0;
|
|
179
|
+
while (srcStart + 30 < srcEnd) {
|
|
180
|
+
value =
|
|
181
|
+
value * 10_000_000_000_000_000 + parse16Digits_SIMD_Unsafe(srcStart);
|
|
182
|
+
srcStart += 32;
|
|
183
|
+
}
|
|
184
|
+
while (srcStart + 14 < srcEnd) {
|
|
185
|
+
value = value * 100_000_000 + parse8Digits_SIMD_Unsafe(srcStart);
|
|
186
|
+
srcStart += 16;
|
|
187
|
+
}
|
|
188
|
+
while (srcStart + 6 < srcEnd) {
|
|
189
|
+
value = value * 10_000 + parse4Digits_PairMul_Unsafe(load<u64>(srcStart));
|
|
190
|
+
srcStart += 8;
|
|
191
|
+
}
|
|
192
|
+
while (srcStart < srcEnd) {
|
|
193
|
+
value = value * 10 + (<u32>load<u16>(srcStart) - ASCII_ZERO);
|
|
194
|
+
srcStart += 2;
|
|
195
|
+
}
|
|
196
|
+
return <T>value;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Scan for an unsigned integer field, stopping at the first non-digit
|
|
201
|
+
* character. Writes the parsed value through `dstObj + dstOffset` and
|
|
202
|
+
* returns the source position immediately after the last digit.
|
|
203
|
+
*
|
|
204
|
+
* @param srcStart Pointer to the first UTF-16 code unit.
|
|
205
|
+
* @param srcEnd Pointer just past the last code unit.
|
|
206
|
+
* @param dstObj Destination object pointer.
|
|
207
|
+
* @param dstOffset Byte offset of the field within `dstObj`.
|
|
208
|
+
* @returns The source position immediately after the last digit consumed.
|
|
209
|
+
*/
|
|
210
|
+
// @ts-expect-error: @inline is a valid decorator
|
|
211
|
+
@inline export function deserializeUnsignedField_SIMD<T extends number>(
|
|
212
|
+
srcStart: usize,
|
|
213
|
+
srcEnd: usize,
|
|
214
|
+
dstObj: usize,
|
|
215
|
+
dstOffset: usize = 0,
|
|
216
|
+
): usize {
|
|
217
|
+
let value: u64 = 0;
|
|
218
|
+
// Unsigned scan uses parse8 + scalar only (see file header).
|
|
219
|
+
while (srcStart + 14 < srcEnd) {
|
|
220
|
+
const parsed = parse8Digits_SIMD(srcStart);
|
|
221
|
+
if (parsed == U32.MAX_VALUE) break;
|
|
222
|
+
value = value * 100_000_000 + parsed;
|
|
223
|
+
srcStart += 16;
|
|
224
|
+
}
|
|
225
|
+
while (srcStart < srcEnd) {
|
|
226
|
+
const digit = <u32>load<u16>(srcStart) - ASCII_ZERO;
|
|
227
|
+
if (digit > 9) break;
|
|
228
|
+
value = value * 10 + digit;
|
|
229
|
+
srcStart += 2;
|
|
230
|
+
}
|
|
231
|
+
storeUnsignedToField<T>(dstObj + dstOffset, value);
|
|
232
|
+
return srcStart;
|
|
233
|
+
}
|