json-as 0.5.51 → 0.5.52

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/LICENSE CHANGED
@@ -1,21 +1,21 @@
1
- MIT License
1
+ MIT License
2
2
 
3
- Copyright (c) 2023 Jairus Tanaka <jairus.v.tanaka@outlook.com>
3
+ Copyright (c) 2023 Jairus Tanaka <jairus.v.tanaka@outlook.com>
4
4
 
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
11
 
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
14
 
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,6 +1,6 @@
1
1
  import { JSON } from "..";
2
2
  import { backSlashCode, quoteCode } from "../src/chars";
3
- import { atoi_fast, parseSciInteger, unsafeCharCodeAt } from "../src/util";
3
+ import { atoi_fast, parseSciInteger, snip_fast, unsafeCharCodeAt } from "../src/util";
4
4
  import { HASH } from "util/hash";
5
5
 
6
6
 
@@ -66,15 +66,31 @@ const vec: Vec3 = {
66
66
  y: 1,
67
67
  z: 8,
68
68
  }
69
- /*
69
+
70
+ bench("Parse Number SNIP", () => {
71
+ blackbox<i32>(snip_fast<i32>("12345"));
72
+ });
73
+
74
+ bench("Parse Number ATOI", () => {
75
+ blackbox<i32>(atoi_fast<i32>("12345"));
76
+ })
77
+
78
+ bench("Parse Number STDLIB", () => {
79
+ blackbox<i32>(i32.parse("12345"));
80
+ });
81
+
82
+ bench("Parse Number OLD", () => {
83
+ blackbox<i32>(parseSciInteger<i32>("12345"));
84
+ });
85
+
70
86
  bench("Stringify Object (Vec3)", () => {
71
87
  blackbox<string>(vec.__JSON_Serialize());
72
88
  });
73
89
 
74
90
  // TODO: Make this allocate without crashing
75
- bench("Parse Object (Vec3)", () => {
91
+ /*bench("Parse Object (Vec3)", () => {
76
92
  blackbox<Vec3>(vec.__JSON_Deserialize('{"x":0,"y":0,"z":0}', vec));
77
- });
93
+ });*/
78
94
 
79
95
  bench("Stringify Number Array", () => {
80
96
  blackbox(JSON.stringify<i32[]>([1, 2, 3]));
@@ -87,7 +103,7 @@ bench("Parse Number Array", () => {
87
103
  bench("Stringify String", () => {
88
104
  blackbox(JSON.stringify(blackbox('Hello "World!')));
89
105
  });
90
- */
106
+
91
107
  bench("Parse String", () => {
92
108
  blackbox(JSON.parse<string>(blackbox('"Hello "World!"')));
93
109
  });
@@ -1,8 +1,8 @@
1
1
  import { JSON } from "..";
2
- function canSerde<T>(data: T): void {
3
- const serialized = JSON.stringify<T>(data);
4
- const deserialized = JSON.stringify<T>(JSON.parse<T>(serialized));
5
- expect(serialized).toBe(deserialized);
2
+ function canSerde<T>(data: T, toBe: string = ""): void {
3
+ if (!toBe) toBe = JSON.stringify<T>(data);
4
+ const deserialized = JSON.stringify<T>(JSON.parse<T>(JSON.stringify(data)));
5
+ expect(deserialized).toBe(toBe);
6
6
  }
7
7
 
8
8
  // @ts-ignore
@@ -53,16 +53,13 @@ describe("Ser/de Numbers", () => {
53
53
  });
54
54
 
55
55
  it("should ser/de floats", () => {
56
- canSerde<f64>(7.23);
57
- canSerde<f64>(10e2);
58
- canSerde<f64>(10e2);
56
+ canSerde<f64>(7.23, "7.23");
57
+ canSerde<f64>(10e2, "1000.0");
59
58
 
60
- canSerde<f64>(123456e-5);
59
+ canSerde<f64>(123456e-5, "1.23456");
61
60
 
62
- canSerde<f64>(123456e-5);
63
-
64
- canSerde<f64>(0.0);
65
- canSerde<f64>(7.23);
61
+ canSerde<f64>(0.0, "0.0");
62
+ canSerde<f64>(7.23, "7.23");
66
63
  });
67
64
 
68
65
  it("should ser/de booleans", () => {
@@ -161,6 +158,6 @@ describe("Ser/de Objects", () => {
161
158
  z: 8.3,
162
159
  },
163
160
  isVerified: true,
164
- });
161
+ }, '{"firstName":"Emmet","lastName":"West","lastActive":[8,27,2022],"age":23,"pos":{"x":3.4,"y":1.2,"z":8.3},"isVerified":true}');
165
162
  });
166
163
  });
@@ -1,5 +1,5 @@
1
1
  import { StringSink } from "as-string-sink/assembly";
