json-as 1.3.6 → 1.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +45 -0
- package/README.md +1 -1
- package/assembly/deserialize/helpers/uint.ts +4 -1
- package/assembly/deserialize/index/arbitrary.ts +7 -3
- package/assembly/deserialize/index/array.ts +42 -17
- package/assembly/deserialize/index/bool.ts +1 -1
- package/assembly/deserialize/index/date.ts +1 -1
- package/assembly/deserialize/index/float.ts +40 -1
- package/assembly/deserialize/index/integer.ts +68 -1
- package/assembly/deserialize/index/map.ts +1 -1
- package/assembly/deserialize/index/object.ts +1 -1
- package/assembly/deserialize/index/raw.ts +1 -1
- package/assembly/deserialize/index/set.ts +1 -1
- package/assembly/deserialize/index/staticarray.ts +4 -1
- package/assembly/deserialize/index/string.ts +32 -4
- package/assembly/deserialize/index/struct.ts +1 -1
- package/assembly/deserialize/index/typedarray.ts +30 -10
- package/assembly/deserialize/index/unsigned.ts +78 -1
- package/assembly/deserialize/index.ts +1 -0
- package/assembly/deserialize/{simple → naive}/array/arbitrary.ts +24 -5
- package/assembly/deserialize/{simple → naive}/array/array.ts +8 -2
- package/assembly/deserialize/naive/array/bool.ts +68 -0
- package/assembly/deserialize/{simple → naive}/array/box.ts +8 -2
- package/assembly/deserialize/naive/array/float.ts +63 -0
- package/assembly/deserialize/{simple → naive}/array/generic.ts +14 -7
- package/assembly/deserialize/naive/array/integer.ts +86 -0
- package/assembly/deserialize/naive/array/map.ts +47 -0
- package/assembly/deserialize/naive/array/object.ts +47 -0
- package/assembly/deserialize/{simple → naive}/array/raw.ts +34 -7
- package/assembly/deserialize/naive/array/string.ts +69 -0
- package/assembly/deserialize/naive/array/struct.ts +47 -0
- package/assembly/deserialize/{simple → naive}/array.ts +15 -10
- package/assembly/deserialize/{simple → naive}/bool.ts +6 -2
- package/assembly/deserialize/naive/float.ts +135 -0
- package/assembly/deserialize/{simple → naive}/integer.ts +10 -2
- package/assembly/deserialize/{simple → naive}/map.ts +106 -27
- package/assembly/deserialize/{simple → naive}/object.ts +65 -19
- package/assembly/deserialize/{simple → naive}/raw.ts +4 -1
- package/assembly/deserialize/{simple → naive}/set.ts +49 -19
- package/assembly/deserialize/{simple → naive}/staticarray/array.ts +1 -1
- package/assembly/deserialize/{simple → naive}/staticarray/bool.ts +1 -1
- package/assembly/deserialize/{simple → naive}/staticarray/float.ts +1 -1
- package/assembly/deserialize/{simple → naive}/staticarray/integer.ts +1 -1
- package/assembly/deserialize/{simple → naive}/staticarray/string.ts +11 -3
- package/assembly/deserialize/{simple → naive}/staticarray/struct.ts +1 -2
- package/assembly/deserialize/{simple → naive}/staticarray.ts +68 -18
- package/assembly/deserialize/naive/string.ts +199 -0
- package/assembly/deserialize/{simple → naive}/struct.ts +5 -1
- package/assembly/deserialize/{simple → naive}/typedarray.ts +17 -4
- package/assembly/deserialize/{simple → naive}/unsigned.ts +10 -15
- package/assembly/deserialize/simd/array/integer.ts +339 -62
- package/assembly/deserialize/simd/float.ts +303 -0
- package/assembly/deserialize/simd/integer.ts +233 -0
- package/assembly/deserialize/simd/string.ts +266 -107
- package/assembly/deserialize/swar/array/arbitrary.ts +11 -3
- package/assembly/deserialize/swar/array/array.ts +40 -9
- package/assembly/deserialize/swar/array/bool.ts +28 -5
- package/assembly/deserialize/swar/array/box.ts +11 -3
- package/assembly/deserialize/swar/array/float.ts +295 -7
- package/assembly/deserialize/swar/array/generic.ts +28 -7
- package/assembly/deserialize/swar/array/integer.ts +363 -112
- package/assembly/deserialize/swar/array/map.ts +11 -3
- package/assembly/deserialize/swar/array/object.ts +37 -25
- package/assembly/deserialize/swar/array/raw.ts +11 -3
- package/assembly/deserialize/swar/array/shared.ts +63 -14
- package/assembly/deserialize/swar/array/string.ts +140 -7
- package/assembly/deserialize/swar/array/struct.ts +66 -12
- package/assembly/deserialize/swar/array.ts +12 -51
- package/assembly/deserialize/swar/float.ts +304 -0
- package/assembly/deserialize/swar/integer.ts +246 -0
- package/assembly/deserialize/swar/string.ts +213 -294
- package/assembly/deserialize/swar/typedarray.ts +224 -0
- package/assembly/index.d.ts +3 -1
- package/assembly/index.ts +402 -261
- package/assembly/serialize/index/array.ts +1 -1
- package/assembly/serialize/index/bool.ts +1 -1
- package/assembly/serialize/index/date.ts +1 -1
- package/assembly/serialize/index/float.ts +5 -1
- package/assembly/serialize/index/integer.ts +1 -1
- package/assembly/serialize/index/map.ts +1 -1
- package/assembly/serialize/index/raw.ts +1 -1
- package/assembly/serialize/index/set.ts +1 -1
- package/assembly/serialize/index/staticarray.ts +1 -1
- package/assembly/serialize/index/string.ts +1 -1
- package/assembly/serialize/index/struct.ts +1 -1
- package/assembly/serialize/index/typedarray.ts +21 -12
- package/assembly/serialize/index.ts +1 -0
- package/assembly/serialize/naive/array.ts +351 -0
- package/assembly/serialize/{simple → naive}/float.ts +4 -1
- package/assembly/serialize/naive/integer.ts +19 -0
- package/assembly/serialize/{simple → naive}/map.ts +6 -2
- package/assembly/serialize/{simple → naive}/raw.ts +5 -1
- package/assembly/serialize/{simple → naive}/set.ts +6 -1
- package/assembly/serialize/{simple → naive}/staticarray.ts +6 -1
- package/assembly/serialize/{simple → naive}/string.ts +1 -2
- package/assembly/serialize/{simple → naive}/typedarray.ts +10 -3
- package/assembly/serialize/simd/string.ts +6 -2
- package/assembly/serialize/swar/string.ts +15 -141
- package/assembly/util/atoi-fast.ts +81 -0
- package/assembly/util/concat.ts +5 -1
- package/assembly/util/dragonbox-cache.ts +443 -2
- package/assembly/util/dragonbox.ts +53 -17
- package/assembly/util/itoa-fast.ts +241 -0
- package/assembly/util/masks.ts +18 -1
- package/assembly/util/parsefloat-fast.ts +167 -0
- package/assembly/util/scanValueEnd.ts +78 -0
- package/assembly/util/scientific.ts +132 -0
- package/assembly/util/simd-int.ts +191 -0
- package/assembly/util/snp.ts +4 -1
- package/assembly/util/swar-int.ts +248 -0
- package/assembly/util/swar.ts +13 -3
- package/lib/as-bs.ts +27 -6
- package/package.json +15 -11
- package/transform/lib/builder.d.ts.map +1 -1
- package/transform/lib/builder.js +13 -5
- package/transform/lib/builder.js.map +1 -1
- package/transform/lib/index.d.ts +5 -0
- package/transform/lib/index.d.ts.map +1 -1
- package/transform/lib/index.js +1046 -340
- package/transform/lib/index.js.map +1 -1
- package/transform/lib/linkers/alias.d.ts.map +1 -1
- package/transform/lib/linkers/alias.js.map +1 -1
- package/transform/lib/linkers/custom.d.ts.map +1 -1
- package/transform/lib/linkers/custom.js +3 -2
- package/transform/lib/linkers/custom.js.map +1 -1
- package/transform/lib/linkers/imports.d.ts.map +1 -1
- package/transform/lib/linkers/imports.js.map +1 -1
- package/transform/lib/types.d.ts.map +1 -1
- package/transform/lib/types.js +54 -16
- package/transform/lib/types.js.map +1 -1
- package/transform/lib/util.d.ts.map +1 -1
- package/transform/lib/util.js +1 -1
- package/transform/lib/util.js.map +1 -1
- package/transform/lib/visitor.d.ts.map +1 -1
- package/transform/lib/visitor.js +2 -1
- package/transform/lib/visitor.js.map +1 -1
- package/assembly/custom/util.ts +0 -310
- package/assembly/deserialize/simple/arbitrary.ts +0 -23
- package/assembly/deserialize/simple/array/bool.ts +0 -17
- package/assembly/deserialize/simple/array/float.ts +0 -28
- package/assembly/deserialize/simple/array/integer.ts +0 -27
- package/assembly/deserialize/simple/array/map.ts +0 -28
- package/assembly/deserialize/simple/array/object.ts +0 -28
- package/assembly/deserialize/simple/array/string.ts +0 -23
- package/assembly/deserialize/simple/array/struct.ts +0 -28
- package/assembly/deserialize/simple/float.ts +0 -201
- package/assembly/deserialize/simple/string.ts +0 -132
- package/assembly/serialize/simple/arbitrary.ts +0 -79
- package/assembly/serialize/simple/array.ts +0 -86
- package/assembly/serialize/simple/integer.ts +0 -20
- package/assembly/serialize/simple/object.ts +0 -42
- /package/assembly/deserialize/{simple → naive}/date.ts +0 -0
- /package/assembly/serialize/{simple → naive}/bool.ts +0 -0
- /package/assembly/serialize/{simple → naive}/date.ts +0 -0
- /package/assembly/serialize/{simple → naive}/struct.ts +0 -0
|
@@ -3,9 +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 { DESERIALIZE_ESCAPE_TABLE
|
|
6
|
+
import { DESERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
7
7
|
import { hex4_to_u16_swar } from "../../util/swar";
|
|
8
|
-
import { deserializeStringField_SWAR } from "../swar/string";
|
|
9
8
|
|
|
10
9
|
// @ts-expect-error: @lazy is a valid decorator
|
|
11
10
|
@lazy const SPLAT_5C = i16x8.splat(0x5c); // \
|
|
@@ -65,7 +64,10 @@ import { deserializeStringField_SWAR } from "../swar/string";
|
|
|
65
64
|
* @returns number of bytes written
|
|
66
65
|
*/
|
|
67
66
|
// @ts-expect-error: @inline is a valid decorator
|
|
68
|
-
@inline function copyStringFromSource_SIMD(
|
|
67
|
+
@inline function copyStringFromSource_SIMD(
|
|
68
|
+
srcStart: usize,
|
|
69
|
+
byteLength: usize,
|
|
70
|
+
): string {
|
|
69
71
|
if (byteLength == 0) return changetype<string>("");
|
|
70
72
|
// @ts-expect-error: __new is a runtime builtin
|
|
71
73
|
const out = __new(byteLength, idof<string>());
|
|
@@ -74,7 +76,11 @@ import { deserializeStringField_SWAR } from "../swar/string";
|
|
|
74
76
|
}
|
|
75
77
|
|
|
76
78
|
// @ts-expect-error: @inline is a valid decorator
|
|
77
|
-
@inline function writeStringToField_SIMD(
|
|
79
|
+
@inline function writeStringToField_SIMD(
|
|
80
|
+
dstFieldPtr: usize,
|
|
81
|
+
srcStart: usize,
|
|
82
|
+
byteLength: u32,
|
|
83
|
+
): void {
|
|
78
84
|
if (byteLength == 0) {
|
|
79
85
|
store<usize>(dstFieldPtr, changetype<usize>(""));
|
|
80
86
|
return;
|
|
@@ -96,106 +102,95 @@ import { deserializeStringField_SWAR } from "../swar/string";
|
|
|
96
102
|
memory.copy(stringPtr, srcStart, byteLength);
|
|
97
103
|
}
|
|
98
104
|
|
|
99
|
-
//
|
|
100
|
-
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
|
|
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(
|
|
116
|
+
payloadStart: usize,
|
|
117
|
+
escapeStart: usize,
|
|
118
|
+
srcEnd: usize,
|
|
119
|
+
): string {
|
|
104
120
|
const prefixLen = <u32>(escapeStart - payloadStart);
|
|
105
|
-
let srcStart = escapeStart;
|
|
106
|
-
const srcEnd16 = srcEnd - 16;
|
|
107
121
|
const outStart = bs.offset - bs.buffer;
|
|
108
|
-
bs.ensureSize(u32(srcEnd -
|
|
122
|
+
bs.ensureSize(<u32>(srcEnd - payloadStart) + 16); // +16 slack for overcopy
|
|
109
123
|
if (prefixLen != 0) {
|
|
110
124
|
memory.copy(bs.offset, payloadStart, prefixLen);
|
|
111
125
|
bs.offset += prefixLen;
|
|
112
126
|
}
|
|
113
127
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
store<v128>(bs.offset, block);
|
|
117
|
-
|
|
118
|
-
const eq5C = i16x8.eq(block, SPLAT_5C);
|
|
119
|
-
let mask = i16x8.bitmask(eq5C);
|
|
128
|
+
let srcStart = escapeStart;
|
|
129
|
+
const srcEnd16 = srcEnd >= 16 ? srcEnd - 16 : 0;
|
|
120
130
|
|
|
131
|
+
while (srcStart <= srcEnd16) {
|
|
132
|
+
const block = load<v128>(srcStart);
|
|
133
|
+
const mask = i16x8.bitmask(i16x8.eq(block, SPLAT_5C)); // backslash only
|
|
121
134
|
if (mask == 0) {
|
|
122
|
-
|
|
135
|
+
// Stream the first clean block cheaply.
|
|
136
|
+
store<v128>(bs.offset, block);
|
|
123
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
|
+
}
|
|
124
155
|
continue;
|
|
125
156
|
}
|
|
126
157
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
bs.offset += laneIdx - lastLane;
|
|
136
|
-
|
|
137
|
-
// Hot path (negative bias)
|
|
138
|
-
if (code !== 0x75) {
|
|
139
|
-
// Short escapes (\n \t \" \\)
|
|
140
|
-
const escaped = load<u16>(DESERIALIZE_ESCAPE_TABLE + code);
|
|
141
|
-
mask &= mask - i32(escaped === 0x5c);
|
|
142
|
-
store<u16>(bs.offset, escaped);
|
|
143
|
-
store<v128>(bs.offset, load<v128>(srcIdx, 4), 2);
|
|
144
|
-
|
|
145
|
-
const l6 = usize(laneIdx === 14);
|
|
146
|
-
// bs.offset -= (1 - l6) << 1;
|
|
147
|
-
bs.offset += 2;
|
|
148
|
-
srcStart += l6 << 1;
|
|
149
|
-
lastLane = laneIdx + 4;
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Unicode escape (\uXXXX)
|
|
154
|
-
const block = load<u64>(srcIdx, 4); // XXXX
|
|
155
|
-
const escaped = hex4_to_u16_swar(block);
|
|
156
|
-
|
|
157
|
-
store<u16>(bs.offset, escaped);
|
|
158
|
-
store<u64>(bs.offset, load<u64>(srcIdx, 12), 2);
|
|
159
|
-
|
|
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));
|
|
160
166
|
bs.offset += 2;
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if (lastLane < 16) {
|
|
168
|
-
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;
|
|
169
172
|
}
|
|
170
|
-
|
|
171
|
-
srcStart += 16 + srcChg;
|
|
172
173
|
}
|
|
173
174
|
|
|
175
|
+
// scalar tail (< 16 bytes remaining)
|
|
174
176
|
while (srcStart < srcEnd) {
|
|
175
|
-
const
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
// Early exit
|
|
180
|
-
if (block !== 0x5c) {
|
|
177
|
+
const char = load<u16>(srcStart);
|
|
178
|
+
if (char != BACK_SLASH) {
|
|
179
|
+
store<u16>(bs.offset, char);
|
|
181
180
|
bs.offset += 2;
|
|
181
|
+
srcStart += 2;
|
|
182
182
|
continue;
|
|
183
183
|
}
|
|
184
|
-
|
|
185
|
-
const code = load<u16>(srcStart);
|
|
184
|
+
const code = load<u16>(srcStart, 2);
|
|
186
185
|
if (code !== 0x75) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
srcStart += 2;
|
|
186
|
+
store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
|
|
187
|
+
bs.offset += 2;
|
|
188
|
+
srcStart += 4;
|
|
191
189
|
} else {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
store<u16>(bs.offset, escaped);
|
|
196
|
-
srcStart += 10;
|
|
190
|
+
store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(srcStart, 4)));
|
|
191
|
+
bs.offset += 2;
|
|
192
|
+
srcStart += 12;
|
|
197
193
|
}
|
|
198
|
-
bs.offset += 2;
|
|
199
194
|
}
|
|
200
195
|
|
|
201
196
|
return bs.sliceOut<string>(outStart);
|
|
@@ -238,12 +233,16 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
|
|
|
238
233
|
}
|
|
239
234
|
|
|
240
235
|
const laneIdx = usize(ctz(mask) << 1);
|
|
241
|
-
return inline.always(
|
|
236
|
+
return inline.always(
|
|
237
|
+
deserializeEscapedString_SIMD(payloadStart, srcStart + laneIdx, srcEnd),
|
|
238
|
+
);
|
|
242
239
|
}
|
|
243
240
|
|
|
244
241
|
while (srcStart < srcEnd) {
|
|
245
242
|
if (load<u16>(srcStart) == BACK_SLASH) {
|
|
246
|
-
return inline.always(
|
|
243
|
+
return inline.always(
|
|
244
|
+
deserializeEscapedString_SIMD(payloadStart, srcStart, srcEnd),
|
|
245
|
+
);
|
|
247
246
|
}
|
|
248
247
|
srcStart += 2;
|
|
249
248
|
}
|
|
@@ -251,52 +250,212 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
|
|
|
251
250
|
return copyStringFromSource_SIMD(payloadStart, srcEnd - payloadStart);
|
|
252
251
|
}
|
|
253
252
|
|
|
254
|
-
//
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
+
}
|
|
258
284
|
|
|
259
|
-
|
|
260
|
-
const payloadStart = srcStart + 2;
|
|
285
|
+
let srcStart = escapeStart;
|
|
261
286
|
const srcEnd16 = srcEnd >= 16 ? srcEnd - 16 : 0;
|
|
262
|
-
srcStart = payloadStart;
|
|
263
287
|
|
|
264
288
|
while (srcStart <= srcEnd16) {
|
|
265
289
|
const block = load<v128>(srcStart);
|
|
266
|
-
|
|
267
|
-
|
|
290
|
+
const mask = i16x8.bitmask(
|
|
291
|
+
v128.or(i16x8.eq(block, SPLAT_5C), i16x8.eq(block, SPLAT_22)),
|
|
292
|
+
);
|
|
268
293
|
if (mask == 0) {
|
|
294
|
+
// Stream the first clean block cheaply.
|
|
295
|
+
store<v128>(bs.offset, block);
|
|
296
|
+
bs.offset += 16;
|
|
269
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
|
+
}
|
|
270
323
|
continue;
|
|
271
324
|
}
|
|
272
325
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
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
|
+
}
|
|
278
341
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
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
|
+
}
|
|
283
353
|
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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>(
|
|
395
|
+
srcStart: usize,
|
|
396
|
+
srcEnd: usize,
|
|
397
|
+
dstObj: usize,
|
|
398
|
+
dstOffset: usize = 0,
|
|
399
|
+
): usize {
|
|
400
|
+
const dstFieldPtr = dstObj + dstOffset;
|
|
401
|
+
if (srcStart + 2 > srcEnd || load<u16>(srcStart) != QUOTE)
|
|
402
|
+
abort("Expected leading quote");
|
|
403
|
+
|
|
404
|
+
const payloadStart = srcStart + 2;
|
|
405
|
+
const srcEnd16 = srcEnd >= 16 ? srcEnd - 16 : 0;
|
|
406
|
+
srcStart = payloadStart;
|
|
407
|
+
|
|
408
|
+
while (srcStart <= srcEnd16) {
|
|
409
|
+
const block = load<v128>(srcStart);
|
|
410
|
+
const mask = i16x8.bitmask(
|
|
411
|
+
v128.or(i16x8.eq(block, SPLAT_5C), i16x8.eq(block, SPLAT_22)),
|
|
412
|
+
);
|
|
413
|
+
if (mask == 0) {
|
|
414
|
+
srcStart += 16;
|
|
415
|
+
continue;
|
|
416
|
+
}
|
|
288
417
|
|
|
289
|
-
|
|
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
|
+
);
|
|
290
438
|
}
|
|
291
439
|
|
|
292
440
|
while (srcStart < srcEnd) {
|
|
293
441
|
const char = load<u16>(srcStart);
|
|
294
442
|
if (char == QUOTE) {
|
|
295
|
-
writeStringToField_SIMD(
|
|
443
|
+
writeStringToField_SIMD(
|
|
444
|
+
dstFieldPtr,
|
|
445
|
+
payloadStart,
|
|
446
|
+
<u32>(srcStart - payloadStart),
|
|
447
|
+
);
|
|
296
448
|
return srcStart + 2;
|
|
297
449
|
}
|
|
298
450
|
if (char == BACK_SLASH) {
|
|
299
|
-
return
|
|
451
|
+
return inline.always(
|
|
452
|
+
deserializeEscapedStringField_SIMD(
|
|
453
|
+
payloadStart,
|
|
454
|
+
srcStart,
|
|
455
|
+
srcEnd,
|
|
456
|
+
dstFieldPtr,
|
|
457
|
+
),
|
|
458
|
+
);
|
|
300
459
|
}
|
|
301
460
|
srcStart += 2;
|
|
302
461
|
}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { JSON } from "../../..";
|
|
2
|
+
import { deserializeGenericArrayBody } from "./generic";
|
|
2
3
|
import { ensureArrayField } from "./shared";
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
@inline export function deserializeArbitraryArrayField(
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
@inline export function deserializeArbitraryArrayField(
|
|
7
|
+
srcStart: usize,
|
|
8
|
+
srcEnd: usize,
|
|
9
|
+
fieldPtr: usize,
|
|
10
|
+
): usize {
|
|
11
|
+
return deserializeGenericArrayBody<JSON.Value[]>(
|
|
12
|
+
srcStart,
|
|
13
|
+
srcEnd,
|
|
14
|
+
ensureArrayField<JSON.Value[]>(fieldPtr),
|
|
15
|
+
);
|
|
8
16
|
}
|
|
@@ -1,15 +1,20 @@
|
|
|
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
|
+
srcStart: usize,
|
|
9
|
+
srcEnd: usize,
|
|
10
|
+
out: T,
|
|
11
|
+
): usize {
|
|
8
12
|
let index = 0;
|
|
9
13
|
|
|
10
14
|
do {
|
|
11
15
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
|
|
12
16
|
srcStart += 2;
|
|
17
|
+
srcStart = skipWhitespace(srcStart, srcEnd);
|
|
13
18
|
if (srcStart >= srcEnd) break;
|
|
14
19
|
if (load<u16>(srcStart) == BRACKET_RIGHT) {
|
|
15
20
|
out.length = 0;
|
|
@@ -25,7 +30,11 @@ import { ensureArrayField, scanValueEnd } from "./shared";
|
|
|
25
30
|
value = changetype<valueof<T>>(instantiate<valueof<T>>());
|
|
26
31
|
out.push(value);
|
|
27
32
|
}
|
|
28
|
-
srcStart =
|
|
33
|
+
srcStart = deserializeFloatArrayBody<valueof<T>>(
|
|
34
|
+
srcStart,
|
|
35
|
+
srcEnd,
|
|
36
|
+
value,
|
|
37
|
+
);
|
|
29
38
|
if (!srcStart || srcStart >= srcEnd) break;
|
|
30
39
|
} else if (isArray<valueof<valueof<T>>>()) {
|
|
31
40
|
let value: valueof<T>;
|
|
@@ -35,7 +44,11 @@ import { ensureArrayField, scanValueEnd } from "./shared";
|
|
|
35
44
|
value = changetype<valueof<T>>(instantiate<valueof<T>>());
|
|
36
45
|
out.push(value);
|
|
37
46
|
}
|
|
38
|
-
srcStart =
|
|
47
|
+
srcStart = deserializeArrayArrayBody<valueof<T>>(
|
|
48
|
+
srcStart,
|
|
49
|
+
srcEnd,
|
|
50
|
+
value,
|
|
51
|
+
);
|
|
39
52
|
if (!srcStart || srcStart >= srcEnd) break;
|
|
40
53
|
} else {
|
|
41
54
|
const valueEnd = scanValueEnd(srcStart, srcEnd);
|
|
@@ -45,20 +58,30 @@ import { ensureArrayField, scanValueEnd } from "./shared";
|
|
|
45
58
|
if (index < out.length) {
|
|
46
59
|
valuePtr = changetype<usize>(unchecked(out[index]));
|
|
47
60
|
}
|
|
48
|
-
const value = JSON.__deserialize<valueof<T>>(
|
|
61
|
+
const value = JSON.__deserialize<valueof<T>>(
|
|
62
|
+
srcStart,
|
|
63
|
+
valueEnd,
|
|
64
|
+
valuePtr,
|
|
65
|
+
);
|
|
49
66
|
if (index < out.length) unchecked((out[index] = value));
|
|
50
67
|
else out.push(value);
|
|
51
68
|
srcStart = valueEnd;
|
|
52
69
|
}
|
|
53
70
|
|
|
71
|
+
srcStart = skipWhitespace(srcStart, srcEnd);
|
|
54
72
|
const code = load<u16>(srcStart);
|
|
55
73
|
if (code == COMMA) {
|
|
56
74
|
srcStart += 2;
|
|
75
|
+
srcStart = skipWhitespace(srcStart, srcEnd);
|
|
57
76
|
index++;
|
|
58
77
|
continue;
|
|
59
78
|
}
|
|
60
79
|
if (code == BRACKET_RIGHT) {
|
|
61
|
-
|
|
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;
|
|
62
85
|
return srcStart + 2;
|
|
63
86
|
}
|
|
64
87
|
break;
|
|
@@ -69,6 +92,14 @@ import { ensureArrayField, scanValueEnd } from "./shared";
|
|
|
69
92
|
}
|
|
70
93
|
|
|
71
94
|
|
|
72
|
-
@inline export function deserializeArrayArrayField<T extends unknown[][]>(
|
|
73
|
-
|
|
95
|
+
@inline export function deserializeArrayArrayField<T extends unknown[][]>(
|
|
96
|
+
srcStart: usize,
|
|
97
|
+
srcEnd: usize,
|
|
98
|
+
fieldPtr: usize,
|
|
99
|
+
): usize {
|
|
100
|
+
return deserializeArrayArrayBody<T>(
|
|
101
|
+
srcStart,
|
|
102
|
+
srcEnd,
|
|
103
|
+
ensureArrayField<T>(fieldPtr),
|
|
104
|
+
);
|
|
74
105
|
}
|
|
@@ -1,13 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
BRACKET_LEFT,
|
|
3
|
+
BRACKET_RIGHT,
|
|
4
|
+
COMMA,
|
|
5
|
+
FALSE_WORD_U64,
|
|
6
|
+
TRUE_WORD_U64,
|
|
7
|
+
} from "../../../custom/chars";
|
|
8
|
+
import { isSpace } from "../../../util";
|
|
2
9
|
import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
3
10
|
|
|
4
11
|
|
|
5
|
-
@inline
|
|
12
|
+
@inline function deserializeBooleanArrayBody<T extends boolean[]>(
|
|
13
|
+
srcStart: usize,
|
|
14
|
+
srcEnd: usize,
|
|
15
|
+
out: T,
|
|
16
|
+
): usize {
|
|
6
17
|
let index = 0;
|
|
7
18
|
|
|
8
19
|
do {
|
|
9
20
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
|
|
10
21
|
srcStart += 2;
|
|
22
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
11
23
|
if (srcStart >= srcEnd) break;
|
|
12
24
|
if (load<u16>(srcStart) == BRACKET_RIGHT) {
|
|
13
25
|
out.length = 0;
|
|
@@ -27,15 +39,18 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
|
27
39
|
break;
|
|
28
40
|
}
|
|
29
41
|
|
|
42
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
30
43
|
if (srcStart >= srcEnd) break;
|
|
31
44
|
const code = load<u16>(srcStart);
|
|
32
45
|
if (code == COMMA) {
|
|
33
46
|
srcStart += 2;
|
|
47
|
+
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
34
48
|
index++;
|
|
35
49
|
continue;
|
|
36
50
|
}
|
|
37
51
|
if (code == BRACKET_RIGHT) {
|
|
38
|
-
|
|
52
|
+
const nextLen = index + 1;
|
|
53
|
+
if (out.length != nextLen) out.length = nextLen;
|
|
39
54
|
return srcStart + 2;
|
|
40
55
|
}
|
|
41
56
|
break;
|
|
@@ -46,6 +61,14 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
|
46
61
|
}
|
|
47
62
|
|
|
48
63
|
|
|
49
|
-
@inline export function deserializeBooleanArrayField<T extends boolean[]>(
|
|
50
|
-
|
|
64
|
+
@inline export function deserializeBooleanArrayField<T extends boolean[]>(
|
|
65
|
+
srcStart: usize,
|
|
66
|
+
srcEnd: usize,
|
|
67
|
+
fieldPtr: usize,
|
|
68
|
+
): usize {
|
|
69
|
+
return deserializeBooleanArrayBody<T>(
|
|
70
|
+
srcStart,
|
|
71
|
+
srcEnd,
|
|
72
|
+
ensureArrayField<T>(fieldPtr),
|
|
73
|
+
);
|
|
51
74
|
}
|
|
@@ -1,8 +1,16 @@
|
|
|
1
1
|
import { JSON } from "../../..";
|
|
2
|
+
import { deserializeGenericArrayBody } from "./generic";
|
|
2
3
|
import { ensureArrayField } from "./shared";
|
|
3
4
|
|
|
4
5
|
|
|
5
|
-
@inline export function deserializeBoxArrayField<T extends JSON.Box<any>[]>(
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
@inline export function deserializeBoxArrayField<T extends JSON.Box<any>[]>(
|
|
7
|
+
srcStart: usize,
|
|
8
|
+
srcEnd: usize,
|
|
9
|
+
fieldPtr: usize,
|
|
10
|
+
): usize {
|
|
11
|
+
return deserializeGenericArrayBody<T>(
|
|
12
|
+
srcStart,
|
|
13
|
+
srcEnd,
|
|
14
|
+
ensureArrayField<T>(fieldPtr),
|
|
15
|
+
);
|
|
8
16
|
}
|