json-as 1.3.5 → 1.3.7
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 +17 -1
- package/assembly/deserialize/helpers/uint.ts +4 -1
- package/assembly/deserialize/index/arbitrary.ts +5 -1
- package/assembly/deserialize/index/array.ts +13 -3
- package/assembly/deserialize/index/integer.ts +68 -1
- package/assembly/deserialize/index/string.ts +4 -1
- package/assembly/deserialize/index/typedarray.ts +13 -3
- package/assembly/deserialize/index/unsigned.ts +78 -1
- package/assembly/deserialize/simd/array/integer.ts +327 -50
- package/assembly/deserialize/simd/integer.ts +233 -0
- package/assembly/deserialize/simd/string.ts +45 -11
- package/assembly/deserialize/simple/arbitrary.ts +11 -4
- package/assembly/deserialize/simple/array/arbitrary.ts +24 -5
- package/assembly/deserialize/simple/array/array.ts +8 -2
- package/assembly/deserialize/simple/array/bool.ts +38 -7
- package/assembly/deserialize/simple/array/box.ts +8 -2
- package/assembly/deserialize/simple/array/float.ts +36 -9
- package/assembly/deserialize/simple/array/generic.ts +12 -4
- package/assembly/deserialize/simple/array/integer.ts +8 -2
- package/assembly/deserialize/simple/array/map.ts +26 -6
- package/assembly/deserialize/simple/array/object.ts +26 -6
- package/assembly/deserialize/simple/array/raw.ts +34 -7
- package/assembly/deserialize/simple/array/string.ts +8 -2
- package/assembly/deserialize/simple/array/struct.ts +26 -6
- package/assembly/deserialize/simple/array.ts +13 -3
- package/assembly/deserialize/simple/bool.ts +6 -2
- package/assembly/deserialize/simple/float.ts +6 -1
- package/assembly/deserialize/simple/integer.ts +10 -2
- package/assembly/deserialize/simple/map.ts +95 -22
- package/assembly/deserialize/simple/object.ts +63 -14
- package/assembly/deserialize/simple/raw.ts +4 -1
- package/assembly/deserialize/simple/set.ts +59 -14
- package/assembly/deserialize/simple/staticarray/string.ts +11 -3
- package/assembly/deserialize/simple/staticarray.ts +64 -14
- package/assembly/deserialize/simple/string.ts +5 -92
- package/assembly/deserialize/simple/struct.ts +5 -1
- package/assembly/deserialize/simple/typedarray.ts +16 -3
- package/assembly/deserialize/simple/unsigned.ts +10 -15
- package/assembly/deserialize/swar/array/arbitrary.ts +5 -1
- package/assembly/deserialize/swar/array/array.ts +30 -6
- package/assembly/deserialize/swar/array/bool.ts +22 -4
- package/assembly/deserialize/swar/array/box.ts +5 -1
- package/assembly/deserialize/swar/array/float.ts +15 -3
- package/assembly/deserialize/swar/array/generic.ts +24 -7
- package/assembly/deserialize/swar/array/integer.ts +328 -84
- package/assembly/deserialize/swar/array/map.ts +5 -1
- package/assembly/deserialize/swar/array/object.ts +27 -7
- package/assembly/deserialize/swar/array/raw.ts +5 -1
- package/assembly/deserialize/swar/array/shared.ts +36 -11
- package/assembly/deserialize/swar/array/string.ts +20 -4
- package/assembly/deserialize/swar/array/struct.ts +27 -7
- package/assembly/deserialize/swar/array.ts +19 -4
- package/assembly/deserialize/swar/integer.ts +246 -0
- package/assembly/deserialize/swar/string.ts +98 -194
- package/assembly/index.d.ts +3 -1
- package/assembly/index.ts +312 -81
- package/assembly/serialize/index/float.ts +5 -1
- package/assembly/serialize/index/typedarray.ts +25 -7
- package/assembly/serialize/simd/string.ts +6 -2
- package/assembly/serialize/simple/array.ts +179 -1
- package/assembly/serialize/simple/float.ts +4 -1
- package/assembly/serialize/simple/integer.ts +9 -9
- package/assembly/serialize/simple/map.ts +6 -2
- package/assembly/serialize/simple/raw.ts +5 -1
- package/assembly/serialize/simple/set.ts +6 -1
- package/assembly/serialize/simple/staticarray.ts +6 -1
- package/assembly/serialize/simple/string.ts +0 -1
- package/assembly/serialize/simple/typedarray.ts +10 -3
- package/assembly/serialize/swar/string.ts +25 -8
- package/assembly/tsconfig.json +1 -21
- 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 +43 -14
- package/assembly/util/itoa-fast.ts +230 -0
- package/assembly/util/masks.ts +18 -1
- package/assembly/util/parsefloat-fast.ts +167 -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 +13 -5
- package/package.json +12 -4
- 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 +1 -0
- package/transform/lib/index.d.ts.map +1 -1
- package/transform/lib/index.js +1030 -241
- 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
|
@@ -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
|
+
}
|
|
@@ -3,7 +3,10 @@ import { OBJECT, TOTAL_OVERHEAD } from "rt/common";
|
|
|
3
3
|
import { __heap_base } from "memory";
|
|
4
4
|
import { QUOTE } from "../../custom/chars";
|
|
5
5
|
import { BACK_SLASH } from "../../custom/chars";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
DESERIALIZE_ESCAPE_TABLE,
|
|
8
|
+
ESCAPE_HEX_TABLE,
|
|
9
|
+
} from "../../globals/tables";
|
|
7
10
|
import { hex4_to_u16_swar } from "../../util/swar";
|
|
8
11
|
import { deserializeStringField_SWAR } from "../swar/string";
|
|
9
12
|
|
|
@@ -65,7 +68,10 @@ import { deserializeStringField_SWAR } from "../swar/string";
|
|
|
65
68
|
* @returns number of bytes written
|
|
66
69
|
*/
|
|
67
70
|
// @ts-expect-error: @inline is a valid decorator
|
|
68
|
-
@inline function copyStringFromSource_SIMD(
|
|
71
|
+
@inline function copyStringFromSource_SIMD(
|
|
72
|
+
srcStart: usize,
|
|
73
|
+
byteLength: usize,
|
|
74
|
+
): string {
|
|
69
75
|
if (byteLength == 0) return changetype<string>("");
|
|
70
76
|
// @ts-expect-error: __new is a runtime builtin
|
|
71
77
|
const out = __new(byteLength, idof<string>());
|
|
@@ -74,7 +80,11 @@ import { deserializeStringField_SWAR } from "../swar/string";
|
|
|
74
80
|
}
|
|
75
81
|
|
|
76
82
|
// @ts-expect-error: @inline is a valid decorator
|
|
77
|
-
@inline function writeStringToField_SIMD(
|
|
83
|
+
@inline function writeStringToField_SIMD(
|
|
84
|
+
dstFieldPtr: usize,
|
|
85
|
+
srcStart: usize,
|
|
86
|
+
byteLength: u32,
|
|
87
|
+
): void {
|
|
78
88
|
if (byteLength == 0) {
|
|
79
89
|
store<usize>(dstFieldPtr, changetype<usize>(""));
|
|
80
90
|
return;
|
|
@@ -100,7 +110,11 @@ import { deserializeStringField_SWAR } from "../swar/string";
|
|
|
100
110
|
|
|
101
111
|
// todo: optimize and stuff. it works, its not pretty. ideally, i'd like this to be (nearly) branchless
|
|
102
112
|
// @ts-expect-error: @inline is a valid decorator
|
|
103
|
-
@inline function deserializeEscapedString_SIMD(
|
|
113
|
+
@inline function deserializeEscapedString_SIMD(
|
|
114
|
+
payloadStart: usize,
|
|
115
|
+
escapeStart: usize,
|
|
116
|
+
srcEnd: usize,
|
|
117
|
+
): string {
|
|
104
118
|
const prefixLen = <u32>(escapeStart - payloadStart);
|
|
105
119
|
let srcStart = escapeStart;
|
|
106
120
|
const srcEnd16 = srcEnd - 16;
|
|
@@ -238,12 +252,16 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
|
|
|
238
252
|
}
|
|
239
253
|
|
|
240
254
|
const laneIdx = usize(ctz(mask) << 1);
|
|
241
|
-
return inline.always(
|
|
255
|
+
return inline.always(
|
|
256
|
+
deserializeEscapedString_SIMD(payloadStart, srcStart + laneIdx, srcEnd),
|
|
257
|
+
);
|
|
242
258
|
}
|
|
243
259
|
|
|
244
260
|
while (srcStart < srcEnd) {
|
|
245
261
|
if (load<u16>(srcStart) == BACK_SLASH) {
|
|
246
|
-
return inline.always(
|
|
262
|
+
return inline.always(
|
|
263
|
+
deserializeEscapedString_SIMD(payloadStart, srcStart, srcEnd),
|
|
264
|
+
);
|
|
247
265
|
}
|
|
248
266
|
srcStart += 2;
|
|
249
267
|
}
|
|
@@ -252,9 +270,15 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
|
|
|
252
270
|
}
|
|
253
271
|
|
|
254
272
|
// @ts-expect-error: @inline is a valid decorator
|
|
255
|
-
@inline export function deserializeStringField_SIMD<T extends string | null>(
|
|
273
|
+
@inline export function deserializeStringField_SIMD<T extends string | null>(
|
|
274
|
+
srcStart: usize,
|
|
275
|
+
srcEnd: usize,
|
|
276
|
+
dstObj: usize,
|
|
277
|
+
dstOffset: usize = 0,
|
|
278
|
+
): usize {
|
|
256
279
|
const dstFieldPtr = dstObj + dstOffset;
|
|
257
|
-
if (srcStart + 2 > srcEnd || load<u16>(srcStart) != QUOTE)
|
|
280
|
+
if (srcStart + 2 > srcEnd || load<u16>(srcStart) != QUOTE)
|
|
281
|
+
abort("Expected leading quote");
|
|
258
282
|
|
|
259
283
|
const quotedStart = srcStart;
|
|
260
284
|
const payloadStart = srcStart + 2;
|
|
@@ -263,7 +287,9 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
|
|
|
263
287
|
|
|
264
288
|
while (srcStart <= srcEnd16) {
|
|
265
289
|
const block = load<v128>(srcStart);
|
|
266
|
-
let mask = i16x8.bitmask(
|
|
290
|
+
let mask = i16x8.bitmask(
|
|
291
|
+
v128.or(i16x8.eq(block, SPLAT_5C), i16x8.eq(block, SPLAT_22)),
|
|
292
|
+
);
|
|
267
293
|
|
|
268
294
|
if (mask == 0) {
|
|
269
295
|
srcStart += 16;
|
|
@@ -277,7 +303,11 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
|
|
|
277
303
|
const char = load<u16>(srcIdx);
|
|
278
304
|
|
|
279
305
|
if (char == QUOTE) {
|
|
280
|
-
writeStringToField_SIMD(
|
|
306
|
+
writeStringToField_SIMD(
|
|
307
|
+
dstFieldPtr,
|
|
308
|
+
payloadStart,
|
|
309
|
+
<u32>(srcIdx - payloadStart),
|
|
310
|
+
);
|
|
281
311
|
return srcIdx + 2;
|
|
282
312
|
}
|
|
283
313
|
|
|
@@ -292,7 +322,11 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
|
|
|
292
322
|
while (srcStart < srcEnd) {
|
|
293
323
|
const char = load<u16>(srcStart);
|
|
294
324
|
if (char == QUOTE) {
|
|
295
|
-
writeStringToField_SIMD(
|
|
325
|
+
writeStringToField_SIMD(
|
|
326
|
+
dstFieldPtr,
|
|
327
|
+
payloadStart,
|
|
328
|
+
<u32>(srcStart - payloadStart),
|
|
329
|
+
);
|
|
296
330
|
return srcStart + 2;
|
|
297
331
|
}
|
|
298
332
|
if (char == BACK_SLASH) {
|
|
@@ -6,15 +6,22 @@ import { deserializeString } from "./string";
|
|
|
6
6
|
import { deserializeObject } from "./object";
|
|
7
7
|
import { BRACE_LEFT, BRACKET_LEFT, CHAR_N, QUOTE } from "../../custom/chars";
|
|
8
8
|
|
|
9
|
-
export function deserializeArbitrary(
|
|
9
|
+
export function deserializeArbitrary(
|
|
10
|
+
srcStart: usize,
|
|
11
|
+
srcEnd: usize,
|
|
12
|
+
dst: usize,
|
|
13
|
+
): JSON.Value {
|
|
10
14
|
const firstChar = load<u16>(srcStart);
|
|
11
15
|
if (firstChar == QUOTE) {
|
|
12
16
|
return JSON.Value.from(deserializeString(srcStart, srcEnd));
|
|
13
|
-
} else if (firstChar == BRACE_LEFT)
|
|
14
|
-
|
|
17
|
+
} else if (firstChar == BRACE_LEFT)
|
|
18
|
+
return JSON.Value.from(deserializeObject(srcStart, srcEnd, 0));
|
|
19
|
+
else if (firstChar - 48 <= 9 || firstChar == 45)
|
|
20
|
+
return JSON.Value.from(deserializeFloat<f64>(srcStart, srcEnd));
|
|
15
21
|
else if (firstChar == BRACKET_LEFT) {
|
|
16
22
|
return JSON.Value.from(deserializeArray<JSON.Value[]>(srcStart, srcEnd, 0));
|
|
17
|
-
} else if (firstChar == 116 || firstChar == 102)
|
|
23
|
+
} else if (firstChar == 116 || firstChar == 102)
|
|
24
|
+
return JSON.Value.from(deserializeBoolean(srcStart, srcEnd));
|
|
18
25
|
else if (firstChar == CHAR_N) {
|
|
19
26
|
const value = JSON.Value.from<usize>(0);
|
|
20
27
|
return value;
|
|
@@ -1,10 +1,27 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
BACK_SLASH,
|
|
3
|
+
BRACE_LEFT,
|
|
4
|
+
BRACE_RIGHT,
|
|
5
|
+
BRACKET_LEFT,
|
|
6
|
+
BRACKET_RIGHT,
|
|
7
|
+
CHAR_F,
|
|
8
|
+
CHAR_N,
|
|
9
|
+
CHAR_T,
|
|
10
|
+
COMMA,
|
|
11
|
+
QUOTE,
|
|
12
|
+
} from "../../../custom/chars";
|
|
2
13
|
import { JSON } from "../../../";
|
|
3
14
|
import { isSpace, scanStringEnd } from "../../../util";
|
|
4
15
|
import { ptrToStr } from "../../../util/ptrToStr";
|
|
5
16
|
|
|
6
|
-
export function deserializeArbitraryArray(
|
|
7
|
-
|
|
17
|
+
export function deserializeArbitraryArray(
|
|
18
|
+
srcStart: usize,
|
|
19
|
+
srcEnd: usize,
|
|
20
|
+
dst: usize,
|
|
21
|
+
): JSON.Value[] {
|
|
22
|
+
const out = changetype<JSON.Value[]>(
|
|
23
|
+
dst || changetype<usize>(instantiate<JSON.Value[]>()),
|
|
24
|
+
);
|
|
8
25
|
let lastIndex: usize = 0;
|
|
9
26
|
let depth: u32 = 0;
|
|
10
27
|
// if (load<u16>(srcStart) != BRACKET_LEFT)
|
|
@@ -31,7 +48,8 @@ export function deserializeArbitraryArray(srcStart: usize, srcEnd: usize, dst: u
|
|
|
31
48
|
} else if (code == QUOTE) {
|
|
32
49
|
lastIndex = srcStart;
|
|
33
50
|
srcStart = scanStringEnd(srcStart, srcEnd);
|
|
34
|
-
if (srcStart >= srcEnd)
|
|
51
|
+
if (srcStart >= srcEnd)
|
|
52
|
+
throw new Error("Unterminated string in JSON array");
|
|
35
53
|
// @ts-ignore: exists
|
|
36
54
|
out.push(JSON.__deserialize<JSON.Value>(lastIndex, srcStart + 2));
|
|
37
55
|
srcStart += 2;
|
|
@@ -78,7 +96,8 @@ export function deserializeArbitraryArray(srcStart: usize, srcEnd: usize, dst: u
|
|
|
78
96
|
const code = load<u16>(srcStart);
|
|
79
97
|
if (code == QUOTE) {
|
|
80
98
|
srcStart = scanStringEnd(srcStart, srcEnd);
|
|
81
|
-
if (srcStart >= srcEnd)
|
|
99
|
+
if (srcStart >= srcEnd)
|
|
100
|
+
throw new Error("Unterminated string in JSON array");
|
|
82
101
|
} else if (code == BRACKET_RIGHT) {
|
|
83
102
|
if (--depth == 0) {
|
|
84
103
|
// @ts-ignore: type
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { BRACKET_LEFT, BRACKET_RIGHT } from "../../../custom/chars";
|
|
2
2
|
import { JSON } from "../../../";
|
|
3
3
|
|
|
4
|
-
export function deserializeArrayArray<T extends unknown[][]>(
|
|
5
|
-
|
|
4
|
+
export function deserializeArrayArray<T extends unknown[][]>(
|
|
5
|
+
srcStart: usize,
|
|
6
|
+
srcEnd: usize,
|
|
7
|
+
dst: usize,
|
|
8
|
+
): T {
|
|
9
|
+
const out = changetype<nonnull<T>>(
|
|
10
|
+
dst || changetype<usize>(instantiate<T>()),
|
|
11
|
+
);
|
|
6
12
|
let lastIndex: usize = 0;
|
|
7
13
|
let depth: u32 = 0;
|
|
8
14
|
srcStart += 2;
|
|
@@ -1,17 +1,48 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { FALSE_WORD_U64, TRUE_WORD_U64 } from "../../../custom/chars";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Boolean-array deserializer (used by every JSON_MODE for top-level
|
|
5
|
+
* `JSON.parse<bool[]>`). Worst-case sizing is one element per `"true,"` =
|
|
6
|
+
* 10 UTF-16 bytes, so pre-allocating `(srcEnd - srcStart) / 10` slots
|
|
7
|
+
* upper-bounds the element count exactly once and lets the loop write
|
|
8
|
+
* through a direct pointer instead of `Array.push`.
|
|
9
|
+
*
|
|
10
|
+
* The token check itself is already SWAR-shaped: a single `u64` load
|
|
11
|
+
* matches all four chars of `true`, and the `false` case adds one `u16`
|
|
12
|
+
* load to confirm the trailing `e`.
|
|
13
|
+
*/
|
|
14
|
+
export function deserializeBooleanArray<T extends boolean[]>(
|
|
15
|
+
srcStart: usize,
|
|
16
|
+
srcEnd: usize,
|
|
17
|
+
dst: usize,
|
|
18
|
+
): T {
|
|
19
|
+
const out = changetype<nonnull<T>>(
|
|
20
|
+
dst || changetype<usize>(instantiate<T>()),
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
// Worst case: every element is `true,` = 5 UTF-16 chars = 10 bytes.
|
|
24
|
+
// `bool` is unmanaged so AS skips zero-fill on `length=`, making the
|
|
25
|
+
// over-allocation essentially free.
|
|
26
|
+
const maxElements = i32(<usize>(srcEnd - srcStart) / 10);
|
|
27
|
+
if (maxElements > 0) out.length = maxElements;
|
|
28
|
+
const dataStart = out.dataStart;
|
|
29
|
+
let writePtr = dataStart;
|
|
30
|
+
|
|
31
|
+
srcStart += 2; // skip `[`
|
|
4
32
|
while (srcStart < srcEnd) {
|
|
5
33
|
const block = load<u64>(srcStart);
|
|
6
|
-
if (block ==
|
|
7
|
-
|
|
34
|
+
if (block == TRUE_WORD_U64) {
|
|
35
|
+
store<bool>(writePtr, true);
|
|
36
|
+
writePtr += 1;
|
|
8
37
|
srcStart += 10;
|
|
9
|
-
} else if (block ==
|
|
10
|
-
|
|
38
|
+
} else if (block == FALSE_WORD_U64 && load<u16>(srcStart, 8) == 101) {
|
|
39
|
+
store<bool>(writePtr, false);
|
|
40
|
+
writePtr += 1;
|
|
11
41
|
srcStart += 12;
|
|
12
42
|
} else {
|
|
13
43
|
srcStart += 2;
|
|
14
44
|
}
|
|
15
45
|
}
|
|
46
|
+
out.length = i32(writePtr - dataStart);
|
|
16
47
|
return out;
|
|
17
48
|
}
|
|
@@ -2,8 +2,14 @@ import { isSpace } from "../../../util";
|
|
|
2
2
|
import { COMMA, BRACKET_RIGHT } from "../../../custom/chars";
|
|
3
3
|
import { JSON } from "../../..";
|
|
4
4
|
|
|
5
|
-
export function deserializeBoxArray<T extends JSON.Box<any>[]>(
|
|
6
|
-
|
|
5
|
+
export function deserializeBoxArray<T extends JSON.Box<any>[]>(
|
|
6
|
+
srcStart: usize,
|
|
7
|
+
srcEnd: usize,
|
|
8
|
+
dst: usize,
|
|
9
|
+
): T {
|
|
10
|
+
const out = changetype<nonnull<T>>(
|
|
11
|
+
dst || changetype<usize>(instantiate<T>()),
|
|
12
|
+
);
|
|
7
13
|
if (isBoolean<valueof<T>>()) {
|
|
8
14
|
srcStart += 2; // skip [
|
|
9
15
|
while (srcStart < srcEnd) {
|
|
@@ -2,21 +2,46 @@ import { isSpace } from "../../../util";
|
|
|
2
2
|
import { COMMA, BRACKET_RIGHT } from "../../../custom/chars";
|
|
3
3
|
import { JSON } from "../../..";
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Float-array deserializer (`f64[]` / `f32[]`).
|
|
7
|
+
*
|
|
8
|
+
* Worst-case sizing: every element is at least `D,` = 2 UTF-16 chars = 4
|
|
9
|
+
* bytes (a single-digit value followed by `,` or `]`). The `srcLen >> 2`
|
|
10
|
+
* upper bound holds even for valid JSON containing negative or fractional
|
|
11
|
+
* widths, because each element advance still consumes >= 4 bytes.
|
|
12
|
+
*
|
|
13
|
+
* f64/f32 are unmanaged so AS skips zero-fill on `length=`. The parse loop
|
|
14
|
+
* writes through a direct `writePtr` pointer, eliminating `Array.push`'s
|
|
15
|
+
* per-element capacity check + length write. Final `out.length` is trimmed
|
|
16
|
+
* to the actual element count.
|
|
17
|
+
*/
|
|
18
|
+
export function deserializeFloatArray<T extends number[]>(
|
|
19
|
+
srcStart: usize,
|
|
20
|
+
srcEnd: usize,
|
|
21
|
+
dst: usize,
|
|
22
|
+
): T {
|
|
23
|
+
const out = changetype<nonnull<T>>(
|
|
24
|
+
dst || changetype<usize>(instantiate<T>()),
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
const elementSize = sizeof<valueof<T>>();
|
|
28
|
+
const maxElements = i32((<usize>(srcEnd - srcStart)) >> 2);
|
|
29
|
+
if (maxElements > 0) out.length = maxElements;
|
|
30
|
+
const dataStart = out.dataStart;
|
|
31
|
+
let writePtr = dataStart;
|
|
32
|
+
|
|
7
33
|
let lastIndex: usize = 0;
|
|
8
34
|
while (srcStart < srcEnd) {
|
|
9
35
|
const code = load<u16>(srcStart);
|
|
10
|
-
if (code - 48 <= 9 || code == 45) {
|
|
36
|
+
if (<u32>code - 48 <= 9 || code == 45) {
|
|
11
37
|
lastIndex = srcStart;
|
|
12
38
|
srcStart += 2;
|
|
13
39
|
while (srcStart < srcEnd) {
|
|
14
|
-
const
|
|
15
|
-
if (
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
// }
|
|
40
|
+
const c = load<u16>(srcStart);
|
|
41
|
+
if (c == COMMA || c == BRACKET_RIGHT || isSpace(c)) {
|
|
42
|
+
const value = JSON.__deserialize<valueof<T>>(lastIndex, srcStart);
|
|
43
|
+
store<valueof<T>>(writePtr, value);
|
|
44
|
+
writePtr += elementSize;
|
|
20
45
|
break;
|
|
21
46
|
}
|
|
22
47
|
srcStart += 2;
|
|
@@ -24,5 +49,7 @@ export function deserializeFloatArray<T extends number[]>(srcStart: usize, srcEn
|
|
|
24
49
|
}
|
|
25
50
|
srcStart += 2;
|
|
26
51
|
}
|
|
52
|
+
|
|
53
|
+
out.length = i32(<usize>(writePtr - dataStart) / elementSize);
|
|
27
54
|
return out;
|
|
28
55
|
}
|
|
@@ -3,15 +3,23 @@ import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
|
|
|
3
3
|
import { isSpace } from "../../../util";
|
|
4
4
|
import { scanValueEnd } from "../../swar/array/shared";
|
|
5
5
|
|
|
6
|
-
export function deserializeGenericArray<T extends unknown[]>(
|
|
7
|
-
|
|
6
|
+
export function deserializeGenericArray<T extends unknown[]>(
|
|
7
|
+
srcStart: usize,
|
|
8
|
+
srcEnd: usize,
|
|
9
|
+
dst: usize,
|
|
10
|
+
): T {
|
|
11
|
+
const out = changetype<nonnull<T>>(
|
|
12
|
+
dst || changetype<usize>(instantiate<T>()),
|
|
13
|
+
);
|
|
8
14
|
out.length = 0;
|
|
9
15
|
|
|
10
16
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
11
17
|
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
12
18
|
|
|
13
|
-
if (srcStart >= srcEnd)
|
|
14
|
-
|
|
19
|
+
if (srcStart >= srcEnd)
|
|
20
|
+
throw new Error("Input string had zero length or was all whitespace");
|
|
21
|
+
if (load<u16>(srcStart) != BRACKET_LEFT)
|
|
22
|
+
throw new Error("Expected '[' at start of array");
|
|
15
23
|
srcStart += 2;
|
|
16
24
|
|
|
17
25
|
while (srcStart < srcEnd) {
|
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
import { atoi, isSpace } from "../../../util";
|
|
2
2
|
import { COMMA, BRACKET_RIGHT } from "../../../custom/chars";
|
|
3
3
|
|
|
4
|
-
export function deserializeIntegerArray<T extends number[]>(
|
|
5
|
-
|
|
4
|
+
export function deserializeIntegerArray<T extends number[]>(
|
|
5
|
+
srcStart: usize,
|
|
6
|
+
srcEnd: usize,
|
|
7
|
+
dst: usize,
|
|
8
|
+
): T {
|
|
9
|
+
const out = changetype<nonnull<T>>(
|
|
10
|
+
dst || changetype<usize>(instantiate<T>()),
|
|
11
|
+
);
|
|
6
12
|
let lastIndex: usize = 0;
|
|
7
13
|
while (srcStart < srcEnd) {
|
|
8
14
|
const code = load<u16>(srcStart);
|