json-as 1.3.6 → 1.3.8

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 (155) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +1 -1
  3. package/assembly/deserialize/helpers/uint.ts +4 -1
  4. package/assembly/deserialize/index/arbitrary.ts +7 -3
  5. package/assembly/deserialize/index/array.ts +42 -17
  6. package/assembly/deserialize/index/bool.ts +1 -1
  7. package/assembly/deserialize/index/date.ts +1 -1
  8. package/assembly/deserialize/index/float.ts +40 -1
  9. package/assembly/deserialize/index/integer.ts +68 -1
  10. package/assembly/deserialize/index/map.ts +1 -1
  11. package/assembly/deserialize/index/object.ts +1 -1
  12. package/assembly/deserialize/index/raw.ts +1 -1
  13. package/assembly/deserialize/index/set.ts +1 -1
  14. package/assembly/deserialize/index/staticarray.ts +4 -1
  15. package/assembly/deserialize/index/string.ts +32 -4
  16. package/assembly/deserialize/index/struct.ts +1 -1
  17. package/assembly/deserialize/index/typedarray.ts +30 -10
  18. package/assembly/deserialize/index/unsigned.ts +78 -1
  19. package/assembly/deserialize/index.ts +1 -0
  20. package/assembly/deserialize/{simple → naive}/array/arbitrary.ts +24 -5
  21. package/assembly/deserialize/{simple → naive}/array/array.ts +8 -2
  22. package/assembly/deserialize/naive/array/bool.ts +68 -0
  23. package/assembly/deserialize/{simple → naive}/array/box.ts +8 -2
  24. package/assembly/deserialize/naive/array/float.ts +63 -0
  25. package/assembly/deserialize/{simple → naive}/array/generic.ts +14 -7
  26. package/assembly/deserialize/naive/array/integer.ts +86 -0
  27. package/assembly/deserialize/naive/array/map.ts +47 -0
  28. package/assembly/deserialize/naive/array/object.ts +47 -0
  29. package/assembly/deserialize/{simple → naive}/array/raw.ts +34 -7
  30. package/assembly/deserialize/naive/array/string.ts +69 -0
  31. package/assembly/deserialize/naive/array/struct.ts +47 -0
  32. package/assembly/deserialize/{simple → naive}/array.ts +15 -10
  33. package/assembly/deserialize/{simple → naive}/bool.ts +6 -2
  34. package/assembly/deserialize/naive/float.ts +135 -0
  35. package/assembly/deserialize/{simple → naive}/integer.ts +10 -2
  36. package/assembly/deserialize/{simple → naive}/map.ts +106 -27
  37. package/assembly/deserialize/{simple → naive}/object.ts +65 -19
  38. package/assembly/deserialize/{simple → naive}/raw.ts +4 -1
  39. package/assembly/deserialize/{simple → naive}/set.ts +49 -19
  40. package/assembly/deserialize/{simple → naive}/staticarray/array.ts +1 -1
  41. package/assembly/deserialize/{simple → naive}/staticarray/bool.ts +1 -1
  42. package/assembly/deserialize/{simple → naive}/staticarray/float.ts +1 -1
  43. package/assembly/deserialize/{simple → naive}/staticarray/integer.ts +1 -1
  44. package/assembly/deserialize/{simple → naive}/staticarray/string.ts +11 -3
  45. package/assembly/deserialize/{simple → naive}/staticarray/struct.ts +1 -2
  46. package/assembly/deserialize/{simple → naive}/staticarray.ts +68 -18
  47. package/assembly/deserialize/naive/string.ts +199 -0
  48. package/assembly/deserialize/{simple → naive}/struct.ts +5 -1
  49. package/assembly/deserialize/{simple → naive}/typedarray.ts +17 -4
  50. package/assembly/deserialize/{simple → naive}/unsigned.ts +10 -15
  51. package/assembly/deserialize/simd/array/integer.ts +339 -62
  52. package/assembly/deserialize/simd/float.ts +303 -0
  53. package/assembly/deserialize/simd/integer.ts +233 -0
  54. package/assembly/deserialize/simd/string.ts +266 -107
  55. package/assembly/deserialize/swar/array/arbitrary.ts +11 -3
  56. package/assembly/deserialize/swar/array/array.ts +40 -9
  57. package/assembly/deserialize/swar/array/bool.ts +28 -5
  58. package/assembly/deserialize/swar/array/box.ts +11 -3
  59. package/assembly/deserialize/swar/array/float.ts +295 -7
  60. package/assembly/deserialize/swar/array/generic.ts +28 -7
  61. package/assembly/deserialize/swar/array/integer.ts +363 -112
  62. package/assembly/deserialize/swar/array/map.ts +11 -3
  63. package/assembly/deserialize/swar/array/object.ts +37 -25
  64. package/assembly/deserialize/swar/array/raw.ts +11 -3
  65. package/assembly/deserialize/swar/array/shared.ts +63 -14
  66. package/assembly/deserialize/swar/array/string.ts +140 -7
  67. package/assembly/deserialize/swar/array/struct.ts +66 -12
  68. package/assembly/deserialize/swar/array.ts +12 -51
  69. package/assembly/deserialize/swar/float.ts +304 -0
  70. package/assembly/deserialize/swar/integer.ts +246 -0
  71. package/assembly/deserialize/swar/string.ts +213 -294
  72. package/assembly/deserialize/swar/typedarray.ts +224 -0
  73. package/assembly/index.d.ts +3 -1
  74. package/assembly/index.ts +402 -261
  75. package/assembly/serialize/index/array.ts +1 -1
  76. package/assembly/serialize/index/bool.ts +1 -1
  77. package/assembly/serialize/index/date.ts +1 -1
  78. package/assembly/serialize/index/float.ts +5 -1
  79. package/assembly/serialize/index/integer.ts +1 -1
  80. package/assembly/serialize/index/map.ts +1 -1
  81. package/assembly/serialize/index/raw.ts +1 -1
  82. package/assembly/serialize/index/set.ts +1 -1
  83. package/assembly/serialize/index/staticarray.ts +1 -1
  84. package/assembly/serialize/index/string.ts +1 -1
  85. package/assembly/serialize/index/struct.ts +1 -1
  86. package/assembly/serialize/index/typedarray.ts +21 -12
  87. package/assembly/serialize/index.ts +1 -0
  88. package/assembly/serialize/naive/array.ts +351 -0
  89. package/assembly/serialize/{simple → naive}/float.ts +4 -1
  90. package/assembly/serialize/naive/integer.ts +19 -0
  91. package/assembly/serialize/{simple → naive}/map.ts +6 -2
  92. package/assembly/serialize/{simple → naive}/raw.ts +5 -1
  93. package/assembly/serialize/{simple → naive}/set.ts +6 -1
  94. package/assembly/serialize/{simple → naive}/staticarray.ts +6 -1
  95. package/assembly/serialize/{simple → naive}/string.ts +1 -2
  96. package/assembly/serialize/{simple → naive}/typedarray.ts +10 -3
  97. package/assembly/serialize/simd/string.ts +6 -2
  98. package/assembly/serialize/swar/string.ts +15 -141
  99. package/assembly/util/atoi-fast.ts +81 -0
  100. package/assembly/util/concat.ts +5 -1
  101. package/assembly/util/dragonbox-cache.ts +443 -2
  102. package/assembly/util/dragonbox.ts +53 -17
  103. package/assembly/util/itoa-fast.ts +241 -0
  104. package/assembly/util/masks.ts +18 -1
  105. package/assembly/util/parsefloat-fast.ts +167 -0
  106. package/assembly/util/scanValueEnd.ts +78 -0
  107. package/assembly/util/scientific.ts +132 -0
  108. package/assembly/util/simd-int.ts +191 -0
  109. package/assembly/util/snp.ts +4 -1
  110. package/assembly/util/swar-int.ts +248 -0
  111. package/assembly/util/swar.ts +13 -3
  112. package/lib/as-bs.ts +27 -6
  113. package/package.json +15 -11
  114. package/transform/lib/builder.d.ts.map +1 -1
  115. package/transform/lib/builder.js +13 -5
  116. package/transform/lib/builder.js.map +1 -1
  117. package/transform/lib/index.d.ts +5 -0
  118. package/transform/lib/index.d.ts.map +1 -1
  119. package/transform/lib/index.js +1046 -340
  120. package/transform/lib/index.js.map +1 -1
  121. package/transform/lib/linkers/alias.d.ts.map +1 -1
  122. package/transform/lib/linkers/alias.js.map +1 -1
  123. package/transform/lib/linkers/custom.d.ts.map +1 -1
  124. package/transform/lib/linkers/custom.js +3 -2
  125. package/transform/lib/linkers/custom.js.map +1 -1
  126. package/transform/lib/linkers/imports.d.ts.map +1 -1
  127. package/transform/lib/linkers/imports.js.map +1 -1
  128. package/transform/lib/types.d.ts.map +1 -1
  129. package/transform/lib/types.js +54 -16
  130. package/transform/lib/types.js.map +1 -1
  131. package/transform/lib/util.d.ts.map +1 -1
  132. package/transform/lib/util.js +1 -1
  133. package/transform/lib/util.js.map +1 -1
  134. package/transform/lib/visitor.d.ts.map +1 -1
  135. package/transform/lib/visitor.js +2 -1
  136. package/transform/lib/visitor.js.map +1 -1
  137. package/assembly/custom/util.ts +0 -310
  138. package/assembly/deserialize/simple/arbitrary.ts +0 -23
  139. package/assembly/deserialize/simple/array/bool.ts +0 -17
  140. package/assembly/deserialize/simple/array/float.ts +0 -28
  141. package/assembly/deserialize/simple/array/integer.ts +0 -27
  142. package/assembly/deserialize/simple/array/map.ts +0 -28
  143. package/assembly/deserialize/simple/array/object.ts +0 -28
  144. package/assembly/deserialize/simple/array/string.ts +0 -23
  145. package/assembly/deserialize/simple/array/struct.ts +0 -28
  146. package/assembly/deserialize/simple/float.ts +0 -201
  147. package/assembly/deserialize/simple/string.ts +0 -132
  148. package/assembly/serialize/simple/arbitrary.ts +0 -79
  149. package/assembly/serialize/simple/array.ts +0 -86
  150. package/assembly/serialize/simple/integer.ts +0 -20
  151. package/assembly/serialize/simple/object.ts +0 -42
  152. /package/assembly/deserialize/{simple → naive}/date.ts +0 -0
  153. /package/assembly/serialize/{simple → naive}/bool.ts +0 -0
  154. /package/assembly/serialize/{simple → naive}/date.ts +0 -0
  155. /package/assembly/serialize/{simple → naive}/struct.ts +0 -0
