json-as 1.3.9 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/CHANGELOG.md +60 -19
  2. package/README.md +120 -21
  3. package/assembly/custom/chars.ts +39 -78
  4. package/assembly/deserialize/index/arbitrary.ts +28 -10
  5. package/assembly/deserialize/index/float.ts +2 -4
  6. package/assembly/deserialize/index/integer.ts +2 -4
  7. package/assembly/deserialize/index/object.ts +6 -1
  8. package/assembly/deserialize/index/string.ts +2 -7
  9. package/assembly/deserialize/index/unsigned.ts +2 -4
  10. package/assembly/deserialize/naive/array/arbitrary.ts +3 -136
  11. package/assembly/deserialize/naive/array/array.ts +30 -1
  12. package/assembly/deserialize/naive/array/integer.ts +2 -7
  13. package/assembly/deserialize/naive/array/map.ts +10 -14
  14. package/assembly/deserialize/naive/array/object.ts +10 -14
  15. package/assembly/deserialize/naive/array/struct.ts +19 -1
  16. package/assembly/deserialize/naive/bool.ts +1 -5
  17. package/assembly/deserialize/naive/date.ts +1 -2
  18. package/assembly/deserialize/naive/float.ts +4 -11
  19. package/assembly/deserialize/naive/integer.ts +2 -4
  20. package/assembly/deserialize/naive/map.ts +42 -205
  21. package/assembly/deserialize/naive/object.ts +291 -174
  22. package/assembly/deserialize/naive/raw.ts +1 -5
  23. package/assembly/deserialize/naive/set.ts +3 -6
  24. package/assembly/deserialize/naive/staticarray.ts +2 -4
  25. package/assembly/deserialize/naive/string.ts +68 -24
  26. package/assembly/deserialize/naive/typedarray.ts +1 -2
  27. package/assembly/deserialize/naive/unsigned.ts +2 -4
  28. package/assembly/deserialize/simd/array/integer.ts +5 -13
  29. package/assembly/deserialize/simd/float.ts +5 -12
  30. package/assembly/deserialize/simd/integer.ts +6 -15
  31. package/assembly/deserialize/simd/string.ts +21 -43
  32. package/assembly/deserialize/swar/array/arbitrary.ts +1 -2
  33. package/assembly/deserialize/swar/array/array.ts +2 -4
  34. package/assembly/deserialize/swar/array/bool.ts +2 -4
  35. package/assembly/deserialize/swar/array/box.ts +1 -2
  36. package/assembly/deserialize/swar/array/float.ts +8 -21
  37. package/assembly/deserialize/swar/array/generic.ts +2 -4
  38. package/assembly/deserialize/swar/array/integer.ts +13 -27
  39. package/assembly/deserialize/swar/array/map.ts +1 -2
  40. package/assembly/deserialize/swar/array/object.ts +2 -4
  41. package/assembly/deserialize/swar/array/raw.ts +1 -2
  42. package/assembly/deserialize/swar/array/shared.ts +9 -21
  43. package/assembly/deserialize/swar/array/string.ts +4 -10
  44. package/assembly/deserialize/swar/array/struct.ts +3 -9
  45. package/assembly/deserialize/swar/array.ts +1 -3
  46. package/assembly/deserialize/swar/float.ts +7 -17
  47. package/assembly/deserialize/swar/integer.ts +6 -15
  48. package/assembly/deserialize/swar/string.ts +40 -54
  49. package/assembly/deserialize/swar/typedarray.ts +4 -4
  50. package/assembly/index.d.ts +259 -21
  51. package/assembly/index.ts +1704 -266
  52. package/assembly/serialize/index/arbitrary.ts +70 -4
  53. package/assembly/serialize/index/jsonarray.ts +51 -0
  54. package/assembly/serialize/index/object.ts +39 -14
  55. package/assembly/serialize/index/string.ts +1 -2
  56. package/assembly/serialize/index/typedarray.ts +1 -2
  57. package/assembly/serialize/index.ts +1 -0
  58. package/assembly/serialize/naive/array.ts +23 -34
  59. package/assembly/serialize/naive/bool.ts +0 -1
  60. package/assembly/serialize/naive/float.ts +16 -25
  61. package/assembly/serialize/naive/integer.ts +1 -5
  62. package/assembly/serialize/naive/raw.ts +1 -2
  63. package/assembly/serialize/naive/set.ts +0 -4
  64. package/assembly/serialize/naive/staticarray.ts +0 -5
  65. package/assembly/serialize/naive/string.ts +11 -7
  66. package/assembly/serialize/naive/typedarray.ts +0 -6
  67. package/assembly/serialize/simd/string.ts +1 -3
  68. package/assembly/serialize/swar/string.ts +2 -4
  69. package/assembly/util/atoi-fast.ts +4 -14
  70. package/assembly/util/atoi.ts +1 -2
  71. package/assembly/util/bytes.ts +1 -2
  72. package/assembly/util/idofd.ts +1 -2
  73. package/assembly/util/isSpace.ts +1 -2
  74. package/assembly/util/itoa-fast.ts +9 -15
  75. package/assembly/util/nextPowerOf2.ts +1 -2
  76. package/assembly/util/parsefloat-fast.ts +4 -7
  77. package/assembly/util/ptrToStr.ts +1 -2
  78. package/assembly/util/scanValueEnd.ts +1 -2
  79. package/assembly/util/scanValueEndSimd.ts +198 -0
  80. package/assembly/util/scanValueEndSwar.ts +184 -0
  81. package/assembly/util/scientific.ts +8 -14
  82. package/assembly/util/simd-int.ts +4 -8
  83. package/assembly/util/snp.ts +2 -7
  84. package/assembly/util/stringScan.ts +2 -4
  85. package/assembly/util/swar-int.ts +8 -16
  86. package/assembly/util/swar.ts +2 -4
  87. package/lib/as-bs.ts +57 -42
  88. package/package.json +27 -10
  89. package/transform/lib/builder.d.ts +0 -1
  90. package/transform/lib/builder.js +0 -1
  91. package/transform/lib/index.d.ts +0 -1
  92. package/transform/lib/index.js +617 -326
  93. package/transform/lib/linkers/alias.d.ts +0 -1
  94. package/transform/lib/linkers/alias.js +0 -1
  95. package/transform/lib/linkers/custom.d.ts +0 -1
  96. package/transform/lib/linkers/custom.js +0 -1
  97. package/transform/lib/linkers/imports.d.ts +0 -1
  98. package/transform/lib/linkers/imports.js +0 -1
  99. package/transform/lib/types.d.ts +4 -2
  100. package/transform/lib/types.js +5 -1
  101. package/transform/lib/util.d.ts +0 -1
  102. package/transform/lib/util.js +0 -1
  103. package/transform/lib/visitor.d.ts +0 -1
  104. package/transform/lib/visitor.js +0 -1
  105. package/assembly/util/dragonbox-cache.ts +0 -445
  106. package/assembly/util/dragonbox.ts +0 -660
  107. package/transform/lib/builder.d.ts.map +0 -1
  108. package/transform/lib/builder.js.map +0 -1
  109. package/transform/lib/index.d.ts.map +0 -1
  110. package/transform/lib/index.js.map +0 -1
  111. package/transform/lib/linkers/alias.d.ts.map +0 -1
  112. package/transform/lib/linkers/alias.js.map +0 -1
  113. package/transform/lib/linkers/custom.d.ts.map +0 -1
  114. package/transform/lib/linkers/custom.js.map +0 -1
  115. package/transform/lib/linkers/imports.d.ts.map +0 -1
  116. package/transform/lib/linkers/imports.js.map +0 -1
  117. package/transform/lib/types.d.ts.map +0 -1
  118. package/transform/lib/types.js.map +0 -1
  119. package/transform/lib/util.d.ts.map +0 -1
  120. package/transform/lib/util.js.map +0 -1
  121. package/transform/lib/visitor.d.ts.map +0 -1
  122. package/transform/lib/visitor.js.map +0 -1
