json-as 1.3.3 → 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.
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
@@ -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) {
@@ -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 {
@@ -58,7 +59,10 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
58
59
  mask &= mask - 1;
59
60
  const srcIdx = srcStart + laneIdx;
60
61
  const char = load<u16>(srcIdx);
61
- if (char == QUOTE) return srcIdx + 2;
62
+ if (char == QUOTE) {
63
+ if (isUnescapedQuote(srcIdx)) return srcIdx + 2;
64
+ continue;
65
+ }
62
66
  if (char == BACK_SLASH) break;
63
67
  } while (mask !== 0);
64
68
 
@@ -67,7 +71,7 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
67
71
 
68
72
  while (srcStart < srcEnd) {
69
73
  const char = load<u16>(srcStart);
70
- if (char == QUOTE && load<u16>(srcStart - 2) != BACK_SLASH) return srcStart + 2;
74
+ if (char == QUOTE && isUnescapedQuote(srcStart)) return srcStart + 2;
71
75
  srcStart += 2;
72
76
  }
73
77
 
@@ -75,6 +79,16 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
75
79
  }
76
80
 
77
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
+
78
92
  @inline export function scanValueEnd(srcStart: usize, srcEnd: usize): usize {
79
93
  if (srcStart >= srcEnd) return 0;
80
94
  const first = load<u16>(srcStart);
@@ -87,7 +101,7 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
87
101
  while (ptr < srcEnd) {
88
102
  const code = load<u16>(ptr);
89
103
  if (code == QUOTE) {
90
- ptr = scanQuotedValueEnd_SWAR(ptr, srcEnd);
104
+ ptr = srcEnd - ptr < 256 ? scanQuotedValueEndScalar(ptr, srcEnd) : scanQuotedValueEnd_SWAR(ptr, srcEnd);
91
105
  if (!ptr) return 0;
92
106
  continue;
93
107
  }
@@ -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) {
package/assembly/index.ts CHANGED
@@ -25,7 +25,7 @@ import { serializeFloat, serializeFloat32, serializeFloat64 } from "./serialize/
25
25
  import { dragonbox_f32_buffered, dragonbox_f64_buffered } from "./util/dragonbox";
26
26
  import { serializeStruct } from "./serialize/index/struct";
27
27
  import { ptrToStr } from "./util/ptrToStr";
28
- import { atoi, bytes, scanStringEnd } from "./util";
28
+ import { atoi, bytes, isUnescapedQuote as utilIsUnescapedQuote, scanStringEnd } from "./util";
29
29
  import { deserializeArbitrary } from "./deserialize/index/arbitrary";
30
30
  import { serializeObject } from "./serialize/index/object";
31
31
  import { deserializeObject } from "./deserialize/index/object";
@@ -1026,6 +1026,10 @@ export namespace JSON {
1026
1026
  return code == 0x20 || code - 9 <= 4;
1027
1027
  }
1028
1028
  // @ts-expect-error: decorator
1029
+ @inline export function isUnescapedQuote(ptr: usize): boolean {
1030
+ return utilIsUnescapedQuote(ptr);
1031
+ }
1032
+ // @ts-expect-error: decorator
1029
1033
  @inline export function scanValueEnd(srcStart: usize, srcEnd: usize): usize {
1030
1034
  if (srcStart >= srcEnd) return 0;
1031
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,14 +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
+ }
5
32
 
6
33
 
7
34
  @inline
8
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
+ }
9
48
  if (isFloat<T>()) {
10
- if (sizeof<T>() == 4) serializeFloat32(<f32>value);
11
- else serializeFloat64(<f64>value);
49
+ if (sizeof<T>() == 4) serializeFloat32Unsafe(<f32>value);
50
+ else serializeFloat64Unsafe(<f64>value);
12
51
  return;
13
52
  }
14
53
  JSON.__serialize<T>(value);
@@ -24,9 +63,11 @@ export function serializeArray<T extends any[]>(src: T): void {
24
63
  bs.offset += 4;
25
64
  return;
26
65
  }
27
- bs.proposeSize(4 + <u32>(len - 1) * 2);
28
- // {} = 4
29
- // 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
+ }
30
71
 
31
72
  store<u16>(bs.offset, BRACKET_LEFT);
32
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);
@@ -2,6 +2,20 @@ import { bs } from "../../../lib/as-bs";
2
2
  import { dragonbox_f32_buffered, dragonbox_f64_buffered } from "../../util/dragonbox";
3
3
 
4
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
+
5
19
  @inline
6
20
  export function serializeFloat32(data: f32): void {
7
21
  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);
@@ -1,6 +1,34 @@
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 { 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 reservePrimitiveSet<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
+ }
4
32
 