package/assembly/index.ts CHANGED
@@ -2,39 +2,62 @@
2
2
 
3
3
  import { bs } from "../lib/as-bs";
4
4
  import { OBJECT, TOTAL_OVERHEAD } from "rt/common";
5
- import { serializeArray } from "./serialize/index/array";
6
- import { serializeMap } from "./serialize/index/map";
7
- import { serializeDate } from "./serialize/index/date";
8
- import { deserializeBoolean } from "./deserialize/index/bool";
9
- import { deserializeArray } from "./deserialize/index/array";
10
- import { deserializeFloat } from "./deserialize/index/float";
11
- import { deserializeMap } from "./deserialize/index/map";
12
- import { deserializeDate } from "./deserialize/index/date";
13
- import { deserializeInteger } from "./deserialize/index/integer";
14
- import { deserializeUnsigned } from "./deserialize/index/unsigned";
15
- import { serializeArbitrary } from "./serialize/index/arbitrary";
16
- import { serializeSet } from "./serialize/index/set";
17
- import { deserializeSet } from "./deserialize/index/set";
18
- import { serializeStaticArray } from "./serialize/index/staticarray";
19
- import { deserializeStaticArray } from "./deserialize/index/staticarray";
20
- import { BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA, NULL_WORD, QUOTE, NULL_WORD_U64, TRUE_WORD_U64, FALSE_WORD_U64 } from "./custom/chars";
5
+ import {
6
+ serializeArray,
7
+ serializeMap,
8
+ serializeDate,
9
+ serializeArbitrary,
10
+ serializeSet,
11
+ serializeStaticArray,
12
+ serializeBool,
13
+ serializeInteger,
14
+ serializeFloat,
15
+ serializeFloat32,
16
+ serializeFloat64,
17
+ serializeStruct,
18
+ serializeObject,
19
+ serializeRaw,
20
+ serializeString,
21
+ serializeArrayBufferUnsafe,
22
+ serializeDynamic,
23
+ serializeTypedArray,
24
+ } from "./serialize";
25
+ import {
26
+ deserializeBoolean,
27
+ deserializeArray,
28
+ deserializeFloat,
29
+ deserializeMap,
30
+ deserializeDate,
31
+ deserializeInteger,
32
+ deserializeUnsigned,
33
+ deserializeSet,
34
+ deserializeStaticArray,
35
+ deserializeArbitrary,
36
+ deserializeObject,
37
+ deserializeRaw,
38
+ deserializeString,
39
+ deserializeArrayBuffer,
40
+ deserializeTypedArray,
41
+ } from "./deserialize";
42
+ import {
43
+ BRACE_LEFT,
44
+ BRACE_RIGHT,
45
+ BRACKET_LEFT,
46
+ BRACKET_RIGHT,
47
+ COMMA,
48
+ NULL_WORD,
49
+ QUOTE,
50
+ NULL_WORD_U64,
51
+ TRUE_WORD_U64,
52
+ FALSE_WORD_U64,
53
+ } from "./custom/chars";
21
54
  import { itoa_buffered } from "util/number";
22
- import { serializeBool } from "./serialize/index/bool";
23
- import { serializeInteger } from "./serialize/index/integer";
24
- import { serializeFloat, serializeFloat32, serializeFloat64 } from "./serialize/index/float";
25
- import { dragonbox_f32_buffered, dragonbox_f64_buffered } from "./util/dragonbox";
26
- import { serializeStruct } from "./serialize/index/struct";
55
+ import {
56
+ dragonbox_f32_buffered,
57
+ dragonbox_f64_buffered,
58
+ } from "./util/dragonbox";
27
59
  import { ptrToStr } from "./util/ptrToStr";
28
60
  import { atoi, bytes, scanStringEnd } from "./util";
29
- import { deserializeArbitrary } from "./deserialize/index/arbitrary";
30
- import { serializeObject } from "./serialize/index/object";
31
- import { deserializeObject } from "./deserialize/index/object";
32
- import { serializeRaw } from "./serialize/index/raw";
33
- import { deserializeRaw } from "./deserialize/index/raw";
34
- import { deserializeString } from "./deserialize/index/string";
35
- import { serializeString } from "./serialize/index/string";
36
- import { deserializeArrayBuffer, deserializeTypedArray } from "./deserialize/index/typedarray";
37
- import { serializeArrayBufferUnsafe, serializeDynamic, serializeTypedArray } from "./serialize/index/typedarray";
38
61
 
39
62
  /**
40
63
  * Offset of the 'storage' property in the JSON.Value class.
@@ -71,7 +94,10 @@ export namespace JSON {
71
94
  * @returns string
72
95
  */
73
96
  // @ts-expect-error: inline