@@ -1,21 +1,77 @@
1
1
  import { JSON } from "../..";
2
2
  import { bs } from "../../../lib/as-bs";
3
- import { serializeArray } from "./array";
3
+ import { bytes } from "../../util/bytes";
4
+ import { QUOTE } from "../../custom/chars";
4
5
  import { serializeBool } from "./bool";
5
6
  import { serializeFloat32, serializeFloat64 } from "./float";
6
7
  import { serializeInteger } from "./integer";
8
+ import { serializeJsonArray } from "./jsonarray";
7
9
  import { serializeMap } from "./map";
8
10
  import { serializeObject } from "./object";
11
+ import { serializeRaw } from "../naive/raw";
9
12
  import { serializeString } from "./string";
10
13
  import { serializeDynamic } from "./typedarray";
11
14
 
15
+ // True if any code unit would need JSON-escaping on serialize: a quote,
16
+ // backslash, control char (< 0x20), or a surrogate (handled conservatively -
17
+ // even valid pairs route to the full path, since they can't be bulk-copied as a
18
+ // plain run). A clean string is none of these, so it emits as a verbatim memcpy.
19
+ // @ts-ignore: decorator
20
+ @inline function stringNeedsEscape(src: string): bool {
21
+ let ptr = changetype<usize>(src);
22
+ const end = ptr + <usize>bytes(src);
23
+ while (ptr < end) {
24
+ const code = load<u16>(ptr);
25
+ if (
26
+ code == 0x22 ||
27
+ code == 0x5c ||
28
+ code < 0x20 ||
29
+ (code >= 0xd800 && code <= 0xdfff)
30
+ )
31
+ return true;
32
+ ptr += 2;
33
+ }
34
+ return false;
35
+ }
36
+
37
+ // Fast path for a string already known to need no escaping: quote + a single
38
+ // bulk copy of the UTF-16 bytes + quote. Skips the per-char escape scan that
39
+ // serializeString would otherwise do (and its trailing second pass).
40
+ function serializeStringClean(src: string): void {
41
+ const size = <usize>bytes(src);
42
+ bs.proposeSize(size + 4);
43
+ store<u16>(bs.offset, QUOTE);
44
+ bs.offset += 2;
45
+ memory.copy(bs.offset, changetype<usize>(src), size);
46
+ bs.offset += size;
47
+ store<u16>(bs.offset, QUOTE);
48
+ bs.offset += 2;
49
+ }
50
+
12
51
  export function serializeArbitrary(src: JSON.Value): void {
52
+ // Verbatim passthrough: an untouched (still-deferred) value emits its original
53
+ // source bytes directly - no materialization, no re-encoding. Peek the slice
54
+ // without reading `src.type` (which would force materialization).
55
+ const lz = src.__lazySlice();
56
+ if (lz != 0) {
57
+ const start = <usize>(lz >>> 32);
58
+ const size = <usize>(<u32>lz) - start;
59
+ bs.proposeSize(size);
60
+ memory.copy(bs.offset, start, size);
61
+ bs.offset += size;
62
+ return;
63
+ }
13
64
  switch (src.type) {
14
65
  case JSON.Types.Null:
15
66
  bs.proposeSize(8);
16
67
  store<u64>(bs.offset, 30399761348886638);
17
68
  bs.offset += 8;
18
69
  break;
70
+ case JSON.Types.Raw:
71
+ // A materialized JSON.Raw value (e.g. set into a JSON.Obj/Arr) emits its
72
+ // pre-formatted bytes as-is - without this it fell into the struct default.
73
+ serializeRaw(src.get<JSON.Raw>());
74
+ break;
19
75
  case JSON.Types.U8:
20
76
  serializeInteger<u8>(src.get<u8>());
21
77
  break;
@@ -46,14 +102,24 @@ export function serializeArbitrary(src: JSON.Value): void {
46
102
  case JSON.Types.F64:
47
103
  serializeFloat64(src.get<f64>());
48
104
  break;
49
- case JSON.Types.String:
50
- serializeString(src.get<string>());
105
+ case JSON.Types.String: {
106
+ const str = src.get<string>();
107
+ // Reuse the cached escape class; classify once on first serialize. Clean
108
+ // strings (the common case) then emit as a single memcpy on every reuse.
109
+ let cls = src.__strClass();
110
+ if (cls == 0) {
111
+ cls = stringNeedsEscape(str) ? 2 : 1;
112
+ src.__setStrClass(cls);
113
+ }
114
+ if (cls == 1) serializeStringClean(str);
115
+ else serializeString(str);
51
116
  break;
117
+ }
52
118
  case JSON.Types.Bool:
53
119
  serializeBool(src.get<bool>());
54
120
  break;
55
121
  case JSON.Types.Array:
56
- serializeArray(src.get<JSON.Value[]>());
122
+ serializeJsonArray(src.get<JSON.Arr>());
57
123
  break;
58
124
  case JSON.Types.Object:
59
125
  serializeObject(src.get<JSON.Obj>());
@@ -0,0 +1,51 @@
1
+ import { bs } from "../../../lib/as-bs";
2
+ import { JSON } from "../..";
3
+ import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../custom/chars";
4
+ import { serializeArbitrary } from "./arbitrary";
5
+
6
+ export function serializeJsonArray(src: JSON.Arr): void {
7
+ const n = src.length;
8
+
9
+ if (n == 0) {
10
+ bs.proposeSize(4);
11
+ store<u32>(bs.offset, 6094939); // "[]"
12
+ bs.offset += 4;
13
+ return;
14
+ }
15
+
16
+ const vals = src._vals;
17
+ // Source base + upper bound for resolving a deferred slot's value range.
18
+ const srcBase = changetype<usize>(src._src);
19
+ const srcEnd = srcBase + ((<usize>src._src.length) << 1);
20
+
21
+ bs.proposeSize(4 + <u32>(n - 1) * 2);
22
+ store<u16>(bs.offset, BRACKET_LEFT);
23
+ bs.offset += 2;
24
+
25
+ // A still-deferred slot is copied out verbatim from the source; an
26
+ // eager/materialized slot is serialized from its boxed bits.
27
+ for (let i = 0; i < n; i++) {
28
+ if (i != 0) {
29
+ store<u16>(bs.offset, COMMA);
30
+ bs.offset += 2;
31
+ }
32
+ const slot = unchecked(vals[i]);
33
+ if (JSON.Value.slotIsLazy(slot)) {
34
+ const start = JSON.Value.slotPtr(slot, srcBase);
35
+ const end = JSON.Value.slotEnd(slot, srcBase, srcEnd);
36
+ const size = end - start;
37
+ bs.proposeSize(size);
38
+ memory.copy(bs.offset, start, size);
39
+ bs.offset += size;
40
+ } else {
41
+ const v = JSON.Value.fromBits(slot);
42
+ serializeArbitrary(v);
43
+ // Persist a cached String escape-class back into the slot (see object.ts).
44
+ const nb = v.__bits();
45
+ if (nb != slot) unchecked((vals[i] = nb));
46
+ }
47
+ }
48
+
49
+ store<u16>(bs.offset, BRACKET_RIGHT);
50
+ bs.offset += 2;
51
+ }
@@ -2,11 +2,10 @@ import { bs } from "../../../lib/as-bs";
2
2
  import { JSON } from "../..";
3
3
  import { BRACE_LEFT, BRACE_RIGHT, COLON, COMMA } from "../../custom/chars";
4
4
  import { serializeArbitrary } from "./arbitrary";
5
- import { serializeString } from "./string";
5
+ import { serializeStringRange } from "../naive/string";
6
6
 
7
7
  export function serializeObject(src: JSON.Obj): void {
8
8
  const srcSize = src.size;
9
- const srcEnd = srcSize - 1;
10
9
 
11
10
  if (srcSize == 0) {
12
11
  bs.proposeSize(4);
@@ -15,28 +14,54 @@ export function serializeObject(src: JSON.Obj): void {
15
14
  return;
16
15
  }
17
16
 
18
- const keys = src.keys();
19
- const values = src.values();
17
+ const vals = src._vals;
18
+ const kbuf = changetype<usize>(src._kbuf);
19
+ const kused = src._kused;
20
+ // Source base + upper bound for resolving a deferred slot's value range.
21
+ const srcBase = changetype<usize>(src._src);
22
+ const srcEnd = srcBase + ((<usize>src._src.length) << 1);
20
23
 
21
24
  bs.proposeSize(4 + <u32>(srcSize - 1) * 2 + <u32>srcSize * 2);
22
25
  store<u16>(bs.offset, BRACE_LEFT);
23
26
  bs.offset += 2;
24
27
 
25
- for (let i = 0; i < srcEnd; i++) {
26
- serializeString(unchecked(keys[i]));
28
+ // Walk the length-prefixed key buffer in lockstep with the value slots,
29
+ // emitting each key straight from its slice (no per-key string
30
+ // materialization). A still-deferred slot is copied out verbatim from the
31
+ // source; an eager/materialized slot is serialized from its boxed bits.
32
+ let pos = 0;
33
+ let i = 0;
34
+ while (pos < kused) {
35
+ const len = <i32>load<u16>(kbuf + ((<usize>pos) << 1));
36
+ if (i != 0) {
37
+ store<u16>(bs.offset, COMMA);
38
+ bs.offset += 2;
39
+ }
40
+ serializeStringRange(kbuf + ((<usize>(pos + 1)) << 1), (<usize>len) << 1);
27
41
  store<u16>(bs.offset, COLON);
28
42
  bs.offset += 2;
29
43
 
30
- serializeArbitrary(unchecked(values[i]));
31
- store<u16>(bs.offset, COMMA);
32
- bs.offset += 2;
44
+ const slot = unchecked(vals[i]);
45
+ if (JSON.Value.slotIsLazy(slot)) {
46
+ const start = JSON.Value.slotPtr(slot, srcBase);
47
+ const end = JSON.Value.slotEnd(slot, srcBase, srcEnd);
48
+ const size = end - start;
49
+ bs.proposeSize(size);
50
+ memory.copy(bs.offset, start, size);
51
+ bs.offset += size;
52
+ } else {
53
+ const v = JSON.Value.fromBits(slot);
54
+ serializeArbitrary(v);
55
+ // Persist any escape-class the serializer cached on a materialized String
56
+ // slot back into the flat slot, so re-serializing this object reuses it
57
+ // (the memcpy fast path) instead of re-scanning. No-op for other types.
58
+ const nb = v.__bits();
59
+ if (nb != slot) unchecked((vals[i] = nb));
60
+ }
61
+ pos += 1 + len;
62
+ i++;
33
63
  }
34
64
 
35
- serializeString(unchecked(keys[srcEnd]));
36
- store<u16>(bs.offset, COLON);
37
- bs.offset += 2;
38
- serializeArbitrary(unchecked(values[srcEnd]));
39
-
40
65
  store<u16>(bs.offset, BRACE_RIGHT);
41
66
  bs.offset += 2;
42
67
  }
@@ -3,8 +3,7 @@ import { serializeString_NAIVE } from "../naive/string";
3
3
  import { serializeString_SIMD } from "../simd/string";
4
4
  import { serializeString_SWAR } from "../swar/string";
5
5
 
6
-
7
- @inline export function serializeString(src: string): void {
6
+ export function serializeString(src: string): void {
8
7
  if (JSON_MODE == JSONMode.SIMD) {
9
8
  serializeString_SIMD(src);
10
9
  } else if (JSON_MODE == JSONMode.NAIVE) {
@@ -9,8 +9,7 @@ import {
9
9
  serializeTypedArray,
10
10
  } from "../naive/typedarray";
11
11
 
12
-
13
- @inline export function serializeDynamic(type: u16, data: usize): void {
12
+ export function serializeDynamic(type: u16, data: usize): void {
14
13
  if (type == JSON.Types.ArrayBuffer) {
15
14
  serializeArrayBufferUnsafe(
16
15
  data,
@@ -4,6 +4,7 @@ export * from "./index/bool";
4
4
  export * from "./index/date";
5
5
  export * from "./index/float";
6
6
  export * from "./index/integer";
7
+ export * from "./index/jsonarray";
7
8
  export * from "./index/map";
8
9
  export * from "./index/object";
9
10
  export * from "./index/raw";
@@ -5,13 +5,8 @@ import { serializeBoolUnsafe } from "./bool";
5
5
  import { serializeFloat32Unsafe, serializeFloat64Unsafe } from "./float";
6
6
  import { serializeIntegerUnsafe } from "./integer";
7
7
  import { serializeString } from "../index/string";
8
- import {
9
- dragonbox_f32_buffered,
10
- dragonbox_f64_buffered,
11
- } from "../../util/dragonbox";
8
+ import { dtoa_buffered, ftoa_buffered } from "xjb-as";
12
9
 
13
-
14
- @inline
15
10
  function maxIntegerBytes<T extends number>(): u32 {
16
11
  if (sizeof<T>() == 1) return isSigned<T>() ? 8 : 6;
17
12
  if (sizeof<T>() == 2) return isSigned<T>() ? 12 : 10;
@@ -19,8 +14,6 @@ function maxIntegerBytes<T extends number>(): u32 {
19
14
  return isSigned<T>() ? 42 : 40;
20
15
  }
21
16
 
22
-
23
- @inline
24
17
  function reservePrimitiveArray<T>(len: i32): void {
25
18
  if (len <= 0) return;
26
19
  if (isBoolean<T>()) {
@@ -34,8 +27,6 @@ function reservePrimitiveArray<T>(len: i32): void {
34
27
  }
35
28
  }
36
29
 
37
-
38
- @inline
39
30
  function serializeArrayElement<T>(value: T): void {
40
31
  if (isString<T>()) {
41
32
  serializeString(value as string);
@@ -78,15 +69,13 @@ function serializeArrayElement<T>(value: T): void {
78
69
  // comma-store + advance per element that the generic `serializeArray` does.
79
70
 
80
71
  // `"true,"` packed UTF-16: `t,r,u,e,,` lanes 0..4 = bytes 0..9.
81
- @inline const TRUE_COMMA_LO: u64 = 0x0065_0075_0072_0074;
72
+ const TRUE_COMMA_LO: u64 = 0x0065_0075_0072_0074;
82
73
 
83
-
84
- @inline const TRUE_COMMA_HI: u16 = 0x002c;
74
+ const TRUE_COMMA_HI: u16 = 0x002c;
85
75
  // `"false,"` packed UTF-16: `f,a,l,s,e,,` lanes 0..5 = bytes 0..11.
86
- @inline const FALSE_COMMA_LO: u64 = 0x0073_006c_0061_0066;
87
-
76
+ const FALSE_COMMA_LO: u64 = 0x0073_006c_0061_0066;
88
77
 
89
- @inline const FALSE_COMMA_HI: u32 = 0x002c_0065;
78
+ const FALSE_COMMA_HI: u32 = 0x002c_0065;
90
79
 
91
80
  function serializeBoolArrayFast(src: bool[]): void {
92
81
  const len = src.length;
@@ -152,8 +141,7 @@ function initU8Lut(): void {
152
141
  _u8LutInited = true;
153
142
  }
154
143
 
155
-
156
- @inline function ensureU8Lut(): void {
144
+ function ensureU8Lut(): void {
157
145
  if (!_u8LutInited) initU8Lut();
158
146
  }
159
147
 
@@ -181,20 +169,20 @@ function serializeU8ArrayFast(src: u8[]): void {
181
169
  store<u16>(bs.offset - 2, BRACKET_RIGHT);
182
170
  }
183
171
 
184
- // Specialized float-array serializer: dragonbox + trailing comma in a
172
+ // Specialized float-array serializer: xjb writer + trailing comma in a
185
173
  // uniform per-iteration body, then overwrite the final comma with `]`. The
186
174
  // generic dispatcher splits the loop into "N-1 elements with comma, then
187
- // last element without, then `]`" the branch on each `i < end` check
175
+ // last element without, then `]`" - the branch on each `i < end` check
188
176
  // stalls the loop's tight bs.offset advance pattern. This variant runs the
189
- // same number of stores per iteration (dragonbox output + COMMA), but the
177
+ // same number of stores per iteration (xjb output + COMMA), but the
190
178
  // uniform loop body inlines better and the trailing `]` is a single fixed
191
179
  // overwrite outside the loop.
192
180
  function serializeF64ArrayFast(src: f64[]): void {
193
181
  const len = src.length;
194
- // Worst case per element: ~24 chars for f64 + comma = 50 bytes.
195
- // Slight over-reserve (66) matches the existing `reservePrimitiveArray`
196
- // budget and keeps a safety margin for any NaN/Inf spelling.
197
- bs.proposeSize(4 + <u32>len * 66);
182
+ // Worst case per element: a 21-char fixed integer (e.g. 1e20) or a ~24-char
183
+ // exponent form, + comma = ~52 bytes; plus the writer's SIMD stores can
184
+ // overshoot the logical end by up to 16 bytes. 80 covers both comfortably.
185
+ bs.proposeSize(4 + <u32>len * 80);
198
186
  store<u16>(bs.offset, BRACKET_LEFT);
199
187
  bs.offset += 2;
200
188
  if (len == 0) {
@@ -210,9 +198,9 @@ function serializeF64ArrayFast(src: f64[]): void {
210
198
  let offset = bs.offset;
211
199
  for (let i: i32 = 0; i < len; i++) {
212
200
  const v = load<f64>(dataStart + ((<usize>i) << 3));
213
- const size = dragonbox_f64_buffered(offset, v) << 1;
214
- store<u16>(offset + size, COMMA);
215
- offset += size + 2;
201
+ offset += dtoa_buffered(offset, v) << 1;
202
+ store<u16>(offset, COMMA);
203
+ offset += 2;
216
204
  }
217
205
  // Overwrite the final trailing comma with `]`.
218
206
  store<u16>(offset - 2, BRACKET_RIGHT);
@@ -221,9 +209,10 @@ function serializeF64ArrayFast(src: f64[]): void {
221
209
 
222
210
  function serializeF32ArrayFast(src: f32[]): void {
223
211
  const len = src.length;
224
- // Worst case for f32 is ~16 chars + comma = ~34 bytes; mirror the budget
225
- // used by `reservePrimitiveArray`.
226
- bs.proposeSize(4 + <u32>len * 34);
212
+ // Worst case for f32 is a 21-char fixed integer (e.g. 1e20 rounds to a
213
+ // 21-digit value), + comma = ~44 bytes, plus up to 16 bytes of SIMD store
214
+ // overshoot. 64 covers it.
215
+ bs.proposeSize(4 + <u32>len * 64);
227
216
  store<u16>(bs.offset, BRACKET_LEFT);
228
217
  bs.offset += 2;
229
218
  if (len == 0) {
@@ -236,9 +225,9 @@ function serializeF32ArrayFast(src: f32[]): void {
236
225
  let offset = bs.offset;
237
226
  for (let i: i32 = 0; i < len; i++) {
238
227
  const v = load<f32>(dataStart + ((<usize>i) << 2));
239
- const size = dragonbox_f32_buffered(offset, v) << 1;
240
- store<u16>(offset + size, COMMA);
241
- offset += size + 2;
228
+ offset += ftoa_buffered(offset, v) << 1;
229
+ store<u16>(offset, COMMA);
230
+ offset += 2;
242
231
  }
243
232
  store<u16>(offset - 2, BRACKET_RIGHT);
244
233
  bs.offset = offset;
@@ -4,7 +4,6 @@ import { bs } from "../../../lib/as-bs";
4
4
  * @param data data to serialize
5
5
  * @returns void
6
6
  */
7
- @inline
8
7
  export function serializeBoolUnsafe(data: bool): void {
9
8
  if (data === true) {
10
9
  store<u64>(bs.offset, 28429475166421108);
@@ -1,43 +1,34 @@
1
1
  import { bs } from "../../../lib/as-bs";
2
- import {
3
- dragonbox_f32_buffered,
4
- dragonbox_f64_buffered,
5
- } from "../../util/dragonbox";
2
+ import { dtoa_buffered, ftoa_buffered } from "xjb-as";
6
3
 
4
+ // Float serialization is backed by xjb-as (a Schubfach/xjb shortest-decimal
5
+ // writer with SWAR + WASM-SIMD digit kernels). The *_buffered writers emit
6
+ // UTF-16 straight into the `bs` buffer and return the number of characters
7
+ // written, so advancing the offset is `count << 1` bytes.
7
8
 
8
- @inline
9
9
  export function serializeFloat32Unsafe(data: f32): void {
10
- const size = dragonbox_f32_buffered(bs.offset, data) << 1;
11
- bs.offset += size;
10
+ bs.offset += ftoa_buffered(bs.offset, data) << 1;
12
11
  }
13
12
 
14
-
15
- @inline
16
13
  export function serializeFloat64Unsafe(data: f64): void {
17
- const size = dragonbox_f64_buffered(bs.offset, data) << 1;
18
- bs.offset += size;
14
+ bs.offset += dtoa_buffered(bs.offset, data) << 1;
19
15
  }
20
16
 
21
-
22
- @inline
23
17
  export function serializeFloat32(data: f32): void {
24
- bs.ensureSize(64);
25
- const size = dragonbox_f32_buffered(bs.offset, data) << 1;
26
- bs.stackSize += size;
27
- bs.offset += size;
18
+ bs.ensureSize(128);
19
+ const bytes = ftoa_buffered(bs.offset, data) << 1;
20
+ bs.stackSize += bytes;
21
+ bs.offset += bytes;
28
22
  }
29
23
 
30
-
31
- @inline
32
24
  export function serializeFloat64(data: f64): void {
33
- bs.ensureSize(64);
34
- const size = dragonbox_f64_buffered(bs.offset, data) << 1;
35
- bs.stackSize += size;
36
- bs.offset += size;
25
+ bs.ensureSize(128);
26
+ const bytes = dtoa_buffered(bs.offset, data) << 1;
27
+ bs.stackSize += bytes;
28
+ bs.offset += bytes;
37
29
  }
38
30
 
39
- // @ts-ignore: inline
40
- @inline export function serializeFloat<T extends number>(data: T): void {
31
+ export function serializeFloat<T extends number>(data: T): void {
41
32
  if (sizeof<T>() == 4) serializeFloat32(<f32>data);
42
33
  else serializeFloat64(<f64>data);
43
34
  }
@@ -1,16 +1,12 @@
1
1
  import { bs } from "../../../lib/as-bs";
2
2
  import { ensureItoaPairs, itoaFast } from "../../util/itoa-fast";
3
3
 
4
-
5
- @inline
6
4
  export function serializeIntegerUnsafe<T extends number>(data: T): void {
7
5
  ensureItoaPairs();
8
6
  const charsWritten = itoaFast<T>(bs.offset, data);
9
7
  bs.offset += (<usize>charsWritten) << 1;
10
8
  }
11
-
12
- // @ts-ignore: inline
13
- @inline export function serializeInteger<T extends number>(data: T): void {
9
+ export function serializeInteger<T extends number>(data: T): void {
14
10
  ensureItoaPairs();
15
11
  bs.ensureSize(sizeof<T>() << 3);
16
12
  const charsWritten = itoaFast<T>(bs.offset, data);
@@ -7,8 +7,7 @@ import { bytes } from "../../util";
7
7
  * @param data data to serialize
8
8
  * @returns void
9
9
  */
10
- // @ts-ignore: inline
11
- @inline export function serializeRaw(data: JSON.Raw): void {
10
+ export function serializeRaw(data: JSON.Raw): void {
12
11
  const dataSize = bytes(data.data);
13
12
  bs.proposeSize(dataSize);
14
13
  memory.copy(
@@ -6,8 +6,6 @@ import { serializeFloat32Unsafe, serializeFloat64Unsafe } from "./float";
6
6
  import { serializeIntegerUnsafe } from "./integer";
7
7
  import { serializeString } from "../index/string";
8
8
 
9
-
10
- @inline
11
9
  function maxIntegerBytes<T extends number>(): u32 {
12
10
  if (sizeof<T>() == 1) return isSigned<T>() ? 8 : 6;
13
11
  if (sizeof<T>() == 2) return isSigned<T>() ? 12 : 10;
@@ -15,8 +13,6 @@ function maxIntegerBytes<T extends number>(): u32 {
15
13
  return isSigned<T>() ? 42 : 40;
16
14
  }
17
15
 
18
-
19
- @inline
20
16
  function reservePrimitiveSet<T>(len: i32): void {
21
17
  if (len <= 0) return;
22
18
  if (isBoolean<T>()) {
@@ -6,17 +6,12 @@ import { serializeFloat32Unsafe, serializeFloat64Unsafe } from "./float";
6
6
  import { serializeIntegerUnsafe } from "./integer";
7
7
  import { serializeString } from "../index/string";
8
8
 
9
-
10
- @inline
11
9
  function maxIntegerBytes<T extends number>(): u32 {
12
10
  if (sizeof<T>() == 1) return isSigned<T>() ? 8 : 6;
13
11
  if (sizeof<T>() == 2) return isSigned<T>() ? 12 : 10;
14
12
  if (sizeof<T>() == 4) return isSigned<T>() ? 22 : 20;
15
13
  return isSigned<T>() ? 42 : 40;
16
14
  }
17
-
18
-
19
- @inline
20
15
  function reservePrimitiveStaticArray<T>(len: i32): void {
21
16
  if (len <= 0) return;
22
17
  if (isBoolean<T>()) {
@@ -15,11 +15,17 @@ import { serializeStruct } from "./struct";
15
15
  * @param src string
16
16
  * @returns void
17
17
  */
18
- // @ts-ignore: inline
19
- @inline export function serializeString_NAIVE(src: string): void {
20
- const srcSize = bytes(src);
18
+ export function serializeString_NAIVE(src: string): void {
19
+ serializeStringRange(changetype<usize>(src), bytes(src));
20
+ }
21
+
22
+ /**
23
+ * Serializes a raw UTF-16 range as a quoted, escaped JSON string. Lets callers
24
+ * (e.g. JSON.Obj key serialization) emit a string straight from a buffer slice
25
+ * without first materializing a heap `string`.
26
+ */
27
+ export function serializeStringRange(srcPtr: usize, srcSize: usize): void {
21
28
  bs.proposeSize(srcSize + 4);
22
- let srcPtr = changetype<usize>(src);
23
29
  const srcEnd = srcPtr + srcSize;
24
30
 
25
31
  store<u16>(bs.offset, QUOTE);
@@ -79,9 +85,7 @@ import { serializeStruct } from "./struct";
79
85
  store<u16>(bs.offset, QUOTE);
80
86
  bs.offset += 2;
81
87
  }
82
-
83
- // @ts-ignore: inline
84
- @inline function write_u_escape(code: u16): void {
88
+ function write_u_escape(code: u16): void {
85
89
  bs.growSize(10);
86
90
  store<u32>(bs.offset, U_MARKER); // "\u"
87
91
  store<u64>(bs.offset, u16_to_hex4_swar(code), 4);
@@ -3,8 +3,6 @@ import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../custom/chars";
3
3
  import { serializeFloat32Unsafe, serializeFloat64Unsafe } from "./float";
4
4
  import { serializeIntegerUnsafe } from "./integer";
5
5
 
6
-
7
- @inline
8
6
  function maxIntegerBytes<T extends number>(): u32 {
9
7
  if (sizeof<T>() == 1) return isSigned<T>() ? 8 : 6;
10
8
  if (sizeof<T>() == 2) return isSigned<T>() ? 12 : 10;
@@ -12,8 +10,6 @@ function maxIntegerBytes<T extends number>(): u32 {
12
10
  return isSigned<T>() ? 42 : 40;
13
11
  }
14
12
 
15
-
16
- @inline
17
13
  function reserveTypedArray<T extends ArrayLike<number>>(len: i32): void {
18
14
  if (len <= 0) return;
19
15
  if (isFloat<valueof<T>>()) {
@@ -23,8 +19,6 @@ function reserveTypedArray<T extends ArrayLike<number>>(len: i32): void {
23
19
  }
24
20
  }
25
21
 
26
-
27
- @inline
28
22
  function serializeTypedArrayElement<T extends ArrayLike<number>>(
29
23
  src: T,
30
24
  index: i32,
@@ -207,9 +207,7 @@ export function serializeString_SIMD(src: string): void {
207
207
  store<u16>(bs.offset, 34); // "
208
208
  bs.offset += 2;
209
209
  }
210
-
211
- // @ts-expect-error: @inline is a valid decorator
212
- @inline function write_u_escape(code: u16): void {
210
+ function write_u_escape(code: u16): void {
213
211
  bs.growSize(10);
214
212
  store<u32>(bs.offset, U_MARKER); // "\u"
215
213
  store<u64>(bs.offset, u16_to_hex4_swar(code), 4);
@@ -174,16 +174,14 @@ export function serializeString_SWAR(src: string): void {
174
174
  bs.offset += 2;
175
175
  }
176
176
 
177
- // @ts-expect-error: @inline is a valid decorator
178
- @inline function write_u_escape(code: u16): void {
177
+ function write_u_escape(code: u16): void {
179
178
  bs.growSize(10);
180
179
  store<u32>(bs.offset, U_MARKER); // "\u"
181
180
  store<u64>(bs.offset, u16_to_hex4_swar(code), 4);
182
181
  bs.offset += 12;
183
182
  }
184
183
 
185
- // @ts-expect-error: @inline is a valid decorator
186
- @inline export function detect_escapable_u64_swar_safe(block: u64): u64 {
184
+ export function detect_escapable_u64_swar_safe(block: u64): u64 {
187
185
  const hi = block & 0xff00_ff00_ff00_ff00;
188
186
  const lo = block & 0x00ff_00ff_00ff_00ff;
189
187
  // Setting bit 8 of each 16-bit lane (high byte LSB) prevents borrow from a