json-as 1.0.3 → 1.0.5
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/.github/dependabot.yml +11 -0
- package/CHANGELOG.md +14 -1
- package/README.md +27 -9
- package/SECURITY.md +32 -0
- package/assembly/__benches__/large.bench.ts +3 -0
- package/assembly/__benches__/vec3.bench.ts +3 -0
- package/assembly/__tests__/arbitrary.spec.ts +3 -3
- package/assembly/__tests__/array.spec.ts +5 -8
- package/assembly/__tests__/box.spec.ts +10 -20
- package/assembly/__tests__/custom.spec.ts +5 -6
- package/assembly/__tests__/date.spec.ts +4 -6
- package/assembly/__tests__/float.spec.ts +3 -3
- package/assembly/__tests__/map.spec.ts +1 -1
- package/assembly/__tests__/raw.spec.ts +3 -3
- package/assembly/__tests__/struct.spec.ts +24 -14
- package/assembly/deserialize/simd/string.ts +1 -2
- package/assembly/deserialize/simple/array/struct.ts +2 -3
- package/assembly/deserialize/simple/array.ts +1 -1
- package/assembly/deserialize/simple/bool.ts +1 -1
- package/assembly/deserialize/simple/map.ts +3 -4
- package/assembly/deserialize/simple/object.ts +2 -3
- package/assembly/deserialize/simple/raw.ts +1 -1
- package/assembly/deserialize/simple/struct.ts +2 -3
- package/assembly/index.ts +23 -7
- package/assembly/serialize/simd/string.ts +1 -0
- package/assembly/serialize/simple/integer.ts +1 -1
- package/assembly/serialize/simple/object.ts +7 -6
- package/assembly/test.ts +23 -24
- package/bench.js +2 -4
- package/index.ts +1 -1
- package/lib/as-bs.ts +4 -13
- package/package.json +6 -4
- package/run-tests.sh +1 -1
- package/transform/lib/index.js +65 -57
- package/transform/lib/index.js.map +1 -1
- package/transform/src/index.ts +80 -124
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# To get started with Dependabot version updates, you'll need to specify which
|
|
2
|
+
# package ecosystems to update and where the package manifests are located.
|
|
3
|
+
# Please see the documentation for all configuration options:
|
|
4
|
+
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
|
|
5
|
+
|
|
6
|
+
version: 2
|
|
7
|
+
updates:
|
|
8
|
+
- package-ecosystem: "npm" # Set to npm since you're using npm
|
|
9
|
+
directory: "/" # Location of package manifests, assuming package.json is at the root
|
|
10
|
+
schedule:
|
|
11
|
+
interval: "weekly" # Frequency of updates: "daily", "weekly", or "monthly"
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# Change Log
|
|
2
2
|
|
|
3
|
+
## 2025-05-11 - 1.0.5
|
|
4
|
+
|
|
5
|
+
- feat: add sanity checks for badly formatted strings
|
|
6
|
+
- fix: [#120](https://github.com/JairusSW/json-as/issues/120) handle empty `JSON.Obj` serialization
|
|
7
|
+
- feat: add SIMD optimization if SIMD is enabled by user
|
|
8
|
+
- fix: handle structs with nullable array as property [#123](https://github.com/JairusSW/json-as/pull/123)
|
|
9
|
+
- fix: struct serialization from writing to incorrect parts of memory when parsing nested structs [#125](https://github.com/JairusSW/json-as/pull/125)
|
|
10
|
+
- chore: add two new contributors
|
|
11
|
+
|
|
12
|
+
## 2025-04-07 - 1.0.4
|
|
13
|
+
|
|
14
|
+
- fix: paths must be resolved as POSIX in order to be valid TypeScript imports [#116](https://github.com/JairusSW/json-as/issues/116)
|
|
15
|
+
|
|
3
16
|
## 2025-03-24 - 1.0.3
|
|
4
17
|
|
|
5
18
|
- fix: make transform windows-compatible [#119](https://github.com/JairusSW/json-as/issues/119?reload=1)
|
|
@@ -150,4 +163,4 @@
|
|
|
150
163
|
- feat: reduce memory usage so that it is viable for low-memory environments
|
|
151
164
|
- feat: write to a central buffer and reduce memory overhead
|
|
152
165
|
- feat: rewrite the transform to properly resolve schemas and link them together
|
|
153
|
-
- feat: pre-allocate and compute the minimum size of a schema to avoid memory out of range errors
|
|
166
|
+
- feat: pre-allocate and compute the minimum size of a schema to avoid memory out of range errors
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
██ ██ ██ ██ ██ ██ ██ ██ ██ ██ ██
|
|
7
7
|
█████ ███████ ██████ ██ ████ ██ ██ ███████
|
|
8
8
|
</span>
|
|
9
|
-
AssemblyScript - v1.0.
|
|
9
|
+
AssemblyScript - v1.0.5
|
|
10
10
|
</pre>
|
|
11
11
|
</h5>
|
|
12
12
|
|
|
@@ -62,13 +62,14 @@ Alternatively, add it to your `asconfig.json`
|
|
|
62
62
|
}
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
If you'd like to see the code that the transform generates, run with `
|
|
65
|
+
If you'd like to see the code that the transform generates, run the build step with `DEBUG=true`
|
|
66
66
|
|
|
67
67
|
## 🪄 Usage
|
|
68
68
|
|
|
69
69
|
```typescript
|
|
70
70
|
import { JSON } from "json-as";
|
|
71
71
|
|
|
72
|
+
|
|
72
73
|
@json
|
|
73
74
|
class Vec3 {
|
|
74
75
|
x: f32 = 0.0;
|
|
@@ -76,8 +77,10 @@ class Vec3 {
|
|
|
76
77
|
z: f32 = 0.0;
|
|
77
78
|
}
|
|
78
79
|
|
|
80
|
+
|
|
79
81
|
@json
|
|
80
82
|
class Player {
|
|
83
|
+
|
|
81
84
|
@alias("first name")
|
|
82
85
|
firstName!: string;
|
|
83
86
|
lastName!: string;
|
|
@@ -85,6 +88,7 @@ class Player {
|
|
|
85
88
|
// Drop in a code block, function, or expression that evaluates to a boolean
|
|
86
89
|
@omitif((self: Player) => self.age < 18)
|
|
87
90
|
age!: i32;
|
|
91
|
+
|
|
88
92
|
@omitnull()
|
|
89
93
|
pos!: Vec3 | null;
|
|
90
94
|
isVerified!: boolean;
|
|
@@ -101,7 +105,7 @@ const player: Player = {
|
|
|
101
105
|
z: 8.3,
|
|
102
106
|
},
|
|
103
107
|
isVerified: true,
|
|
104
|
-
}
|
|
108
|
+
};
|
|
105
109
|
|
|
106
110
|
const serialized = JSON.stringify<Player>(player);
|
|
107
111
|
const deserialized = JSON.parse<Player>(serialized);
|
|
@@ -121,9 +125,11 @@ This library allows selective omission of fields during serialization using the
|
|
|
121
125
|
This decorator excludes a field from serialization entirely.
|
|
122
126
|
|
|
123
127
|
```typescript
|
|
128
|
+
|
|
124
129
|
@json
|
|
125
130
|
class Example {
|
|
126
131
|
name!: string;
|
|
132
|
+
|
|
127
133
|
@omit
|
|
128
134
|
SSN!: string;
|
|
129
135
|
}
|
|
@@ -140,9 +146,11 @@ console.log(JSON.stringify(obj)); // { "name": "Jairus" }
|
|
|
140
146
|
This decorator omits a field only if its value is null.
|
|
141
147
|
|
|
142
148
|
```typescript
|
|
149
|
+
|
|
143
150
|
@json
|
|
144
151
|
class Example {
|
|
145
152
|
name!: string;
|
|
153
|
+
|
|
146
154
|
@omitnull()
|
|
147
155
|
optionalField!: string | null;
|
|
148
156
|
}
|
|
@@ -159,9 +167,11 @@ console.log(JSON.stringify(obj)); // { "name": "Jairus" }
|
|
|
159
167
|
This decorator omits a field based on a custom predicate function.
|
|
160
168
|
|
|
161
169
|
```typescript
|
|
170
|
+
|
|
162
171
|
@json
|
|
163
172
|
class Example {
|
|
164
173
|
name!: string;
|
|
174
|
+
|
|
165
175
|
@omitif((self: Example) => self.age <= 18)
|
|
166
176
|
age!: number;
|
|
167
177
|
}
|
|
@@ -186,6 +196,7 @@ AssemblyScript doesn't support using nullable primitive types, so instead, json-
|
|
|
186
196
|
For example, this schema won't compile in AssemblyScript:
|
|
187
197
|
|
|
188
198
|
```typescript
|
|
199
|
+
|
|
189
200
|
@json
|
|
190
201
|
class Person {
|
|
191
202
|
name!: string;
|
|
@@ -196,6 +207,7 @@ class Person {
|
|
|
196
207
|
Instead, use `JSON.Box` to allow nullable primitives:
|
|
197
208
|
|
|
198
209
|
```typescript
|
|
210
|
+
|
|
199
211
|
@json
|
|
200
212
|
class Person {
|
|
201
213
|
name: string;
|
|
@@ -239,11 +251,12 @@ When dealing with an object with an unknown structure, use the `JSON.Obj` type
|
|
|
239
251
|
const obj = JSON.parse<JSON.Obj>('{"a":3.14,"b":true,"c":[1,2,3],"d":{"x":1,"y":2,"z":3}}');
|
|
240
252
|
|
|
241
253
|
console.log("Keys: " + obj.keys().join(" ")); // a b c d
|
|
242
|
-
console.log(
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
254
|
+
console.log(
|
|
255
|
+
"Values: " +
|
|
256
|
+
obj
|
|
257
|
+
.values()
|
|
258
|
+
.map<string>((v) => JSON.stringify(v))
|
|
259
|
+
.join(" "),
|
|
247
260
|
); // 3.14 true [1,2,3] {"x":1,"y":2,"z":3}
|
|
248
261
|
|
|
249
262
|
const y = obj.get("d")!.get<JSON.Obj>().get("y")!;
|
|
@@ -257,6 +270,7 @@ More often, objects will be completely statically typed except for one or two va
|
|
|
257
270
|
In such cases, `JSON.Value` can be used to handle fields that may hold different types at runtime.
|
|
258
271
|
|
|
259
272
|
```typescript
|
|
273
|
+
|
|
260
274
|
@json
|
|
261
275
|
class DynamicObj {
|
|
262
276
|
id: i32 = 0;
|
|
@@ -313,6 +327,7 @@ Here's an example of creating a custom data type called `Point` which serializes
|
|
|
313
327
|
```typescript
|
|
314
328
|
import { bytes } from "json-as/assembly/util";
|
|
315
329
|
|
|
330
|
+
|
|
316
331
|
@json
|
|
317
332
|
class Point {
|
|
318
333
|
x: f64 = 0.0;
|
|
@@ -322,11 +337,13 @@ class Point {
|
|
|
322
337
|
this.y = y;
|
|
323
338
|
}
|
|
324
339
|
|
|
340
|
+
|
|
325
341
|
@serializer
|
|
326
342
|
serializer(self: Point): string {
|
|
327
343
|
return `(${self.x},${self.y})`;
|
|
328
344
|
}
|
|
329
345
|
|
|
346
|
+
|
|
330
347
|
@deserializer
|
|
331
348
|
deserializer(data: string): Point {
|
|
332
349
|
const dataSize = bytes(data);
|
|
@@ -403,6 +420,7 @@ These benchmarks compare this library to JavaScript's native `JSON.stringify` an
|
|
|
403
420
|
- JSON-AS consistently outperforms JavaScript's native implementation.
|
|
404
421
|
|
|
405
422
|
- **Serialization Speed:**
|
|
423
|
+
|
|
406
424
|
- JSON-AS achieves speeds up to `2,133 MB/s`, significantly faster than JavaScript's peak of `1,416 MB/s`.
|
|
407
425
|
- Large objects see the biggest improvement, with JSON-AS at `2,074 MB/s` vs. JavaScript’s `749.7 MB/s`.
|
|
408
426
|
|
|
@@ -457,4 +475,4 @@ Please send all issues to [GitHub Issues](https://github.com/JairusSW/json-as/is
|
|
|
457
475
|
- **Email:** Send me inquiries, questions, or requests at [me@jairus.dev](mailto:me@jairus.dev)
|
|
458
476
|
- **GitHub:** Visit the official GitHub repository [Here](https://github.com/JairusSW/json-as)
|
|
459
477
|
- **Website:** Visit my official website at [jairus.dev](https://jairus.dev/)
|
|
460
|
-
- **Discord:** Contact me at [My Discord](https://discord.com/users/600700584038760448) or on the [AssemblyScript Discord Server](https://discord.gg/assemblyscript/)
|
|
478
|
+
- **Discord:** Contact me at [My Discord](https://discord.com/users/600700584038760448) or on the [AssemblyScript Discord Server](https://discord.gg/assemblyscript/)
|
package/SECURITY.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
| Version | Supported |
|
|
6
|
+
| ------- | ------------------ |
|
|
7
|
+
| 1.x.x | Active Development |
|
|
8
|
+
| 0.9.x | Maintainence |
|
|
9
|
+
| <=0.8.x | Unsupported |
|
|
10
|
+
|
|
11
|
+
## Reporting a Vulnerability
|
|
12
|
+
|
|
13
|
+
If you believe you have discovered a security vulnerability in this project, please report it as soon as possible. Follow the steps below to report a vulnerability:
|
|
14
|
+
|
|
15
|
+
1. **Report via GitHub Issues**: Please submit your vulnerability report via the [GitHub Issues](https://github.com/JairusSW/json-as/issues) page. If you prefer, you can also send an email directly to [me@jairus.dev](mailto:me@jairus.dev).
|
|
16
|
+
|
|
17
|
+
2. **Provide Details**: Include as much detail as possible, including the steps to reproduce, what you believe the impact is, and any suggested remediation if applicable.
|
|
18
|
+
|
|
19
|
+
3. **Acknowledgment**: Once your report is received, we will acknowledge it within 48 hours. You will then receive an update on the progress of the investigation and a timeline for a potential fix.
|
|
20
|
+
|
|
21
|
+
4. **Resolution Timeline**:
|
|
22
|
+
|
|
23
|
+
- Critical vulnerabilities will be prioritized, and we aim to issue a patch within 7 business days.
|
|
24
|
+
- Non-critical vulnerabilities will be evaluated and addressed in the next stable release.
|
|
25
|
+
- If the vulnerability is accepted, a fix will be issued, and a security advisory will be published.
|
|
26
|
+
- If the vulnerability is declined, you will be notified of the decision with an explanation.
|
|
27
|
+
|
|
28
|
+
5. **Confidentiality**: Please do not disclose the vulnerability to the public or other parties until the issue has been addressed. This helps ensure that users of the project are not at risk before a patch is available.
|
|
29
|
+
|
|
30
|
+
6. **Credits**: If your report leads to a fix, we will credit your contribution in the release notes, if you are comfortable with that.
|
|
31
|
+
|
|
32
|
+
Thank you for helping us keep this project secure!
|
|
@@ -8,6 +8,7 @@ class Vec3 {
|
|
|
8
8
|
public y!: i32;
|
|
9
9
|
public z!: i32;
|
|
10
10
|
|
|
11
|
+
|
|
11
12
|
@inline __SERIALIZE(ptr: usize): void {
|
|
12
13
|
bs.proposeSize(98);
|
|
13
14
|
store<u64>(bs.offset, 9570664606466171, 0); // {"x"
|
|
@@ -26,10 +27,12 @@ class Vec3 {
|
|
|
26
27
|
bs.offset += 2;
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
|
|
29
31
|
@inline __INITIALIZE(): this {
|
|
30
32
|
return this;
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
|
|
33
36
|
@inline __DESERIALIZE(keyStart: usize, keyEnd: usize, valStart: usize, valEnd: usize, ptr: usize): void {
|
|
34
37
|
switch (load<u16>(keyStart)) {
|
|
35
38
|
case 120: {
|
|
@@ -8,6 +8,7 @@ class Vec3 {
|
|
|
8
8
|
public y!: i32;
|
|
9
9
|
public z!: i32;
|
|
10
10
|
|
|
11
|
+
|
|
11
12
|
@inline __SERIALIZE(ptr: usize): void {
|
|
12
13
|
bs.proposeSize(98);
|
|
13
14
|
store<u64>(bs.offset, 9570664606466171, 0); // {"x"
|
|
@@ -26,10 +27,12 @@ class Vec3 {
|
|
|
26
27
|
bs.offset += 2;
|
|
27
28
|
}
|
|
28
29
|
|
|
30
|
+
|
|
29
31
|
@inline __INITIALIZE(): this {
|
|
30
32
|
return this;
|
|
31
33
|
}
|
|
32
34
|
|
|
35
|
+
|
|
33
36
|
@inline __DESERIALIZE(keyStart: usize, keyEnd: usize, valStart: usize, valEnd: usize, ptr: usize): void {
|
|
34
37
|
switch (load<u16>(keyStart)) {
|
|
35
38
|
case 120: {
|
|
@@ -3,7 +3,7 @@ import { describe, expect } from "./lib";
|
|
|
3
3
|
import { Vec3 } from "./types";
|
|
4
4
|
|
|
5
5
|
describe("Should serialize arbitrary types", () => {
|
|
6
|
-
expect(JSON.stringify(JSON.Value.from("hello world"))).toBe("
|
|
6
|
+
expect(JSON.stringify(JSON.Value.from("hello world"))).toBe('"hello world"');
|
|
7
7
|
expect(JSON.stringify(JSON.Value.from(0))).toBe("0");
|
|
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}');
|
|
@@ -11,9 +11,9 @@ describe("Should serialize arbitrary types", () => {
|
|
|
11
11
|
});
|
|
12
12
|
|
|
13
13
|
describe("Should deserialize arbitrary types", () => {
|
|
14
|
-
expect(JSON.parse<JSON.Value>("
|
|
14
|
+
expect(JSON.parse<JSON.Value>('"hello world"').get<string>()).toBe("hello world");
|
|
15
15
|
expect(JSON.parse<JSON.Value>("0.0").toString()).toBe("0.0");
|
|
16
16
|
expect(JSON.parse<JSON.Value>("true").toString()).toBe("true");
|
|
17
17
|
expect(JSON.stringify(JSON.parse<JSON.Value>('{"x":1.0,"y":2.0,"z":3.0}'))).toBe('{"x":1.0,"y":2.0,"z":3.0}');
|
|
18
18
|
expect(JSON.stringify(JSON.parse<JSON.Value[]>('["string",true,3.14,{"x":1.0,"y":2.0,"z":3.0},[1.0,2.0,3,true]]'))).toBe('["string",true,3.14,{"x":1.0,"y":2.0,"z":3.0},[1.0,2.0,3.0,true]]');
|
|
19
|
-
});
|
|
19
|
+
});
|
|
@@ -59,8 +59,8 @@ describe("Should serialize object arrays", () => {
|
|
|
59
59
|
});
|
|
60
60
|
|
|
61
61
|
describe("Should deserialize integer arrays", () => {
|
|
62
|
-
expect(JSON.stringify(JSON.parse<u32[]>("[0,100,101]"))).toBe(
|
|
63
|
-
expect(JSON.stringify(JSON.parse<u64[]>("[0,100,101]"))).toBe(
|
|
62
|
+
expect(JSON.stringify(JSON.parse<u32[]>("[0,100,101]"))).toBe("[0,100,101]");
|
|
63
|
+
expect(JSON.stringify(JSON.parse<u64[]>("[0,100,101]"))).toBe("[0,100,101]");
|
|
64
64
|
expect(JSON.stringify(JSON.parse<i32[]>("[0,100,101,-100,-101]"))).toBe("[0,100,101,-100,-101]");
|
|
65
65
|
expect(JSON.stringify(JSON.parse<i64[]>("[0,100,101,-100,-101]"))).toBe("[0,100,101,-100,-101]");
|
|
66
66
|
});
|
|
@@ -76,7 +76,7 @@ describe("Should deserialize boolean arrays", () => {
|
|
|
76
76
|
});
|
|
77
77
|
|
|
78
78
|
describe("Should deserialize string arrays", () => {
|
|
79
|
-
expect(JSON.stringify(JSON.parse<string[]>(
|
|
79
|
+
expect(JSON.stringify(JSON.parse<string[]>('["string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"]'))).toBe('["string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"]');
|
|
80
80
|
});
|
|
81
81
|
|
|
82
82
|
describe("Should deserialize nested integer arrays", () => {
|
|
@@ -93,13 +93,10 @@ describe("Should deserialize nested boolean arrays", () => {
|
|
|
93
93
|
});
|
|
94
94
|
|
|
95
95
|
describe("Should deserialize object arrays", () => {
|
|
96
|
-
expect(
|
|
97
|
-
JSON.stringify(JSON.parse<Vec3[]>(
|
|
98
|
-
'[{"x":3.4,"y":1.2,"z":8.3},{"x":3.4,"y":-2.1,"z":9.3}]'
|
|
99
|
-
)
|
|
100
|
-
)).toBe('[{"x":3.4,"y":1.2,"z":8.3},{"x":3.4,"y":-2.1,"z":9.3}]');
|
|
96
|
+
expect(JSON.stringify(JSON.parse<Vec3[]>('[{"x":3.4,"y":1.2,"z":8.3},{"x":3.4,"y":-2.1,"z":9.3}]'))).toBe('[{"x":3.4,"y":1.2,"z":8.3},{"x":3.4,"y":-2.1,"z":9.3}]');
|
|
101
97
|
});
|
|
102
98
|
|
|
99
|
+
|
|
103
100
|
@json
|
|
104
101
|
class Vec3 {
|
|
105
102
|
x: f64 = 0.0;
|
|
@@ -2,36 +2,26 @@ import { JSON } from "..";
|
|
|
2
2
|
import { describe, expect } from "./lib";
|
|
3
3
|
|
|
4
4
|
describe("Should serialize JSON.Box<T>", () => {
|
|
5
|
-
expect(JSON.stringify<JSON.Box<i32> | null>(null))
|
|
6
|
-
.toBe("null");
|
|
5
|
+
expect(JSON.stringify<JSON.Box<i32> | null>(null)).toBe("null");
|
|
7
6
|
|
|
8
|
-
expect(JSON.stringify<JSON.Box<i32> | null>(new JSON.Box<i32>(0)))
|
|
9
|
-
.toBe("0");
|
|
7
|
+
expect(JSON.stringify<JSON.Box<i32> | null>(new JSON.Box<i32>(0))).toBe("0");
|
|
10
8
|
|
|
11
|
-
expect(JSON.stringify<JSON.Box<i32> | null>(new JSON.Box<i32>(1)))
|
|
12
|
-
.toBe("1");
|
|
9
|
+
expect(JSON.stringify<JSON.Box<i32> | null>(new JSON.Box<i32>(1))).toBe("1");
|
|
13
10
|
|
|
14
|
-
expect(JSON.stringify<JSON.Box<boolean> | null>(new JSON.Box<boolean>(false)))
|
|
15
|
-
.toBe("false");
|
|
11
|
+
expect(JSON.stringify<JSON.Box<boolean> | null>(new JSON.Box<boolean>(false))).toBe("false");
|
|
16
12
|
|
|
17
|
-
expect(JSON.stringify<JSON.Box<boolean> | null>(new JSON.Box<boolean>(true)))
|
|
18
|
-
.toBe("true");
|
|
13
|
+
expect(JSON.stringify<JSON.Box<boolean> | null>(new JSON.Box<boolean>(true))).toBe("true");
|
|
19
14
|
});
|
|
20
15
|
|
|
21
16
|
// This is somewhat clumsy to use. Perhaps I can redesign it or use some transform to make it more transparent.
|
|
22
17
|
describe("Should deserialize JSON.Box<T>", () => {
|
|
23
|
-
expect((JSON.parse<JSON.Box<i32> | null>("null") == null).toString())
|
|
24
|
-
.toBe("true");
|
|
18
|
+
expect((JSON.parse<JSON.Box<i32> | null>("null") == null).toString()).toBe("true");
|
|
25
19
|
|
|
26
|
-
expect(JSON.parse<JSON.Box<i32> | null>("0")!.value.toString())
|
|
27
|
-
.toBe("0");
|
|
20
|
+
expect(JSON.parse<JSON.Box<i32> | null>("0")!.value.toString()).toBe("0");
|
|
28
21
|
|
|
29
|
-
expect(JSON.parse<JSON.Box<i32> | null>("1")!.value.toString())
|
|
30
|
-
.toBe("1");
|
|
22
|
+
expect(JSON.parse<JSON.Box<i32> | null>("1")!.value.toString()).toBe("1");
|
|
31
23
|
|
|
32
|
-
expect(JSON.parse<JSON.Box<boolean> | null>("false")!.value.toString())
|
|
33
|
-
.toBe("false");
|
|
24
|
+
expect(JSON.parse<JSON.Box<boolean> | null>("false")!.value.toString()).toBe("false");
|
|
34
25
|
|
|
35
|
-
expect(JSON.parse<JSON.Box<boolean> | null>("true")!.value.toString())
|
|
36
|
-
.toBe("true");
|
|
26
|
+
expect(JSON.parse<JSON.Box<boolean> | null>("true")!.value.toString()).toBe("true");
|
|
37
27
|
});
|
|
@@ -2,6 +2,7 @@ import { JSON } from "..";
|
|
|
2
2
|
import { describe, expect } from "./lib";
|
|
3
3
|
import { bytes } from "../util";
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
@json
|
|
6
7
|
class Point {
|
|
7
8
|
x: f64 = 0.0;
|
|
@@ -10,10 +11,12 @@ class Point {
|
|
|
10
11
|
this.x = x;
|
|
11
12
|
this.y = y;
|
|
12
13
|
}
|
|
14
|
+
|
|
13
15
|
@serializer
|
|
14
16
|
serializer(self: Point): string {
|
|
15
17
|
return `(${self.x},${self.y})`;
|
|
16
18
|
}
|
|
19
|
+
|
|
17
20
|
@deserializer
|
|
18
21
|
deserializer(data: string): Point {
|
|
19
22
|
const dataSize = bytes(data);
|
|
@@ -23,16 +26,12 @@ class Point {
|
|
|
23
26
|
const x = data.slice(1, c);
|
|
24
27
|
const y = data.slice(c + 1, data.length - 1);
|
|
25
28
|
|
|
26
|
-
return new Point(
|
|
27
|
-
f64.parse(x),
|
|
28
|
-
f64.parse(y)
|
|
29
|
-
);
|
|
29
|
+
return new Point(f64.parse(x), f64.parse(y));
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
|
|
34
33
|
describe("Should serialize using custom serializers", () => {
|
|
35
|
-
expect(JSON.stringify<Point>(new Point(1,2))).toBe("(1.0,2.0)");
|
|
34
|
+
expect(JSON.stringify<Point>(new Point(1, 2))).toBe("(1.0,2.0)");
|
|
36
35
|
});
|
|
37
36
|
|
|
38
37
|
describe("Should deserialize using custom deserializers", () => {
|
|
@@ -2,10 +2,8 @@ import { JSON } from "..";
|
|
|
2
2
|
import { describe, expect } from "./lib";
|
|
3
3
|
|
|
4
4
|
describe("Should serialize Date", () => {
|
|
5
|
-
expect(JSON.stringify<Date>(new Date(0)))
|
|
6
|
-
|
|
7
|
-
expect(JSON.stringify<Date>(new Date(1738618120525)))
|
|
8
|
-
.toBe('"2025-02-03T21:28:40.525Z"');
|
|
5
|
+
expect(JSON.stringify<Date>(new Date(0))).toBe('"1970-01-01T00:00:00.000Z"');
|
|
6
|
+
expect(JSON.stringify<Date>(new Date(1738618120525))).toBe('"2025-02-03T21:28:40.525Z"');
|
|
9
7
|
});
|
|
10
8
|
|
|
11
9
|
describe("Should deserialize booleans", () => {
|
|
@@ -17,7 +15,7 @@ describe("Should deserialize booleans", () => {
|
|
|
17
15
|
// console.log("Minutes: " + date.getUTCMinutes().toString());
|
|
18
16
|
// console.log("Seconds: " + date.getUTCSeconds().toString());
|
|
19
17
|
// console.log("Milliseconds: " + date.getUTCMilliseconds().toString());
|
|
20
|
-
|
|
18
|
+
|
|
21
19
|
const date1 = JSON.parse<Date>('"1970-01-01T00:00:00.000Z"');
|
|
22
20
|
expect(date1.getUTCFullYear().toString()).toBe("1970");
|
|
23
21
|
expect(date1.getUTCMonth().toString()).toBe("0");
|
|
@@ -34,5 +32,5 @@ describe("Should deserialize booleans", () => {
|
|
|
34
32
|
expect(date2.getUTCHours().toString()).toBe("21");
|
|
35
33
|
expect(date2.getUTCMinutes().toString()).toBe("28");
|
|
36
34
|
expect(date2.getUTCSeconds().toString()).toBe("40");
|
|
37
|
-
expect(date2.getUTCMilliseconds().toString()).toBe("525");
|
|
35
|
+
expect(date2.getUTCMilliseconds().toString()).toBe("525");
|
|
38
36
|
});
|
|
@@ -34,9 +34,9 @@ describe("Should deserialize floats", () => {
|
|
|
34
34
|
|
|
35
35
|
expect(JSON.parse<f64>("0.000001").toString()).toBe("0.000001");
|
|
36
36
|
|
|
37
|
-
expect(JSON.parse<f64>("1e-7").toString()).toBe(1e-7.toString());
|
|
37
|
+
expect(JSON.parse<f64>("1e-7").toString()).toBe((1e-7).toString());
|
|
38
38
|
|
|
39
|
-
expect(JSON.parse<f64>("100000000000000000000.0").toString()).toBe(1e20.toString());
|
|
39
|
+
expect(JSON.parse<f64>("100000000000000000000.0").toString()).toBe((1e20).toString());
|
|
40
40
|
|
|
41
|
-
expect(JSON.parse<f64>("1e+21").toString()).toBe(1e21.toString());
|
|
41
|
+
expect(JSON.parse<f64>("1e+21").toString()).toBe((1e21).toString());
|
|
42
42
|
});
|
|
@@ -4,4 +4,4 @@ import { describe, expect } from "./lib";
|
|
|
4
4
|
describe("Should deserialize complex objects", () => {
|
|
5
5
|
const input = '{"a":{"b":{"c":[{"d":"random value 1"},{"e":["value 2","value 3"]}],"f":{"g":{"h":[1,2,3],"i":{"j":"nested value"}}}},"k":"simple value"},"l":[{"m":"another value","n":{"o":"deep nested","p":[{"q":"even deeper"},"final value"]}}],"r":null}';
|
|
6
6
|
expect(JSON.stringify(JSON.parse<Map<string, JSON.Raw>>(input))).toBe(input);
|
|
7
|
-
})
|
|
7
|
+
});
|
|
@@ -11,8 +11,8 @@ describe("Should deserialize JSON.Raw", () => {
|
|
|
11
11
|
|
|
12
12
|
describe("Should serialize Map<string, JSON.Raw>", () => {
|
|
13
13
|
const m1 = new Map<string, JSON.Raw>();
|
|
14
|
-
m1.set("hello", new JSON.Raw("
|
|
15
|
-
m1.set("pos", new JSON.Raw(
|
|
14
|
+
m1.set("hello", new JSON.Raw('"world"'));
|
|
15
|
+
m1.set("pos", new JSON.Raw('{"x":1.0,"y":2.0,"z":3.0}'));
|
|
16
16
|
|
|
17
17
|
expect(JSON.stringify(m1)).toBe('{"hello":"world","pos":{"x":1.0,"y":2.0,"z":3.0}}');
|
|
18
18
|
});
|
|
@@ -20,4 +20,4 @@ describe("Should serialize Map<string, JSON.Raw>", () => {
|
|
|
20
20
|
describe("Should deserialize Map<string, JSON.Raw>", () => {
|
|
21
21
|
const m1 = JSON.parse<Map<string, JSON.Raw>>('{"hello":"world","pos":{"x":1.0,"y":2.0,"z":3.0}}');
|
|
22
22
|
expect(JSON.stringify(m1)).toBe('{"hello":"world","pos":{"x":1.0,"y":2.0,"z":3.0}}');
|
|
23
|
-
});
|
|
23
|
+
});
|
|
@@ -53,26 +53,24 @@ describe("Should ignore properties decorated with @omit", () => {
|
|
|
53
53
|
});
|
|
54
54
|
|
|
55
55
|
describe("Should deserialize structs", () => {
|
|
56
|
-
expect(
|
|
57
|
-
JSON.stringify(JSON.parse<Vec3>('{"x":3.4,"y":1.2,"z":8.3}')),
|
|
58
|
-
).toBe('{"x":3.4,"y":1.2,"z":8.3}');
|
|
56
|
+
expect(JSON.stringify(JSON.parse<Vec3>('{"x":3.4,"y":1.2,"z":8.3}'))).toBe('{"x":3.4,"y":1.2,"z":8.3}');
|
|
59
57
|
});
|
|
60
58
|
|
|
61
59
|
describe("Should deserialize structs with whitespace", () => {
|
|
62
|
-
expect(
|
|
63
|
-
JSON.stringify(JSON.parse<Vec3>(' { "x" : 3.4 , "y" : 1.2 , "z" : 8.3 } ')),
|
|
64
|
-
).toBe('{"x":3.4,"y":1.2,"z":8.3}');
|
|
60
|
+
expect(JSON.stringify(JSON.parse<Vec3>(' { "x" : 3.4 , "y" : 1.2 , "z" : 8.3 } '))).toBe('{"x":3.4,"y":1.2,"z":8.3}');
|
|
65
61
|
});
|
|
66
62
|
|
|
67
63
|
describe("Should deserialize structs with nullable properties", () => {
|
|
68
|
-
expect(
|
|
69
|
-
JSON.stringify(JSON.parse<NullableObj>('{"bar":{"value":"test"}}'))
|
|
70
|
-
).toBe('{"bar":{"value":"test"}}');
|
|
64
|
+
expect(JSON.stringify(JSON.parse<NullableObj>('{"bar":{"value":"test"}}'))).toBe('{"bar":{"value":"test"}}');
|
|
71
65
|
|
|
72
|
-
expect(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
66
|
+
expect(JSON.stringify(JSON.parse<NullableObj>('{"bar":null}'))).toBe('{"bar":null}');
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
describe("Should deserialize structs with nullable arrays in properties", () => {
|
|
70
|
+
expect(JSON.stringify(JSON.parse<NullableArrayObj>('{"bars":[{"value":"test"}]}'))).toBe('{"bars":[{"value":"test"}]}');
|
|
71
|
+
|
|
72
|
+
expect(JSON.stringify(JSON.parse<NullableArrayObj>('{"bars":null}'))).toBe('{"bars":null}');
|
|
73
|
+
});
|
|
76
74
|
|
|
77
75
|
// describe("Should serialize Suite struct", () => {
|
|
78
76
|
|
|
@@ -118,33 +116,45 @@ class Player {
|
|
|
118
116
|
|
|
119
117
|
@json
|
|
120
118
|
class ObjWithStrangeKey<T> {
|
|
119
|
+
|
|
121
120
|
@alias('a\\\t"\x02b`c')
|
|
122
121
|
data!: T;
|
|
123
122
|
}
|
|
124
123
|
|
|
124
|
+
|
|
125
125
|
@json
|
|
126
126
|
class ObjectWithFloat {
|
|
127
127
|
f!: f64;
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
+
|
|
130
131
|
@json
|
|
131
132
|
class OmitIf {
|
|
132
133
|
x: i32 = 1;
|
|
133
134
|
|
|
135
|
+
|
|
134
136
|
@omitif("this.y == -1")
|
|
135
137
|
y: i32 = -1;
|
|
136
138
|
z: i32 = 1;
|
|
137
139
|
|
|
140
|
+
|
|
138
141
|
@omitnull()
|
|
139
142
|
foo: string | null = null;
|
|
140
143
|
}
|
|
141
144
|
|
|
145
|
+
|
|
142
146
|
@json
|
|
143
147
|
class NullableObj {
|
|
144
148
|
bar: Bar | null = null;
|
|
145
149
|
}
|
|
146
150
|
|
|
151
|
+
@json
|
|
152
|
+
class NullableArrayObj {
|
|
153
|
+
bars: Bar[] | null = null;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
|
|
147
157
|
@json
|
|
148
158
|
class Bar {
|
|
149
159
|
value: string = "";
|
|
150
|
-
}
|
|
160
|
+
}
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
import { BACK_SLASH } from "../../custom/chars";
|
|
2
2
|
import { DESERIALIZE_ESCAPE_TABLE, ESCAPE_HEX_TABLE } from "../../globals/tables";
|
|
3
3
|
|
|
4
|
-
const SPLAT_92 = i16x8.splat(92); /* \ */
|
|
5
|
-
|
|
6
4
|
/**
|
|
7
5
|
* Deserializes strings back into into their original form using SIMD operations
|
|
8
6
|
* @param src string to deserialize
|
|
@@ -11,6 +9,7 @@ const SPLAT_92 = i16x8.splat(92); /* \ */
|
|
|
11
9
|
*/
|
|
12
10
|
// todo: optimize and stuff. it works, its not pretty. ideally, i'd like this to be (nearly) branchless
|
|
13
11
|
export function deserializeString_SIMD(srcStart: usize, srcEnd: usize, dst: usize): usize {
|
|
12
|
+
const SPLAT_92 = i16x8.splat(92); /* \ */
|
|
14
13
|
let src_ptr = srcStart + 2;
|
|
15
14
|
let dst_ptr = changetype<usize>(dst);
|
|
16
15
|
|
|
@@ -10,8 +10,7 @@ export function deserializeStructArray<T extends unknown[]>(srcStart: usize, src
|
|
|
10
10
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
11
11
|
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
|
|
12
12
|
|
|
13
|
-
if (srcStart - srcEnd == 0)
|
|
14
|
-
throw new Error("Input string had zero length or was all whitespace");
|
|
13
|
+
if (srcStart - srcEnd == 0) throw new Error("Input string had zero length or was all whitespace");
|
|
15
14
|
|
|
16
15
|
if (load<u16>(srcStart) != BRACKET_LEFT) throw new Error("Expected '[' at start of object at position " + (srcEnd - srcStart).toString());
|
|
17
16
|
if (load<u16>(srcEnd - 2) != BRACKET_RIGHT) throw new Error("Expected ']' at end of object at position " + (srcEnd - srcStart).toString());
|
|
@@ -21,7 +20,7 @@ export function deserializeStructArray<T extends unknown[]>(srcStart: usize, src
|
|
|
21
20
|
if (code == BRACE_LEFT && depth++ == 0) {
|
|
22
21
|
lastIndex = srcStart;
|
|
23
22
|
} else if (code == BRACE_RIGHT && --depth == 0) {
|
|
24
|
-
out.push(JSON.__deserialize<valueof<T>>(lastIndex, srcStart += 2));
|
|
23
|
+
out.push(JSON.__deserialize<valueof<T>>(lastIndex, (srcStart += 2)));
|
|
25
24
|
}
|
|
26
25
|
srcStart += 2;
|
|
27
26
|
}
|
|
@@ -17,8 +17,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
|
|
|
17
17
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
18
18
|
while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2; // would like to optimize this later
|
|
19
19
|
|
|
20
|
-
if (srcStart - srcEnd == 0)
|
|
21
|
-
throw new Error("Input string had zero length or was all whitespace");
|
|
20
|
+
if (srcStart - srcEnd == 0) throw new Error("Input string had zero length or was all whitespace");
|
|
22
21
|
if (load<u16>(srcStart) != BRACE_LEFT) throw new Error("Expected '{' at start of object at position " + (srcEnd - srcStart).toString());
|
|
23
22
|
if (load<u16>(srcEnd - 2) != BRACE_RIGHT) throw new Error("Expected '}' at end of object at position " + (srcEnd - srcStart).toString());
|
|
24
23
|
|
|
@@ -32,7 +31,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
|
|
|
32
31
|
keyEnd = srcStart;
|
|
33
32
|
// console.log("Key: " + ptrToStr(lastIndex, srcStart));
|
|
34
33
|
// console.log("Next: " + String.fromCharCode(load<u16>(srcStart + 2)));
|
|
35
|
-
while (isSpace((code = load<u16>((srcStart += 2))))) {
|
|
34
|
+
while (isSpace((code = load<u16>((srcStart += 2))))) {}
|
|
36
35
|
if (code !== COLON) throw new Error("Expected ':' after key at position " + (srcEnd - srcStart).toString());
|
|
37
36
|
isKey = false;
|
|
38
37
|
} else {
|
|
@@ -169,4 +168,4 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
|
|
|
169
168
|
}
|
|
170
169
|
}
|
|
171
170
|
return out;
|
|
172
|
-
}
|
|
171
|
+
}
|