json-as 1.3.9 → 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.
Files changed (84) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/README.md +49 -1
  3. package/assembly/deserialize/index/arbitrary.ts +2 -2
  4. package/assembly/deserialize/naive/array/arbitrary.ts +3 -136
  5. package/assembly/deserialize/naive/array/array.ts +30 -1
  6. package/assembly/deserialize/naive/array/integer.ts +1 -6
  7. package/assembly/deserialize/naive/array/map.ts +10 -14
  8. package/assembly/deserialize/naive/array/object.ts +10 -14
  9. package/assembly/deserialize/naive/float.ts +2 -4
  10. package/assembly/deserialize/naive/integer.ts +1 -2
  11. package/assembly/deserialize/naive/map.ts +40 -202
  12. package/assembly/deserialize/naive/object.ts +153 -174
  13. package/assembly/deserialize/naive/set.ts +1 -2
  14. package/assembly/deserialize/naive/staticarray.ts +1 -2
  15. package/assembly/deserialize/naive/string.ts +65 -18
  16. package/assembly/deserialize/naive/typedarray.ts +1 -2
  17. package/assembly/deserialize/naive/unsigned.ts +1 -2
  18. package/assembly/deserialize/simd/array/integer.ts +3 -6
  19. package/assembly/deserialize/simd/float.ts +2 -7
  20. package/assembly/deserialize/simd/integer.ts +4 -8
  21. package/assembly/deserialize/simd/string.ts +16 -21
  22. package/assembly/deserialize/swar/array/array.ts +1 -2
  23. package/assembly/deserialize/swar/array/bool.ts +1 -2
  24. package/assembly/deserialize/swar/array/float.ts +2 -3
  25. package/assembly/deserialize/swar/array/generic.ts +1 -2
  26. package/assembly/deserialize/swar/array/integer.ts +6 -11
  27. package/assembly/deserialize/swar/array/object.ts +1 -2
  28. package/assembly/deserialize/swar/array/shared.ts +3 -8
  29. package/assembly/deserialize/swar/array/string.ts +1 -2
  30. package/assembly/deserialize/swar/array/struct.ts +1 -1
  31. package/assembly/deserialize/swar/float.ts +3 -8
  32. package/assembly/deserialize/swar/integer.ts +4 -8
  33. package/assembly/deserialize/swar/string.ts +29 -41
  34. package/assembly/index.d.ts +248 -15
  35. package/assembly/index.ts +468 -146
  36. package/assembly/serialize/index/object.ts +18 -15
  37. package/assembly/serialize/naive/string.ts +9 -2
  38. package/assembly/serialize/swar/string.ts +1 -2
  39. package/assembly/util/atoi.ts +1 -2
  40. package/assembly/util/dragonbox.ts +0 -8
  41. package/assembly/util/itoa-fast.ts +3 -6
  42. package/assembly/util/parsefloat-fast.ts +1 -2
  43. package/assembly/util/scanValueEnd.ts +1 -2
  44. package/assembly/util/scanValueEndSimd.ts +160 -0
  45. package/assembly/util/scanValueEndSwar.ts +142 -0
  46. package/assembly/util/scientific.ts +3 -6
  47. package/assembly/util/simd-int.ts +4 -8
  48. package/assembly/util/snp.ts +1 -5
  49. package/assembly/util/stringScan.ts +2 -4
  50. package/assembly/util/swar-int.ts +3 -6
  51. package/lib/as-bs.ts +37 -0
  52. package/package.json +14 -4
  53. package/transform/lib/builder.d.ts +0 -1
  54. package/transform/lib/builder.js +0 -1
  55. package/transform/lib/index.d.ts +0 -1
  56. package/transform/lib/index.js +535 -288
  57. package/transform/lib/linkers/alias.d.ts +0 -1
  58. package/transform/lib/linkers/alias.js +0 -1
  59. package/transform/lib/linkers/custom.d.ts +0 -1
  60. package/transform/lib/linkers/custom.js +0 -1
  61. package/transform/lib/linkers/imports.d.ts +0 -1
  62. package/transform/lib/linkers/imports.js +0 -1
  63. package/transform/lib/types.d.ts +3 -2
  64. package/transform/lib/types.js +2 -1
  65. package/transform/lib/util.d.ts +0 -1
  66. package/transform/lib/util.js +0 -1
  67. package/transform/lib/visitor.d.ts +0 -1
  68. package/transform/lib/visitor.js +0 -1
  69. package/transform/lib/builder.d.ts.map +0 -1
  70. package/transform/lib/builder.js.map +0 -1
  71. package/transform/lib/index.d.ts.map +0 -1
  72. package/transform/lib/index.js.map +0 -1
  73. package/transform/lib/linkers/alias.d.ts.map +0 -1
  74. package/transform/lib/linkers/alias.js.map +0 -1
  75. package/transform/lib/linkers/custom.d.ts.map +0 -1
  76. package/transform/lib/linkers/custom.js.map +0 -1
  77. package/transform/lib/linkers/imports.d.ts.map +0 -1
  78. package/transform/lib/linkers/imports.js.map +0 -1
  79. package/transform/lib/types.d.ts.map +0 -1
  80. package/transform/lib/types.js.map +0 -1
  81. package/transform/lib/util.d.ts.map +0 -1
  82. package/transform/lib/util.js.map +0 -1
  83. package/transform/lib/visitor.d.ts.map +0 -1
  84. 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, isUnescapedQuote, scanStringEnd } from "../../util";
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
- let keyStart: usize = 0;
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 += 2;
56
- while (srcStart < srcEnd) {
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
- @inline function deserializeMapBody<T extends Map<any, any>>(
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
- changetype<nonnull<T>>(out).clear();
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
- const valueEnd = scanValueEnd(srcStart, srcEnd);
262
- if (!valueEnd || valueEnd <= srcStart) break;
263
-
264
- // @ts-ignore: type
265
- changetype<nonnull<T>>(out).set(
266
- deserializeMapKey<indexof<T>>(keyStart, keyEnd),
267
- JSON.__deserialize<valueof<T>>(srcStart, valueEnd),
268
- );
269
- srcStart = valueEnd;
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, isUnescapedQuote, scanStringEnd } from "../../util";
17
- import { ptrToStr } from "../../util/ptrToStr";
18
- import { deserializeArray } from "./array";
19
- import { deserializeBoolean } from "./bool";
20
- import { deserializeFloat_NAIVE } from "./float";
21
- import { deserializeString_NAIVE } from "./string";
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
- let keyStart: usize = 0;
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
- while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2; // would like to optimize this later
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 += 2;
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
- let code = load<u16>(srcStart); // while (isSpace(code)) code = load<u16>(srcStart += 2);
54
- if (keyStart == 0) {
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
- } else {
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 out;
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
- // @ts-expect-error: Decorator valid here
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 {