json-as 1.5.0 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +5 -1
- package/README.md +42 -0
- package/assembly/deserialize/error.ts +21 -0
- package/assembly/deserialize/index/arbitrary.ts +29 -8
- package/assembly/deserialize/index/array.ts +2 -1
- package/assembly/deserialize/index/bool.ts +1 -1
- package/assembly/deserialize/index/object.ts +3 -0
- package/assembly/deserialize/index/string.ts +37 -0
- package/assembly/deserialize/index.ts +0 -1
- package/assembly/deserialize/naive/array/arbitrary.ts +3 -2
- package/assembly/deserialize/naive/array/array.ts +1 -0
- package/assembly/deserialize/naive/array/bool.ts +21 -11
- package/assembly/deserialize/naive/array/box.ts +13 -33
- package/assembly/deserialize/naive/array/float.ts +23 -13
- package/assembly/deserialize/naive/array/integer.ts +33 -21
- package/assembly/deserialize/naive/array/object.ts +1 -0
- package/assembly/deserialize/naive/array/string.ts +29 -16
- package/assembly/deserialize/naive/bool.ts +17 -5
- package/assembly/deserialize/naive/float.ts +1 -2
- package/assembly/deserialize/naive/map.ts +339 -39
- package/assembly/deserialize/naive/object.ts +122 -65
- package/assembly/deserialize/naive/raw.ts +13 -1
- package/assembly/deserialize/naive/set.ts +0 -1
- package/assembly/deserialize/naive/staticarray.ts +3 -2
- package/assembly/deserialize/naive/string.ts +41 -52
- package/assembly/deserialize/parseMode.ts +216 -0
- package/assembly/deserialize/simd/array/integer.ts +6 -3
- package/assembly/deserialize/simd/float.ts +35 -29
- package/assembly/deserialize/simd/string.ts +79 -17
- package/assembly/deserialize/string-validation.ts +34 -0
- package/assembly/deserialize/swar/array/array.ts +63 -13
- package/assembly/deserialize/swar/array/bool.ts +14 -3
- package/assembly/deserialize/swar/array/float.ts +394 -13
- package/assembly/deserialize/swar/array/generic.ts +12 -2
- package/assembly/deserialize/swar/array/integer.ts +22 -62
- package/assembly/deserialize/swar/array/object.ts +11 -2
- package/assembly/deserialize/swar/array/shared.ts +1 -1
- package/assembly/deserialize/swar/array/string.ts +34 -5
- package/assembly/deserialize/swar/array/struct.ts +109 -15
- package/assembly/deserialize/swar/float.ts +19 -12
- package/assembly/deserialize/swar/string.ts +70 -111
- package/assembly/index.d.ts +6 -0
- package/assembly/index.ts +411 -33
- package/assembly/serialize/index/bool.ts +1 -1
- package/assembly/serialize/index/float.ts +1 -5
- package/assembly/serialize/index/integer.ts +1 -1
- package/assembly/serialize/index/object.ts +17 -0
- package/assembly/serialize/naive/array.ts +64 -0
- package/assembly/serialize/naive/float.ts +0 -5
- package/assembly/serialize/naive/map.ts +37 -0
- package/assembly/serialize/naive/set.ts +0 -1
- package/assembly/serialize/naive/staticarray.ts +0 -1
- package/assembly/serialize/naive/string.ts +0 -6
- package/assembly/serialize/naive/typedarray.ts +0 -1
- package/assembly/util/eisel-lemire.ts +179 -0
- package/assembly/util/prettyWhitespaceSimd.ts +69 -0
- package/assembly/util/scanValueEndSimd.ts +59 -36
- package/assembly/util/validateJson.ts +338 -0
- package/lib/as-bs.ts +28 -6
- package/package.json +13 -3
- package/transform/lib/index.d.ts +6 -0
- package/transform/lib/index.js +770 -111
- package/transform/lib/types.d.ts +1 -0
- package/transform/lib/types.js +3 -0
- package/assembly/deserialize/index/struct.ts +0 -1
- package/assembly/deserialize/naive/struct.ts +0 -21
|
@@ -0,0 +1,338 @@
|
|
|
1
|
+
// Scalar RFC 8259 syntax validator used by strict builds. It keeps an explicit
|
|
2
|
+
// container-state stack so hostile nesting is rejected without recursing
|
|
3
|
+
// through the Wasm call stack. This is intentionally a cold correctness pass:
|
|
4
|
+
// optimized backends retain their existing scanners, and the compile-time
|
|
5
|
+
// JSON_STRICT guard removes this pass entirely from normal builds.
|
|
6
|
+
|
|
7
|
+
const ARRAY_FIRST_OR_END: u8 = 0;
|
|
8
|
+
const ARRAY_VALUE: u8 = 1;
|
|
9
|
+
const ARRAY_COMMA_OR_END: u8 = 2;
|
|
10
|
+
const OBJECT_FIRST_KEY_OR_END: u8 = 3;
|
|
11
|
+
const OBJECT_KEY: u8 = 4;
|
|
12
|
+
const OBJECT_COLON: u8 = 5;
|
|
13
|
+
const OBJECT_VALUE: u8 = 6;
|
|
14
|
+
const OBJECT_COMMA_OR_END: u8 = 7;
|
|
15
|
+
// RFC 8259 permits implementations to set a maximum nesting depth. Keep the
|
|
16
|
+
// limit below typical Wasm shadow-stack exhaustion while accepting practical
|
|
17
|
+
// documents and reporting excessive nesting as a normal parse error.
|
|
18
|
+
const MAX_JSON_DEPTH: i32 = 256;
|
|
19
|
+
// Validation completes before target-specific parsing begins, so a single
|
|
20
|
+
// fixed scratch stack is sufficient and avoids allocating a managed Array for
|
|
21
|
+
// every strict parse. The public parser is already synchronous; a custom
|
|
22
|
+
// deserializer can only start a nested parse after this pass has returned.
|
|
23
|
+
// @ts-expect-error: decorator is valid for module-level scratch storage
|
|
24
|
+
@lazy const JSON_STATE_STACK = new StaticArray<u8>(MAX_JSON_DEPTH);
|
|
25
|
+
let jsonStateDepth: i32 = 0;
|
|
26
|
+
|
|
27
|
+
// Four UTF-16 lanes per word. These masks only identify candidates; every hit
|
|
28
|
+
// is re-read as u16 before it can affect validation, so lane-borrow false
|
|
29
|
+
// positives are harmless while plain string runs advance eight bytes at once.
|
|
30
|
+
const JSON_LANE_ONES: u64 = 0x0001_0001_0001_0001;
|
|
31
|
+
const JSON_LANE_HI: u64 = 0x0080_0080_0080_0080;
|
|
32
|
+
const JSON_QUOTE_SPLAT: u64 = 0x0022_0022_0022_0022;
|
|
33
|
+
const JSON_SLASH_SPLAT: u64 = 0x005c_005c_005c_005c;
|
|
34
|
+
const JSON_CONTROL_MASK: u64 = 0xffe0_ffe0_ffe0_ffe0;
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@inline
|
|
38
|
+
function jsonEqualPart(block: u64, splat: u64): u64 {
|
|
39
|
+
const diff = block ^ splat;
|
|
40
|
+
return (diff - JSON_LANE_ONES) & ~diff;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
@inline
|
|
45
|
+
function jsonStringSpecialMask(block: u64): u64 {
|
|
46
|
+
return (
|
|
47
|
+
(jsonEqualPart(block, JSON_QUOTE_SPLAT) |
|
|
48
|
+
jsonEqualPart(block, JSON_SLASH_SPLAT) |
|
|
49
|
+
jsonEqualPart(block & JSON_CONTROL_MASK, 0)) &
|
|
50
|
+
JSON_LANE_HI
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@inline
|
|
56
|
+
function isJSONWhitespace(code: u16): bool {
|
|
57
|
+
return code == 0x20 || code == 0x09 || code == 0x0a || code == 0x0d;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@inline
|
|
62
|
+
function isDigit(code: u16): bool {
|
|
63
|
+
return code >= 0x30 && code <= 0x39;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@inline
|
|
68
|
+
function isHexDigit(code: u16): bool {
|
|
69
|
+
return (
|
|
70
|
+
isDigit(code) ||
|
|
71
|
+
(code >= 0x41 && code <= 0x46) ||
|
|
72
|
+
(code >= 0x61 && code <= 0x66)
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function skipJSONWhitespace(ptr: usize, end: usize): usize {
|
|
77
|
+
while (ptr < end && isJSONWhitespace(load<u16>(ptr))) ptr += 2;
|
|
78
|
+
return ptr;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function scanJSONString(ptr: usize, end: usize): usize {
|
|
82
|
+
if (ptr >= end || load<u16>(ptr) != 0x22) return 0;
|
|
83
|
+
ptr += 2;
|
|
84
|
+
const end8 = end >= 8 ? end - 8 : 0;
|
|
85
|
+
|
|
86
|
+
while (ptr < end) {
|
|
87
|
+
while (ptr <= end8) {
|
|
88
|
+
const mask = jsonStringSpecialMask(load<u64>(ptr));
|
|
89
|
+
if (!mask) {
|
|
90
|
+
ptr += 8;
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const candidate = ptr + (usize(ctz(mask)) >> 3);
|
|
95
|
+
const code = load<u16>(candidate);
|
|
96
|
+
if (code == 0x22) return candidate + 2;
|
|
97
|
+
if (code < 0x20) return 0;
|
|
98
|
+
if (code != 0x5c) {
|
|
99
|
+
// A non-ASCII lane may collide with the filter's low bits.
|
|
100
|
+
ptr = candidate + 2;
|
|
101
|
+
continue;
|
|
102
|
+
}
|
|
103
|
+
ptr = candidate;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
if (ptr >= end) return 0;
|
|
108
|
+
const code = load<u16>(ptr);
|
|
109
|
+
if (code == 0x22) return ptr + 2;
|
|
110
|
+
if (code < 0x20) return 0;
|
|
111
|
+
|
|
112
|
+
if (code == 0x5c) {
|
|
113
|
+
ptr += 2;
|
|
114
|
+
if (ptr >= end) return 0;
|
|
115
|
+
const escape = load<u16>(ptr);
|
|
116
|
+
if (escape == 0x75) {
|
|
117
|
+
if (ptr + 10 > end) return 0;
|
|
118
|
+
if (
|
|
119
|
+
!isHexDigit(load<u16>(ptr, 2)) ||
|
|
120
|
+
!isHexDigit(load<u16>(ptr, 4)) ||
|
|
121
|
+
!isHexDigit(load<u16>(ptr, 6)) ||
|
|
122
|
+
!isHexDigit(load<u16>(ptr, 8))
|
|
123
|
+
)
|
|
124
|
+
return 0;
|
|
125
|
+
ptr += 10;
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (
|
|
129
|
+
escape != 0x22 &&
|
|
130
|
+
escape != 0x5c &&
|
|
131
|
+
escape != 0x2f &&
|
|
132
|
+
escape != 0x62 &&
|
|
133
|
+
escape != 0x66 &&
|
|
134
|
+
escape != 0x6e &&
|
|
135
|
+
escape != 0x72 &&
|
|
136
|
+
escape != 0x74
|
|
137
|
+
)
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
ptr += 2;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function scanJSONNumber(ptr: usize, end: usize): usize {
|
|
148
|
+
if (ptr < end && load<u16>(ptr) == 0x2d) ptr += 2;
|
|
149
|
+
if (ptr >= end) return 0;
|
|
150
|
+
|
|
151
|
+
let code = load<u16>(ptr);
|
|
152
|
+
if (code == 0x30) {
|
|
153
|
+
ptr += 2;
|
|
154
|
+
if (ptr < end && isDigit(load<u16>(ptr))) return 0;
|
|
155
|
+
} else if (code >= 0x31 && code <= 0x39) {
|
|
156
|
+
do {
|
|
157
|
+
ptr += 2;
|
|
158
|
+
} while (ptr < end && isDigit(load<u16>(ptr)));
|
|
159
|
+
} else {
|
|
160
|
+
return 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (ptr < end && load<u16>(ptr) == 0x2e) {
|
|
164
|
+
ptr += 2;
|
|
165
|
+
if (ptr >= end || !isDigit(load<u16>(ptr))) return 0;
|
|
166
|
+
do {
|
|
167
|
+
ptr += 2;
|
|
168
|
+
} while (ptr < end && isDigit(load<u16>(ptr)));
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
if (ptr < end) {
|
|
172
|
+
code = load<u16>(ptr);
|
|
173
|
+
if (code == 0x65 || code == 0x45) {
|
|
174
|
+
ptr += 2;
|
|
175
|
+
if (ptr < end) {
|
|
176
|
+
code = load<u16>(ptr);
|
|
177
|
+
if (code == 0x2b || code == 0x2d) ptr += 2;
|
|
178
|
+
}
|
|
179
|
+
if (ptr >= end || !isDigit(load<u16>(ptr))) return 0;
|
|
180
|
+
do {
|
|
181
|
+
ptr += 2;
|
|
182
|
+
} while (ptr < end && isDigit(load<u16>(ptr)));
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return ptr;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function scanLiteral(
|
|
190
|
+
ptr: usize,
|
|
191
|
+
end: usize,
|
|
192
|
+
a: u16,
|
|
193
|
+
b: u16,
|
|
194
|
+
c: u16,
|
|
195
|
+
d: u16,
|
|
196
|
+
e: u16 = 0,
|
|
197
|
+
): usize {
|
|
198
|
+
const byteLength: usize = e ? 10 : 8;
|
|
199
|
+
if (ptr + byteLength > end) return 0;
|
|
200
|
+
if (
|
|
201
|
+
load<u16>(ptr) != a ||
|
|
202
|
+
load<u16>(ptr, 2) != b ||
|
|
203
|
+
load<u16>(ptr, 4) != c ||
|
|
204
|
+
load<u16>(ptr, 6) != d ||
|
|
205
|
+
(e && load<u16>(ptr, 8) != e)
|
|
206
|
+
)
|
|
207
|
+
return 0;
|
|
208
|
+
return ptr + byteLength;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function scanJSONValue(ptr: usize, end: usize): usize {
|
|
212
|
+
if (ptr >= end) return 0;
|
|
213
|
+
const code = load<u16>(ptr);
|
|
214
|
+
if (code == 0x22) return scanJSONString(ptr, end);
|
|
215
|
+
if (code == 0x5b) {
|
|
216
|
+
if (jsonStateDepth >= MAX_JSON_DEPTH) return 0;
|
|
217
|
+
unchecked((JSON_STATE_STACK[jsonStateDepth++] = ARRAY_FIRST_OR_END));
|
|
218
|
+
return ptr + 2;
|
|
219
|
+
}
|
|
220
|
+
if (code == 0x7b) {
|
|
221
|
+
if (jsonStateDepth >= MAX_JSON_DEPTH) return 0;
|
|
222
|
+
unchecked((JSON_STATE_STACK[jsonStateDepth++] = OBJECT_FIRST_KEY_OR_END));
|
|
223
|
+
return ptr + 2;
|
|
224
|
+
}
|
|
225
|
+
if (code == 0x74) return scanLiteral(ptr, end, 0x74, 0x72, 0x75, 0x65); // true
|
|
226
|
+
if (code == 0x66) return scanLiteral(ptr, end, 0x66, 0x61, 0x6c, 0x73, 0x65); // false
|
|
227
|
+
if (code == 0x6e) return scanLiteral(ptr, end, 0x6e, 0x75, 0x6c, 0x6c); // null
|
|
228
|
+
return scanJSONNumber(ptr, end);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
// JSONTestSuite's implementation-defined UTF-16-without-BOM fixtures arrive
|
|
232
|
+
// in an AssemblyScript string as alternating byte-valued code units. Recover
|
|
233
|
+
// the intended UTF-16 code units before validating or dispatching them.
|
|
234
|
+
export function normalizeJSONEncoding(data: string): string {
|
|
235
|
+
const length = data.length;
|
|
236
|
+
if (length < 4) return data;
|
|
237
|
+
|
|
238
|
+
let byteIndex = -1;
|
|
239
|
+
if (data.charCodeAt(0) == 0) {
|
|
240
|
+
byteIndex = 0; // UTF-16BE bytes: NUL, code unit, NUL, code unit, ...
|
|
241
|
+
} else if (data.charCodeAt(1) == 0) {
|
|
242
|
+
byteIndex = 1; // UTF-16LE bytes: code unit, NUL, code unit, NUL, ...
|
|
243
|
+
} else {
|
|
244
|
+
return data;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
for (let i = byteIndex; i < length; i += 2) {
|
|
248
|
+
if (data.charCodeAt(i) != 0) return data;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
let out = "";
|
|
252
|
+
const codeIndex = byteIndex ^ 1;
|
|
253
|
+
for (let i = codeIndex; i < length; i += 2)
|
|
254
|
+
out += String.fromCharCode(data.charCodeAt(i));
|
|
255
|
+
return out;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function validateJSON(data: string): bool {
|
|
259
|
+
let ptr = changetype<usize>(data);
|
|
260
|
+
const end = ptr + ((<usize>data.length) << 1);
|
|
261
|
+
ptr = skipJSONWhitespace(ptr, end);
|
|
262
|
+
if (ptr >= end) return false;
|
|
263
|
+
|
|
264
|
+
jsonStateDepth = 0;
|
|
265
|
+
ptr = scanJSONValue(ptr, end);
|
|
266
|
+
if (!ptr) return false;
|
|
267
|
+
let rootComplete = jsonStateDepth == 0;
|
|
268
|
+
|
|
269
|
+
while (true) {
|
|
270
|
+
ptr = skipJSONWhitespace(ptr, end);
|
|
271
|
+
if (jsonStateDepth == 0) return rootComplete && ptr == end;
|
|
272
|
+
if (ptr >= end) return false;
|
|
273
|
+
|
|
274
|
+
const top = jsonStateDepth - 1;
|
|
275
|
+
const state = unchecked(JSON_STATE_STACK[top]);
|
|
276
|
+
const code = load<u16>(ptr);
|
|
277
|
+
|
|
278
|
+
if (state == ARRAY_FIRST_OR_END) {
|
|
279
|
+
if (code == 0x5d) {
|
|
280
|
+
jsonStateDepth--;
|
|
281
|
+
ptr += 2;
|
|
282
|
+
if (jsonStateDepth == 0) rootComplete = true;
|
|
283
|
+
} else {
|
|
284
|
+
unchecked((JSON_STATE_STACK[top] = ARRAY_COMMA_OR_END));
|
|
285
|
+
ptr = scanJSONValue(ptr, end);
|
|
286
|
+
if (!ptr) return false;
|
|
287
|
+
}
|
|
288
|
+
} else if (state == ARRAY_VALUE) {
|
|
289
|
+
unchecked((JSON_STATE_STACK[top] = ARRAY_COMMA_OR_END));
|
|
290
|
+
ptr = scanJSONValue(ptr, end);
|
|
291
|
+
if (!ptr) return false;
|
|
292
|
+
} else if (state == ARRAY_COMMA_OR_END) {
|
|
293
|
+
if (code == 0x2c) {
|
|
294
|
+
unchecked((JSON_STATE_STACK[top] = ARRAY_VALUE));
|
|
295
|
+
ptr += 2;
|
|
296
|
+
} else if (code == 0x5d) {
|
|
297
|
+
jsonStateDepth--;
|
|
298
|
+
ptr += 2;
|
|
299
|
+
if (jsonStateDepth == 0) rootComplete = true;
|
|
300
|
+
} else {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
} else if (state == OBJECT_FIRST_KEY_OR_END) {
|
|
304
|
+
if (code == 0x7d) {
|
|
305
|
+
jsonStateDepth--;
|
|
306
|
+
ptr += 2;
|
|
307
|
+
if (jsonStateDepth == 0) rootComplete = true;
|
|
308
|
+
} else {
|
|
309
|
+
ptr = scanJSONString(ptr, end);
|
|
310
|
+
if (!ptr) return false;
|
|
311
|
+
unchecked((JSON_STATE_STACK[top] = OBJECT_COLON));
|
|
312
|
+
}
|
|
313
|
+
} else if (state == OBJECT_KEY) {
|
|
314
|
+
ptr = scanJSONString(ptr, end);
|
|
315
|
+
if (!ptr) return false;
|
|
316
|
+
unchecked((JSON_STATE_STACK[top] = OBJECT_COLON));
|
|
317
|
+
} else if (state == OBJECT_COLON) {
|
|
318
|
+
if (code != 0x3a) return false;
|
|
319
|
+
unchecked((JSON_STATE_STACK[top] = OBJECT_VALUE));
|
|
320
|
+
ptr += 2;
|
|
321
|
+
} else if (state == OBJECT_VALUE) {
|
|
322
|
+
unchecked((JSON_STATE_STACK[top] = OBJECT_COMMA_OR_END));
|
|
323
|
+
ptr = scanJSONValue(ptr, end);
|
|
324
|
+
if (!ptr) return false;
|
|
325
|
+
} else {
|
|
326
|
+
if (code == 0x2c) {
|
|
327
|
+
unchecked((JSON_STATE_STACK[top] = OBJECT_KEY));
|
|
328
|
+
ptr += 2;
|
|
329
|
+
} else if (code == 0x7d) {
|
|
330
|
+
jsonStateDepth--;
|
|
331
|
+
ptr += 2;
|
|
332
|
+
if (jsonStateDepth == 0) rootComplete = true;
|
|
333
|
+
} else {
|
|
334
|
+
return false;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
}
|
package/lib/as-bs.ts
CHANGED
|
@@ -41,9 +41,13 @@ export namespace bs {
|
|
|
41
41
|
* @param newSize - The new size to incorporate into the average
|
|
42
42
|
*/
|
|
43
43
|
function updateTypicalSize(newSize: usize): void {
|
|
44
|
-
//
|
|
45
|
-
//
|
|
46
|
-
|
|
44
|
+
// Keep the delta unsigned in both directions. `(newSize - typicalSize)`
|
|
45
|
+
// underflows when output shrinks because usize arithmetic wraps.
|
|
46
|
+
if (newSize >= typicalSize) {
|
|
47
|
+
typicalSize += (newSize - typicalSize) >> EMA_ALPHA_SHIFT;
|
|
48
|
+
} else {
|
|
49
|
+
typicalSize -= (typicalSize - newSize) >> EMA_ALPHA_SHIFT;
|
|
50
|
+
}
|
|
47
51
|
}
|
|
48
52
|
|
|
49
53
|
function renewBuffer(newSize: usize): void {
|
|
@@ -66,9 +70,12 @@ export namespace bs {
|
|
|
66
70
|
|
|
67
71
|
function finalizeDynamicOutput(len: usize): void {
|
|
68
72
|
counter += 1;
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
73
|
+
// Resizing is intentionally sampled. Updating an EMA on every tiny
|
|
74
|
+
// stringify used to put several integer ops on the hottest return path even
|
|
75
|
+
// though we only consult it once per 256 outputs.
|
|
76
|
+
if ((counter & SHRINK_EVERY_N_MASK) == 0) {
|
|
77
|
+
updateTypicalSize(len);
|
|
78
|
+
if (bufferSize > typicalSize << 2) resize(u32(typicalSize << 1));
|
|
72
79
|
}
|
|
73
80
|
offset = buffer;
|
|
74
81
|
stackSize = 0;
|
|
@@ -106,6 +113,8 @@ export namespace bs {
|
|
|
106
113
|
export function reset(): void {
|
|
107
114
|
offset = buffer;
|
|
108
115
|
stackSize = 0;
|
|
116
|
+
cacheOutput = 0;
|
|
117
|
+
cacheOutputLen = 0;
|
|
109
118
|
pauseOffsets.length = 0;
|
|
110
119
|
pauseStackSizes.length = 0;
|
|
111
120
|
}
|
|
@@ -173,6 +182,19 @@ export namespace bs {
|
|
|
173
182
|
* @returns The new object containing the buffer's content.
|
|
174
183
|
*/
|
|
175
184
|
export function cpyOut<T>(): T {
|
|
185
|
+
if (cacheOutput !== 0) {
|
|
186
|
+
// A nested/internal stringify can still cover its complete local output
|
|
187
|
+
// with a cached source slice when its saved offset is zero. Copy that
|
|
188
|
+
// slice and restore the caller's staging state just like the regular
|
|
189
|
+
// paused-buffer branch below.
|
|
190
|
+
// @ts-expect-error: __new is a runtime builtin
|
|
191
|
+
const _out = __new(cacheOutputLen, idof<T>());
|
|
192
|
+
memory.copy(_out, cacheOutput, cacheOutputLen);
|
|
193
|
+
cacheOutput = 0;
|
|
194
|
+
cacheOutputLen = 0;
|
|
195
|
+
bs.loadState();
|
|
196
|
+
return changetype<T>(_out);
|
|
197
|
+
}
|
|
176
198
|
if (pauseOffsets.length == 0) {
|
|
177
199
|
const len = offset - buffer;
|
|
178
200
|
// @ts-expect-error: __new is a runtime builtin
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "json-as",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.0",
|
|
4
4
|
"author": "Jairus Tanaka <me@jairus.dev>",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -13,10 +13,11 @@
|
|
|
13
13
|
"@eslint/js": "^10.0.1",
|
|
14
14
|
"@types/node": "^25.9.2",
|
|
15
15
|
"as-heap-analyzer": "^1.2.0",
|
|
16
|
-
"as-test": "
|
|
16
|
+
"as-test": "github:JairusSW/as-test#332ae81bedeb68b98b824563c6efe7e050eaef4e",
|
|
17
17
|
"assemblyscript": "^0.28.18",
|
|
18
18
|
"assemblyscript-json": "^1.1.0",
|
|
19
19
|
"assemblyscript-prettier": "^3.0.4",
|
|
20
|
+
"canvas": "^3.2.3",
|
|
20
21
|
"chartjs-node-canvas": "^5.0.0",
|
|
21
22
|
"chartjs-plugin-datalabels": "^2.2.0",
|
|
22
23
|
"eslint": "^10.4.1",
|
|
@@ -29,7 +30,8 @@
|
|
|
29
30
|
"tinybench": "^6.0.2",
|
|
30
31
|
"try-as": "^1.1.4",
|
|
31
32
|
"typescript": "^6.0.3",
|
|
32
|
-
"typescript-eslint": "^8.60.1"
|
|
33
|
+
"typescript-eslint": "^8.60.1",
|
|
34
|
+
"warpo": "^3.1.1"
|
|
33
35
|
},
|
|
34
36
|
"bugs": {
|
|
35
37
|
"url": "https://github.com/JairusSW/json-as/issues"
|
|
@@ -46,6 +48,7 @@
|
|
|
46
48
|
"Loredana Cirstea",
|
|
47
49
|
"Accipiter Nisus",
|
|
48
50
|
"Deon Groenewald",
|
|
51
|
+
"Guillermo Diez",
|
|
49
52
|
"yoyo837"
|
|
50
53
|
],
|
|
51
54
|
"description": "The only JSON library you'll need for AssemblyScript with SIMD and SWAR",
|
|
@@ -101,8 +104,13 @@
|
|
|
101
104
|
"bench:all": "bash ./scripts/bench-all.sh",
|
|
102
105
|
"bench:as": "bash ./scripts/run-bench.as.sh",
|
|
103
106
|
"bench:js": "bash ./scripts/run-bench.js.sh",
|
|
107
|
+
"bench:runtimes": "bash ./scripts/run-bench.runtimes.sh",
|
|
108
|
+
"bench:warpo": "bash ./scripts/run-bench.warpo.sh",
|
|
104
109
|
"charts": "bun run charts:build && bun run charts:serve",
|
|
105
110
|
"charts:build": "bash ./scripts/build-charts.sh",
|
|
111
|
+
"charts:runtimes": "bun ./scripts/build-chart-runtimes.ts",
|
|
112
|
+
"charts:warpo": "bun ./scripts/build-chart-warpo.ts",
|
|
113
|
+
"charts:rfc": "bun ./scripts/build-rfc-coverage.ts",
|
|
106
114
|
"charts:publish": "bash ./scripts/publish-benchmarks.sh",
|
|
107
115
|
"charts:serve": "serve ./build/charts/",
|
|
108
116
|
"build:test": "JSON_DEBUG=0 JSON_WRITE=assembly/test.ts asc assembly/test.ts --transform ./transform -o ./build/test.wasm --textFile ./build/test.wat --enable simd --debug --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json",
|
|
@@ -112,10 +120,12 @@
|
|
|
112
120
|
"test:wasmer": "wasmer ./build/test.wasm",
|
|
113
121
|
"build:transform": "tsc -p ./transform",
|
|
114
122
|
"build:playground": "npm run build:transform && JSON_DEBUG=0 JSON_WRITE=assembly/playground.ts asc assembly/playground.ts --transform ./transform -o ./build/playground.wasm --textFile ./build/playground.wat --enable simd --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json",
|
|
123
|
+
"build:playground:nosimd": "npm run build:transform && JSON_DEBUG=0 JSON_WRITE=assembly/playground.ts asc assembly/playground.ts --transform ./transform -o ./build/playground.wasm --textFile ./build/playground.wat --disable simd --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json",
|
|
115
124
|
"build:pg": "npm run build:playground",
|
|
116
125
|
"run:playground": "wasmtime ./build/playground.wasm",
|
|
117
126
|
"run:pg": "npm run run:playground",
|
|
118
127
|
"playground": "npm run build:playground && npm run run:playground",
|
|
128
|
+
"playground:nosimd": "npm run build:playground:nosimd && npm run run:playground",
|
|
119
129
|
"build:playground:tmp": "asc assembly/playground.tmp.ts -o ./build/playground.tmp.wasm --textFile ./build/playground.tmp.wat -O3 --noAssert --uncheckedBehavior always --runtime incremental --enable bulk-memory --enable simd --use JSON_MODE=1 --exportStart start --exportRuntime",
|
|
120
130
|
"playground:tmp": "npm run build:playground:tmp && v8 --no-liftoff --module ./bench/runners/assemblyscript.js -- playground.tmp.wasm",
|
|
121
131
|
"pg:tmp": "npm run playground:tmp",
|
package/transform/lib/index.d.ts
CHANGED
|
@@ -21,6 +21,12 @@ export declare class JSONTransform extends Visitor {
|
|
|
21
21
|
private collectInheritedFieldMembers;
|
|
22
22
|
visitClassDeclarationRef(node: ClassDeclaration): void;
|
|
23
23
|
resolveType(type: string, source: Src, visited?: Set<string>): string;
|
|
24
|
+
private getDefaultSchema;
|
|
25
|
+
private getDefaultElementType;
|
|
26
|
+
private getDefaultExpressionJSON;
|
|
27
|
+
private getImplicitDefaultJSON;
|
|
28
|
+
private getDefaultObjectJSON;
|
|
29
|
+
private getDefaultMatcherSource;
|
|
24
30
|
visitClassDeclaration(node: ClassDeclaration): void;
|
|
25
31
|
getSchema(name: string): Schema | null;
|
|
26
32
|
generateEmptyMethods(node: ClassDeclaration): void;
|