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.
Files changed (122) hide show
  1. package/CHANGELOG.md +60 -19
  2. package/README.md +120 -21
  3. package/assembly/custom/chars.ts +39 -78
  4. package/assembly/deserialize/index/arbitrary.ts +28 -10
  5. package/assembly/deserialize/index/float.ts +2 -4
  6. package/assembly/deserialize/index/integer.ts +2 -4
  7. package/assembly/deserialize/index/object.ts +6 -1
  8. package/assembly/deserialize/index/string.ts +2 -7
  9. package/assembly/deserialize/index/unsigned.ts +2 -4
  10. package/assembly/deserialize/naive/array/arbitrary.ts +3 -136
  11. package/assembly/deserialize/naive/array/array.ts +30 -1
  12. package/assembly/deserialize/naive/array/integer.ts +2 -7
  13. package/assembly/deserialize/naive/array/map.ts +10 -14
  14. package/assembly/deserialize/naive/array/object.ts +10 -14
  15. package/assembly/deserialize/naive/array/struct.ts +19 -1
  16. package/assembly/deserialize/naive/bool.ts +1 -5
  17. package/assembly/deserialize/naive/date.ts +1 -2
  18. package/assembly/deserialize/naive/float.ts +4 -11
  19. package/assembly/deserialize/naive/integer.ts +2 -4
  20. package/assembly/deserialize/naive/map.ts +42 -205
  21. package/assembly/deserialize/naive/object.ts +291 -174
  22. package/assembly/deserialize/naive/raw.ts +1 -5
  23. package/assembly/deserialize/naive/set.ts +3 -6
  24. package/assembly/deserialize/naive/staticarray.ts +2 -4
  25. package/assembly/deserialize/naive/string.ts +68 -24
  26. package/assembly/deserialize/naive/typedarray.ts +1 -2
  27. package/assembly/deserialize/naive/unsigned.ts +2 -4
  28. package/assembly/deserialize/simd/array/integer.ts +5 -13
  29. package/assembly/deserialize/simd/float.ts +5 -12
  30. package/assembly/deserialize/simd/integer.ts +6 -15
  31. package/assembly/deserialize/simd/string.ts +21 -43
  32. package/assembly/deserialize/swar/array/arbitrary.ts +1 -2
  33. package/assembly/deserialize/swar/array/array.ts +2 -4
  34. package/assembly/deserialize/swar/array/bool.ts +2 -4
  35. package/assembly/deserialize/swar/array/box.ts +1 -2
  36. package/assembly/deserialize/swar/array/float.ts +8 -21
  37. package/assembly/deserialize/swar/array/generic.ts +2 -4
  38. package/assembly/deserialize/swar/array/integer.ts +13 -27
  39. package/assembly/deserialize/swar/array/map.ts +1 -2
  40. package/assembly/deserialize/swar/array/object.ts +2 -4
  41. package/assembly/deserialize/swar/array/raw.ts +1 -2
  42. package/assembly/deserialize/swar/array/shared.ts +9 -21
  43. package/assembly/deserialize/swar/array/string.ts +4 -10
  44. package/assembly/deserialize/swar/array/struct.ts +3 -9
  45. package/assembly/deserialize/swar/array.ts +1 -3
  46. package/assembly/deserialize/swar/float.ts +7 -17
  47. package/assembly/deserialize/swar/integer.ts +6 -15
  48. package/assembly/deserialize/swar/string.ts +40 -54
  49. package/assembly/deserialize/swar/typedarray.ts +4 -4
  50. package/assembly/index.d.ts +259 -21
  51. package/assembly/index.ts +1704 -266
  52. package/assembly/serialize/index/arbitrary.ts +70 -4
  53. package/assembly/serialize/index/jsonarray.ts +51 -0
  54. package/assembly/serialize/index/object.ts +39 -14
  55. package/assembly/serialize/index/string.ts +1 -2
  56. package/assembly/serialize/index/typedarray.ts +1 -2
  57. package/assembly/serialize/index.ts +1 -0
  58. package/assembly/serialize/naive/array.ts +23 -34
  59. package/assembly/serialize/naive/bool.ts +0 -1
  60. package/assembly/serialize/naive/float.ts +16 -25
  61. package/assembly/serialize/naive/integer.ts +1 -5
  62. package/assembly/serialize/naive/raw.ts +1 -2
  63. package/assembly/serialize/naive/set.ts +0 -4
  64. package/assembly/serialize/naive/staticarray.ts +0 -5
  65. package/assembly/serialize/naive/string.ts +11 -7
  66. package/assembly/serialize/naive/typedarray.ts +0 -6
  67. package/assembly/serialize/simd/string.ts +1 -3
  68. package/assembly/serialize/swar/string.ts +2 -4
  69. package/assembly/util/atoi-fast.ts +4 -14
  70. package/assembly/util/atoi.ts +1 -2
  71. package/assembly/util/bytes.ts +1 -2
  72. package/assembly/util/idofd.ts +1 -2
  73. package/assembly/util/isSpace.ts +1 -2
  74. package/assembly/util/itoa-fast.ts +9 -15
  75. package/assembly/util/nextPowerOf2.ts +1 -2
  76. package/assembly/util/parsefloat-fast.ts +4 -7
  77. package/assembly/util/ptrToStr.ts +1 -2
  78. package/assembly/util/scanValueEnd.ts +1 -2
  79. package/assembly/util/scanValueEndSimd.ts +198 -0
  80. package/assembly/util/scanValueEndSwar.ts +184 -0
  81. package/assembly/util/scientific.ts +8 -14
  82. package/assembly/util/simd-int.ts +4 -8
  83. package/assembly/util/snp.ts +2 -7
  84. package/assembly/util/stringScan.ts +2 -4
  85. package/assembly/util/swar-int.ts +8 -16
  86. package/assembly/util/swar.ts +2 -4
  87. package/lib/as-bs.ts +57 -42
  88. package/package.json +27 -10
  89. package/transform/lib/builder.d.ts +0 -1
  90. package/transform/lib/builder.js +0 -1
  91. package/transform/lib/index.d.ts +0 -1
  92. package/transform/lib/index.js +617 -326
  93. package/transform/lib/linkers/alias.d.ts +0 -1
  94. package/transform/lib/linkers/alias.js +0 -1
  95. package/transform/lib/linkers/custom.d.ts +0 -1
  96. package/transform/lib/linkers/custom.js +0 -1
  97. package/transform/lib/linkers/imports.d.ts +0 -1
  98. package/transform/lib/linkers/imports.js +0 -1
  99. package/transform/lib/types.d.ts +4 -2
  100. package/transform/lib/types.js +5 -1
  101. package/transform/lib/util.d.ts +0 -1
  102. package/transform/lib/util.js +0 -1
  103. package/transform/lib/visitor.d.ts +0 -1
  104. package/transform/lib/visitor.js +0 -1
  105. package/assembly/util/dragonbox-cache.ts +0 -445
  106. package/assembly/util/dragonbox.ts +0 -660
  107. package/transform/lib/builder.d.ts.map +0 -1
  108. package/transform/lib/builder.js.map +0 -1
  109. package/transform/lib/index.d.ts.map +0 -1
  110. package/transform/lib/index.js.map +0 -1
  111. package/transform/lib/linkers/alias.d.ts.map +0 -1
  112. package/transform/lib/linkers/alias.js.map +0 -1
  113. package/transform/lib/linkers/custom.d.ts.map +0 -1
  114. package/transform/lib/linkers/custom.js.map +0 -1
  115. package/transform/lib/linkers/imports.d.ts.map +0 -1
  116. package/transform/lib/linkers/imports.js.map +0 -1
  117. package/transform/lib/types.d.ts.map +0 -1
  118. package/transform/lib/types.js.map +0 -1
  119. package/transform/lib/util.d.ts.map +0 -1
  120. package/transform/lib/util.js.map +0 -1
  121. package/transform/lib/visitor.d.ts.map +0 -1
  122. 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, 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
+ 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 out = changetype<JSON.Obj>(dst || changetype<usize>(new JSON.Obj()));
29
-
30
- let keyStart: usize = 0;
31
- let keyEnd: usize = 0;
32
- let isKey = false;
33
- let depth = 0;
34
- let lastIndex: usize = 0;
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; // would like to optimize this later
59
+ while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
37
60
 
38
- if (srcStart - srcEnd == 0)
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 += 2;
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
- 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;
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
- 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
- }
123
+ out.pushRawSlot(
124
+ JSON.Value.bitsFrom<JSON.Value>(parseValue(srcStart, srcEnd)),
125
+ );
210
126
  }
127
+ srcStart = parseValueEnd;
211
128
  }
212
- return out;
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
- // @ts-ignore: inline
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
- // @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) {
@@ -157,8 +156,7 @@ export function deserializeSet<T extends Set<any>>(
157
156
  return out;
158
157
  }
159
158
 
160
- // @ts-expect-error: Decorator valid here
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
- // @ts-expect-error: Decorator valid here
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,