json-as 1.3.7 → 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 (116) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/README.md +1 -1
  3. package/assembly/deserialize/index/arbitrary.ts +2 -2
  4. package/assembly/deserialize/index/array.ts +29 -14
  5. package/assembly/deserialize/index/bool.ts +1 -1
  6. package/assembly/deserialize/index/date.ts +1 -1
  7. package/assembly/deserialize/index/float.ts +40 -1
  8. package/assembly/deserialize/index/integer.ts +3 -3
  9. package/assembly/deserialize/index/map.ts +1 -1
  10. package/assembly/deserialize/index/object.ts +1 -1
  11. package/assembly/deserialize/index/raw.ts +1 -1
  12. package/assembly/deserialize/index/set.ts +1 -1
  13. package/assembly/deserialize/index/staticarray.ts +4 -1
  14. package/assembly/deserialize/index/string.ts +28 -3
  15. package/assembly/deserialize/index/struct.ts +1 -1
  16. package/assembly/deserialize/index/typedarray.ts +25 -15
  17. package/assembly/deserialize/index/unsigned.ts +3 -3
  18. package/assembly/deserialize/index.ts +1 -0
  19. package/assembly/deserialize/naive/array/bool.ts +68 -0
  20. package/assembly/deserialize/naive/array/float.ts +63 -0
  21. package/assembly/deserialize/{simple → naive}/array/generic.ts +1 -2
  22. package/assembly/deserialize/naive/array/integer.ts +86 -0
  23. package/assembly/deserialize/{simple → naive}/array/map.ts +0 -1
  24. package/assembly/deserialize/{simple → naive}/array/object.ts +0 -1
  25. package/assembly/deserialize/naive/array/string.ts +69 -0
  26. package/assembly/deserialize/{simple → naive}/array/struct.ts +0 -1
  27. package/assembly/deserialize/{simple → naive}/array.ts +6 -11
  28. package/assembly/deserialize/naive/float.ts +135 -0
  29. package/assembly/deserialize/{simple → naive}/integer.ts +2 -2
  30. package/assembly/deserialize/{simple → naive}/map.ts +12 -6
  31. package/assembly/deserialize/{simple → naive}/object.ts +4 -7
  32. package/assembly/deserialize/{simple → naive}/set.ts +12 -27
  33. package/assembly/deserialize/{simple → naive}/staticarray/array.ts +1 -1
  34. package/assembly/deserialize/{simple → naive}/staticarray/bool.ts +1 -1
  35. package/assembly/deserialize/{simple → naive}/staticarray/float.ts +1 -1
  36. package/assembly/deserialize/{simple → naive}/staticarray/integer.ts +1 -1
  37. package/assembly/deserialize/{simple → naive}/staticarray/struct.ts +1 -2
  38. package/assembly/deserialize/{simple → naive}/staticarray.ts +4 -4
  39. package/assembly/deserialize/naive/string.ts +199 -0
  40. package/assembly/deserialize/{simple → naive}/typedarray.ts +4 -4
  41. package/assembly/deserialize/{simple → naive}/unsigned.ts +2 -2
  42. package/assembly/deserialize/simd/array/integer.ts +19 -19
  43. package/assembly/deserialize/simd/float.ts +303 -0
  44. package/assembly/deserialize/simd/string.ts +233 -108
  45. package/assembly/deserialize/swar/array/arbitrary.ts +6 -2
  46. package/assembly/deserialize/swar/array/array.ts +14 -7
  47. package/assembly/deserialize/swar/array/bool.ts +8 -3
  48. package/assembly/deserialize/swar/array/box.ts +6 -2
  49. package/assembly/deserialize/swar/array/float.ts +282 -6
  50. package/assembly/deserialize/swar/array/generic.ts +6 -2
  51. package/assembly/deserialize/swar/array/integer.ts +81 -74
  52. package/assembly/deserialize/swar/array/map.ts +6 -2
  53. package/assembly/deserialize/swar/array/object.ts +24 -32
  54. package/assembly/deserialize/swar/array/raw.ts +6 -2
  55. package/assembly/deserialize/swar/array/shared.ts +32 -8
  56. package/assembly/deserialize/swar/array/string.ts +127 -10
  57. package/assembly/deserialize/swar/array/struct.ts +45 -11
  58. package/assembly/deserialize/swar/array.ts +2 -56
  59. package/assembly/deserialize/swar/float.ts +304 -0
  60. package/assembly/deserialize/swar/string.ts +119 -104
  61. package/assembly/deserialize/swar/typedarray.ts +224 -0
  62. package/assembly/index.ts +203 -293
  63. package/assembly/serialize/index/array.ts +1 -1
  64. package/assembly/serialize/index/bool.ts +1 -1
  65. package/assembly/serialize/index/date.ts +1 -1
  66. package/assembly/serialize/index/float.ts +1 -1
  67. package/assembly/serialize/index/integer.ts +1 -1
  68. package/assembly/serialize/index/map.ts +1 -1
  69. package/assembly/serialize/index/raw.ts +1 -1
  70. package/assembly/serialize/index/set.ts +1 -1
  71. package/assembly/serialize/index/staticarray.ts +1 -1
  72. package/assembly/serialize/index/string.ts +1 -1
  73. package/assembly/serialize/index/struct.ts +1 -1
  74. package/assembly/serialize/index/typedarray.ts +2 -11
  75. package/assembly/serialize/index.ts +1 -0
  76. package/assembly/serialize/{simple → naive}/array.ts +87 -0
  77. package/assembly/serialize/{simple → naive}/string.ts +1 -1
  78. package/assembly/serialize/swar/string.ts +0 -139
  79. package/assembly/util/dragonbox.ts +10 -3
  80. package/assembly/util/itoa-fast.ts +29 -18
  81. package/assembly/util/scanValueEnd.ts +78 -0
  82. package/assembly/util/scientific.ts +132 -0
  83. package/lib/as-bs.ts +14 -1
  84. package/package.json +14 -13
  85. package/transform/lib/index.d.ts +4 -0
  86. package/transform/lib/index.d.ts.map +1 -1
  87. package/transform/lib/index.js +153 -236
  88. package/transform/lib/index.js.map +1 -1
  89. package/assembly/deserialize/simple/arbitrary.ts +0 -30
  90. package/assembly/deserialize/simple/array/bool.ts +0 -48
  91. package/assembly/deserialize/simple/array/float.ts +0 -55
  92. package/assembly/deserialize/simple/array/integer.ts +0 -33
  93. package/assembly/deserialize/simple/array/string.ts +0 -29
  94. package/assembly/deserialize/simple/float.ts +0 -206
  95. package/assembly/deserialize/simple/string.ts +0 -45
  96. package/assembly/serialize/simple/arbitrary.ts +0 -79
  97. package/assembly/serialize/simple/object.ts +0 -42
  98. /package/assembly/deserialize/{simple → naive}/array/arbitrary.ts +0 -0
  99. /package/assembly/deserialize/{simple → naive}/array/array.ts +0 -0
  100. /package/assembly/deserialize/{simple → naive}/array/box.ts +0 -0
  101. /package/assembly/deserialize/{simple → naive}/array/raw.ts +0 -0
  102. /package/assembly/deserialize/{simple → naive}/bool.ts +0 -0
  103. /package/assembly/deserialize/{simple → naive}/date.ts +0 -0
  104. /package/assembly/deserialize/{simple → naive}/raw.ts +0 -0
  105. /package/assembly/deserialize/{simple → naive}/staticarray/string.ts +0 -0
  106. /package/assembly/deserialize/{simple → naive}/struct.ts +0 -0
  107. /package/assembly/serialize/{simple → naive}/bool.ts +0 -0
  108. /package/assembly/serialize/{simple → naive}/date.ts +0 -0
  109. /package/assembly/serialize/{simple → naive}/float.ts +0 -0
  110. /package/assembly/serialize/{simple → naive}/integer.ts +0 -0
  111. /package/assembly/serialize/{simple → naive}/map.ts +0 -0
  112. /package/assembly/serialize/{simple → naive}/raw.ts +0 -0
  113. /package/assembly/serialize/{simple → naive}/set.ts +0 -0
  114. /package/assembly/serialize/{simple → naive}/staticarray.ts +0 -0
  115. /package/assembly/serialize/{simple → naive}/struct.ts +0 -0
  116. /package/assembly/serialize/{simple → naive}/typedarray.ts +0 -0
