json-as 1.3.8 → 1.4.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 +24 -0
- package/README.md +49 -1
- package/assembly/deserialize/index/arbitrary.ts +2 -2
- package/assembly/deserialize/naive/array/arbitrary.ts +3 -136
- package/assembly/deserialize/naive/array/array.ts +30 -1
- package/assembly/deserialize/naive/array/integer.ts +1 -6
- package/assembly/deserialize/naive/array/map.ts +10 -14
- package/assembly/deserialize/naive/array/object.ts +10 -14
- package/assembly/deserialize/naive/float.ts +2 -4
- package/assembly/deserialize/naive/integer.ts +1 -2
- package/assembly/deserialize/naive/map.ts +40 -202
- package/assembly/deserialize/naive/object.ts +153 -174
- package/assembly/deserialize/naive/set.ts +1 -2
- package/assembly/deserialize/naive/staticarray.ts +1 -2
- package/assembly/deserialize/naive/string.ts +65 -18
- package/assembly/deserialize/naive/typedarray.ts +1 -2
- package/assembly/deserialize/naive/unsigned.ts +1 -2
- package/assembly/deserialize/simd/array/integer.ts +3 -6
- package/assembly/deserialize/simd/float.ts +2 -7
- package/assembly/deserialize/simd/integer.ts +4 -8
- package/assembly/deserialize/simd/string.ts +16 -21
- package/assembly/deserialize/swar/array/array.ts +1 -2
- package/assembly/deserialize/swar/array/bool.ts +1 -2
- package/assembly/deserialize/swar/array/float.ts +2 -3
- package/assembly/deserialize/swar/array/generic.ts +1 -2
- package/assembly/deserialize/swar/array/integer.ts +6 -11
- package/assembly/deserialize/swar/array/object.ts +1 -2
- package/assembly/deserialize/swar/array/shared.ts +3 -8
- package/assembly/deserialize/swar/array/string.ts +1 -2
- package/assembly/deserialize/swar/array/struct.ts +1 -1
- package/assembly/deserialize/swar/float.ts +3 -8
- package/assembly/deserialize/swar/integer.ts +4 -8
- package/assembly/deserialize/swar/string.ts +29 -41
- package/assembly/index.d.ts +248 -15
- package/assembly/index.ts +468 -146
- package/assembly/serialize/index/object.ts +18 -15
- package/assembly/serialize/naive/string.ts +9 -2
- package/assembly/serialize/swar/string.ts +1 -2
- package/assembly/util/atoi.ts +1 -2
- package/assembly/util/dragonbox.ts +0 -8
- package/assembly/util/itoa-fast.ts +3 -6
- package/assembly/util/parsefloat-fast.ts +1 -2
- package/assembly/util/scanValueEnd.ts +1 -2
- package/assembly/util/scanValueEndSimd.ts +160 -0
- package/assembly/util/scanValueEndSwar.ts +142 -0
- package/assembly/util/scientific.ts +3 -6
- package/assembly/util/simd-int.ts +4 -8
- package/assembly/util/snp.ts +1 -5
- package/assembly/util/stringScan.ts +2 -4
- package/assembly/util/swar-int.ts +3 -6
- package/lib/as-bs.ts +37 -0
- package/package.json +14 -4
- package/transform/lib/builder.d.ts +0 -1
- package/transform/lib/builder.js +0 -1
- package/transform/lib/index.d.ts +0 -1
- package/transform/lib/index.js +537 -290
- package/transform/lib/linkers/alias.d.ts +0 -1
- package/transform/lib/linkers/alias.js +0 -1
- package/transform/lib/linkers/custom.d.ts +0 -1
- package/transform/lib/linkers/custom.js +0 -1
- package/transform/lib/linkers/imports.d.ts +0 -1
- package/transform/lib/linkers/imports.js +0 -1
- package/transform/lib/types.d.ts +3 -2
- package/transform/lib/types.js +2 -1
- package/transform/lib/util.d.ts +0 -1
- package/transform/lib/util.js +0 -1
- package/transform/lib/visitor.d.ts +0 -1
- package/transform/lib/visitor.js +0 -1
- package/transform/lib/builder.d.ts.map +0 -1
- package/transform/lib/builder.js.map +0 -1
- package/transform/lib/index.d.ts.map +0 -1
- package/transform/lib/index.js.map +0 -1
- package/transform/lib/linkers/alias.d.ts.map +0 -1
- package/transform/lib/linkers/alias.js.map +0 -1
- package/transform/lib/linkers/custom.d.ts.map +0 -1
- package/transform/lib/linkers/custom.js.map +0 -1
- package/transform/lib/linkers/imports.d.ts.map +0 -1
- package/transform/lib/linkers/imports.js.map +0 -1
- package/transform/lib/types.d.ts.map +0 -1
- package/transform/lib/types.js.map +0 -1
- package/transform/lib/util.d.ts.map +0 -1
- package/transform/lib/util.js.map +0 -1
- package/transform/lib/visitor.d.ts.map +0 -1
- package/transform/lib/visitor.js.map +0 -1
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import { JSON } from "../..";
|
|
2
2
|
import {
|
|
3
|
-
BACK_SLASH,
|
|
4
3
|
COMMA,
|
|
5
|
-
CHAR_F,
|
|
6
4
|
BRACE_LEFT,
|
|
7
|
-
BRACKET_LEFT,
|
|
8
|
-
CHAR_N,
|
|
9
5
|
QUOTE,
|
|
10
6
|
BRACE_RIGHT,
|
|
11
|
-
BRACKET_RIGHT,
|
|
12
|
-
CHAR_T,
|
|
13
7
|
COLON,
|
|
14
8
|
} from "../../custom/chars";
|
|
15
|
-
import { isSpace,
|
|
9
|
+
import { isSpace, scanStringEnd } from "../../util";
|
|
16
10
|
import { scanValueEnd } from "../../util/scanValueEnd";
|
|
11
|
+
import { lastValueEnd, parseValue } from "./object";
|
|
17
12
|
|
|
18
13
|
// @ts-ignore: Decorator is valid here
|
|
19
14
|
@inline function deserializeMapKey<T>(start: usize, end: usize): T {
|
|
@@ -31,13 +26,7 @@ export function deserializeMap<T extends Map<any, any>>(
|
|
|
31
26
|
dst || changetype<usize>(instantiate<T>()),
|
|
32
27
|
);
|
|
33
28
|
|
|
34
|
-
|
|
35
|
-
let keyEnd: usize = 0;
|
|
36
|
-
let isKey = false;
|
|
37
|
-
let depth = 0;
|
|
38
|
-
let lastIndex: usize = 0;
|
|
39
|
-
|
|
40
|
-
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2; // would like to optimize this later
|
|
29
|
+
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
41
30
|
|
|
42
31
|
if (srcStart - srcEnd == 0)
|
|
43
32
|
throw new Error("Input string had zero length or was all whitespace");
|
|
@@ -52,190 +41,26 @@ export function deserializeMap<T extends Map<any, any>>(
|
|
|
52
41
|
(srcEnd - srcStart).toString(),
|
|
53
42
|
);
|
|
54
43
|
|
|
55
|
-
srcStart
|
|
56
|
-
|
|
57
|
-
let code = load<u16>(srcStart); // while (isSpace(code)) code = load<u16>(srcStart += 2);
|
|
58
|
-
if (keyStart == 0) {
|
|
59
|
-
if (code == QUOTE && isUnescapedQuote(srcStart)) {
|
|
60
|
-
if (isKey) {
|
|
61
|
-
keyStart = lastIndex;
|
|
62
|
-
keyEnd = srcStart;
|
|
63
|
-
// console.log("Key: " + ptrToStr(lastIndex, srcStart));
|
|
64
|
-
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart + 2)));
|
|
65
|
-
while (isSpace((code = load<u16>((srcStart += 2))))) {}
|
|
66
|
-
if (code !== COLON)
|
|
67
|
-
throw new Error(
|
|
68
|
-
"Expected ':' after key at position " +
|
|
69
|
-
(srcEnd - srcStart).toString(),
|
|
70
|
-
);
|
|
71
|
-
isKey = false;
|
|
72
|
-
} else {
|
|
73
|
-
// console.log("Got key start");
|
|
74
|
-
isKey = true; // i don't like this
|
|
75
|
-
lastIndex = srcStart + 2;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
// isKey = !isKey;
|
|
79
|
-
srcStart += 2;
|
|
80
|
-
} else {
|
|
81
|
-
if (code == QUOTE) {
|
|
82
|
-
lastIndex = srcStart;
|
|
83
|
-
srcStart = scanStringEnd(srcStart, srcEnd);
|
|
84
|
-
if (srcStart >= srcEnd)
|
|
85
|
-
throw new Error("Unterminated string in JSON object");
|
|
86
|
-
// @ts-ignore: type
|
|
87
|
-
out.set(
|
|
88
|
-
deserializeMapKey<indexof<T>>(keyStart, keyEnd),
|
|
89
|
-
JSON.__deserialize<valueof<T>>(lastIndex, srcStart + 2),
|
|
90
|
-
);
|
|
91
|
-
srcStart += 2;
|
|
92
|
-
keyStart = 0;
|
|
93
|
-
continue;
|
|
94
|
-
} else if (code - 48 <= 9 || code == 45) {
|
|
95
|
-
lastIndex = srcStart;
|
|
96
|
-
srcStart += 2;
|
|
97
|
-
while (srcStart < srcEnd) {
|
|
98
|
-
const code = load<u16>(srcStart);
|
|
99
|
-
if (code == COMMA || code == BRACE_RIGHT || isSpace(code)) {
|
|
100
|
-
// console.log("Value (number): " + ptrToStr(lastIndex, srcStart));
|
|
101
|
-
// @ts-ignore: type
|
|
102
|
-
out.set(
|
|
103
|
-
deserializeMapKey<indexof<T>>(keyStart, keyEnd),
|
|
104
|
-
JSON.__deserialize<valueof<T>>(lastIndex, srcStart),
|
|
105
|
-
);
|
|
106
|
-
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
107
|
-
// /* empty */
|
|
108
|
-
// }
|
|
109
|
-
srcStart += 2;
|
|
110
|
-
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
111
|
-
keyStart = 0;
|
|
112
|
-
break;
|
|
113
|
-
}
|
|
114
|
-
srcStart += 2;
|
|
115
|
-
}
|
|
116
|
-
} else if (code == BRACE_LEFT) {
|
|
117
|
-
lastIndex = srcStart;
|
|
118
|
-
depth++;
|
|
119
|
-
srcStart += 2;
|
|
120
|
-
while (srcStart < srcEnd) {
|
|
121
|
-
const code = load<u16>(srcStart);
|
|
122
|
-
if (code == QUOTE) {
|
|
123
|
-
srcStart = scanStringEnd(srcStart, srcEnd);
|
|
124
|
-
if (srcStart >= srcEnd)
|
|
125
|
-
throw new Error("Unterminated string in JSON object");
|
|
126
|
-
} else if (code == BRACE_RIGHT) {
|
|
127
|
-
if (--depth == 0) {
|
|
128
|
-
// console.log("Value (object): " + ptrToStr(lastIndex, srcStart + 2));
|
|
129
|
-
// @ts-ignore: type
|
|
130
|
-
out.set(
|
|
131
|
-
deserializeMapKey<indexof<T>>(keyStart, keyEnd),
|
|
132
|
-
JSON.__deserialize<valueof<T>>(lastIndex, (srcStart += 2)),
|
|
133
|
-
);
|
|
134
|
-
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
135
|
-
keyStart = 0;
|
|
136
|
-
// while (isSpace(load<u16>(srcStart))) {
|
|
137
|
-
// /* empty */
|
|
138
|
-
// }
|
|
139
|
-
break;
|
|
140
|
-
}
|
|
141
|
-
} else if (code == BRACE_LEFT) depth++;
|
|
142
|
-
srcStart += 2;
|
|
143
|
-
}
|
|
144
|
-
} else if (code == BRACKET_LEFT) {
|
|
145
|
-
lastIndex = srcStart;
|
|
146
|
-
depth++;
|
|
147
|
-
srcStart += 2;
|
|
148
|
-
while (srcStart < srcEnd) {
|
|
149
|
-
const code = load<u16>(srcStart);
|
|
150
|
-
if (code == QUOTE) {
|
|
151
|
-
srcStart = scanStringEnd(srcStart, srcEnd);
|
|
152
|
-
if (srcStart >= srcEnd)
|
|
153
|
-
throw new Error("Unterminated string in JSON object");
|
|
154
|
-
} else if (code == BRACKET_RIGHT) {
|
|
155
|
-
if (--depth == 0) {
|
|
156
|
-
// console.log("Value (array): " + ptrToStr(lastIndex, srcStart + 2));
|
|
157
|
-
// @ts-ignore: type
|
|
158
|
-
out.set(
|
|
159
|
-
deserializeMapKey<indexof<T>>(keyStart, keyEnd),
|
|
160
|
-
JSON.__deserialize<valueof<T>>(lastIndex, (srcStart += 2)),
|
|
161
|
-
);
|
|
162
|
-
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
163
|
-
keyStart = 0;
|
|
164
|
-
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
165
|
-
// /* empty */
|
|
166
|
-
// }
|
|
167
|
-
break;
|
|
168
|
-
}
|
|
169
|
-
} else if (code == BRACKET_LEFT) depth++;
|
|
170
|
-
srcStart += 2;
|
|
171
|
-
}
|
|
172
|
-
} else if (code == CHAR_T) {
|
|
173
|
-
if (load<u64>(srcStart) == 28429475166421108) {
|
|
174
|
-
// console.log("Value (bool): " + ptrToStr(srcStart, srcStart + 8));
|
|
175
|
-
// @ts-ignore: type
|
|
176
|
-
out.set(
|
|
177
|
-
deserializeMapKey<indexof<T>>(keyStart, keyEnd),
|
|
178
|
-
JSON.__deserialize<valueof<T>>(srcStart, (srcStart += 8)),
|
|
179
|
-
);
|
|
180
|
-
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
181
|
-
// /* empty */
|
|
182
|
-
// }
|
|
183
|
-
srcStart += 2;
|
|
184
|
-
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart)) + " " + (srcStart < srcEnd).toString());
|
|
185
|
-
keyStart = 0;
|
|
186
|
-
}
|
|
187
|
-
} else if (code == CHAR_F) {
|
|
188
|
-
if (load<u64>(srcStart, 2) == 28429466576093281) {
|
|
189
|
-
// console.log("Value (bool): " + ptrToStr(srcStart, srcStart + 10));
|
|
190
|
-
// @ts-ignore: type
|
|
191
|
-
out.set(
|
|
192
|
-
deserializeMapKey<indexof<T>>(keyStart, keyEnd),
|
|
193
|
-
JSON.__deserialize<valueof<T>>(srcStart, (srcStart += 10)),
|
|
194
|
-
);
|
|
195
|
-
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
196
|
-
// /* empty */
|
|
197
|
-
// }
|
|
198
|
-
srcStart += 2;
|
|
199
|
-
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
200
|
-
keyStart = 0;
|
|
201
|
-
}
|
|
202
|
-
} else if (code == CHAR_N) {
|
|
203
|
-
if (load<u64>(srcStart) == 30399761348886638) {
|
|
204
|
-
// console.log("Value (null): " + ptrToStr(srcStart, srcStart + 8));
|
|
205
|
-
// @ts-ignore: type
|
|
206
|
-
out.set(
|
|
207
|
-
deserializeMapKey<indexof<T>>(keyStart, keyEnd),
|
|
208
|
-
JSON.__deserialize<valueof<T>>(srcStart, (srcStart += 8)),
|
|
209
|
-
);
|
|
210
|
-
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
211
|
-
/* empty */
|
|
212
|
-
// }
|
|
213
|
-
srcStart += 2;
|
|
214
|
-
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
215
|
-
keyStart = 0;
|
|
216
|
-
}
|
|
217
|
-
} else if (isSpace(code)) {
|
|
218
|
-
srcStart += 2;
|
|
219
|
-
} else {
|
|
220
|
-
throw new Error(
|
|
221
|
-
"Unexpected character in JSON object '" +
|
|
222
|
-
String.fromCharCode(code) +
|
|
223
|
-
"' at position " +
|
|
224
|
-
(srcEnd - srcStart).toString(),
|
|
225
|
-
);
|
|
226
|
-
}
|
|
227
|
-
}
|
|
228
|
-
}
|
|
229
|
-
return out;
|
|
44
|
+
deserializeMapBody<T>(srcStart, srcEnd, changetype<T>(out));
|
|
45
|
+
return changetype<T>(out);
|
|
230
46
|
}
|
|
231
47
|
|
|
232
|
-
|
|
233
|
-
|
|
48
|
+
/**
|
|
49
|
+
* Shared single-pass map-body parser used by both the top-level and struct-field
|
|
50
|
+
* entry points. Dynamic `JSON.Value` values are parsed in one pass via
|
|
51
|
+
* {@link parseValue}; typed values are bounds-scanned with {@link scanValueEnd}
|
|
52
|
+
* because their generated deserializers take exact `(start, end)` bounds.
|
|
53
|
+
*/
|
|
54
|
+
export function deserializeMapBody<T extends Map<any, any>>(
|
|
234
55
|
srcStart: usize,
|
|
235
56
|
srcEnd: usize,
|
|
236
57
|
out: T,
|
|
237
58
|
): usize {
|
|
238
|
-
|
|
59
|
+
let arbitraryValue = false;
|
|
60
|
+
if (isManaged<valueof<T>>() || isReference<valueof<T>>()) {
|
|
61
|
+
// @ts-ignore: instanceof on the (reference) value type
|
|
62
|
+
arbitraryValue = changetype<nonnull<valueof<T>>>(0) instanceof JSON.Value;
|
|
63
|
+
}
|
|
239
64
|
|
|
240
65
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACE_LEFT)
|
|
241
66
|
throw new Error("Failed to parse JSON!");
|
|
@@ -258,15 +83,24 @@ export function deserializeMap<T extends Map<any, any>>(
|
|
|
258
83
|
srcStart += 2;
|
|
259
84
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
260
85
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
86
|
+
if (isReference<valueof<T>>() && arbitraryValue) {
|
|
87
|
+
const val = parseValue(srcStart, srcEnd);
|
|
88
|
+
// @ts-ignore: type — valueof<T> is JSON.Value in this branch
|
|
89
|
+
changetype<nonnull<T>>(out).set(
|
|
90
|
+
deserializeMapKey<indexof<T>>(keyStart, keyEnd),
|
|
91
|
+
changetype<valueof<T>>(changetype<usize>(val)),
|
|
92
|
+
);
|
|
93
|
+
srcStart = lastValueEnd();
|
|
94
|
+
} else {
|
|
95
|
+
const valueEnd = scanValueEnd(srcStart, srcEnd);
|
|
96
|
+
if (!valueEnd || valueEnd <= srcStart) break;
|
|
97
|
+
// @ts-ignore: type
|
|
98
|
+
changetype<nonnull<T>>(out).set(
|
|
99
|
+
deserializeMapKey<indexof<T>>(keyStart, keyEnd),
|
|
100
|
+
JSON.__deserialize<valueof<T>>(srcStart, valueEnd),
|
|
101
|
+
);
|
|
102
|
+
srcStart = valueEnd;
|
|
103
|
+
}
|
|
270
104
|
|
|
271
105
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
272
106
|
if (srcStart >= srcEnd) break;
|
|
@@ -283,7 +117,7 @@ export function deserializeMap<T extends Map<any, any>>(
|
|
|
283
117
|
throw new Error("Failed to parse JSON!");
|
|
284
118
|
}
|
|
285
119
|
|
|
286
|
-
|
|
120
|
+
// @ts-ignore: Decorator is valid here
|
|
287
121
|
@inline export function deserializeMapField<T extends Map<any, any>>(
|
|
288
122
|
srcStart: usize,
|
|
289
123
|
srcEnd: usize,
|
|
@@ -295,6 +129,10 @@ export function deserializeMap<T extends Map<any, any>>(
|
|
|
295
129
|
if (!changetype<usize>(out)) {
|
|
296
130
|
out = changetype<T>(instantiate<T>());
|
|
297
131
|
store<T>(fieldPtr, out);
|
|
132
|
+
} else {
|
|
133
|
+
// Reusing an existing field map — clear it before repopulating. Fresh maps
|
|
134
|
+
// (deserializeMap / deserializeMapArray) skip this.
|
|
135
|
+
changetype<nonnull<T>>(out).clear();
|
|
298
136
|
}
|
|
299
137
|
return deserializeMapBody<T>(srcStart, srcEnd, out);
|
|
300
138
|
}
|
|
@@ -1,7 +1,5 @@
|
|
|
1
1
|
import { JSON } from "../..";
|
|
2
|
-
import { bs } from "../../../lib/as-bs";
|
|
3
2
|
import {
|
|
4
|
-
BACK_SLASH,
|
|
5
3
|
COMMA,
|
|
6
4
|
CHAR_F,
|
|
7
5
|
BRACE_LEFT,
|
|
@@ -13,12 +11,26 @@ import {
|
|
|
13
11
|
CHAR_T,
|
|
14
12
|
COLON,
|
|
15
13
|
} from "../../custom/chars";
|
|
16
|
-
import { isSpace,
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
import { isSpace, scanStringEnd } from "../../util";
|
|
15
|
+
import { deserializeFloat } from "../index/float";
|
|
16
|
+
import { deserializeString } from "../index/string";
|
|
17
|
+
|
|
18
|
+
// "true" as a u64 of UTF-16 code units (LE).
|
|
19
|
+
// @ts-ignore: inline
|
|
20
|
+
@inline const TRUE_WORD: u64 = 28429475166421108;
|
|
21
|
+
// "alse" — the tail of "false", read at +2 so the leading 'f' is skipped.
|
|
22
|
+
// @ts-ignore: inline
|
|
23
|
+
@inline const ALSE_WORD: u64 = 28429466576093281;
|
|
24
|
+
// "null" as a u64 of UTF-16 code units (LE).
|
|
25
|
+
// @ts-ignore: inline
|
|
26
|
+
@inline const NULL_WORD: u64 = 30399761348886638;
|
|
27
|
+
|
|
28
|
+
// End offset (just past the value) of the most recent parseValue() call. The
|
|
29
|
+
// recursive-descent parser reports each value's end through this single cursor
|
|
30
|
+
// so containers can resume after a child without a separate bounds scan. It is
|
|
31
|
+
// only read immediately after parseValue() returns, before any other
|
|
32
|
+
// parseValue() runs, so recursion never clobbers a still-needed value.
|
|
33
|
+
let parseValueEnd: usize = 0;
|
|
22
34
|
|
|
23
35
|
export function deserializeObject(
|
|
24
36
|
srcStart: usize,
|
|
@@ -27,15 +39,9 @@ export function deserializeObject(
|
|
|
27
39
|
): JSON.Obj {
|
|
28
40
|
const out = changetype<JSON.Obj>(dst || changetype<usize>(new JSON.Obj()));
|
|
29
41
|
|
|
30
|
-
|
|
31
|
-
let keyEnd: usize = 0;
|
|
32
|
-
let isKey = false;
|
|
33
|
-
let depth = 0;
|
|
34
|
-
let lastIndex: usize = 0;
|
|
42
|
+
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
35
43
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
if (srcStart - srcEnd == 0)
|
|
44
|
+
if (srcEnd == srcStart)
|
|
39
45
|
throw new Error("Input string had zero length or was all whitespace");
|
|
40
46
|
if (load<u16>(srcStart) != BRACE_LEFT)
|
|
41
47
|
throw new Error(
|
|
@@ -48,166 +54,139 @@ export function deserializeObject(
|
|
|
48
54
|
(srcEnd - srcStart).toString(),
|
|
49
55
|
);
|
|
50
56
|
|
|
51
|
-
srcStart
|
|
57
|
+
parseObjectBody(out, srcStart + 2, srcEnd);
|
|
58
|
+
return out;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Parses a single JSON value whose first character is at `srcStart` (`srcEnd`
|
|
63
|
+
* is an upper bound). Returns the value and sets {@link parseValueEnd} to the
|
|
64
|
+
* offset just after it. Nested objects and arrays recurse here, so every byte
|
|
65
|
+
* is scanned exactly once.
|
|
66
|
+
*/
|
|
67
|
+
/** Offset just past the value returned by the most recent {@link parseValue}. */
|
|
68
|
+
export function lastValueEnd(): usize {
|
|
69
|
+
return parseValueEnd;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function parseValue(srcStart: usize, srcEnd: usize): JSON.Value {
|
|
73
|
+
const code = load<u16>(srcStart);
|
|
74
|
+
if (code == QUOTE) {
|
|
75
|
+
const end = scanStringEnd(srcStart, srcEnd);
|
|
76
|
+
if (end >= srcEnd) throw new Error("Unterminated string in JSON");
|
|
77
|
+
parseValueEnd = end + 2;
|
|
78
|
+
return JSON.Value.from(deserializeString(srcStart, end + 2));
|
|
79
|
+
} else if (code == BRACE_LEFT) {
|
|
80
|
+
const obj = new JSON.Obj();
|
|
81
|
+
parseValueEnd = parseObjectBody(obj, srcStart + 2, srcEnd);
|
|
82
|
+
return JSON.Value.from(obj);
|
|
83
|
+
} else if (code == BRACKET_LEFT) {
|
|
84
|
+
const arr = instantiate<JSON.Value[]>();
|
|
85
|
+
parseValueEnd = parseArrayBody(arr, srcStart + 2, srcEnd);
|
|
86
|
+
return JSON.Value.from(arr);
|
|
87
|
+
} else if (code - 48 <= 9 || code == 45) {
|
|
88
|
+
let p = srcStart + 2;
|
|
89
|
+
while (p < srcEnd) {
|
|
90
|
+
const c = load<u16>(p);
|
|
91
|
+
if (c == COMMA || c == BRACKET_RIGHT || c == BRACE_RIGHT || isSpace(c))
|
|
92
|
+
break;
|
|
93
|
+
p += 2;
|
|
94
|
+
}
|
|
95
|
+
parseValueEnd = p;
|
|
96
|
+
return JSON.Value.from(deserializeFloat<f64>(srcStart, p));
|
|
97
|
+
} else if (code == CHAR_T) {
|
|
98
|
+
if (load<u64>(srcStart) != TRUE_WORD)
|
|
99
|
+
throw new Error("Expected 'true' in JSON");
|
|
100
|
+
parseValueEnd = srcStart + 8;
|
|
101
|
+
return JSON.Value.from(true);
|
|
102
|
+
} else if (code == CHAR_F) {
|
|
103
|
+
if (load<u64>(srcStart, 2) != ALSE_WORD)
|
|
104
|
+
throw new Error("Expected 'false' in JSON");
|
|
105
|
+
parseValueEnd = srcStart + 10;
|
|
106
|
+
return JSON.Value.from(false);
|
|
107
|
+
} else if (code == CHAR_N) {
|
|
108
|
+
if (load<u64>(srcStart) != NULL_WORD)
|
|
109
|
+
throw new Error("Expected 'null' in JSON");
|
|
110
|
+
parseValueEnd = srcStart + 8;
|
|
111
|
+
return JSON.Value.from<usize>(0);
|
|
112
|
+
}
|
|
113
|
+
throw new Error(
|
|
114
|
+
"Unexpected character in JSON '" + String.fromCharCode(code) + "'",
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Parses array elements starting at `srcStart` (just past the opening `[`)
|
|
120
|
+
* until the matching `]`, returning the offset just after that `]`. Nested
|
|
121
|
+
* values are parsed in the same pass.
|
|
122
|
+
*/
|
|
123
|
+
export function parseArrayBody(
|
|
124
|
+
out: JSON.Value[],
|
|
125
|
+
srcStart: usize,
|
|
126
|
+
srcEnd: usize,
|
|
127
|
+
): usize {
|
|
52
128
|
while (srcStart < srcEnd) {
|
|
53
|
-
|
|
54
|
-
if (
|
|
55
|
-
if (code == QUOTE && isUnescapedQuote(srcStart)) {
|
|
56
|
-
if (isKey) {
|
|
57
|
-
keyStart = lastIndex;
|
|
58
|
-
keyEnd = srcStart;
|
|
59
|
-
// console.log("Key: " + ptrToStr(lastIndex, srcStart));
|
|
60
|
-
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart + 2)));
|
|
61
|
-
while (isSpace((code = load<u16>((srcStart += 2))))) {}
|
|
62
|
-
if (code !== COLON)
|
|
63
|
-
throw new Error(
|
|
64
|
-
"Expected ':' after key at position " +
|
|
65
|
-
(srcEnd - srcStart).toString(),
|
|
66
|
-
);
|
|
67
|
-
isKey = false;
|
|
68
|
-
} else {
|
|
69
|
-
// console.log("Got key start");
|
|
70
|
-
isKey = true; // i don't like this
|
|
71
|
-
lastIndex = srcStart + 2;
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
// isKey = !isKey;
|
|
129
|
+
const code = load<u16>(srcStart);
|
|
130
|
+
if (isSpace(code) || code == COMMA) {
|
|
75
131
|
srcStart += 2;
|
|
76
|
-
|
|
77
|
-
if (code == QUOTE) {
|
|
78
|
-
lastIndex = srcStart;
|
|
79
|
-
srcStart = scanStringEnd(srcStart, srcEnd);
|
|
80
|
-
if (srcStart >= srcEnd)
|
|
81
|
-
throw new Error("Unterminated string in JSON object");
|
|
82
|
-
out.set(
|
|
83
|
-
ptrToStr(keyStart, keyEnd),
|
|
84
|
-
deserializeString_NAIVE(lastIndex, srcStart + 2),
|
|
85
|
-
);
|
|
86
|
-
srcStart += 2;
|
|
87
|
-
keyStart = 0;
|
|
88
|
-
continue;
|
|
89
|
-
} else if (code - 48 <= 9 || code == 45) {
|
|
90
|
-
lastIndex = srcStart;
|
|
91
|
-
srcStart += 2;
|
|
92
|
-
while (srcStart < srcEnd) {
|
|
93
|
-
const code = load<u16>(srcStart);
|
|
94
|
-
if (code == COMMA || code == BRACE_RIGHT || isSpace(code)) {
|
|
95
|
-
// console.log("Value (number): " + ptrToStr(lastIndex, srcStart));
|
|
96
|
-
out.set(
|
|
97
|
-
ptrToStr(keyStart, keyEnd),
|
|
98
|
-
deserializeFloat_NAIVE<f64>(lastIndex, srcStart),
|
|
99
|
-
);
|
|
100
|
-
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
101
|
-
// /* empty */
|
|
102
|
-
// }
|
|
103
|
-
srcStart += 2;
|
|
104
|
-
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
105
|
-
keyStart = 0;
|
|
106
|
-
break;
|
|
107
|
-
}
|
|
108
|
-
srcStart += 2;
|
|
109
|
-
}
|
|
110
|
-
} else if (code == BRACE_LEFT) {
|
|
111
|
-
lastIndex = srcStart;
|
|
112
|
-
depth++;
|
|
113
|
-
srcStart += 2;
|
|
114
|
-
while (srcStart < srcEnd) {
|
|
115
|
-
const code = load<u16>(srcStart);
|
|
116
|
-
if (code == QUOTE) {
|
|
117
|
-
srcStart = scanStringEnd(srcStart, srcEnd);
|
|
118
|
-
if (srcStart >= srcEnd)
|
|
119
|
-
throw new Error("Unterminated string in JSON object");
|
|
120
|
-
} else if (code == BRACE_RIGHT) {
|
|
121
|
-
if (--depth == 0) {
|
|
122
|
-
// console.log("Value (object): " + ptrToStr(lastIndex, srcStart + 2));
|
|
123
|
-
out.set(
|
|
124
|
-
ptrToStr(keyStart, keyEnd),
|
|
125
|
-
deserializeObject(lastIndex, (srcStart += 2), 0),
|
|
126
|
-
);
|
|
127
|
-
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
128
|
-
keyStart = 0;
|
|
129
|
-
// while (isSpace(load<u16>(srcStart))) {
|
|
130
|
-
// /* empty */
|
|
131
|
-
// }
|
|
132
|
-
break;
|
|
133
|
-
}
|
|
134
|
-
} else if (code == BRACE_LEFT) depth++;
|
|
135
|
-
srcStart += 2;
|
|
136
|
-
}
|
|
137
|
-
} else if (code == BRACKET_LEFT) {
|
|
138
|
-
lastIndex = srcStart;
|
|
139
|
-
depth++;
|
|
140
|
-
srcStart += 2;
|
|
141
|
-
while (srcStart < srcEnd) {
|
|
142
|
-
const code = load<u16>(srcStart);
|
|
143
|
-
if (code == QUOTE) {
|
|
144
|
-
srcStart = scanStringEnd(srcStart, srcEnd);
|
|
145
|
-
if (srcStart >= srcEnd)
|
|
146
|
-
throw new Error("Unterminated string in JSON object");
|
|
147
|
-
} else if (code == BRACKET_RIGHT) {
|
|
148
|
-
if (--depth == 0) {
|
|
149
|
-
// console.log("Value (array): " + ptrToStr(lastIndex, srcStart + 2));
|
|
150
|
-
out.set(
|
|
151
|
-
ptrToStr(keyStart, keyEnd),
|
|
152
|
-
deserializeArray<JSON.Value[]>(lastIndex, (srcStart += 2), 0),
|
|
153
|
-
);
|
|
154
|
-
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
155
|
-
keyStart = 0;
|
|
156
|
-
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
157
|
-
// /* empty */
|
|
158
|
-
// }
|
|
159
|
-
break;
|
|
160
|
-
}
|
|
161
|
-
} else if (code == BRACKET_LEFT) depth++;
|
|
162
|
-
srcStart += 2;
|
|
163
|
-
}
|
|
164
|
-
} else if (code == CHAR_T) {
|
|
165
|
-
if (load<u64>(srcStart) == 28429475166421108) {
|
|
166
|
-
// console.log("Value (bool): " + ptrToStr(srcStart, srcStart + 8));
|
|
167
|
-
out.set(ptrToStr(keyStart, keyEnd), true);
|
|
168
|
-
srcStart += 8;
|
|
169
|
-
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
170
|
-
// /* empty */
|
|
171
|
-
// }
|
|
172
|
-
srcStart += 2;
|
|
173
|
-
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart)) + " " + (srcStart < srcEnd).toString());
|
|
174
|
-
keyStart = 0;
|
|
175
|
-
}
|
|
176
|
-
} else if (code == CHAR_F) {
|
|
177
|
-
if (load<u64>(srcStart, 2) == 28429466576093281) {
|
|
178
|
-
// console.log("Value (bool): " + ptrToStr(srcStart, srcStart + 10));
|
|
179
|
-
out.set(ptrToStr(keyStart, keyEnd), false);
|
|
180
|
-
srcStart += 10;
|
|
181
|
-
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
182
|
-
// /* empty */
|
|
183
|
-
// }
|
|
184
|
-
srcStart += 2;
|
|
185
|
-
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
186
|
-
keyStart = 0;
|
|
187
|
-
}
|
|
188
|
-
} else if (code == CHAR_N) {
|
|
189
|
-
if (load<u64>(srcStart) == 30399761348886638) {
|
|
190
|
-
// console.log("Value (null): " + ptrToStr(srcStart, srcStart + 8));
|
|
191
|
-
out.set(ptrToStr(keyStart, keyEnd), JSON.Value.from<usize>(0));
|
|
192
|
-
srcStart += 8;
|
|
193
|
-
// while (isSpace(load<u16>((srcStart += 2)))) {
|
|
194
|
-
/* empty */
|
|
195
|
-
// }
|
|
196
|
-
srcStart += 2;
|
|
197
|
-
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
|
|
198
|
-
keyStart = 0;
|
|
199
|
-
}
|
|
200
|
-
} else if (isSpace(code)) {
|
|
201
|
-
srcStart += 2;
|
|
202
|
-
} else {
|
|
203
|
-
throw new Error(
|
|
204
|
-
"Unexpected character in JSON object '" +
|
|
205
|
-
String.fromCharCode(code) +
|
|
206
|
-
"' at position " +
|
|
207
|
-
(srcEnd - srcStart).toString(),
|
|
208
|
-
);
|
|
209
|
-
}
|
|
132
|
+
continue;
|
|
210
133
|
}
|
|
134
|
+
if (code == BRACKET_RIGHT) return srcStart + 2;
|
|
135
|
+
out.push(parseValue(srcStart, srcEnd));
|
|
136
|
+
srcStart = parseValueEnd;
|
|
211
137
|
}
|
|
212
|
-
return
|
|
138
|
+
return srcEnd;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Parses object members starting at `srcStart` (just past the opening `{`)
|
|
143
|
+
* until the matching `}`, returning the offset just after that `}`. `srcEnd`
|
|
144
|
+
* is an upper bound (end of the enclosing buffer), not the object's exact end.
|
|
145
|
+
*/
|
|
146
|
+
export function parseObjectBody(
|
|
147
|
+
out: JSON.Obj,
|
|
148
|
+
srcStart: usize,
|
|
149
|
+
srcEnd: usize,
|
|
150
|
+
): usize {
|
|
151
|
+
while (srcStart < srcEnd) {
|
|
152
|
+
let code = load<u16>(srcStart);
|
|
153
|
+
|
|
154
|
+
// Skip insignificant whitespace and member separators before each key.
|
|
155
|
+
if (isSpace(code) || code == COMMA) {
|
|
156
|
+
srcStart += 2;
|
|
157
|
+
continue;
|
|
158
|
+
}
|
|
159
|
+
if (code == BRACE_RIGHT) return srcStart + 2;
|
|
160
|
+
|
|
161
|
+
// --- key ---
|
|
162
|
+
if (code != QUOTE)
|
|
163
|
+
throw new Error(
|
|
164
|
+
"Unexpected character in JSON object '" +
|
|
165
|
+
String.fromCharCode(code) +
|
|
166
|
+
"' at position " +
|
|
167
|
+
(srcEnd - srcStart).toString(),
|
|
168
|
+
);
|
|
169
|
+
const keyStart = srcStart + 2;
|
|
170
|
+
srcStart = scanStringEnd(srcStart, srcEnd);
|
|
171
|
+
if (srcStart >= srcEnd)
|
|
172
|
+
throw new Error("Unterminated string in JSON object");
|
|
173
|
+
const keyEnd = srcStart;
|
|
174
|
+
srcStart += 2;
|
|
175
|
+
|
|
176
|
+
// --- colon ---
|
|
177
|
+
while (srcStart < srcEnd && isSpace((code = load<u16>(srcStart))))
|
|
178
|
+
srcStart += 2;
|
|
179
|
+
if (srcStart >= srcEnd || code != COLON)
|
|
180
|
+
throw new Error(
|
|
181
|
+
"Expected ':' after key at position " + (srcEnd - srcStart).toString(),
|
|
182
|
+
);
|
|
183
|
+
srcStart += 2;
|
|
184
|
+
|
|
185
|
+
// --- value ---
|
|
186
|
+
while (srcStart < srcEnd && isSpace((code = load<u16>(srcStart))))
|
|
187
|
+
srcStart += 2;
|
|
188
|
+
out.appendRaw(keyStart, keyEnd, parseValue(srcStart, srcEnd));
|
|
189
|
+
srcStart = parseValueEnd;
|
|
190
|
+
}
|
|
191
|
+
return srcEnd;
|
|
213
192
|
}
|
|
@@ -11,8 +11,7 @@ import {
|
|
|
11
11
|
} from "../../custom/chars";
|
|
12
12
|
import { isSpace, atoi, scanStringEnd } from "../../util";
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
@inline function scanSetElementEnd(srcStart: usize, srcEnd: usize): usize {
|
|
14
|
+
function scanSetElementEnd(srcStart: usize, srcEnd: usize): usize {
|
|
16
15
|
const first = load<u16>(srcStart);
|
|
17
16
|
|
|
18
17
|
if (first == QUOTE) {
|
|
@@ -17,8 +17,7 @@ import { deserializeStaticArrayInteger } from "./staticarray/integer";
|
|
|
17
17
|
import { deserializeStaticArrayString } from "./staticarray/string";
|
|
18
18
|
import { scanValueEnd } from "../../util/scanValueEnd";
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
@inline function materializeStaticArray<T extends StaticArray<any>>(
|
|
20
|
+
function materializeStaticArray<T extends StaticArray<any>>(
|
|
22
21
|
src: valueof<T>[],
|
|
23
22
|
dst: usize,
|
|
24
23
|
): T {
|