json-as 1.3.2 → 1.3.4

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 (34) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +3 -2
  3. package/assembly/deserialize/simd/string.ts +20 -0
  4. package/assembly/deserialize/simple/map.ts +2 -0
  5. package/assembly/deserialize/simple/staticarray.ts +1 -0
  6. package/assembly/deserialize/simple/struct.ts +3 -1
  7. package/assembly/deserialize/swar/array/bool.ts +1 -0
  8. package/assembly/deserialize/swar/array/generic.ts +2 -1
  9. package/assembly/deserialize/swar/array/integer.ts +1 -0
  10. package/assembly/deserialize/swar/array/object.ts +1 -0
  11. package/assembly/deserialize/swar/array/shared.ts +18 -3
  12. package/assembly/deserialize/swar/array/string.ts +1 -0
  13. package/assembly/deserialize/swar/array/struct.ts +1 -0
  14. package/assembly/deserialize/swar/array.ts +1 -0
  15. package/assembly/deserialize/swar/string.ts +21 -2
  16. package/assembly/index.d.ts +1 -0
  17. package/assembly/index.ts +27 -22
  18. package/assembly/serialize/simd/string.ts +43 -16
  19. package/assembly/serialize/simple/array.ts +48 -6
  20. package/assembly/serialize/simple/bool.ts +12 -0
  21. package/assembly/serialize/simple/float.ts +16 -0
  22. package/assembly/serialize/simple/integer.ts +6 -0
  23. package/assembly/serialize/simple/set.ts +59 -5
  24. package/assembly/serialize/simple/staticarray.ts +57 -3
  25. package/assembly/serialize/simple/typedarray.ts +29 -9
  26. package/assembly/serialize/swar/string.ts +165 -18
  27. package/assembly/tsconfig.json +2 -2
  28. package/assembly/util/dragonbox-cache.ts +2 -1320
  29. package/assembly/util/dragonbox.ts +63 -35
  30. package/lib/as-bs.ts +26 -18
  31. package/package.json +11 -10
  32. package/transform/lib/index.d.ts.map +1 -1
  33. package/transform/lib/index.js +288 -73
  34. package/transform/lib/index.js.map +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,11 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ ## 2026-04-28 - 1.3.3
6
+
7
+ - perf: made deserialization 200% to 300% faster
8
+ - chore: enable JSON_USE_FAST_PATH by default
9
+
5
10
  ## 2026-04-13 - 1.3.2
6
11
 
7
12
  - fix: remove the fast double parser dependency and return float deserialization to the local legacy parser path
package/README.md CHANGED
@@ -542,13 +542,13 @@ The following charts compare JSON-AS (both SWAR and SIMD variants) against JavaS
542
542
  Instead of using flags for setting options, `json-as` is configured by environmental variables.
543
543
  Here's a short list:
544
544
 
545
- **JSON_CACHE** (default: 0) - Enables caching costly strings based on hit frequency. May boost string serialization in excess of 22 GB/s.
545
+ **JSON_CACHE** (default: 0) - Enables and sizes string cache. Supports `true|false`, raw bytes (`JSON_CACHE=1048576`), bits (`JSON_CACHE=512kb`, `2mb`, `1gb`), and bytes (`JSON_CACHE=64KB`, `2MB`, `1GB`). May boost string serialization in excess of 22 GB/s.
546
546
 
547
547
  **JSON_DEBUG** (default: 0) - Sets the debug level. May be within range `0-3`
548
548
 
549
549
  **JSON_MODE** (default: SWAR) - Selects which mode should be used. Can be `NAIVE,SWAR,SIMD`. Note that `--enable simd` may be required.
550
550
 
551
- **JSON_USE_FAST_PATH** (default: 0) - When set to `1`, the transform emits the fast `__DESERIALIZE` implementation for generated structs. When unset or `0`, it emits only the slow path. See [FAST_PATH_DESERIALIZE.md](./FAST_PATH_DESERIALIZE.md) for the current support matrix, known gaps, and dedicated test command.
551
+ **JSON_USE_FAST_PATH** (default: 1) - The transform emits the fast `__DESERIALIZE` implementation for generated structs by default. Set to `0`, `false`, `off`, or `no` to force slow-path-only output. See [FAST_PATH_DESERIALIZE.md](./FAST_PATH_DESERIALIZE.md) for the current support matrix, known gaps, and dedicated test command.
552
552
 
