json-as 1.5.0 → 1.6.1
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 +10 -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 +17 -7
- package/transform/lib/index.d.ts +6 -0
- package/transform/lib/index.js +773 -112
- 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
|
@@ -14,6 +14,11 @@ import {
|
|
|
14
14
|
import { isSpace, scanStringEnd } from "../../util";
|
|
15
15
|
import { deserializeFloat } from "../index/float";
|
|
16
16
|
import { deserializeString } from "../index/string";
|
|
17
|
+
import {
|
|
18
|
+
failProductionParse,
|
|
19
|
+
markProductionParseError,
|
|
20
|
+
takeProductionParseError,
|
|
21
|
+
} from "../error";
|
|
17
22
|
|
|
18
23
|
// "true" as a u64 of UTF-16 code units (LE).
|
|
19
24
|
const TRUE_WORD: u64 = 28429475166421108;
|
|
@@ -42,6 +47,7 @@ export function setParseSrc(s: string): void {
|
|
|
42
47
|
export function getParseSrc(): string {
|
|
43
48
|
return parseSrc;
|
|
44
49
|
}
|
|
50
|
+
export { markProductionParseError, takeProductionParseError };
|
|
45
51
|
|
|
46
52
|
export function deserializeObject(
|
|
47
53
|
srcStart: usize,
|
|
@@ -58,21 +64,19 @@ export function deserializeObject(
|
|
|
58
64
|
|
|
59
65
|
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
60
66
|
|
|
61
|
-
if (
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
throw new Error(
|
|
70
|
-
"Expected '}' at end of object at position " +
|
|
71
|
-
(srcEnd - srcStart).toString(),
|
|
72
|
-
);
|
|
67
|
+
if (
|
|
68
|
+
srcEnd == srcStart ||
|
|
69
|
+
load<u16>(srcStart) != BRACE_LEFT ||
|
|
70
|
+
load<u16>(srcEnd - 2) != BRACE_RIGHT
|
|
71
|
+
) {
|
|
72
|
+
failProductionParse();
|
|
73
|
+
return changetype<JSON.Obj>(0);
|
|
74
|
+
}
|
|
73
75
|
|
|
74
|
-
|
|
75
|
-
return out
|
|
76
|
+
out.reserveForParse((srcEnd - srcStart) >> 1);
|
|
77
|
+
return parseObjectBody(out, srcStart + 2, srcEnd) != 0
|
|
78
|
+
? out
|
|
79
|
+
: changetype<JSON.Obj>(0);
|
|
76
80
|
}
|
|
77
81
|
|
|
78
82
|
export function deserializeJsonArray(
|
|
@@ -88,15 +92,18 @@ export function deserializeJsonArray(
|
|
|
88
92
|
|
|
89
93
|
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
90
94
|
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
if (
|
|
96
|
+
srcEnd == srcStart ||
|
|
97
|
+
load<u16>(srcStart) != BRACKET_LEFT ||
|
|
98
|
+
load<u16>(srcEnd - 2) != BRACKET_RIGHT
|
|
99
|
+
) {
|
|
100
|
+
failProductionParse();
|
|
101
|
+
return changetype<JSON.Arr>(0);
|
|
102
|
+
}
|
|
97
103
|
|
|
98
|
-
parseArrayBodySlots(out, srcStart + 2, srcEnd)
|
|
99
|
-
|
|
104
|
+
return parseArrayBodySlots(out, srcStart + 2, srcEnd) != 0
|
|
105
|
+
? out
|
|
106
|
+
: changetype<JSON.Arr>(0);
|
|
100
107
|
}
|
|
101
108
|
|
|
102
109
|
/**
|
|
@@ -120,13 +127,14 @@ export function parseArrayBodySlots(
|
|
|
120
127
|
if (parseSrc.length != 0) {
|
|
121
128
|
out.pushRawSlot(parseSlotBits(srcStart, srcEnd));
|
|
122
129
|
} else {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
);
|
|
130
|
+
const value = parseValue(srcStart, srcEnd);
|
|
131
|
+
if (parseValueEnd == 0) return 0;
|
|
132
|
+
out.pushRawSlot(JSON.Value.bitsFrom<JSON.Value>(value));
|
|
126
133
|
}
|
|
134
|
+
if (parseValueEnd == 0) return 0;
|
|
127
135
|
srcStart = parseValueEnd;
|
|
128
136
|
}
|
|
129
|
-
return
|
|
137
|
+
return 0;
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
/**
|
|
@@ -137,9 +145,19 @@ export function parseArrayBodySlots(
|
|
|
137
145
|
* to be set (the caller guards), since a lazy slot points into it.
|
|
138
146
|
*/
|
|
139
147
|
function parseSlotBits(srcStart: usize, srcEnd: usize): u64 {
|
|
148
|
+
if (srcStart >= srcEnd) {
|
|
149
|
+
parseValueEnd = 0;
|
|
150
|
+
markProductionParseError();
|
|
151
|
+
return JSON.Value.nullBits();
|
|
152
|
+
}
|
|
140
153
|
const code = load<u16>(srcStart);
|
|
141
154
|
if (code == QUOTE || code == BRACE_LEFT || code == BRACKET_LEFT) {
|
|
142
155
|
const end = JSON.Util.scanValueEnd<JSON.Value>(srcStart, srcEnd);
|
|
156
|
+
if (end == 0) {
|
|
157
|
+
parseValueEnd = 0;
|
|
158
|
+
markProductionParseError();
|
|
159
|
+
return JSON.Value.nullBits();
|
|
160
|
+
}
|
|
143
161
|
parseValueEnd = end;
|
|
144
162
|
return JSON.Value.lazyBits(changetype<usize>(parseSrc), srcStart, end);
|
|
145
163
|
} else if (code - 48 <= 9 || code == 45) {
|
|
@@ -153,24 +171,33 @@ function parseSlotBits(srcStart: usize, srcEnd: usize): u64 {
|
|
|
153
171
|
parseValueEnd = p;
|
|
154
172
|
return JSON.Value.f64Bits(deserializeFloat<f64>(srcStart, p));
|
|
155
173
|
} else if (code == CHAR_T) {
|
|
156
|
-
if (load<u64>(srcStart) != TRUE_WORD)
|
|
157
|
-
|
|
174
|
+
if (srcEnd - srcStart < 8 || load<u64>(srcStart) != TRUE_WORD) {
|
|
175
|
+
parseValueEnd = 0;
|
|
176
|
+
markProductionParseError();
|
|
177
|
+
return JSON.Value.nullBits();
|
|
178
|
+
}
|
|
158
179
|
parseValueEnd = srcStart + 8;
|
|
159
180
|
return JSON.Value.boolBits(true);
|
|
160
181
|
} else if (code == CHAR_F) {
|
|
161
|
-
if (load<u64>(srcStart, 2) != ALSE_WORD)
|
|
162
|
-
|
|
182
|
+
if (srcEnd - srcStart < 10 || load<u64>(srcStart, 2) != ALSE_WORD) {
|
|
183
|
+
parseValueEnd = 0;
|
|
184
|
+
markProductionParseError();
|
|
185
|
+
return JSON.Value.nullBits();
|
|
186
|
+
}
|
|
163
187
|
parseValueEnd = srcStart + 10;
|
|
164
188
|
return JSON.Value.boolBits(false);
|
|
165
189
|
} else if (code == CHAR_N) {
|
|
166
|
-
if (load<u64>(srcStart) != NULL_WORD)
|
|
167
|
-
|
|
190
|
+
if (srcEnd - srcStart < 8 || load<u64>(srcStart) != NULL_WORD) {
|
|
191
|
+
parseValueEnd = 0;
|
|
192
|
+
markProductionParseError();
|
|
193
|
+
return JSON.Value.nullBits();
|
|
194
|
+
}
|
|
168
195
|
parseValueEnd = srcStart + 8;
|
|
169
196
|
return JSON.Value.nullBits();
|
|
170
197
|
}
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
);
|
|
198
|
+
parseValueEnd = 0;
|
|
199
|
+
markProductionParseError();
|
|
200
|
+
return JSON.Value.nullBits();
|
|
174
201
|
}
|
|
175
202
|
|
|
176
203
|
/**
|
|
@@ -185,12 +212,27 @@ export function lastValueEnd(): usize {
|
|
|
185
212
|
}
|
|
186
213
|
|
|
187
214
|
export function parseValue(srcStart: usize, srcEnd: usize): JSON.Value {
|
|
215
|
+
if (srcStart >= srcEnd) {
|
|
216
|
+
parseValueEnd = 0;
|
|
217
|
+
markProductionParseError();
|
|
218
|
+
return changetype<JSON.Value>(0);
|
|
219
|
+
}
|
|
188
220
|
const code = load<u16>(srcStart);
|
|
189
221
|
if (code == QUOTE) {
|
|
190
222
|
const end = scanStringEnd(srcStart, srcEnd);
|
|
191
|
-
if (end >= srcEnd)
|
|
223
|
+
if (end >= srcEnd) {
|
|
224
|
+
parseValueEnd = 0;
|
|
225
|
+
markProductionParseError();
|
|
226
|
+
return changetype<JSON.Value>(0);
|
|
227
|
+
}
|
|
192
228
|
parseValueEnd = end + 2;
|
|
193
|
-
|
|
229
|
+
const value = deserializeString(srcStart, end + 2);
|
|
230
|
+
if (changetype<usize>(value) == 0) {
|
|
231
|
+
parseValueEnd = 0;
|
|
232
|
+
markProductionParseError();
|
|
233
|
+
return changetype<JSON.Value>(0);
|
|
234
|
+
}
|
|
235
|
+
return JSON.Value.from(value);
|
|
194
236
|
} else if (code == BRACE_LEFT) {
|
|
195
237
|
const obj = new JSON.Obj();
|
|
196
238
|
parseValueEnd = parseObjectBody(obj, srcStart + 2, srcEnd);
|
|
@@ -210,24 +252,33 @@ export function parseValue(srcStart: usize, srcEnd: usize): JSON.Value {
|
|
|
210
252
|
parseValueEnd = p;
|
|
211
253
|
return JSON.Value.from(deserializeFloat<f64>(srcStart, p));
|
|
212
254
|
} else if (code == CHAR_T) {
|
|
213
|
-
if (load<u64>(srcStart) != TRUE_WORD)
|
|
214
|
-
|
|
255
|
+
if (srcEnd - srcStart < 8 || load<u64>(srcStart) != TRUE_WORD) {
|
|
256
|
+
parseValueEnd = 0;
|
|
257
|
+
markProductionParseError();
|
|
258
|
+
return changetype<JSON.Value>(0);
|
|
259
|
+
}
|
|
215
260
|
parseValueEnd = srcStart + 8;
|
|
216
261
|
return JSON.Value.from(true);
|
|
217
262
|
} else if (code == CHAR_F) {
|
|
218
|
-
if (load<u64>(srcStart, 2) != ALSE_WORD)
|
|
219
|
-
|
|
263
|
+
if (srcEnd - srcStart < 10 || load<u64>(srcStart, 2) != ALSE_WORD) {
|
|
264
|
+
parseValueEnd = 0;
|
|
265
|
+
markProductionParseError();
|
|
266
|
+
return changetype<JSON.Value>(0);
|
|
267
|
+
}
|
|
220
268
|
parseValueEnd = srcStart + 10;
|
|
221
269
|
return JSON.Value.from(false);
|
|
222
270
|
} else if (code == CHAR_N) {
|
|
223
|
-
if (load<u64>(srcStart) != NULL_WORD)
|
|
224
|
-
|
|
271
|
+
if (srcEnd - srcStart < 8 || load<u64>(srcStart) != NULL_WORD) {
|
|
272
|
+
parseValueEnd = 0;
|
|
273
|
+
markProductionParseError();
|
|
274
|
+
return changetype<JSON.Value>(0);
|
|
275
|
+
}
|
|
225
276
|
parseValueEnd = srcStart + 8;
|
|
226
277
|
return JSON.Value.from<usize>(0);
|
|
227
278
|
}
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
);
|
|
279
|
+
parseValueEnd = 0;
|
|
280
|
+
markProductionParseError();
|
|
281
|
+
return changetype<JSON.Value>(0);
|
|
231
282
|
}
|
|
232
283
|
|
|
233
284
|
/**
|
|
@@ -254,14 +305,20 @@ export function parseArrayBody(
|
|
|
254
305
|
// Defer strings and composites (the allocating shapes): store the raw
|
|
255
306
|
// slice, parse on first access. Cheap primitives stay eager below.
|
|
256
307
|
const end = JSON.Util.scanValueEnd<JSON.Value>(srcStart, srcEnd);
|
|
308
|
+
if (end == 0) {
|
|
309
|
+
markProductionParseError();
|
|
310
|
+
return 0;
|
|
311
|
+
}
|
|
257
312
|
out.push(JSON.Value.fromSlice(srcStart, end, parseSrc));
|
|
258
313
|
srcStart = end;
|
|
259
314
|
} else {
|
|
260
|
-
|
|
315
|
+
const value = parseValue(srcStart, srcEnd);
|
|
316
|
+
if (parseValueEnd == 0) return 0;
|
|
317
|
+
out.push(value);
|
|
261
318
|
srcStart = parseValueEnd;
|
|
262
319
|
}
|
|
263
320
|
}
|
|
264
|
-
return
|
|
321
|
+
return 0;
|
|
265
322
|
}
|
|
266
323
|
|
|
267
324
|
/**
|
|
@@ -274,6 +331,7 @@ export function parseObjectBody(
|
|
|
274
331
|
srcStart: usize,
|
|
275
332
|
srcEnd: usize,
|
|
276
333
|
): usize {
|
|
334
|
+
const objectStart = srcStart - 2;
|
|
277
335
|
// Anchor the source for this object's deferred value slots (start pointers
|
|
278
336
|
// into it). Set here so every caller - deserializeObject, the JSON.Obj[]
|
|
279
337
|
// array path, parseValue, the map path - gets it. Empty off the parse path,
|
|
@@ -287,44 +345,43 @@ export function parseObjectBody(
|
|
|
287
345
|
srcStart += 2;
|
|
288
346
|
continue;
|
|
289
347
|
}
|
|
290
|
-
if (code == BRACE_RIGHT)
|
|
348
|
+
if (code == BRACE_RIGHT) {
|
|
349
|
+
const objectEnd = srcStart + 2;
|
|
350
|
+
out._rawStart = objectStart;
|
|
351
|
+
out._rawEnd = objectEnd;
|
|
352
|
+
return objectEnd;
|
|
353
|
+
}
|
|
291
354
|
|
|
292
355
|
// --- key ---
|
|
293
|
-
if (code != QUOTE)
|
|
294
|
-
throw new Error(
|
|
295
|
-
"Unexpected character in JSON object '" +
|
|
296
|
-
String.fromCharCode(code) +
|
|
297
|
-
"' at position " +
|
|
298
|
-
(srcEnd - srcStart).toString(),
|
|
299
|
-
);
|
|
356
|
+
if (code != QUOTE) return failProductionParse();
|
|
300
357
|
const keyStart = srcStart + 2;
|
|
301
358
|
srcStart = scanStringEnd(srcStart, srcEnd);
|
|
302
|
-
if (srcStart >= srcEnd)
|
|
303
|
-
throw new Error("Unterminated string in JSON object");
|
|
359
|
+
if (srcStart >= srcEnd) return failProductionParse();
|
|
304
360
|
const keyEnd = srcStart;
|
|
305
361
|
srcStart += 2;
|
|
306
362
|
|
|
307
363
|
// --- colon ---
|
|
308
364
|
while (srcStart < srcEnd && isSpace((code = load<u16>(srcStart))))
|
|
309
365
|
srcStart += 2;
|
|
310
|
-
if (srcStart >= srcEnd || code != COLON)
|
|
311
|
-
throw new Error(
|
|
312
|
-
"Expected ':' after key at position " + (srcEnd - srcStart).toString(),
|
|
313
|
-
);
|
|
366
|
+
if (srcStart >= srcEnd || code != COLON) return failProductionParse();
|
|
314
367
|
srcStart += 2;
|
|
315
368
|
|
|
316
369
|
// --- value ---
|
|
317
370
|
while (srcStart < srcEnd && isSpace((code = load<u16>(srcStart))))
|
|
318
371
|
srcStart += 2;
|
|
372
|
+
if (srcStart >= srcEnd) return failProductionParse();
|
|
319
373
|
if (parseSrc.length != 0) {
|
|
320
374
|
// Parsing: store a NaN-boxed slot directly (strings/composites deferred,
|
|
321
375
|
// scalars eager) - no per-value JSON.Value object.
|
|
322
|
-
out.
|
|
376
|
+
out.appendParsedSlot(keyStart, keyEnd, parseSlotBits(srcStart, srcEnd));
|
|
377
|
+
if (parseValueEnd == 0) return 0;
|
|
323
378
|
} else {
|
|
324
379
|
// Off the parse path (no source anchor): box eagerly.
|
|
325
|
-
|
|
380
|
+
const value = parseValue(srcStart, srcEnd);
|
|
381
|
+
if (parseValueEnd == 0) return 0;
|
|
382
|
+
out.appendRaw(keyStart, keyEnd, value);
|
|
326
383
|
}
|
|
327
384
|
srcStart = parseValueEnd;
|
|
328
385
|
}
|
|
329
|
-
return
|
|
386
|
+
return 0;
|
|
330
387
|
}
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { JSON } from "../..";
|
|
2
2
|
import { ptrToStr } from "../../util/ptrToStr";
|
|
3
3
|
|
|
4
|
-
export function deserializeRaw(
|
|
4
|
+
export function deserializeRaw(
|
|
5
|
+
srcStart: usize,
|
|
6
|
+
srcEnd: usize,
|
|
7
|
+
dst: usize = 0,
|
|
8
|
+
): JSON.Raw {
|
|
9
|
+
const size = srcEnd - srcStart;
|
|
10
|
+
if (dst) {
|
|
11
|
+
const out = changetype<JSON.Raw>(dst);
|
|
12
|
+
const data = changetype<string>(__renew(changetype<usize>(out.data), size));
|
|
13
|
+
memory.copy(changetype<usize>(data), srcStart, size);
|
|
14
|
+
out.data = data;
|
|
15
|
+
return out;
|
|
16
|
+
}
|
|
5
17
|
return JSON.Raw.from(ptrToStr(srcStart, srcEnd));
|
|
6
18
|
}
|
|
@@ -63,7 +63,6 @@ function deserializeSetDirect<T extends Set<any>>(
|
|
|
63
63
|
if (load<u16>(srcStart) == BRACKET_RIGHT) return srcStart + 2;
|
|
64
64
|
|
|
65
65
|
while (srcStart < srcEnd) {
|
|
66
|
-
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
67
66
|
const code = load<u16>(srcStart);
|
|
68
67
|
|
|
69
68
|
// @ts-ignore: type
|
|
@@ -21,7 +21,8 @@ function materializeStaticArray<T extends StaticArray<any>>(
|
|
|
21
21
|
src: valueof<T>[],
|
|
22
22
|
dst: usize,
|
|
23
23
|
): T {
|
|
24
|
-
const
|
|
24
|
+
const len = src.length;
|
|
25
|
+
const byteLength = <usize>len * sizeof<valueof<T>>();
|
|
25
26
|
let out = dst;
|
|
26
27
|
|
|
27
28
|
if (!out) {
|
|
@@ -31,7 +32,7 @@ function materializeStaticArray<T extends StaticArray<any>>(
|
|
|
31
32
|
}
|
|
32
33
|
|
|
33
34
|
const typed = changetype<T>(out);
|
|
34
|
-
for (let i = 0; i <
|
|
35
|
+
for (let i = 0; i < len; i++) {
|
|
35
36
|
unchecked((typed[i] = unchecked(src[i])));
|
|
36
37
|
}
|
|
37
38
|
return typed;
|
|
@@ -3,6 +3,8 @@ import { OBJECT, TOTAL_OVERHEAD } from "rt/common";
|
|
|
3
3
|
import { __heap_base } from "memory";
|
|
4
4
|
import { BACK_SLASH, QUOTE } from "../../custom/chars";
|
|
5
5
|
import { DESERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
6
|
+
import { markProductionParseError } from "../error";
|
|
7
|
+
import { isValidStringEscape } from "../string-validation";
|
|
6
8
|
|
|
7
9
|
function hexDigit(c: u16): u32 {
|
|
8
10
|
if (c <= 0x39) return c - 0x30; // '0'-'9'
|
|
@@ -19,46 +21,10 @@ function hex4ToU16(srcStart: usize): u16 {
|
|
|
19
21
|
);
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
(c >= 0x61 && c <= 0x66)
|
|
27
|
-
);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// Strict RFC 8259 check for the char following a backslash, at [escPtr, srcEnd).
|
|
31
|
-
// Legal escapes: " \ / b f n r t and \uXXXX (4 hex digits). Throws otherwise:
|
|
32
|
-
// unknown escape letter, a trailing backslash, or a short / non-hex \u.
|
|
33
|
-
function validateEscape(escPtr: usize, srcEnd: usize): void {
|
|
34
|
-
if (escPtr >= srcEnd)
|
|
35
|
-
throw new Error("Invalid JSON string: incomplete escape");
|
|
36
|
-
const code = load<u16>(escPtr);
|
|
37
|
-
if (code == 0x75) {
|
|
38
|
-
// \uXXXX
|
|
39
|
-
if (escPtr + 10 > srcEnd)
|
|
40
|
-
throw new Error("Invalid JSON string: incomplete \\u escape");
|
|
41
|
-
if (
|
|
42
|
-
!isHexDigit(load<u16>(escPtr, 2)) ||
|
|
43
|
-
!isHexDigit(load<u16>(escPtr, 4)) ||
|
|
44
|
-
!isHexDigit(load<u16>(escPtr, 6)) ||
|
|
45
|
-
!isHexDigit(load<u16>(escPtr, 8))
|
|
46
|
-
)
|
|
47
|
-
throw new Error("Invalid JSON string: \\u escape needs 4 hex digits");
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
// short escapes: " \ / b f n r t
|
|
51
|
-
if (
|
|
52
|
-
code != 0x22 &&
|
|
53
|
-
code != 0x5c &&
|
|
54
|
-
code != 0x2f &&
|
|
55
|
-
code != 0x62 &&
|
|
56
|
-
code != 0x66 &&
|
|
57
|
-
code != 0x6e &&
|
|
58
|
-
code != 0x72 &&
|
|
59
|
-
code != 0x74
|
|
60
|
-
)
|
|
61
|
-
throw new Error("Invalid JSON string: illegal escape");
|
|
24
|
+
// RFC 8259 check for the char following a backslash, at [escPtr, srcEnd).
|
|
25
|
+
// Legal escapes: " \ / b f n r t and \uXXXX (4 hex digits).
|
|
26
|
+
function validateEscape(escPtr: usize, srcEnd: usize): bool {
|
|
27
|
+
return isValidStringEscape(escPtr - 2, srcEnd);
|
|
62
28
|
}
|
|
63
29
|
|
|
64
30
|
export function deserializeString_NAIVE(
|
|
@@ -72,8 +38,10 @@ export function deserializeString_NAIVE(
|
|
|
72
38
|
srcEnd - srcStart < 4 ||
|
|
73
39
|
load<u16>(srcStart) != QUOTE ||
|
|
74
40
|
load<u16>(srcEnd - 2) != QUOTE
|
|
75
|
-
)
|
|
76
|
-
|
|
41
|
+
) {
|
|
42
|
+
markProductionParseError();
|
|
43
|
+
return changetype<string>(0);
|
|
44
|
+
}
|
|
77
45
|
// Strip quotes
|
|
78
46
|
srcStart += 2;
|
|
79
47
|
srcEnd -= 2;
|
|
@@ -88,13 +56,20 @@ export function deserializeString_NAIVE(
|
|
|
88
56
|
// Early exit
|
|
89
57
|
if (block !== 0x5c) {
|
|
90
58
|
// RFC 8259: literal control chars (U+0000..U+001F) must be escaped.
|
|
91
|
-
if (block < 0x20)
|
|
92
|
-
|
|
59
|
+
if (block < 0x20) {
|
|
60
|
+
bs.offset = bs.buffer + outStart;
|
|
61
|
+
markProductionParseError();
|
|
62
|
+
return changetype<string>(0);
|
|
63
|
+
}
|
|
93
64
|
bs.offset += 2;
|
|
94
65
|
continue;
|
|
95
66
|
}
|
|
96
67
|
|
|
97
|
-
validateEscape(srcStart, srcEnd)
|
|
68
|
+
if (!validateEscape(srcStart, srcEnd)) {
|
|
69
|
+
bs.offset = bs.buffer + outStart;
|
|
70
|
+
markProductionParseError();
|
|
71
|
+
return changetype<string>(0);
|
|
72
|
+
}
|
|
98
73
|
const code = load<u16>(srcStart);
|
|
99
74
|
if (code !== 0x75) {
|
|
100
75
|
// Short escapes (\n \t \" \\)
|
|
@@ -191,7 +166,7 @@ function deserializeEscapedStringField_NAIVE(
|
|
|
191
166
|
bs.offset += 2;
|
|
192
167
|
}
|
|
193
168
|
|
|
194
|
-
|
|
169
|
+
markProductionParseError();
|
|
195
170
|
return 0;
|
|
196
171
|
}
|
|
197
172
|
|
|
@@ -206,12 +181,26 @@ export function deserializeStringField_NAIVE<T extends string | null>(
|
|
|
206
181
|
dstObj: usize,
|
|
207
182
|
dstOffset: usize = 0,
|
|
208
183
|
): usize {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
184
|
+
if (srcStart + 2 > srcEnd || load<u16>(srcStart) != QUOTE) {
|
|
185
|
+
markProductionParseError();
|
|
186
|
+
return 0;
|
|
187
|
+
}
|
|
188
|
+
return deserializeStringFieldTrusted_NAIVE(
|
|
189
|
+
srcStart + 2,
|
|
190
|
+
srcEnd,
|
|
191
|
+
dstObj,
|
|
192
|
+
dstOffset,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
212
195
|
|
|
213
|
-
|
|
214
|
-
|
|
196
|
+
export function deserializeStringFieldTrusted_NAIVE(
|
|
197
|
+
payloadStart: usize,
|
|
198
|
+
srcEnd: usize,
|
|
199
|
+
dstObj: usize,
|
|
200
|
+
dstOffset: usize = 0,
|
|
201
|
+
): usize {
|
|
202
|
+
const dstFieldPtr = dstObj + dstOffset;
|
|
203
|
+
let srcStart = payloadStart;
|
|
215
204
|
|
|
216
205
|
// Scan for the closing quote without touching the scratch buffer. For the
|
|
217
206
|
// common escape-free case the bytes are a verbatim slice of the source, so we
|
|
@@ -238,6 +227,6 @@ export function deserializeStringField_NAIVE<T extends string | null>(
|
|
|
238
227
|
srcStart += 2;
|
|
239
228
|
}
|
|
240
229
|
|
|
241
|
-
|
|
230
|
+
markProductionParseError();
|
|
242
231
|
return 0;
|
|
243
232
|
}
|