json-as 1.4.0 → 1.6.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.
- package/CHANGELOG.md +54 -29
- package/README.md +126 -33
- package/assembly/custom/chars.ts +39 -78
- package/assembly/deserialize/error.ts +21 -0
- package/assembly/deserialize/index/arbitrary.ts +50 -11
- package/assembly/deserialize/index/array.ts +2 -1
- package/assembly/deserialize/index/bool.ts +1 -1
- package/assembly/deserialize/index/float.ts +2 -4
- package/assembly/deserialize/index/integer.ts +2 -4
- package/assembly/deserialize/index/object.ts +9 -1
- package/assembly/deserialize/index/string.ts +39 -7
- package/assembly/deserialize/index/unsigned.ts +2 -4
- package/assembly/deserialize/index.ts +0 -1
- package/assembly/deserialize/naive/array/arbitrary.ts +3 -2
- package/assembly/deserialize/naive/array/array.ts +1 -0
- package/assembly/deserialize/naive/array/bool.ts +21 -11
- package/assembly/deserialize/naive/array/box.ts +13 -33
- package/assembly/deserialize/naive/array/float.ts +23 -13
- package/assembly/deserialize/naive/array/integer.ts +34 -22
- package/assembly/deserialize/naive/array/map.ts +1 -1
- package/assembly/deserialize/naive/array/object.ts +2 -1
- package/assembly/deserialize/naive/array/string.ts +29 -16
- package/assembly/deserialize/naive/array/struct.ts +19 -1
- package/assembly/deserialize/naive/bool.ts +18 -10
- package/assembly/deserialize/naive/date.ts +1 -2
- package/assembly/deserialize/naive/float.ts +3 -9
- package/assembly/deserialize/naive/integer.ts +1 -2
- package/assembly/deserialize/naive/map.ts +342 -43
- package/assembly/deserialize/naive/object.ts +250 -55
- package/assembly/deserialize/naive/raw.ts +10 -2
- package/assembly/deserialize/naive/set.ts +2 -5
- package/assembly/deserialize/naive/staticarray.ts +4 -4
- package/assembly/deserialize/naive/string.ts +46 -60
- package/assembly/deserialize/naive/unsigned.ts +1 -2
- package/assembly/deserialize/parseMode.ts +216 -0
- package/assembly/deserialize/simd/array/integer.ts +8 -10
- package/assembly/deserialize/simd/float.ts +38 -34
- package/assembly/deserialize/simd/integer.ts +2 -7
- package/assembly/deserialize/simd/string.ts +84 -39
- package/assembly/deserialize/string-validation.ts +34 -0
- package/assembly/deserialize/swar/array/arbitrary.ts +1 -2
- package/assembly/deserialize/swar/array/array.ts +64 -15
- package/assembly/deserialize/swar/array/bool.ts +15 -5
- package/assembly/deserialize/swar/array/box.ts +1 -2
- package/assembly/deserialize/swar/array/float.ts +399 -30
- package/assembly/deserialize/swar/array/generic.ts +13 -4
- package/assembly/deserialize/swar/array/integer.ts +29 -78
- package/assembly/deserialize/swar/array/map.ts +1 -2
- package/assembly/deserialize/swar/array/object.ts +12 -4
- package/assembly/deserialize/swar/array/raw.ts +1 -2
- package/assembly/deserialize/swar/array/shared.ts +7 -14
- package/assembly/deserialize/swar/array/string.ts +37 -13
- package/assembly/deserialize/swar/array/struct.ts +109 -21
- package/assembly/deserialize/swar/array.ts +1 -3
- package/assembly/deserialize/swar/float.ts +23 -21
- package/assembly/deserialize/swar/integer.ts +2 -7
- package/assembly/deserialize/swar/string.ts +81 -124
- package/assembly/deserialize/swar/typedarray.ts +4 -4
- package/assembly/index.d.ts +35 -24
- package/assembly/index.ts +1775 -281
- package/assembly/serialize/index/arbitrary.ts +70 -4
- package/assembly/serialize/index/bool.ts +1 -1
- package/assembly/serialize/index/float.ts +1 -5
- package/assembly/serialize/index/integer.ts +1 -1
- package/assembly/serialize/index/jsonarray.ts +51 -0
- package/assembly/serialize/index/object.ts +42 -3
- package/assembly/serialize/index/string.ts +1 -2
- package/assembly/serialize/index/typedarray.ts +1 -2
- package/assembly/serialize/index.ts +1 -0
- package/assembly/serialize/naive/array.ts +87 -34
- package/assembly/serialize/naive/bool.ts +0 -1
- package/assembly/serialize/naive/float.ts +15 -29
- package/assembly/serialize/naive/integer.ts +1 -5
- package/assembly/serialize/naive/map.ts +37 -0
- package/assembly/serialize/naive/raw.ts +1 -2
- package/assembly/serialize/naive/set.ts +0 -5
- package/assembly/serialize/naive/staticarray.ts +0 -6
- package/assembly/serialize/naive/string.ts +1 -10
- package/assembly/serialize/naive/typedarray.ts +0 -7
- package/assembly/serialize/simd/string.ts +1 -3
- package/assembly/serialize/swar/string.ts +1 -2
- package/assembly/util/atoi-fast.ts +4 -14
- package/assembly/util/bytes.ts +1 -2
- package/assembly/util/eisel-lemire.ts +179 -0
- package/assembly/util/idofd.ts +1 -2
- package/assembly/util/isSpace.ts +1 -2
- package/assembly/util/itoa-fast.ts +6 -9
- package/assembly/util/nextPowerOf2.ts +1 -2
- package/assembly/util/parsefloat-fast.ts +3 -5
- package/assembly/util/prettyWhitespaceSimd.ts +69 -0
- package/assembly/util/ptrToStr.ts +1 -2
- package/assembly/util/scanValueEndSimd.ts +83 -22
- package/assembly/util/scanValueEndSwar.ts +67 -25
- package/assembly/util/scientific.ts +5 -8
- package/assembly/util/snp.ts +1 -2
- package/assembly/util/swar-int.ts +5 -10
- package/assembly/util/swar.ts +2 -4
- package/assembly/util/validateJson.ts +338 -0
- package/lib/as-bs.ts +51 -51
- package/package.json +25 -8
- package/transform/lib/index.d.ts +6 -0
- package/transform/lib/index.js +874 -171
- package/transform/lib/types.d.ts +3 -1
- package/transform/lib/types.js +6 -0
- package/assembly/deserialize/index/struct.ts +0 -1
- package/assembly/deserialize/naive/struct.ts +0 -21
- package/assembly/util/dragonbox-cache.ts +0 -445
- package/assembly/util/dragonbox.ts +0 -652
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
let SIMD_PRETTY_PARSE = false;
|
|
2
|
+
const ENABLE_EXACT_SOURCE_TRACES = true;
|
|
3
|
+
|
|
4
|
+
// Steady-state SIMD string-field trace. JSON source strings and parsed field
|
|
5
|
+
// strings are immutable at the language level, so the tuple
|
|
6
|
+
// (source position, destination slot, current string reference) proves that a
|
|
7
|
+
// field still contains the bytes produced by the prior parse. Any mutation or
|
|
8
|
+
// graph replacement changes at least one tuple component and takes the normal
|
|
9
|
+
// decoder, which refreshes that trace slot.
|
|
10
|
+
let STRING_TRACE_DEPTH = 0;
|
|
11
|
+
let STRING_TRACE_ACTIVE = false;
|
|
12
|
+
let STRING_TRACE_COMPLETE = false;
|
|
13
|
+
let STRING_TRACE_SOURCE: string | null = null;
|
|
14
|
+
let STRING_TRACE_OUT: usize = 0;
|
|
15
|
+
let STRING_TRACE_TYPE: u32 = 0;
|
|
16
|
+
let STRING_TRACE_INDEX = 0;
|
|
17
|
+
let STRING_TRACE_COUNT = 0;
|
|
18
|
+
let STRING_TRACE_SLOT = 0;
|
|
19
|
+
const STRING_TRACE_FIELDS = new Array<usize>();
|
|
20
|
+
const STRING_TRACE_STARTS = new Array<usize>();
|
|
21
|
+
const STRING_TRACE_ENDS = new Array<usize>();
|
|
22
|
+
const STRING_TRACE_VALUES = new Array<string>();
|
|
23
|
+
let OBJECT_TRACE_INDEX = 0;
|
|
24
|
+
let OBJECT_TRACE_COUNT = 0;
|
|
25
|
+
const OBJECT_TRACE_FIELDS = new Array<usize>();
|
|
26
|
+
const OBJECT_TRACE_STARTS = new Array<usize>();
|
|
27
|
+
const OBJECT_TRACE_TYPES = new Array<string>();
|
|
28
|
+
const OBJECT_TRACE_MASKS = new Array<u64>();
|
|
29
|
+
const OBJECT_TRACE_TIERS = new Array<u8>();
|
|
30
|
+
const OBJECT_TRACE_SEPARATORS = new Array<usize>();
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
@inline
|
|
34
|
+
export function isPrettyParse(): bool {
|
|
35
|
+
return SIMD_PRETTY_PARSE;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@inline
|
|
40
|
+
export function setPrettyParse(value: bool): void {
|
|
41
|
+
SIMD_PRETTY_PARSE = value;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/** Enter one public parse call, suspending trace use in nested parses. */
|
|
45
|
+
@inline
|
|
46
|
+
export function beginStringFieldTrace(
|
|
47
|
+
source: string,
|
|
48
|
+
out: usize,
|
|
49
|
+
typeId: u32,
|
|
50
|
+
): void {
|
|
51
|
+
if (!ENABLE_EXACT_SOURCE_TRACES) return;
|
|
52
|
+
STRING_TRACE_DEPTH++;
|
|
53
|
+
if (!ASC_FEATURE_SIMD || STRING_TRACE_DEPTH != 1 || out == 0) return;
|
|
54
|
+
|
|
55
|
+
const sourcePtr = changetype<usize>(source);
|
|
56
|
+
const sameRoot =
|
|
57
|
+
STRING_TRACE_COMPLETE &&
|
|
58
|
+
STRING_TRACE_OUT == out &&
|
|
59
|
+
STRING_TRACE_TYPE == typeId &&
|
|
60
|
+
changetype<usize>(STRING_TRACE_SOURCE) == sourcePtr;
|
|
61
|
+
if (!sameRoot) {
|
|
62
|
+
STRING_TRACE_SOURCE = source;
|
|
63
|
+
STRING_TRACE_OUT = out;
|
|
64
|
+
STRING_TRACE_TYPE = typeId;
|
|
65
|
+
STRING_TRACE_COUNT = 0;
|
|
66
|
+
STRING_TRACE_FIELDS.length = 0;
|
|
67
|
+
STRING_TRACE_STARTS.length = 0;
|
|
68
|
+
STRING_TRACE_ENDS.length = 0;
|
|
69
|
+
STRING_TRACE_VALUES.length = 0;
|
|
70
|
+
OBJECT_TRACE_COUNT = 0;
|
|
71
|
+
OBJECT_TRACE_FIELDS.length = 0;
|
|
72
|
+
OBJECT_TRACE_STARTS.length = 0;
|
|
73
|
+
OBJECT_TRACE_TYPES.length = 0;
|
|
74
|
+
OBJECT_TRACE_MASKS.length = 0;
|
|
75
|
+
OBJECT_TRACE_TIERS.length = 0;
|
|
76
|
+
OBJECT_TRACE_SEPARATORS.length = 0;
|
|
77
|
+
}
|
|
78
|
+
STRING_TRACE_INDEX = 0;
|
|
79
|
+
OBJECT_TRACE_INDEX = 0;
|
|
80
|
+
STRING_TRACE_COMPLETE = false;
|
|
81
|
+
STRING_TRACE_ACTIVE = true;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** Leave the matching public parse call. */
|
|
85
|
+
@inline
|
|
86
|
+
export function endStringFieldTrace(success: bool): void {
|
|
87
|
+
if (!ENABLE_EXACT_SOURCE_TRACES) return;
|
|
88
|
+
if (STRING_TRACE_DEPTH == 1) {
|
|
89
|
+
STRING_TRACE_ACTIVE = false;
|
|
90
|
+
STRING_TRACE_COMPLETE = success;
|
|
91
|
+
}
|
|
92
|
+
STRING_TRACE_DEPTH--;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Return the cached cursor, or zero when this field must be decoded. */
|
|
96
|
+
@inline
|
|
97
|
+
export function probeStringFieldTrace(
|
|
98
|
+
dstFieldPtr: usize,
|
|
99
|
+
payloadStart: usize,
|
|
100
|
+
): usize {
|
|
101
|
+
if (!ENABLE_EXACT_SOURCE_TRACES) return 0;
|
|
102
|
+
if (!STRING_TRACE_ACTIVE || STRING_TRACE_DEPTH != 1) return 0;
|
|
103
|
+
const slot = STRING_TRACE_INDEX++;
|
|
104
|
+
STRING_TRACE_SLOT = slot;
|
|
105
|
+
if (
|
|
106
|
+
slot < STRING_TRACE_COUNT &&
|
|
107
|
+
unchecked(STRING_TRACE_FIELDS[slot]) == dstFieldPtr &&
|
|
108
|
+
unchecked(STRING_TRACE_STARTS[slot]) == payloadStart &&
|
|
109
|
+
load<usize>(dstFieldPtr) ==
|
|
110
|
+
changetype<usize>(unchecked(STRING_TRACE_VALUES[slot]))
|
|
111
|
+
)
|
|
112
|
+
return unchecked(STRING_TRACE_ENDS[slot]);
|
|
113
|
+
return 0;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** Record the successful decode corresponding to the most recent probe. */
|
|
117
|
+
@inline
|
|
118
|
+
export function recordStringFieldTrace(
|
|
119
|
+
dstFieldPtr: usize,
|
|
120
|
+
payloadStart: usize,
|
|
121
|
+
next: usize,
|
|
122
|
+
): void {
|
|
123
|
+
if (!ENABLE_EXACT_SOURCE_TRACES) return;
|
|
124
|
+
if (!STRING_TRACE_ACTIVE || STRING_TRACE_DEPTH != 1) return;
|
|
125
|
+
const slot = STRING_TRACE_SLOT;
|
|
126
|
+
const value = load<string>(dstFieldPtr);
|
|
127
|
+
if (slot < STRING_TRACE_COUNT) {
|
|
128
|
+
unchecked((STRING_TRACE_FIELDS[slot] = dstFieldPtr));
|
|
129
|
+
unchecked((STRING_TRACE_STARTS[slot] = payloadStart));
|
|
130
|
+
unchecked((STRING_TRACE_ENDS[slot] = next));
|
|
131
|
+
unchecked((STRING_TRACE_VALUES[slot] = value));
|
|
132
|
+
} else {
|
|
133
|
+
STRING_TRACE_FIELDS.push(dstFieldPtr);
|
|
134
|
+
STRING_TRACE_STARTS.push(payloadStart);
|
|
135
|
+
STRING_TRACE_ENDS.push(next);
|
|
136
|
+
STRING_TRACE_VALUES.push(value);
|
|
137
|
+
STRING_TRACE_COUNT = slot + 1;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Reserve or find one optional-struct trace slot.
|
|
143
|
+
*
|
|
144
|
+
* Returns i32.MIN_VALUE when tracing is inactive, a non-negative slot on a
|
|
145
|
+
* miss, or `~slot` (negative) on a hit. Reserving at begin keeps nested object
|
|
146
|
+
* records well-ordered even though a parent only commits after its children.
|
|
147
|
+
*/
|
|
148
|
+
@inline
|
|
149
|
+
export function beginObjectFieldTrace(
|
|
150
|
+
dst: usize,
|
|
151
|
+
srcStart: usize,
|
|
152
|
+
typeTag: string,
|
|
153
|
+
): i32 {
|
|
154
|
+
if (!ENABLE_EXACT_SOURCE_TRACES) return i32.MIN_VALUE;
|
|
155
|
+
if (!STRING_TRACE_ACTIVE || STRING_TRACE_DEPTH != 1) return i32.MIN_VALUE;
|
|
156
|
+
const slot = OBJECT_TRACE_INDEX++;
|
|
157
|
+
if (
|
|
158
|
+
slot < OBJECT_TRACE_COUNT &&
|
|
159
|
+
unchecked(OBJECT_TRACE_FIELDS[slot]) == dst &&
|
|
160
|
+
unchecked(OBJECT_TRACE_STARTS[slot]) == srcStart &&
|
|
161
|
+
unchecked(OBJECT_TRACE_TIERS[slot]) != 0 &&
|
|
162
|
+
changetype<usize>(unchecked(OBJECT_TRACE_TYPES[slot])) ==
|
|
163
|
+
changetype<usize>(typeTag)
|
|
164
|
+
)
|
|
165
|
+
return ~slot;
|
|
166
|
+
|
|
167
|
+
if (slot < OBJECT_TRACE_COUNT) {
|
|
168
|
+
unchecked((OBJECT_TRACE_FIELDS[slot] = dst));
|
|
169
|
+
unchecked((OBJECT_TRACE_STARTS[slot] = srcStart));
|
|
170
|
+
unchecked((OBJECT_TRACE_TYPES[slot] = typeTag));
|
|
171
|
+
unchecked((OBJECT_TRACE_MASKS[slot] = 0));
|
|
172
|
+
unchecked((OBJECT_TRACE_TIERS[slot] = 0));
|
|
173
|
+
unchecked((OBJECT_TRACE_SEPARATORS[slot] = 0));
|
|
174
|
+
} else {
|
|
175
|
+
OBJECT_TRACE_FIELDS.push(dst);
|
|
176
|
+
OBJECT_TRACE_STARTS.push(srcStart);
|
|
177
|
+
OBJECT_TRACE_TYPES.push(typeTag);
|
|
178
|
+
OBJECT_TRACE_MASKS.push(0);
|
|
179
|
+
OBJECT_TRACE_TIERS.push(0);
|
|
180
|
+
OBJECT_TRACE_SEPARATORS.push(0);
|
|
181
|
+
OBJECT_TRACE_COUNT = slot + 1;
|
|
182
|
+
}
|
|
183
|
+
return slot;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
@inline
|
|
188
|
+
export function objectFieldTraceMask(slot: i32): u64 {
|
|
189
|
+
return unchecked(OBJECT_TRACE_MASKS[slot]);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
@inline
|
|
194
|
+
export function objectFieldTraceTier(slot: i32): u8 {
|
|
195
|
+
return unchecked(OBJECT_TRACE_TIERS[slot]);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
@inline
|
|
200
|
+
export function objectFieldTraceSeparator(slot: i32): usize {
|
|
201
|
+
return unchecked(OBJECT_TRACE_SEPARATORS[slot]);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
@inline
|
|
206
|
+
export function recordObjectFieldTrace(
|
|
207
|
+
slot: i32,
|
|
208
|
+
present: u64,
|
|
209
|
+
tier: u8,
|
|
210
|
+
separator: usize = 0,
|
|
211
|
+
): void {
|
|
212
|
+
if (slot < 0 || slot >= OBJECT_TRACE_COUNT) return;
|
|
213
|
+
unchecked((OBJECT_TRACE_MASKS[slot] = present));
|
|
214
|
+
unchecked((OBJECT_TRACE_TIERS[slot] = tier));
|
|
215
|
+
unchecked((OBJECT_TRACE_SEPARATORS[slot] = separator));
|
|
216
|
+
}
|
|
@@ -40,11 +40,7 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
|
|
|
40
40
|
// @ts-expect-error: decorators valid here
|
|
41
41
|
@lazy const PAIR_WEIGHTS_100_1 = i16x8(100, 1, 100, 1, 0, 0, 0, 0);
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
@inline function storeSignedInteger<T extends number[]>(
|
|
45
|
-
slot: usize,
|
|
46
|
-
value: i64,
|
|
47
|
-
): void {
|
|
43
|
+
function storeSignedInteger<T extends number[]>(slot: usize, value: i64): void {
|
|
48
44
|
if (sizeof<valueof<T>>() == sizeof<i8>()) {
|
|
49
45
|
store<i8>(slot, <i8>value);
|
|
50
46
|
} else if (sizeof<valueof<T>>() == sizeof<i16>()) {
|
|
@@ -58,8 +54,7 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
|
|
|
58
54
|
}
|
|
59
55
|
}
|
|
60
56
|
|
|
61
|
-
|
|
62
|
-
@inline function storeUnsignedInteger<T extends number[]>(
|
|
57
|
+
function storeUnsignedInteger<T extends number[]>(
|
|
63
58
|
slot: usize,
|
|
64
59
|
value: u64,
|
|
65
60
|
): void {
|
|
@@ -402,7 +397,10 @@ export function deserializeIntegerArray_SIMD<T extends number[]>(
|
|
|
402
397
|
dst || changetype<usize>(instantiate<T>()),
|
|
403
398
|
);
|
|
404
399
|
const originalSrcStart = srcStart;
|
|
405
|
-
const reusableLength =
|
|
400
|
+
const reusableLength = load<i32>(
|
|
401
|
+
changetype<usize>(out),
|
|
402
|
+
offsetof<T>("length_"),
|
|
403
|
+
);
|
|
406
404
|
|
|
407
405
|
if (reusableLength != 0) {
|
|
408
406
|
const dataStart = out.dataStart;
|
|
@@ -460,7 +458,7 @@ export function deserializeIntegerArray_SIMD<T extends number[]>(
|
|
|
460
458
|
continue;
|
|
461
459
|
}
|
|
462
460
|
if (code == BRACKET_RIGHT) {
|
|
463
|
-
out.length = index;
|
|
461
|
+
if (index != reusableLength) out.length = index;
|
|
464
462
|
return out;
|
|
465
463
|
}
|
|
466
464
|
break;
|
|
@@ -499,7 +497,7 @@ export function deserializeIntegerArray_SIMD<T extends number[]>(
|
|
|
499
497
|
continue;
|
|
500
498
|
}
|
|
501
499
|
if (code == BRACKET_RIGHT) {
|
|
502
|
-
out.length = index;
|
|
500
|
+
if (index != reusableLength) out.length = index;
|
|
503
501
|
return out;
|
|
504
502
|
}
|
|
505
503
|
break;
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
// SIMD float deserializers. Same Lemire
|
|
2
|
-
//
|
|
3
|
-
// `
|
|
4
|
-
//
|
|
1
|
+
// SIMD float deserializers. Same Eisel-Lemire / `scientific()` / `f*.parse`
|
|
2
|
+
// cascade as `swar/float.ts`, but the fractional digit loop starts with a
|
|
3
|
+
// `parse16Digits_SIMD` stride before falling through to `parse4Digits_PairMul`
|
|
4
|
+
// and finally the scalar tail.
|
|
5
5
|
//
|
|
6
|
-
// Output is bit-identical to `f64.parse` / `f32.parse` for every input
|
|
6
|
+
// Output is bit-identical to `f64.parse` / `f32.parse` for every input - the
|
|
7
7
|
// SIMD strides only change how the u64 mantissa is accumulated, not what it
|
|
8
8
|
// becomes.
|
|
9
9
|
//
|
|
@@ -14,6 +14,7 @@ import { ptrToStr } from "../../util/ptrToStr";
|
|
|
14
14
|
import { parse4Digits_PairMul } from "../../util/swar-int";
|
|
15
15
|
import { parse16Digits_SIMD } from "../../util/simd-int";
|
|
16
16
|
import { scientific } from "../../util/scientific";
|
|
17
|
+
import { eiselLemire22 } from "../../util/eisel-lemire";
|
|
17
18
|
import { loadPow10, MAX_EXACT_MANTISSA, MAX_EXACT_POW10 } from "../swar/float";
|
|
18
19
|
|
|
19
20
|
const ASCII_PLUS: u16 = 43;
|
|
@@ -23,8 +24,7 @@ const ASCII_ZERO: u16 = 48;
|
|
|
23
24
|
const ASCII_E_UP: u16 = 69;
|
|
24
25
|
const ASCII_E_LO: u16 = 101;
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
@inline function fallback<T>(srcStart: usize, srcEnd: usize): T {
|
|
27
|
+
function fallback<T>(srcStart: usize, srcEnd: usize): T {
|
|
28
28
|
const s = ptrToStr(srcStart, srcEnd);
|
|
29
29
|
// @ts-ignore
|
|
30
30
|
const type: T = 0;
|
|
@@ -34,8 +34,7 @@ const ASCII_E_LO: u16 = 101;
|
|
|
34
34
|
return <T>(<f32>f32.parse(s));
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
-
|
|
38
|
-
@inline function fallbackField<T extends number>(
|
|
37
|
+
function fallbackField<T extends number>(
|
|
39
38
|
origStart: usize,
|
|
40
39
|
end: usize,
|
|
41
40
|
fieldPtr: usize,
|
|
@@ -58,48 +57,47 @@ export function deserializeFloat_SIMD<T>(srcStart: usize, srcEnd: usize): T {
|
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
let mantissa: u64 = 0;
|
|
61
|
-
|
|
60
|
+
const intStart = p;
|
|
62
61
|
while (p < srcEnd) {
|
|
63
62
|
const d = <u32>load<u16>(p) - ASCII_ZERO;
|
|
64
63
|
if (d > 9) break;
|
|
65
64
|
mantissa = mantissa * 10 + <u64>d;
|
|
66
|
-
intDigits++;
|
|
67
65
|
p += 2;
|
|
68
66
|
}
|
|
67
|
+
const intDigits = <i32>((p - intStart) >> 1);
|
|
69
68
|
|
|
70
|
-
// Fractional part:
|
|
71
|
-
//
|
|
72
|
-
|
|
73
|
-
let fracDigits: i32 = 0;
|
|
69
|
+
// Fractional part: one parse16 SIMD stride → parse4 SWAR stride →
|
|
70
|
+
// scalar tail. The parse16 gate keeps the combined mantissa within u64.
|
|
71
|
+
let fracStart = p;
|
|
74
72
|
if (p < srcEnd && load<u16>(p) == ASCII_DOT) {
|
|
75
73
|
p += 2;
|
|
74
|
+
fracStart = p;
|
|
76
75
|
// parse16 SIMD only fires on long fractions (>=16 digits ahead, with
|
|
77
76
|
// <=3 integer digits to keep mantissa * 1e16 under u64 max). Rare in
|
|
78
77
|
// typical JSON but a big win when it does fire (8 digits per stride in
|
|
79
78
|
// SWAR's parse8 was benched even/worse than parse4, so we skip parse8
|
|
80
79
|
// entirely and let parse4 handle the 4-15 char tail).
|
|
81
|
-
|
|
80
|
+
if (p + 30 < srcEnd && intDigits <= 3) {
|
|
82
81
|
const parsed = parse16Digits_SIMD(p);
|
|
83
|
-
if (parsed
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
82
|
+
if (parsed != U64.MAX_VALUE) {
|
|
83
|
+
mantissa = mantissa * 10_000_000_000_000_000 + parsed;
|
|
84
|
+
p += 32;
|
|
85
|
+
}
|
|
87
86
|
}
|
|
88
87
|
while (p + 6 < srcEnd) {
|
|
89
88
|
const parsed = parse4Digits_PairMul(load<u64>(p));
|
|
90
89
|
if (parsed == U32.MAX_VALUE) break;
|
|
91
90
|
mantissa = mantissa * 10_000 + <u64>parsed;
|
|
92
|
-
fracDigits += 4;
|
|
93
91
|
p += 8;
|
|
94
92
|
}
|
|
95
93
|
while (p < srcEnd) {
|
|
96
94
|
const d = <u32>load<u16>(p) - ASCII_ZERO;
|
|
97
95
|
if (d > 9) break;
|
|
98
96
|
mantissa = mantissa * 10 + <u64>d;
|
|
99
|
-
fracDigits++;
|
|
100
97
|
p += 2;
|
|
101
98
|
}
|
|
102
99
|
}
|
|
100
|
+
const fracDigits = <i32>((p - fracStart) >> 1);
|
|
103
101
|
|
|
104
102
|
const mantDigits = intDigits + fracDigits;
|
|
105
103
|
if (mantDigits == 0) return fallback<T>(origStart, srcEnd);
|
|
@@ -150,7 +148,10 @@ export function deserializeFloat_SIMD<T>(srcStart: usize, srcEnd: usize): T {
|
|
|
150
148
|
result /= loadPow10(<u32>-exponent);
|
|
151
149
|
}
|
|
152
150
|
} else if (mantDigits <= 19) {
|
|
153
|
-
result =
|
|
151
|
+
result =
|
|
152
|
+
exponent >= -22 && exponent <= 22
|
|
153
|
+
? eiselLemire22(mantissa, exponent)
|
|
154
|
+
: scientific(mantissa, exponent);
|
|
154
155
|
} else {
|
|
155
156
|
return fallback<T>(origStart, srcEnd);
|
|
156
157
|
}
|
|
@@ -180,45 +181,45 @@ export function deserializeFloatField_SIMD<T extends number>(
|
|
|
180
181
|
}
|
|
181
182
|
|
|
182
183
|
let mantissa: u64 = 0;
|
|
183
|
-
|
|
184
|
+
const intStart = p;
|
|
184
185
|
while (p < srcEnd) {
|
|
185
186
|
const d = <u32>load<u16>(p) - ASCII_ZERO;
|
|
186
187
|
if (d > 9) break;
|
|
187
188
|
mantissa = mantissa * 10 + <u64>d;
|
|
188
|
-
intDigits++;
|
|
189
189
|
p += 2;
|
|
190
190
|
}
|
|
191
|
+
const intDigits = <i32>((p - intStart) >> 1);
|
|
191
192
|
|
|
192
|
-
let
|
|
193
|
+
let fracStart = p;
|
|
193
194
|
if (p < srcEnd && load<u16>(p) == ASCII_DOT) {
|
|
194
195
|
p += 2;
|
|
196
|
+
fracStart = p;
|
|
195
197
|
// parse16 SIMD only fires on long fractions (>=16 digits ahead, with
|
|
196
198
|
// <=3 integer digits to keep mantissa * 1e16 under u64 max). Rare in
|
|
197
199
|
// typical JSON but a big win when it does fire (8 digits per stride in
|
|
198
200
|
// SWAR's parse8 was benched even/worse than parse4, so we skip parse8
|
|
199
201
|
// entirely and let parse4 handle the 4-15 char tail).
|
|
200
|
-
|
|
202
|
+
if (p + 30 < srcEnd && intDigits <= 3) {
|
|
201
203
|
const parsed = parse16Digits_SIMD(p);
|
|
202
|
-
if (parsed
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
204
|
+
if (parsed != U64.MAX_VALUE) {
|
|
205
|
+
mantissa = mantissa * 10_000_000_000_000_000 + parsed;
|
|
206
|
+
p += 32;
|
|
207
|
+
}
|
|
206
208
|
}
|
|
207
209
|
while (p + 6 < srcEnd) {
|
|
208
210
|
const parsed = parse4Digits_PairMul(load<u64>(p));
|
|
209
211
|
if (parsed == U32.MAX_VALUE) break;
|
|
210
212
|
mantissa = mantissa * 10_000 + <u64>parsed;
|
|
211
|
-
fracDigits += 4;
|
|
212
213
|
p += 8;
|
|
213
214
|
}
|
|
214
215
|
while (p < srcEnd) {
|
|
215
216
|
const d = <u32>load<u16>(p) - ASCII_ZERO;
|
|
216
217
|
if (d > 9) break;
|
|
217
218
|
mantissa = mantissa * 10 + <u64>d;
|
|
218
|
-
fracDigits++;
|
|
219
219
|
p += 2;
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
|
+
const fracDigits = <i32>((p - fracStart) >> 1);
|
|
222
223
|
|
|
223
224
|
const mantDigits = intDigits + fracDigits;
|
|
224
225
|
if (mantDigits == 0) unreachable();
|
|
@@ -281,7 +282,10 @@ export function deserializeFloatField_SIMD<T extends number>(
|
|
|
281
282
|
result /= loadPow10(<u32>-exponent);
|
|
282
283
|
}
|
|
283
284
|
} else if (mantDigits <= 19) {
|
|
284
|
-
result =
|
|
285
|
+
result =
|
|
286
|
+
exponent >= -22 && exponent <= 22
|
|
287
|
+
? eiselLemire22(mantissa, exponent)
|
|
288
|
+
: scientific(mantissa, exponent);
|
|
285
289
|
} else {
|
|
286
290
|
fallbackField<T>(origStart, p, fieldPtr);
|
|
287
291
|
return p;
|
|
@@ -38,11 +38,7 @@ const ASCII_ZERO: u16 = 48;
|
|
|
38
38
|
* @param value The `u64` accumulator, interpreted as a two's-complement
|
|
39
39
|
* signed integer for narrower types.
|
|
40
40
|
*/
|
|
41
|
-
|
|
42
|
-
@inline function storeSignedToField<T extends number>(
|
|
43
|
-
dstPtr: usize,
|
|
44
|
-
value: u64,
|
|
45
|
-
): void {
|
|
41
|
+
function storeSignedToField<T extends number>(dstPtr: usize, value: u64): void {
|
|
46
42
|
if (sizeof<T>() == 1) {
|
|
47
43
|
store<i8>(dstPtr, <i8>value);
|
|
48
44
|
} else if (sizeof<T>() == 2) {
|
|
@@ -61,8 +57,7 @@ const ASCII_ZERO: u16 = 48;
|
|
|
61
57
|
* @param dstPtr Destination pointer (already includes any field offset).
|
|
62
58
|
* @param value The `u64` accumulator.
|
|
63
59
|
*/
|
|
64
|
-
|
|
65
|
-
@inline function storeUnsignedToField<T extends number>(
|
|
60
|
+
function storeUnsignedToField<T extends number>(
|
|
66
61
|
dstPtr: usize,
|
|
67
62
|
value: u64,
|
|
68
63
|
): void {
|