74
- @inline export function stringify<T>(data: T, out: string | null = null): string {
97
+ @inline export function stringify<T>(
98
+ data: T,
99
+ out: string | null = null,
100
+ ): string {
75
101
  if (isBoolean<T>()) {
76
102
  if (out) {
77
103
  if (<bool>data == true) {
@@ -85,7 +111,12 @@ export namespace JSON {
85
111
  return out;
86
112
  }
87
113
  return data ? "true" : "false";
88
- } else if (isInteger<T>() && !isSigned<T>() && nameof<T>() == "usize" && data == 0) {
114
+ } else if (
115
+ isInteger<T>() &&
116
+ !isSigned<T>() &&
117
+ nameof<T>() == "usize" &&
118
+ data == 0
119
+ ) {
89
120
  if (out) {
90
121
  out = changetype<string>(__renew(changetype<usize>(out), 8));
91
122
  store<u64>(changetype<usize>(out), NULL_WORD_U64);
@@ -94,15 +125,24 @@ export namespace JSON {
94
125
  return NULL_WORD;
95
126
  } else if (isInteger<T>(data)) {
96
127
  if (out) {
97
- out = changetype<string>(__renew(changetype<usize>(out), sizeof<T>() << 3));
128
+ out = changetype<string>(
129
+ __renew(changetype<usize>(out), sizeof<T>() << 3),
130
+ );
98
131
 
99
132
  const bytes = itoa_buffered(changetype<usize>(out), data) << 1;
100
- return (out = changetype<string>(__renew(changetype<usize>(out), bytes)));
133
+ return (out = changetype<string>(
134
+ __renew(changetype<usize>(out), bytes),
135
+ ));
101
136
  }
102
137
  return data.toString();
103
138
  } else if (isFloat<T>(data)) {
104
- out = out ? changetype<string>(__renew(changetype<usize>(out), 64)) : changetype<string>(__new(64, idof<string>()));
105
- const bytes = (sizeof<T>() == 4 ? dragonbox_f32_buffered(changetype<usize>(out), <f32>data) : dragonbox_f64_buffered(changetype<usize>(out), <f64>data)) << 1;
139
+ out = out
140
+ ? changetype<string>(__renew(changetype<usize>(out), 64))
141
+ : changetype<string>(__new(64, idof<string>()));
142
+ const bytes =
143
+ (sizeof<T>() == 4
144
+ ? dragonbox_f32_buffered(changetype<usize>(out), <f32>data)
145
+ : dragonbox_f64_buffered(changetype<usize>(out), <f64>data)) << 1;
106
146
  return changetype<string>(__renew(changetype<usize>(out), bytes));
107
147
  } else if (isNullable<T>() && changetype<usize>(data) == <usize>0) {
108
148
  if (out) {
@@ -125,78 +165,21 @@ export namespace JSON {
125
165
  inline.always(data.__SERIALIZE(changetype<usize>(data)));
126
166
  return bs.out<string>();
127
167
  } else if (data instanceof Date) {
128
- out = out ? changetype<string>(__renew(changetype<usize>(out), 52)) : changetype<string>(__new(52, idof<string>()));
168
+ out = out
169
+ ? changetype<string>(__renew(changetype<usize>(out), 52))
170
+ : changetype<string>(__new(52, idof<string>()));
129
171
 
130
172
  store<u16>(changetype<usize>(out), QUOTE);
131
- memory.copy(changetype<usize>(out) + 2, changetype<usize>(data.toISOString()), 48);
173
+ memory.copy(
174
+ changetype<usize>(out) + 2,
175
+ changetype<usize>(data.toISOString()),
176
+ 48,
177
+ );
132
178
  store<u16>(changetype<usize>(out), QUOTE, 50);
133
179
  return changetype<string>(out);
134
- } else if (data instanceof Array) {
135
- // @ts-expect-error
136
- inline.always(serializeArray(changetype<nonnull<T>>(data)));
137
- return bs.out<string>();
138
- } else if (data instanceof StaticArray) {
139
- // @ts-expect-error
140
- inline.always(serializeStaticArray(changetype<nonnull<T>>(data)));
141
- return bs.out<string>();
142
- } else if (data instanceof Int8Array) {
143
- inline.always(serializeTypedArray<Int8Array>(data));
144
- return bs.out<string>();
145
- } else if (data instanceof Uint8Array) {
146
- inline.always(serializeTypedArray<Uint8Array>(data));
147
- return bs.out<string>();
148
- } else if (data instanceof Uint8ClampedArray) {
149
- inline.always(serializeTypedArray<Uint8ClampedArray>(data));
150
- return bs.out<string>();
151
- } else if (data instanceof Int16Array) {
152
- inline.always(serializeTypedArray<Int16Array>(data));
153
- return bs.out<string>();
154
- } else if (data instanceof Uint16Array) {
155
- inline.always(serializeTypedArray<Uint16Array>(data));
156
- return bs.out<string>();
157
- } else if (data instanceof Int32Array) {
158
- inline.always(serializeTypedArray<Int32Array>(data));
159
- return bs.out<string>();
160
- } else if (data instanceof Uint32Array) {
161
- inline.always(serializeTypedArray<Uint32Array>(data));
162
- return bs.out<string>();
163
- } else if (data instanceof Int64Array) {
164
- inline.always(serializeTypedArray<Int64Array>(data));
165
- return bs.out<string>();
166
- } else if (data instanceof Uint64Array) {
167
- inline.always(serializeTypedArray<Uint64Array>(data));
168
- return bs.out<string>();
169
- } else if (data instanceof Float32Array) {
170
- inline.always(serializeTypedArray<Float32Array>(data));
171
- return bs.out<string>();
172
- } else if (data instanceof Float64Array) {
173
- inline.always(serializeTypedArray<Float64Array>(data));
174
- return bs.out<string>();
175
- } else if (data instanceof ArrayBuffer) {
176
- const dataStart = changetype<usize>(data);
177
- serializeArrayBufferUnsafe(dataStart, changetype<OBJECT>(dataStart - TOTAL_OVERHEAD).rtSize);
178
- return bs.out<string>();
179
- } else if (data instanceof Set) {
180
- // @ts-expect-error
181
- inline.always(serializeSet(changetype<nonnull<T>>(data)));
182
- return bs.out<string>();
183
- } else if (data instanceof Map) {
184
- // @ts-expect-error
185
- inline.always(serializeMap(changetype<nonnull<T>>(data)));
186
- return bs.out<string>();
187
- } else if (data instanceof JSON.Raw) {
188
- serializeRaw(data);
189
- return bs.out<string>();
190
- } else if (data instanceof JSON.Value) {
191
- inline.always(serializeArbitrary(data));
192
- return bs.out<string>();
193
- } else if (data instanceof JSON.Obj) {
194
- inline.always(serializeObject(data));
195
- return bs.out<string>();
196
- } else if (data instanceof JSON.Box) {
197
- return JSON.stringify(data.value);
198
180
  } else {
199
- throw new Error(`Could not serialize data of type '${nameof<T>()}'. ` + `If this is a custom class, add the @json decorator: @json class ${nameof<T>()} { ... }. ` + `Supported types: primitives, string, Array, StaticArray, TypedArray, ArrayBuffer, Map, Date, and @json decorated classes.`);
181
+ serializeReference<T>(data);
182
+ return bs.out<string>();
200
183
  }
201
184
  }
202
185
 
@@ -210,15 +193,29 @@ export namespace JSON {
210
193
  */
211
194
  // @ts-expect-error: inline
212
195
  @inline export function parse<T>(data: string): T {
213
- const dataSize = bytes(data);
214
- const dataPtr = changetype<usize>(data);
196
+ let dataPtr = changetype<usize>(data);
197
+ const dataEnd = dataPtr + bytes(data);
198
+ // Entry point skips leading whitespace: every deserialize handler may then
199
+ // assume srcStart points at the first non-whitespace char. Handlers must
200
+ // NOT re-skip leading whitespace themselves. (Trailing whitespace is left
201
+ // intact — scalars stop at the value end, composites self-trim, and
202
+ // JSON.Raw intentionally preserves trailing bytes.)
203
+ while (dataPtr < dataEnd && JSON.Util.isSpace(load<u16>(dataPtr)))
204
+ dataPtr += 2;
205
+ const dataSize = dataEnd - dataPtr;
215
206
  if (isBoolean<T>()) {
216
207
  return deserializeBoolean(dataPtr, dataPtr + dataSize) as T;
217
208
  } else if (isInteger<T>()) {
218
- return isSigned<T>() ? deserializeInteger<T>(dataPtr, dataPtr + dataSize) : deserializeUnsigned<T>(dataPtr, dataPtr + dataSize);
209
+ return isSigned<T>()
210
+ ? deserializeInteger<T>(dataPtr, dataPtr + dataSize)
211
+ : deserializeUnsigned<T>(dataPtr, dataPtr + dataSize);
219
212
  } else if (isFloat<T>()) {
220
213
  return deserializeFloat<T>(dataPtr, dataPtr + dataSize);
221
- } else if (isNullable<T>() && dataSize == 8 && load<u64>(dataPtr) == NULL_WORD_U64) {
214
+ } else if (
215
+ isNullable<T>() &&
216
+ dataSize == 8 &&
217
+ load<u64>(dataPtr) == NULL_WORD_U64
218
+ ) {
222
219
  return null;
223
220
  } else if (isString<T>()) {
224
221
  return deserializeString(dataPtr, dataPtr + dataSize) as T;
@@ -230,13 +227,30 @@ export namespace JSON {
230
227
  // @ts-expect-error
231
228
  return out.__DESERIALIZE_CUSTOM(data);
232
229
  // @ts-expect-error: Defined by transform
233
- } else if (isDefined(type.__DESERIALIZE_SLOW) || isDefined(type.__DESERIALIZE_FAST)) {
234
- const out = changetype<nonnull<T>>(__new(offsetof<nonnull<T>>(), idof<nonnull<T>>()));
230
+ } else if (
231
+ isDefined(type.__DESERIALIZE_SLOW) ||
232
+ isDefined(type.__DESERIALIZE_FAST)
233
+ ) {
234
+ const out = changetype<nonnull<T>>(
235
+ __new(offsetof<nonnull<T>>(), idof<nonnull<T>>()),
236
+ );
235
237
  // @ts-expect-error: Defined by transform
236
238
  if (isDefined(type.__DESERIALIZE_FAST)) {
237
239
  // @ts-expect-error: Defined by transform
238
- if (out.__DESERIALIZE_FAST(dataPtr, dataPtr + dataSize, out) == dataPtr + dataSize) return out;
239
- // @ts-expect-error: Defined by transform
240
+ const fastEnd = out.__DESERIALIZE_FAST(
241
+ dataPtr,
242
+ dataPtr + dataSize,
243
+ out,
244
+ );
245
+ // A non-zero return means the fast path matched; accept it when only
246
+ // trailing whitespace remains (pretty-printed input ends with a
247
+ // newline, so the cursor stops just past `}` rather than at srcEnd).
248
+ if (
249
+ fastEnd != 0 &&
250
+ JSON.Util.skipWhitespace(fastEnd, dataPtr + dataSize) ==
251
+ dataPtr + dataSize
252
+ )
253
+ return out;
240
254
  }
241
255
  if (isDefined(type.__INITIALIZE)) out.__INITIALIZE();
242
256
  // @ts-expect-error: Defined by transform
@@ -249,40 +263,48 @@ export namespace JSON {
249
263
  }
250
264
  if (type instanceof StaticArray) {
251
265
  // @ts-expect-error
252
- return inline.always(deserializeStaticArray<nonnull<T>>(dataPtr, dataPtr + dataSize, 0));
266
+ return inline.always(
267
+ deserializeStaticArray<nonnull<T>>(dataPtr, dataPtr + dataSize, 0),
268
+ );
253
269
  } else if (type instanceof Array) {
254
270
  // @ts-expect-error
255
- return inline.always(deserializeArray<nonnull<T>>(dataPtr, dataPtr + dataSize, changetype<usize>(instantiate<T>())));
256
- } else if (type instanceof Int8Array) {
257
- return deserializeTypedArray<nonnull<T>>(dataPtr, dataPtr + dataSize, 0) as T;
258
- } else if (type instanceof Uint8Array) {
259
- return deserializeTypedArray<nonnull<T>>(dataPtr, dataPtr + dataSize, 0) as T;
260
- } else if (type instanceof Uint8ClampedArray) {
261
- return deserializeTypedArray<nonnull<T>>(dataPtr, dataPtr + dataSize, 0) as T;
262
- } else if (type instanceof Int16Array) {
263
- return deserializeTypedArray<nonnull<T>>(dataPtr, dataPtr + dataSize, 0) as T;
264
- } else if (type instanceof Uint16Array) {
265
- return deserializeTypedArray<nonnull<T>>(dataPtr, dataPtr + dataSize, 0) as T;
266
- } else if (type instanceof Int32Array) {
267
- return deserializeTypedArray<nonnull<T>>(dataPtr, dataPtr + dataSize, 0) as T;
268
- } else if (type instanceof Uint32Array) {
269
- return deserializeTypedArray<nonnull<T>>(dataPtr, dataPtr + dataSize, 0) as T;
270
- } else if (type instanceof Int64Array) {
271
- return deserializeTypedArray<nonnull<T>>(dataPtr, dataPtr + dataSize, 0) as T;
272
- } else if (type instanceof Uint64Array) {
273
- return deserializeTypedArray<nonnull<T>>(dataPtr, dataPtr + dataSize, 0) as T;
274
- } else if (type instanceof Float32Array) {
275
- return deserializeTypedArray<nonnull<T>>(dataPtr, dataPtr + dataSize, 0) as T;
276
- } else if (type instanceof Float64Array) {
277
- return deserializeTypedArray<nonnull<T>>(dataPtr, dataPtr + dataSize, 0) as T;
271
+ return inline.always(
272
+ deserializeArray<nonnull<T>>(
273
+ dataPtr,
274
+ dataPtr + dataSize,
275
+ changetype<usize>(instantiate<T>()),
276
+ ),
277
+ );
278
+ } else if (
279
+ type instanceof Int8Array ||
280
+ type instanceof Uint8Array ||
281
+ type instanceof Uint8ClampedArray ||
282
+ type instanceof Int16Array ||
283
+ type instanceof Uint16Array ||
284
+ type instanceof Int32Array ||
285
+ type instanceof Uint32Array ||
286
+ type instanceof Int64Array ||
287
+ type instanceof Uint64Array ||
288
+ type instanceof Float32Array ||
289
+ type instanceof Float64Array
290
+ ) {
291
+ return deserializeTypedArray<nonnull<T>>(
292
+ dataPtr,
293
+ dataPtr + dataSize,
294
+ 0,
295
+ ) as T;
278
296
  } else if (type instanceof ArrayBuffer) {
279
297
  return deserializeArrayBuffer(dataPtr, dataPtr + dataSize, 0) as T;
280
298
  } else if (type instanceof Set) {
281
299
  // @ts-expect-error
282
- return inline.always(deserializeSet<nonnull<T>>(dataPtr, dataPtr + dataSize, 0));
300
+ return inline.always(
301
+ deserializeSet<nonnull<T>>(dataPtr, dataPtr + dataSize, 0),
302
+ );
283
303
  } else if (type instanceof Map) {
284
304
  // @ts-expect-error
285
- return inline.always(deserializeMap<nonnull<T>>(dataPtr, dataPtr + dataSize, 0));
305
+ return inline.always(
306
+ deserializeMap<nonnull<T>>(dataPtr, dataPtr + dataSize, 0),
307
+ );
286
308
  } else if (type instanceof Date) {
287
309
  // @ts-expect-error
288
310
  return deserializeDate(dataPtr, dataPtr + dataSize);
@@ -291,7 +313,9 @@ export namespace JSON {
291
313
  return deserializeRaw(dataPtr, dataPtr + dataSize);
292
314
  } else if (type instanceof JSON.Value) {
293
315
  // @ts-expect-error
294
- return inline.always(deserializeArbitrary(dataPtr, dataPtr + dataSize, 0));
316
+ return inline.always(
317
+ deserializeArbitrary(dataPtr, dataPtr + dataSize, 0),
318
+ );
295
319
  } else if (type instanceof JSON.Obj) {
296
320
  // @ts-expect-error
297
321
  return inline.always(deserializeObject(dataPtr, dataPtr + dataSize, 0));
@@ -299,7 +323,11 @@ export namespace JSON {
299
323
  // @ts-expect-error
300
324
  return new JSON.Box(parseBox(data, changetype<nonnull<T>>(0).value));
301
325
  } else {
302
- throw new Error(`Could not deserialize JSON to type '${nameof<T>()}'. ` + `If this is a custom class, ensure it has the @json decorator: @json class ${nameof<T>()} { ... }. ` + `Input: "${data.length > 50 ? data.slice(0, 50) + "..." : data}"`);
326
+ throw new Error(
327
+ `Could not deserialize JSON to type '${nameof<T>()}'. ` +
328
+ `If this is a custom class, ensure it has the @json decorator: @json class ${nameof<T>()} { ... }. ` +
329
+ `Input: "${data.length > 50 ? data.slice(0, 50) + "..." : data}"`,
330
+ );
303
331
  }
304
332
  }
305
333
  }
@@ -450,7 +478,9 @@ export namespace JSON {
450
478
  * @returns An instance of JSON.Value.
451
479
  */
452
480
  @inline static empty(): JSON.Value {
453
- return changetype<JSON.Value>(__new(offsetof<JSON.Value>(), idof<JSON.Value>()));
481
+ return changetype<JSON.Value>(
482
+ __new(offsetof<JSON.Value>(), idof<JSON.Value>()),
483
+ );
454
484
  }
455
485
 
456
486
  /**
@@ -460,7 +490,9 @@ export namespace JSON {
460
490
  */
461
491
  @inline static from<T>(value: T): JSON.Value {
462
492
  if (value instanceof JSON.Value) return value;
463
- const out = changetype<JSON.Value>(__new(offsetof<JSON.Value>(), idof<JSON.Value>()));
493
+ const out = changetype<JSON.Value>(
494
+ __new(offsetof<JSON.Value>(), idof<JSON.Value>()),
495
+ );
464
496
  out.set<T>(value);
465
497
  return out;
466
498
  }
@@ -471,12 +503,20 @@ export namespace JSON {
471
503
  * @returns JSON.Types
472
504
  */
473
505
  @inline getType<T>(value: T): JSON.Types {
474
- if (isNullable<T>() && changetype<usize>(value) === 0) return JSON.Types.Null;
506
+ if (isNullable<T>() && changetype<usize>(value) === 0)
507
+ return JSON.Types.Null;
475
508
  if (isBoolean<T>()) return JSON.Types.Bool;
476
- if (isInteger<T>() && !isSigned<T>() && changetype<usize>(value) == 0 && nameof<T>() == "usize") return JSON.Types.Null;
509
+ if (
510
+ isInteger<T>() &&
511
+ !isSigned<T>() &&
512
+ changetype<usize>(value) == 0 &&
513
+ nameof<T>() == "usize"
514
+ )
515
+ return JSON.Types.Null;
477
516
  if (isString<T>()) return JSON.Types.String;
478
517
  // @ts-expect-error: can assume that T is ArrayLike based on previous condition
479
- if (isArray<T>() && idof<valueof<T>>() == idof<JSON.Value>()) return JSON.Types.Array;
518
+ if (isArray<T>() && idof<valueof<T>>() == idof<JSON.Value>())
519
+ return JSON.Types.Array;
480
520
  if (value instanceof JSON.Box) return this.getType(value.value);
481
521
  if (value instanceof i8) return JSON.Types.I8;
482
522
  if (value instanceof i16) return JSON.Types.I16;
@@ -489,18 +529,22 @@ export namespace JSON {
489
529
  if (value instanceof f32) return JSON.Types.F32;
490
530
  if (value instanceof f64) return JSON.Types.F64;
491
531
  // @ts-expect-error: supplied by transform
492
- if (isDefined(value.__SERIALIZE) && isManaged<T>(value)) return u16(idof<T>()) + JSON.Types.Struct;
493
- if (value instanceof Int8Array) return JSON.Types.TypedArray;
494
- if (value instanceof Uint8Array) return JSON.Types.TypedArray;
495
- if (value instanceof Uint8ClampedArray) return JSON.Types.TypedArray;
496
- if (value instanceof Int16Array) return JSON.Types.TypedArray;
497
- if (value instanceof Uint16Array) return JSON.Types.TypedArray;
498
- if (value instanceof Int32Array) return JSON.Types.TypedArray;
499
- if (value instanceof Uint32Array) return JSON.Types.TypedArray;
500
- if (value instanceof Int64Array) return JSON.Types.TypedArray;
501
- if (value instanceof Uint64Array) return JSON.Types.TypedArray;
502
- if (value instanceof Float32Array) return JSON.Types.TypedArray;
503
- if (value instanceof Float64Array) return JSON.Types.TypedArray;
532
+ if (isDefined(value.__SERIALIZE) && isManaged<T>(value))
533
+ return u16(idof<T>()) + JSON.Types.Struct;
534
+ if (
535
+ value instanceof Int8Array ||
536
+ value instanceof Uint8Array ||
537
+ value instanceof Uint8ClampedArray ||
538
+ value instanceof Int16Array ||
539
+ value instanceof Uint16Array ||
540
+ value instanceof Int32Array ||
541
+ value instanceof Uint32Array ||
542
+ value instanceof Int64Array ||
543
+ value instanceof Uint64Array ||
544
+ value instanceof Float32Array ||
545
+ value instanceof Float64Array
546
+ )
547
+ return JSON.Types.TypedArray;
504
548
  if (value instanceof ArrayBuffer) return JSON.Types.ArrayBuffer;
505
549
  if (value instanceof Map) return JSON.Types.Map;
506
550
  if (value instanceof JSON.Raw) return JSON.Types.Raw;
@@ -515,29 +559,47 @@ export namespace JSON {
515
559
  this.type = this.getType<T>(value);
516
560
 
517
561
  if (value instanceof JSON.Box) this.set(value.value);
518
- else if (isBoolean<T>()) store<T>(changetype<usize>(this), value, STORAGE);
519
- else if (isInteger<T>() && !isSigned<T>() && changetype<usize>(value) == 0 && nameof<T>() == "usize") store<usize>(changetype<usize>(this), 0, STORAGE);
520
- else if (isInteger<T>() || isFloat<T>()) store<T>(changetype<usize>(this), value, STORAGE);
521
- else if (isNullable<T>() && changetype<usize>(value) === 0) store<usize>(changetype<usize>(this), 0, STORAGE);
562
+ else if (isBoolean<T>())
563
+ store<T>(changetype<usize>(this), value, STORAGE);
564
+ else if (
565
+ isInteger<T>() &&
566
+ !isSigned<T>() &&
567
+ changetype<usize>(value) == 0 &&
568
+ nameof<T>() == "usize"
569
+ )
570
+ store<usize>(changetype<usize>(this), 0, STORAGE);
571
+ else if (isInteger<T>() || isFloat<T>())
572
+ store<T>(changetype<usize>(this), value, STORAGE);
573
+ else if (isNullable<T>() && changetype<usize>(value) === 0)
574
+ store<usize>(changetype<usize>(this), 0, STORAGE);
522
575
  else if (isString<T>()) store<T>(changetype<usize>(this), value, STORAGE);
523
- else if (value instanceof JSON.Raw) store<T>(changetype<usize>(this), value, STORAGE);
576
+ else if (value instanceof JSON.Raw)
577
+ store<T>(changetype<usize>(this), value, STORAGE);
524
578
  // @ts-expect-error: supplied by transform
525
579
  else if (isDefined(value.__SERIALIZE) && isManaged<T>(value)) {
526
580
  // @ts-expect-error
527
- if (!JSON.Value.METHODS.has(idof<T>())) JSON.Value.METHODS.set(idof<T>(), value.__SERIALIZE.index);
528
- store<usize>(changetype<usize>(this), changetype<usize>(value), STORAGE);
529
- } else if (value instanceof Int8Array) store<T>(changetype<usize>(this), value, STORAGE);
530
- else if (value instanceof Uint8Array) store<T>(changetype<usize>(this), value, STORAGE);
531
- else if (value instanceof Uint8ClampedArray) store<T>(changetype<usize>(this), value, STORAGE);
532
- else if (value instanceof Int16Array) store<T>(changetype<usize>(this), value, STORAGE);
533
- else if (value instanceof Uint16Array) store<T>(changetype<usize>(this), value, STORAGE);
534
- else if (value instanceof Int32Array) store<T>(changetype<usize>(this), value, STORAGE);
535
- else if (value instanceof Uint32Array) store<T>(changetype<usize>(this), value, STORAGE);
536
- else if (value instanceof Int64Array) store<T>(changetype<usize>(this), value, STORAGE);
537
- else if (value instanceof Uint64Array) store<T>(changetype<usize>(this), value, STORAGE);
538
- else if (value instanceof Float32Array) store<T>(changetype<usize>(this), value, STORAGE);
539
- else if (value instanceof Float64Array) store<T>(changetype<usize>(this), value, STORAGE);
540
- else if (value instanceof ArrayBuffer) store<T>(changetype<usize>(this), value, STORAGE);
581
+ if (!JSON.Value.METHODS.has(idof<T>()))
582
+ JSON.Value.METHODS.set(idof<T>(), value.__SERIALIZE.index);
583
+ store<usize>(
584
+ changetype<usize>(this),
585
+ changetype<usize>(value),
586
+ STORAGE,
587
+ );
588
+ } else if (
589
+ value instanceof Int8Array ||
590
+ value instanceof Uint8Array ||
591
+ value instanceof Uint8ClampedArray ||
592
+ value instanceof Int16Array ||
593
+ value instanceof Uint16Array ||
594
+ value instanceof Int32Array ||
595
+ value instanceof Uint32Array ||
596
+ value instanceof Int64Array ||
597
+ value instanceof Uint64Array ||
598
+ value instanceof Float32Array ||
599
+ value instanceof Float64Array ||
600
+ value instanceof ArrayBuffer
601
+ )
602
+ store<T>(changetype<usize>(this), value, STORAGE);
541
603
  else if (value instanceof Map) {
542
604
  if (idof<T>() !== idof<Map<string, JSON.Value>>()) {
543
605
  abort("Maps must be of type Map<string, JSON.Value>!");
@@ -769,7 +831,9 @@ export namespace JSON {
769
831
 
770
832
  const parsed = JSON.parse<JSON.Value>(JSON.stringify(value));
771
833
  if (parsed.type != JSON.Types.Object) {
772
- throw new Error("JSON.Obj.from expects a value that serializes to a JSON object");
834
+ throw new Error(
835
+ "JSON.Obj.from expects a value that serializes to a JSON object",
836
+ );
773
837
  }
774
838
  return parsed.get<JSON.Obj>();
775
839
  }
@@ -779,7 +843,8 @@ export namespace JSON {
779
843
  */
780
844
  export class Box<T> {
781
845
  constructor(public value: T) {
782
- if (!isInteger<T>() && !isFloat<T>() && !isBoolean<T>()) ERROR("JSON.Box should only hold primitive types!");
846
+ if (!isInteger<T>() && !isFloat<T>() && !isBoolean<T>())
847
+ ERROR("JSON.Box should only hold primitive types!");
783
848
  }
784
849
  /**
785
850
  * Set the internal value of Box to new value
@@ -802,9 +867,11 @@ export namespace JSON {
802
867
  * @returns Box<T> | null
803
868
  */
804
869
  @inline static fromValue<T>(value: JSON.Value): Box<T> | null {
805
- if (!(value instanceof JSON.Value)) throw new Error("value must be of type JSON.Value");
870
+ if (!(value instanceof JSON.Value))
871
+ throw new Error("value must be of type JSON.Value");
806
872
  if (value.type === JSON.Types.Null) return null;
807
- const v = value.type === JSON.Types.F64 ? value.get<f64>() : value.get<T>();
873
+ const v =
874
+ value.type === JSON.Types.F64 ? value.get<f64>() : value.get<T>();
808
875
  // @ts-expect-error
809
876
  return new Box(isInteger<T>() || isFloat<T>() ? <T>v : v);
810
877
  }
@@ -835,7 +902,7 @@ export namespace JSON {
835
902
  * @param data - T
836
903
  * @returns void
837
904
  */
838
- export function __serialize<T>(data: T): void {
905
+ function __serialize<T>(data: T): void {
839
906
  if (isBoolean<T>()) {
840
907
  serializeBool(data as bool);
841
908
  } else if (isInteger<T>() && nameof<T>() == "usize" && data == 0) {
@@ -867,53 +934,8 @@ export namespace JSON {
867
934
  } else if (data instanceof Date) {
868
935
  // @ts-expect-error
869
936
  inline.always(serializeDate(changetype<nonnull<T>>(data)));
870
- } else if (data instanceof Array) {
871
- // @ts-expect-error
872
- serializeArray(changetype<nonnull<T>>(data));
873
- } else if (data instanceof StaticArray) {
874
- // @ts-expect-error
875
- serializeStaticArray(changetype<nonnull<T>>(data));
876
- } else if (data instanceof Int8Array) {
877
- serializeTypedArray<Int8Array>(data);
878
- } else if (data instanceof Uint8Array) {
879
- serializeTypedArray<Uint8Array>(data);
880
- } else if (data instanceof Uint8ClampedArray) {
881
- serializeTypedArray<Uint8ClampedArray>(data);
882
- } else if (data instanceof Int16Array) {
883
- serializeTypedArray<Int16Array>(data);
884
- } else if (data instanceof Uint16Array) {
885
- serializeTypedArray<Uint16Array>(data);
886
- } else if (data instanceof Int32Array) {
887
- serializeTypedArray<Int32Array>(data);
888
- } else if (data instanceof Uint32Array) {
889
- serializeTypedArray<Uint32Array>(data);
890
- } else if (data instanceof Int64Array) {
891
- serializeTypedArray<Int64Array>(data);
892
- } else if (data instanceof Uint64Array) {
893
- serializeTypedArray<Uint64Array>(data);
894
- } else if (data instanceof Float32Array) {
895
- serializeTypedArray<Float32Array>(data);
896
- } else if (data instanceof Float64Array) {
897
- serializeTypedArray<Float64Array>(data);
898
- } else if (data instanceof ArrayBuffer) {
899
- const dataStart = changetype<usize>(data);
900
- serializeArrayBufferUnsafe(dataStart, changetype<OBJECT>(dataStart - TOTAL_OVERHEAD).rtSize);
901
- } else if (data instanceof Set) {
902
- // @ts-expect-error
903
- serializeSet(changetype<nonnull<T>>(data));
904
- } else if (data instanceof Map) {
905
- // @ts-expect-error
906
- serializeMap(changetype<nonnull<T>>(data));
907
- } else if (data instanceof JSON.Raw) {
908
- serializeRaw(data);
909
- } else if (data instanceof JSON.Value) {
910
- serializeArbitrary(data);
911
- } else if (data instanceof JSON.Obj) {
912
- serializeObject(data);
913
- } else if (data instanceof JSON.Box) {
914
- __serialize(data.value);
915
937
  } else {
916
- throw new Error(`Could not serialize data of type '${nameof<T>()}'. ` + `If this is a custom class, add the @json decorator: @json class ${nameof<T>()} { ... }. ` + `Supported types: primitives, string, Array, StaticArray, TypedArray, ArrayBuffer, Map, Date, and @json decorated classes.`);
938
+ serializeReference<T>(data);
917
939
  }
918
940
  }
919
941
 
@@ -925,19 +947,33 @@ export namespace JSON {
925
947
  * @param dst - usize
926
948
  * @returns void
927
949
  */
928
- export function __deserialize<T>(srcStart: usize, srcEnd: usize, dst: usize = 0): T {
950
+ function __deserialize<T>(srcStart: usize, srcEnd: usize, dst: usize = 0): T {
951
+ // Skip leading whitespace once here so every handler below may assume
952
+ // srcStart is at the first non-whitespace char. (Trailing whitespace is
953
+ // left intact — composites self-trim and JSON.Raw preserves it.)
954
+ while (srcStart < srcEnd && JSON.Util.isSpace(load<u16>(srcStart)))
955
+ srcStart += 2;
929
956
  if (isBoolean<T>()) {
930
957
  // @ts-expect-error: type
931
958
  return deserializeBoolean(srcStart, srcEnd);
932
959
  } else if (isInteger<T>()) {
933
- return isSigned<T>() ? deserializeInteger<T>(srcStart, srcEnd) : deserializeUnsigned<T>(srcStart, srcEnd);
960
+ return isSigned<T>()
961
+ ? deserializeInteger<T>(srcStart, srcEnd)
962
+ : deserializeUnsigned<T>(srcStart, srcEnd);
934
963
  } else if (isFloat<T>()) {
935
964
  return deserializeFloat<T>(srcStart, srcEnd);
936
965
  } else if (isString<T>()) {
937
- if (srcEnd - srcStart < 4) throw new Error("Cannot parse data as string because it was formatted incorrectly!");
966
+ if (srcEnd - srcStart < 4)
967
+ throw new Error(
968
+ "Cannot parse data as string because it was formatted incorrectly!",
969
+ );
938
970
 
939
971
  return deserializeString(srcStart, srcEnd) as T;
940
- } else if (isNullable<T>() && srcEnd - srcStart == 8 && load<u64>(srcStart) == NULL_WORD_U64) {
972
+ } else if (
973
+ isNullable<T>() &&
974
+ srcEnd - srcStart == 8 &&
975
+ load<u64>(srcStart) == NULL_WORD_U64
976
+ ) {
941
977
  return null;
942
978
  } else {
943
979
  let type: nonnull<T> = changetype<nonnull<T>>(0);
@@ -947,12 +983,23 @@ export namespace JSON {
947
983
  // @ts-expect-error: Defined by transform
948
984
  return out.__DESERIALIZE_CUSTOM(ptrToStr(srcStart, srcEnd));
949
985
  // @ts-expect-error: Defined by transform
950
- } else if (isDefined(type.__DESERIALIZE_SLOW) || isDefined(type.__DESERIALIZE_FAST)) {
951
- const out = changetype<nonnull<T>>(dst || __new(offsetof<nonnull<T>>(), idof<nonnull<T>>()));
986
+ } else if (
987
+ isDefined(type.__DESERIALIZE_SLOW) ||
988
+ isDefined(type.__DESERIALIZE_FAST)
989
+ ) {
990
+ const out = changetype<nonnull<T>>(
991
+ dst || __new(offsetof<nonnull<T>>(), idof<nonnull<T>>()),
992
+ );
952
993
  // @ts-expect-error: Defined by transform
953
994
  if (isDefined(type.__DESERIALIZE_FAST)) {
954
995
  // @ts-expect-error: Defined by transform
955
- if (out.__DESERIALIZE_FAST(srcStart, srcEnd, out) == srcEnd) return out;
996
+ const fastEnd = out.__DESERIALIZE_FAST(srcStart, srcEnd, out);
997
+ // Accept the fast path when only trailing whitespace remains.
998
+ if (
999
+ fastEnd != 0 &&
1000
+ JSON.Util.skipWhitespace(fastEnd, srcEnd) == srcEnd
1001
+ )
1002
+ return out;
956
1003
  }
957
1004
  // @ts-expect-error: Defined by transform
958
1005
  if (isDefined(type.__INITIALIZE)) out.__INITIALIZE();
@@ -970,27 +1017,19 @@ export namespace JSON {
970
1017
  } else if (type instanceof Array) {
971
1018
  // @ts-expect-error: type
972
1019
  return deserializeArray<T>(srcStart, srcEnd, dst);
973
- } else if (type instanceof Int8Array) {
974
- return deserializeTypedArray<nonnull<T>>(srcStart, srcEnd, dst) as T;
975
- } else if (type instanceof Uint8Array) {
976
- return deserializeTypedArray<nonnull<T>>(srcStart, srcEnd, dst) as T;
977
- } else if (type instanceof Uint8ClampedArray) {
978
- return deserializeTypedArray<nonnull<T>>(srcStart, srcEnd, dst) as T;
979
- } else if (type instanceof Int16Array) {
980
- return deserializeTypedArray<nonnull<T>>(srcStart, srcEnd, dst) as T;
981
- } else if (type instanceof Uint16Array) {
982
- return deserializeTypedArray<nonnull<T>>(srcStart, srcEnd, dst) as T;
983
- } else if (type instanceof Int32Array) {
984
- return deserializeTypedArray<nonnull<T>>(srcStart, srcEnd, dst) as T;
985
- } else if (type instanceof Uint32Array) {
986
- return deserializeTypedArray<nonnull<T>>(srcStart, srcEnd, dst) as T;
987
- } else if (type instanceof Int64Array) {
988
- return deserializeTypedArray<nonnull<T>>(srcStart, srcEnd, dst) as T;
989
- } else if (type instanceof Uint64Array) {
990
- return deserializeTypedArray<nonnull<T>>(srcStart, srcEnd, dst) as T;
991
- } else if (type instanceof Float32Array) {
992
- return deserializeTypedArray<nonnull<T>>(srcStart, srcEnd, dst) as T;
993
- } else if (type instanceof Float64Array) {
1020
+ } else if (
1021
+ type instanceof Int8Array ||
1022
+ type instanceof Uint8Array ||
1023
+ type instanceof Uint8ClampedArray ||
1024
+ type instanceof Int16Array ||
1025
+ type instanceof Uint16Array ||
1026
+ type instanceof Int32Array ||
1027
+ type instanceof Uint32Array ||
1028
+ type instanceof Int64Array ||
1029
+ type instanceof Uint64Array ||
1030
+ type instanceof Float32Array ||
1031
+ type instanceof Float64Array
1032
+ ) {
994
1033
  return deserializeTypedArray<nonnull<T>>(srcStart, srcEnd, dst) as T;
995
1034
  } else if (type instanceof ArrayBuffer) {
996
1035
  return deserializeArrayBuffer(srcStart, srcEnd, dst) as T;
@@ -1014,19 +1053,42 @@ export namespace JSON {
1014
1053
  return deserializeObject(srcStart, srcEnd, 0);
1015
1054
  } else if (type instanceof JSON.Box) {
1016
1055
  // @ts-expect-error: type
1017
- return new JSON.Box(deserializeBox(srcStart, srcEnd, dst, changetype<nonnull<T>>(0).value));
1056
+ return new JSON.Box(
1057
+ deserializeBox(
1058
+ srcStart,
1059
+ srcEnd,
1060
+ dst,
1061
+ changetype<nonnull<T>>(0).value,
1062
+ ),
1063
+ );
1018
1064
  }
1019
1065
  }
1020
1066
  const snippet = ptrToStr(srcStart, srcEnd);
1021
- throw new Error(`Could not deserialize JSON to type '${nameof<T>()}'. ` + `If this is a custom class, ensure it has the @json decorator: @json class ${nameof<T>()} { ... }. ` + `Input: "${snippet.length > 50 ? snippet.slice(0, 50) + "..." : snippet}"`);
1067
+ throw new Error(
1068
+ `Could not deserialize JSON to type '${nameof<T>()}'. ` +
1069
+ `If this is a custom class, ensure it has the @json decorator: @json class ${nameof<T>()} { ... }. ` +
1070
+ `Input: "${snippet.length > 50 ? snippet.slice(0, 50) + "..." : snippet}"`,
1071
+ );
1022
1072
  }
1023
1073
  export namespace Util {
1024
1074
  // @ts-expect-error: decorator
1025
1075
  @inline export function isSpace(code: u16): boolean {
1026
1076
  return code == 0x20 || code - 9 <= 4;
1027
1077
  }
1078
+ /** Advance past JSON whitespace (space, tab, LF, VT, FF, CR). */
1079
+ // @ts-expect-error: decorator
1080
+ @inline export function skipWhitespace(
1081
+ srcStart: usize,
1082
+ srcEnd: usize,
1083
+ ): usize {
1084
+ while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
1085
+ return srcStart;
1086
+ }
1028
1087
  // @ts-expect-error: decorator
1029
- @inline export function scanValueEnd(srcStart: usize, srcEnd: usize): usize {
1088
+ @inline export function scanValueEnd(
1089
+ srcStart: usize,
1090
+ srcEnd: usize,
1091
+ ): usize {
1030
1092
  if (srcStart >= srcEnd) return 0;
1031
1093
  let ptr = srcStart;
1032
1094
  while (ptr < srcEnd && isSpace(load<u16>(ptr))) ptr += 2;
@@ -1061,7 +1123,13 @@ export namespace JSON {
1061
1123
 
1062
1124
  while (ptr < srcEnd) {
1063
1125
  const code = load<u16>(ptr);
1064
- if (code == COMMA || code == BRACKET_RIGHT || code == BRACE_RIGHT || isSpace(code)) return ptr;
1126
+ if (
1127
+ code == COMMA ||
1128
+ code == BRACKET_RIGHT ||
1129
+ code == BRACE_RIGHT ||
1130
+ isSpace(code)
1131
+ )
1132
+ return ptr;
1065
1133
  ptr += 2;
1066
1134
  }
1067
1135
 
@@ -1079,7 +1147,7 @@ export namespace JSON {
1079
1147
  * Methods for use when using JSON methods inside another JSON method or custom serializer/deserializer
1080
1148
  * Transform will automatically convert JSON.x calls to JSON.internal.x when in a custom (de)serializer
1081
1149
  */
1082
- export namespace internal {
1150
+ namespace internal {
1083
1151
  /**
1084
1152
  * Serializes JSON data. Don't use this directly, use `JSON.stringify` instead.
1085
1153
  * @param data - T
@@ -1087,7 +1155,10 @@ export namespace JSON {
1087
1155
  * @returns - string
1088
1156
  */
1089
1157
  // @ts-expect-error: inline
1090
- @inline export function stringify<T>(data: T, out: string | null = null): string {
1158
+ @inline export function stringify<T>(
1159
+ data: T,
1160
+ out: string | null = null,
1161
+ ): string {
1091
1162
  bs.saveState();
1092
1163
  JSON.__serialize<T>(data);
1093
1164
  const result = bs.cpyOut<string>();
@@ -1116,6 +1187,71 @@ export namespace JSON {
1116
1187
  }
1117
1188
  }
1118
1189
 
1190
+ /**
1191
+ * Shared reference-type serialization chain used by both {@link JSON.stringify}
1192
+ * and {@link JSON.__serialize}. Writes directly to the active buffer. Primitive
1193
+ * and `Date` fast paths are handled by the callers (which have buffer-reuse
1194
+ * optimizations); everything else routes here so the dispatch chain lives once.
1195
+ */
1196
+ // @ts-expect-error: @inline is a valid decorator
1197
+ @inline function serializeReference<T>(data: T): void {
1198
+ if (data instanceof Array) {
1199
+ // @ts-expect-error
1200
+ serializeArray(changetype<nonnull<T>>(data));
1201
+ } else if (data instanceof StaticArray) {
1202
+ // @ts-expect-error
1203
+ serializeStaticArray(changetype<nonnull<T>>(data));
1204
+ } else if (data instanceof Int8Array) {
1205
+ serializeTypedArray<Int8Array>(data);
1206
+ } else if (data instanceof Uint8Array) {
1207
+ serializeTypedArray<Uint8Array>(data);
1208
+ } else if (data instanceof Uint8ClampedArray) {
1209
+ serializeTypedArray<Uint8ClampedArray>(data);
1210
+ } else if (data instanceof Int16Array) {
1211
+ serializeTypedArray<Int16Array>(data);
1212
+ } else if (data instanceof Uint16Array) {
1213
+ serializeTypedArray<Uint16Array>(data);
1214
+ } else if (data instanceof Int32Array) {
1215
+ serializeTypedArray<Int32Array>(data);
1216
+ } else if (data instanceof Uint32Array) {
1217
+ serializeTypedArray<Uint32Array>(data);
1218
+ } else if (data instanceof Int64Array) {
1219
+ serializeTypedArray<Int64Array>(data);
1220
+ } else if (data instanceof Uint64Array) {
1221
+ serializeTypedArray<Uint64Array>(data);
1222
+ } else if (data instanceof Float32Array) {
1223
+ serializeTypedArray<Float32Array>(data);
1224
+ } else if (data instanceof Float64Array) {
1225
+ serializeTypedArray<Float64Array>(data);
1226
+ } else if (data instanceof ArrayBuffer) {
1227
+ const dataStart = changetype<usize>(data);
1228
+ serializeArrayBufferUnsafe(
1229
+ dataStart,
1230
+ changetype<OBJECT>(dataStart - TOTAL_OVERHEAD).rtSize,
1231
+ );
1232
+ } else if (data instanceof Set) {
1233
+ // @ts-expect-error
1234
+ serializeSet(changetype<nonnull<T>>(data));
1235
+ } else if (data instanceof Map) {
1236
+ // @ts-expect-error
1237
+ serializeMap(changetype<nonnull<T>>(data));
1238
+ } else if (data instanceof JSON.Raw) {
1239
+ serializeRaw(data);
1240
+ } else if (data instanceof JSON.Value) {
1241
+ serializeArbitrary(data);
1242
+ } else if (data instanceof JSON.Obj) {
1243
+ serializeObject(data);
1244
+ } else if (data instanceof JSON.Box) {
1245
+ JSON.__serialize(data.value);
1246
+ } else {
1247
+ throw new Error(
1248
+ `Could not serialize data of type '${nameof<T>()}'. ` +
1249
+ `If this is a custom class, add the @json decorator: @json class ${nameof<T>()} { ... }. ` +
1250
+ `Supported types: primitives, string, Array, StaticArray, TypedArray, ArrayBuffer, Map, Date, and @json decorated classes.`,
1251
+ );
1252
+ }
1253
+ }
1254
+
1119
1255
  export enum JSONMode {
1120
1256
  SWAR = 0,
1121
1257
  SIMD = 1,
@@ -1127,7 +1263,12 @@ export enum JSONMode {
1127
1263
  return JSON.parse<T>(data);
1128
1264
  }
1129
1265
  // @ts-expect-error: inline
1130
- @inline function deserializeBox<T>(srcStart: usize, srcEnd: usize, dst: usize, ty: T): T {
1266
+ @inline function deserializeBox<T>(
1267
+ srcStart: usize,
1268
+ srcEnd: usize,
1269
+ dst: usize,
1270
+ ty: T,
1271
+ ): T {
1131
1272
  return JSON.__deserialize<T>(srcStart, srcEnd, dst);
1132
1273
  }
1133
1274