json-as 1.3.9 → 1.5.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 +60 -19
- package/README.md +120 -21
- package/assembly/custom/chars.ts +39 -78
- package/assembly/deserialize/index/arbitrary.ts +28 -10
- package/assembly/deserialize/index/float.ts +2 -4
- package/assembly/deserialize/index/integer.ts +2 -4
- package/assembly/deserialize/index/object.ts +6 -1
- package/assembly/deserialize/index/string.ts +2 -7
- package/assembly/deserialize/index/unsigned.ts +2 -4
- 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 +2 -7
- package/assembly/deserialize/naive/array/map.ts +10 -14
- package/assembly/deserialize/naive/array/object.ts +10 -14
- package/assembly/deserialize/naive/array/struct.ts +19 -1
- package/assembly/deserialize/naive/bool.ts +1 -5
- package/assembly/deserialize/naive/date.ts +1 -2
- package/assembly/deserialize/naive/float.ts +4 -11
- package/assembly/deserialize/naive/integer.ts +2 -4
- package/assembly/deserialize/naive/map.ts +42 -205
- package/assembly/deserialize/naive/object.ts +291 -174
- package/assembly/deserialize/naive/raw.ts +1 -5
- package/assembly/deserialize/naive/set.ts +3 -6
- package/assembly/deserialize/naive/staticarray.ts +2 -4
- package/assembly/deserialize/naive/string.ts +68 -24
- package/assembly/deserialize/naive/typedarray.ts +1 -2
- package/assembly/deserialize/naive/unsigned.ts +2 -4
- package/assembly/deserialize/simd/array/integer.ts +5 -13
- package/assembly/deserialize/simd/float.ts +5 -12
- package/assembly/deserialize/simd/integer.ts +6 -15
- package/assembly/deserialize/simd/string.ts +21 -43
- package/assembly/deserialize/swar/array/arbitrary.ts +1 -2
- package/assembly/deserialize/swar/array/array.ts +2 -4
- package/assembly/deserialize/swar/array/bool.ts +2 -4
- package/assembly/deserialize/swar/array/box.ts +1 -2
- package/assembly/deserialize/swar/array/float.ts +8 -21
- package/assembly/deserialize/swar/array/generic.ts +2 -4
- package/assembly/deserialize/swar/array/integer.ts +13 -27
- package/assembly/deserialize/swar/array/map.ts +1 -2
- package/assembly/deserialize/swar/array/object.ts +2 -4
- package/assembly/deserialize/swar/array/raw.ts +1 -2
- package/assembly/deserialize/swar/array/shared.ts +9 -21
- package/assembly/deserialize/swar/array/string.ts +4 -10
- package/assembly/deserialize/swar/array/struct.ts +3 -9
- package/assembly/deserialize/swar/array.ts +1 -3
- package/assembly/deserialize/swar/float.ts +7 -17
- package/assembly/deserialize/swar/integer.ts +6 -15
- package/assembly/deserialize/swar/string.ts +40 -54
- package/assembly/deserialize/swar/typedarray.ts +4 -4
- package/assembly/index.d.ts +259 -21
- package/assembly/index.ts +1704 -266
- package/assembly/serialize/index/arbitrary.ts +70 -4
- package/assembly/serialize/index/jsonarray.ts +51 -0
- package/assembly/serialize/index/object.ts +39 -14
- package/assembly/serialize/index/string.ts +1 -2
- package/assembly/serialize/index/typedarray.ts +1 -2
- package/assembly/serialize/index.ts +1 -0
- package/assembly/serialize/naive/array.ts +23 -34
- package/assembly/serialize/naive/bool.ts +0 -1
- package/assembly/serialize/naive/float.ts +16 -25
- package/assembly/serialize/naive/integer.ts +1 -5
- package/assembly/serialize/naive/raw.ts +1 -2
- package/assembly/serialize/naive/set.ts +0 -4
- package/assembly/serialize/naive/staticarray.ts +0 -5
- package/assembly/serialize/naive/string.ts +11 -7
- package/assembly/serialize/naive/typedarray.ts +0 -6
- package/assembly/serialize/simd/string.ts +1 -3
- package/assembly/serialize/swar/string.ts +2 -4
- package/assembly/util/atoi-fast.ts +4 -14
- package/assembly/util/atoi.ts +1 -2
- package/assembly/util/bytes.ts +1 -2
- package/assembly/util/idofd.ts +1 -2
- package/assembly/util/isSpace.ts +1 -2
- package/assembly/util/itoa-fast.ts +9 -15
- package/assembly/util/nextPowerOf2.ts +1 -2
- package/assembly/util/parsefloat-fast.ts +4 -7
- package/assembly/util/ptrToStr.ts +1 -2
- package/assembly/util/scanValueEnd.ts +1 -2
- package/assembly/util/scanValueEndSimd.ts +198 -0
- package/assembly/util/scanValueEndSwar.ts +184 -0
- package/assembly/util/scientific.ts +8 -14
- package/assembly/util/simd-int.ts +4 -8
- package/assembly/util/snp.ts +2 -7
- package/assembly/util/stringScan.ts +2 -4
- package/assembly/util/swar-int.ts +8 -16
- package/assembly/util/swar.ts +2 -4
- package/lib/as-bs.ts +57 -42
- package/package.json +27 -10
- 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 +617 -326
- 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 +4 -2
- package/transform/lib/types.js +5 -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/assembly/util/dragonbox-cache.ts +0 -445
- package/assembly/util/dragonbox.ts +0 -660
- 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,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,29 +11,54 @@ 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
|
+
const TRUE_WORD: u64 = 28429475166421108;
|
|
20
|
+
// "alse" - the tail of "false", read at +2 so the leading 'f' is skipped.
|
|
21
|
+
const ALSE_WORD: u64 = 28429466576093281;
|
|
22
|
+
// "null" as a u64 of UTF-16 code units (LE).
|
|
23
|
+
const NULL_WORD: u64 = 30399761348886638;
|
|
24
|
+
|
|
25
|
+
// End offset (just past the value) of the most recent parseValue() call. The
|
|
26
|
+
// recursive-descent parser reports each value's end through this single cursor
|
|
27
|
+
// so containers can resume after a child without a separate bounds scan. It is
|
|
28
|
+
// only read immediately after parseValue() returns, before any other
|
|
29
|
+
// parseValue() runs, so recursion never clobbers a still-needed value.
|
|
30
|
+
let parseValueEnd: usize = 0;
|
|
31
|
+
|
|
32
|
+
// Source string for the parse currently in flight. When non-empty, nested
|
|
33
|
+
// objects/arrays are deferred: instead of being recursively materialized, each
|
|
34
|
+
// is stored as a lazy JSON.Value holding its raw slice + this anchor (see
|
|
35
|
+
// JSON.Value.fromSlice / JSON.Types.Lazy). Set at the top of JSON.parse (and in
|
|
36
|
+
// JSON.Value.materialize), both of which save/restore it for re-entrancy, so any
|
|
37
|
+
// lazy value built during a parse points into that parse's own source buffer.
|
|
38
|
+
let parseSrc: string = "";
|
|
39
|
+
export function setParseSrc(s: string): void {
|
|
40
|
+
parseSrc = s;
|
|
41
|
+
}
|
|
42
|
+
export function getParseSrc(): string {
|
|
43
|
+
return parseSrc;
|
|
44
|
+
}
|
|
22
45
|
|
|
23
46
|
export function deserializeObject(
|
|
24
47
|
srcStart: usize,
|
|
25
48
|
srcEnd: usize,
|
|
26
49
|
dst: usize,
|
|
27
50
|
): JSON.Obj {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
51
|
+
const reuse = dst != 0;
|
|
52
|
+
const out = changetype<JSON.Obj>(
|
|
53
|
+
reuse ? dst : changetype<usize>(new JSON.Obj()),
|
|
54
|
+
);
|
|
55
|
+
// Reuse path (`JSON.parse<JSON.Obj>(data, out)`): empty the handle first,
|
|
56
|
+
// keeping its buffer capacity, so we overwrite rather than append stale data.
|
|
57
|
+
if (reuse) out.clear();
|
|
35
58
|
|
|
36
|
-
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
59
|
+
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
37
60
|
|
|
38
|
-
if (
|
|
61
|
+
if (srcEnd == srcStart)
|
|
39
62
|
throw new Error("Input string had zero length or was all whitespace");
|
|
40
63
|
if (load<u16>(srcStart) != BRACE_LEFT)
|
|
41
64
|
throw new Error(
|
|
@@ -48,166 +71,260 @@ export function deserializeObject(
|
|
|
48
71
|
(srcEnd - srcStart).toString(),
|
|
49
72
|
);
|
|
50
73
|
|
|
51
|
-
srcStart
|
|
74
|
+
parseObjectBody(out, srcStart + 2, srcEnd);
|
|
75
|
+
return out;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function deserializeJsonArray(
|
|
79
|
+
srcStart: usize,
|
|
80
|
+
srcEnd: usize,
|
|
81
|
+
dst: usize,
|
|
82
|
+
): JSON.Arr {
|
|
83
|
+
const reuse = dst != 0;
|
|
84
|
+
const out = changetype<JSON.Arr>(
|
|
85
|
+
reuse ? dst : changetype<usize>(new JSON.Arr()),
|
|
86
|
+
);
|
|
87
|
+
if (reuse) out.clear();
|
|
88
|
+
|
|
89
|
+
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
90
|
+
|
|
91
|
+
if (srcEnd == srcStart)
|
|
92
|
+
throw new Error("Input string had zero length or was all whitespace");
|
|
93
|
+
if (load<u16>(srcStart) != BRACKET_LEFT)
|
|
94
|
+
throw new Error("Expected '[' at start of array");
|
|
95
|
+
if (load<u16>(srcEnd - 2) != BRACKET_RIGHT)
|
|
96
|
+
throw new Error("Expected ']' at end of array");
|
|
97
|
+
|
|
98
|
+
parseArrayBodySlots(out, srcStart + 2, srcEnd);
|
|
99
|
+
return out;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Parses array elements starting at `srcStart` (just past the opening `[`) into
|
|
104
|
+
* a JSON.Arr's NaN-boxed value slots, returning the offset just after the
|
|
105
|
+
* matching `]`. Mirrors {@link parseObjectBody} without keys.
|
|
106
|
+
*/
|
|
107
|
+
export function parseArrayBodySlots(
|
|
108
|
+
out: JSON.Arr,
|
|
109
|
+
srcStart: usize,
|
|
110
|
+
srcEnd: usize,
|
|
111
|
+
): usize {
|
|
112
|
+
out._src = parseSrc;
|
|
52
113
|
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;
|
|
114
|
+
const code = load<u16>(srcStart);
|
|
115
|
+
if (isSpace(code) || code == COMMA) {
|
|
75
116
|
srcStart += 2;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (code == BRACKET_RIGHT) return srcStart + 2;
|
|
120
|
+
if (parseSrc.length != 0) {
|
|
121
|
+
out.pushRawSlot(parseSlotBits(srcStart, srcEnd));
|
|
76
122
|
} else {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
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
|
-
}
|
|
123
|
+
out.pushRawSlot(
|
|
124
|
+
JSON.Value.bitsFrom<JSON.Value>(parseValue(srcStart, srcEnd)),
|
|
125
|
+
);
|
|
210
126
|
}
|
|
127
|
+
srcStart = parseValueEnd;
|
|
211
128
|
}
|
|
212
|
-
return
|
|
129
|
+
return srcEnd;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Parses a single object-member value into a NaN-boxed value slot, setting
|
|
134
|
+
* {@link parseValueEnd} to the offset just past it. Strings and composites are
|
|
135
|
+
* deferred (a `valBox(Lazy, startPtr)` slice the object parses on first access);
|
|
136
|
+
* numbers, booleans and null are parsed eagerly inline. Requires {@link parseSrc}
|
|
137
|
+
* to be set (the caller guards), since a lazy slot points into it.
|
|
138
|
+
*/
|
|
139
|
+
function parseSlotBits(srcStart: usize, srcEnd: usize): u64 {
|
|
140
|
+
const code = load<u16>(srcStart);
|
|
141
|
+
if (code == QUOTE || code == BRACE_LEFT || code == BRACKET_LEFT) {
|
|
142
|
+
const end = JSON.Util.scanValueEnd<JSON.Value>(srcStart, srcEnd);
|
|
143
|
+
parseValueEnd = end;
|
|
144
|
+
return JSON.Value.lazyBits(changetype<usize>(parseSrc), srcStart, end);
|
|
145
|
+
} else if (code - 48 <= 9 || code == 45) {
|
|
146
|
+
let p = srcStart + 2;
|
|
147
|
+
while (p < srcEnd) {
|
|
148
|
+
const c = load<u16>(p);
|
|
149
|
+
if (c == COMMA || c == BRACKET_RIGHT || c == BRACE_RIGHT || isSpace(c))
|
|
150
|
+
break;
|
|
151
|
+
p += 2;
|
|
152
|
+
}
|
|
153
|
+
parseValueEnd = p;
|
|
154
|
+
return JSON.Value.f64Bits(deserializeFloat<f64>(srcStart, p));
|
|
155
|
+
} else if (code == CHAR_T) {
|
|
156
|
+
if (load<u64>(srcStart) != TRUE_WORD)
|
|
157
|
+
throw new Error("Expected 'true' in JSON");
|
|
158
|
+
parseValueEnd = srcStart + 8;
|
|
159
|
+
return JSON.Value.boolBits(true);
|
|
160
|
+
} else if (code == CHAR_F) {
|
|
161
|
+
if (load<u64>(srcStart, 2) != ALSE_WORD)
|
|
162
|
+
throw new Error("Expected 'false' in JSON");
|
|
163
|
+
parseValueEnd = srcStart + 10;
|
|
164
|
+
return JSON.Value.boolBits(false);
|
|
165
|
+
} else if (code == CHAR_N) {
|
|
166
|
+
if (load<u64>(srcStart) != NULL_WORD)
|
|
167
|
+
throw new Error("Expected 'null' in JSON");
|
|
168
|
+
parseValueEnd = srcStart + 8;
|
|
169
|
+
return JSON.Value.nullBits();
|
|
170
|
+
}
|
|
171
|
+
throw new Error(
|
|
172
|
+
"Unexpected character in JSON '" + String.fromCharCode(code) + "'",
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Parses a single JSON value whose first character is at `srcStart` (`srcEnd`
|
|
178
|
+
* is an upper bound). Returns the value and sets {@link parseValueEnd} to the
|
|
179
|
+
* offset just after it. Nested objects and arrays recurse here, so every byte
|
|
180
|
+
* is scanned exactly once.
|
|
181
|
+
*/
|
|
182
|
+
/** Offset just past the value returned by the most recent {@link parseValue}. */
|
|
183
|
+
export function lastValueEnd(): usize {
|
|
184
|
+
return parseValueEnd;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
export function parseValue(srcStart: usize, srcEnd: usize): JSON.Value {
|
|
188
|
+
const code = load<u16>(srcStart);
|
|
189
|
+
if (code == QUOTE) {
|
|
190
|
+
const end = scanStringEnd(srcStart, srcEnd);
|
|
191
|
+
if (end >= srcEnd) throw new Error("Unterminated string in JSON");
|
|
192
|
+
parseValueEnd = end + 2;
|
|
193
|
+
return JSON.Value.from(deserializeString(srcStart, end + 2));
|
|
194
|
+
} else if (code == BRACE_LEFT) {
|
|
195
|
+
const obj = new JSON.Obj();
|
|
196
|
+
parseValueEnd = parseObjectBody(obj, srcStart + 2, srcEnd);
|
|
197
|
+
return JSON.Value.from(obj);
|
|
198
|
+
} else if (code == BRACKET_LEFT) {
|
|
199
|
+
const arr = new JSON.Arr();
|
|
200
|
+
parseValueEnd = parseArrayBodySlots(arr, srcStart + 2, srcEnd);
|
|
201
|
+
return JSON.Value.from(arr);
|
|
202
|
+
} else if (code - 48 <= 9 || code == 45) {
|
|
203
|
+
let p = srcStart + 2;
|
|
204
|
+
while (p < srcEnd) {
|
|
205
|
+
const c = load<u16>(p);
|
|
206
|
+
if (c == COMMA || c == BRACKET_RIGHT || c == BRACE_RIGHT || isSpace(c))
|
|
207
|
+
break;
|
|
208
|
+
p += 2;
|
|
209
|
+
}
|
|
210
|
+
parseValueEnd = p;
|
|
211
|
+
return JSON.Value.from(deserializeFloat<f64>(srcStart, p));
|
|
212
|
+
} else if (code == CHAR_T) {
|
|
213
|
+
if (load<u64>(srcStart) != TRUE_WORD)
|
|
214
|
+
throw new Error("Expected 'true' in JSON");
|
|
215
|
+
parseValueEnd = srcStart + 8;
|
|
216
|
+
return JSON.Value.from(true);
|
|
217
|
+
} else if (code == CHAR_F) {
|
|
218
|
+
if (load<u64>(srcStart, 2) != ALSE_WORD)
|
|
219
|
+
throw new Error("Expected 'false' in JSON");
|
|
220
|
+
parseValueEnd = srcStart + 10;
|
|
221
|
+
return JSON.Value.from(false);
|
|
222
|
+
} else if (code == CHAR_N) {
|
|
223
|
+
if (load<u64>(srcStart) != NULL_WORD)
|
|
224
|
+
throw new Error("Expected 'null' in JSON");
|
|
225
|
+
parseValueEnd = srcStart + 8;
|
|
226
|
+
return JSON.Value.from<usize>(0);
|
|
227
|
+
}
|
|
228
|
+
throw new Error(
|
|
229
|
+
"Unexpected character in JSON '" + String.fromCharCode(code) + "'",
|
|
230
|
+
);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Parses array elements starting at `srcStart` (just past the opening `[`)
|
|
235
|
+
* until the matching `]`, returning the offset just after that `]`. Nested
|
|
236
|
+
* values are parsed in the same pass.
|
|
237
|
+
*/
|
|
238
|
+
export function parseArrayBody(
|
|
239
|
+
out: JSON.Value[],
|
|
240
|
+
srcStart: usize,
|
|
241
|
+
srcEnd: usize,
|
|
242
|
+
): usize {
|
|
243
|
+
while (srcStart < srcEnd) {
|
|
244
|
+
const code = load<u16>(srcStart);
|
|
245
|
+
if (isSpace(code) || code == COMMA) {
|
|
246
|
+
srcStart += 2;
|
|
247
|
+
continue;
|
|
248
|
+
}
|
|
249
|
+
if (code == BRACKET_RIGHT) return srcStart + 2;
|
|
250
|
+
if (
|
|
251
|
+
(code == BRACE_LEFT || code == BRACKET_LEFT || code == QUOTE) &&
|
|
252
|
+
parseSrc.length != 0
|
|
253
|
+
) {
|
|
254
|
+
// Defer strings and composites (the allocating shapes): store the raw
|
|
255
|
+
// slice, parse on first access. Cheap primitives stay eager below.
|
|
256
|
+
const end = JSON.Util.scanValueEnd<JSON.Value>(srcStart, srcEnd);
|
|
257
|
+
out.push(JSON.Value.fromSlice(srcStart, end, parseSrc));
|
|
258
|
+
srcStart = end;
|
|
259
|
+
} else {
|
|
260
|
+
out.push(parseValue(srcStart, srcEnd));
|
|
261
|
+
srcStart = parseValueEnd;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return srcEnd;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Parses object members starting at `srcStart` (just past the opening `{`)
|
|
269
|
+
* until the matching `}`, returning the offset just after that `}`. `srcEnd`
|
|
270
|
+
* is an upper bound (end of the enclosing buffer), not the object's exact end.
|
|
271
|
+
*/
|
|
272
|
+
export function parseObjectBody(
|
|
273
|
+
out: JSON.Obj,
|
|
274
|
+
srcStart: usize,
|
|
275
|
+
srcEnd: usize,
|
|
276
|
+
): usize {
|
|
277
|
+
// Anchor the source for this object's deferred value slots (start pointers
|
|
278
|
+
// into it). Set here so every caller - deserializeObject, the JSON.Obj[]
|
|
279
|
+
// array path, parseValue, the map path - gets it. Empty off the parse path,
|
|
280
|
+
// where no lazy slots are produced.
|
|
281
|
+
out._src = parseSrc;
|
|
282
|
+
while (srcStart < srcEnd) {
|
|
283
|
+
let code = load<u16>(srcStart);
|
|
284
|
+
|
|
285
|
+
// Skip insignificant whitespace and member separators before each key.
|
|
286
|
+
if (isSpace(code) || code == COMMA) {
|
|
287
|
+
srcStart += 2;
|
|
288
|
+
continue;
|
|
289
|
+
}
|
|
290
|
+
if (code == BRACE_RIGHT) return srcStart + 2;
|
|
291
|
+
|
|
292
|
+
// --- 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
|
+
);
|
|
300
|
+
const keyStart = srcStart + 2;
|
|
301
|
+
srcStart = scanStringEnd(srcStart, srcEnd);
|
|
302
|
+
if (srcStart >= srcEnd)
|
|
303
|
+
throw new Error("Unterminated string in JSON object");
|
|
304
|
+
const keyEnd = srcStart;
|
|
305
|
+
srcStart += 2;
|
|
306
|
+
|
|
307
|
+
// --- colon ---
|
|
308
|
+
while (srcStart < srcEnd && isSpace((code = load<u16>(srcStart))))
|
|
309
|
+
srcStart += 2;
|
|
310
|
+
if (srcStart >= srcEnd || code != COLON)
|
|
311
|
+
throw new Error(
|
|
312
|
+
"Expected ':' after key at position " + (srcEnd - srcStart).toString(),
|
|
313
|
+
);
|
|
314
|
+
srcStart += 2;
|
|
315
|
+
|
|
316
|
+
// --- value ---
|
|
317
|
+
while (srcStart < srcEnd && isSpace((code = load<u16>(srcStart))))
|
|
318
|
+
srcStart += 2;
|
|
319
|
+
if (parseSrc.length != 0) {
|
|
320
|
+
// Parsing: store a NaN-boxed slot directly (strings/composites deferred,
|
|
321
|
+
// scalars eager) - no per-value JSON.Value object.
|
|
322
|
+
out.appendRawSlot(keyStart, keyEnd, parseSlotBits(srcStart, srcEnd));
|
|
323
|
+
} else {
|
|
324
|
+
// Off the parse path (no source anchor): box eagerly.
|
|
325
|
+
out.appendRaw(keyStart, keyEnd, parseValue(srcStart, srcEnd));
|
|
326
|
+
}
|
|
327
|
+
srcStart = parseValueEnd;
|
|
328
|
+
}
|
|
329
|
+
return srcEnd;
|
|
213
330
|
}
|
|
@@ -1,10 +1,6 @@
|
|
|
1
1
|
import { JSON } from "../..";
|
|
2
2
|
import { ptrToStr } from "../../util/ptrToStr";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
@inline export function deserializeRaw(
|
|
6
|
-
srcStart: usize,
|
|
7
|
-
srcEnd: usize,
|
|
8
|
-
): JSON.Raw {
|
|
4
|
+
export function deserializeRaw(srcStart: usize, srcEnd: usize): JSON.Raw {
|
|
9
5
|
return JSON.Raw.from(ptrToStr(srcStart, srcEnd));
|
|
10
6
|
}
|
|
@@ -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) {
|
|
@@ -157,8 +156,7 @@ export function deserializeSet<T extends Set<any>>(
|
|
|
157
156
|
return out;
|
|
158
157
|
}
|
|
159
158
|
|
|
160
|
-
|
|
161
|
-
@inline function deserializeSetBody<T extends Set<any>>(
|
|
159
|
+
function deserializeSetBody<T extends Set<any>>(
|
|
162
160
|
srcStart: usize,
|
|
163
161
|
srcEnd: usize,
|
|
164
162
|
out: T,
|
|
@@ -167,8 +165,7 @@ export function deserializeSet<T extends Set<any>>(
|
|
|
167
165
|
return deserializeSetDirect<T>(srcStart, srcEnd, changetype<nonnull<T>>(out));
|
|
168
166
|
}
|
|
169
167
|
|
|
170
|
-
|
|
171
|
-
@inline export function deserializeSetField<T extends Set<any>>(
|
|
168
|
+
export function deserializeSetField<T extends Set<any>>(
|
|
172
169
|
srcStart: usize,
|
|
173
170
|
srcEnd: usize,
|
|
174
171
|
dstObj: usize,
|
|
@@ -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 {
|
|
@@ -113,8 +112,7 @@ export function deserializeStaticArray<T extends StaticArray<any>>(
|
|
|
113
112
|
throw new Error("Could not parse static array of type " + nameof<T>() + "!");
|
|
114
113
|
}
|
|
115
114
|
|
|
116
|
-
|
|
117
|
-
@inline export function deserializeStaticArrayField<T extends StaticArray<any>>(
|
|
115
|
+
export function deserializeStaticArrayField<T extends StaticArray<any>>(
|
|
118
116
|
srcStart: usize,
|
|
119
117
|
srcEnd: usize,
|
|
120
118
|
dstObj: usize,
|