json-as 1.3.7 → 1.3.9
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 +36 -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 +155 -238
- 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
|
@@ -3,12 +3,8 @@ 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 {
|
|
7
|
-
DESERIALIZE_ESCAPE_TABLE,
|
|
8
|
-
ESCAPE_HEX_TABLE,
|
|
9
|
-
} from "../../globals/tables";
|
|
6
|
+
import { DESERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
10
7
|
import { hex4_to_u16_swar } from "../../util/swar";
|
|
11
|
-
import { deserializeStringField_SWAR } from "../swar/string";
|
|
12
8
|
|
|
13
9
|
// @ts-expect-error: @lazy is a valid decorator
|
|
14
10
|
@lazy const SPLAT_5C = i16x8.splat(0x5c); // \
|
|
@@ -106,110 +102,95 @@ import { deserializeStringField_SWAR } from "../swar/string";
|
|
|
106
102
|
memory.copy(stringPtr, srcStart, byteLength);
|
|
107
103
|
}
|
|
108
104
|
|
|
109
|
-
//
|
|
110
|
-
|
|
111
|
-
//
|
|
112
|
-
//
|
|
113
|
-
|
|
105
|
+
// Vectorized escaped scanner for the standalone (whole-value) path. Quotes are
|
|
106
|
+
// already stripped, so `srcEnd` is the payload end and only `\` escapes need
|
|
107
|
+
// handling (no closing-quote search). Same HYBRID strategy as the field path
|
|
108
|
+
// (see deserializeEscapedStringField_SIMD): escape blocks use a free
|
|
109
|
+
// whole-block v128 store for the plain prefix; clean runs stream the first
|
|
110
|
+
// block then bulk-memcpy the remainder. Output is sliced out of `bs` scratch.
|
|
111
|
+
//
|
|
112
|
+
// Deliberately NOT @inline: cold escape path. Inlining the nested-loop v128
|
|
113
|
+
// body at every call site explodes `--converge` compile time on large schemas
|
|
114
|
+
// for no runtime gain (one call per escaped string).
|
|
115
|
+
function deserializeEscapedString_SIMD(
|
|
114
116
|
payloadStart: usize,
|
|
115
117
|
escapeStart: usize,
|
|
116
118
|
srcEnd: usize,
|
|
117
119
|
): string {
|
|
118
120
|
const prefixLen = <u32>(escapeStart - payloadStart);
|
|
119
|
-
let srcStart = escapeStart;
|
|
120
|
-
const srcEnd16 = srcEnd - 16;
|
|
121
121
|
const outStart = bs.offset - bs.buffer;
|
|
122
|
-
bs.ensureSize(u32(srcEnd -
|
|
122
|
+
bs.ensureSize(<u32>(srcEnd - payloadStart) + 16); // +16 slack for overcopy
|
|
123
123
|
if (prefixLen != 0) {
|
|
124
124
|
memory.copy(bs.offset, payloadStart, prefixLen);
|
|
125
125
|
bs.offset += prefixLen;
|
|
126
126
|
}
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
store<v128>(bs.offset, block);
|
|
131
|
-
|
|
132
|
-
const eq5C = i16x8.eq(block, SPLAT_5C);
|
|
133
|
-
let mask = i16x8.bitmask(eq5C);
|
|
128
|
+
let srcStart = escapeStart;
|
|
129
|
+
const srcEnd16 = srcEnd >= 16 ? srcEnd - 16 : 0;
|
|
134
130
|
|
|
131
|
+
while (srcStart <= srcEnd16) {
|
|
132
|
+
const block = load<v128>(srcStart);
|
|
133
|
+
const mask = i16x8.bitmask(i16x8.eq(block, SPLAT_5C)); // backslash only
|
|
135
134
|
if (mask == 0) {
|
|
136
|
-
|
|
135
|
+
// Stream the first clean block cheaply.
|
|
136
|
+
store<v128>(bs.offset, block);
|
|
137
137
|
bs.offset += 16;
|
|
138
|
+
srcStart += 16;
|
|
139
|
+
// If the clean run continues, bulk-copy the remainder in one shot.
|
|
140
|
+
if (srcStart <= srcEnd16) {
|
|
141
|
+
const b2 = load<v128>(srcStart);
|
|
142
|
+
if (i16x8.bitmask(i16x8.eq(b2, SPLAT_5C)) == 0) {
|
|
143
|
+
const runStart = srcStart;
|
|
144
|
+
srcStart += 16;
|
|
145
|
+
while (srcStart <= srcEnd16) {
|
|
146
|
+
if (i16x8.bitmask(i16x8.eq(load<v128>(srcStart), SPLAT_5C)) != 0)
|
|
147
|
+
break;
|
|
148
|
+
srcStart += 16;
|
|
149
|
+
}
|
|
150
|
+
const runLen = <u32>(srcStart - runStart);
|
|
151
|
+
memory.copy(bs.offset, runStart, runLen);
|
|
152
|
+
bs.offset += runLen;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
138
155
|
continue;
|
|
139
156
|
}
|
|
140
157
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
bs.offset += laneIdx - lastLane;
|
|
150
|
-
|
|
151
|
-
// Hot path (negative bias)
|
|
152
|
-
if (code !== 0x75) {
|
|
153
|
-
// Short escapes (\n \t \" \\)
|
|
154
|
-
const escaped = load<u16>(DESERIALIZE_ESCAPE_TABLE + code);
|
|
155
|
-
mask &= mask - i32(escaped === 0x5c);
|
|
156
|
-
store<u16>(bs.offset, escaped);
|
|
157
|
-
store<v128>(bs.offset, load<v128>(srcIdx, 4), 2);
|
|
158
|
-
|
|
159
|
-
const l6 = usize(laneIdx === 14);
|
|
160
|
-
// bs.offset -= (1 - l6) << 1;
|
|
161
|
-
bs.offset += 2;
|
|
162
|
-
srcStart += l6 << 1;
|
|
163
|
-
lastLane = laneIdx + 4;
|
|
164
|
-
continue;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
// Unicode escape (\uXXXX)
|
|
168
|
-
const block = load<u64>(srcIdx, 4); // XXXX
|
|
169
|
-
const escaped = hex4_to_u16_swar(block);
|
|
170
|
-
|
|
171
|
-
store<u16>(bs.offset, escaped);
|
|
172
|
-
store<u64>(bs.offset, load<u64>(srcIdx, 12), 2);
|
|
173
|
-
|
|
158
|
+
// Escape block: one whole-block store covers the plain prefix.
|
|
159
|
+
store<v128>(bs.offset, block);
|
|
160
|
+
const laneIdx = usize(ctz(mask) << 1);
|
|
161
|
+
bs.offset += laneIdx;
|
|
162
|
+
const srcIdx = srcStart + laneIdx;
|
|
163
|
+
const code = load<u16>(srcIdx, 2);
|
|
164
|
+
if (code !== 0x75) {
|
|
165
|
+
store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
|
|
174
166
|
bs.offset += 2;
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
if (lastLane < 16) {
|
|
182
|
-
bs.offset += 16 - lastLane;
|
|
167
|
+
srcStart = srcIdx + 4;
|
|
168
|
+
} else {
|
|
169
|
+
store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(srcIdx, 4)));
|
|
170
|
+
bs.offset += 2;
|
|
171
|
+
srcStart = srcIdx + 12;
|
|
183
172
|
}
|
|
184
|
-
|
|
185
|
-
srcStart += 16 + srcChg;
|
|
186
173
|
}
|
|
187
174
|
|
|
175
|
+
// scalar tail (< 16 bytes remaining)
|
|
188
176
|
while (srcStart < srcEnd) {
|
|
189
|
-
const
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
// Early exit
|
|
194
|
-
if (block !== 0x5c) {
|
|
177
|
+
const char = load<u16>(srcStart);
|
|
178
|
+
if (char != BACK_SLASH) {
|
|
179
|
+
store<u16>(bs.offset, char);
|
|
195
180
|
bs.offset += 2;
|
|
181
|
+
srcStart += 2;
|
|
196
182
|
continue;
|
|
197
183
|
}
|
|
198
|
-
|
|
199
|
-
const code = load<u16>(srcStart);
|
|
184
|
+
const code = load<u16>(srcStart, 2);
|
|
200
185
|
if (code !== 0x75) {
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
srcStart += 2;
|
|
186
|
+
store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
|
|
187
|
+
bs.offset += 2;
|
|
188
|
+
srcStart += 4;
|
|
205
189
|
} else {
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
store<u16>(bs.offset, escaped);
|
|
210
|
-
srcStart += 10;
|
|
190
|
+
store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(srcStart, 4)));
|
|
191
|
+
bs.offset += 2;
|
|
192
|
+
srcStart += 12;
|
|
211
193
|
}
|
|
212
|
-
bs.offset += 2;
|
|
213
194
|
}
|
|
214
195
|
|
|
215
196
|
return bs.sliceOut<string>(outStart);
|
|
@@ -269,8 +250,148 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
|
|
|
269
250
|
return copyStringFromSource_SIMD(payloadStart, srcEnd - payloadStart);
|
|
270
251
|
}
|
|
271
252
|
|
|
272
|
-
//
|
|
273
|
-
|
|
253
|
+
// Vectorized escaped scanner for the field path. `escapeStart` points at the
|
|
254
|
+
// first `\` located by the caller's v128 scan. Output is assembled in the
|
|
255
|
+
// reused `bs` scratch buffer, then written once via writeStringToField_SIMD.
|
|
256
|
+
//
|
|
257
|
+
// Strategy (validated against run-copy and pure-stream variants across escape
|
|
258
|
+
// densities — see __benches__/custom/simd-string-deser-variants-h2h):
|
|
259
|
+
// * Escape-bearing block: a single whole-block v128 store copies the plain
|
|
260
|
+
// prefix for free; then the escape is decoded scalar.
|
|
261
|
+
// * Clean block: stream the first one cheaply, but if the clean run keeps
|
|
262
|
+
// going, switch to one bulk memory.copy for the remainder — bandwidth-
|
|
263
|
+
// optimal on long sparse runs, avoiding a per-block-store cliff on large
|
|
264
|
+
// inputs. This dominates both alternatives: stream-cheap on dense escapes,
|
|
265
|
+
// memcpy-fast on long runs.
|
|
266
|
+
//
|
|
267
|
+
// Deliberately NOT @inline: cold escape path. As an @inline it was inlined into
|
|
268
|
+
// deserializeStringField_SIMD at every struct string-field site, exploding
|
|
269
|
+
// `--converge` compile time ~24x on large schemas (4s → 99s) for no runtime
|
|
270
|
+
// gain. The hot no-escape scan + writeStringToField stay inline in the caller.
|
|
271
|
+
function deserializeEscapedStringField_SIMD(
|
|
272
|
+
payloadStart: usize,
|
|
273
|
+
escapeStart: usize,
|
|
274
|
+
srcEnd: usize,
|
|
275
|
+
dstFieldPtr: usize,
|
|
276
|
+
): usize {
|
|
277
|
+
const prefixLen = <u32>(escapeStart - payloadStart);
|
|
278
|
+
bs.offset = bs.buffer;
|
|
279
|
+
bs.ensureSize(<u32>(srcEnd - payloadStart) + 16); // +16 slack for overcopy
|
|
280
|
+
if (prefixLen != 0) {
|
|
281
|
+
memory.copy(bs.buffer, payloadStart, prefixLen);
|
|
282
|
+
bs.offset += prefixLen;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
let srcStart = escapeStart;
|
|
286
|
+
const srcEnd16 = srcEnd >= 16 ? srcEnd - 16 : 0;
|
|
287
|
+
|
|
288
|
+
while (srcStart <= srcEnd16) {
|
|
289
|
+
const block = load<v128>(srcStart);
|
|
290
|
+
const mask = i16x8.bitmask(
|
|
291
|
+
v128.or(i16x8.eq(block, SPLAT_5C), i16x8.eq(block, SPLAT_22)),
|
|
292
|
+
);
|
|
293
|
+
if (mask == 0) {
|
|
294
|
+
// Stream the first clean block cheaply.
|
|
295
|
+
store<v128>(bs.offset, block);
|
|
296
|
+
bs.offset += 16;
|
|
297
|
+
srcStart += 16;
|
|
298
|
+
// If the clean run continues, bulk-copy the remainder in one shot.
|
|
299
|
+
if (srcStart <= srcEnd16) {
|
|
300
|
+
const b2 = load<v128>(srcStart);
|
|
301
|
+
if (
|
|
302
|
+
i16x8.bitmask(
|
|
303
|
+
v128.or(i16x8.eq(b2, SPLAT_5C), i16x8.eq(b2, SPLAT_22)),
|
|
304
|
+
) == 0
|
|
305
|
+
) {
|
|
306
|
+
const runStart = srcStart;
|
|
307
|
+
srcStart += 16;
|
|
308
|
+
while (srcStart <= srcEnd16) {
|
|
309
|
+
const b3 = load<v128>(srcStart);
|
|
310
|
+
if (
|
|
311
|
+
i16x8.bitmask(
|
|
312
|
+
v128.or(i16x8.eq(b3, SPLAT_5C), i16x8.eq(b3, SPLAT_22)),
|
|
313
|
+
) != 0
|
|
314
|
+
)
|
|
315
|
+
break;
|
|
316
|
+
srcStart += 16;
|
|
317
|
+
}
|
|
318
|
+
const runLen = <u32>(srcStart - runStart);
|
|
319
|
+
memory.copy(bs.offset, runStart, runLen);
|
|
320
|
+
bs.offset += runLen;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Escape/quote block: one whole-block store covers the plain prefix.
|
|
327
|
+
store<v128>(bs.offset, block);
|
|
328
|
+
const laneIdx = usize(ctz(mask) << 1);
|
|
329
|
+
bs.offset += laneIdx;
|
|
330
|
+
const srcIdx = srcStart + laneIdx;
|
|
331
|
+
const char = load<u16>(srcIdx);
|
|
332
|
+
if (char == QUOTE) {
|
|
333
|
+
writeStringToField_SIMD(
|
|
334
|
+
dstFieldPtr,
|
|
335
|
+
bs.buffer,
|
|
336
|
+
<u32>(bs.offset - bs.buffer),
|
|
337
|
+
);
|
|
338
|
+
bs.offset = bs.buffer;
|
|
339
|
+
return srcIdx + 2;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
const code = load<u16>(srcIdx, 2);
|
|
343
|
+
if (code !== 0x75) {
|
|
344
|
+
store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
|
|
345
|
+
bs.offset += 2;
|
|
346
|
+
srcStart = srcIdx + 4;
|
|
347
|
+
} else {
|
|
348
|
+
store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(srcIdx, 4)));
|
|
349
|
+
bs.offset += 2;
|
|
350
|
+
srcStart = srcIdx + 12;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
// scalar tail (< 16 bytes remaining): emit chars directly.
|
|
355
|
+
while (srcStart < srcEnd) {
|
|
356
|
+
const char = load<u16>(srcStart);
|
|
357
|
+
if (char == QUOTE) {
|
|
358
|
+
writeStringToField_SIMD(
|
|
359
|
+
dstFieldPtr,
|
|
360
|
+
bs.buffer,
|
|
361
|
+
<u32>(bs.offset - bs.buffer),
|
|
362
|
+
);
|
|
363
|
+
bs.offset = bs.buffer;
|
|
364
|
+
return srcStart + 2;
|
|
365
|
+
}
|
|
366
|
+
if (char != BACK_SLASH) {
|
|
367
|
+
store<u16>(bs.offset, char);
|
|
368
|
+
bs.offset += 2;
|
|
369
|
+
srcStart += 2;
|
|
370
|
+
continue;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
const code = load<u16>(srcStart, 2);
|
|
374
|
+
if (code !== 0x75) {
|
|
375
|
+
store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
|
|
376
|
+
bs.offset += 2;
|
|
377
|
+
srcStart += 4;
|
|
378
|
+
} else {
|
|
379
|
+
store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(srcStart, 4)));
|
|
380
|
+
bs.offset += 2;
|
|
381
|
+
srcStart += 12;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
bs.offset = bs.buffer;
|
|
386
|
+
abort("Unterminated string literal");
|
|
387
|
+
return srcStart;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
// NOT @inline: as an @inline entry, binaryen inlined the (loop-bearing) escaped
|
|
391
|
+
// scanner into every per-field copy, exploding `large` SIMD compile ~24x
|
|
392
|
+
// (4s→99s, 221KB→555KB wasm). Kept as a single shared function — matches
|
|
393
|
+
// deserializeStringField_SWAR (also non-inline) and costs only one call/field.
|
|
394
|
+
export function deserializeStringField_SIMD<T extends string | null>(
|
|
274
395
|
srcStart: usize,
|
|
275
396
|
srcEnd: usize,
|
|
276
397
|
dstObj: usize,
|
|
@@ -280,43 +401,40 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
|
|
|
280
401
|
if (srcStart + 2 > srcEnd || load<u16>(srcStart) != QUOTE)
|
|
281
402
|
abort("Expected leading quote");
|
|
282
403
|
|
|
283
|
-
const quotedStart = srcStart;
|
|
284
404
|
const payloadStart = srcStart + 2;
|
|
285
405
|
const srcEnd16 = srcEnd >= 16 ? srcEnd - 16 : 0;
|
|
286
406
|
srcStart = payloadStart;
|
|
287
407
|
|
|
288
408
|
while (srcStart <= srcEnd16) {
|
|
289
409
|
const block = load<v128>(srcStart);
|
|
290
|
-
|
|
410
|
+
const mask = i16x8.bitmask(
|
|
291
411
|
v128.or(i16x8.eq(block, SPLAT_5C), i16x8.eq(block, SPLAT_22)),
|
|
292
412
|
);
|
|
293
|
-
|
|
294
413
|
if (mask == 0) {
|
|
295
414
|
srcStart += 16;
|
|
296
415
|
continue;
|
|
297
416
|
}
|
|
298
417
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
srcStart += 16;
|
|
418
|
+
const laneIdx = usize(ctz(mask) << 1);
|
|
419
|
+
const srcIdx = srcStart + laneIdx;
|
|
420
|
+
const char = load<u16>(srcIdx);
|
|
421
|
+
if (char == QUOTE) {
|
|
422
|
+
writeStringToField_SIMD(
|
|
423
|
+
dstFieldPtr,
|
|
424
|
+
payloadStart,
|
|
425
|
+
<u32>(srcIdx - payloadStart),
|
|
426
|
+
);
|
|
427
|
+
return srcIdx + 2;
|
|
428
|
+
}
|
|
429
|
+
// backslash → vectorized escaped scan (no more SWAR fallback)
|
|
430
|
+
return inline.always(
|
|
431
|
+
deserializeEscapedStringField_SIMD(
|
|
432
|
+
payloadStart,
|
|
433
|
+
srcIdx,
|
|
434
|
+
srcEnd,
|
|
435
|
+
dstFieldPtr,
|
|
436
|
+
),
|
|
437
|
+
);
|
|
320
438
|
}
|
|
321
439
|
|
|
322
440
|
while (srcStart < srcEnd) {
|
|
@@ -330,7 +448,14 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
|
|
|
330
448
|
return srcStart + 2;
|
|
331
449
|
}
|
|
332
450
|
if (char == BACK_SLASH) {
|
|
333
|
-
return
|
|
451
|
+
return inline.always(
|
|
452
|
+
deserializeEscapedStringField_SIMD(
|
|
453
|
+
payloadStart,
|
|
454
|
+
srcStart,
|
|
455
|
+
srcEnd,
|
|
456
|
+
dstFieldPtr,
|
|
457
|
+
),
|
|
458
|
+
);
|
|
334
459
|
}
|
|
335
460
|
srcStart += 2;
|
|
336
461
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { JSON } from "../../..";
|
|
2
|
+
import { deserializeGenericArrayBody } from "./generic";
|
|
2
3
|
import { ensureArrayField } from "./shared";
|
|
3
4
|
|
|
4
5
|
|
|
@@ -7,6 +8,9 @@ import { ensureArrayField } from "./shared";
|
|
|
7
8
|
srcEnd: usize,
|
|
8
9
|
fieldPtr: usize,
|
|
9
10
|
): usize {
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
return deserializeGenericArrayBody<JSON.Value[]>(
|
|
12
|
+
srcStart,
|
|
13
|
+
srcEnd,
|
|
14
|
+
ensureArrayField<JSON.Value[]>(fieldPtr),
|
|
15
|
+
);
|
|
12
16
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { JSON } from "../../..";
|
|
2
2
|
import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
|
|
3
|
-
import {
|
|
4
|
-
import { ensureArrayField, scanValueEnd } from "./shared";
|
|
3
|
+
import { deserializeFloatArrayBody } from "./float";
|
|
4
|
+
import { ensureArrayField, scanValueEnd, skipWhitespace } from "./shared";
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
@inline export function
|
|
7
|
+
@inline export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
8
8
|
srcStart: usize,
|
|
9
9
|
srcEnd: usize,
|
|
10
10
|
out: T,
|
|
@@ -14,6 +14,7 @@ import { ensureArrayField, scanValueEnd } from "./shared";
|
|
|
14
14
|
do {
|
|
15
15
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
|
|
16
16
|
srcStart += 2;
|
|
17
|
+
srcStart = skipWhitespace(srcStart, srcEnd);
|
|
17
18
|
if (srcStart >= srcEnd) break;
|
|
18
19
|
if (load<u16>(srcStart) == BRACKET_RIGHT) {
|
|
19
20
|
out.length = 0;
|
|
@@ -29,7 +30,7 @@ import { ensureArrayField, scanValueEnd } from "./shared";
|
|
|
29
30
|
value = changetype<valueof<T>>(instantiate<valueof<T>>());
|
|
30
31
|
out.push(value);
|
|
31
32
|
}
|
|
32
|
-
srcStart =
|
|
33
|
+
srcStart = deserializeFloatArrayBody<valueof<T>>(
|
|
33
34
|
srcStart,
|
|
34
35
|
srcEnd,
|
|
35
36
|
value,
|
|
@@ -43,7 +44,7 @@ import { ensureArrayField, scanValueEnd } from "./shared";
|
|
|
43
44
|
value = changetype<valueof<T>>(instantiate<valueof<T>>());
|
|
44
45
|
out.push(value);
|
|
45
46
|
}
|
|
46
|
-
srcStart =
|
|
47
|
+
srcStart = deserializeArrayArrayBody<valueof<T>>(
|
|
47
48
|
srcStart,
|
|
48
49
|
srcEnd,
|
|
49
50
|
value,
|
|
@@ -67,14 +68,20 @@ import { ensureArrayField, scanValueEnd } from "./shared";
|
|
|
67
68
|
srcStart = valueEnd;
|
|
68
69
|
}
|
|
69
70
|
|
|
71
|
+
srcStart = skipWhitespace(srcStart, srcEnd);
|
|
70
72
|
const code = load<u16>(srcStart);
|
|
71
73
|
if (code == COMMA) {
|
|
72
74
|
srcStart += 2;
|
|
75
|
+
srcStart = skipWhitespace(srcStart, srcEnd);
|
|
73
76
|
index++;
|
|
74
77
|
continue;
|
|
75
78
|
}
|
|
76
79
|
if (code == BRACKET_RIGHT) {
|
|
77
|
-
|
|
80
|
+
// Skip the runtime `ensureCapacity` call when the length is already
|
|
81
|
+
// correct (the array is being reused with the same shape, e.g.
|
|
82
|
+
// canada's geometry rings across repeated parses).
|
|
83
|
+
const nextLen = index + 1;
|
|
84
|
+
if (out.length != nextLen) out.length = nextLen;
|
|
78
85
|
return srcStart + 2;
|
|
79
86
|
}
|
|
80
87
|
break;
|
|
@@ -90,7 +97,7 @@ import { ensureArrayField, scanValueEnd } from "./shared";
|
|
|
90
97
|
srcEnd: usize,
|
|
91
98
|
fieldPtr: usize,
|
|
92
99
|
): usize {
|
|
93
|
-
return
|
|
100
|
+
return deserializeArrayArrayBody<T>(
|
|
94
101
|
srcStart,
|
|
95
102
|
srcEnd,
|
|
96
103
|
ensureArrayField<T>(fieldPtr),
|
|
@@ -5,10 +5,11 @@ import {
|
|
|
5
5
|
FALSE_WORD_U64,
|
|
6
6
|
TRUE_WORD_U64,
|
|
7
7
|
} from "../../../custom/chars";
|
|
8
|
+
import { isSpace } from "../../../util";
|
|
8
9
|
import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
9
10
|
|
|
10
11
|
|
|
11
|
-
@inline
|
|
12
|
+
@inline function deserializeBooleanArrayBody<T extends boolean[]>(
|
|
12
13
|
srcStart: usize,
|
|
13
14
|
srcEnd: usize,
|
|
14
15
|
out: T,
|
|
@@ -18,6 +19,7 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
|
18
19
|
do {
|
|
19
20
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
|
|
20
21
|
srcStart += 2;
|
|
22
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
21
23
|
if (srcStart >= srcEnd) break;
|
|
22
24
|
if (load<u16>(srcStart) == BRACKET_RIGHT) {
|
|
23
25
|
out.length = 0;
|
|
@@ -37,15 +39,18 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
|
37
39
|
break;
|
|
38
40
|
}
|
|
39
41
|
|
|
42
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
40
43
|
if (srcStart >= srcEnd) break;
|
|
41
44
|
const code = load<u16>(srcStart);
|
|
42
45
|
if (code == COMMA) {
|
|
43
46
|
srcStart += 2;
|
|
47
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
44
48
|
index++;
|
|
45
49
|
continue;
|
|
46
50
|
}
|
|
47
51
|
if (code == BRACKET_RIGHT) {
|
|
48
|
-
|
|
52
|
+
const nextLen = index + 1;
|
|
53
|
+
if (out.length != nextLen) out.length = nextLen;
|
|
49
54
|
return srcStart + 2;
|
|
50
55
|
}
|
|
51
56
|
break;
|
|
@@ -61,7 +66,7 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
|
61
66
|
srcEnd: usize,
|
|
62
67
|
fieldPtr: usize,
|
|
63
68
|
): usize {
|
|
64
|
-
return
|
|
69
|
+
return deserializeBooleanArrayBody<T>(
|
|
65
70
|
srcStart,
|
|
66
71
|
srcEnd,
|
|
67
72
|
ensureArrayField<T>(fieldPtr),
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { JSON } from "../../..";
|
|
2
|
+
import { deserializeGenericArrayBody } from "./generic";
|
|
2
3
|
import { ensureArrayField } from "./shared";
|
|
3
4
|
|
|
4
5
|
|
|
@@ -7,6 +8,9 @@ import { ensureArrayField } from "./shared";
|
|
|
7
8
|
srcEnd: usize,
|
|
8
9
|
fieldPtr: usize,
|
|
9
10
|
): usize {
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
return deserializeGenericArrayBody<T>(
|
|
12
|
+
srcStart,
|
|
13
|
+
srcEnd,
|
|
14
|
+
ensureArrayField<T>(fieldPtr),
|
|
15
|
+
);
|
|
12
16
|
}
|