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
@@ -1,7 +1,15 @@
1
+ import { deserializeGenericArrayBody } from "./generic";
1
2
  import { ensureArrayField } from "./shared";
2
3
 
3
4
 
4
- @inline export function deserializeMapArrayField<T extends Map<any, any>[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
5
- ensureArrayField<T>(fieldPtr);
6
- throw new Error("Failed to parse JSON!");
5
+ @inline export function deserializeMapArrayField<T extends Map<any, any>[]>(
6
+ srcStart: usize,
7
+ srcEnd: usize,
8
+ fieldPtr: usize,
9
+ ): usize {
10
+ return deserializeGenericArrayBody<T>(
11
+ srcStart,
12
+ srcEnd,
13
+ ensureArrayField<T>(fieldPtr),
14
+ );
7
15
  }
@@ -1,13 +1,24 @@
1
+ import { JSON } from "../../..";
1
2
  import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
2
- import { ensureArrayElementSlot, ensureArrayField } from "./shared";
3
+ import {
4
+ ensureArrayElementSlot,
5
+ ensureArrayField,
6
+ scanValueEnd,
7
+ skipWhitespace,
8
+ } from "./shared";
3
9
 
4
10
 
5
- @inline export function deserializeObjectArrayInto<T extends unknown[]>(srcStart: usize, srcEnd: usize, out: T): usize {
11
+ @inline function deserializeObjectArrayBody<T extends unknown[]>(
12
+ srcStart: usize,
13
+ srcEnd: usize,
14
+ out: T,
15
+ ): usize {
6
16
  let index = 0;
7
17
 
8
18
  do {
9
19
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
10
20
  srcStart += 2;
21
+ srcStart = skipWhitespace(srcStart, srcEnd);
11
22
  if (srcStart >= srcEnd) break;
12
23
  if (load<u16>(srcStart) == BRACKET_RIGHT) {
13
24
  out.length = 0;
@@ -16,36 +27,29 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
16
27
 
17
28
  while (srcStart < srcEnd) {
18
29
  const slot = ensureArrayElementSlot<T>(out, index);
19
- let value = load<valueof<T>>(slot);
20
- if (changetype<usize>(value) == 0) {
21
- value = changetype<valueof<T>>(__new(offsetof<nonnull<valueof<T>>>(), idof<nonnull<valueof<T>>>()));
22
- // @ts-ignore: supplied by transform
23
- if (isDefined(changetype<nonnull<valueof<T>>>(value).__INITIALIZE)) {
24
- // @ts-ignore: supplied by transform
25
- changetype<nonnull<valueof<T>>>(value).__INITIALIZE();
26
- }
27
- store<valueof<T>>(slot, value);
28
- }
29
-
30
30
  const valueStart = srcStart;
31
- // @ts-ignore: supplied by transform
32
- if (isDefined(changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST)) {
33
- // @ts-ignore: supplied by transform
34
- srcStart = changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST<valueof<T>>(valueStart, srcEnd, value);
35
- } else {
36
- // @ts-ignore: supplied by transform
37
- srcStart = changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_SLOW<valueof<T>>(valueStart, srcEnd, value);
38
- }
39
- if (!srcStart || srcStart >= srcEnd) break;
31
+ const valueEnd = scanValueEnd(valueStart, srcEnd);
32
+ if (!valueEnd) break;
33
+
34
+ const value = JSON.__deserialize<valueof<T>>(
35
+ valueStart,
36
+ valueEnd,
37
+ changetype<usize>(load<valueof<T>>(slot)),
38
+ );
39
+ store<valueof<T>>(slot, value);
40
+ srcStart = valueEnd;
40
41
 
42
+ srcStart = skipWhitespace(srcStart, srcEnd);
41
43
  const code = load<u16>(srcStart);
42
44
  if (code == COMMA) {
43
45
  srcStart += 2;
46
+ srcStart = skipWhitespace(srcStart, srcEnd);
44
47
  index++;
45
48
  continue;
46
49
  }
47
50
  if (code == BRACKET_RIGHT) {
48
- out.length = index + 1;
51
+ const nextLen = index + 1;
52
+ if (out.length != nextLen) out.length = nextLen;
49
53
  return srcStart + 2;
50
54
  }
51
55
  break;
@@ -56,6 +60,14 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
56
60
  }
57
61
 
58
62
 
59
- @inline export function deserializeObjectArrayField<T extends unknown[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
60
- return deserializeObjectArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
63
+ @inline export function deserializeObjectArrayField<T extends unknown[]>(
64
+ srcStart: usize,
65
+ srcEnd: usize,
66
+ fieldPtr: usize,
67
+ ): usize {
68
+ return deserializeObjectArrayBody<T>(
69
+ srcStart,
70
+ srcEnd,
71
+ ensureArrayField<T>(fieldPtr),
72
+ );
61
73
  }
@@ -1,8 +1,16 @@
1
1
  import { JSON } from "../../..";
2
+ import { deserializeGenericArrayBody } from "./generic";
2
3
  import { ensureArrayField } from "./shared";
3
4
 
4
5
 
5
- @inline export function deserializeRawArrayField(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
6
- ensureArrayField<JSON.Raw[]>(fieldPtr);
7
- throw new Error("Failed to parse JSON!");
6
+ @inline export function deserializeRawArrayField(
7
+ srcStart: usize,
8
+ srcEnd: usize,
9
+ fieldPtr: usize,
10
+ ): usize {
11
+ return deserializeGenericArrayBody<JSON.Raw[]>(
12
+ srcStart,
13
+ srcEnd,
14
+ ensureArrayField<JSON.Raw[]>(fieldPtr),
15
+ );
8
16
  }
@@ -1,7 +1,25 @@
1
- import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA, QUOTE } from "../../../custom/chars";
1
+ import {
2
+ BACK_SLASH,
3
+ BRACE_LEFT,
4
+ BRACE_RIGHT,
5
+ BRACKET_LEFT,
6
+ BRACKET_RIGHT,
7
+ COMMA,
8
+ QUOTE,
9
+ } from "../../../custom/chars";
10
+ import { isSpace } from "../../../util";
11
+
12
+ /** Advance past JSON whitespace (space, tab, LF, CR). */
13
+ // @ts-expect-error: @inline is a valid decorator
14
+ @inline export function skipWhitespace(srcStart: usize, srcEnd: usize): usize {
15
+ while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
16
+ return srcStart;
17
+ }
2
18
 
3
19
 
4
- @inline export function ensureArrayField<T extends Array<any>>(fieldPtr: usize): T {
20
+ @inline export function ensureArrayField<T extends Array<any>>(
21
+ fieldPtr: usize,
22
+ ): T {
5
23
  let out = load<T>(fieldPtr);
6
24
  if (!changetype<usize>(out)) {
7
25
  out = changetype<T>(instantiate<T>());
@@ -11,7 +29,10 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
11
29
  }
12
30
 
13
31
 
14
- @inline export function ensureArrayFieldAt<T extends Array<any>>(dstObj: usize, dstOffset: usize): T {
32
+ @inline export function ensureArrayFieldAt<T extends Array<any>>(
33
+ dstObj: usize,
34
+ dstOffset: usize,
35
+ ): T {
15
36
  let out = load<T>(dstObj, dstOffset);
16
37
  if (!changetype<usize>(out)) {
17
38
  out = changetype<T>(instantiate<T>());
@@ -24,25 +45,43 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
24
45
  @inline function backslashOrQuoteMask(block: u64): u64 {
25
46
  const b = block ^ 0x005c_005c_005c_005c;
26
47
  const q = block ^ 0x0022_0022_0022_0022;
27
- return (((q - 0x0001_0001_0001_0001) & ~q) | ((b - 0x0001_0001_0001_0001) & ~b)) & 0x0080_0080_0080_0080;
48
+ return (
49
+ (((q - 0x0001_0001_0001_0001) & ~q) | ((b - 0x0001_0001_0001_0001) & ~b)) &
50
+ 0x0080_0080_0080_0080
51
+ );
28
52
  }
29
53
 
30
54
 
31
- @inline export function ensureArrayElementSlot<T extends Array<any>>(out: T, index: i32): usize {
55
+ @inline export function ensureArrayElementSlot<T extends Array<any>>(
56
+ out: T,
57
+ index: i32,
58
+ ): usize {
32
59
  const nextLength = index + 1;
33
60
  if (out.length < nextLength) {
34
- out.length = nextLength;
35
- const slot = out.dataStart + <usize>index * sizeof<valueof<T>>();
36
- // Reference arrays can allocate recursively before the caller stores the new element.
37
- // Zero the newly exposed slot immediately so incremental GC never observes a garbage pointer.
38
- if (isManaged<valueof<T>>() || isReference<valueof<T>>()) store<usize>(slot, 0);
39
- return slot;
61
+ // Grow via `push`, not `out.length = nextLength`. AS's `length=`
62
+ // setter calls `ensureCapacity(canGrow=false)` which reallocates to
63
+ // *exactly* the requested size fine for a one-shot resize, but
64
+ // catastrophic in the per-element loop (every push triggers a full
65
+ // copy of the array, giving O() growth cost). `push` goes through
66
+ // `canGrow=true`, doubling capacity geometrically as needed.
67
+ //
68
+ // We push a zero-bit default: `0` for primitives, the null reference
69
+ // for managed/reference element types. The caller overwrites this
70
+ // slot immediately, so the placeholder is never observed.
71
+ if (isManaged<valueof<T>>() || isReference<valueof<T>>()) {
72
+ out.push(changetype<valueof<T>>(0));
73
+ } else {
74
+ out.push(<valueof<T>>0);
75
+ }
40
76
  }
41
77
  return out.dataStart + <usize>index * sizeof<valueof<T>>();
42
78
  }
43
79
 
44
80
 
45
- @inline export function scanQuotedValueEnd_SWAR(srcStart: usize, srcEnd: usize): usize {
81
+ @inline export function scanQuotedValueEnd_SWAR(
82
+ srcStart: usize,
83
+ srcEnd: usize,
84
+ ): usize {
46
85
  srcStart += 2;
47
86
  const srcEnd8 = srcEnd >= 8 ? srcEnd - 8 : 0;
48
87
 
@@ -67,7 +106,8 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
67
106
 
68
107
  while (srcStart < srcEnd) {
69
108
  const char = load<u16>(srcStart);
70
- if (char == QUOTE && load<u16>(srcStart - 2) != BACK_SLASH) return srcStart + 2;
109
+ if (char == QUOTE && load<u16>(srcStart - 2) != BACK_SLASH)
110
+ return srcStart + 2;
71
111
  srcStart += 2;
72
112
  }
73
113
 
@@ -103,7 +143,16 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
103
143
 
104
144
  while (srcStart < srcEnd) {
105
145
  const code = load<u16>(srcStart);
106
- if (code == COMMA || code == BRACKET_RIGHT || code == BRACE_RIGHT) return srcStart;
146
+ // Stop at the structural terminator OR trailing whitespace, so the returned
147
+ // range is the exact value (scalar parsers assume no trailing whitespace).
148
+ // Callers skip whitespace to reach the following `,`/`]`/`}`.
149
+ if (
150
+ code == COMMA ||
151
+ code == BRACKET_RIGHT ||
152
+ code == BRACE_RIGHT ||
153
+ isSpace(code)
154
+ )
155
+ return srcStart;
107
156
  srcStart += 2;
108
157
  }
109
158
 
@@ -1,14 +1,34 @@
1
- import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
1
+ import {
2
+ BRACKET_LEFT,
3
+ BRACKET_RIGHT,
4
+ COMMA,
5
+ NULL_WORD_U64,
6
+ } from "../../../custom/chars";
7
+ import { isSpace } from "../../../util";
2
8
  import { ensureArrayElementSlot, ensureArrayField } from "./shared";
3
9
  import { deserializeStringField_SWAR } from "../string";
4
10
 
5
11
 
6
- @inline export function deserializeStringArrayInto<T extends string[]>(srcStart: usize, srcEnd: usize, out: T): usize {
12
+ @inline function skipStringArrayWhitespace(
13
+ srcStart: usize,
14
+ srcEnd: usize,
15
+ ): usize {
16
+ while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
17
+ return srcStart;
18
+ }
19
+
20
+
21
+ @inline function deserializeStringArrayBody<T extends string[]>(
22
+ srcStart: usize,
23
+ srcEnd: usize,
24
+ out: T,
25
+ ): usize {
7
26
  let index = 0;
8
27
 
9
28
  do {
10
29
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
11
30
  srcStart += 2;
31
+ srcStart = skipStringArrayWhitespace(srcStart, srcEnd);
12
32
  if (srcStart >= srcEnd) break;
13
33
  if (load<u16>(srcStart) == BRACKET_RIGHT) {
14
34
  out.length = 0;
@@ -17,17 +37,41 @@ import { deserializeStringField_SWAR } from "../string";
17
37
 
18
38
  while (srcStart < srcEnd) {
19
39
  const slot = ensureArrayElementSlot<T>(out, index);
20
- srcStart = deserializeStringField_SWAR<valueof<T>>(srcStart, srcEnd, slot);
21
- if (!srcStart || srcStart >= srcEnd) break;
40
+ // Null fast path for `(string | null)[]`. "null" is 4 UTF-16 chars
41
+ // = 8 bytes, exactly one u64 compare. Store 0 (the AS null reference)
42
+ // and skip 8 bytes.
43
+ //
44
+ // We accept null tokens unconditionally rather than gating on
45
+ // `isNullable<valueof<T>>()` — AS's `valueof` of a nullable-array
46
+ // element type doesn't always preserve the nullable marker through
47
+ // the dispatcher's `<T>` cast, so the gate would mis-fire for the
48
+ // very case it's meant to handle. The runtime cost on plain
49
+ // `string[]` arrays is one extra u64 compare per element; a
50
+ // well-formed `string[]` input never matches it.
51
+ if (srcStart + 8 <= srcEnd && load<u64>(srcStart) == NULL_WORD_U64) {
52
+ store<usize>(slot, 0);
53
+ srcStart += 8;
54
+ } else {
55
+ srcStart = deserializeStringField_SWAR<valueof<T>>(
56
+ srcStart,
57
+ srcEnd,
58
+ slot,
59
+ );
60
+ }
61
+ if (!srcStart) break;
62
+ srcStart = skipStringArrayWhitespace(srcStart, srcEnd);
63
+ if (srcStart >= srcEnd) break;
22
64
 
23
65
  const code = load<u16>(srcStart);
24
66
  if (code == COMMA) {
25
67
  srcStart += 2;
68
+ srcStart = skipStringArrayWhitespace(srcStart, srcEnd);
26
69
  index++;
27
70
  continue;
28
71
  }
29
72
  if (code == BRACKET_RIGHT) {
30
- out.length = index + 1;
73
+ const nextLen = index + 1;
74
+ if (out.length != nextLen) out.length = nextLen;
31
75
  return srcStart + 2;
32
76
  }
33
77
  break;
@@ -38,6 +82,95 @@ import { deserializeStringField_SWAR } from "../string";
38
82
  }
39
83
 
40
84
 
41
- @inline export function deserializeStringArrayField<T extends string[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
42
- return deserializeStringArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
85
+ @inline export function deserializeStringArrayField<T extends string[]>(
86
+ srcStart: usize,
87
+ srcEnd: usize,
88
+ fieldPtr: usize,
89
+ ): usize {
90
+ return deserializeStringArrayBody<T>(
91
+ srcStart,
92
+ srcEnd,
93
+ ensureArrayField<T>(fieldPtr),
94
+ );
95
+ }
96
+
97
+ // Top-level entry for `JSON.parse<string[]>` / `JSON.parse<(string | null)[]>`.
98
+ //
99
+ // Pre-grows the destination array to a worst-case bound in one allocation,
100
+ // then writes elements via `writePtr` direct stores. This sidesteps the
101
+ // per-element growth cost of `deserializeStringArrayBody`'s
102
+ // `ensureArrayElementSlot` path. The incremental runtime zero-initializes
103
+ // new allocations, so `null` slots need no per-element store on cold
104
+ // arrays (we still write 0 to handle reused arrays whose old refs would
105
+ // otherwise leak through).
106
+ export function deserializeStringArray_SWAR<T extends string[]>(
107
+ srcStart: usize,
108
+ srcEnd: usize,
109
+ dst: usize,
110
+ ): T {
111
+ const out = changetype<nonnull<T>>(
112
+ dst || changetype<usize>(instantiate<T>()),
113
+ );
114
+
115
+ // Worst-case sizing: shortest possible element is `""` followed by `,` =
116
+ // 6 UTF-16 bytes, so `(srcLen + 5) / 6` upper-bounds the count. Clamp to
117
+ // AS's BLOCK_MAXSIZE / sizeof<string ref> = (1<<28) - 4 elements; payloads
118
+ // that exceed this would fail the `out.length =` setter anyway. For a
119
+ // 1 GiB UTF-16 source that maxes at ~256M slot allocation = ~1 GiB.
120
+ const elementSize: usize = sizeof<usize>();
121
+ const maxBlockElements: i32 = i32((<usize>0x40000000 - 16) / elementSize);
122
+ let maxElements: i32 = i32((<usize>(srcEnd - srcStart) + 5) / 6);
123
+ if (maxElements < 0 || maxElements > maxBlockElements) {
124
+ maxElements = maxBlockElements;
125
+ }
126
+ if (out.length < maxElements) out.length = maxElements;
127
+
128
+ const dataStart: usize = out.dataStart;
129
+ let writePtr: usize = dataStart;
130
+ const writePtrLimit: usize = dataStart + <usize>maxElements * elementSize;
131
+
132
+ // Caller guarantees srcStart is at the opening `[`.
133
+ if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) {
134
+ out.length = 0;
135
+ return out;
136
+ }
137
+ srcStart += 2;
138
+ srcStart = skipStringArrayWhitespace(srcStart, srcEnd);
139
+ if (srcStart < srcEnd && load<u16>(srcStart) == BRACKET_RIGHT) {
140
+ out.length = 0;
141
+ return out;
142
+ }
143
+
144
+ while (srcStart < srcEnd && writePtr < writePtrLimit) {
145
+ // Null fast path: one u64 compare.
146
+ if (srcStart + 8 <= srcEnd && load<u64>(srcStart) == NULL_WORD_U64) {
147
+ store<usize>(writePtr, 0);
148
+ srcStart += 8;
149
+ } else {
150
+ srcStart = deserializeStringField_SWAR<valueof<T>>(
151
+ srcStart,
152
+ srcEnd,
153
+ writePtr,
154
+ );
155
+ if (!srcStart) break;
156
+ }
157
+ writePtr += elementSize;
158
+ srcStart = skipStringArrayWhitespace(srcStart, srcEnd);
159
+ if (srcStart >= srcEnd) break;
160
+
161
+ const code = load<u16>(srcStart);
162
+ if (code == COMMA) {
163
+ srcStart += 2;
164
+ srcStart = skipStringArrayWhitespace(srcStart, srcEnd);
165
+ continue;
166
+ }
167
+ if (code == BRACKET_RIGHT) {
168
+ const finalLen = i32(<usize>(writePtr - dataStart) / elementSize);
169
+ if (out.length != finalLen) out.length = finalLen;
170
+ return out;
171
+ }
172
+ break;
173
+ }
174
+
175
+ throw new Error("Failed to parse JSON!");
43
176
  }
@@ -1,13 +1,42 @@
1
1
  import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
2
+ import { isSpace } from "../../../util";
2
3
  import { ensureArrayElementSlot, ensureArrayField } from "./shared";
3
4
 
4
5
 
5
- @inline export function deserializeStructArrayInto<T extends unknown[]>(srcStart: usize, srcEnd: usize, out: T): usize {
6
+ @inline function skipStructArrayWhitespace(
7
+ srcStart: usize,
8
+ srcEnd: usize,
9
+ ): usize {
10
+ while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
11
+ return srcStart;
12
+ }
13
+
14
+ // Per-element worker for `@json class Foo[]` fields. Each element is a
15
+ // managed reference whose value is produced by the transform-generated
16
+ // `__DESERIALIZE_FAST` / `__DESERIALIZE_SLOW` methods, so the loop pattern
17
+ // is structurally different from primitive-array bodies:
18
+ //
19
+ // - The slot stores a *reference*; reused arrays may already hold an
20
+ // allocated instance whose fields we just overwrite. Only allocate +
21
+ // `__INITIALIZE` when the slot is null.
22
+ // - `__DESERIALIZE_FAST` returns the cursor past the closing `}` on
23
+ // success, or `0` to signal "bail to slow path". We mirror the
24
+ // dispatcher in `JSON.__deserialize` here: try FAST first, fall back
25
+ // to SLOW on `0`.
26
+ // - Whitespace is skipped at each separator boundary so struct-array
27
+ // fields tolerate the same `[ {...} , {...} ]` shape that top-level
28
+ // `JSON.parse` does.
29
+ @inline function deserializeStructArrayBody<T extends unknown[]>(
30
+ srcStart: usize,
31
+ srcEnd: usize,
32
+ out: T,
33
+ ): usize {
6
34
  let index = 0;
7
35
 
8
36
  do {
9
37
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
10
38
  srcStart += 2;
39
+ srcStart = skipStructArrayWhitespace(srcStart, srcEnd);
11
40
  if (srcStart >= srcEnd) break;
12
41
  if (load<u16>(srcStart) == BRACKET_RIGHT) {
13
42
  out.length = 0;
@@ -18,7 +47,9 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
18
47
  const slot = ensureArrayElementSlot<T>(out, index);
19
48
  let value = load<valueof<T>>(slot);
20
49
  if (changetype<usize>(value) == 0) {
21
- value = changetype<valueof<T>>(__new(offsetof<nonnull<valueof<T>>>(), idof<nonnull<valueof<T>>>()));
50
+ value = changetype<valueof<T>>(
51
+ __new(offsetof<nonnull<valueof<T>>>(), idof<nonnull<valueof<T>>>()),
52
+ );
22
53
  // @ts-ignore: supplied by transform
23
54
  if (isDefined(changetype<nonnull<valueof<T>>>(value).__INITIALIZE)) {
24
55
  // @ts-ignore: supplied by transform
@@ -27,25 +58,40 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
27
58
  store<valueof<T>>(slot, value);
28
59
  }
29
60
 
30
- const valueStart = srcStart;
31
- // @ts-ignore: supplied by transform
32
- if (isDefined(changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST)) {
61
+ let next: usize = 0;
62
+ if (
33
63
  // @ts-ignore: supplied by transform
34
- srcStart = changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST<valueof<T>>(valueStart, srcEnd, value);
35
- } else {
64
+ isDefined(changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST)
65
+ ) {
66
+ // @ts-ignore: supplied by transform
67
+ next = changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_FAST<
68
+ valueof<T>
69
+ >(srcStart, srcEnd, value);
70
+ }
71
+ if (!next) {
36
72
  // @ts-ignore: supplied by transform
37
- srcStart = changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_SLOW<valueof<T>>(valueStart, srcEnd, value);
73
+ next = changetype<nonnull<valueof<T>>>(value).__DESERIALIZE_SLOW<
74
+ valueof<T>
75
+ >(srcStart, srcEnd, value);
38
76
  }
39
- if (!srcStart || srcStart >= srcEnd) break;
77
+ if (!next) break;
78
+ srcStart = next;
79
+ srcStart = skipStructArrayWhitespace(srcStart, srcEnd);
80
+ if (srcStart >= srcEnd) break;
40
81
 
41
82
  const code = load<u16>(srcStart);
42
83
  if (code == COMMA) {
43
84
  srcStart += 2;
85
+ srcStart = skipStructArrayWhitespace(srcStart, srcEnd);
44
86
  index++;
45
87
  continue;
46
88
  }
47
89
  if (code == BRACKET_RIGHT) {
48
- out.length = index + 1;
90
+ // Skip `ensureCapacity` when the reused array already has the
91
+ // right length (e.g. canada-style geometry rings whose count
92
+ // matches a previous parse).
93
+ const nextLen = index + 1;
94
+ if (out.length != nextLen) out.length = nextLen;
49
95
  return srcStart + 2;
50
96
  }
51
97
  break;
@@ -56,6 +102,14 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
56
102
  }
57
103
 
58
104
 
59
- @inline export function deserializeStructArrayField<T extends unknown[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
60
- return deserializeStructArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
105
+ @inline export function deserializeStructArrayField<T extends unknown[]>(
106
+ srcStart: usize,
107
+ srcEnd: usize,
108
+ fieldPtr: usize,
109
+ ): usize {
110
+ return deserializeStructArrayBody<T>(
111
+ srcStart,
112
+ srcEnd,
113
+ ensureArrayField<T>(fieldPtr),
114
+ );
61
115
  }
@@ -11,17 +11,16 @@ import { deserializeObjectArrayField } from "./array/object";
11
11
  import { deserializeRawArrayField } from "./array/raw";
12
12
  import { deserializeStringArrayField } from "./array/string";
13
13
  import { deserializeStructArrayField } from "./array/struct";
14
- import { deserializeArrayArrayInto } from "./array/array";
15
- import { deserializeBooleanArrayInto } from "./array/bool";
16
- import { deserializeFloatArrayInto } from "./array/float";
17
- import { deserializeGenericArrayInto } from "./array/generic";
18
- import { deserializeIntegerArrayInto } from "./array/integer";
19
- import { deserializeObjectArrayInto } from "./array/object";
20
- import { deserializeStringArrayInto } from "./array/string";
21
- import { deserializeStructArrayInto } from "./array/struct";
22
14
 
15
+ export { deserializeArrayField as deserializeArrayField_SWAR };
23
16
 
24
- @inline export function deserializeArrayField<T extends unknown[]>(srcStart: usize, srcEnd: usize, dstObj: usize, dstOffset: usize = 0): usize {
17
+
18
+ @inline export function deserializeArrayField<T extends unknown[]>(
19
+ srcStart: usize,
20
+ srcEnd: usize,
21
+ dstObj: usize,
22
+ dstOffset: usize = 0,
23
+ ): usize {
25
24
  const fieldPtr = dstObj + dstOffset;
26
25
  if (isString<valueof<T>>()) {
27
26
  return deserializeStringArrayField<T>(srcStart, srcEnd, fieldPtr);
@@ -53,7 +52,10 @@ import { deserializeStructArrayInto } from "./array/struct";
53
52
  } else if (isDefined(type.__DESERIALIZE_CUSTOM)) {
54
53
  return deserializeStructArrayField<T>(srcStart, srcEnd, fieldPtr);
55
54
  // @ts-ignore: defined by transform
56
- } else if (isDefined(type.__DESERIALIZE_SLOW) || isDefined(type.__DESERIALIZE_FAST)) {
55
+ } else if (
56
+ isDefined(type.__DESERIALIZE_SLOW) ||
57
+ isDefined(type.__DESERIALIZE_FAST)
58
+ ) {
57
59
  return deserializeStructArrayField<T>(srcStart, srcEnd, fieldPtr);
58
60
  }
59
61
  throw new Error("Could not parse array field of type " + nameof<T>() + "!");
@@ -61,44 +63,3 @@ import { deserializeStructArrayInto } from "./array/struct";
61
63
  throw new Error("Could not parse array field of type " + nameof<T>() + "!");
62
64
  }
63
65
  }
64
-
65
-
66
- @inline export function deserializeArrayInto_SWAR<T extends unknown[]>(srcStart: usize, srcEnd: usize, out: T): usize {
67
- if (isString<valueof<T>>()) {
68
- return deserializeStringArrayInto<T>(srcStart, srcEnd, out);
69
- } else if (isBoolean<valueof<T>>()) {
70
- return deserializeBooleanArrayInto<T>(srcStart, srcEnd, out);
71
- } else if (isInteger<valueof<T>>()) {
72
- return deserializeIntegerArrayInto<T>(srcStart, srcEnd, out);
73
- } else if (isFloat<valueof<T>>()) {
74
- return deserializeFloatArrayInto<T>(srcStart, srcEnd, out);
75
- } else if (isArray<valueof<T>>()) {
76
- return deserializeArrayArrayInto<T>(srcStart, srcEnd, out);
77
- } else if (isManaged<valueof<T>>() || isReference<valueof<T>>()) {
78
- const type = changetype<nonnull<valueof<T>>>(0);
79
- if (type instanceof JSON.Value) {
80
- return deserializeGenericArrayInto<T>(srcStart, srcEnd, out);
81
- } else if (type instanceof JSON.Box) {
82
- throw new Error("Failed to parse JSON!");
83
- } else if (type instanceof JSON.Obj) {
84
- return deserializeObjectArrayInto<T>(srcStart, srcEnd, out);
85
- } else if (type instanceof JSON.Raw) {
86
- throw new Error("Failed to parse JSON!");
87
- } else if (type instanceof Date) {
88
- return deserializeGenericArrayInto<T>(srcStart, srcEnd, out);
89
- } else if (type instanceof Set) {
90
- return deserializeGenericArrayInto<T>(srcStart, srcEnd, out);
91
- } else if (type instanceof Map) {
92
- throw new Error("Failed to parse JSON!");
93
- // @ts-ignore: defined by transform
94
- } else if (isDefined(type.__DESERIALIZE_CUSTOM)) {
95
- return deserializeStructArrayInto<T>(srcStart, srcEnd, out);
96
- // @ts-ignore: defined by transform
97
- } else if (isDefined(type.__DESERIALIZE_SLOW) || isDefined(type.__DESERIALIZE_FAST)) {
98
- return deserializeStructArrayInto<T>(srcStart, srcEnd, out);
99
- }
100
- throw new Error("Could not parse array field of type " + nameof<T>() + "!");
101
- } else {
102
- throw new Error("Could not parse array field of type " + nameof<T>() + "!");
103
- }
104
- }