json-as 0.5.65 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -60,6 +60,7 @@ const player: Player = {
60
60
  };
61
61
 
62
62
  const stringified = JSON.stringify<Player>(player);
63
+ // Alternative: use JSON.serializeTo(player, out);
63
64
 
64
65
  const parsed = JSON.parse<Player>(stringified);
65
66
  ```
@@ -189,7 +189,7 @@ class HttpResp {
189
189
 
190
190
  describe("Deser externals", () => {
191
191
  it("should deserialize valid JSON strings", () => {
192
- canDeser<Map4>('\n{"a":\r"\\\\",\n\r"b":"}","c":"][","d"\t:\t"\\""}', { a: '\\', b: '}', c: '][', d: '"' })
192
+ canDeser<Map4>('{"a":\r"\\\\",\n\r"b":"}","c":"][","d"\t:\t"\\""}', { a: '\\', b: '}', c: '][', d: '"' })
193
193
  canDeser<Vec3>('{"x":0.4,"y":1.4,"z":0.0}', { x: 0.4, y: 1.4, z: 0 })
194
194
  canDeser<HttpResp>('{"statusCode":200,"headers":[["Conn\\\\ection","close"],["Content-Length","375"],["ETag","W/\\"177-/Ihew5Z+fiI8NLbTM2Wyphl/PFY\\""]],\n"body":"{\\n \\\"args\\\": {},\\n \\\"headers\\\": {\\n \\\"content-length\\\": \\\"0\\\",\\n \\\"accept\\\": \\\"*/*\\\" \\n}}"}',
195
195
  {
@@ -1,5 +1,6 @@
1
1
  import { StringSink } from "as-string-sink/assembly";
2
2
  import { isSpace } from "util/string";
3
+ import { E_INVALIDDATE } from "util/error";
3
4
  import {
4
5
  backSlashCode,
5
6
  commaCode,
@@ -101,6 +102,87 @@ export namespace JSON {
101
102
  );
102
103
  }
103
104
  }
105
+ /**
106
+ * Stringifies valid JSON data.
107
+ * ```js
108
+ * JSON.stringify<T>(data)
109
+ * ```
110
+ * @param data T
111
+ * @returns string
112
+ */
113
+ // @ts-ignore: Decorator
114
+ @inline export function stringifyTo<T>(data: T, out: string): void {
115
+ // String
116
+ if (isString<T>() && data != null) {
117
+ out = serializeString(data as string);
118
+ return;
119
+ } else if (isBoolean<T>()) {
120
+ out = data ? "true" : "false";
121
+ return;
122
+ } else if (isNullable<T>() && data == null) {
123
+ out = "null";
124
+ return;
125
+ // @ts-ignore
126
+ } else if ((isInteger<T>() || isFloat<T>()) && isFinite(data)) {
127
+ // @ts-ignore
128
+ out = data.toString();
129
+ return;
130
+ // @ts-ignore: Hidden function
131
+ } else if (isDefined(data.__JSON_Serialize)) {
132
+ // @ts-ignore: Hidden function
133
+ out = data.__JSON_Serialize();
134
+ return;
135
+ } else if (data instanceof Date) {
136
+ out = data.toISOString();
137
+ return;
138
+ } else if (isArrayLike<T>()) {
139
+ // @ts-ignore
140
+ if (data.length == 0) {
141
+ out = emptyArrayWord;
142
+ return;
143
+ // @ts-ignore
144
+ } else if (isString<valueof<T>>()) {
145
+ out = "[";
146
+ // @ts-ignore
147
+ for (let i = 0; i < data.length - 1; i++) {
148
+ // @ts-ignore
149
+ out += serializeString(unchecked(data[i]));
150
+ out += commaWord;
151
+ }
152
+ // @ts-ignore
153
+ out += serializeString(unchecked(data[data.length - 1]));
154
+ out += rightBracketWord;
155
+ return;
156
+ // @ts-ignore
157
+ } else if (isBoolean<valueof<T>>()) {
158
+ // @ts-ignore
159
+ out = leftBracketWord + data.join(commaWord) + rightBracketWord;
160
+ return;
161
+ // @ts-ignore
162
+ } else if (isFloat<valueof<T>>() || isInteger<valueof<T>>()) {
163
+ // @ts-ignore
164
+ out = leftBracketWord + data.join(commaWord) + rightBracketWord;
165
+ return;
166
+ } else {
167
+ let result = new StringSink(leftBracketWord);
168
+ // @ts-ignore
169
+ for (let i = 0; i < data.length - 1; i++) {
170
+ // @ts-ignore
171
+ result.write(JSON.stringify(unchecked(data[i])));
172
+ result.write(commaWord);
173
+ }
174
+ // @ts-ignore
175
+ result.write(JSON.stringify(unchecked(data[data.length - 1])));
176
+ result.write(rightBracketWord);
177
+ out = result.toString();
178
+ return;
179
+ }
180
+ } else {
181
+ throw new Error(
182
+ `Could not serialize data of type ${nameof<T>()}. Make sure to add the correct decorators to classes.`
183
+ );
184
+ }
185
+ }
104
186
  /**
105
187
  * Parses valid JSON strings into their original format.
106
188
  * ```js
