json-as 1.4.0 → 1.5.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/CHANGELOG.md +50 -29
- package/README.md +84 -33
- package/assembly/custom/chars.ts +39 -78
- package/assembly/deserialize/index/arbitrary.ts +26 -8
- package/assembly/deserialize/index/float.ts +2 -4
- package/assembly/deserialize/index/integer.ts +2 -4
- package/assembly/deserialize/index/object.ts +6 -1
- package/assembly/deserialize/index/string.ts +2 -7
- package/assembly/deserialize/index/unsigned.ts +2 -4
- package/assembly/deserialize/naive/array/integer.ts +1 -1
- package/assembly/deserialize/naive/array/map.ts +1 -1
- package/assembly/deserialize/naive/array/object.ts +1 -1
- package/assembly/deserialize/naive/array/struct.ts +19 -1
- package/assembly/deserialize/naive/bool.ts +1 -5
- package/assembly/deserialize/naive/date.ts +1 -2
- package/assembly/deserialize/naive/float.ts +2 -7
- package/assembly/deserialize/naive/integer.ts +1 -2
- package/assembly/deserialize/naive/map.ts +5 -6
- package/assembly/deserialize/naive/object.ts +151 -13
- package/assembly/deserialize/naive/raw.ts +1 -5
- package/assembly/deserialize/naive/set.ts +2 -4
- package/assembly/deserialize/naive/staticarray.ts +1 -2
- package/assembly/deserialize/naive/string.ts +6 -9
- package/assembly/deserialize/naive/unsigned.ts +1 -2
- package/assembly/deserialize/simd/array/integer.ts +2 -7
- package/assembly/deserialize/simd/float.ts +3 -5
- package/assembly/deserialize/simd/integer.ts +2 -7
- package/assembly/deserialize/simd/string.ts +5 -22
- package/assembly/deserialize/swar/array/arbitrary.ts +1 -2
- package/assembly/deserialize/swar/array/array.ts +1 -2
- package/assembly/deserialize/swar/array/bool.ts +1 -2
- package/assembly/deserialize/swar/array/box.ts +1 -2
- package/assembly/deserialize/swar/array/float.ts +6 -18
- package/assembly/deserialize/swar/array/generic.ts +1 -2
- package/assembly/deserialize/swar/array/integer.ts +7 -16
- package/assembly/deserialize/swar/array/map.ts +1 -2
- package/assembly/deserialize/swar/array/object.ts +1 -2
- package/assembly/deserialize/swar/array/raw.ts +1 -2
- package/assembly/deserialize/swar/array/shared.ts +6 -13
- package/assembly/deserialize/swar/array/string.ts +3 -8
- package/assembly/deserialize/swar/array/struct.ts +2 -8
- package/assembly/deserialize/swar/array.ts +1 -3
- package/assembly/deserialize/swar/float.ts +4 -9
- package/assembly/deserialize/swar/integer.ts +2 -7
- package/assembly/deserialize/swar/string.ts +13 -15
- package/assembly/deserialize/swar/typedarray.ts +4 -4
- package/assembly/index.d.ts +29 -24
- package/assembly/index.ts +1362 -246
- package/assembly/serialize/index/arbitrary.ts +70 -4
- package/assembly/serialize/index/jsonarray.ts +51 -0
- package/assembly/serialize/index/object.ts +25 -3
- package/assembly/serialize/index/string.ts +1 -2
- package/assembly/serialize/index/typedarray.ts +1 -2
- package/assembly/serialize/index.ts +1 -0
- package/assembly/serialize/naive/array.ts +23 -34
- package/assembly/serialize/naive/bool.ts +0 -1
- package/assembly/serialize/naive/float.ts +16 -25
- package/assembly/serialize/naive/integer.ts +1 -5
- package/assembly/serialize/naive/raw.ts +1 -2
- package/assembly/serialize/naive/set.ts +0 -4
- package/assembly/serialize/naive/staticarray.ts +0 -5
- package/assembly/serialize/naive/string.ts +2 -5
- package/assembly/serialize/naive/typedarray.ts +0 -6
- package/assembly/serialize/simd/string.ts +1 -3
- package/assembly/serialize/swar/string.ts +1 -2
- package/assembly/util/atoi-fast.ts +4 -14
- package/assembly/util/bytes.ts +1 -2
- package/assembly/util/idofd.ts +1 -2
- package/assembly/util/isSpace.ts +1 -2
- package/assembly/util/itoa-fast.ts +6 -9
- package/assembly/util/nextPowerOf2.ts +1 -2
- package/assembly/util/parsefloat-fast.ts +3 -5
- package/assembly/util/ptrToStr.ts +1 -2
- package/assembly/util/scanValueEndSimd.ts +54 -16
- package/assembly/util/scanValueEndSwar.ts +67 -25
- package/assembly/util/scientific.ts +5 -8
- package/assembly/util/snp.ts +1 -2
- package/assembly/util/swar-int.ts +5 -10
- package/assembly/util/swar.ts +2 -4
- package/lib/as-bs.ts +23 -45
- package/package.json +14 -7
- package/transform/lib/index.js +108 -64
- package/transform/lib/types.d.ts +2 -1
- package/transform/lib/types.js +3 -0
- package/assembly/util/dragonbox-cache.ts +0 -445
- package/assembly/util/dragonbox.ts +0 -652
package/lib/as-bs.ts
CHANGED
|
@@ -40,15 +40,13 @@ export namespace bs {
|
|
|
40
40
|
* Using bit shifts for efficiency: alpha = 1/8, so (1 - alpha) = 7/8
|
|
41
41
|
* @param newSize - The new size to incorporate into the average
|
|
42
42
|
*/
|
|
43
|
-
|
|
44
|
-
@inline function updateTypicalSize(newSize: usize): void {
|
|
43
|
+
function updateTypicalSize(newSize: usize): void {
|
|
45
44
|
// EMA: typicalSize = (newSize >> 3) + typicalSize - (typicalSize >> 3)
|
|
46
45
|
// Simplified: typicalSize += (newSize - typicalSize) >> 3
|
|
47
46
|
typicalSize += (newSize - typicalSize) >> EMA_ALPHA_SHIFT;
|
|
48
47
|
}
|
|
49
48
|
|
|
50
|
-
|
|
51
|
-
@inline function renewBuffer(newSize: usize): void {
|
|
49
|
+
function renewBuffer(newSize: usize): void {
|
|
52
50
|
const oldPtr = buffer;
|
|
53
51
|
const relOffset = offset - oldPtr;
|
|
54
52
|
const newPtr = heap.realloc(oldPtr, newSize);
|
|
@@ -57,8 +55,7 @@ export namespace bs {
|
|
|
57
55
|
bufferSize = newSize;
|
|
58
56
|
}
|
|
59
57
|
|
|
60
|
-
|
|
61
|
-
@inline function reserve(requiredSize: usize, extra: usize): void {
|
|
58
|
+
function reserve(requiredSize: usize, extra: usize): void {
|
|
62
59
|
if (requiredSize <= bufferSize) return;
|
|
63
60
|
// Grow aggressively (2x) to minimize realloc frequency in hot serialization paths.
|
|
64
61
|
let next = bufferSize << 1;
|
|
@@ -67,8 +64,7 @@ export namespace bs {
|
|
|
67
64
|
renewBuffer(next);
|
|
68
65
|
}
|
|
69
66
|
|
|
70
|
-
|
|
71
|
-
@inline function finalizeDynamicOutput(len: usize): void {
|
|
67
|
+
function finalizeDynamicOutput(len: usize): void {
|
|
72
68
|
counter += 1;
|
|
73
69
|
updateTypicalSize(len);
|
|
74
70
|
if ((counter & SHRINK_EVERY_N_MASK) == 0 && bufferSize > typicalSize << 2) {
|
|
@@ -83,8 +79,7 @@ export namespace bs {
|
|
|
83
79
|
/**
|
|
84
80
|
* Stores the state of the buffer, allowing further changes to be reset
|
|
85
81
|
*/
|
|
86
|
-
|
|
87
|
-
@inline export function saveState(): void {
|
|
82
|
+
export function saveState(): void {
|
|
88
83
|
pauseOffsets.push(offset - buffer);
|
|
89
84
|
pauseStackSizes.push(stackSize);
|
|
90
85
|
}
|
|
@@ -93,8 +88,7 @@ export namespace bs {
|
|
|
93
88
|
* Resets the buffer to the state it was in when `pause()` was called.
|
|
94
89
|
* This allows for changes made after the pause to be discarded.
|
|
95
90
|
*/
|
|
96
|
-
|
|
97
|
-
@inline export function loadState(): void {
|
|
91
|
+
export function loadState(): void {
|
|
98
92
|
const length = pauseOffsets.length;
|
|
99
93
|
if (length == 0) return;
|
|
100
94
|
const index = length - 1;
|
|
@@ -109,8 +103,7 @@ export namespace bs {
|
|
|
109
103
|
* serialize/deserialize op mid-flight: a partial run can leave `offset`
|
|
110
104
|
* advanced and the pause stacks non-empty, which would corrupt the next op.
|
|
111
105
|
*/
|
|
112
|
-
|
|
113
|
-
@inline export function reset(): void {
|
|
106
|
+
export function reset(): void {
|
|
114
107
|
offset = buffer;
|
|
115
108
|
stackSize = 0;
|
|
116
109
|
pauseOffsets.length = 0;
|
|
@@ -122,8 +115,7 @@ export namespace bs {
|
|
|
122
115
|
* If necessary, reallocates the buffer to the exact new size.
|
|
123
116
|
* @param size - The size to propose.
|
|
124
117
|
*/
|
|
125
|
-
|
|
126
|
-
@inline export function ensureSize(size: u32): void {
|
|
118
|
+
export function ensureSize(size: u32): void {
|
|
127
119
|
reserve(offset - buffer + usize(size), MIN_BUFFER_SIZE);
|
|
128
120
|
}
|
|
129
121
|
|
|
@@ -132,8 +124,7 @@ export namespace bs {
|
|
|
132
124
|
* If necessary, reallocates the buffer to the exact new size.
|
|
133
125
|
* @param size - The size to propose.w
|
|
134
126
|
*/
|
|
135
|
-
|
|
136
|
-
@inline export function proposeSize(size: u32): void {
|
|
127
|
+
export function proposeSize(size: u32): void {
|
|
137
128
|
stackSize += size;
|
|
138
129
|
reserve(stackSize, 0);
|
|
139
130
|
}
|
|
@@ -143,8 +134,7 @@ export namespace bs {
|
|
|
143
134
|
* If necessary, reallocates the buffer to the exact new size.
|
|
144
135
|
* @param size - The size to grow by.
|
|
145
136
|
*/
|
|
146
|
-
|
|
147
|
-
@inline export function growSize(size: u32): void {
|
|
137
|
+
export function growSize(size: u32): void {
|
|
148
138
|
stackSize += size;
|
|
149
139
|
reserve(stackSize, MIN_BUFFER_SIZE);
|
|
150
140
|
}
|
|
@@ -153,8 +143,7 @@ export namespace bs {
|
|
|
153
143
|
* Resizes the buffer to the specified size.
|
|
154
144
|
* @param newSize - The new buffer size.
|
|
155
145
|
*/
|
|
156
|
-
|
|
157
|
-
@inline export function resize(newSize: u32): void {
|
|
146
|
+
export function resize(newSize: u32): void {
|
|
158
147
|
const oldPtr = buffer;
|
|
159
148
|
const relOffset = offset - oldPtr;
|
|
160
149
|
const newPtr = heap.realloc(buffer, newSize);
|
|
@@ -168,8 +157,7 @@ export namespace bs {
|
|
|
168
157
|
* finalization. Keeps enough capacity for the recent typical output size while
|
|
169
158
|
* releasing clearly excess memory.
|
|
170
159
|
*/
|
|
171
|
-
|
|
172
|
-
@inline export function shrink(): void {
|
|
160
|
+
export function shrink(): void {
|
|
173
161
|
let next = typicalSize << 1;
|
|
174
162
|
if (next < MIN_BUFFER_SIZE) next = MIN_BUFFER_SIZE;
|
|
175
163
|
if (bufferSize > next) {
|
|
@@ -184,8 +172,7 @@ export namespace bs {
|
|
|
184
172
|
* Copies the buffer's content to a new object of a specified type. Does not shrink the buffer.
|
|
185
173
|
* @returns The new object containing the buffer's content.
|
|
186
174
|
*/
|
|
187
|
-
|
|
188
|
-
@inline export function cpyOut<T>(): T {
|
|
175
|
+
export function cpyOut<T>(): T {
|
|
189
176
|
if (pauseOffsets.length == 0) {
|
|
190
177
|
const len = offset - buffer;
|
|
191
178
|
// @ts-expect-error: __new is a runtime builtin
|
|
@@ -214,8 +201,7 @@ export namespace bs {
|
|
|
214
201
|
* Note: this restores only `offset`. Deserialization paths do not currently depend on
|
|
215
202
|
* `stackSize`, which is tracked for serialization growth heuristics.
|
|
216
203
|
*/
|
|
217
|
-
|
|
218
|
-
@inline export function sliceOut<T>(start: usize): T {
|
|
204
|
+
export function sliceOut<T>(start: usize): T {
|
|
219
205
|
const sliceStart = buffer + start;
|
|
220
206
|
const len = offset - sliceStart;
|
|
221
207
|
// @ts-expect-error: __new is a runtime builtin
|
|
@@ -229,8 +215,7 @@ export namespace bs {
|
|
|
229
215
|
* Copies the slice starting at a caller-provided relative buffer offset into a string field
|
|
230
216
|
* and restores `offset` back to that slice start.
|
|
231
217
|
*/
|
|
232
|
-
|
|
233
|
-
@inline export function toField(start: usize, dstFieldPtr: usize): void {
|
|
218
|
+
export function toField(start: usize, dstFieldPtr: usize): void {
|
|
234
219
|
const sliceStart = buffer + start;
|
|
235
220
|
const byteLength = <u32>(offset - sliceStart);
|
|
236
221
|
if (byteLength == 0) {
|
|
@@ -265,8 +250,7 @@ export namespace bs {
|
|
|
265
250
|
* adaptive buffer management - shrinks buffer when consistently oversized.
|
|
266
251
|
* @returns The new object containing the buffer's content.
|
|
267
252
|
*/
|
|
268
|
-
|
|
269
|
-
@inline export function out<T>(): T {
|
|
253
|
+
export function out<T>(): T {
|
|
270
254
|
let out: usize;
|
|
271
255
|
if (cacheOutput === 0) {
|
|
272
256
|
const len = offset - buffer;
|
|
@@ -296,8 +280,7 @@ export namespace bs {
|
|
|
296
280
|
* can pass an empty/uninitialized target on the first call. Mirrors the reuse
|
|
297
281
|
* policy of `toField`, but for the top-level return value.
|
|
298
282
|
*/
|
|
299
|
-
|
|
300
|
-
@inline export function outTo<T>(target: usize): T {
|
|
283
|
+
export function outTo<T>(target: usize): T {
|
|
301
284
|
if (target < __heap_base) return out<T>();
|
|
302
285
|
let len: usize;
|
|
303
286
|
let src: usize;
|
|
@@ -309,7 +292,7 @@ export namespace bs {
|
|
|
309
292
|
src = cacheOutput;
|
|
310
293
|
}
|
|
311
294
|
let dst = target;
|
|
312
|
-
if (changetype<OBJECT>(target - TOTAL_OVERHEAD).rtSize != len) {
|
|
295
|
+
if (changetype<OBJECT>(target - TOTAL_OVERHEAD).rtSize != <u32>len) {
|
|
313
296
|
// @ts-expect-error: __renew is a runtime builtin
|
|
314
297
|
dst = __renew(target, len);
|
|
315
298
|
}
|
|
@@ -371,12 +354,9 @@ export namespace bs {
|
|
|
371
354
|
* avoiding re-serialization of previously seen strings.
|
|
372
355
|
*/
|
|
373
356
|
export namespace sc {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
@inline export const ENTRY_PTR = offsetof<sc.Entry>("ptr");
|
|
378
|
-
// @ts-expect-error: @inline is a valid decorator
|
|
379
|
-
@inline export const ENTRY_LEN = offsetof<sc.Entry>("len");
|
|
357
|
+
export const ENTRY_KEY = offsetof<sc.Entry>("key");
|
|
358
|
+
export const ENTRY_PTR = offsetof<sc.Entry>("ptr");
|
|
359
|
+
export const ENTRY_LEN = offsetof<sc.Entry>("len");
|
|
380
360
|
|
|
381
361
|
// @ts-expect-error: JSON_CACHE may not be defined. If so, it will default to false.
|
|
382
362
|
export const CACHE_ENABLED: bool = isDefined(JSON_CACHE) ? JSON_CACHE : false;
|
|
@@ -425,8 +405,7 @@ export namespace sc {
|
|
|
425
405
|
* Uses pointer address shifted right by 4 bits (aligned to 16-byte boundaries)
|
|
426
406
|
* masked to fit within cache size.
|
|
427
407
|
*/
|
|
428
|
-
|
|
429
|
-
@inline
|
|
408
|
+
|
|
430
409
|
export function indexFor(ptr: usize): usize {
|
|
431
410
|
return (ptr >> 4) & CACHE_MASK;
|
|
432
411
|
}
|
|
@@ -437,8 +416,7 @@ export namespace sc {
|
|
|
437
416
|
* @param key - The string pointer to look up
|
|
438
417
|
* @returns true if cache hit, false if cache miss
|
|
439
418
|
*/
|
|
440
|
-
|
|
441
|
-
@inline
|
|
419
|
+
|
|
442
420
|
export function tryEmitCached(key: usize): bool {
|
|
443
421
|
const e = unchecked(entries[indexFor(key)]);
|
|
444
422
|
if (e.key == key) {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "json-as",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"author": "Jairus Tanaka",
|
|
3
|
+
"version": "1.5.0",
|
|
4
|
+
"author": "Jairus Tanaka <me@jairus.dev>",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
7
7
|
"url": "git+https://github.com/JairusSW/json-as.git"
|
|
@@ -11,15 +11,15 @@
|
|
|
11
11
|
"devDependencies": {
|
|
12
12
|
"@assemblyscript/wasi-shim": "^0.1.0",
|
|
13
13
|
"@eslint/js": "^10.0.1",
|
|
14
|
-
"@types/node": "^25.9.
|
|
14
|
+
"@types/node": "^25.9.2",
|
|
15
15
|
"as-heap-analyzer": "^1.2.0",
|
|
16
16
|
"as-test": "^1.6.0",
|
|
17
|
-
"assemblyscript": "^0.28.
|
|
17
|
+
"assemblyscript": "^0.28.18",
|
|
18
18
|
"assemblyscript-json": "^1.1.0",
|
|
19
19
|
"assemblyscript-prettier": "^3.0.4",
|
|
20
20
|
"chartjs-node-canvas": "^5.0.0",
|
|
21
21
|
"chartjs-plugin-datalabels": "^2.2.0",
|
|
22
|
-
"eslint": "^10.4.
|
|
22
|
+
"eslint": "^10.4.1",
|
|
23
23
|
"fast-json-parse": "^1.0.3",
|
|
24
24
|
"fast-json-stringify": "^6.4.0",
|
|
25
25
|
"husky": "^9.1.7",
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"tinybench": "^6.0.2",
|
|
30
30
|
"try-as": "^1.1.4",
|
|
31
31
|
"typescript": "^6.0.3",
|
|
32
|
-
"typescript-eslint": "^8.60.
|
|
32
|
+
"typescript-eslint": "^8.60.1"
|
|
33
33
|
},
|
|
34
34
|
"bugs": {
|
|
35
35
|
"url": "https://github.com/JairusSW/json-as/issues"
|
|
@@ -98,6 +98,7 @@
|
|
|
98
98
|
"test:ci": "ast test --parallel --clean --enable try-as",
|
|
99
99
|
"test:coverage": "ast test --enable coverage --enable try-as",
|
|
100
100
|
"bench": "bash -c 'bash ./scripts/run-bench.as.sh \"$@\" && { arg=\"${1:-}\"; if [ -z \"$arg\" ] || [ \"${arg#custom/}\" = \"$arg\" ]; then bash ./scripts/run-bench.js.sh \"$@\"; fi; } && bash ./scripts/build-charts.sh' --",
|
|
101
|
+
"bench:all": "bash ./scripts/bench-all.sh",
|
|
101
102
|
"bench:as": "bash ./scripts/run-bench.as.sh",
|
|
102
103
|
"bench:js": "bash ./scripts/run-bench.js.sh",
|
|
103
104
|
"charts": "bun run charts:build && bun run charts:serve",
|
|
@@ -115,6 +116,9 @@
|
|
|
115
116
|
"run:playground": "wasmtime ./build/playground.wasm",
|
|
116
117
|
"run:pg": "npm run run:playground",
|
|
117
118
|
"playground": "npm run build:playground && npm run run:playground",
|
|
119
|
+
"build:playground:tmp": "asc assembly/playground.tmp.ts -o ./build/playground.tmp.wasm --textFile ./build/playground.tmp.wat -O3 --noAssert --uncheckedBehavior always --runtime incremental --enable bulk-memory --enable simd --use JSON_MODE=1 --exportStart start --exportRuntime",
|
|
120
|
+
"playground:tmp": "npm run build:playground:tmp && v8 --no-liftoff --module ./bench/runners/assemblyscript.js -- playground.tmp.wasm",
|
|
121
|
+
"pg:tmp": "npm run playground:tmp",
|
|
118
122
|
"play": "npm run playground",
|
|
119
123
|
"pg": "npm run playground",
|
|
120
124
|
"bench:wasmer": "wasmer ./build/bench.wasm --llvm",
|
|
@@ -132,5 +136,8 @@
|
|
|
132
136
|
"prepare": "husky"
|
|
133
137
|
},
|
|
134
138
|
"type": "module",
|
|
135
|
-
"types": "assembly/index.ts"
|
|
139
|
+
"types": "assembly/index.ts",
|
|
140
|
+
"dependencies": {
|
|
141
|
+
"xjb-as": "^0.1.0"
|
|
142
|
+
}
|
|
136
143
|
}
|
package/transform/lib/index.js
CHANGED
|
@@ -310,6 +310,7 @@ export class JSONTransform extends Visitor {
|
|
|
310
310
|
}) ??
|
|
311
311
|
false));
|
|
312
312
|
let __hasLazy = false;
|
|
313
|
+
const __lazyValInits = new Map();
|
|
313
314
|
for (let i = node.members.length - 1; i >= 0; i--) {
|
|
314
315
|
const fd = node.members[i];
|
|
315
316
|
if (fd.kind !== NodeKind.FieldDeclaration ||
|
|
@@ -340,7 +341,7 @@ export class JSONTransform extends Visitor {
|
|
|
340
341
|
continue;
|
|
341
342
|
if (hasCustomSerde)
|
|
342
343
|
throwError("Lazy fields (@lazy / JSON.Lazy<T> / @json({ lazy })) are not supported " +
|
|
343
|
-
"on a class with a custom @serializer/@deserializer
|
|
344
|
+
"on a class with a custom @serializer/@deserializer - the custom methods " +
|
|
344
345
|
"bypass the generated (de)serializer, so the deferred slot is never filled. " +
|
|
345
346
|
"Remove the lazy marker or the custom (de)serializer.", fd.range);
|
|
346
347
|
const fname = fd.name.text;
|
|
@@ -356,6 +357,13 @@ export class JSONTransform extends Visitor {
|
|
|
356
357
|
: "null";
|
|
357
358
|
const fdInit = fd.initializer;
|
|
358
359
|
const fieldDefault = fdInit ? toString(fdInit) : null;
|
|
360
|
+
const refDefault = fieldDefault != null
|
|
361
|
+
? fieldDefault
|
|
362
|
+
: !storesScalar && baseT == T && !baseT.startsWith("StaticArray<")
|
|
363
|
+
? baseT == "string" || baseT == "String"
|
|
364
|
+
? '""'
|
|
365
|
+
: `new ${baseT}()`
|
|
366
|
+
: null;
|
|
359
367
|
__hasLazy = true;
|
|
360
368
|
const omitIfDeco = decos?.find((d) => d.name.text === "omitif");
|
|
361
369
|
lazyInner.set("__" + fname + "_lz", {
|
|
@@ -397,8 +405,8 @@ export class JSONTransform extends Visitor {
|
|
|
397
405
|
` this.__${fname}_lz = ((<u64>0xffffffff) << 32) | ${encVal("value")};\n}`,
|
|
398
406
|
]
|
|
399
407
|
: [
|
|
400
|
-
`@alias(${key}) private __${fname}_lz: u64 = ${
|
|
401
|
-
`private __${fname}_val: ${valueType} = ${
|
|
408
|
+
`@alias(${key}) private __${fname}_lz: u64 = ${refDefault != null ? "u64.MAX_VALUE" : "0"};`,
|
|
409
|
+
`private __${fname}_val: ${valueType} = ${refDefault ?? valueDefault};`,
|
|
402
410
|
`get ${fname}(): ${T} {\n` +
|
|
403
411
|
` const __lz = this.__${fname}_lz;\n` +
|
|
404
412
|
` if (__lz != 0 && __lz != u64.MAX_VALUE) {\n` +
|
|
@@ -411,6 +419,9 @@ export class JSONTransform extends Visitor {
|
|
|
411
419
|
` this.__${fname}_lz = u64.MAX_VALUE;\n}`,
|
|
412
420
|
]).map((src) => SimpleParser.parseClassMember(src, node));
|
|
413
421
|
node.members.splice(i, 1, ...lowered);
|
|
422
|
+
if (!packScalar && refDefault != null) {
|
|
423
|
+
__lazyValInits.set(`__${fname}_lz`, ` store<${valueType}>(changetype<usize>(this), ${refDefault}, offsetof<this>(${JSON.stringify(`__${fname}_val`)}));\n`);
|
|
424
|
+
}
|
|
414
425
|
}
|
|
415
426
|
if (__hasLazy) {
|
|
416
427
|
node.members.push(SimpleParser.parseClassMember(`private __src: string = "";`, node), SimpleParser.parseClassMember(`__SET_SRC(s: string): void { this.__src = s; }`, node));
|
|
@@ -569,7 +580,7 @@ export class JSONTransform extends Visitor {
|
|
|
569
580
|
else if (isString(type) || isPrimitive(type)) {
|
|
570
581
|
return types;
|
|
571
582
|
}
|
|
572
|
-
else if (["JSON.Box", "JSON.Obj", "JSON.Value", "JSON.Raw"].includes(type)) {
|
|
583
|
+
else if (["JSON.Box", "JSON.Obj", "JSON.Arr", "JSON.Value", "JSON.Raw"].includes(type)) {
|
|
573
584
|
return types;
|
|
574
585
|
}
|
|
575
586
|
else if (node.isGeneric &&
|
|
@@ -665,9 +676,9 @@ export class JSONTransform extends Visitor {
|
|
|
665
676
|
this.visitedClasses.add(fullClassPath);
|
|
666
677
|
const requestedFastPath = USE_FAST_PATH;
|
|
667
678
|
let SERIALIZE = "__SERIALIZE(ptr: usize): void {\n";
|
|
668
|
-
let INITIALIZE = "
|
|
679
|
+
let INITIALIZE = " __INITIALIZE(): this {\n";
|
|
669
680
|
let DESERIALIZE = "__DESERIALIZE_SLOW<__JSON_T>(srcStart: usize, srcEnd: usize, out: __JSON_T): usize {\n";
|
|
670
|
-
let DESERIALIZE_FAST = "
|
|
681
|
+
let DESERIALIZE_FAST = "__DESERIALIZE_FAST<__JSON_T>(srcStart: usize, srcEnd: usize, out: __JSON_T): usize {\n";
|
|
671
682
|
let DESERIALIZE_CUSTOM = "";
|
|
672
683
|
let SERIALIZE_CUSTOM = "";
|
|
673
684
|
if (DEBUG > 0)
|
|
@@ -773,8 +784,7 @@ export class JSONTransform extends Visitor {
|
|
|
773
784
|
if (!deserializer.decorators.some((v) => v.name.text == "inline")) {
|
|
774
785
|
deserializer.decorators.push(Node.createDecorator(Node.createIdentifierExpression("inline", deserializer.range), null, deserializer.range));
|
|
775
786
|
}
|
|
776
|
-
DESERIALIZE_CUSTOM +=
|
|
777
|
-
" @inline __DESERIALIZE_CUSTOM(data: string): this {\n";
|
|
787
|
+
DESERIALIZE_CUSTOM += " __DESERIALIZE_CUSTOM(data: string): this {\n";
|
|
778
788
|
DESERIALIZE_CUSTOM +=
|
|
779
789
|
" return this." + deserializer.name.text + "(data);\n";
|
|
780
790
|
DESERIALIZE_CUSTOM += " }\n";
|
|
@@ -841,6 +851,10 @@ export class JSONTransform extends Visitor {
|
|
|
841
851
|
this.schema.static = false;
|
|
842
852
|
break;
|
|
843
853
|
}
|
|
854
|
+
case "optional": {
|
|
855
|
+
mem.flags.set(PropertyFlags.Optional, null);
|
|
856
|
+
break;
|
|
857
|
+
}
|
|
844
858
|
case "omitnull": {
|
|
845
859
|
if (isPrimitive(type)) {
|
|
846
860
|
throwError("@omitnull cannot be used on primitive types!", member.range);
|
|
@@ -857,11 +871,11 @@ export class JSONTransform extends Visitor {
|
|
|
857
871
|
}
|
|
858
872
|
this.schema.members.push(mem);
|
|
859
873
|
}
|
|
860
|
-
|
|
861
|
-
this.schema.members = sortMembers(this.schema.members);
|
|
874
|
+
this.schema.members = sortMembers(this.schema.members);
|
|
862
875
|
const hasOmitIfMembers = this.schema.members.some((v) => v.flags.has(PropertyFlags.OmitIf));
|
|
863
876
|
const hasOmitNullMembers = this.schema.members.some((v) => v.flags.has(PropertyFlags.OmitNull));
|
|
864
|
-
const
|
|
877
|
+
const hasExplicitOptionalMembers = this.schema.members.some((v) => v.flags.has(PropertyFlags.Optional));
|
|
878
|
+
const hasOptionalMembers = hasOmitIfMembers || hasOmitNullMembers || hasExplicitOptionalMembers;
|
|
865
879
|
const hasLazyMembers = this.schema.members.some((v) => v.flags.has(PropertyFlags.Lazy));
|
|
866
880
|
const supportsFastOptionalPath = requestedFastPath && hasOptionalMembers;
|
|
867
881
|
const hasTypeParams = !!node.typeParameters && node.typeParameters.length > 0;
|
|
@@ -942,12 +956,10 @@ export class JSONTransform extends Visitor {
|
|
|
942
956
|
const aliasName = JSON.stringify(member.alias || member.name);
|
|
943
957
|
const realName = member.name;
|
|
944
958
|
if (member.value) {
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
INITIALIZE += ` store<${member.type}>(changetype<usize>(this), ${member.value}, offsetof<this>(${JSON.stringify(member.name)}));\n`;
|
|
950
|
-
}
|
|
959
|
+
INITIALIZE += ` store<${member.type}>(changetype<usize>(this), ${member.value}, offsetof<this>(${JSON.stringify(member.name)}));\n`;
|
|
960
|
+
const __valInit = __lazyValInits.get(member.name);
|
|
961
|
+
if (__valInit)
|
|
962
|
+
INITIALIZE += __valInit;
|
|
951
963
|
}
|
|
952
964
|
else if (member.generic) {
|
|
953
965
|
INITIALIZE += ` if (isManaged<nonnull<${member.type}>>() || isReference<nonnull<${member.type}>>()) {\n`;
|
|
@@ -974,6 +986,9 @@ export class JSONTransform extends Visitor {
|
|
|
974
986
|
INITIALIZE += ` store<${member.type}>(changetype<usize>(this), "", offsetof<this>(${JSON.stringify(member.name)}));\n`;
|
|
975
987
|
}
|
|
976
988
|
}
|
|
989
|
+
else {
|
|
990
|
+
INITIALIZE += ` store<${member.type}>(changetype<usize>(this), null, offsetof<this>(${JSON.stringify(member.name)}));\n`;
|
|
991
|
+
}
|
|
977
992
|
const SIMD_ENABLED = this.program.options.hasFeature(16);
|
|
978
993
|
if (!isRegular &&
|
|
979
994
|
!member.flags.has(PropertyFlags.OmitIf) &&
|
|
@@ -1116,7 +1131,7 @@ export class JSONTransform extends Visitor {
|
|
|
1116
1131
|
type.startsWith("JSON.Box<") ||
|
|
1117
1132
|
isEnum(type, this.sources.get(this.schema.node.range.source), this.parser))
|
|
1118
1133
|
sortedMembers.number.push(member);
|
|
1119
|
-
else if (isArray(type))
|
|
1134
|
+
else if (isArray(type) || type == "JSON.Arr" || type == "Arr")
|
|
1120
1135
|
sortedMembers.array.push(member);
|
|
1121
1136
|
else
|
|
1122
1137
|
sortedMembers.object.push(member);
|
|
@@ -1337,9 +1352,18 @@ export class JSONTransform extends Visitor {
|
|
|
1337
1352
|
out.push(` if (changetype<usize>(value) == 0) {`);
|
|
1338
1353
|
out.push(` value = changetype<${resolvedType}>(__new(offsetof<nonnull<${resolvedType}>>(), idof<nonnull<${resolvedType}>>()));`);
|
|
1339
1354
|
out.push(` store<${resolvedType}>(${outPtr}, value, ${fieldOffset});`);
|
|
1355
|
+
out.push(` changetype<nonnull<${resolvedType}>>(value).__INITIALIZE();`);
|
|
1340
1356
|
out.push(" }");
|
|
1341
|
-
out.push(`
|
|
1342
|
-
out.push(` if (
|
|
1357
|
+
out.push(` const __fe = changetype<nonnull<${resolvedType}>>(value).__DESERIALIZE_FAST<${resolvedType}>(${valuePtr}, srcEnd, value);`);
|
|
1358
|
+
out.push(` if (__fe) {`);
|
|
1359
|
+
out.push(` ${srcPtr} = __fe;`);
|
|
1360
|
+
out.push(` } else {`);
|
|
1361
|
+
out.push(` const __ve = JSON.Util.scanValueEnd<${resolvedType}>(${valuePtr}, srcEnd);`);
|
|
1362
|
+
out.push(` if (!__ve) break;`);
|
|
1363
|
+
out.push(` changetype<nonnull<${resolvedType}>>(value).__INITIALIZE();`);
|
|
1364
|
+
out.push(` changetype<nonnull<${resolvedType}>>(value).__DESERIALIZE_SLOW<${resolvedType}>(${valuePtr}, __ve, value);`);
|
|
1365
|
+
out.push(` ${srcPtr} = __ve;`);
|
|
1366
|
+
out.push(` }`);
|
|
1343
1367
|
if (member.node.type.isNullable) {
|
|
1344
1368
|
out.push(" }");
|
|
1345
1369
|
}
|
|
@@ -1454,13 +1478,24 @@ export class JSONTransform extends Visitor {
|
|
|
1454
1478
|
out.push(" if (changetype<usize>(item) == 0) {");
|
|
1455
1479
|
out.push(` item = changetype<${valueType}>(__new(offsetof<nonnull<${valueType}>>(), idof<nonnull<${valueType}>>()));`);
|
|
1456
1480
|
out.push(" unchecked((value[index] = item));");
|
|
1481
|
+
out.push(` changetype<nonnull<${valueType}>>(item).__INITIALIZE();`);
|
|
1457
1482
|
out.push(" }");
|
|
1458
1483
|
out.push(" } else {");
|
|
1459
1484
|
out.push(` item = changetype<${valueType}>(__new(offsetof<nonnull<${valueType}>>(), idof<nonnull<${valueType}>>()));`);
|
|
1460
1485
|
out.push(" value.push(item);");
|
|
1486
|
+
out.push(` changetype<nonnull<${valueType}>>(item).__INITIALIZE();`);
|
|
1461
1487
|
out.push(" }");
|
|
1462
|
-
out.push(`
|
|
1463
|
-
out.push(`
|
|
1488
|
+
out.push(` const __es = ${srcPtr};`);
|
|
1489
|
+
out.push(` const __ee = changetype<nonnull<${valueType}>>(item).__DESERIALIZE_FAST<${valueType}>(${srcPtr}, srcEnd, item);`);
|
|
1490
|
+
out.push(` if (__ee) {`);
|
|
1491
|
+
out.push(` ${srcPtr} = __ee;`);
|
|
1492
|
+
out.push(` } else {`);
|
|
1493
|
+
out.push(` const __ve = JSON.Util.scanValueEnd<${valueType}>(__es, srcEnd);`);
|
|
1494
|
+
out.push(` if (!__ve) break;`);
|
|
1495
|
+
out.push(` changetype<nonnull<${valueType}>>(item).__INITIALIZE();`);
|
|
1496
|
+
out.push(` changetype<nonnull<${valueType}>>(item).__DESERIALIZE_SLOW<${valueType}>(__es, __ve, item);`);
|
|
1497
|
+
out.push(` ${srcPtr} = __ve;`);
|
|
1498
|
+
out.push(` }`);
|
|
1464
1499
|
out.push(" index++;");
|
|
1465
1500
|
out.push(` ${srcPtr} = JSON.Util.skipWhitespace(${srcPtr}, srcEnd);`);
|
|
1466
1501
|
out.push(` const code = load<u16>(${srcPtr});`);
|
|
@@ -1481,7 +1516,7 @@ export class JSONTransform extends Visitor {
|
|
|
1481
1516
|
out.push("}");
|
|
1482
1517
|
return out;
|
|
1483
1518
|
}
|
|
1484
|
-
out.push(` ${srcPtr} = __deserializeArrayField_SWAR<${resolvedType}
|
|
1519
|
+
out.push(` ${srcPtr} = __deserializeArrayField_SWAR<nonnull<${resolvedType}>>(${valuePtr}, srcEnd, ${outPtr}, ${fieldOffset});`);
|
|
1485
1520
|
out.push(` if (!${srcPtr}) break;`);
|
|
1486
1521
|
if (member.node.type.isNullable) {
|
|
1487
1522
|
out.push(" }");
|
|
@@ -1502,6 +1537,7 @@ export class JSONTransform extends Visitor {
|
|
|
1502
1537
|
}
|
|
1503
1538
|
else if (resolvedType == "JSON.Value" ||
|
|
1504
1539
|
resolvedType == "JSON.Obj" ||
|
|
1540
|
+
resolvedType == "JSON.Arr" ||
|
|
1505
1541
|
isEnum(resolvedType, this.sources.get(this.schema.node.range.source), this.parser)) {
|
|
1506
1542
|
out.push("break;");
|
|
1507
1543
|
}
|
|
@@ -1531,29 +1567,8 @@ export class JSONTransform extends Visitor {
|
|
|
1531
1567
|
}
|
|
1532
1568
|
return calls;
|
|
1533
1569
|
};
|
|
1534
|
-
const chunkFastBlocksOptional = (blocks,
|
|
1535
|
-
|
|
1536
|
-
return blocks.join("");
|
|
1537
|
-
let calls = "";
|
|
1538
|
-
for (let c = 0; c < blocks.length; c += FAST_CHUNK_SIZE) {
|
|
1539
|
-
const name = `__DESERIALIZE_FAST_${tag}_${fastChunkId++}`;
|
|
1540
|
-
const body = blocks
|
|
1541
|
-
.slice(c, c + FAST_CHUNK_SIZE)
|
|
1542
|
-
.join("")
|
|
1543
|
-
.replace(/\bbreak;/g, "return 0;");
|
|
1544
|
-
fastChunkMethods.push(`${name}(srcStart: usize, srcEnd: usize, dst: usize, seenAny: bool): u64 {\n` +
|
|
1545
|
-
(needsKp ? " let kp: usize = 0;\n" : "") +
|
|
1546
|
-
`${body}\n` +
|
|
1547
|
-
` return (<u64>srcStart) | ((<u64>(seenAny ? 1 : 0)) << 32);\n}`);
|
|
1548
|
-
calls +=
|
|
1549
|
-
`${callIndent}{\n` +
|
|
1550
|
-
`${callIndent} const __r = this.${name}(srcStart, srcEnd, dst, seenAny);\n` +
|
|
1551
|
-
`${callIndent} if (__r == 0) break;\n` +
|
|
1552
|
-
`${callIndent} srcStart = <usize>(<u32>__r);\n` +
|
|
1553
|
-
`${callIndent} seenAny = (<u32>(__r >>> 32)) != 0;\n` +
|
|
1554
|
-
`${callIndent}}\n`;
|
|
1555
|
-
}
|
|
1556
|
-
return calls;
|
|
1570
|
+
const chunkFastBlocksOptional = (blocks, _tag, _callIndent, _needsKp) => {
|
|
1571
|
+
return blocks.join("");
|
|
1557
1572
|
};
|
|
1558
1573
|
DESERIALIZE_FAST += indent + "const start = srcStart;\n";
|
|
1559
1574
|
DESERIALIZE_FAST += indent + "const dst = changetype<usize>(out);\n";
|
|
@@ -2520,9 +2535,9 @@ export class JSONTransform extends Visitor {
|
|
|
2520
2535
|
.find((s) => s.name == name) || null);
|
|
2521
2536
|
}
|
|
2522
2537
|
generateEmptyMethods(node) {
|
|
2523
|
-
const SERIALIZE_EMPTY = "
|
|
2524
|
-
const INITIALIZE_EMPTY = "
|
|
2525
|
-
const DESERIALIZE_SLOW_EMPTY = "
|
|
2538
|
+
const SERIALIZE_EMPTY = "__SERIALIZE(ptr: usize): void {\n bs.proposeSize(4);\n store<u32>(bs.offset, 8192123);\n bs.offset += 4;\n}";
|
|
2539
|
+
const INITIALIZE_EMPTY = "__INITIALIZE(): this {\n return this;\n}";
|
|
2540
|
+
const DESERIALIZE_SLOW_EMPTY = "__DESERIALIZE_SLOW<__JSON_T>(srcStart: usize, srcEnd: usize, out: __JSON_T): usize {\n return srcEnd;\n}";
|
|
2526
2541
|
if (DEBUG > 0) {
|
|
2527
2542
|
console.log(SERIALIZE_EMPTY);
|
|
2528
2543
|
console.log(INITIALIZE_EMPTY);
|
|
@@ -2698,9 +2713,11 @@ export class JSONTransform extends Visitor {
|
|
|
2698
2713
|
"Date",
|
|
2699
2714
|
"JSON.Value",
|
|
2700
2715
|
"JSON.Obj",
|
|
2716
|
+
"JSON.Arr",
|
|
2701
2717
|
"JSON.Raw",
|
|
2702
2718
|
"Value",
|
|
2703
2719
|
"Obj",
|
|
2720
|
+
"Arr",
|
|
2704
2721
|
"Raw",
|
|
2705
2722
|
...this.schemas
|
|
2706
2723
|
.get(this.schema.node.range.source.internalPath)
|
|
@@ -2729,6 +2746,8 @@ var JSONMode;
|
|
|
2729
2746
|
JSONMode[JSONMode["NAIVE"] = 2] = "NAIVE";
|
|
2730
2747
|
})(JSONMode || (JSONMode = {}));
|
|
2731
2748
|
let MODE = JSONMode.SWAR;
|
|
2749
|
+
let MODE_TEXT = "SWAR";
|
|
2750
|
+
const STAGES = process.env["JSON_STAGES"] !== undefined;
|
|
2732
2751
|
export default class Transformer extends Transform {
|
|
2733
2752
|
afterInitialize(program) {
|
|
2734
2753
|
if (program.options.hasFeature(16))
|
|
@@ -2749,6 +2768,21 @@ export default class Transformer extends Transform {
|
|
|
2749
2768
|
}
|
|
2750
2769
|
}
|
|
2751
2770
|
}
|
|
2771
|
+
switch (MODE) {
|
|
2772
|
+
case JSONMode.SWAR:
|
|
2773
|
+
MODE_TEXT = "SWAR";
|
|
2774
|
+
break;
|
|
2775
|
+
case JSONMode.SIMD:
|
|
2776
|
+
MODE_TEXT = "SIMD";
|
|
2777
|
+
break;
|
|
2778
|
+
case JSONMode.NAIVE:
|
|
2779
|
+
MODE_TEXT = "NAIVE";
|
|
2780
|
+
break;
|
|
2781
|
+
}
|
|
2782
|
+
if (STAGES)
|
|
2783
|
+
console.log("[transform]: Finished initializing transformer in " +
|
|
2784
|
+
MODE_TEXT +
|
|
2785
|
+
" mode");
|
|
2752
2786
|
program.registerConstantInteger("JSON_MODE", Type.i32, i64_new(MODE));
|
|
2753
2787
|
if (JSON_CACHE_CONFIG.enabled) {
|
|
2754
2788
|
program.registerConstantInteger("JSON_CACHE", Type.bool, i64_one);
|
|
@@ -2757,6 +2791,8 @@ export default class Transformer extends Transform {
|
|
|
2757
2791
|
}
|
|
2758
2792
|
afterParse(parser) {
|
|
2759
2793
|
const transformer = new JSONTransform();
|
|
2794
|
+
if (STAGES)
|
|
2795
|
+
console.log("[transform]: Walking AST and generating schemas");
|
|
2760
2796
|
const sources = parser.sources
|
|
2761
2797
|
.filter((source) => {
|
|
2762
2798
|
const p = source.internalPath;
|
|
@@ -2812,23 +2848,12 @@ export default class Transformer extends Transform {
|
|
|
2812
2848
|
source.sourceKind = 2;
|
|
2813
2849
|
}
|
|
2814
2850
|
}
|
|
2851
|
+
if (STAGES)
|
|
2852
|
+
console.log("[transform]: Finished generating " +
|
|
2853
|
+
transformer.schemas.size +
|
|
2854
|
+
" schemas");
|
|
2815
2855
|
}
|
|
2816
2856
|
}
|
|
2817
|
-
function sortMembers(members) {
|
|
2818
|
-
return members.sort((a, b) => {
|
|
2819
|
-
const aMove = a.flags.has(PropertyFlags.OmitIf) || a.flags.has(PropertyFlags.OmitNull);
|
|
2820
|
-
const bMove = b.flags.has(PropertyFlags.OmitIf) || b.flags.has(PropertyFlags.OmitNull);
|
|
2821
|
-
if (aMove && !bMove) {
|
|
2822
|
-
return -1;
|
|
2823
|
-
}
|
|
2824
|
-
else if (!aMove && bMove) {
|
|
2825
|
-
return 1;
|
|
2826
|
-
}
|
|
2827
|
-
else {
|
|
2828
|
-
return 0;
|
|
2829
|
-
}
|
|
2830
|
-
});
|
|
2831
|
-
}
|
|
2832
2857
|
function toU16(data, offset = 0) {
|
|
2833
2858
|
return data.charCodeAt(offset + 0);
|
|
2834
2859
|
}
|
|
@@ -3011,6 +3036,8 @@ function lazyTypeCost(type, source, parser) {
|
|
|
3011
3036
|
base === "Value" ||
|
|
3012
3037
|
base === "JSON.Obj" ||
|
|
3013
3038
|
base === "Obj" ||
|
|
3039
|
+
base === "JSON.Arr" ||
|
|
3040
|
+
base === "Arr" ||
|
|
3014
3041
|
base === "JSON.Raw" ||
|
|
3015
3042
|
base === "Raw")
|
|
3016
3043
|
return 15;
|
|
@@ -3076,6 +3103,8 @@ function estimatedSerializedByteSize(type, source, parser) {
|
|
|
3076
3103
|
}
|
|
3077
3104
|
else if (baseType == "JSON.Obj" ||
|
|
3078
3105
|
baseType == "Obj" ||
|
|
3106
|
+
baseType == "JSON.Arr" ||
|
|
3107
|
+
baseType == "Arr" ||
|
|
3079
3108
|
baseType == "JSON.Raw" ||
|
|
3080
3109
|
baseType == "Raw" ||
|
|
3081
3110
|
baseType == "JSON.Value" ||
|
|
@@ -3134,6 +3163,21 @@ export function stripNull(type) {
|
|
|
3134
3163
|
}
|
|
3135
3164
|
return type;
|
|
3136
3165
|
}
|
|
3166
|
+
function sortMembers(members) {
|
|
3167
|
+
return members.sort((a, b) => {
|
|
3168
|
+
const aMove = a.flags.has(PropertyFlags.OmitIf) || a.flags.has(PropertyFlags.OmitNull);
|
|
3169
|
+
const bMove = b.flags.has(PropertyFlags.OmitIf) || b.flags.has(PropertyFlags.OmitNull);
|
|
3170
|
+
if (aMove && !bMove) {
|
|
3171
|
+
return -1;
|
|
3172
|
+
}
|
|
3173
|
+
else if (!aMove && bMove) {
|
|
3174
|
+
return 1;
|
|
3175
|
+
}
|
|
3176
|
+
else {
|
|
3177
|
+
return 0;
|
|
3178
|
+
}
|
|
3179
|
+
});
|
|
3180
|
+
}
|
|
3137
3181
|
function getComparison(data) {
|
|
3138
3182
|
switch (data.length << 1) {
|
|
3139
3183
|
case 2: {
|