json-as 1.1.19 → 1.1.21

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
@@ -1,5 +1,14 @@
1
1
  # Change Log
2
2
 
3
+ ## 2025-08-14 - 1.1.21
4
+
5
+ - fix: JSON.parse on classes with enums [#155](https://github.com/JairusSW/json-as/issues/155)
6
+ - fix: Resolve memory OOB issue within `serializeFloat` function [#153](https://github.com/JairusSW/json-as/issues/153)
7
+
8
+ ## 2025-07-14 - 1.1.20
9
+
10
+ - feat: enable SIMD string serialization
11
+
3
12
  ## 2025-06-30 - 1.1.19
4
13
 
5
14
  - fix: wrong path used in `readFileSync` when importing from a library
package/README.md CHANGED
@@ -1,34 +1,6 @@
1
- <h6 align="center">
2
- <pre>
3
- <span style="font-size: 0.8em;"> ██ ███████ ██████ ███ ██ █████ ███████
4
- ██ ██ ██ ██ ████ ██ ██ ██ ██
5
- ██ ███████ ██ ██ ██ ██ ██ █████ ███████ ███████
6
- ██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
7
- █████ ███████ ██████ ██ ████ ██ ██ ███████
8
- </span>
9
- AssemblyScript - v1.1.19
10
- </pre>
11
- </h6>
12
-
13
- ## 📝 About
14
-
15
- JSON is the de-facto serialization format of modern web applications, but its serialization and deserialization remain a significant performance bottleneck, especially at scale. Traditional parsing approaches are computationally expensive, adding unnecessary overhead to both clients and servers. This library is designed to mitigate this by leveraging SIMD acceleration and highly optimized transformations.
16
-
17
- ## 📚 Contents
18
-
19
- - [Installation](#-installation)
20
- - [Usage](#-usage)
21
- - [Examples](#-examples)
22
- - [Omitting Fields](#️-omitting-fields)
23
- - [Nullable Primitives](#️-using-nullable-primitives)
24
- - [Unknown or Dynamic Data](#-working-with-unknown-or-dynamic-data)
25
- - [Using Raw JSON Strings](#️-using-raw-json-strings)
26
- - [Using Enums](#️-working-with-enums)
27
- - [Custom Serializers](#️-using-custom-serializers-or-deserializers)
28
- - [Performance](#-performance)
29
- - [Debugging](#-debugging)
30
- - [License](#-license)
31
- - [Contact](#-contact)
1
+ <h1 align="center"><pre> ╦╔═╗╔═╗╔╗╔ ╔═╗╔═╗
2
+ ║╚═╗║ ║║║║══╠═╣╚═╗
3
+ ╚╝╚═╝╚═╝╝╚╝ ╩ ╩╚═╝</pre></h1>
32
4
 
33
5
  ## 💾 Installation
34
6
 
@@ -297,7 +269,7 @@ console.log(JSON.stringify(map));
297
269
 
298
270
  ### 📝 Working with enums
299
271
 
300
- By default, enums arn't supported by `json-as`. However, you can use a workaround:
272
+ By default, enums with values other than `i32` arn't supported by AssemblyScript. However, you can use a workaround:
301
273
 
302
274
  ```typescript
303
275
  namespace Foo {
@@ -391,7 +363,7 @@ These benchmarks compare this library to JavaScript's native `JSON.stringify` an
391
363
  | Test Case | Size | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
392
364
  | --------------- | ---------- | --------------------- | ----------------------- | -------------------- | ---------------------- |
393
365
  | Vector3 Object | 38 bytes | 26,611,226 ops/s | 32,160,804 ops/s | 1,357 MB/s | 1,348 MB/s |
394
- | Alphabet String | 104 bytes | 13,617,021 ops/s | 18,390,804 ops/s | 1,416 MB/s | 1,986 MB/s |
366
+ | Alphabet String | 104 bytes | 16,916,886 ops/s | 18,390,804 ops/s | 1,759 MB/s | 1,986 MB/s |
395
367
  | Small Object | 88 bytes | 24,242,424 ops/s | 12,307,692 ops/s | 2,133 MB/s | 1,083 MB/s |
396
368
  | Medium Object | 494 bytes | 4,060,913 ops/s | 1,396,160 ops/s | 2,006 MB/s | 689.7 MB/s |
397
369
  | Large Object | 3374 bytes | 479,616 ops/s | 132,802 ops/s | 2,074 MB/s | 448.0 MB/s |
@@ -401,7 +373,7 @@ These benchmarks compare this library to JavaScript's native `JSON.stringify` an
401
373
  | Test Case | Size | Serialization (ops/s) | Deserialization (ops/s) | Serialization (MB/s) | Deserialization (MB/s) |
402
374
  | --------------- | ---------- | --------------------- | ----------------------- | -------------------- | ---------------------- |
403
375
  | Vector3 Object | 38 bytes | 8,791,209 ops/s | 5,369,12 ops/s | 357.4 MB/s | 204.3 MB/s |
404
- | Alphabet String | 104 bytes | 13,793,103 ops/s | 14,746,544 ops/s | 1,416 MB/s | 1,592 MB/s |
376
+ | Alphabet String | 104 bytes | 12,830,228 ops/s | 12,140,296 ops/s | 1,334 MB/s | 1,311 MB/s |
405
377
  | Small Object | 88 bytes | 8,376,963 ops/s | 4,968,944 ops/s | 737.1 MB/s | 437.2 MB/s |
406
378
  | Medium Object | 494 bytes | 2,395,210 ops/s | 1,381,693 ops/s | 1,183 MB/s | 682.5 MB/s |
407
379
  | Large Object | 3374 bytes | 222,222 ops/s | 117,233 ops/s | 749.7 MB/s | 395.5 MB/s |
@@ -5,20 +5,28 @@ import { bench } from "./lib/bench";
5
5
  const v1 = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
6
6
  const v2 = '"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"';
7
7
 
8
+ const blackBoxArea = memory.data(64);
8
9
  expect(JSON.stringify(v1)).toBe(v2);
9
10
 
10
11
  bench(
11
12
  "Serialize Alphabet",
12
13
  () => {
13
- inline.always(JSON.stringify(v1));
14
+ blackbox(inline.always(JSON.stringify(blackbox(v1))));
14
15
  },
15
- 64_000_00,
16
+ 24_000_00,
17
+ v1.length << 1,
16
18
  );
17
19
 
18
20
  bench(
19
21
  "Deserialize Alphabet",
20
22
  () => {
21
- inline.always(JSON.parse<string>(v2));
23
+ blackbox(inline.always(JSON.parse<string>(blackbox(v2))));
22
24
  },
23
- 64_000_00,
25
+ 24_000_00,
26
+ v2.length << 1,
24
27
  );
28
+
29
+ function blackbox<T>(value: T): T {
30
+ store<T>(blackBoxArea, value);
31
+ return load<T>(blackBoxArea);
32
+ }
@@ -1,20 +1,32 @@
1
- export function bench(description: string, routine: () => void, ops: u64 = 1_000_000): void {
1
+ export function bench(description: string, routine: () => void, ops: u64 = 1_000_000, bytesPerOp: u64 = 0): void {
2
2
  console.log(" - Benchmarking " + description);
3
+
3
4
  let warmup = ops / 10;
4
5
  while (--warmup) {
5
6
  routine();
6
7
  }
7
- const start = Date.now();
8
+
9
+ const start = performance.now();
10
+
8
11
  let count = ops;
9
- while (count != 0) {
12
+ while (count--) {
10
13
  routine();
11
- count--;
12
14
  }
13
- const elapsed = Date.now() - start;
14
15
 
15
- let opsPerSecond = (ops * 1000) / elapsed;
16
+ const end = performance.now();
17
+ const elapsed = Math.max(1, end - start);
18
+
19
+ const opsPerSecond = f64(ops * 1000) / elapsed;
20
+
21
+ let log = ` Completed benchmark in ${formatNumber(u64(Math.round(elapsed)))}ms at ${formatNumber(u64(Math.round(opsPerSecond)))} ops/s`;
22
+
23
+ if (bytesPerOp > 0) {
24
+ const totalBytes = bytesPerOp * ops;
25
+ const mbPerSec = f64(totalBytes) / (elapsed / 1000) / (1000 * 1000);
26
+ log += ` @ ${formatNumber(u64(Math.round(mbPerSec)))}MB/s`;
27
+ }
16
28
 
17
- console.log(` Completed benchmark in ${formatNumber(elapsed)}ms at ${formatNumber(opsPerSecond)} ops/s\n`);
29
+ console.log(log + "\n");
18
30
  }
19
31
 
20
32
  function formatNumber(n: u64): string {
@@ -8,6 +8,22 @@ describe("Should serialize arbitrary types", () => {
8
8
  expect(JSON.stringify(JSON.Value.from(true))).toBe("true");
9
9
  expect(JSON.stringify(JSON.Value.from(new Vec3()))).toBe('{"x":1.0,"y":2.0,"z":3.0}');
10
10
  expect(JSON.stringify([JSON.Value.from("string"), JSON.Value.from(true), JSON.Value.from(3.14), JSON.Value.from(new Vec3())])).toBe('["string",true,3.14,{"x":1.0,"y":2.0,"z":3.0}]');
11
+
12
+ const o = new JSON.Obj();
13
+ o.set("schema", "http://json-schema.org/draft-07/schema#");
14
+ o.set("additionalProperties", false);
15
+ o.set("properties", new JSON.Obj());
16
+ o.get("properties")!.as<JSON.Obj>().set("duration", new JSON.Obj());
17
+ o.get("properties")!.as<JSON.Obj>().get("duration")!.as<JSON.Obj>().set("default", 10.0);
18
+ o.get("properties")!.as<JSON.Obj>().get("duration")!.as<JSON.Obj>().set("description", "Duration of the operation in seconds");
19
+ o.get("properties")!.as<JSON.Obj>().get("duration")!.as<JSON.Obj>().set("type", "number");
20
+ o.get("properties")!.as<JSON.Obj>().set("steps", new JSON.Obj());
21
+ o.get("properties")!.as<JSON.Obj>().get("steps")!.as<JSON.Obj>().set("default", 5.0);
22
+ o.get("properties")!.as<JSON.Obj>().get("steps")!.as<JSON.Obj>().set("description", "Number of steps in the operation");
23
+ o.get("properties")!.as<JSON.Obj>().get("steps")!.as<JSON.Obj>().set("type", "number");
24
+ o.set("type", "object");
25
+
26
+ expect(o.toString()).toBe('{"schema":"http://json-schema.org/draft-07/schema#","additionalProperties":false,"properties":{"duration":{"default":10.0,"description":"Duration of the operation in seconds","type":"number"},"steps":{"default":5.0,"description":"Number of steps in the operation","type":"number"}},"type":"object"}');
11
27
  });
12
28
 
13
29
  describe("Should deserialize arbitrary types", () => {
@@ -0,0 +1,35 @@
1
+ import { JSON } from "..";
2
+ import { describe, expect } from "./lib";
3
+
4
+ enum Enum1 {
5
+ Zero = 0,
6
+ One = 1,
7
+ Two = 2,
8
+ Three = 3,
9
+ }
10
+
11
+
12
+ @json
13
+ class DataWithEnum {
14
+ v: Enum1 = Enum1.One;
15
+ constructor(v: Enum1) {
16
+ this.v = v;
17
+ }
18
+ }
19
+
20
+ describe("Should serialize enums", () => {
21
+ expect(JSON.stringify<Enum1>(Enum1.One)).toBe("1");
22
+ expect(JSON.stringify<Enum1>(Enum1.Zero)).toBe("0");
23
+ expect(JSON.stringify<DataWithEnum>(new DataWithEnum(Enum1.Two))).toBe('{"v":2}');
24
+ });
25
+
26
+ describe("Should deserialize enums", () => {
27
+ const date1 = JSON.parse<Enum1>("2");
28
+ expect(date1).toBe(Enum1.Two);
29
+
30
+ const date2 = JSON.parse<Enum1>("0");
31
+ expect(date2).toBe(Enum1.Zero);
32
+
33
+ const date3 = JSON.parse<DataWithEnum>('{"v":3}');
34
+ expect(date3.v).toBe(Enum1.Three);
35
+ });
@@ -10,14 +10,13 @@ import { DESERIALIZE_ESCAPE_TABLE, ESCAPE_HEX_TABLE } from "../../globals/tables
10
10
  // todo: optimize and stuff. it works, its not pretty. ideally, i'd like this to be (nearly) branchless
11
11
  export function deserializeString_SIMD(srcStart: usize, srcEnd: usize, dst: usize): usize {
12
12
  const SPLAT_92 = i16x8.splat(92); /* \ */
13
- let src_ptr = srcStart + 2;
13
+ srcStart += 2;
14
+ srcEnd -= 2;
14
15
  let dst_ptr = changetype<usize>(dst);
16
+ const src_end_15 = srcEnd - 15;
15
17
 
16
- const src_end = srcEnd - 2;
17
- const src_end_15 = src_end - 15;
18
-
19
- while (src_ptr < src_end_15) {
20
- const block = v128.load(src_ptr);
18
+ while (srcStart < src_end_15) {
19
+ const block = v128.load(srcStart);
21
20
  v128.store(dst_ptr, block);
22
21
 
23
22
  const backslash_indices = i16x8.eq(block, SPLAT_92);
@@ -26,7 +25,7 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize, dst: usiz
26
25
  while (mask != 0) {
27
26
  const lane_index = ctz(mask) << 1;
28
27
  const dst_offset = dst_ptr + lane_index;
29
- const src_offset = src_ptr + lane_index;
28
+ const src_offset = srcStart + lane_index;
30
29
  const code = load<u16>(src_offset, 2);
31
30
 
32
31
  mask &= mask - 1;
@@ -48,7 +47,7 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize, dst: usiz
48
47
  v128.store(dst_offset, v128.load(src_offset, 4), 2);
49
48
  if (lane_index >= 6) {
50
49
  const bytes_left = lane_index - 4;
51
- src_ptr += bytes_left;
50
+ srcStart += bytes_left;
52
51
  dst_ptr += bytes_left;
53
52
  // console.log(" e: " + (bytes_left).toString())
54
53
  }
@@ -59,25 +58,25 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize, dst: usiz
59
58
  v128.store(dst_offset, v128.load(src_offset, 4), 2);
60
59
  // console.log("Escaped:");
61
60
  if (lane_index == 14) {
62
- src_ptr += 2;
61
+ srcStart += 2;
63
62
  } else {
64
63
  dst_ptr -= 2;
65
64
  }
66
65
  }
67
66
  }
68
67
 
69
- src_ptr += 16;
68
+ srcStart += 16;
70
69
  dst_ptr += 16;
71
70
 
72
- // console.log("src: " + (src_ptr - changetype<usize>(src)).toString());
71
+ // console.log("src: " + (srcStart - changetype<usize>(src)).toString());
73
72
  // console.log("dst: " + (dst_ptr - dst).toString());
74
73
  }
75
- while (src_ptr < src_end) {
76
- let code = load<u16>(src_ptr);
74
+ while (srcStart < srcEnd) {
75
+ let code = load<u16>(srcStart);
77
76
  if (code == BACK_SLASH) {
78
- code = load<u16>(DESERIALIZE_ESCAPE_TABLE + load<u8>(src_ptr, 2));
79
- if (code == 117 && load<u32>(src_ptr, 4) == 3145776) {
80
- const block = load<u32>(src_ptr, 8);
77
+ code = load<u16>(DESERIALIZE_ESCAPE_TABLE + load<u8>(srcStart, 2));
78
+ if (code == 117 && load<u32>(srcStart, 4) == 3145776) {
79
+ const block = load<u32>(srcStart, 8);
81
80
  const codeA = block & 0xffff;
82
81
  const codeB = (block >> 16) & 0xffff;
83
82
  const escapedA = load<u8>(ESCAPE_HEX_TABLE + codeA);
@@ -85,16 +84,16 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize, dst: usiz
85
84
  const escaped = (escapedA << 4) + escapedB;
86
85
  store<u16>(dst_ptr, escaped);
87
86
  dst_ptr += 2;
88
- src_ptr += 12;
87
+ srcStart += 12;
89
88
  } else {
90
89
  store<u16>(dst_ptr, code);
91
90
  dst_ptr += 2;
92
- src_ptr += 4;
91
+ srcStart += 4;
93
92
  }
94
93
  } else {
95
94
  store<u16>(dst_ptr, code);
96
95
  dst_ptr += 2;
97
- src_ptr += 2;
96
+ srcStart += 2;
98
97
  }
99
98
  }
100
99
 
@@ -3,6 +3,10 @@ import { BACK_SLASH, COMMA, CHAR_F, BRACE_LEFT, BRACKET_LEFT, CHAR_N, QUOTE, BRA
3
3
  import { isSpace } from "../../util";
4
4
  import { ptrToStr } from "../../util/ptrToStr";
5
5
  import { deserializeArbitrary } from "./arbitrary";
6
+ import { deserializeArray } from "./array";
7
+ import { deserializeBoolean } from "./bool";
8
+ import { deserializeFloat } from "./float";
9
+ import { deserializeString } from "./string";
6
10
 
7
11
  export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): JSON.Obj {
8
12
  const out = changetype<JSON.Obj>(dst || changetype<usize>(new JSON.Obj()));
@@ -48,8 +52,8 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
48
52
  while (srcStart < srcEnd) {
49
53
  const code = load<u16>(srcStart);
50
54
  if (code == QUOTE && load<u16>(srcStart - 2) !== BACK_SLASH) {
51
- // console.log("Value (string): " + ptrToStr(lastIndex, srcStart + 2));
52
- out.set(ptrToStr(keyStart, keyEnd), deserializeArbitrary(lastIndex, srcStart + 2, dst));
55
+ // console.log("Value (string):-" + deserializeString(lastIndex, srcStart + 2, 0) + "-");
56
+ out.set(ptrToStr(keyStart, keyEnd), deserializeString(lastIndex, srcStart + 2, 0));
53
57
  // while (isSpace(load<u16>(srcStart))) srcStart += 2;
54
58
  srcStart += 4;
55
59
  // console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
@@ -65,7 +69,7 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
65
69
  const code = load<u16>(srcStart);
66
70
  if (code == COMMA || code == BRACE_RIGHT || isSpace(code)) {
67
71
  // console.log("Value (number): " + ptrToStr(lastIndex, srcStart));
68
- out.set(ptrToStr(keyStart, keyEnd), deserializeArbitrary(lastIndex, srcStart, dst));
72
+ out.set(ptrToStr(keyStart, keyEnd), deserializeFloat<f64>(lastIndex, srcStart));
69
73
  // while (isSpace(load<u16>((srcStart += 2)))) {
70
74
  // /* empty */
71
75
  // }
@@ -88,7 +92,7 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
88
92
  } else if (code == BRACE_RIGHT) {
89
93
  if (--depth == 0) {
90
94
  // console.log("Value (object): " + ptrToStr(lastIndex, srcStart + 2));
91
- out.set(ptrToStr(keyStart, keyEnd), deserializeArbitrary(lastIndex, (srcStart += 2), dst));
95
+ out.set(ptrToStr(keyStart, keyEnd), deserializeObject(lastIndex, (srcStart += 2), 0));
92
96
  // console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
93
97
  keyStart = 0;
94
98
  // while (isSpace(load<u16>(srcStart))) {
@@ -108,7 +112,7 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
108
112
  if (code == BRACKET_RIGHT) {
109
113
  if (--depth == 0) {
110
114
  // console.log("Value (array): " + ptrToStr(lastIndex, srcStart + 2));
111
- out.set(ptrToStr(keyStart, keyEnd), deserializeArbitrary(lastIndex, (srcStart += 2), dst));
115
+ out.set(ptrToStr(keyStart, keyEnd), deserializeArray<JSON.Value[]>(lastIndex, (srcStart += 2), 0));
112
116
  // console.log("Next: " + String.fromCharCode(load<u16>(srcStart)));
113
117
  keyStart = 0;
114
118
  // while (isSpace(load<u16>((srcStart += 2)))) {
@@ -122,7 +126,8 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
122
126
  } else if (code == CHAR_T) {
123
127
  if (load<u64>(srcStart) == 28429475166421108) {
124
128
  // console.log("Value (bool): " + ptrToStr(srcStart, srcStart + 8));
125
- out.set(ptrToStr(keyStart, keyEnd), deserializeArbitrary(srcStart, (srcStart += 8), dst));
129
+ out.set(ptrToStr(keyStart, keyEnd), true);
130
+ srcStart += 8;
126
131
  // while (isSpace(load<u16>((srcStart += 2)))) {
127
132
  // /* empty */
128
133
  // }
@@ -133,7 +138,8 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
133
138
  } else if (code == CHAR_F) {
134
139
  if (load<u64>(srcStart, 2) == 28429466576093281) {
135
140
  // console.log("Value (bool): " + ptrToStr(srcStart, srcStart + 10));
136
- out.set(ptrToStr(keyStart, keyEnd), deserializeArbitrary(srcStart, (srcStart += 10), dst));
141
+ out.set(ptrToStr(keyStart, keyEnd), false);
142
+ srcStart += 10;
137
143
  // while (isSpace(load<u16>((srcStart += 2)))) {
138
144
  // /* empty */
139
145
  // }
@@ -144,7 +150,8 @@ export function deserializeObject(srcStart: usize, srcEnd: usize, dst: usize): J
144
150
  } else if (code == CHAR_N) {
145
151
  if (load<u64>(srcStart) == 30399761348886638) {
146
152
  // console.log("Value (null): " + ptrToStr(srcStart, srcStart + 8));
147
- out.set(ptrToStr(keyStart, keyEnd), deserializeArbitrary(srcStart, (srcStart += 8), dst));
153
+ out.set(ptrToStr(keyStart, keyEnd), JSON.Value.from<usize>(0));
154
+ srcStart += 8;
148
155
  // while (isSpace(load<u16>((srcStart += 2)))) {
149
156
  /* empty */
150
157
  // }
package/assembly/index.ts CHANGED
@@ -27,6 +27,8 @@ import { serializeObject } from "./serialize/simple/object";
27
27
  import { deserializeObject } from "./deserialize/simple/object";
28
28
  import { serializeRaw } from "./serialize/simple/raw";
29
29
  import { deserializeRaw } from "./deserialize/simple/raw";
30
+ import { serializeString_SIMD } from "./serialize/simd/string";
31
+ // import { deserializeString_SIMD } from "./deserialize/simd/string";
30
32
 
31
33
  /**
32
34
  * Offset of the 'storage' property in the JSON.Value class.
@@ -112,11 +114,11 @@ export namespace JSON {
112
114
  // bs.setBuffer(oldBuf);
113
115
  // return changetype<string>(newBuf);
114
116
  // }
115
- // if (ASC_FEATURE_SIMD) {
116
- // serializeString_SIMD(data as string);
117
- // } else {
118
- serializeString(data as string);
119
- // }
117
+ if (ASC_FEATURE_SIMD) {
118
+ serializeString_SIMD(data as string);
119
+ } else {
120
+ serializeString(data as string);
121
+ }
120
122
  return bs.out<string>();
121
123
  // @ts-ignore: Supplied by transform
122
124
  } else if (isDefined(data.__SERIALIZE)) {
@@ -179,7 +181,7 @@ export namespace JSON {
179
181
  } else if (isString<T>()) {
180
182
  if (dataSize < 4) throw new Error("Cannot parse data as string because it was formatted incorrectly!");
181
183
  // if (ASC_FEATURE_SIMD) {
182
- // // @ts-ignore
184
+ // // @ts-ignore
183
185
  // return changetype<string>(deserializeString_SIMD(dataPtr, dataPtr + dataSize, __new(dataSize - 4, idof<string>())));
184
186
  // } else {
185
187
  // @ts-ignore
@@ -258,6 +260,8 @@ export namespace JSON {
258
260
  }
259
261
  }
260
262
 
263
+
264
+ @final
261
265
  export class Value {
262
266
  static METHODS: Map<u32, u32> = new Map<u32, u32>();
263
267
  public type: i32;
@@ -332,7 +336,7 @@ export namespace JSON {
332
336
  this.type = JSON.Types.Struct;
333
337
  store<T>(changetype<usize>(this), value, STORAGE);
334
338
  // @ts-ignore: supplied by transform
335
- } else if (isDefined(value.__SERIALIZE)) {
339
+ } else if (isDefined(value.__SERIALIZE) && isManaged<T>(value)) {
336
340
  this.type = idof<T>() + JSON.Types.Struct;
337
341
  // @ts-ignore
338
342
  if (!JSON.Value.METHODS.has(idof<T>())) JSON.Value.METHODS.set(idof<T>(), value.__SERIALIZE.index);
@@ -417,14 +421,18 @@ export namespace JSON {
417
421
  }
418
422
  }
419
423
  }
424
+
425
+
426
+ @unsafe private __visit(cookie: u32): void {
427
+ if (this.type >= JSON.Types.String) {
428
+ __visit(load<usize>(changetype<usize>(this), STORAGE), cookie);
429
+ }
430
+ }
420
431
  }
421
432
 
422
433
  export class Obj {
423
- // When accessing stackSize, subtract 2
424
- // @ts-ignore: type
425
- private stackSize: u32 = 6;
426
434
  // @ts-ignore: type
427
- private storage: Map<string, JSON.Value> = new Map<string, JSON.Value>();
435
+ storage: Map<string, JSON.Value> = new Map<string, JSON.Value>();
428
436
 
429
437
  constructor() {}
430
438
 
@@ -435,7 +443,7 @@ export namespace JSON {
435
443
 
436
444
  // @ts-ignore: decorator
437
445
  @inline set<T>(key: string, value: T): void {
438
- if (!this.storage.has(key)) this.stackSize += bytes(key) + 8;
446
+ // if (!this.storage.has(key)) this.stackSize += bytes(key) + 8;
439
447
  this.storage.set(key, JSON.Value.from<T>(value));
440
448
  }
441
449
 
@@ -473,7 +481,7 @@ export namespace JSON {
473
481
  // @ts-ignore: decorator
474
482
  @inline static from<T>(value: T): JSON.Obj {
475
483
  if (value instanceof JSON.Obj) return value;
476
- const out = changetype<JSON.Obj>(__new(offsetof<JSON.Obj>(), idof<JSON.Obj>()));
484
+ const out = new JSON.Obj();
477
485
 
478
486
  if (value instanceof Map) {
479
487
  }
@@ -542,7 +550,11 @@ export namespace JSON {
542
550
  store<u64>(bs.offset, 30399761348886638);
543
551
  bs.offset += 8;
544
552
  } else if (isString<nonnull<T>>()) {
545
- serializeString(src as string);
553
+ if (ASC_FEATURE_SIMD) {
554
+ serializeString_SIMD(src as string);
555
+ } else {
556
+ serializeString(src as string);
557
+ }
546
558
  // @ts-ignore: Supplied by transform
547
559
  } else if (isDefined(src.__SERIALIZE_CUSTOM)) {
548
560
  // @ts-ignore
@@ -714,12 +726,12 @@ export namespace JSON {
714
726
  // bs.setBuffer(oldBuf);
715
727
  // return changetype<string>(newBuf);
716
728
  // }
717
- // if (ASC_FEATURE_SIMD) {
718
- // serializeString_SIMD(data as string);
719
- // } else {
720
- bs.saveState();
721
- serializeString(data as string);
722
- // }
729
+ if (ASC_FEATURE_SIMD) {
730
+ serializeString_SIMD(data as string);
731
+ } else {
732
+ bs.saveState();
733
+ serializeString(data as string);
734
+ }
723
735
  return bs.cpyOut<string>();
724
736
  // @ts-ignore: Supplied by transform
725
737
  } else if (isDefined(data.__SERIALIZE)) {
@@ -9,11 +9,11 @@ import { bytes } from "../../util";
9
9
  * @param srcEnd pointer to end serialization at
10
10
  */
11
11
  export function serializeString_SIMD(src: string): void {
12
+ const U00_MARKER = 13511005048209500;
12
13
  const SPLAT_34 = i16x8.splat(34); /* " */
13
14
  const SPLAT_92 = i16x8.splat(92); /* \ */
14
15
 
15
16
  const SPLAT_32 = i16x8.splat(32); /* [ESC] */
16
- const SPLAT_0 = i16x8.splat(0); /* 0 */
17
17
 
18
18
  const srcSize = bytes(src);
19
19
  let srcStart = changetype<usize>(src);
@@ -27,6 +27,7 @@ export function serializeString_SIMD(src: string): void {
27
27
 
28
28
  while (srcStart <= srcEnd16) {
29
29
  const block = v128.load(srcStart);
30
+
30
31
  v128.store(bs.offset, block);
31
32
 
32
33
  const backslash_indices = i16x8.eq(block, SPLAT_92);
@@ -38,21 +39,20 @@ export function serializeString_SIMD(src: string): void {
38
39
 
39
40
  while (mask != 0) {
40
41
  const lane_index = ctz(mask) << 1;
41
- const dst_offset = bs.offset + lane_index;
42
42
  const src_offset = srcStart + lane_index;
43
43
  const code = load<u16>(src_offset) << 2;
44
44
  const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + code);
45
-
46
45
  mask &= mask - 1;
47
-
48
46
  if ((escaped & 0xffff) != BACK_SLASH) {
49
47
  bs.growSize(10);
50
- store<u64>(dst_offset, 13511005048209500);
48
+ const dst_offset = bs.offset + lane_index;
49
+ store<u64>(dst_offset, U00_MARKER);
51
50
  store<u32>(dst_offset, escaped, 8);
52
51
  v128.store(dst_offset, v128.load(src_offset, 2), 12);
53
52
  bs.offset += 10;
54
53
  } else {
55
54
  bs.growSize(2);
55
+ const dst_offset = bs.offset + lane_index;
56
56
  store<u32>(dst_offset, escaped);
57
57
  v128.store(dst_offset, v128.load(src_offset, 2), 4);
58
58
  bs.offset += 2;
@@ -63,101 +63,13 @@ export function serializeString_SIMD(src: string): void {
63
63
  bs.offset += 16;
64
64
  }
65
65
 
66
- const rem = srcEnd - srcStart;
67
- if (rem & 8) {
68
- const block = v128.load64_zero(srcStart);
69
- v128.store64_lane(bs.offset, block, 0);
70
-
71
- const backslash_indices = i16x8.eq(block, SPLAT_92);
72
- const quote_indices = i16x8.eq(block, SPLAT_34);
73
- const escape_indices = i16x8.lt_u(block, SPLAT_32);
74
- const zero_indices = i16x8.eq(block, SPLAT_0);
75
- const sieve = v128.and(v128.or(v128.or(backslash_indices, quote_indices), escape_indices), v128.not(zero_indices));
76
-
77
- let mask = i16x8.bitmask(sieve);
78
- while (mask != 0) {
79
- let lane_index = ctz(mask) << 1;
80
- const dst_offset = bs.offset + lane_index;
81
- const src_offset = srcStart + lane_index;
82
- const code = load<u16>(src_offset) << 2;
83
- const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + code);
84
- mask &= mask - 1;
85
-
86
- if ((escaped & 0xffff) != BACK_SLASH) {
87
- bs.growSize(10);
88
- store<u64>(dst_offset, 13511005048209500);
89
- store<u32>(dst_offset, escaped, 8);
90
- while (lane_index < 6) {
91
- store<u8>(bs.offset + lane_index, load<u8>(srcStart + lane_index, 2), 12);
92
- lane_index += 2;
93
- }
94
- bs.offset += 10;
95
- } else {
96
- bs.growSize(2);
97
- store<u32>(dst_offset, escaped);
98
-
99
- while (lane_index < 6) {
100
- store<u8>(bs.offset + lane_index, load<u8>(srcStart + lane_index, 2), 4);
101
- lane_index += 2;
102
- }
103
- bs.offset += 2;
104
- }
105
- }
106
-
107
- bs.offset += 8;
108
- srcStart += 8;
109
- }
110
- if (rem & 4) {
111
- const block = load<u32>(srcStart);
112
- const codeA = block & 0xffff;
113
- const codeB = (block >> 16) & 0xffff;
114
-
115
- if (codeA == 92 || codeA == 34 || codeA < 32) {
116
- const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + (codeA << 2));
117
-
118
- if ((escaped & 0xffff) != BACK_SLASH) {
119
- bs.growSize(10);
120
- store<u64>(bs.offset, 13511005048209500);
121
- store<u32>(bs.offset, escaped, 8);
122
- bs.offset += 12;
123
- } else {
124
- bs.growSize(2);
125
- store<u32>(bs.offset, escaped);
126
- bs.offset += 4;
127
- }
128
- } else {
129
- store<u16>(bs.offset, codeA);
130
- bs.offset += 2;
131
- }
132
-
133
- if (codeB == 92 || codeB == 34 || codeB < 32) {
134
- const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + (codeB << 2));
135
-
136
- if ((escaped & 0xffff) != BACK_SLASH) {
137
- bs.growSize(10);
138
- store<u64>(bs.offset, 13511005048209500);
139
- store<u32>(bs.offset, escaped, 8);
140
- bs.offset += 12;
141
- } else {
142
- bs.growSize(2);
143
- store<u32>(bs.offset, escaped);
144
- bs.offset += 4;
145
- }
146
- } else {
147
- store<u16>(bs.offset, codeB);
148
- bs.offset += 2;
149
- }
150
-
151
- srcStart += 4;
152
- }
153
- if (rem & 2) {
66
+ while (srcStart <= srcEnd - 2) {
154
67
  const code = load<u16>(srcStart);
155
68
  if (code == 92 || code == 34 || code < 32) {
156
69
  const escaped = load<u32>(SERIALIZE_ESCAPE_TABLE + (code << 2));
157
-
158
70
  if ((escaped & 0xffff) != BACK_SLASH) {
159
71
  bs.growSize(10);
160
- store<u64>(bs.offset, 13511005048209500);
72
+ store<u64>(bs.offset, U00_MARKER);
161
73
  store<u32>(bs.offset, escaped, 8);
162
74
  bs.offset += 12;
163
75
  } else {
@@ -169,6 +81,7 @@ export function serializeString_SIMD(src: string): void {
169
81
  store<u16>(bs.offset, code);
170
82
  bs.offset += 2;
171
83
  }
84
+ srcStart += 2;
172
85
  }
173
86
 
174
87
  store<u8>(bs.offset, 34); /* " */