json-as 1.3.7 → 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 +32 -0
- package/README.md +1 -1
- package/assembly/deserialize/index/arbitrary.ts +2 -2
- package/assembly/deserialize/index/array.ts +29 -14
- 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 +3 -3
- 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 +28 -3
- package/assembly/deserialize/index/struct.ts +1 -1
- package/assembly/deserialize/index/typedarray.ts +25 -15
- package/assembly/deserialize/index/unsigned.ts +3 -3
- package/assembly/deserialize/index.ts +1 -0
- package/assembly/deserialize/naive/array/bool.ts +68 -0
- package/assembly/deserialize/naive/array/float.ts +63 -0
- package/assembly/deserialize/{simple → naive}/array/generic.ts +1 -2
- package/assembly/deserialize/naive/array/integer.ts +86 -0
- package/assembly/deserialize/{simple → naive}/array/map.ts +0 -1
- package/assembly/deserialize/{simple → naive}/array/object.ts +0 -1
- package/assembly/deserialize/naive/array/string.ts +69 -0
- package/assembly/deserialize/{simple → naive}/array/struct.ts +0 -1
- package/assembly/deserialize/{simple → naive}/array.ts +6 -11
- package/assembly/deserialize/naive/float.ts +135 -0
- package/assembly/deserialize/{simple → naive}/integer.ts +2 -2
- package/assembly/deserialize/{simple → naive}/map.ts +12 -6
- package/assembly/deserialize/{simple → naive}/object.ts +4 -7
- package/assembly/deserialize/{simple → naive}/set.ts +12 -27
- 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/struct.ts +1 -2
- package/assembly/deserialize/{simple → naive}/staticarray.ts +4 -4
- package/assembly/deserialize/naive/string.ts +199 -0
- package/assembly/deserialize/{simple → naive}/typedarray.ts +4 -4
- package/assembly/deserialize/{simple → naive}/unsigned.ts +2 -2
- package/assembly/deserialize/simd/array/integer.ts +19 -19
- package/assembly/deserialize/simd/float.ts +303 -0
- package/assembly/deserialize/simd/string.ts +233 -108
- package/assembly/deserialize/swar/array/arbitrary.ts +6 -2
- package/assembly/deserialize/swar/array/array.ts +14 -7
- package/assembly/deserialize/swar/array/bool.ts +8 -3
- package/assembly/deserialize/swar/array/box.ts +6 -2
- package/assembly/deserialize/swar/array/float.ts +282 -6
- package/assembly/deserialize/swar/array/generic.ts +6 -2
- package/assembly/deserialize/swar/array/integer.ts +81 -74
- package/assembly/deserialize/swar/array/map.ts +6 -2
- package/assembly/deserialize/swar/array/object.ts +24 -32
- package/assembly/deserialize/swar/array/raw.ts +6 -2
- package/assembly/deserialize/swar/array/shared.ts +32 -8
- package/assembly/deserialize/swar/array/string.ts +127 -10
- package/assembly/deserialize/swar/array/struct.ts +45 -11
- package/assembly/deserialize/swar/array.ts +2 -56
- package/assembly/deserialize/swar/float.ts +304 -0
- package/assembly/deserialize/swar/string.ts +119 -104
- package/assembly/deserialize/swar/typedarray.ts +224 -0
- package/assembly/index.ts +203 -293
- 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 +1 -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 +2 -11
- package/assembly/serialize/index.ts +1 -0
- package/assembly/serialize/{simple → naive}/array.ts +87 -0
- package/assembly/serialize/{simple → naive}/string.ts +1 -1
- package/assembly/serialize/swar/string.ts +0 -139
- package/assembly/util/dragonbox.ts +10 -3
- package/assembly/util/itoa-fast.ts +29 -18
- package/assembly/util/scanValueEnd.ts +78 -0
- package/assembly/util/scientific.ts +132 -0
- package/lib/as-bs.ts +14 -1
- package/package.json +14 -13
- package/transform/lib/index.d.ts +4 -0
- package/transform/lib/index.d.ts.map +1 -1
- package/transform/lib/index.js +153 -236
- package/transform/lib/index.js.map +1 -1
- package/assembly/deserialize/simple/arbitrary.ts +0 -30
- package/assembly/deserialize/simple/array/bool.ts +0 -48
- package/assembly/deserialize/simple/array/float.ts +0 -55
- package/assembly/deserialize/simple/array/integer.ts +0 -33
- package/assembly/deserialize/simple/array/string.ts +0 -29
- package/assembly/deserialize/simple/float.ts +0 -206
- package/assembly/deserialize/simple/string.ts +0 -45
- package/assembly/serialize/simple/arbitrary.ts +0 -79
- package/assembly/serialize/simple/object.ts +0 -42
- /package/assembly/deserialize/{simple → naive}/array/arbitrary.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/array/array.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/array/box.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/array/raw.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/bool.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/date.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/raw.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/staticarray/string.ts +0 -0
- /package/assembly/deserialize/{simple → naive}/struct.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}/float.ts +0 -0
- /package/assembly/serialize/{simple → naive}/integer.ts +0 -0
- /package/assembly/serialize/{simple → naive}/map.ts +0 -0
- /package/assembly/serialize/{simple → naive}/raw.ts +0 -0
- /package/assembly/serialize/{simple → naive}/set.ts +0 -0
- /package/assembly/serialize/{simple → naive}/staticarray.ts +0 -0
- /package/assembly/serialize/{simple → naive}/struct.ts +0 -0
- /package/assembly/serialize/{simple → naive}/typedarray.ts +0 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import { bs } from "../../../lib/as-bs";
|
|
2
|
+
import { OBJECT, TOTAL_OVERHEAD } from "rt/common";
|
|
3
|
+
import { __heap_base } from "memory";
|
|
4
|
+
import { BACK_SLASH, QUOTE } from "../../custom/chars";
|
|
5
|
+
import { DESERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
6
|
+
|
|
7
|
+
// @ts-ignore: inline
|
|
8
|
+
@inline function hexDigit(c: u16): u32 {
|
|
9
|
+
if (c <= 0x39) return c - 0x30; // '0'-'9'
|
|
10
|
+
if (c <= 0x46) return c - 0x37; // 'A'-'F'
|
|
11
|
+
return c - 0x57; // 'a'-'f'
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// @ts-ignore: inline
|
|
15
|
+
@inline function hex4ToU16(srcStart: usize): u16 {
|
|
16
|
+
return <u16>(
|
|
17
|
+
((hexDigit(load<u16>(srcStart)) << 12) |
|
|
18
|
+
(hexDigit(load<u16>(srcStart, 2)) << 8) |
|
|
19
|
+
(hexDigit(load<u16>(srcStart, 4)) << 4) |
|
|
20
|
+
hexDigit(load<u16>(srcStart, 6)))
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// @ts-ignore: inline
|
|
25
|
+
@inline function isHexDigit(c: u16): bool {
|
|
26
|
+
return (
|
|
27
|
+
(c >= 0x30 && c <= 0x39) ||
|
|
28
|
+
(c >= 0x41 && c <= 0x46) ||
|
|
29
|
+
(c >= 0x61 && c <= 0x66)
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Strict RFC 8259 check for the char following a backslash, at [escPtr, srcEnd).
|
|
34
|
+
// Legal escapes: " \ / b f n r t and \uXXXX (4 hex digits). Throws otherwise:
|
|
35
|
+
// unknown escape letter, a trailing backslash, or a short / non-hex \u.
|
|
36
|
+
// @ts-ignore: inline
|
|
37
|
+
@inline function validateEscape(escPtr: usize, srcEnd: usize): void {
|
|
38
|
+
if (escPtr >= srcEnd)
|
|
39
|
+
throw new Error("Invalid JSON string: incomplete escape");
|
|
40
|
+
const code = load<u16>(escPtr);
|
|
41
|
+
if (code == 0x75) {
|
|
42
|
+
// \uXXXX
|
|
43
|
+
if (escPtr + 10 > srcEnd)
|
|
44
|
+
throw new Error("Invalid JSON string: incomplete \\u escape");
|
|
45
|
+
if (
|
|
46
|
+
!isHexDigit(load<u16>(escPtr, 2)) ||
|
|
47
|
+
!isHexDigit(load<u16>(escPtr, 4)) ||
|
|
48
|
+
!isHexDigit(load<u16>(escPtr, 6)) ||
|
|
49
|
+
!isHexDigit(load<u16>(escPtr, 8))
|
|
50
|
+
)
|
|
51
|
+
throw new Error("Invalid JSON string: \\u escape needs 4 hex digits");
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
// short escapes: " \ / b f n r t
|
|
55
|
+
if (
|
|
56
|
+
code != 0x22 &&
|
|
57
|
+
code != 0x5c &&
|
|
58
|
+
code != 0x2f &&
|
|
59
|
+
code != 0x62 &&
|
|
60
|
+
code != 0x66 &&
|
|
61
|
+
code != 0x6e &&
|
|
62
|
+
code != 0x72 &&
|
|
63
|
+
code != 0x74
|
|
64
|
+
)
|
|
65
|
+
throw new Error("Invalid JSON string: illegal escape");
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// @ts-ignore: inline
|
|
69
|
+
@inline export function deserializeString_NAIVE(
|
|
70
|
+
srcStart: usize,
|
|
71
|
+
srcEnd: usize,
|
|
72
|
+
): string {
|
|
73
|
+
// RFC 8259: a string is quote-framed. All callers pass quote-inclusive
|
|
74
|
+
// bounds, so the first and last chars must be `"` (rejects `"` alone and
|
|
75
|
+
// trailing garbage like `""x`).
|
|
76
|
+
if (
|
|
77
|
+
srcEnd - srcStart < 4 ||
|
|
78
|
+
load<u16>(srcStart) != QUOTE ||
|
|
79
|
+
load<u16>(srcEnd - 2) != QUOTE
|
|
80
|
+
)
|
|
81
|
+
throw new Error("Invalid JSON string: missing surrounding quotes");
|
|
82
|
+
// Strip quotes
|
|
83
|
+
srcStart += 2;
|
|
84
|
+
srcEnd -= 2;
|
|
85
|
+
const outStart = bs.offset - bs.buffer;
|
|
86
|
+
bs.ensureSize(u32(srcEnd - srcStart));
|
|
87
|
+
|
|
88
|
+
while (srcStart < srcEnd) {
|
|
89
|
+
const block = load<u16>(srcStart);
|
|
90
|
+
store<u16>(bs.offset, block);
|
|
91
|
+
srcStart += 2;
|
|
92
|
+
|
|
93
|
+
// Early exit
|
|
94
|
+
if (block !== 0x5c) {
|
|
95
|
+
// RFC 8259: literal control chars (U+0000..U+001F) must be escaped.
|
|
96
|
+
if (block < 0x20)
|
|
97
|
+
throw new Error("Invalid JSON string: unescaped control character");
|
|
98
|
+
bs.offset += 2;
|
|
99
|
+
continue;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
validateEscape(srcStart, srcEnd);
|
|
103
|
+
const code = load<u16>(srcStart);
|
|
104
|
+
if (code !== 0x75) {
|
|
105
|
+
// Short escapes (\n \t \" \\)
|
|
106
|
+
const block = load<u16>(srcStart);
|
|
107
|
+
const escape = load<u16>(DESERIALIZE_ESCAPE_TABLE + block);
|
|
108
|
+
store<u16>(bs.offset, escape);
|
|
109
|
+
srcStart += 2;
|
|
110
|
+
} else {
|
|
111
|
+
// Unicode escape (\uXXXX)
|
|
112
|
+
const escaped = hex4ToU16(srcStart + 2);
|
|
113
|
+
store<u16>(bs.offset, escaped);
|
|
114
|
+
srcStart += 10;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
bs.offset += 2;
|
|
118
|
+
}
|
|
119
|
+
return bs.sliceOut<string>(outStart);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Writes into the destination field, reusing or resizing the backing string.
|
|
123
|
+
// Mirrors `writeStringToField` in ../swar/string.ts.
|
|
124
|
+
// @ts-ignore: inline
|
|
125
|
+
@inline function writeStringToField(
|
|
126
|
+
dstFieldPtr: usize,
|
|
127
|
+
srcStart: usize,
|
|
128
|
+
byteLength: u32,
|
|
129
|
+
): void {
|
|
130
|
+
if (byteLength == 0) {
|
|
131
|
+
store<usize>(dstFieldPtr, changetype<usize>(""));
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const current = load<usize>(dstFieldPtr);
|
|
136
|
+
let stringPtr: usize;
|
|
137
|
+
if (current >= __heap_base) {
|
|
138
|
+
if (changetype<OBJECT>(current - TOTAL_OVERHEAD).rtSize == byteLength) {
|
|
139
|
+
stringPtr = current;
|
|
140
|
+
} else {
|
|
141
|
+
stringPtr = __renew(current, byteLength);
|
|
142
|
+
store<usize>(dstFieldPtr, stringPtr);
|
|
143
|
+
}
|
|
144
|
+
} else {
|
|
145
|
+
stringPtr = __new(byteLength, idof<string>());
|
|
146
|
+
store<usize>(dstFieldPtr, stringPtr);
|
|
147
|
+
}
|
|
148
|
+
memory.copy(stringPtr, srcStart, byteLength);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// @ts-ignore: inline
|
|
152
|
+
@inline export function deserializeStringField_NAIVE<T extends string | null>(
|
|
153
|
+
srcStart: usize,
|
|
154
|
+
srcEnd: usize,
|
|
155
|
+
dstObj: usize,
|
|
156
|
+
dstOffset: usize = 0,
|
|
157
|
+
): usize {
|
|
158
|
+
const dstFieldPtr = dstObj + dstOffset;
|
|
159
|
+
if (srcStart + 2 > srcEnd || load<u16>(srcStart) != QUOTE)
|
|
160
|
+
abort("Expected leading quote");
|
|
161
|
+
|
|
162
|
+
const payloadStart = srcStart + 2;
|
|
163
|
+
srcStart = payloadStart;
|
|
164
|
+
|
|
165
|
+
bs.offset = bs.buffer;
|
|
166
|
+
bs.ensureSize(<u32>(srcEnd - payloadStart));
|
|
167
|
+
|
|
168
|
+
while (srcStart < srcEnd) {
|
|
169
|
+
const block = load<u16>(srcStart);
|
|
170
|
+
|
|
171
|
+
if (block == QUOTE) {
|
|
172
|
+
writeStringToField(dstFieldPtr, bs.buffer, <u32>(bs.offset - bs.buffer));
|
|
173
|
+
bs.offset = bs.buffer;
|
|
174
|
+
return srcStart + 2;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if (block != BACK_SLASH) {
|
|
178
|
+
store<u16>(bs.offset, block);
|
|
179
|
+
bs.offset += 2;
|
|
180
|
+
srcStart += 2;
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
const code = load<u16>(srcStart, 2);
|
|
185
|
+
if (code !== 0x75) {
|
|
186
|
+
// Short escapes (\n \t \" \\)
|
|
187
|
+
store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
|
|
188
|
+
srcStart += 4;
|
|
189
|
+
} else {
|
|
190
|
+
// Unicode escape (\uXXXX)
|
|
191
|
+
store<u16>(bs.offset, hex4ToU16(srcStart + 4));
|
|
192
|
+
srcStart += 12;
|
|
193
|
+
}
|
|
194
|
+
bs.offset += 2;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
abort("Expected closing quote");
|
|
198
|
+
return 0;
|
|
199
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { COMMA, BRACKET_RIGHT } from "../../custom/chars";
|
|
2
|
-
import {
|
|
2
|
+
import { deserializeFloat_NAIVE } from "./float";
|
|
3
3
|
import { atoi, isSpace } from "../../util";
|
|
4
4
|
|
|
5
5
|
|
|
@@ -25,7 +25,7 @@ import { atoi, isSpace } from "../../util";
|
|
|
25
25
|
return count;
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
export function
|
|
28
|
+
export function deserializeTypedArray_NAIVE<T extends ArrayLike<number>>(
|
|
29
29
|
srcStart: usize,
|
|
30
30
|
srcEnd: usize,
|
|
31
31
|
dst: usize = 0,
|
|
@@ -49,7 +49,7 @@ export function deserializeTypedArray<T extends ArrayLike<number>>(
|
|
|
49
49
|
if (code == COMMA || code == BRACKET_RIGHT || isSpace(code)) {
|
|
50
50
|
if (isFloat<valueof<T>>()) {
|
|
51
51
|
unchecked(
|
|
52
|
-
(out[index++] =
|
|
52
|
+
(out[index++] = deserializeFloat_NAIVE<valueof<T>>(
|
|
53
53
|
lastIndex,
|
|
54
54
|
srcStart,
|
|
55
55
|
)),
|
|
@@ -69,7 +69,7 @@ export function deserializeTypedArray<T extends ArrayLike<number>>(
|
|
|
69
69
|
return out;
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
export function
|
|
72
|
+
export function deserializeArrayBuffer_NAIVE(
|
|
73
73
|
srcStart: usize,
|
|
74
74
|
srcEnd: usize,
|
|
75
75
|
dst: usize = 0,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { atoi } from "../../util/atoi";
|
|
2
2
|
|
|
3
3
|
// @ts-ignore: inline
|
|
4
|
-
@inline export function
|
|
4
|
+
@inline export function deserializeUnsigned_NAIVE<T extends number>(
|
|
5
5
|
srcStart: usize,
|
|
6
6
|
srcEnd: usize,
|
|
7
7
|
): T {
|
|
@@ -9,7 +9,7 @@ import { atoi } from "../../util/atoi";
|
|
|
9
9
|
}
|
|
10
10
|
|
|
11
11
|
// @ts-ignore: inline
|
|
12
|
-
@inline export function
|
|
12
|
+
@inline export function deserializeUnsignedField_NAIVE<T extends number>(
|
|
13
13
|
srcStart: usize,
|
|
14
14
|
srcEnd: usize,
|
|
15
15
|
dstObj: usize,
|
|
@@ -5,19 +5,19 @@ import { isSpace } from "../../../util";
|
|
|
5
5
|
const ASCII_LANE_MASK_4: u64 = 0x00ff00ff00ff00ff;
|
|
6
6
|
const ASCII_ZERO_4: u64 = 0x0030003000300030;
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
// @ts-expect-error: decorators valid here
|
|
9
9
|
@lazy const SPLAT_30 = i16x8.splat(0x30);
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
// @ts-expect-error: decorators valid here
|
|
12
12
|
@lazy const SPLAT_09 = i16x8.splat(9);
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
// @ts-expect-error: decorators valid here
|
|
15
15
|
@lazy const ZERO_I16X8 = i16x8.splat(0);
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
// @ts-expect-error: decorators valid here
|
|
18
18
|
@lazy const ZERO_I32X4 = i32x4.splat(0);
|
|
19
19
|
|
|
20
|
-
|
|
20
|
+
// @ts-expect-error: decorators valid here
|
|
21
21
|
@lazy const PACK_WEIGHTS_10_1 = i8x16(
|
|
22
22
|
10,
|
|
23
23
|
1,
|
|
@@ -37,10 +37,10 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
|
|
|
37
37
|
0,
|
|
38
38
|
);
|
|
39
39
|
|
|
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
|
-
|
|
43
|
+
// @ts-expect-error: decorators valid here
|
|
44
44
|
@inline function storeSignedInteger<T extends number[]>(
|
|
45
45
|
slot: usize,
|
|
46
46
|
value: i64,
|
|
@@ -58,7 +58,7 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
// @ts-expect-error: decorators valid here
|
|
62
62
|
@inline function storeUnsignedInteger<T extends number[]>(
|
|
63
63
|
slot: usize,
|
|
64
64
|
value: u64,
|
|
@@ -76,7 +76,7 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
|
|
|
76
76
|
}
|
|
77
77
|
}
|
|
78
78
|
|
|
79
|
-
|
|
79
|
+
// @ts-expect-error: decorators valid here
|
|
80
80
|
@inline function tryParseEightDigitsSIMD(srcStart: usize, value: u64): u64 {
|
|
81
81
|
const block = load<v128>(srcStart);
|
|
82
82
|
const digits = i16x8.sub(block, SPLAT_30);
|
|
@@ -96,8 +96,7 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
|
|
|
96
96
|
// As in the SWAR variant: the parse helpers take a `slot` (`writePtr`) and
|
|
97
97
|
// store directly. The dispatcher owns `out.length = maxElements` and the
|
|
98
98
|
// per-element `writePtr` advance so `Array.push` is removed for every
|
|
99
|
-
//
|
|
100
|
-
|
|
99
|
+
// @ts-expect-error: decorators valid here
|
|
101
100
|
@inline function parseSignedIntegerSIMD<T extends number[]>(
|
|
102
101
|
srcStart: usize,
|
|
103
102
|
srcEnd: usize,
|
|
@@ -136,7 +135,7 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
|
|
|
136
135
|
return srcStart;
|
|
137
136
|
}
|
|
138
137
|
|
|
139
|
-
|
|
138
|
+
// @ts-expect-error: decorators valid here
|
|
140
139
|
@inline function parseUnsignedIntegerSIMD<T extends number[]>(
|
|
141
140
|
srcStart: usize,
|
|
142
141
|
srcEnd: usize,
|
|
@@ -166,7 +165,7 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
|
|
|
166
165
|
return srcStart;
|
|
167
166
|
}
|
|
168
167
|
|
|
169
|
-
|
|
168
|
+
// @ts-expect-error: decorators valid here
|
|
170
169
|
@lazy const COMMA_SPLAT_8 = i16x8.splat(<i16>COMMA);
|
|
171
170
|
|
|
172
171
|
// Pair-multiply weights for the common two-element packings in 8-char blocks.
|
|
@@ -181,24 +180,25 @@ const ASCII_ZERO_4: u64 = 0x0030003000300030;
|
|
|
181
180
|
// 2+2 (`DD,DD,??`) bitmask 0x24, advance 12
|
|
182
181
|
// 3+1 (`DDD,D,??`) bitmask 0x28, advance 12
|
|
183
182
|
// 1+3 (`D,DDD,??`) bitmask 0x22, advance 12
|
|
183
|
+
// @ts-expect-error: decorators valid here
|
|
184
184
|
@lazy const PAIR_WEIGHTS_3_3_8 = i16x8(100, 10, 1, 0, 100, 10, 1, 0);
|
|
185
185
|
|
|
186
|
-
|
|
186
|
+
// @ts-expect-error: decorators valid here
|
|
187
187
|
@lazy const PAIR_WEIGHTS_3_2_8 = i16x8(100, 10, 1, 0, 10, 1, 0, 0);
|
|
188
188
|
|
|
189
|
-
|
|
189
|
+
// @ts-expect-error: decorators valid here
|
|
190
190
|
@lazy const PAIR_WEIGHTS_2_3_8 = i16x8(10, 1, 0, 100, 10, 1, 0, 0);
|
|
191
191
|
|
|
192
|
-
|
|
192
|
+
// @ts-expect-error: decorators valid here
|
|
193
193
|
@lazy const PAIR_WEIGHTS_2_2_8 = i16x8(10, 1, 0, 10, 1, 0, 0, 0);
|
|
194
194
|
|
|
195
|
-
|
|
195
|
+
// @ts-expect-error: decorators valid here
|
|
196
196
|
@lazy const PAIR_WEIGHTS_3_1_8 = i16x8(100, 10, 1, 0, 1, 0, 0, 0);
|
|
197
197
|
|
|
198
|
-
|
|
198
|
+
// @ts-expect-error: decorators valid here
|
|
199
199
|
@lazy const PAIR_WEIGHTS_1_3_8 = i16x8(1, 0, 100, 10, 1, 0, 0, 0);
|
|
200
200
|
|
|
201
|
-
|
|
201
|
+
// @ts-expect-error: decorators valid here
|
|
202
202
|
@lazy const SPLAT_30_8 = i16x8.splat(0x30);
|
|
203
203
|
|
|
204
204
|
/**
|
|
@@ -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
|
+
}
|