553
553
  **JSON_WRITE** (default: "") - Select a series of files to output after transform and optimization passes have completed for easy inspection. Usage: `JSON_WRITE=.path-to-file-a.ts,./path-to-file-b.ts`
554
554
 
@@ -629,6 +629,7 @@ A few companies and open-source projects use json-as!
629
629
  | [Klave](https://klave.com) | Privacy-first platform |
630
630
  | [Bifrost](https://github.com/maximhq/bifrost) | Open source project by Maxim HQ |
631
631
  | [Massa Labs](https://github.com/massalabs) | Massa blockchain tooling |
632
+ | [Seda Protocol](https://github.com/sedaprotocl) | Wasm-based blockchain VM |
632
633
 
633
634
  ## License
634
635
 
@@ -206,6 +206,26 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
206
206
  srcStart += 2;
207
207
  srcEnd -= 2;
208
208
  const payloadStart = srcStart;
209
+ do {
210
+ const srcEnd16Fast = srcEnd - 16;
211
+
212
+ while (srcStart < srcEnd16Fast) {
213
+ const block = load<v128>(srcStart);
214
+ if (i16x8.bitmask(i16x8.eq(block, SPLAT_5C)) != 0) break;
215
+ srcStart += 16;
216
+ }
217
+ if (srcStart < srcEnd16Fast) break;
218
+
219
+ while (srcStart < srcEnd) {
220
+ if (load<u16>(srcStart) == BACK_SLASH) break;
221
+ srcStart += 2;
222
+ }
223
+ if (srcStart < srcEnd) break;
224
+
225
+ return copyStringFromSource_SIMD(payloadStart, srcEnd - payloadStart);
226
+ } while (false);
227
+
228
+ srcStart = payloadStart;
209
229
  const srcEnd16 = srcEnd - 16;
210
230
 
211
231
  while (srcStart < srcEnd16) {
@@ -170,6 +170,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
170
170
  return out;
171
171
  }
172
172
 
173
+
173
174
  @inline export function deserializeMapInto<T extends Map<any, any>>(srcStart: usize, srcEnd: usize, out: T): usize {
174
175
  changetype<nonnull<T>>(out).clear();
175
176
 
@@ -209,6 +210,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
209
210
  throw new Error("Failed to parse JSON!");
210
211
  }
211
212
 
213
+
212
214
  @inline export function deserializeMapField<T extends Map<any, any>>(srcStart: usize, srcEnd: usize, dstObj: usize, dstOffset: usize = 0): usize {
213
215
  let out = load<T>(dstObj, dstOffset);
214
216
  if (!changetype<usize>(out)) {
@@ -72,6 +72,7 @@ export function deserializeStaticArray<T extends StaticArray<any>>(srcStart: usi
72
72
  throw new Error("Could not parse static array of type " + nameof<T>() + "!");
73
73
  }
74
74
 
75
+
75
76
  @inline export function deserializeStaticArrayField<T extends StaticArray<any>>(srcStart: usize, srcEnd: usize, dstObj: usize, dstOffset: usize = 0): usize {
76
77
  const valueEnd = scanValueEnd(srcStart, srcEnd);
77
78
  if (!valueEnd) throw new Error("Failed to parse JSON!");
@@ -7,9 +7,11 @@ export function deserializeStruct<T>(srcStart: usize, srcEnd: usize, dst: usize)
7
7
  if (isDefined(out.__DESERIALIZE_FAST)) {
8
8
  // @ts-ignore: supplied by transform
9
9
  out.__DESERIALIZE_FAST(srcStart, srcEnd, out);
10
- } else {
10
+ } else if (isDefined(out.__DESERIALIZE_SLOW)) {
11
11
  // @ts-ignore: supplied by transform
12
12
  out.__DESERIALIZE_SLOW(srcStart, srcEnd, out);
13
+ } else {
14
+ throw new Error("Missing __DESERIALIZE_FAST/__DESERIALIZE_SLOW");
13
15
  }
14
16
  return out;
15
17
  }
@@ -45,6 +45,7 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
45
45
  throw new Error("Failed to parse JSON!");
46
46
  }
47
47
 
48
+
48
49
  @inline export function deserializeBooleanArrayField<T extends boolean[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
49
50
  return deserializeBooleanArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
50
51
  }
@@ -2,8 +2,8 @@ import { JSON } from "../../..";
2
2
  import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
3
3
  import { ensureArrayElementSlot, ensureArrayField, scanValueEnd } from "./shared";
4
4
 
5
- @inline export function deserializeGenericArrayInto<T extends unknown[]>(srcStart: usize, srcEnd: usize, out: T): usize {
6
5
 
6
+ @inline export function deserializeGenericArrayInto<T extends unknown[]>(srcStart: usize, srcEnd: usize, out: T): usize {
7
7
  if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) throw new Error("Failed to parse JSON!");
8
8
  srcStart += 2;
9
9
  if (srcStart >= srcEnd) throw new Error("Failed to parse JSON!");
@@ -35,6 +35,7 @@ import { ensureArrayElementSlot, ensureArrayField, scanValueEnd } from "./shared
35
35
  throw new Error("Failed to parse JSON!");
36
36
  }
37
37
 
38
+
38
39
  @inline export function deserializeGenericArrayField<T extends unknown[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
39
40
  return deserializeGenericArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
40
41
  }
@@ -459,6 +459,7 @@ export function deserializeIntegerArray_SWAR<T extends number[]>(srcStart: usize
459
459
  throw new Error("Failed to parse JSON!");
460
460
  }
461
461
 
462
+
462
463
  @inline export function deserializeIntegerArrayField<T extends number[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
463
464
  return deserializeIntegerArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
464
465
  }
@@ -55,6 +55,7 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
55
55
  throw new Error("Failed to parse JSON!");
56
56
  }
57
57
 
58
+
58
59
  @inline export function deserializeObjectArrayField<T extends unknown[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
59
60
  return deserializeObjectArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
60
61
  }
@@ -1,4 +1,5 @@
1
1
  import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA, QUOTE } from "../../../custom/chars";
2
+ import { isUnescapedQuote } from "../../../util";
2
3
 
3
4
 
4
5
  @inline export function ensureArrayField<T extends Array<any>>(fieldPtr: usize): T {
@@ -10,6 +11,7 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
10
11
  return out;
11
12
  }
12
13
 
14
+
13
15
  @inline export function ensureArrayFieldAt<T extends Array<any>>(dstObj: usize, dstOffset: usize): T {
14
16
  let out = load<T>(dstObj, dstOffset);
15
17
  if (!changetype<usize>(out)) {
@@ -57,7 +59,10 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
57
59
  mask &= mask - 1;
58
60
  const srcIdx = srcStart + laneIdx;
59
61
  const char = load<u16>(srcIdx);
60
- if (char == QUOTE) return srcIdx + 2;
62
+ if (char == QUOTE) {
63
+ if (isUnescapedQuote(srcIdx)) return srcIdx + 2;
64
+ continue;
65
+ }
61
66
  if (char == BACK_SLASH) break;
62
67
  } while (mask !== 0);
63
68
 
@@ -66,7 +71,7 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
66
71
 
67
72
  while (srcStart < srcEnd) {
68
73
  const char = load<u16>(srcStart);
69
- if (char == QUOTE && load<u16>(srcStart - 2) != BACK_SLASH) return srcStart + 2;
74
+ if (char == QUOTE && isUnescapedQuote(srcStart)) return srcStart + 2;
70
75
  srcStart += 2;
71
76
  }
72
77
 
@@ -74,6 +79,16 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
74
79
  }
75
80
 
76
81
 
82
+ @inline function scanQuotedValueEndScalar(srcStart: usize, srcEnd: usize): usize {
83
+ srcStart += 2;
84
+ while (srcStart < srcEnd) {
85
+ if (load<u16>(srcStart) == QUOTE && isUnescapedQuote(srcStart)) return srcStart + 2;
86
+ srcStart += 2;
87
+ }
88
+ return 0;
89
+ }
90
+
91
+
77
92
  @inline export function scanValueEnd(srcStart: usize, srcEnd: usize): usize {
78
93
  if (srcStart >= srcEnd) return 0;
79
94
  const first = load<u16>(srcStart);
@@ -86,7 +101,7 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
86
101
  while (ptr < srcEnd) {
87
102
  const code = load<u16>(ptr);
88
103
  if (code == QUOTE) {
89
- ptr = scanQuotedValueEnd_SWAR(ptr, srcEnd);
104
+ ptr = srcEnd - ptr < 256 ? scanQuotedValueEndScalar(ptr, srcEnd) : scanQuotedValueEnd_SWAR(ptr, srcEnd);
90
105
  if (!ptr) return 0;
91
106
  continue;
92
107
  }
@@ -37,6 +37,7 @@ import { deserializeStringField_SWAR } from "../string";
37
37
  throw new Error("Failed to parse JSON!");
38
38
  }
39
39
 
40
+
40
41
  @inline export function deserializeStringArrayField<T extends string[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
41
42
  return deserializeStringArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
42
43
  }
@@ -55,6 +55,7 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
55
55
  throw new Error("Failed to parse JSON!");
56
56
  }
57
57
 
58
+
58
59
  @inline export function deserializeStructArrayField<T extends unknown[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
59
60
  return deserializeStructArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
60
61
  }
@@ -62,6 +62,7 @@ import { deserializeStructArrayInto } from "./array/struct";
62
62
  }
63
63
  }
64
64
 
65
+
65
66
  @inline export function deserializeArrayInto_SWAR<T extends unknown[]>(srcStart: usize, srcEnd: usize, out: T): usize {
66
67
  if (isString<valueof<T>>()) {
67
68
  return deserializeStringArrayInto<T>(srcStart, srcEnd, out);
@@ -149,6 +149,27 @@ export function deserializeString_SWAR(srcStart: usize, srcEnd: usize): string {
149
149
  srcStart += 2;
150
150
  srcEnd -= 2;
151
151
  const payloadStart = srcStart;
152
+ do {
153
+ const srcEnd16Fast = srcEnd - 16;
154
+
155
+ while (srcStart < srcEnd16Fast) {
156
+ const m0 = inline.always(backslash_mask_unsafe(load<u64>(srcStart)));
157
+ const m1 = inline.always(backslash_mask_unsafe(load<u64>(srcStart, 8)));
158
+ if ((m0 | m1) != 0) break;
159
+ srcStart += 16;
160
+ }
161
+ if (srcStart < srcEnd16Fast) break;
162
+
163
+ while (srcStart < srcEnd) {
164
+ if (load<u16>(srcStart) == BACK_SLASH) break;
165
+ srcStart += 2;
166
+ }
167
+ if (srcStart < srcEnd) break;
168
+
169
+ return copyStringFromSource(payloadStart, srcEnd - payloadStart);
170
+ } while (false);
171
+
172
+ srcStart = payloadStart;
152
173
  const srcEnd8 = srcEnd - 8;
153
174
 
154
175
  while (srcStart < srcEnd8) {
@@ -209,7 +230,6 @@ export function deserializeString_SWAR(srcStart: usize, srcEnd: usize): string {
209
230
  memory.copy(stringPtr, srcStart, byteLength);
210
231
  }
211
232
 
212
-
213
233
  // @ts-expect-error: @inline is a valid decorator
214
234
  @inline function deserializeEscapedStringContinuation_SWAR(lastPtr: usize, srcStart: usize, srcEnd: usize, dstFieldPtr: usize, outStart: usize): usize {
215
235
  const srcEnd8 = srcEnd - 8;
@@ -630,7 +650,6 @@ export function deserializeStringField_SWAR<T extends string | null>(srcStart: u
630
650
  return srcStart;
631
651
  }
632
652
 
633
-
634
653
  /**
635
654
  * Computes a per-byte mask identifying ASCII backslash or quote bytes.
636
655
  *
@@ -52,3 +52,4 @@ declare enum JSONMode {
52
52
 
53
53
  declare const JSON_MODE: JSONMode;
54
54
  declare const JSON_CACHE: bool;
55
+ declare const JSON_CACHE_SIZE: usize;
package/assembly/index.ts CHANGED
@@ -17,18 +17,7 @@ import { serializeSet } from "./serialize/index/set";
17
17
  import { deserializeSet } from "./deserialize/index/set";
18
18
  import { serializeStaticArray } from "./serialize/index/staticarray";
19
19
  import { deserializeStaticArray } from "./deserialize/index/staticarray";
20
- import {
21
- BRACE_LEFT,
22
- BRACE_RIGHT,
23
- BRACKET_LEFT,
24
- BRACKET_RIGHT,
25
- COMMA,
26
- NULL_WORD,
27
- QUOTE,
28
- NULL_WORD_U64,
29
- TRUE_WORD_U64,
30
- FALSE_WORD_U64,
31
- } from "./custom/chars";
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";
32
21
  import { itoa_buffered } from "util/number";
33
22
  import { serializeBool } from "./serialize/index/bool";
34
23
  import { serializeInteger } from "./serialize/index/integer";
@@ -36,7 +25,7 @@ import { serializeFloat, serializeFloat32, serializeFloat64 } from "./serialize/
36
25
  import { dragonbox_f32_buffered, dragonbox_f64_buffered } from "./util/dragonbox";
37
26
  import { serializeStruct } from "./serialize/index/struct";
38
27
  import { ptrToStr } from "./util/ptrToStr";
39
- import { atoi, bytes, scanStringEnd } from "./util";
28
+ import { atoi, bytes, isUnescapedQuote as utilIsUnescapedQuote, scanStringEnd } from "./util";
40
29
  import { deserializeArbitrary } from "./deserialize/index/arbitrary";
41
30
  import { serializeObject } from "./serialize/index/object";
42
31
  import { deserializeObject } from "./deserialize/index/object";
@@ -244,14 +233,19 @@ export namespace JSON {
244
233
  } else if (isDefined(type.__DESERIALIZE_SLOW) || isDefined(type.__DESERIALIZE_FAST)) {
245
234
  const out = changetype<nonnull<T>>(__new(offsetof<nonnull<T>>(), idof<nonnull<T>>()));
246
235
  // @ts-expect-error: Defined by transform
247
- if (isDefined(type.__INITIALIZE)) out.__INITIALIZE();
248
- // @ts-expect-error: Defined by transform
249
236
  if (isDefined(type.__DESERIALIZE_FAST)) {
250
237
  // @ts-expect-error: Defined by transform
251
- out.__DESERIALIZE_FAST(dataPtr, dataPtr + dataSize, out);
238
+ if (out.__DESERIALIZE_FAST(dataPtr, dataPtr + dataSize, out) == dataPtr + dataSize) return out;
252
239
  // @ts-expect-error: Defined by transform
253
- } else out.__DESERIALIZE_SLOW(dataPtr, dataPtr + dataSize, out);
254
- return out;
240
+ }
241
+ if (isDefined(type.__INITIALIZE)) out.__INITIALIZE();
242
+ // @ts-expect-error: Defined by transform
243
+ if (isDefined(type.__DESERIALIZE_SLOW)) {
244
+ // @ts-expect-error: Defined by transform
245
+ out.__DESERIALIZE_SLOW(dataPtr, dataPtr + dataSize, out);
246
+ return out;
247
+ }
248
+ throw new Error(`No deserialize method defined for type ${type}`);
255
249
  }
256
250
  if (type instanceof StaticArray) {
257
251
  // @ts-expect-error
@@ -956,12 +950,19 @@ export namespace JSON {
956
950
  } else if (isDefined(type.__DESERIALIZE_SLOW) || isDefined(type.__DESERIALIZE_FAST)) {
957
951
  const out = changetype<nonnull<T>>(dst || __new(offsetof<nonnull<T>>(), idof<nonnull<T>>()));
958
952
  // @ts-expect-error: Defined by transform
959
- if (isDefined(type.__INITIALIZE)) out.__INITIALIZE();
953
+ if (isDefined(type.__DESERIALIZE_FAST)) {
954
+ // @ts-expect-error: Defined by transform
955
+ if (out.__DESERIALIZE_FAST(srcStart, srcEnd, out) == srcEnd) return out;
956
+ }
960
957
  // @ts-expect-error: Defined by transform
961
- if (isDefined(type.__DESERIALIZE_FAST)) out.__DESERIALIZE_FAST(srcStart, srcEnd, out);
958
+ if (isDefined(type.__INITIALIZE)) out.__INITIALIZE();
962
959
  // @ts-expect-error: Defined by transform
963
- else out.__DESERIALIZE_SLOW(srcStart, srcEnd, out);
964
- return out;
960
+ if (isDefined(type.__DESERIALIZE_SLOW)) {
961
+ // @ts-expect-error: Defined by transform
962
+ out.__DESERIALIZE_SLOW(srcStart, srcEnd, out);
963
+ return out;
964
+ }
965
+ throw new Error(`No deserialize method defined for type ${type}`);
965
966
  }
966
967
  if (type instanceof StaticArray) {
967
968
  // @ts-expect-error: type
@@ -1025,6 +1026,10 @@ export namespace JSON {
1025
1026
  return code == 0x20 || code - 9 <= 4;
1026
1027
  }
1027
1028
  // @ts-expect-error: decorator
1029
+ @inline export function isUnescapedQuote(ptr: usize): boolean {
1030
+ return utilIsUnescapedQuote(ptr);
1031
+ }
1032
+ // @ts-expect-error: decorator
1028
1033
  @inline export function scanValueEnd(srcStart: usize, srcEnd: usize): usize {
1029
1034
  if (srcStart >= srcEnd) return 0;
1030
1035
  let ptr = srcStart;
@@ -1,5 +1,5 @@
1
1
  import { OBJECT, TOTAL_OVERHEAD } from "rt/common";
2
- import { bs, sc } from "../../../lib/as-bs";
2
+ import { bs } from "../../../lib/as-bs";
3
3
  import { BACK_SLASH } from "../../custom/chars";
4
4
  import { SERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
5
5
  import { u16_to_hex4_swar } from "../../util/swar";
@@ -21,20 +21,49 @@ import { u16_to_hex4_swar } from "../../util/swar";
21
21
  */
22
22
  export function serializeString_SIMD(src: string): void {
23
23
  let srcStart = changetype<usize>(src);
24
- if (isDefined(JSON_CACHE)) {
25
- // check cache
26
- const e = unchecked(sc.entries[i32((srcStart >> 4) & sc.CACHE_MASK)]);
27
- if (e.key == srcStart) {
28
- // bs.offset += e.len;
29
- // bs.stackSize += e.len;
30
- bs.cacheOutput = e.ptr;
31
- bs.cacheOutputLen = e.len;
32
- return;
33
- }
34
- }
24
+ const srcInitial = srcStart;
35
25
 
36
26
  const srcSize = changetype<OBJECT>(srcStart - TOTAL_OVERHEAD).rtSize;
37
27
  const srcEnd = srcStart + srcSize;
28
+ do {
29
+ const srcEnd16Fast = srcEnd - 16;
30
+ bs.proposeSize(srcSize + 4);
31
+
32
+ const dstStart = bs.offset;
33
+ let dst = dstStart + 2;
34
+
35
+ while (srcStart < srcEnd16Fast) {
36
+ const block = load<v128>(srcStart);
37
+ const eq22 = i16x8.eq(block, SPLAT_0022);
38
+ const eq5C = i16x8.eq(block, SPLAT_005C);
39
+ const lt20 = i16x8.lt_u(block, SPLAT_0020);
40
+ const gteD8 = i8x16.gt_u(block, SPLAT_FFD8);
41
+
42
+ const mask = i8x16.bitmask(v128.or(eq22, v128.or(eq5C, v128.or(lt20, gteD8))));
43
+ if (mask != 0) break;
44
+
45
+ store<v128>(dst, block);
46
+ srcStart += 16;
47
+ dst += 16;
48
+ }
49
+ if (srcStart < srcEnd16Fast) break;
50
+
51
+ while (srcStart <= srcEnd - 2) {
52
+ const code = load<u16>(srcStart);
53
+ if (code > 0x7f || code == BACK_SLASH || code == 34 || code < 32) break;
54
+ store<u16>(dst, code);
55
+ srcStart += 2;
56
+ dst += 2;
57
+ }
58
+ if (srcStart <= srcEnd - 2) break;
59
+
60
+ store<u16>(dstStart, 34); // "
61
+ store<u16>(dst, 34); // "
62
+ bs.offset = dst + 2;
63
+ return;
64
+ } while (false);
65
+
66
+ srcStart = srcInitial;
38
67
  const srcEnd16 = srcEnd - 16;
39
68
 
40
69
  bs.proposeSize(srcSize + 4);
@@ -43,7 +72,6 @@ export function serializeString_SIMD(src: string): void {
43
72
 
44
73
  while (srcStart < srcEnd16) {
45
74
  const block = load<v128>(srcStart);
46
- store<v128>(bs.offset, block);
47
75
 
48
76
  const eq22 = i16x8.eq(block, SPLAT_0022);
49
77
  const eq5C = i16x8.eq(block, SPLAT_005C);
@@ -58,12 +86,13 @@ export function serializeString_SIMD(src: string): void {
58
86
  let mask = i8x16.bitmask(v128.or(eq22, v128.or(eq5C, v128.or(lt20, gteD8))));
59
87
 
60
88
  if (mask == 0) {
89
+ store<v128>(bs.offset, block);
61
90
  bs.offset += 16;
62
91
  srcStart += 16;
63
92
  continue;
64
93
  }
65
94
 
66
- bs.growSize(popcnt(mask) * 10 + 12);
95
+ store<v128>(bs.offset, block);
67
96
 
68
97
  do {
69
98
  const laneIdx = ctz(mask);
@@ -173,8 +202,6 @@ export function serializeString_SIMD(src: string): void {
173
202
 
174
203
  store<u16>(bs.offset, 34); // "
175
204
  bs.offset += 2;
176
-
177
- if (isDefined(JSON_CACHE)) sc.insertCached(changetype<usize>(src), srcStart, srcSize);
178
205
  }
179
206
 
180
207
  // @ts-expect-error: @inline is a valid decorator
@@ -1,13 +1,53 @@
1
1
  import { bs } from "../../../lib/as-bs";
2
2
  import { COMMA, BRACKET_RIGHT, BRACKET_LEFT } from "../../custom/chars";
3
3
  import { JSON } from "../..";
4
- import { serializeFloat32, serializeFloat64 } from "./float";
4
+ import { serializeBoolUnsafe } from "./bool";
5
+ import { serializeFloat32Unsafe, serializeFloat64Unsafe } from "./float";
6
+ import { serializeIntegerUnsafe } from "./integer";
7
+ import { serializeString } from "../index/string";
8
+
9
+
10
+ @inline
11
+ function maxIntegerBytes<T extends number>(): u32 {
12
+ if (sizeof<T>() == 1) return isSigned<T>() ? 8 : 6;
13
+ if (sizeof<T>() == 2) return isSigned<T>() ? 12 : 10;
14
+ if (sizeof<T>() == 4) return isSigned<T>() ? 22 : 20;
15
+ return isSigned<T>() ? 42 : 40;
16
+ }
17
+
18
+
19
+ @inline
20
+ function reservePrimitiveArray<T>(len: i32): void {
21
+ if (len <= 0) return;
22
+ if (isBoolean<T>()) {
23
+ bs.proposeSize(4 + <u32>len * 12);
24
+ } else if (isInteger<T>()) {
25
+ bs.proposeSize(4 + <u32>len * (maxIntegerBytes<T>() + 2));
26
+ } else if (isFloat<T>()) {
27
+ bs.proposeSize(4 + <u32>len * (sizeof<T>() == 4 ? 34 : 66));
28
+ } else {
29
+ bs.proposeSize(4 + <u32>(len - 1) * 2);
30
+ }
31
+ }
32
+
5
33
 
6
34
  @inline
7
35
  function serializeArrayElement<T>(value: T): void {
36
+ if (isString<T>()) {
37
+ serializeString(value as string);
38
+ return;
39
+ }
40
+ if (isBoolean<T>()) {
41
+ serializeBoolUnsafe(<bool>value);
42
+ return;
43
+ }
44
+ if (isInteger<T>()) {
45
+ serializeIntegerUnsafe<T>(value);
46
+ return;
47
+ }
8
48
  if (isFloat<T>()) {
9
- if (sizeof<T>() == 4) serializeFloat32(<f32>value);
10
- else serializeFloat64(<f64>value);
49
+ if (sizeof<T>() == 4) serializeFloat32Unsafe(<f32>value);
50
+ else serializeFloat64Unsafe(<f64>value);
11
51
  return;
12
52
  }
13
53
  JSON.__serialize<T>(value);
@@ -23,9 +63,11 @@ export function serializeArray<T extends any[]>(src: T): void {
23
63
  bs.offset += 4;
24
64
  return;
25
65
  }
26
- bs.proposeSize(4 + <u32>(len - 1) * 2);
27
- // {} = 4
28
- // xi, = n << 1
66
+ if (isBoolean<valueof<T>>() || isInteger<valueof<T>>() || isFloat<valueof<T>>() || isString<valueof<T>>()) {
67
+ reservePrimitiveArray<valueof<T>>(len);
68
+ } else {
69
+ bs.proposeSize(4 + <u32>(len - 1) * 2);
70
+ }
29
71
 
30
72
  store<u16>(bs.offset, BRACKET_LEFT);
31
73
  bs.offset += 2;
@@ -4,6 +4,18 @@ import { bs } from "../../../lib/as-bs";
4
4
  * @param data data to serialize
5
5
  * @returns void
6
6
  */
7
+ @inline
8
+ export function serializeBoolUnsafe(data: bool): void {
9
+ if (data === true) {
10
+ store<u64>(bs.offset, 28429475166421108);
11
+ bs.offset += 8;
12
+ } else {
13
+ store<u64>(bs.offset, 32370086184550502);
14
+ store<u64>(bs.offset, 101, 8);
15
+ bs.offset += 10;
16
+ }
17
+ }
18
+
7
19
  export function serializeBool(data: bool): void {
8
20
  if (data === true) {
9
21
  bs.proposeSize(8);
@@ -1,6 +1,21 @@
1
1
  import { bs } from "../../../lib/as-bs";
2
2
  import { dragonbox_f32_buffered, dragonbox_f64_buffered } from "../../util/dragonbox";
3
3
 
4
+
5
+ @inline
6
+ export function serializeFloat32Unsafe(data: f32): void {
7
+ const size = dragonbox_f32_buffered(bs.offset, data) << 1;
8
+ bs.offset += size;
9
+ }
10
+
11
+
12
+ @inline
13
+ export function serializeFloat64Unsafe(data: f64): void {
14
+ const size = dragonbox_f64_buffered(bs.offset, data) << 1;
15
+ bs.offset += size;
16
+ }
17
+
18
+
4
19
  @inline
5
20
  export function serializeFloat32(data: f32): void {
6
21
  bs.ensureSize(64);
@@ -9,6 +24,7 @@ export function serializeFloat32(data: f32): void {
9
24
  bs.offset += size;
10
25
  }
11
26
 
27
+
12
28
  @inline
13
29
  export function serializeFloat64(data: f64): void {
14
30
  bs.ensureSize(64);
@@ -1,6 +1,12 @@
1
1
  import { bs } from "../../../lib/as-bs";
2
2
  import { itoa_buffered } from "util/number";
3
3
 
4
+ @inline
5
+ export function serializeIntegerUnsafe<T extends number>(data: T): void {
6
+ const bytesWritten = itoa_buffered(bs.offset, data) << 1;
7
+ bs.offset += bytesWritten;
8
+ }
9
+
4
10
  // @ts-ignore: inline
5
11
  @inline export function serializeInteger<T extends number>(data: T): void {
6
12
  bs.ensureSize(sizeof<T>() << 3);