json-as 0.5.52 → 0.5.56
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/workflows/nodejs.yml +25 -0
- package/LICENSE +1 -1
- package/README.md +0 -23
- package/asconfig.json +2 -2
- package/assembly/__benches__/as-json.ts +10 -64
- package/assembly/__tests__/as-json.spec.ts +4 -13
- package/assembly/src/chars.ts +52 -26
- package/assembly/src/json.ts +130 -204
- package/assembly/src/util.ts +175 -148
- package/assembly/test.ts +51 -72
- package/bench/benchmark.ts +206 -0
- package/bench/tsconfig.json +99 -0
- package/bench-results/INTEGER-PARSING.md +52 -0
- package/package.json +16 -10
- package/transform/lib/index.js +26 -11
- package/transform/package.json +1 -1
- package/transform/src/index.ts +44 -22
package/assembly/src/util.ts
CHANGED
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
import { StringSink } from "as-string-sink/assembly";
|
|
2
|
-
import {
|
|
2
|
+
import { isSpace } from "util/string";
|
|
3
3
|
import { backSlashCode, quoteCode } from "./chars";
|
|
4
4
|
|
|
5
|
-
// @ts-ignore
|
|
6
|
-
@inline
|
|
7
|
-
export function unsafeCharCodeAt(data: string, pos: i32): i32 {
|
|
5
|
+
// @ts-ignore: Decorator
|
|
6
|
+
@inline export function unsafeCharCodeAt(data: string, pos: i32): i32 {
|
|
8
7
|
return load<u16>(changetype<usize>(data) + ((<usize>pos) << 1));
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
// @ts-ignore
|
|
12
|
-
@inline
|
|
13
|
-
export function removeWhitespace(data: string): string {
|
|
10
|
+
// @ts-ignore: Decorator
|
|
11
|
+
@inline export function removeWhitespace(data: string): string {
|
|
14
12
|
const result = new StringSink();
|
|
15
13
|
let instr = false;
|
|
16
14
|
for (let i = 0; i < data.length; i++) {
|
|
17
15
|
const char = data.charCodeAt(i);
|
|
18
16
|
if (instr === false && char === quoteCode) instr = true;
|
|
19
17
|
else if (
|
|
20
|
-
instr === true &&
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
)
|
|
24
|
-
instr = false;
|
|
18
|
+
instr === true && char === quoteCode
|
|
19
|
+
&& data.charCodeAt(i - 1) !== backSlashCode
|
|
20
|
+
) instr = false;
|
|
25
21
|
|
|
26
22
|
if (instr === false) {
|
|
27
23
|
if (!isSpace(char)) result.write(data.charAt(i));
|
|
@@ -32,9 +28,8 @@ export function removeWhitespace(data: string): string {
|
|
|
32
28
|
return result.toString();
|
|
33
29
|
}
|
|
34
30
|
|
|
35
|
-
// @ts-ignore
|
|
36
|
-
@inline
|
|
37
|
-
export function escapeChar(char: string): string {
|
|
31
|
+
// @ts-ignore: Decorator
|
|
32
|
+
@inline export function escapeChar(char: string): string {
|
|
38
33
|
switch (unsafeCharCodeAt(char, 0)) {
|
|
39
34
|
case 0x22:
|
|
40
35
|
return '\\"';
|
|
@@ -63,143 +58,169 @@ export function escapeChar(char: string): string {
|
|
|
63
58
|
* @returns depth of array
|
|
64
59
|
*/
|
|
65
60
|
|
|
66
|
-
// @ts-ignore
|
|
67
|
-
@inline
|
|
68
|
-
export function getArrayDepth<T>(depth: i32 = 1): i32 {
|
|
69
|
-
// @ts-ignore
|
|
61
|
+
// @ts-ignore: Decorator
|
|
62
|
+
@inline export function getArrayDepth<T extends ArrayLike>(depth: i32 = 1): i32 {
|
|
70
63
|
if (!isArray<T>()) {
|
|
71
64
|
return 0;
|
|
72
|
-
// @ts-ignore
|
|
73
65
|
} else if (isArray<valueof<T>>()) {
|
|
74
66
|
depth++;
|
|
75
|
-
// @ts-ignore
|
|
76
67
|
return getArrayDepth<valueof<T>>(depth);
|
|
77
68
|
} else {
|
|
78
69
|
return depth;
|
|
79
70
|
}
|
|
80
71
|
}
|
|
81
72
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
73
|
+
/** Scientific Notation Integer Parsing - SNIP
|
|
74
|
+
* This is absolutely the fastest algorithm I could think of while adding full support for Scientific Notation
|
|
75
|
+
* Loads 32 bits and retrieves the high/low bits.
|
|
76
|
+
* The reason why we only load 4 bytes at a time is that numbers in the 32-bit range are 7 chars long at most.
|
|
77
|
+
* Using SIMD or 64 bit loads would only work well when parsing large 128+ numbers.
|
|
78
|
+
*
|
|
79
|
+
* Here are some benchmarks
|
|
80
|
+
* Parsing: "12345"
|
|
81
|
+
* Results are spread over 5000ms
|
|
82
|
+
*
|
|
83
|
+
* SNIP: 270M iterations
|
|
84
|
+
* ATOI: 285M iterations
|
|
85
|
+
* ParseInt: 176M iterations
|
|
86
|
+
*
|
|
87
|
+
* @param str - Any number. Can include scientific notation.
|
|
88
|
+
*/
|
|
89
|
+
// @ts-ignore: Decorator
|
|
90
|
+
@inline export function snip_fast<T extends number>(str: string, len: u32 = 0, offset: u32 = 0): T {
|
|
91
|
+
if (isSigned<T>()) {
|
|
92
|
+
const firstChar: u32 = load<u16>(changetype<usize>(str));
|
|
93
|
+
if (firstChar === 48) return 0 as T;
|
|
94
|
+
const isNegative = firstChar === 45; // Check if the number is negative
|
|
95
|
+
let val: T = 0 as T;
|
|
96
|
+
if (len == 0) len = u32(str.length << 1);
|
|
97
|
+
if (isNegative) {
|
|
98
|
+
offset += 2;
|
|
99
|
+
if (len >= 4) {
|
|
100
|
+
// 32-bit route
|
|
101
|
+
for (; offset < (len - 3); offset += 4) {
|
|
102
|
+
const ch = load<u32>(changetype<usize>(str) + <usize>offset);
|
|
103
|
+
const low = ch & 0xFFFF;
|
|
104
|
+
const high = ch >> 16;
|
|
105
|
+
// 9 is 57. The highest group of two numbers is 114, so if a e or an E is included, this will fire.
|
|
106
|
+
if (low > 57) {
|
|
107
|
+
// The first char (f) is E or e
|
|
108
|
+
// We push the offset up by two and apply the notation.
|
|
109
|
+
if (load<u16>(changetype<usize>(str) + <usize>offset + 2) == 45) {
|
|
110
|
+
return -(val / (10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1))) as T;
|
|
111
|
+
} else {
|
|
112
|
+
// Inlined this operation instead of using a loop
|
|
113
|
+
return -(val * (10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1))) as T;
|
|
116
114
|
}
|
|
117
|
-
} else {
|
|
118
|
-
|
|
119
|
-
|
|
115
|
+
} else if (high > 57) {
|
|
116
|
+
// The first char (f) is E or e
|
|
117
|
+
// We push the offset up by two and apply the notation.
|
|
118
|
+
if (load<u16>(changetype<usize>(str) + <usize>offset + 4) == 45) {
|
|
119
|
+
return -(val / (10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1))) as T;
|
|
120
|
+
} else {
|
|
121
|
+
// Inlined this operation instead of using a loop
|
|
122
|
+
return -(val * (10 ** (__atoi_fast<u32>(str, offset + 4, offset + 6) + 1))) as T;
|
|
120
123
|
}
|
|
124
|
+
} else {
|
|
125
|
+
val = (val * 100 + ((low - 48) * 10) + (high - 48)) as T;
|
|
121
126
|
}
|
|
122
|
-
|
|
123
|
-
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Finish up the remainder with 16 bits.
|
|
130
|
+
for (; offset < len; offset += 2) {
|
|
131
|
+
const ch = load<u16>(changetype<usize>(str) + <usize>offset);
|
|
132
|
+
// 9 is 57. E and e are larger. Assumes valid JSON.
|
|
133
|
+
if (ch > 57) {
|
|
124
134
|
// The first char (f) is E or e
|
|
125
135
|
// We push the offset up by two and apply the notation.
|
|
126
|
-
offset
|
|
127
|
-
|
|
128
|
-
if (exp < 0) {
|
|
129
|
-
for (let i = 0; i < exp; i++) {
|
|
130
|
-
val = (val / 10) as T;
|
|
131
|
-
}
|
|
136
|
+
if (load<u16>(changetype<usize>(str) + <usize>offset + 2) == 45) {
|
|
137
|
+
return -(val / (10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1))) as T;
|
|
132
138
|
} else {
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
139
|
+
// Inlined this operation instead of using a loop
|
|
140
|
+
return -(val * (10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1))) as T;
|
|
136
141
|
}
|
|
137
|
-
return -val as T;
|
|
138
142
|
} else {
|
|
139
|
-
val = (val *
|
|
143
|
+
val = (val * 10) + (ch - 48) as T;
|
|
140
144
|
}
|
|
141
145
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
146
|
+
return -val as T;
|
|
147
|
+
} else {
|
|
148
|
+
if (len >= 4) {
|
|
149
|
+
// Duplet 16 bit lane load
|
|
150
|
+
for (; offset < (len - 3); offset += 4) {
|
|
151
|
+
const ch = load<u32>(changetype<usize>(str) + <usize>offset);
|
|
152
|
+
const low = ch & 0xFFFF;
|
|
153
|
+
const high = ch >> 16;
|
|
154
|
+
// 9 is 57. The highest group of two numbers is 114, so if a e or an E is included, this will fire.
|
|
155
|
+
if (low > 57) {
|
|
156
|
+
// The first char (f) is E or e
|
|
157
|
+
// We push the offset up by two and apply the notation.
|
|
158
|
+
if (load<u16>(changetype<usize>(str) + <usize>offset + 2) == 45) {
|
|
159
|
+
return (val / (10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1))) as T;
|
|
160
|
+
} else {
|
|
161
|
+
// Inlined this operation instead of using a loop
|
|
162
|
+
return (val * (10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1))) as T;
|
|
163
|
+
}
|
|
164
|
+
} else if (high > 57) {
|
|
165
|
+
if (load<u16>(changetype<usize>(str) + <usize>offset + 4) == 45) {
|
|
166
|
+
return (val / (10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1))) as T;
|
|
167
|
+
} else {
|
|
168
|
+
// Inlined this operation instead of using a loop
|
|
169
|
+
return (val * (10 ** (__atoi_fast<u32>(str, offset + 4, offset + 6) + 1))) as T;
|
|
170
|
+
}
|
|
171
|
+
} else {
|
|
172
|
+
// Optimized with multiplications and shifts.
|
|
173
|
+
val = (val * 100 + ((low - 48) * 10) + (high - 48)) as T;
|
|
155
174
|
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Cover the remaining numbers with 16 bit loads.
|
|
178
|
+
for (; offset < len; offset += 2) {
|
|
179
|
+
const ch = load<u16>(changetype<usize>(str) + <usize>offset);
|
|
180
|
+
// 0's char is 48 and 9 is 57. Anything above this range would signify an exponent (e or E).
|
|
181
|
+
// e is 101 and E is 69.
|
|
182
|
+
if (ch > 57) {
|
|
183
|
+
if (load<u16>(changetype<usize>(str) + <usize>offset + 2) == 45) {
|
|
184
|
+
val = (val / (10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1))) as T;
|
|
185
|
+
} else {
|
|
186
|
+
// Inlined this operation instead of using a loop
|
|
187
|
+
val = (val * (10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1))) as T;
|
|
159
188
|
}
|
|
189
|
+
return val as T;
|
|
190
|
+
} else {
|
|
191
|
+
val = (val * 10) + (ch - 48) as T;
|
|
160
192
|
}
|
|
161
|
-
return -val as T;
|
|
162
|
-
} else {
|
|
163
|
-
val = (val * 10) + (ch - 48) as T;
|
|
164
193
|
}
|
|
194
|
+
return val as T;
|
|
165
195
|
}
|
|
166
|
-
return -val as T;
|
|
167
196
|
} else {
|
|
197
|
+
const firstChar: u32 = load<u16>(changetype<usize>(str));
|
|
198
|
+
if (firstChar === 48) return 0 as T;
|
|
199
|
+
let val: T = 0 as T;
|
|
200
|
+
if (len == 0) len = u32(str.length << 1);
|
|
168
201
|
if (len >= 4) {
|
|
169
202
|
// Duplet 16 bit lane load
|
|
170
203
|
for (; offset < (len - 3); offset += 4) {
|
|
171
|
-
ch = load<u32>(changetype<usize>(str) + <usize>offset);
|
|
204
|
+
const ch = load<u32>(changetype<usize>(str) + <usize>offset);
|
|
172
205
|
const low = ch & 0xFFFF;
|
|
173
206
|
const high = ch >> 16;
|
|
174
207
|
// 9 is 57. The highest group of two numbers is 114, so if a e or an E is included, this will fire.
|
|
175
208
|
if (low > 57) {
|
|
176
209
|
// The first char (f) is E or e
|
|
177
210
|
// We push the offset up by two and apply the notation.
|
|
178
|
-
offset
|
|
179
|
-
|
|
180
|
-
if (exp < 0) {
|
|
181
|
-
for (let i = 0; i < exp; i++) {
|
|
182
|
-
val = (val / 10) as T;
|
|
183
|
-
}
|
|
211
|
+
if (load<u16>(changetype<usize>(str) + <usize>offset + 2) == 45) {
|
|
212
|
+
return (val / (10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1))) as T;
|
|
184
213
|
} else {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
214
|
+
// Inlined this operation instead of using a loop
|
|
215
|
+
return (val * (10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1))) as T;
|
|
188
216
|
}
|
|
189
|
-
return val as T;
|
|
190
217
|
} else if (high > 57) {
|
|
191
|
-
offset
|
|
192
|
-
|
|
193
|
-
if (exp < 0) {
|
|
194
|
-
for (let i = 0; i < exp; i++) {
|
|
195
|
-
val = (val / 10) as T;
|
|
196
|
-
}
|
|
218
|
+
if (load<u16>(changetype<usize>(str) + <usize>offset + 4) == 45) {
|
|
219
|
+
return (val / (10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1))) as T;
|
|
197
220
|
} else {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
}
|
|
221
|
+
// Inlined this operation instead of using a loop
|
|
222
|
+
return (val * (10 ** (__atoi_fast<u32>(str, offset + 4, offset + 6) + 1))) as T;
|
|
201
223
|
}
|
|
202
|
-
return val as T;
|
|
203
224
|
} else {
|
|
204
225
|
// Optimized with multiplications and shifts.
|
|
205
226
|
val = (val * 100 + ((low - 48) * 10) + (high - 48)) as T;
|
|
@@ -208,22 +229,16 @@ export function snip_fast<T extends number>(str: string, offset: u32 = 0): T {
|
|
|
208
229
|
}
|
|
209
230
|
// Cover the remaining numbers with 16 bit loads.
|
|
210
231
|
for (; offset < len; offset += 2) {
|
|
211
|
-
ch = load<u16>(changetype<usize>(str) + <usize>offset);
|
|
232
|
+
const ch = load<u16>(changetype<usize>(str) + <usize>offset);
|
|
212
233
|
// 0's char is 48 and 9 is 57. Anything above this range would signify an exponent (e or E).
|
|
213
234
|
// e is 101 and E is 69.
|
|
214
235
|
if (ch > 57) {
|
|
215
|
-
offset
|
|
216
|
-
|
|
217
|
-
if (exp < 0) {
|
|
218
|
-
for (let i = 0; i > exp; i--) {
|
|
219
|
-
val = (val / 10) as T;
|
|
220
|
-
}
|
|
236
|
+
if (load<u16>(changetype<usize>(str) + <usize>offset + 2) == 45) {
|
|
237
|
+
return (val / (10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1))) as T;
|
|
221
238
|
} else {
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}
|
|
239
|
+
// Inlined this operation instead of using a loop
|
|
240
|
+
return (val * (10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1))) as T;
|
|
225
241
|
}
|
|
226
|
-
return val as T;
|
|
227
242
|
} else {
|
|
228
243
|
val = (val * 10) + (ch - 48) as T;
|
|
229
244
|
}
|
|
@@ -234,34 +249,37 @@ export function snip_fast<T extends number>(str: string, offset: u32 = 0): T {
|
|
|
234
249
|
|
|
235
250
|
/**
|
|
236
251
|
* Implementation of ATOI. Can be much much faster with SIMD.
|
|
237
|
-
* Benchmark: 40-46m ops/s
|
|
238
252
|
*/
|
|
239
253
|
|
|
240
254
|
// @ts-ignore
|
|
241
|
-
@inline
|
|
242
|
-
export function atoi_fast<T extends number>(str: string, offset: u32 = 0): T {
|
|
255
|
+
@global @inline export function __atoi_fast<T extends number>(str: string, start: u32 = 0, end: u32 = 0): T {
|
|
243
256
|
// @ts-ignore
|
|
244
257
|
let val: T = 0;
|
|
245
|
-
|
|
246
|
-
if (
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
258
|
+
if (!end) end = start + u32(str.length << 1);
|
|
259
|
+
if (isSigned<T>()) {
|
|
260
|
+
// Negative path
|
|
261
|
+
if (load<u16>(changetype<usize>(str) + <usize>start) === 45) {
|
|
262
|
+
start += 2;
|
|
263
|
+
for (; start < end; start += 2) {
|
|
264
|
+
val = (val * 10) + (load<u16>(changetype<usize>(str) + <usize>start) - 48) as T;
|
|
265
|
+
}
|
|
266
|
+
return -val as T;
|
|
267
|
+
} else {
|
|
268
|
+
for (; start < end; start += 2) {
|
|
269
|
+
val = ((val * 10) + (load<u16>(changetype<usize>(str) + <usize>start) - 48)) as T;
|
|
270
|
+
}
|
|
271
|
+
return val as T;
|
|
251
272
|
}
|
|
252
|
-
// @ts-ignore
|
|
253
|
-
return -val;
|
|
254
273
|
} else {
|
|
255
|
-
for (;
|
|
256
|
-
|
|
257
|
-
val = (val << 1) + (val << 3) + (load<u16>(changetype<usize>(str) + <usize>offset) - 48);
|
|
274
|
+
for (; start < end; start += 2) {
|
|
275
|
+
val = ((val * 10) + (load<u16>(changetype<usize>(str) + <usize>start) - 48)) as T;
|
|
258
276
|
}
|
|
259
|
-
return val;
|
|
277
|
+
return val as T;
|
|
260
278
|
}
|
|
261
279
|
}
|
|
262
280
|
|
|
263
281
|
/**
|
|
264
|
-
* Parses an integer using
|
|
282
|
+
* Parses an integer using __atoi_fast and applies the appended exponential number to it as scientific notation.
|
|
265
283
|
* Benchmark: Hovers around 30m ops/s
|
|
266
284
|
* Only safe if the string is valid.
|
|
267
285
|
* @param str integer to parse. example: 123e1, 123e-1, 123E100
|
|
@@ -269,8 +287,7 @@ export function atoi_fast<T extends number>(str: string, offset: u32 = 0): T {
|
|
|
269
287
|
*/
|
|
270
288
|
|
|
271
289
|
// @ts-ignore
|
|
272
|
-
@inline
|
|
273
|
-
export function parseSciInteger<T extends number>(str: string): T {
|
|
290
|
+
@inline export function parseSciInteger<T extends number>(str: string): T {
|
|
274
291
|
// @ts-ignore
|
|
275
292
|
let val: T = 0;
|
|
276
293
|
let offset = 0;
|
|
@@ -284,12 +301,12 @@ export function parseSciInteger<T extends number>(str: string): T {
|
|
|
284
301
|
const char = load<u16>(changetype<usize>(str) + <usize>(offset += 2));
|
|
285
302
|
if (char === 45) {
|
|
286
303
|
// @ts-ignore
|
|
287
|
-
val /= sciNote<T>(
|
|
304
|
+
val /= sciNote<T>(__atoi_fast<T>(str, (offset += 2)));
|
|
288
305
|
// @ts-ignore
|
|
289
306
|
return val;
|
|
290
307
|
} else {
|
|
291
308
|
// @ts-ignore
|
|
292
|
-
val *= sciNote<T>(
|
|
309
|
+
val *= sciNote<T>(__atoi_fast<T>(str, offset));
|
|
293
310
|
// @ts-ignore
|
|
294
311
|
return val;
|
|
295
312
|
}
|
|
@@ -299,25 +316,35 @@ export function parseSciInteger<T extends number>(str: string): T {
|
|
|
299
316
|
// We use load because in this case, there is no need to have bounds-checking
|
|
300
317
|
}
|
|
301
318
|
if (firstChar === 45) {
|
|
302
|
-
val = -val;
|
|
319
|
+
val = -val as T;
|
|
303
320
|
}
|
|
304
321
|
return val;
|
|
305
322
|
}
|
|
306
323
|
|
|
307
324
|
// @ts-ignore
|
|
308
|
-
@inline
|
|
309
|
-
function sciNote<T extends number>(num: T): T {
|
|
325
|
+
@inline function sciNote<T extends number>(num: T): T {
|
|
310
326
|
let res = 1;
|
|
311
327
|
// @ts-ignore
|
|
312
328
|
if (num > 0) {
|
|
313
|
-
for (let i: T = 0; i < num; i++) {
|
|
329
|
+
for (let i: T = <T>0; i < num; i++) {
|
|
314
330
|
res *= 10;
|
|
315
331
|
}
|
|
316
332
|
} else {
|
|
317
|
-
for (let i: T = 0; i < num; i++) {
|
|
333
|
+
for (let i: T = <T>0; i < num; i++) {
|
|
318
334
|
res /= 10;
|
|
319
335
|
}
|
|
320
336
|
}
|
|
321
337
|
// @ts-ignore
|
|
322
338
|
return res;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
// @ts-ignore
|
|
342
|
+
@inline function equalsSlice(p1_data: string, p1_start: i32, p1_end: i32, p2_data: string, p2_start: i32, p2_end: i32): boolean {
|
|
343
|
+
const p1_len = p1_end - p1_start;
|
|
344
|
+
const p2_len = p2_end - p2_start;
|
|
345
|
+
if (p1_len != p2_len) return false;
|
|
346
|
+
if (p1_len == 2) {
|
|
347
|
+
return load<u16>(changetype<usize>(p1_data) + p1_start) == load<u16>(changetype<usize>(p2_data) + p2_start)
|
|
348
|
+
}
|
|
349
|
+
return memory.compare(changetype<usize>(p1_data) + p1_start, changetype<usize>(p2_data) + p2_start, p1_len) === 0;
|
|
323
350
|
}
|
package/assembly/test.ts
CHANGED
|
@@ -1,89 +1,68 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
import { bench, blackbox } from "../../../WebAssembly/benchmark-wasm/assembly/bench";
|
|
2
|
+
import { JSON, serializeString } from "./src/json";
|
|
3
|
+
// @ts-ignore
|
|
4
4
|
@json
|
|
5
5
|
class Vec3 {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
6
|
+
x: f64;
|
|
7
|
+
y: f64;
|
|
8
|
+
z: f64;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
// @ts-ignore
|
|
11
12
|
@json
|
|
12
13
|
class Player {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
firstName: string;
|
|
15
|
+
lastName: string;
|
|
16
|
+
lastActive: i32[];
|
|
17
|
+
age: i32;
|
|
18
|
+
pos: Vec3 | null;
|
|
19
|
+
isVerified: boolean;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
|
-
const
|
|
22
|
-
firstName: "Emmet",
|
|
23
|
-
lastName: "West",
|
|
24
|
-
lastActive: [8, 27, 2022],
|
|
25
|
-
age: 23,
|
|
26
|
-
pos: {
|
|
22
|
+
const vec: Vec3 = {
|
|
27
23
|
x: 3.4,
|
|
28
24
|
y: 1.2,
|
|
29
25
|
z: 8.3,
|
|
30
|
-
|
|
31
|
-
isVerified: true,
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
const vec: Vec3 = {
|
|
35
|
-
x: 3,
|
|
36
|
-
y: 1,
|
|
37
|
-
z: 8,
|
|
38
|
-
};
|
|
26
|
+
}
|
|
39
27
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
28
|
+
const player: Player = {
|
|
29
|
+
firstName: "Emmet",
|
|
30
|
+
lastName: "West",
|
|
31
|
+
lastActive: [8, 27, 2022],
|
|
32
|
+
age: 23,
|
|
33
|
+
pos: {
|
|
34
|
+
x: 3.4,
|
|
35
|
+
y: 1.2,
|
|
36
|
+
z: 8.3,
|
|
37
|
+
},
|
|
38
|
+
isVerified: true,
|
|
49
39
|
}
|
|
50
40
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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}');
|
|
63
|
-
const serializedPlayer = JSON.stringify<Player>(player);
|
|
64
|
-
console.log(serializedPlayer);
|
|
65
|
-
const parsedPlayer = JSON.parse<Player>(serializedPlayer);
|
|
66
|
-
console.log(JSON.stringify(parsedPlayer));
|
|
41
|
+
console.log("Original: " + JSON.stringify(vec));
|
|
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}')));
|
|
44
|
+
|
|
45
|
+
console.log("Original: " + JSON.stringify(player));
|
|
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}')));
|
|
48
|
+
|
|
49
|
+
console.log("Serialized String: " + serializeString('st"ring" w""ith quotes"'));
|
|
67
50
|
|
|
68
|
-
|
|
69
|
-
|
|
51
|
+
bench("New Stringify String", () => {
|
|
52
|
+
blackbox<string>(serializeString(blackbox<string>('st"ring" w""ith quotes"')));
|
|
53
|
+
});
|
|
54
|
+
/*
|
|
55
|
+
// 9,325,755
|
|
56
|
+
bench("Stringify Object (Vec3)", () => {
|
|
57
|
+
blackbox<string>(vec.__JSON_Serialize());
|
|
58
|
+
});
|
|
70
59
|
|
|
71
|
-
|
|
72
|
-
|
|
60
|
+
// 17,747,531 -> 55,517,015
|
|
61
|
+
/*bench("New Parse Object (Vec3)", () => {
|
|
62
|
+
blackbox<Vec3>(vec.__JSON_Deserialize(blackbox<string>('{"x":0,"y":0,"z":0}')));
|
|
63
|
+
});
|
|
73
64
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
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]]));
|
|
65
|
+
// 17,747,531
|
|
66
|
+
bench("Old Parse Object (Vec3)", () => {
|
|
67
|
+
blackbox<Vec3>(JSON.parse<Vec3>(blackbox<string>('{"x":3.4,"y":1.2,"z":8.3}')));
|
|
68
|
+
});*/
|