@@ -133,7 +215,7 @@ export namespace JSON {
133
215
  return parseObject<T>(data.trimStart());
134
216
  } else if (idof<nonnull<T>>() == idof<Date>()) {
135
217
  // @ts-ignore
136
- return Date.fromString(data);
218
+ return parseDate(data);
137
219
  } else {
138
220
  // @ts-ignore
139
221
  throw new Error(
@@ -210,7 +292,7 @@ export namespace JSON {
210
292
  return parseObject<T>(data.trimStart());
211
293
  } else if (idof<nonnull<T>>() == idof<Date>()) {
212
294
  // @ts-ignore
213
- return Date.fromString(data);
295
+ return parseDate(data);
214
296
  } else {
215
297
  // @ts-ignore
216
298
  throw new Error(
@@ -630,3 +712,95 @@ export namespace JSON {
630
712
  }
631
713
  return result;
632
714
  }
715
+
716
+ // @ts-ignore: decorator
717
+ @inline const
718
+ MILLIS_PER_DAY = 1000 * 60 * 60 * 24,
719
+ MILLIS_PER_HOUR = 1000 * 60 * 60,
720
+ MILLIS_PER_MINUTE = 1000 * 60,
721
+ MILLIS_PER_SECOND = 1000,
722
+
723
+ YEARS_PER_EPOCH = 400,
724
+ DAYS_PER_EPOCH = 146097,
725
+ EPOCH_OFFSET = 719468, // Jan 1, 1970
726
+ MILLIS_LIMIT = 8640000000000000;
727
+
728
+ function parseDate(dateTimeString: string): Date {
729
+ if (!dateTimeString.length) throw new RangeError(E_INVALIDDATE);
730
+ var
731
+ hour: i32 = 0,
732
+ min: i32 = 0,
733
+ sec: i32 = 0,
734
+ ms: i32 = 0;
735
+
736
+ let dateString = dateTimeString;
737
+ let posT = dateTimeString.indexOf("T");
738
+ if (~posT) {
739
+ // includes a time component
740
+ let timeString: string;
741
+ dateString = dateTimeString.substring(0, posT);
742
+ timeString = dateTimeString.substring(posT + 1);
743
+ // parse the HH-MM-SS component
744
+ let timeParts = timeString.split(":");
745
+ let len = timeParts.length;
746
+ if (len <= 1) throw new RangeError(E_INVALIDDATE);
747
+
748
+ hour = i32.parse(timeParts[0]);
749
+ min = i32.parse(timeParts[1]);
750
+ if (len >= 3) {
751
+ let secAndMs = timeParts[2];
752
+ let posDot = secAndMs.indexOf(".");
753
+ if (~posDot) {
754
+ // includes milliseconds
755
+ sec = i32.parse(secAndMs.substring(0, posDot));
756
+ ms = i32.parse(secAndMs.substring(posDot + 1));
757
+ } else {
758
+ sec = i32.parse(secAndMs);
759
+ }
760
+ }
761
+ }
762
+ // parse the YYYY-MM-DD component
763
+ let parts = dateString.split("-");
764
+ let year = i32.parse(parts[0]);
765
+ let month = 1, day = 1;
766
+ let len = parts.length;
767
+ if (len >= 2) {
768
+ month = i32.parse(parts[1]);
769
+ if (len >= 3) {
770
+ day = i32.parse(parts[2]);
771
+ }
772
+ }
773
+ return new Date(epochMillis(year, month, day, hour, min, sec, ms));
774
+ }
775
+
776
+ function epochMillis(
777
+ year: i32,
778
+ month: i32,
779
+ day: i32,
780
+ hour: i32,
781
+ minute: i32,
782
+ second: i32,
783
+ milliseconds: i32
784
+ ): i64 {
785
+ return (
786
+ daysSinceEpoch(year, month, day) * MILLIS_PER_DAY +
787
+ hour * MILLIS_PER_HOUR +
788
+ minute * MILLIS_PER_MINUTE +
789
+ second * MILLIS_PER_SECOND +
790
+ milliseconds
791
+ );
792
+ }
793
+
794
+ function daysSinceEpoch(y: i32, m: i32, d: i32): i64 {
795
+ y -= i32(m <= 2);
796
+ let era = <u32>floorDiv(y, YEARS_PER_EPOCH);
797
+ let yoe = <u32>y - era * YEARS_PER_EPOCH; // [0, 399]
798
+ let doy = <u32>(153 * (m + (m > 2 ? -3 : 9)) + 2) / 5 + d - 1; // [0, 365]
799
+ let doe = yoe * 365 + yoe / 4 - yoe / 100 + doy; // [0, 146096]
800
+ return <i64><i32>(era * 146097 + doe - EPOCH_OFFSET);
801
+ }
802
+
803
+ // @ts-ignore: decorator
804
+ @inline function floorDiv<T extends number>(a: T, b: T): T {
805
+ return (a - (a < 0 ? b - 1 : 0)) / b as T;
806
+ }
package/assembly/test.ts CHANGED
@@ -1,11 +1,11 @@
1
- import { bench, blackbox } from "as-bench/assembly/bench";
2
1
  import { JSON } from "./src/json";
2
+
3
3
  // @ts-ignore
4
4
  @json
5
5
  class Vec3 {
6
- x: f64;
7
- y: f64;
8
- z: f64;
6
+ x: f64 = 3.4;
7
+ y: f64 = 1.2;
8
+ z: f64 = 8.3;
9
9
  }
10
10
 
11
11
  // @ts-ignore
@@ -13,22 +13,18 @@ class Vec3 {
13
13
  class Player {
14
14
  firstName: string;
15
15
  lastName: string;
16
- lastActive: i32[];
16
+ lastActive: Date;
17
17
  age: i32;
18
18
  pos: Vec3 | null;
19
19
  isVerified: boolean;
20
20
  }
21
21
 
22
- const vec: Vec3 = {
23
- x: 3.4,
24
- y: 1.2,
25
- z: 8.3,
26
- }
22
+ const vec = new Vec3();
27
23
 
28
24
  const player: Player = {
29
25
  firstName: "Emmet",
30
26
  lastName: "West",
31
- lastActive: [8, 27, 2022],
27
+ lastActive: new Date(0),
32
28
  age: 23,
33
29
  pos: {
34
30
  x: 3.4,
@@ -38,13 +34,17 @@ const player: Player = {
38
34
  isVerified: true,
39
35
  }
40
36
 
41
- console.log("Original: " + JSON.stringify(vec));
37
+ let out = "";
38
+
39
+ JSON.stringifyTo(vec, out);
40
+
41
+ console.log("Original: " + out);
42
42
  //console.log("Revised: " + vec.__JSON_Deserialize('{"x":3,"y":1,"z":8}').__JSON_Serialize());
43
- console.log("Implemented: " + JSON.stringify(JSON.parse<Vec3>('{"x":3.4,"y":1.2,"z":8.3}')));
43
+ console.log("Implemented: " + JSON.stringify(JSON.parse<Vec3>('{}')));
44
44
 
45
45
  console.log("Original: " + JSON.stringify(player));
46
46
  //console.log("Revised: " + vec.__JSON_Deserialize('{"x":3,"y":1,"z":8}').__JSON_Serialize());
47
- console.log("Implemented: " + JSON.stringify(JSON.parse<Player>('{"firstName":"Emmet","lastName":"West","lastActive":[8,27,2022],"age":23,"pos":{"x":3.4,"y":1.2,"z":8.3},"isVerified":true}')));
47
+ console.log("Implemented: " + JSON.stringify(JSON.parse<Player>('{"firstName":"Emmet","lastName":"West","lastActive":2023-11-16T04:06:35.108285303Z,"age":23,"pos":{"x":3.4,"y":1.2,"z":8.3},"isVerified":true}')));
48
48
  /*
49
49
  // 9,325,755
50
50
  bench("Stringify Object (Vec3)", () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-as",
3
- "version": "0.5.65",
3
+ "version": "0.6.0",
4
4
  "description": "JSON encoder/decoder for AssemblyScript",
5
5
  "types": "assembly/index.ts",
6
6
  "author": "Jairus Tanaka",
@@ -16,8 +16,7 @@
16
16
  "scripts": {
17
17
  "test:aspect": "asp",
18
18
  "bench:astral": "astral --optimizeLevel 3 --shrinkLevel 0 --converge --noAssert --uncheckedBehavior never --runtime stub",
19
- "build:test": "asc assembly/test.ts -o build/test.wasm --transform ./transform/lib/index.js --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json --optimizeLevel 3 --shrinkLevel 0 --noAssert --uncheckedBehavior always --converge --runtime stub",
20
- "build:test:debug": "asc assembly/test.ts -o build/test.wasm --transform ./transform --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json --optimizeLevel 0 --shrinkLevel 0 --noAssert --uncheckedBehavior always --runtime stub",
19
+ "build:test": "asc assembly/test.ts -o build/test.wasm --transform ./transform --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json --optimizeLevel 0 --shrinkLevel 0 --noAssert --uncheckedBehavior always --runtime stub",
21
20
  "build:bench": "asc bench/benchmark.ts -o bench/benchmark.wasm --transform ./transform --config ./node_modules/@assemblyscript/wasi-shim/asconfig.json --optimizeLevel 3 --shrinkLevel 0 --converge --noAssert --uncheckedBehavior always --runtime stub",
22
21
  "bench:wasmtime": "wasmtime ./bench/benchmark.wasm",
23
22
  "bench:wavm": "wavm run ./bench/benchmark.wasm",
@@ -33,14 +32,14 @@
33
32
  "@as-tral/cli": "^2.0.1",
34
33
  "@assemblyscript/wasi-shim": "^0.1.0",
35
34
  "as-bench": "^0.0.0-alpha",
36
- "assemblyscript": "^0.27.9",
35
+ "assemblyscript": "^0.27.18",
37
36
  "assemblyscript-prettier": "^3.0.1",
38
37
  "benchmark": "^2.1.4",
39
38
  "kati": "^0.6.2",
40
39
  "microtime": "^3.1.1",
41
- "prettier": "^3.0.2",
42
- "tinybench": "^2.5.0",
43
- "typescript": "^5.1.6",
40
+ "prettier": "^3.1.0",
41
+ "tinybench": "^2.5.1",
42
+ "typescript": "^5.2.2",
44
43
  "visitor-as": "^0.11.4"
45
44
  },
46
45
  "dependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@json-as/transform",
3
- "version": "0.5.65",
3
+ "version": "0.6.0",
4
4
  "description": "JSON encoder/decoder for AssemblyScript",
5
5
  "main": "./lib/index.js",
6
6
  "author": "Jairus Tanaka",
@@ -18,6 +18,7 @@ class SchemaData {
18
18
  public encodeStmts: string[] = [];
19
19
  public setDataStmts: string[] = [];
20
20
  }
21
+
21
22
  class AsJSONTransform extends BaseVisitor {
22
23
  public schemasList: SchemaData[] = [];
23
24
  public currentClass!: SchemaData;
@@ -137,18 +138,18 @@ class AsJSONTransform extends BaseVisitor {
137
138
  `
138
139
  );
139
140
  } else {
140
- this.currentClass.encodeStmts.push(
141
- `"${name}":\${JSON.stringify<${type}>(this.${name})},`
142
- );
143
- // @ts-ignore
144
- this.currentClass.setDataStmts.push(
145
- `if (key.equals("${name}")) {
141
+ this.currentClass.encodeStmts.push(
142
+ `"${name}":\${JSON.stringify<${type}>(this.${name})},`
143
+ );
144
+ // @ts-ignore
145
+ this.currentClass.setDataStmts.push(
146
+ `if (key.equals("${name}")) {
146
147
  this.${name} = __parseObjectValue<${type}>(val_start ? data.slice(val_start, val_end) : data);
147
148
  return;
148
149
  }
149
150
  `
150
- );
151
- }
151
+ );
152
+ }
152
153
  }
153
154
  }
154
155