2
- import { isSpace, CharCode } from "util/string";
2
+ import { isSpace } from "util/string";
3
3
  import {
4
4
  backSlashCode,
5
5
  commaCode,
@@ -21,7 +21,7 @@ import {
21
21
  uCode,
22
22
  emptyArrayWord,
23
23
  } from "./chars";
24
- import { parseSciInteger, unsafeCharCodeAt } from "./util";
24
+ import { snip_fast, unsafeCharCodeAt } from "./util";
25
25
 
26
26
  /**
27
27
  * JSON Encoder/Decoder for AssemblyScript
@@ -184,7 +184,7 @@ export namespace JSON {
184
184
  } else if (
185
185
  char === 117 &&
186
186
  load<u64>(changetype<usize>(data) + <usize>((i + 1) << 1)) ===
187
- 27584753879220272
187
+ 27584753879220272
188
188
  ) {
189
189
  result += "\u000b";
190
190
  i += 4;
@@ -359,7 +359,7 @@ export namespace JSON {
359
359
  if (
360
360
  char === 117 &&
361
361
  load<u64>(changetype<usize>(data) + <usize>((i + 1) << 1)) ===
362
- 27584753879220272
362
+ 27584753879220272
363
363
  ) {
364
364
  result += "\u000b";
365
365
  i += 4;
@@ -385,12 +385,10 @@ export namespace JSON {
385
385
  }
386
386
 
387
387
  // @ts-ignore
388
- @inline export function parseNumber<
389
- T
390
- >(data: string): T {
388
+ @inline export function parseNumber<T>(data: string): T {
391
389
  if (isInteger<T>()) {
392
390
  // @ts-ignore
393
- return parseSciInteger<T>(data);
391
+ return snip_fast<T>(data);
394
392
  }
395
393
  // @ts-ignore
396
394
  const type: T = 0;
@@ -401,9 +399,7 @@ export namespace JSON {
401
399
  }
402
400
 
403
401
  // @ts-ignore
404
- @inline function parseObject<
405
- T
406
- >(data: string): T {
402
+ @inline function parseObject<T>(data: string): T {
407
403
  let schema: nonnull<T> = changetype<nonnull<T>>(
408
404
  __new(offsetof<nonnull<T>>(), idof<nonnull<T>>())
409
405
  );
@@ -1,5 +1,5 @@
1
1
  import { StringSink } from "as-string-sink/assembly";
2
- import { isSpace } from "util/string";
2
+ import { CharCode, isSpace } from "util/string";
3
3
  import { backSlashCode, quoteCode } from "./chars";
4
4
 
5
5
  // @ts-ignore
@@ -79,6 +79,159 @@ export function getArrayDepth<T>(depth: i32 = 1): i32 {
79
79
  }
80
80
  }
81
81
 
82
+ // Scientific Notation Integer Parsing - SNIP
83
+ // This is absolutely the fastest algorithm I could think of while adding full support for Scientific Notation
84
+ // Loads 32 bits and retrieves the high/low bits
85
+ // Here are some benchmarks
86
+ // Parsing: "12345"
87
+ // Results are spread over 5000ms
88
+ // SNIP: 207M iterations
89
+ // ATOI: 222M iterations
90
+ // STD (parseInt): 162M iterations
91
+ export function snip_fast<T extends number>(str: string, offset: u32 = 0): T {
92
+ let ch: u32 = load<u32>(changetype<usize>(str));
93
+ const h = ch & 0xFFFF;
94
+ if (h === 48) return 0 as T;
95
+ const isNegative = h === 45; // Check if the number is negative
96
+ let val: T = 0 as T;
97
+ const len = u32(str.length << 1);
98
+ if (isNegative) {
99
+ if ((ch >> 16) === 48) return -0 as T;
100
+ offset += 2;
101
+ if (len >= 4) {
102
+ // 32-bit route
103
+ for (; offset < (len - 3); offset += 4) {
104
+ ch = load<u32>(changetype<usize>(str) + <usize>offset);
105
+ const low = ch & 0xFFFF;
106
+ const high = ch >> 16;
107
+ // 9 is 57. The highest group of two numbers is 114, so if a e or an E is included, this will fire.
108
+ if (low > 57) {
109
+ // The first char (f) is E or e
110
+ // We push the offset up by two and apply the notation.
111
+ offset += 2;
112
+ let exp: i32 = atoi_fast<i32>(str, offset);
113
+ if (exp < 0) {
114
+ for (let i = 0; i < exp; i++) {
115
+ val = (val / 10) as T;
116
+ }
117
+ } else {
118
+ for (let i = 0; i < exp; i++) {
119
+ val = (val * 10) as T;
120
+ }
121
+ }
122
+ return -val as T;
123
+ } else if (high > 57) {
124
+ // The first char (f) is E or e
125
+ // We push the offset up by two and apply the notation.
126
+ offset += 4;
127
+ let exp: i32 = atoi_fast<i32>(str, offset);
128
+ if (exp < 0) {
129
+ for (let i = 0; i < exp; i++) {
130
+ val = (val / 10) as T;
131
+ }
132
+ } else {
133
+ for (let i = 0; i < exp; i++) {
134
+ val = (val * 10) as T;
135
+ }
136
+ }
137
+ return -val as T;
138
+ } else {
139
+ val = (val * 100 + ((low - 48) * 10) + (high - 48)) as T;
140
+ }
141
+ }
142
+ }
143
+ // Finish up the remainder with 16 bits.
144
+ for (; offset < len; offset += 2) {
145
+ ch = load<u16>(changetype<usize>(str) + <usize>offset);
146
+ // 9 is 57. E and e are larger. Assumes valid JSON.
147
+ if (ch > 57) {
148
+ // The first char (f) is E or e
149
+ // We push the offset up by two and apply the notation.
150
+ offset += 2;
151
+ let exp: i32 = atoi_fast<i32>(str, offset);
152
+ if (exp < 0) {
153
+ for (let i = 0; i > exp; i--) {
154
+ val = (val / 10) as T;
155
+ }
156
+ } else {
157
+ for (let i = 0; i < exp; i++) {
158
+ val = (val * 10) as T;
159
+ }
160
+ }
161
+ return -val as T;
162
+ } else {
163
+ val = (val * 10) + (ch - 48) as T;
164
+ }
165
+ }
166
+ return -val as T;
167
+ } else {
168
+ if (len >= 4) {
169
+ // Duplet 16 bit lane load
170
+ for (; offset < (len - 3); offset += 4) {
171
+ ch = load<u32>(changetype<usize>(str) + <usize>offset);
172
+ const low = ch & 0xFFFF;
173
+ const high = ch >> 16;
174
+ // 9 is 57. The highest group of two numbers is 114, so if a e or an E is included, this will fire.
175
+ if (low > 57) {
176
+ // The first char (f) is E or e
177
+ // We push the offset up by two and apply the notation.
178
+ offset += 2;
179
+ let exp: i32 = atoi_fast<i32>(str, offset);
180
+ if (exp < 0) {
181
+ for (let i = 0; i < exp; i++) {
182
+ val = (val / 10) as T;
183
+ }
184
+ } else {
185
+ for (let i = 0; i < exp; i++) {
186
+ val = (val * 10) as T;
187
+ }
188
+ }
189
+ return val as T;
190
+ } else if (high > 57) {
191
+ offset += 4;
192
+ let exp: i32 = atoi_fast<i32>(str, offset);
193
+ if (exp < 0) {
194
+ for (let i = 0; i < exp; i++) {
195
+ val = (val / 10) as T;
196
+ }
197
+ } else {
198
+ for (let i = 0; i < exp; i++) {
199
+ val = (val * 10) as T;
200
+ }
201
+ }
202
+ return val as T;
203
+ } else {
204
+ // Optimized with multiplications and shifts.
205
+ val = (val * 100 + ((low - 48) * 10) + (high - 48)) as T;
206
+ }
207
+ }
208
+ }
209
+ // Cover the remaining numbers with 16 bit loads.
210
+ for (; offset < len; offset += 2) {
211
+ ch = load<u16>(changetype<usize>(str) + <usize>offset);
212
+ // 0's char is 48 and 9 is 57. Anything above this range would signify an exponent (e or E).
213
+ // e is 101 and E is 69.
214
+ if (ch > 57) {
215
+ offset += 2;
216
+ let exp: i32 = atoi_fast<i32>(str, offset);
217
+ if (exp < 0) {
218
+ for (let i = 0; i > exp; i--) {
219
+ val = (val / 10) as T;
220
+ }
221
+ } else {
222
+ for (let i = 0; i < exp; i++) {
223
+ val = (val * 10) as T;
224
+ }
225
+ }
226
+ return val as T;
227
+ } else {
228
+ val = (val * 10) + (ch - 48) as T;
229
+ }
230
+ }
231
+ return val as T;
232
+ }
233
+ }
234
+
82
235
  /**
83
236
  * Implementation of ATOI. Can be much much faster with SIMD.
84
237
  * Benchmark: 40-46m ops/s
@@ -86,33 +239,25 @@ export function getArrayDepth<T>(depth: i32 = 1): i32 {
86
239
 
87
240
  // @ts-ignore
88
241
  @inline
89
- export function atoi_fast<T extends number>(str: string, offset: i32 = 0): T {
242
+ export function atoi_fast<T extends number>(str: string, offset: u32 = 0): T {
90
243
  // @ts-ignore
91
244
  let val: T = 0;
92
- let firstChar = load<u16>(changetype<usize>(str) + <usize>offset);
93
- if (firstChar === 45) {
245
+ const len = u32(str.length << 1);
246
+ if (load<u16>(changetype<usize>(str) + <usize>offset) === 45) {
94
247
  offset += 2;
95
- for (; offset < str.length << 1; offset += 2) {
248
+ for (; offset < len; offset += 2) {
96
249
  // @ts-ignore
97
- val =
98
- (val << 1) +
99
- (val << 3) +
100
- (load<u16>(changetype<usize>(str) + <usize>offset) - 48);
101
- // We use load because in this case, there is no need to have bounds-checking
250
+ val = (val << 1) + (val << 3) + (load<u16>(changetype<usize>(str) + <usize>offset) - 48);
102
251
  }
103
252
  // @ts-ignore
104
- val = -val;
253
+ return -val;
105
254
  } else {
106
- for (; offset < str.length << 1; offset += 2) {
255
+ for (; offset < len; offset += 2) {
107
256
  // @ts-ignore
108
- val =
109
- (val << 1) +
110
- (val << 3) +
111
- (load<u16>(changetype<usize>(str) + <usize>offset) - 48);
112
- // We use load because in this case, there is no need to have bounds-checking
257
+ val = (val << 1) + (val << 3) + (load<u16>(changetype<usize>(str) + <usize>offset) - 48);
113
258
  }
259
+ return val;
114
260
  }
115
- return val;
116
261
  }
117
262
 
118
263
  /**
@@ -164,15 +309,15 @@ export function parseSciInteger<T extends number>(str: string): T {
164
309
  function sciNote<T extends number>(num: T): T {
165
310
  let res = 1;
166
311
  // @ts-ignore
167
- if (num > 0) {
168
- for (let i: T = 0; i < num; i++) {
169
- res *= 10;
170
- }
171
- } else {
172
- for (let i: T = 0; i < num; i++) {
173
- res /= 10;
174
- }
312
+ if (num > 0) {
313
+ for (let i: T = 0; i < num; i++) {
314
+ res *= 10;
175
315
  }
316
+ } else {
317
+ for (let i: T = 0; i < num; i++) {
318
+ res /= 10;
319
+ }
320
+ }
176
321
  // @ts-ignore
177
322
  return res;
178
323
  }
package/assembly/test.ts CHANGED
@@ -1,13 +1,11 @@
1
- import { JSON } from "./src/json";
2
- import { atoi_fast, parseSciInteger } from "./src/util";
3
- import * as a from "util/number";
4
- // "st\"ring\" w\"\"ith quotes\""
1
+ import { snip_fast } from "./src/util";
5
2
 
3
+ import { JSON } from "./src/json";
6
4
  @json
7
5
  class Vec3 {
8
- x!: f32;
9
- y!: f32;
10
- z!: f32;
6
+ x!: f64;
7
+ y!: f64;
8
+ z!: f64;
11
9
  }
12
10
 
13
11
  @json
@@ -39,6 +37,29 @@ const vec: Vec3 = {
39
37
  z: 8,
40
38
  };
41
39
 
40
+ function canSerde<T>(data: T, toBe: string = ""): void {
41
+ if (!toBe) toBe = JSON.stringify<T>(data);
42
+ const deserialized = JSON.stringify<T>(JSON.parse<T>(JSON.stringify(data)));
43
+ if (deserialized != toBe) {
44
+ console.log("Expected: " + toBe);
45
+ console.log("Actual: " + deserialized);
46
+ } else {
47
+ console.log("Passed Test")
48
+ }
49
+ }
50
+
51
+ canSerde<Player>({
52
+ firstName: "Emmet",
53
+ lastName: "West",
54
+ lastActive: [8, 27, 2022],
55
+ age: 23,
56
+ pos: {
57
+ x: 3.4,
58
+ y: 1.2,
59
+ z: 8.3,
60
+ },
61
+ isVerified: true,
62
+ }, '{"firstName":"Emmet","lastName":"West","lastActive":[8,27,2022],"age":23,"pos":{"x":3.4,"y":1.2,"z":8.3},"isVerified":true}');
42
63
  const serializedPlayer = JSON.stringify<Player>(player);
43
64
  console.log(serializedPlayer);
44
65
  const parsedPlayer = JSON.parse<Player>(serializedPlayer);
@@ -50,230 +71,19 @@ console.log(serializedVec3);
50
71
  const parsedVec3 = JSON.parse<Vec3>(serializedVec3);
51
72
  console.log(JSON.stringify(parsedVec3));
52
73
 
53
- console.log("atoi:");
54
- console.log("123 - " + parseSciInteger<i32>("123").toString());
55
- console.log("1230 - " + parseSciInteger<i32>("123e1").toString());
56
- console.log("12300 - " + parseSciInteger<i32>("123e2").toString());
57
- console.log("123000 - " + parseSciInteger<i32>("123e3").toString());
58
- console.log("12 - " + parseSciInteger<i32>("123e-1").toString());
59
- console.log(parseSciInteger<i32>("100").toString());
60
- console.log(parseSciInteger<i32>("-100").toString());
61
-
62
- console.log(JSON.stringify("abcdefg"));
63
- console.log('"abcdefg"')
64
- console.log(JSON.stringify('st"ring" w""ith quotes"'));
65
- console.log('"st\\"ring\\" w\\"\\"ith quotes\\""')
66
- console.log(JSON.stringify(['string "with random spa\nces and \nnewlines\n\n\n']));
67
- console.log(JSON.stringify(JSON.parse<string[]>(JSON.stringify(['string "with random spa\nces and \nnewlines\n\n\n']))));
68
- console.log('"string \\"with random spa\\nces and \\nnewlines\\n\\n\\n"')
69
- console.log(JSON.stringify('string with colon : comma , brace [ ] bracket { } and quote " and other quote "'));
70
- /*console.log(JSON.stringify(JSON.parse<string[]>(JSON.stringify([
71
- "abcdefg",
72
- 'st"ring" w""ith quotes"',
73
- 'string \t\r"with ran\tdom spa\nces and \nnewlines\n\n\n',
74
- 'string with colon : comma , brace [ ] bracket { } and quote " and other quote "',
75
- ]))));
76
- console.log('["abcdefg","st\"ring\" w\"\"ith quotes\"","string \t\r\"with ran\tdom spa\nces and \nnewlines\n\n\n","string with colon : comma , brace [ ] bracket { } and quote \" and other quote \""]');/*
77
- console.log(
78
- JSON.stringify(
79
- JSON.parse<string[]>(
80
- JSON.stringify([
81
- "abcdefg",
82
- 'st"ring" w""ith quotes"',
83
- 'string \t\r"with ran\tdom spa\nces and \nnewlines\n\n\n',
84
- 'string with colon : comma , brace [ ] bracket { } and quote " and other quote "',
85
- ])
86
- )
87
- )
88
- );
89
- /*
90
- const str = changetype<string>(new ArrayBuffer(6));
91
- console.log("istr:");
92
- console.log("123 - " + istr8(123));
93
- console.log("32 - " + istr8(32));
94
- console.log("3 - " + istr8(3));
95
-
96
- console.log(Uint8Array.wrap(changetype<ArrayBuffer>(istr8(12))).join(" "));
97
- console.log(load<u32>(changetype<usize>(istr8(12))).toString());
98
- @inline function istr8<
99
- T extends number
100
- >(int: T): string {
101
- if (int >= 100) {
102
- const str = changetype<string>(__new(6, idof<String>()));
103
- store<u16>(changetype<usize>(str), ((int / 100) % 10) + 48);
104
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48, 2);
105
- store<u16>(changetype<usize>(str), (int % 10) + 48, 4);
106
- return str;
107
- } else if (int >= 10) {
108
- const str = changetype<string>(__new(4, idof<String>()));
109
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48);
110
- store<u16>(changetype<usize>(str), (int % 10) + 48, 2);
111
- return str;
112
- } else {
113
- const str = changetype<string>(__new(2, idof<String>()));
114
- store<u16>(changetype<usize>(str), (int % 10) + 48);
115
- return str;
116
- }
117
- }
118
-
119
- export function istr16<T extends number>(int: T): string {
120
- if (int >= 10_000) {
121
- const str = changetype<string>(__new(10, idof<String>()));
122
- store<u16>(changetype<usize>(str), ((int / 10000) % 10) + 48);
123
- store<u16>(changetype<usize>(str), ((int / 1000) % 10) + 48, 2);
124
- store<u16>(changetype<usize>(str), ((int / 100) % 10) + 48, 4);
125
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48, 6);
126
- store<u16>(changetype<usize>(str), (int % 10) + 48, 8);
127
- return str;
128
- } else if (int >= 1_000) {
129
- const str = changetype<string>(__new(8, idof<String>()));
130
- store<u16>(changetype<usize>(str), ((int / 1000) % 10) + 48);
131
- store<u16>(changetype<usize>(str), ((int / 100) % 10) + 48, 2);
132
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48, 4);
133
- store<u16>(changetype<usize>(str), (int % 10) + 48, 6);
134
- return str;
135
- } else if (int >= 100) {
136
- const str = changetype<string>(__new(6, idof<String>()));
137
- store<u16>(changetype<usize>(str), ((int / 100) % 10) + 48);
138
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48, 2);
139
- store<u16>(changetype<usize>(str), (int % 10) + 48, 4);
140
- return str;
141
- } else if (int >= 10) {
142
- const str = changetype<string>(__new(4, idof<String>()));
143
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48);
144
- store<u16>(changetype<usize>(str), (int % 10) + 48, 2);
145
- return str;
146
- } else {
147
- const str = changetype<string>(__new(2, idof<String>()));
148
- store<u16>(changetype<usize>(str), (int % 10) + 48);
149
- return str;
150
- }
151
- }
152
-
153
- export function istr32<T extends number>(int: T): string {
154
- if (int >= 1_000_000_000) {
155
- const str = changetype<string>(__new(22, idof<String>()));
156
- store<u16>(changetype<usize>(str), ((int / 10000000000) % 10) + 48);
157
- store<u16>(changetype<usize>(str), ((int / 1000000000) % 10) + 48, 2);
158
- store<u16>(changetype<usize>(str), ((int / 100000000) % 10) + 48, 4);
159
- store<u16>(changetype<usize>(str), ((int / 10000000) % 10) + 48, 6);
160
- store<u16>(changetype<usize>(str), ((int / 1000000) % 10) + 48, 8);
161
- store<u16>(changetype<usize>(str), ((int / 100000) % 10) + 48, 10);
162
- store<u16>(changetype<usize>(str), ((int / 10000) % 10) + 48, 12);
163
- store<u16>(changetype<usize>(str), ((int / 1000) % 10) + 48, 14);
164
- store<u16>(changetype<usize>(str), ((int / 100) % 10) + 48, 16);
165
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48, 18);
166
- store<u16>(changetype<usize>(str), (int % 10) + 48, 20);
167
- return str;
168
- } else if (int >= 100_000_000) {
169
- const str = changetype<string>(__new(20, idof<String>()));
170
- store<u16>(changetype<usize>(str), ((int / 1000000000) % 10) + 48);
171
- store<u16>(changetype<usize>(str), ((int / 100000000) % 10) + 48, 2);
172
- store<u16>(changetype<usize>(str), ((int / 10000000) % 10) + 48, 4);
173
- store<u16>(changetype<usize>(str), ((int / 1000000) % 10) + 48, 6);
174
- store<u16>(changetype<usize>(str), ((int / 100000) % 10) + 48, 8);
175
- store<u16>(changetype<usize>(str), ((int / 10000) % 10) + 48, 10);
176
- store<u16>(changetype<usize>(str), ((int / 1000) % 10) + 48, 12);
177
- store<u16>(changetype<usize>(str), ((int / 100) % 10) + 48, 14);
178
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48, 16);
179
- store<u16>(changetype<usize>(str), (int % 10) + 48, 18);
180
- return str;
181
- } else if (int >= 10_000_000) {
182
- const str = changetype<string>(__new(18, idof<String>()));
183
- store<u16>(changetype<usize>(str), ((int / 100000000) % 10) + 48);
184
- store<u16>(changetype<usize>(str), ((int / 10000000) % 10) + 48, 2);
185
- store<u16>(changetype<usize>(str), ((int / 1000000) % 10) + 48, 4);
186
- store<u16>(changetype<usize>(str), ((int / 100000) % 10) + 48, 6);
187
- store<u16>(changetype<usize>(str), ((int / 10000) % 10) + 48, 8);
188
- store<u16>(changetype<usize>(str), ((int / 1000) % 10) + 48, 10);
189
- store<u16>(changetype<usize>(str), ((int / 100) % 10) + 48, 12);
190
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48, 14);
191
- store<u16>(changetype<usize>(str), (int % 10) + 48, 16);
192
- return str;
193
- } else if (int >= 10_000_000) {
194
- const str = changetype<string>(__new(16, idof<String>()));
195
- store<u16>(changetype<usize>(str), ((int / 10000000) % 10) + 48);
196
- store<u16>(changetype<usize>(str), ((int / 1000000) % 10) + 48, 2);
197
- store<u16>(changetype<usize>(str), ((int / 100000) % 10) + 48, 4);
198
- store<u16>(changetype<usize>(str), ((int / 10000) % 10) + 48, 6);
199
- store<u16>(changetype<usize>(str), ((int / 1000) % 10) + 48, 8);
200
- store<u16>(changetype<usize>(str), ((int / 100) % 10) + 48, 10);
201
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48, 12);
202
- store<u16>(changetype<usize>(str), (int % 10) + 48, 14);
203
- return str;
204
- } else if (int >= 1_000_000) {
205
- const str = changetype<string>(__new(14, idof<String>()));
206
- store<u16>(changetype<usize>(str), ((int / 1000000) % 10) + 48);
207
- store<u16>(changetype<usize>(str), ((int / 100000) % 10) + 48, 2);
208
- store<u16>(changetype<usize>(str), ((int / 10000) % 10) + 48, 4);
209
- store<u16>(changetype<usize>(str), ((int / 1000) % 10) + 48, 6);
210
- store<u16>(changetype<usize>(str), ((int / 100) % 10) + 48, 8);
211
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48, 10);
212
- store<u16>(changetype<usize>(str), (int % 10) + 48, 12);
213
- return str;
214
- } else if (int >= 100_000) {
215
- const str = changetype<string>(__new(12, idof<String>()));
216
- store<u16>(changetype<usize>(str), ((int / 100000) % 10) + 48);
217
- store<u16>(changetype<usize>(str), ((int / 10000) % 10) + 48, 2);
218
- store<u16>(changetype<usize>(str), ((int / 1000) % 10) + 48, 4);
219
- store<u16>(changetype<usize>(str), ((int / 100) % 10) + 48, 6);
220
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48, 8);
221
- store<u16>(changetype<usize>(str), (int % 10) + 48, 10);
222
- return str;
223
- } else if (int >= 10_000) {
224
- const str = changetype<string>(__new(10, idof<String>()));
225
- store<u16>(changetype<usize>(str), ((int / 10000) % 10) + 48);
226
- store<u16>(changetype<usize>(str), ((int / 1000) % 10) + 48, 2);
227
- store<u16>(changetype<usize>(str), ((int / 100) % 10) + 48, 4);
228
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48, 6);
229
- store<u16>(changetype<usize>(str), (int % 10) + 48, 8);
230
- return str;
231
- } else if (int >= 1_000) {
232
- const str = changetype<string>(__new(8, idof<String>()));
233
- store<u16>(changetype<usize>(str), ((int / 1000) % 10) + 48);
234
- store<u16>(changetype<usize>(str), ((int / 100) % 10) + 48, 2);
235
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48, 4);
236
- store<u16>(changetype<usize>(str), (int % 10) + 48, 6);
237
- return str;
238
- } else if (int >= 100) {
239
- const str = changetype<string>(__new(6, idof<String>()));
240
- store<u16>(changetype<usize>(str), ((int / 100) % 10) + 48);
241
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48, 2);
242
- store<u16>(changetype<usize>(str), (int % 10) + 48, 4);
243
- return str;
244
- } else if (int >= 10) {
245
- const str = changetype<string>(__new(4, idof<String>()));
246
- store<u16>(changetype<usize>(str), ((int / 10) % 10) + 48);
247
- store<u16>(changetype<usize>(str), (int % 10) + 48, 2);
248
- return str;
249
- } else {
250
- const str = changetype<string>(__new(2, idof<String>()));
251
- store<u16>(changetype<usize>(str), (int % 10) + 48);
252
- return str;
253
- }
254
- }
255
-
256
- export function istr64<T extends number>(int: T): string {
257
- const val = new ArrayBuffer(6);
258
- store<u16>(changetype<usize>(val), (int % 10) + 48, 4);
259
- if ((int = (int / 10) as T) > 0)
260
- store<u16>(changetype<usize>(val), (int % 10) + 48, 2);
261
- else return changetype<string>(val);
262
- if ((int = (int / 10) as T) > 0)
263
- store<u16>(changetype<usize>(val), (int % 10) + 48);
264
- return changetype<string>(val);
265
- }
266
-
267
- // 0 = 48
268
- // 1 = 49
269
- // 2 = 50
270
- // 3 = 51
271
- // 4 = 52
272
- // 5 = 53
273
- // 6 = 54
274
- // 7 = 55
275
- // 8 = 56
276
- // 9 = 57
277
-
278
- console.log(JSON.stringify("h\\i from gray\bson"));
279
- */
74
+ console.log("snip:");
75
+ console.log("1234 <-> " + snip_fast<i32>("1234").toString());
76
+ console.log("-1234 <-> " + snip_fast<i32>("-1234").toString());
77
+ console.log("12340 <-> " + snip_fast<i32>("1234e1").toString());
78
+ console.log("123400 <-> " + snip_fast<i32>("1234e2").toString());
79
+ console.log("1234000 <-> " + snip_fast<i32>("1234e3").toString());
80
+ console.log("12 <-> " + snip_fast<i32>("123e-1").toString());
81
+ console.log("123 <-> " + snip_fast<i32>("123").toString());
82
+ console.log("-12340 <-> " + snip_fast<i32>("-1234e1").toString());
83
+ console.log("-1234500 <-> " + snip_fast<i32>("-12345e2").toString());
84
+ console.log("-123456000 <-> " + snip_fast<i32>("-123456e3").toString());
85
+ console.log("-12 <-> " + snip_fast<i32>("-123e-1").toString());
86
+ console.log("-123 <-> " + snip_fast<i32>("-123").toString());
87
+ console.log(snip_fast<i32>("1000").toString());
88
+ console.log(snip_fast<i32>("-1000").toString());
89
+ console.log(JSON.stringify([[1, 2, 3, 4, 5], [5, 4, 3, 2, 1]]));
package/bench.js CHANGED
@@ -1,7 +1,5 @@
1
1
  import { Bench } from "tinybench";
2
2
  // Trying a new benchmarking lib.
3
- // It seems to not warmup long at all, so its probably bad.
4
- // benchmark will probably be best here
5
3
 
6
4
  // JavaScript Results
7
5
  // ┌─────────┬───────────────────────────┬─────────────┬────────────────────┬──────────┬─────────┐
@@ -35,7 +33,7 @@ const vec = {
35
33
 
36
34
  let data;
37
35
 
38
- const bench = new Bench({ time: 100 })
36
+ const bench = new Bench({ time: 1000 })
39
37
 
40
38
  .add("Stringify Object (Vec3)", () => {
41
39
  data = JSON.stringify(vec);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "json-as",
3
- "version": "0.5.51",
3
+ "version": "0.5.52",
4
4
  "description": "JSON encoder/decoder for AssemblyScript",
5
5
  "types": "assembly/index.ts",
6
6
  "author": "Jairus Tanaka",
@@ -14,8 +14,8 @@
14
14
  "license": "MIT",
15
15
  "scripts": {
16
16
  "aspect": "asp",
17
- "bench:astral": "astral -Ospeed --noAssert --uncheckedBehavior always --runtime minimal",
18
- "build:test": "asc assembly/test.ts --target test --runtime stub",
17
+ "bench:astral": "astral -Ospeed --noAssert --uncheckedBehavior always --runtime stub",
18
+ "build:test": "asc assembly/test.ts --target test",
19
19
  "build:transform": "tsc -p ./transform",
20
20
  "test:wasmtime": "wasmtime ./build/test.wasm",
21
21
  "test:lunatic": "lunatic ./build/test.wasm",
@@ -26,8 +26,11 @@
26
26
  "@as-pect/cli": "^8.0.1",
27
27
  "@as-tral/cli": "^2.0.0",
28
28
  "@assemblyscript/wasi-shim": "^0.1.0",
29
- "assemblyscript": "^0.27.1",
29
+ "assemblyscript": "^0.27.8",
30
30
  "assemblyscript-prettier": "^1.0.7",
31
+ "benchmark": "^2.1.4",
32
+ "kati": "^0.6.2",
33
+ "microtime": "^3.1.1",
31
34
  "prettier": "^2.8.4",
32
35
  "tinybench": "^2.5.0",
33
36
  "typescript": "^4.9.5",
@@ -1,4 +1,4 @@
1
- import { getName, toString, isStdlib } from "visitor-as/dist/utils.js";
1
+ import { toString, isStdlib } from "visitor-as/dist/utils.js";
2
2
  import { BaseVisitor, SimpleParser } from "visitor-as/dist/index.js";
3
3
  import { Transform } from "assemblyscript/dist/transform.js";
4
4
  class SchemaData {
@@ -19,48 +19,23 @@ class AsJSONTransform extends BaseVisitor {
19
19
  this.sources = [];
20
20
  }
21
21
  visitMethodDeclaration() { }
22
- visitFieldDeclaration(node) {
23
- if (toString(node).startsWith("static"))
24
- return;
25
- const lineText = toString(node);
26
- if (lineText.startsWith("private"))
27
- return;
28
- const name = getName(node);
29
- if (!node.type) {
30
- throw new Error(`Field ${name} is missing a type declaration`);
31
- }
32
- let type = getName(node.type);
33
- // @ts-ignore
34
- this.currentClass.encodeStmts.push(`"${name}":\${JSON.stringify<${type}>(this.${name})},`);
35
- // @ts-ignore
36
- //this.decodeStmts.push(
37
- // `${name}: JSON.parseObjectValue<${type}>(values.get("${name}")),\n`
38
- //);
39
- // @ts-ignore
40
- this.currentClass.setDataStmts.push(`if (key.length === ${name.length} && (memory.compare(changetype<usize>("${name}"), changetype<usize>(key), ${name.length}) == 0)) {
41
- this.${name} = JSON.parseObjectValue<${type}>(value);
42
- return;
43
- }
44
- `);
45
- // @ts-ignore
46
- //this.checkDecodeStmts.push(
47
- // ' if (!values.has("${name}")) throw new Error("Key "${name}" was not found. Cannot instantiate object.");\n'
48
- //);
49
- }
50
22
  visitClassDeclaration(node) {
51
- var _a;
23
+ var _c;
52
24
  const className = node.name.text;
53
- if (!((_a = node.decorators) === null || _a === void 0 ? void 0 : _a.length))
25
+ if (!((_c = node.decorators) === null || _c === void 0 ? void 0 : _c.length))
54
26
  return;
55
27
  let foundDecorator = false;
56
28
  for (const decorator of node.decorators) {
29
+ if (
57
30
  // @ts-ignore
58
- if (decorator.name.text.toLowerCase() == "json" || decorator.name.text.toLowerCase() == "serializable")
31
+ decorator.name.text.toLowerCase() == "json" ||
32
+ // @ts-ignore
33
+ decorator.name.text.toLowerCase() == "serializable")
59
34
  foundDecorator = true;
60
35
  }
61
36
  if (!foundDecorator)
62
37
  return;
63
- // Prevent from being triggered twice
38
+ // Prevent from being triggered twice.
64
39
  for (const member of node.members) {
65
40
  if (member.name.text == "__JSON_Serialize")
66
41
  return;
@@ -73,7 +48,7 @@ class AsJSONTransform extends BaseVisitor {
73
48
  parent: node.extendsType ? toString(node.extendsType) : "",
74
49
  node: node,
75
50
  encodeStmts: [],
76
- setDataStmts: []
51
+ setDataStmts: [],
77
52
  };
78
53
  if (this.currentClass.parent.length > 0) {
79
54
  const parentSchema = this.schemasList.find((v) => v.name == this.currentClass.parent);
@@ -82,17 +57,64 @@ class AsJSONTransform extends BaseVisitor {
82
57
  this.currentClass.encodeStmts.push(...parentSchema === null || parentSchema === void 0 ? void 0 : parentSchema.encodeStmts);
83
58
  }
84
59
  else {
85
- console.error("Class extends " + this.currentClass.parent + ", but parent class not found. Maybe add the @json decorator over parent class?");
60
+ console.error("Class extends " +
61
+ this.currentClass.parent +
62
+ ", but parent class not found. Maybe add the @json decorator over parent class?");
86
63
  }
87
64
  }
88
65
  const parentSchema = this.schemasList.find((v) => v.name == this.currentClass.parent);
89
- const members = [...node.members, ...(parentSchema ? parentSchema.node.members : [])];
90
- this.visit(members);
66
+ const members = [
67
+ ...node.members,
68
+ ...(parentSchema ? parentSchema.node.members : []),
69
+ ];
70
+ for (const mem of members) {
71
+ // @ts-ignore
72
+ if (mem.type && mem.type.name && mem.type.name.identifier.text) {
73
+ const member = mem;
74
+ if (toString(member).startsWith("static"))
75
+ return;
76
+ const lineText = toString(member);
77
+ if (lineText.startsWith("private"))
78
+ return;
79
+ // @ts-ignore
80
+ let type = toString(member.type);
81
+ const name = member.name.text;
82
+ this.currentClass.keys.push(name);
83
+ // @ts-ignore
84
+ this.currentClass.types.push(type);
85
+ // @ts-ignore
86
+ if ([
87
+ "u8",
88
+ "i8",
89
+ "u16",
90
+ "i16",
91
+ "u32",
92
+ "i32",
93
+ "f32",
94
+ "u64",
95
+ "i64",
96
+ "f64",
97
+ ].includes(type.toLowerCase())) {
98
+ this.currentClass.encodeStmts.push(`"${name}":\${this.${name}.toString()},`);
99
+ }
100
+ else {
101
+ this.currentClass.encodeStmts.push(`"${name}":\${JSON.stringify<${type}>(this.${name})},`);
102
+ }
103
+ // @ts-ignore
104
+ this.currentClass.setDataStmts.push(`if (key == "${name}") {
105
+ this.${name} = JSON.parseObjectValue<${type}>(value);
106
+ return;
107
+ }
108
+ `);
109
+ }
110
+ }
91
111
  let serializeFunc = "";
92
112
  if (this.currentClass.encodeStmts.length > 0) {
93
113
  const stmt = this.currentClass.encodeStmts[this.currentClass.encodeStmts.length - 1];
94
- this.currentClass.encodeStmts[this.currentClass.encodeStmts.length - 1] = stmt.slice(0, stmt.length - 1);
114
+ this.currentClass.encodeStmts[this.currentClass.encodeStmts.length - 1] =
115
+ stmt.slice(0, stmt.length - 1);
95
116
  serializeFunc = `
117
+ @inline
96
118
  __JSON_Serialize(): string {
97
119
  return \`{${this.currentClass.encodeStmts.join("")}}\`;
98
120
  }
@@ -100,16 +122,18 @@ class AsJSONTransform extends BaseVisitor {
100
122
  }
101
123
  else {
102
124
  serializeFunc = `
125
+ @inline
103
126
  __JSON_Serialize(): string {
104
127
  return "{}";
105
128
  }
106
129
  `;
107
130
  }
108
131
  const setKeyFunc = `
132
+ @inline
109
133
  __JSON_Set_Key(key: string, value: string): void {
110
134
  ${
111
- // @ts-ignore
112
- this.currentClass.setDataStmts.join("")}
135
+ // @ts-ignore
136
+ this.currentClass.setDataStmts.join("")}
113
137
  }
114
138
  `;
115
139
  const serializeMethod = SimpleParser.parseClassMember(serializeFunc, node);
@@ -127,18 +151,23 @@ export default class Transformer extends Transform {
127
151
  afterParse(parser) {
128
152
  // Create new transform
129
153
  const transformer = new AsJSONTransform();
130
- // Loop over every source
131
- const sources = parser.sources.filter(source => !isStdlib(source)).sort((_a, _b) => {
132
- const a = _a.internalPath
133
- const b = _b.internalPath
154
+ // Sort the sources so that user scripts are visited last
155
+ const sources = parser.sources
156
+ .filter((source) => !isStdlib(source))
157
+ .sort((_a, _b) => {
158
+ const a = _a.internalPath;
159
+ const b = _b.internalPath;
134
160
  if (a[0] === "~" && b[0] !== "~") {
135
161
  return -1;
136
- } else if (a[0] !== "~" && b[0] === "~") {
162
+ }
163
+ else if (a[0] !== "~" && b[0] === "~") {
137
164
  return 1;
138
- } else {
165
+ }
166
+ else {
139
167
  return 0;
140
168
  }
141
- })
169
+ });
170
+ // Loop over every source
142
171
  for (const source of sources) {
143
172
  // Ignore all lib and std. Visit everything else.
144
173
  if (!isStdlib(source)) {
@@ -146,4 +175,4 @@ export default class Transformer extends Transform {
146
175
  }
147
176
  }
148
177
  }
149
- }
178
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@json-as/transform",
3
- "version": "0.5.51",
3
+ "version": "0.5.52",
4
4
  "description": "JSON encoder/decoder for AssemblyScript",
5
5
  "main": "./lib/index.js",
6
6
  "author": "Jairus Tanaka",