5
33
  export function serializeSet<T extends Set<any>>(src: T): void {
6
34
  const srcSize = src.size;
@@ -10,7 +38,11 @@ export function serializeSet<T extends Set<any>>(src: T): void {
10
38
  bs.offset += 4;
11
39
  return;
12
40
  }
13
- bs.proposeSize(4 + <u32>(srcSize - 1) * 2);
41
+ if (isBoolean<indexof<T>>() || isInteger<indexof<T>>() || isFloat<indexof<T>>() || isString<indexof<T>>()) {
42
+ reservePrimitiveSet<indexof<T>>(srcSize);
43
+ } else {
44
+ bs.proposeSize(4 + <u32>(srcSize - 1) * 2);
45
+ }
14
46
 
15
47
  const values = src.values();
16
48
  store<u16>(bs.offset, BRACKET_LEFT);
@@ -19,15 +51,37 @@ export function serializeSet<T extends Set<any>>(src: T): void {
19
51
  const end = srcSize - 1;
20
52
  for (let i = 0; i < end; i++) {
21
53
  const block = unchecked(values[i]);
22
- // @ts-ignore: type
23
- JSON.__serialize<indexof<T>>(block);
54
+ if (isString<indexof<T>>()) {
55
+ serializeString(block as string);
56
+ } else if (isBoolean<indexof<T>>()) {
57
+ serializeBoolUnsafe(<bool>block);
58
+ } else if (isInteger<indexof<T>>()) {
59
+ serializeIntegerUnsafe<indexof<T>>(block);
60
+ } else if (isFloat<indexof<T>>()) {
61
+ if (sizeof<indexof<T>>() == 4) serializeFloat32Unsafe(<f32>block);
62
+ else serializeFloat64Unsafe(<f64>block);
63
+ } else {
64
+ // @ts-ignore: type
65
+ JSON.__serialize<indexof<T>>(block);
66
+ }
24
67
  store<u16>(bs.offset, COMMA);
25
68
  bs.offset += 2;
26
69
  }
27
70
 
28
71
  const lastBlock = unchecked(values[end]);
29
- // @ts-ignore: type
30
- JSON.__serialize<indexof<T>>(lastBlock);
72
+ if (isString<indexof<T>>()) {
73
+ serializeString(lastBlock as string);
74
+ } else if (isBoolean<indexof<T>>()) {
75
+ serializeBoolUnsafe(<bool>lastBlock);
76
+ } else if (isInteger<indexof<T>>()) {
77
+ serializeIntegerUnsafe<indexof<T>>(lastBlock);
78
+ } else if (isFloat<indexof<T>>()) {
79
+ if (sizeof<indexof<T>>() == 4) serializeFloat32Unsafe(<f32>lastBlock);
80
+ else serializeFloat64Unsafe(<f64>lastBlock);
81
+ } else {
82
+ // @ts-ignore: type
83
+ JSON.__serialize<indexof<T>>(lastBlock);
84
+ }
31
85
  store<u16>(bs.offset, BRACKET_RIGHT);
32
86
  bs.offset += 2;
33
87
  }
@@ -1,6 +1,34 @@
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 { 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 reservePrimitiveStaticArray<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
+ }
4
32
 
5
33
  export function serializeStaticArray<T extends StaticArray<any>>(src: T): void {
6
34
  const len = src.length;
@@ -12,20 +40,46 @@ export function serializeStaticArray<T extends StaticArray<any>>(src: T): void {
12
40
  bs.offset += 4;
13
41
  return;
14
42
  }
15
- bs.proposeSize(4 + <u32>(len - 1) * 2);
43
+ if (isBoolean<valueof<T>>() || isInteger<valueof<T>>() || isFloat<valueof<T>>() || isString<valueof<T>>()) {
44
+ reservePrimitiveStaticArray<valueof<T>>(len);
45
+ } else {
46
+ bs.proposeSize(4 + <u32>(len - 1) * 2);
47
+ }
16
48
 
17
49
  store<u16>(bs.offset, BRACKET_LEFT);
18
50
  bs.offset += 2;
19
51
 
20
52
  while (i < end) {
21
53
  const block = unchecked(src[i++]);
22
- JSON.__serialize<valueof<T>>(block);
54
+ if (isString<valueof<T>>()) {
55
+ serializeString(block as string);
56
+ } else if (isBoolean<valueof<T>>()) {
57
+ serializeBoolUnsafe(<bool>block);
58
+ } else if (isInteger<valueof<T>>()) {
59
+ serializeIntegerUnsafe<valueof<T>>(block);
60
+ } else if (isFloat<valueof<T>>()) {
61
+ if (sizeof<valueof<T>>() == 4) serializeFloat32Unsafe(<f32>block);
62
+ else serializeFloat64Unsafe(<f64>block);
63
+ } else {
64
+ JSON.__serialize<valueof<T>>(block);
65
+ }
23
66
  store<u16>(bs.offset, COMMA);
24
67
  bs.offset += 2;
25
68
  }
26
69
 
27
70
  const lastBlock = unchecked(src[end]);
28
- JSON.__serialize<valueof<T>>(lastBlock);
71
+ if (isString<valueof<T>>()) {
72
+ serializeString(lastBlock as string);
73
+ } else if (isBoolean<valueof<T>>()) {
74
+ serializeBoolUnsafe(<bool>lastBlock);
75
+ } else if (isInteger<valueof<T>>()) {
76
+ serializeIntegerUnsafe<valueof<T>>(lastBlock);
77
+ } else if (isFloat<valueof<T>>()) {
78
+ if (sizeof<valueof<T>>() == 4) serializeFloat32Unsafe(<f32>lastBlock);
79
+ else serializeFloat64Unsafe(<f64>lastBlock);
80
+ } else {
81
+ JSON.__serialize<valueof<T>>(lastBlock);
82
+ }
29
83
  store<u16>(bs.offset, BRACKET_RIGHT);
30
84
  bs.offset += 2;
31
85
  }
@@ -1,16 +1,36 @@
1
1
  import { bs } from "../../../lib/as-bs";
2
2
  import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../custom/chars";
3
- import { serializeFloat32, serializeFloat64 } from "./float";
4
- import { serializeInteger } from "./integer";
3
+ import { serializeFloat32Unsafe, serializeFloat64Unsafe } from "./float";
4
+ import { serializeIntegerUnsafe } from "./integer";
5
+
6
+
7
+ @inline
8
+ function maxIntegerBytes<T extends number>(): u32 {
9
+ if (sizeof<T>() == 1) return isSigned<T>() ? 8 : 6;
10
+ if (sizeof<T>() == 2) return isSigned<T>() ? 12 : 10;
11
+ if (sizeof<T>() == 4) return isSigned<T>() ? 22 : 20;
12
+ return isSigned<T>() ? 42 : 40;
13
+ }
14
+
15
+
16
+ @inline
17
+ function reserveTypedArray<T extends ArrayLike<number>>(len: i32): void {
18
+ if (len <= 0) return;
19
+ if (isFloat<valueof<T>>()) {
20
+ bs.proposeSize(4 + <u32>len * (sizeof<valueof<T>>() == 4 ? 34 : 66));
21
+ } else {
22
+ bs.proposeSize(4 + <u32>len * (maxIntegerBytes<valueof<T>>() + 2));
23
+ }
24
+ }
5
25
 
6
26
 
7
27
  @inline
8
28
  function serializeTypedArrayElement<T extends ArrayLike<number>>(src: T, index: i32): void {
9
29
  if (isFloat<valueof<T>>()) {
10
- if (sizeof<valueof<T>>() == 4) serializeFloat32(<f32>unchecked(src[index]));
11
- else serializeFloat64(<f64>unchecked(src[index]));
30
+ if (sizeof<valueof<T>>() == 4) serializeFloat32Unsafe(<f32>unchecked(src[index]));
31
+ else serializeFloat64Unsafe(<f64>unchecked(src[index]));
12
32
  } else {
13
- serializeInteger<valueof<T>>(unchecked(src[index]));
33
+ serializeIntegerUnsafe<valueof<T>>(unchecked(src[index]));
14
34
  }
15
35
  }
16
36
 
@@ -23,7 +43,7 @@ export function serializeTypedArray<T extends ArrayLike<number>>(src: T): void {
23
43
  bs.offset += 4;
24
44
  return;
25
45
  }
26
- bs.proposeSize(4 + <u32>(len - 1) * 2);
46
+ reserveTypedArray<T>(len);
27
47
 
28
48
  store<u16>(bs.offset, BRACKET_LEFT);
29
49
  bs.offset += 2;
@@ -48,18 +68,18 @@ export function serializeArrayBufferUnsafe(srcStart: usize, byteLength: i32): vo
48
68
  bs.offset += 4;
49
69
  return;
50
70
  }
51
- bs.proposeSize(4 + <u32>end * 2);
71
+ bs.proposeSize(4 + <u32>byteLength * 8);
52
72
 
53
73
  store<u16>(bs.offset, BRACKET_LEFT);
54
74
  bs.offset += 2;
55
75
 
56
76
  for (let i = 0; i < end; i++) {
57
- serializeInteger<u8>(load<u8>(srcStart + <usize>i));
77
+ serializeIntegerUnsafe<u8>(load<u8>(srcStart + <usize>i));
58
78
  store<u16>(bs.offset, COMMA);
59
79
  bs.offset += 2;
60
80
  }
61
81
 
62
- serializeInteger<u8>(load<u8>(srcStart + <usize>end));
82
+ serializeIntegerUnsafe<u8>(load<u8>(srcStart + <usize>end));
63
83
  store<u16>(bs.offset, BRACKET_RIGHT);
64
84
  bs.offset += 2;
65
85
  }