@@ -1,9 +1,271 @@
1
- import { deserializeFloatField } from "../../simple/float";
1
+ import { ptrToStr } from "../../../util/ptrToStr";
2
+ import { scientific } from "../../../util/scientific";
3
+ import { deserializeFloatField_NAIVE } from "../../naive/float";
4
+ import { deserializeFloatArray_NAIVE } from "../../naive/array/float";
2
5
  import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
3
6
  import { ensureArrayElementSlot, ensureArrayField } from "./shared";
7
+ import { parse4Digits_PairMul } from "../../../util/swar-int";
8
+ import { loadPow10, MAX_EXACT_MANTISSA, MAX_EXACT_POW10 } from "../float";
9
+ import { isSpace } from "../../../util";
4
10
 
5
11
 
6
- @inline export function deserializeFloatArrayInto<T extends number[]>(
12
+ @inline function skipFloatArrayWhitespace(
13
+ srcStart: usize,
14
+ srcEnd: usize,
15
+ ): usize {
16
+ while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
17
+ return srcStart;
18
+ }
19
+
20
+ // @ts-ignore: inline
21
+ @inline function fallbackStore<E>(
22
+ origStart: usize,
23
+ end: usize,
24
+ slot: usize,
25
+ ): void {
26
+ const s = ptrToStr(origStart, end);
27
+ if (sizeof<E>() == sizeof<f32>()) {
28
+ store<f32>(slot, f32.parse(s));
29
+ } else {
30
+ store<f64>(slot, f64.parse(s));
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Inline single-pass Lemire-style float element parser for arrays.
36
+ *
37
+ * Bit-identical output to `f64.parse` / `f32.parse`: u64 mantissa
38
+ * accumulation is exact, and `<f64>mantissa * pow10[exp]` is correctly
39
+ * rounded for the fast-path range (mantissa <= 2^53, |exp| <= 22).
40
+ * Pathological inputs (oversized mantissa or exponent) fall back to
41
+ * `f*.parse` over the float's own substring, again matching the NAIVE
42
+ * baseline.
43
+ *
44
+ * Returns the advanced source position on success, or `0` to signal "bail
45
+ * to the per-element NAIVE path" only for truly malformed input (no leading
46
+ * digit, lone minus, malformed exponent suffix). Valid-but-out-of-range
47
+ * numbers are handled inline.
48
+ */
49
+ // @ts-expect-error: decorators valid here
50
+ @inline export function parseFloatElementSWAR<E>(
51
+ srcStart: usize,
52
+ srcEnd: usize,
53
+ slot: usize,
54
+ ): usize {
55
+ const origStart = srcStart;
56
+ let p = srcStart;
57
+ let negative = false;
58
+ let code = load<u16>(p);
59
+ if (code == 45) {
60
+ negative = true;
61
+ p += 2;
62
+ if (p >= srcEnd) return 0;
63
+ code = load<u16>(p);
64
+ }
65
+
66
+ let firstDigit = <u32>code - 48;
67
+ if (firstDigit > 9) return 0;
68
+
69
+ // Integer mantissa: scalar (most JSON integers are 1-3 digits).
70
+ let mantissa: u64 = 0;
71
+ let intDigits: i32 = 0;
72
+ while (p < srcEnd) {
73
+ const d = <u32>load<u16>(p) - 48;
74
+ if (d > 9) break;
75
+ mantissa = mantissa * 10 + <u64>d;
76
+ intDigits++;
77
+ p += 2;
78
+ }
79
+
80
+ // Fractional mantissa: parse4 SWAR stride + scalar tail. Same u64
81
+ // accumulator as the integer part — exponent compensates for fracDigits.
82
+ let fracDigits: i32 = 0;
83
+ if (p < srcEnd && load<u16>(p) == 46) {
84
+ p += 2;
85
+ while (p + 6 < srcEnd) {
86
+ const parsed = parse4Digits_PairMul(load<u64>(p));
87
+ if (parsed == U32.MAX_VALUE) break;
88
+ mantissa = mantissa * 10_000 + <u64>parsed;
89
+ fracDigits += 4;
90
+ p += 8;
91
+ }
92
+ while (p < srcEnd) {
93
+ const d = <u32>load<u16>(p) - 48;
94
+ if (d > 9) break;
95
+ mantissa = mantissa * 10 + <u64>d;
96
+ fracDigits++;
97
+ p += 2;
98
+ }
99
+ }
100
+
101
+ const mantDigits = intDigits + fracDigits;
102
+ let exponent: i32 = -fracDigits;
103
+
104
+ // Optional `e[+-]NNN` suffix.
105
+ if (p < srcEnd) {
106
+ code = load<u16>(p);
107
+ if (code == 101 || code == 69) {
108
+ const expStart = p;
109
+ p += 2;
110
+ if (p >= srcEnd) {
111
+ fallbackStore<E>(origStart, expStart, slot);
112
+ return expStart;
113
+ }
114
+ let expNeg = false;
115
+ const sc = load<u16>(p);
116
+ if (sc == 45) {
117
+ expNeg = true;
118
+ p += 2;
119
+ } else if (sc == 43) {
120
+ p += 2;
121
+ }
122
+ if (p >= srcEnd) {
123
+ fallbackStore<E>(origStart, expStart, slot);
124
+ return expStart;
125
+ }
126
+ let exp: i32 = 0;
127
+ let expDigits: i32 = 0;
128
+ while (p < srcEnd) {
129
+ const d = <u32>load<u16>(p) - 48;
130
+ if (d > 9) break;
131
+ exp = exp * 10 + <i32>d;
132
+ expDigits++;
133
+ if (expDigits > 4) {
134
+ fallbackStore<E>(origStart, p, slot);
135
+ return p;
136
+ }
137
+ p += 2;
138
+ }
139
+ if (expDigits == 0) {
140
+ fallbackStore<E>(origStart, expStart, slot);
141
+ return expStart;
142
+ }
143
+ exponent += expNeg ? -exp : exp;
144
+ }
145
+ }
146
+
147
+ // Lemire fast path: mantissa <= 2^53 and |exp| <= 22 means a single fmul
148
+ // on two exactly-representable operands, correctly rounded.
149
+ let result: f64;
150
+ if (
151
+ mantDigits <= 19 &&
152
+ mantissa <= MAX_EXACT_MANTISSA &&
153
+ exponent <= MAX_EXACT_POW10 &&
154
+ exponent >= -MAX_EXACT_POW10
155
+ ) {
156
+ result = <f64>mantissa;
157
+ if (exponent > 0) {
158
+ result *= loadPow10(<u32>exponent);
159
+ } else if (exponent < 0) {
160
+ result /= loadPow10(<u32>-exponent);
161
+ }
162
+ } else if (mantDigits <= 19) {
163
+ // Mantissa fits in u64 but the fast-path constraints don't hold. Call
164
+ // `scientific` directly with our already-parsed mantissa+exp, skipping
165
+ // the `ptrToStr` allocation + strtod re-parse.
166
+ result = scientific(mantissa, exponent);
167
+ } else {
168
+ // >19 mantissa digits — beyond u64 capacity, may need strtod's sticky-bit
169
+ // pattern. Hand off to f*.parse on the float's substring.
170
+ fallbackStore<E>(origStart, p, slot);
171
+ return p;
172
+ }
173
+ if (negative) result = -result;
174
+
175
+ if (sizeof<E>() == sizeof<f32>()) {
176
+ store<f32>(slot, <f32>result);
177
+ } else {
178
+ store<f64>(slot, result);
179
+ }
180
+ return p;
181
+ }
182
+
183
+ /**
184
+ * Top-level SWAR float-array deserializer (`f64[]` / `f32[]`).
185
+ *
186
+ * Worst-case sizing matches the NAIVE variant: each element occupies >=
187
+ * 2 UTF-16 chars (`D,`), so `(srcEnd - srcStart) >> 2` upper-bounds the
188
+ * element count. `f64`/`f32` are unmanaged so AS skips zero-fill on
189
+ * `length=`, making the over-allocation effectively free; the trailing
190
+ * `out.length` trim recovers the true count.
191
+ *
192
+ * The fast loop writes directly through `writePtr` (eliminating the
193
+ * per-element `Array.push` capacity check + length write) and inlines
194
+ * `parseFloatElementSWAR` (eliminating the double-scan that
195
+ * `deserializeFloatArray_NAIVE` performs: scan-to-terminator +
196
+ * `JSON.__deserialize` re-parse). If the inline parser bails (truly
197
+ * malformed input), we hand off to the NAIVE path with the pre-allocated
198
+ * buffer retained so capacity is reused.
199
+ */
200
+ export function deserializeFloatArray_SWAR<T extends number[]>(
201
+ srcStart: usize,
202
+ srcEnd: usize,
203
+ dst: usize,
204
+ ): T {
205
+ const out = changetype<nonnull<T>>(
206
+ dst || changetype<usize>(instantiate<T>()),
207
+ );
208
+ const originalSrcStart = srcStart;
209
+
210
+ const elementSize = sizeof<valueof<T>>();
211
+ const maxElements = i32((<usize>(srcEnd - srcStart)) >> 2);
212
+ if (maxElements > 0) out.length = maxElements;
213
+ const dataStart = out.dataStart;
214
+ let writePtr = dataStart;
215
+
216
+ do {
217
+ if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
218
+ srcStart += 2;
219
+ srcStart = skipFloatArrayWhitespace(srcStart, srcEnd);
220
+ if (srcStart >= srcEnd) break;
221
+ if (load<u16>(srcStart) == BRACKET_RIGHT) {
222
+ out.length = 0;
223
+ return out;
224
+ }
225
+
226
+ while (srcStart < srcEnd) {
227
+ const next = parseFloatElementSWAR<valueof<T>>(
228
+ srcStart,
229
+ srcEnd,
230
+ writePtr,
231
+ );
232
+ if (!next) break;
233
+ writePtr += elementSize;
234
+ srcStart = skipFloatArrayWhitespace(next, srcEnd);
235
+ if (srcStart >= srcEnd) break;
236
+
237
+ const code = load<u16>(srcStart);
238
+ if (code == COMMA) {
239
+ srcStart += 2;
240
+ srcStart = skipFloatArrayWhitespace(srcStart, srcEnd);
241
+ continue;
242
+ }
243
+ if (code == BRACKET_RIGHT) {
244
+ out.length = i32(<usize>(writePtr - dataStart) / elementSize);
245
+ return out;
246
+ }
247
+ break;
248
+ }
249
+ } while (false);
250
+
251
+ return deserializeFloatArray_NAIVE<T>(
252
+ originalSrcStart,
253
+ srcEnd,
254
+ changetype<usize>(out),
255
+ );
256
+ }
257
+
258
+ /**
259
+ * Field/into variant — parses `[..]` into the existing `out` array and
260
+ * returns the cursor past the closing `]`.
261
+ *
262
+ * Worst-case pre-sizing (`(srcEnd - srcStart) >> 2`) used by the top-level
263
+ * SWAR entry is unsafe here: nested callers pass the *outer* container's
264
+ * `srcEnd`, so on `f64[][][]` payloads like canada.json each tiny inner
265
+ * `[lon,lat]` would over-allocate megabytes of f64 capacity. Instead we
266
+ * use `ensureArrayElementSlot`'s grow-or-reuse strategy.
267
+ */
268
+ @inline export function deserializeFloatArrayBody<T extends number[]>(
7
269
  srcStart: usize,
8
270
  srcEnd: usize,
9
271
  out: T,
@@ -13,6 +275,7 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
13
275
  do {
14
276
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
15
277
  srcStart += 2;
278
+ srcStart = skipFloatArrayWhitespace(srcStart, srcEnd);
16
279
  if (srcStart >= srcEnd) break;
17
280
  if (load<u16>(srcStart) == BRACKET_RIGHT) {
18
281
  out.length = 0;
@@ -21,17 +284,30 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
21
284
 
22
285
  while (srcStart < srcEnd) {
23
286
  const slot = ensureArrayElementSlot<T>(out, index);
24
- srcStart = deserializeFloatField<valueof<T>>(srcStart, srcEnd, slot);
25
- if (!srcStart || srcStart >= srcEnd) break;
287
+ let next = parseFloatElementSWAR<valueof<T>>(srcStart, srcEnd, slot);
288
+ if (!next) {
289
+ next = deserializeFloatField_NAIVE<valueof<T>>(srcStart, srcEnd, slot);
290
+ }
291
+ srcStart = next;
292
+ if (!srcStart) break;
293
+ srcStart = skipFloatArrayWhitespace(srcStart, srcEnd);
294
+ if (srcStart >= srcEnd) break;
26
295
 
27
296
  const code = load<u16>(srcStart);
28
297
  if (code == COMMA) {
29
298
  srcStart += 2;
299
+ srcStart = skipFloatArrayWhitespace(srcStart, srcEnd);
30
300
  index++;
31
301
  continue;
32
302
  }
33
303
  if (code == BRACKET_RIGHT) {
34
- out.length = index + 1;
304
+ // Skip the `out.length =` call when the length is already correct.
305
+ // `ensureArrayElementSlot` only grows (never shrinks), so when this
306
+ // array is being reused with the same shape (canada's `[lon,lat]`
307
+ // pairs across thousands of geometries) the length is unchanged
308
+ // and the AS runtime's `ensureCapacity` call is pure overhead.
309
+ const nextLen = index + 1;
310
+ if (out.length != nextLen) out.length = nextLen;
35
311
  return srcStart + 2;
36
312
  }
37
313
  break;
@@ -47,7 +323,7 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
47
323
  srcEnd: usize,
48
324
  fieldPtr: usize,
49
325
  ): usize {
50
- return deserializeFloatArrayInto<T>(
326
+ return deserializeFloatArrayBody<T>(
51
327
  srcStart,
52
328
  srcEnd,
53
329
  ensureArrayField<T>(fieldPtr),
@@ -4,10 +4,11 @@ import {
4
4
  ensureArrayElementSlot,
5
5
  ensureArrayField,
6
6
  scanValueEnd,
7
+ skipWhitespace,
7
8
  } from "./shared";
8
9
 
9
10
 
10
- @inline export function deserializeGenericArrayInto<T extends unknown[]>(
11
+ @inline export function deserializeGenericArrayBody<T extends unknown[]>(
11
12
  srcStart: usize,
12
13
  srcEnd: usize,
13
14
  out: T,
@@ -15,6 +16,7 @@ import {
15
16
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT)
16
17
  throw new Error("Failed to parse JSON!");
17
18
  srcStart += 2;
19
+ srcStart = skipWhitespace(srcStart, srcEnd);
18
20
  if (srcStart >= srcEnd) throw new Error("Failed to parse JSON!");
19
21
  if (load<u16>(srcStart) == BRACKET_RIGHT) return srcStart + 2;
20
22
 
@@ -28,10 +30,12 @@ import {
28
30
  store<valueof<T>>(slot, JSON.__deserialize<valueof<T>>(srcStart, valueEnd));
29
31
  srcStart = valueEnd;
30
32
 
33
+ srcStart = skipWhitespace(srcStart, srcEnd);
31
34
  if (srcStart >= srcEnd) break;
32
35
  const code = load<u16>(srcStart);
33
36
  if (code == COMMA) {
34
37
  srcStart += 2;
38
+ srcStart = skipWhitespace(srcStart, srcEnd);
35
39
  continue;
36
40
  }
37
41
  if (code == BRACKET_RIGHT) {
@@ -50,7 +54,7 @@ import {
50
54
  srcEnd: usize,
51
55
  fieldPtr: usize,
52
56
  ): usize {
53
- return deserializeGenericArrayInto<T>(
57
+ return deserializeGenericArrayBody<T>(
54
58
  srcStart,
55
59
  srcEnd,
56
60
  ensureArrayField<T>(fieldPtr),