json-as 1.3.2 → 1.3.4
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 +5 -0
- package/README.md +3 -2
- package/assembly/deserialize/simd/string.ts +20 -0
- package/assembly/deserialize/simple/map.ts +2 -0
- package/assembly/deserialize/simple/staticarray.ts +1 -0
- package/assembly/deserialize/simple/struct.ts +3 -1
- package/assembly/deserialize/swar/array/bool.ts +1 -0
- package/assembly/deserialize/swar/array/generic.ts +2 -1
- package/assembly/deserialize/swar/array/integer.ts +1 -0
- package/assembly/deserialize/swar/array/object.ts +1 -0
- package/assembly/deserialize/swar/array/shared.ts +18 -3
- package/assembly/deserialize/swar/array/string.ts +1 -0
- package/assembly/deserialize/swar/array/struct.ts +1 -0
- package/assembly/deserialize/swar/array.ts +1 -0
- package/assembly/deserialize/swar/string.ts +21 -2
- package/assembly/index.d.ts +1 -0
- package/assembly/index.ts +27 -22
- package/assembly/serialize/simd/string.ts +43 -16
- package/assembly/serialize/simple/array.ts +48 -6
- package/assembly/serialize/simple/bool.ts +12 -0
- package/assembly/serialize/simple/float.ts +16 -0
- package/assembly/serialize/simple/integer.ts +6 -0
- package/assembly/serialize/simple/set.ts +59 -5
- package/assembly/serialize/simple/staticarray.ts +57 -3
- package/assembly/serialize/simple/typedarray.ts +29 -9
- package/assembly/serialize/swar/string.ts +165 -18
- package/assembly/tsconfig.json +2 -2
- package/assembly/util/dragonbox-cache.ts +2 -1320
- package/assembly/util/dragonbox.ts +63 -35
- package/lib/as-bs.ts +26 -18
- package/package.json +11 -10
- package/transform/lib/index.d.ts.map +1 -1
- package/transform/lib/index.js +288 -73
- package/transform/lib/index.js.map +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 2026-04-28 - 1.3.3
|
|
6
|
+
|
|
7
|
+
- perf: made deserialization 200% to 300% faster
|
|
8
|
+
- chore: enable JSON_USE_FAST_PATH by default
|
|
9
|
+
|
|
5
10
|
## 2026-04-13 - 1.3.2
|
|
6
11
|
|
|
7
12
|
- fix: remove the fast double parser dependency and return float deserialization to the local legacy parser path
|
package/README.md
CHANGED
|
@@ -542,13 +542,13 @@ The following charts compare JSON-AS (both SWAR and SIMD variants) against JavaS
|
|
|
542
542
|
Instead of using flags for setting options, `json-as` is configured by environmental variables.
|
|
543
543
|
Here's a short list:
|
|
544
544
|
|
|
545
|
-
**JSON_CACHE** (default: 0) - Enables
|
|
545
|
+
**JSON_CACHE** (default: 0) - Enables and sizes string cache. Supports `true|false`, raw bytes (`JSON_CACHE=1048576`), bits (`JSON_CACHE=512kb`, `2mb`, `1gb`), and bytes (`JSON_CACHE=64KB`, `2MB`, `1GB`). May boost string serialization in excess of 22 GB/s.
|
|
546
546
|
|
|
547
547
|
**JSON_DEBUG** (default: 0) - Sets the debug level. May be within range `0-3`
|
|
548
548
|
|
|
549
549
|
**JSON_MODE** (default: SWAR) - Selects which mode should be used. Can be `NAIVE,SWAR,SIMD`. Note that `--enable simd` may be required.
|
|
550
550
|
|
|
551
|
-
**JSON_USE_FAST_PATH** (default:
|
|
551
|
+
**JSON_USE_FAST_PATH** (default: 1) - The transform emits the fast `__DESERIALIZE` implementation for generated structs by default. Set to `0`, `false`, `off`, or `no` to force slow-path-only output. See [FAST_PATH_DESERIALIZE.md](./FAST_PATH_DESERIALIZE.md) for the current support matrix, known gaps, and dedicated test command.
|
|
552
552
|
|
|
553
553
|
**JSON_WRITE** (default: "") - Select a series of files to output after transform and optimization passes have completed for easy inspection. Usage: `JSON_WRITE=.path-to-file-a.ts,./path-to-file-b.ts`
|
|
554
554
|
|
|
@@ -629,6 +629,7 @@ A few companies and open-source projects use json-as!
|
|
|
629
629
|
| [Klave](https://klave.com) | Privacy-first platform |
|
|
630
630
|
| [Bifrost](https://github.com/maximhq/bifrost) | Open source project by Maxim HQ |
|
|
631
631
|
| [Massa Labs](https://github.com/massalabs) | Massa blockchain tooling |
|
|
632
|
+
| [Seda Protocol](https://github.com/sedaprotocl) | Wasm-based blockchain VM |
|
|
632
633
|
|
|
633
634
|
## License
|
|
634
635
|
|
|
@@ -206,6 +206,26 @@ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): string {
|
|
|
206
206
|
srcStart += 2;
|
|
207
207
|
srcEnd -= 2;
|
|
208
208
|
const payloadStart = srcStart;
|
|
209
|
+
do {
|
|
210
|
+
const srcEnd16Fast = srcEnd - 16;
|
|
211
|
+
|
|
212
|
+
while (srcStart < srcEnd16Fast) {
|
|
213
|
+
const block = load<v128>(srcStart);
|
|
214
|
+
if (i16x8.bitmask(i16x8.eq(block, SPLAT_5C)) != 0) break;
|
|
215
|
+
srcStart += 16;
|
|
216
|
+
}
|
|
217
|
+
if (srcStart < srcEnd16Fast) break;
|
|
218
|
+
|
|
219
|
+
while (srcStart < srcEnd) {
|
|
220
|
+
if (load<u16>(srcStart) == BACK_SLASH) break;
|
|
221
|
+
srcStart += 2;
|
|
222
|
+
}
|
|
223
|
+
if (srcStart < srcEnd) break;
|
|
224
|
+
|
|
225
|
+
return copyStringFromSource_SIMD(payloadStart, srcEnd - payloadStart);
|
|
226
|
+
} while (false);
|
|
227
|
+
|
|
228
|
+
srcStart = payloadStart;
|
|
209
229
|
const srcEnd16 = srcEnd - 16;
|
|
210
230
|
|
|
211
231
|
while (srcStart < srcEnd16) {
|
|
@@ -170,6 +170,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
|
|
|
170
170
|
return out;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
|
|
173
174
|
@inline export function deserializeMapInto<T extends Map<any, any>>(srcStart: usize, srcEnd: usize, out: T): usize {
|
|
174
175
|
changetype<nonnull<T>>(out).clear();
|
|
175
176
|
|
|
@@ -209,6 +210,7 @@ export function deserializeMap<T extends Map<any, any>>(srcStart: usize, srcEnd:
|
|
|
209
210
|
throw new Error("Failed to parse JSON!");
|
|
210
211
|
}
|
|
211
212
|
|
|
213
|
+
|
|
212
214
|
@inline export function deserializeMapField<T extends Map<any, any>>(srcStart: usize, srcEnd: usize, dstObj: usize, dstOffset: usize = 0): usize {
|
|
213
215
|
let out = load<T>(dstObj, dstOffset);
|
|
214
216
|
if (!changetype<usize>(out)) {
|
|
@@ -72,6 +72,7 @@ export function deserializeStaticArray<T extends StaticArray<any>>(srcStart: usi
|
|
|
72
72
|
throw new Error("Could not parse static array of type " + nameof<T>() + "!");
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
+
|
|
75
76
|
@inline export function deserializeStaticArrayField<T extends StaticArray<any>>(srcStart: usize, srcEnd: usize, dstObj: usize, dstOffset: usize = 0): usize {
|
|
76
77
|
const valueEnd = scanValueEnd(srcStart, srcEnd);
|
|
77
78
|
if (!valueEnd) throw new Error("Failed to parse JSON!");
|
|
@@ -7,9 +7,11 @@ export function deserializeStruct<T>(srcStart: usize, srcEnd: usize, dst: usize)
|
|
|
7
7
|
if (isDefined(out.__DESERIALIZE_FAST)) {
|
|
8
8
|
// @ts-ignore: supplied by transform
|
|
9
9
|
out.__DESERIALIZE_FAST(srcStart, srcEnd, out);
|
|
10
|
-
} else {
|
|
10
|
+
} else if (isDefined(out.__DESERIALIZE_SLOW)) {
|
|
11
11
|
// @ts-ignore: supplied by transform
|
|
12
12
|
out.__DESERIALIZE_SLOW(srcStart, srcEnd, out);
|
|
13
|
+
} else {
|
|
14
|
+
throw new Error("Missing __DESERIALIZE_FAST/__DESERIALIZE_SLOW");
|
|
13
15
|
}
|
|
14
16
|
return out;
|
|
15
17
|
}
|
|
@@ -45,6 +45,7 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
|
45
45
|
throw new Error("Failed to parse JSON!");
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
+
|
|
48
49
|
@inline export function deserializeBooleanArrayField<T extends boolean[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
|
|
49
50
|
return deserializeBooleanArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
|
|
50
51
|
}
|
|
@@ -2,8 +2,8 @@ import { JSON } from "../../..";
|
|
|
2
2
|
import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
|
|
3
3
|
import { ensureArrayElementSlot, ensureArrayField, scanValueEnd } from "./shared";
|
|
4
4
|
|
|
5
|
-
@inline export function deserializeGenericArrayInto<T extends unknown[]>(srcStart: usize, srcEnd: usize, out: T): usize {
|
|
6
5
|
|
|
6
|
+
@inline export function deserializeGenericArrayInto<T extends unknown[]>(srcStart: usize, srcEnd: usize, out: T): usize {
|
|
7
7
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) throw new Error("Failed to parse JSON!");
|
|
8
8
|
srcStart += 2;
|
|
9
9
|
if (srcStart >= srcEnd) throw new Error("Failed to parse JSON!");
|
|
@@ -35,6 +35,7 @@ import { ensureArrayElementSlot, ensureArrayField, scanValueEnd } from "./shared
|
|
|
35
35
|
throw new Error("Failed to parse JSON!");
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
|
|
38
39
|
@inline export function deserializeGenericArrayField<T extends unknown[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
|
|
39
40
|
return deserializeGenericArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
|
|
40
41
|
}
|
|
@@ -459,6 +459,7 @@ export function deserializeIntegerArray_SWAR<T extends number[]>(srcStart: usize
|
|
|
459
459
|
throw new Error("Failed to parse JSON!");
|
|
460
460
|
}
|
|
461
461
|
|
|
462
|
+
|
|
462
463
|
@inline export function deserializeIntegerArrayField<T extends number[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
|
|
463
464
|
return deserializeIntegerArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
|
|
464
465
|
}
|
|
@@ -55,6 +55,7 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
|
55
55
|
throw new Error("Failed to parse JSON!");
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
|
|
58
59
|
@inline export function deserializeObjectArrayField<T extends unknown[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
|
|
59
60
|
return deserializeObjectArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
|
|
60
61
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA, QUOTE } from "../../../custom/chars";
|
|
2
|
+
import { isUnescapedQuote } from "../../../util";
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
@inline export function ensureArrayField<T extends Array<any>>(fieldPtr: usize): T {
|
|
@@ -10,6 +11,7 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
|
|
|
10
11
|
return out;
|
|
11
12
|
}
|
|
12
13
|
|
|
14
|
+
|
|
13
15
|
@inline export function ensureArrayFieldAt<T extends Array<any>>(dstObj: usize, dstOffset: usize): T {
|
|
14
16
|
let out = load<T>(dstObj, dstOffset);
|
|
15
17
|
if (!changetype<usize>(out)) {
|
|
@@ -57,7 +59,10 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
|
|
|
57
59
|
mask &= mask - 1;
|
|
58
60
|
const srcIdx = srcStart + laneIdx;
|
|
59
61
|
const char = load<u16>(srcIdx);
|
|
60
|
-
if (char == QUOTE)
|
|
62
|
+
if (char == QUOTE) {
|
|
63
|
+
if (isUnescapedQuote(srcIdx)) return srcIdx + 2;
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
61
66
|
if (char == BACK_SLASH) break;
|
|
62
67
|
} while (mask !== 0);
|
|
63
68
|
|
|
@@ -66,7 +71,7 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
|
|
|
66
71
|
|
|
67
72
|
while (srcStart < srcEnd) {
|
|
68
73
|
const char = load<u16>(srcStart);
|
|
69
|
-
if (char == QUOTE &&
|
|
74
|
+
if (char == QUOTE && isUnescapedQuote(srcStart)) return srcStart + 2;
|
|
70
75
|
srcStart += 2;
|
|
71
76
|
}
|
|
72
77
|
|
|
@@ -74,6 +79,16 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
|
|
|
74
79
|
}
|
|
75
80
|
|
|
76
81
|
|
|
82
|
+
@inline function scanQuotedValueEndScalar(srcStart: usize, srcEnd: usize): usize {
|
|
83
|
+
srcStart += 2;
|
|
84
|
+
while (srcStart < srcEnd) {
|
|
85
|
+
if (load<u16>(srcStart) == QUOTE && isUnescapedQuote(srcStart)) return srcStart + 2;
|
|
86
|
+
srcStart += 2;
|
|
87
|
+
}
|
|
88
|
+
return 0;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
77
92
|
@inline export function scanValueEnd(srcStart: usize, srcEnd: usize): usize {
|
|
78
93
|
if (srcStart >= srcEnd) return 0;
|
|
79
94
|
const first = load<u16>(srcStart);
|
|
@@ -86,7 +101,7 @@ import { BACK_SLASH, BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA
|
|
|
86
101
|
while (ptr < srcEnd) {
|
|
87
102
|
const code = load<u16>(ptr);
|
|
88
103
|
if (code == QUOTE) {
|
|
89
|
-
ptr = scanQuotedValueEnd_SWAR(ptr, srcEnd);
|
|
104
|
+
ptr = srcEnd - ptr < 256 ? scanQuotedValueEndScalar(ptr, srcEnd) : scanQuotedValueEnd_SWAR(ptr, srcEnd);
|
|
90
105
|
if (!ptr) return 0;
|
|
91
106
|
continue;
|
|
92
107
|
}
|
|
@@ -37,6 +37,7 @@ import { deserializeStringField_SWAR } from "../string";
|
|
|
37
37
|
throw new Error("Failed to parse JSON!");
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
|
|
40
41
|
@inline export function deserializeStringArrayField<T extends string[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
|
|
41
42
|
return deserializeStringArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
|
|
42
43
|
}
|
|
@@ -55,6 +55,7 @@ import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
|
55
55
|
throw new Error("Failed to parse JSON!");
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
|
|
58
59
|
@inline export function deserializeStructArrayField<T extends unknown[]>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
|
|
59
60
|
return deserializeStructArrayInto<T>(srcStart, srcEnd, ensureArrayField<T>(fieldPtr));
|
|
60
61
|
}
|
|
@@ -62,6 +62,7 @@ import { deserializeStructArrayInto } from "./array/struct";
|
|
|
62
62
|
}
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
|
|
65
66
|
@inline export function deserializeArrayInto_SWAR<T extends unknown[]>(srcStart: usize, srcEnd: usize, out: T): usize {
|
|
66
67
|
if (isString<valueof<T>>()) {
|
|
67
68
|
return deserializeStringArrayInto<T>(srcStart, srcEnd, out);
|
|
@@ -149,6 +149,27 @@ export function deserializeString_SWAR(srcStart: usize, srcEnd: usize): string {
|
|
|
149
149
|
srcStart += 2;
|
|
150
150
|
srcEnd -= 2;
|
|
151
151
|
const payloadStart = srcStart;
|
|
152
|
+
do {
|
|
153
|
+
const srcEnd16Fast = srcEnd - 16;
|
|
154
|
+
|
|
155
|
+
while (srcStart < srcEnd16Fast) {
|
|
156
|
+
const m0 = inline.always(backslash_mask_unsafe(load<u64>(srcStart)));
|
|
157
|
+
const m1 = inline.always(backslash_mask_unsafe(load<u64>(srcStart, 8)));
|
|
158
|
+
if ((m0 | m1) != 0) break;
|
|
159
|
+
srcStart += 16;
|
|
160
|
+
}
|
|
161
|
+
if (srcStart < srcEnd16Fast) break;
|
|
162
|
+
|
|
163
|
+
while (srcStart < srcEnd) {
|
|
164
|
+
if (load<u16>(srcStart) == BACK_SLASH) break;
|
|
165
|
+
srcStart += 2;
|
|
166
|
+
}
|
|
167
|
+
if (srcStart < srcEnd) break;
|
|
168
|
+
|
|
169
|
+
return copyStringFromSource(payloadStart, srcEnd - payloadStart);
|
|
170
|
+
} while (false);
|
|
171
|
+
|
|
172
|
+
srcStart = payloadStart;
|
|
152
173
|
const srcEnd8 = srcEnd - 8;
|
|
153
174
|
|
|
154
175
|
while (srcStart < srcEnd8) {
|
|
@@ -209,7 +230,6 @@ export function deserializeString_SWAR(srcStart: usize, srcEnd: usize): string {
|
|
|
209
230
|
memory.copy(stringPtr, srcStart, byteLength);
|
|
210
231
|
}
|
|
211
232
|
|
|
212
|
-
|
|
213
233
|
// @ts-expect-error: @inline is a valid decorator
|
|
214
234
|
@inline function deserializeEscapedStringContinuation_SWAR(lastPtr: usize, srcStart: usize, srcEnd: usize, dstFieldPtr: usize, outStart: usize): usize {
|
|
215
235
|
const srcEnd8 = srcEnd - 8;
|
|
@@ -630,7 +650,6 @@ export function deserializeStringField_SWAR<T extends string | null>(srcStart: u
|
|
|
630
650
|
return srcStart;
|
|
631
651
|
}
|
|
632
652
|
|
|
633
|
-
|
|
634
653
|
/**
|
|
635
654
|
* Computes a per-byte mask identifying ASCII backslash or quote bytes.
|
|
636
655
|
*
|
package/assembly/index.d.ts
CHANGED
package/assembly/index.ts
CHANGED
|
@@ -17,18 +17,7 @@ import { serializeSet } from "./serialize/index/set";
|
|
|
17
17
|
import { deserializeSet } from "./deserialize/index/set";
|
|
18
18
|
import { serializeStaticArray } from "./serialize/index/staticarray";
|
|
19
19
|
import { deserializeStaticArray } from "./deserialize/index/staticarray";
|
|
20
|
-
import {
|
|
21
|
-
BRACE_LEFT,
|
|
22
|
-
BRACE_RIGHT,
|
|
23
|
-
BRACKET_LEFT,
|
|
24
|
-
BRACKET_RIGHT,
|
|
25
|
-
COMMA,
|
|
26
|
-
NULL_WORD,
|
|
27
|
-
QUOTE,
|
|
28
|
-
NULL_WORD_U64,
|
|
29
|
-
TRUE_WORD_U64,
|
|
30
|
-
FALSE_WORD_U64,
|
|
31
|
-
} from "./custom/chars";
|
|
20
|
+
import { BRACE_LEFT, BRACE_RIGHT, BRACKET_LEFT, BRACKET_RIGHT, COMMA, NULL_WORD, QUOTE, NULL_WORD_U64, TRUE_WORD_U64, FALSE_WORD_U64 } from "./custom/chars";
|
|
32
21
|
import { itoa_buffered } from "util/number";
|
|
33
22
|
import { serializeBool } from "./serialize/index/bool";
|
|
34
23
|
import { serializeInteger } from "./serialize/index/integer";
|
|
@@ -36,7 +25,7 @@ import { serializeFloat, serializeFloat32, serializeFloat64 } from "./serialize/
|
|
|
36
25
|
import { dragonbox_f32_buffered, dragonbox_f64_buffered } from "./util/dragonbox";
|
|
37
26
|
import { serializeStruct } from "./serialize/index/struct";
|
|
38
27
|
import { ptrToStr } from "./util/ptrToStr";
|
|
39
|
-
import { atoi, bytes, scanStringEnd } from "./util";
|
|
28
|
+
import { atoi, bytes, isUnescapedQuote as utilIsUnescapedQuote, scanStringEnd } from "./util";
|
|
40
29
|
import { deserializeArbitrary } from "./deserialize/index/arbitrary";
|
|
41
30
|
import { serializeObject } from "./serialize/index/object";
|
|
42
31
|
import { deserializeObject } from "./deserialize/index/object";
|
|
@@ -244,14 +233,19 @@ export namespace JSON {
|
|
|
244
233
|
} else if (isDefined(type.__DESERIALIZE_SLOW) || isDefined(type.__DESERIALIZE_FAST)) {
|
|
245
234
|
const out = changetype<nonnull<T>>(__new(offsetof<nonnull<T>>(), idof<nonnull<T>>()));
|
|
246
235
|
// @ts-expect-error: Defined by transform
|
|
247
|
-
if (isDefined(type.__INITIALIZE)) out.__INITIALIZE();
|
|
248
|
-
// @ts-expect-error: Defined by transform
|
|
249
236
|
if (isDefined(type.__DESERIALIZE_FAST)) {
|
|
250
237
|
// @ts-expect-error: Defined by transform
|
|
251
|
-
out.__DESERIALIZE_FAST(dataPtr, dataPtr + dataSize, out);
|
|
238
|
+
if (out.__DESERIALIZE_FAST(dataPtr, dataPtr + dataSize, out) == dataPtr + dataSize) return out;
|
|
252
239
|
// @ts-expect-error: Defined by transform
|
|
253
|
-
}
|
|
254
|
-
|
|
240
|
+
}
|
|
241
|
+
if (isDefined(type.__INITIALIZE)) out.__INITIALIZE();
|
|
242
|
+
// @ts-expect-error: Defined by transform
|
|
243
|
+
if (isDefined(type.__DESERIALIZE_SLOW)) {
|
|
244
|
+
// @ts-expect-error: Defined by transform
|
|
245
|
+
out.__DESERIALIZE_SLOW(dataPtr, dataPtr + dataSize, out);
|
|
246
|
+
return out;
|
|
247
|
+
}
|
|
248
|
+
throw new Error(`No deserialize method defined for type ${type}`);
|
|
255
249
|
}
|
|
256
250
|
if (type instanceof StaticArray) {
|
|
257
251
|
// @ts-expect-error
|
|
@@ -956,12 +950,19 @@ export namespace JSON {
|
|
|
956
950
|
} else if (isDefined(type.__DESERIALIZE_SLOW) || isDefined(type.__DESERIALIZE_FAST)) {
|
|
957
951
|
const out = changetype<nonnull<T>>(dst || __new(offsetof<nonnull<T>>(), idof<nonnull<T>>()));
|
|
958
952
|
// @ts-expect-error: Defined by transform
|
|
959
|
-
if (isDefined(type.
|
|
953
|
+
if (isDefined(type.__DESERIALIZE_FAST)) {
|
|
954
|
+
// @ts-expect-error: Defined by transform
|
|
955
|
+
if (out.__DESERIALIZE_FAST(srcStart, srcEnd, out) == srcEnd) return out;
|
|
956
|
+
}
|
|
960
957
|
// @ts-expect-error: Defined by transform
|
|
961
|
-
if (isDefined(type.
|
|
958
|
+
if (isDefined(type.__INITIALIZE)) out.__INITIALIZE();
|
|
962
959
|
// @ts-expect-error: Defined by transform
|
|
963
|
-
|
|
964
|
-
|
|
960
|
+
if (isDefined(type.__DESERIALIZE_SLOW)) {
|
|
961
|
+
// @ts-expect-error: Defined by transform
|
|
962
|
+
out.__DESERIALIZE_SLOW(srcStart, srcEnd, out);
|
|
963
|
+
return out;
|
|
964
|
+
}
|
|
965
|
+
throw new Error(`No deserialize method defined for type ${type}`);
|
|
965
966
|
}
|
|
966
967
|
if (type instanceof StaticArray) {
|
|
967
968
|
// @ts-expect-error: type
|
|
@@ -1025,6 +1026,10 @@ export namespace JSON {
|
|
|
1025
1026
|
return code == 0x20 || code - 9 <= 4;
|
|
1026
1027
|
}
|
|
1027
1028
|
// @ts-expect-error: decorator
|
|
1029
|
+
@inline export function isUnescapedQuote(ptr: usize): boolean {
|
|
1030
|
+
return utilIsUnescapedQuote(ptr);
|
|
1031
|
+
}
|
|
1032
|
+
// @ts-expect-error: decorator
|
|
1028
1033
|
@inline export function scanValueEnd(srcStart: usize, srcEnd: usize): usize {
|
|
1029
1034
|
if (srcStart >= srcEnd) return 0;
|
|
1030
1035
|
let ptr = srcStart;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { OBJECT, TOTAL_OVERHEAD } from "rt/common";
|
|
2
|
-
import { bs
|
|
2
|
+
import { bs } from "../../../lib/as-bs";
|
|
3
3
|
import { BACK_SLASH } from "../../custom/chars";
|
|
4
4
|
import { SERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
|
|
5
5
|
import { u16_to_hex4_swar } from "../../util/swar";
|
|
@@ -21,20 +21,49 @@ import { u16_to_hex4_swar } from "../../util/swar";
|
|
|
21
21
|
*/
|
|
22
22
|
export function serializeString_SIMD(src: string): void {
|
|
23
23
|
let srcStart = changetype<usize>(src);
|
|
24
|
-
|
|
25
|
-
// check cache
|
|
26
|
-
const e = unchecked(sc.entries[i32((srcStart >> 4) & sc.CACHE_MASK)]);
|
|
27
|
-
if (e.key == srcStart) {
|
|
28
|
-
// bs.offset += e.len;
|
|
29
|
-
// bs.stackSize += e.len;
|
|
30
|
-
bs.cacheOutput = e.ptr;
|
|
31
|
-
bs.cacheOutputLen = e.len;
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
}
|
|
24
|
+
const srcInitial = srcStart;
|
|
35
25
|
|
|
36
26
|
const srcSize = changetype<OBJECT>(srcStart - TOTAL_OVERHEAD).rtSize;
|
|
37
27
|
const srcEnd = srcStart + srcSize;
|
|
28
|
+
do {
|
|
29
|
+
const srcEnd16Fast = srcEnd - 16;
|
|
30
|
+
bs.proposeSize(srcSize + 4);
|
|
31
|
+
|
|
32
|
+
const dstStart = bs.offset;
|
|
33
|
+
let dst = dstStart + 2;
|
|
34
|
+
|
|
35
|
+
while (srcStart < srcEnd16Fast) {
|
|
36
|
+
const block = load<v128>(srcStart);
|
|
37
|
+
const eq22 = i16x8.eq(block, SPLAT_0022);
|
|
38
|
+
const eq5C = i16x8.eq(block, SPLAT_005C);
|
|
39
|
+
const lt20 = i16x8.lt_u(block, SPLAT_0020);
|
|
40
|
+
const gteD8 = i8x16.gt_u(block, SPLAT_FFD8);
|
|
41
|
+
|
|
42
|
+
const mask = i8x16.bitmask(v128.or(eq22, v128.or(eq5C, v128.or(lt20, gteD8))));
|
|
43
|
+
if (mask != 0) break;
|
|
44
|
+
|
|
45
|
+
store<v128>(dst, block);
|
|
46
|
+
srcStart += 16;
|
|
47
|
+
dst += 16;
|
|
48
|
+
}
|
|
49
|
+
if (srcStart < srcEnd16Fast) break;
|
|
50
|
+
|
|
51
|
+
while (srcStart <= srcEnd - 2) {
|
|
52
|
+
const code = load<u16>(srcStart);
|
|
53
|
+
if (code > 0x7f || code == BACK_SLASH || code == 34 || code < 32) break;
|
|
54
|
+
store<u16>(dst, code);
|
|
55
|
+
srcStart += 2;
|
|
56
|
+
dst += 2;
|
|
57
|
+
}
|
|
58
|
+
if (srcStart <= srcEnd - 2) break;
|
|
59
|
+
|
|
60
|
+
store<u16>(dstStart, 34); // "
|
|
61
|
+
store<u16>(dst, 34); // "
|
|
62
|
+
bs.offset = dst + 2;
|
|
63
|
+
return;
|
|
64
|
+
} while (false);
|
|
65
|
+
|
|
66
|
+
srcStart = srcInitial;
|
|
38
67
|
const srcEnd16 = srcEnd - 16;
|
|
39
68
|
|
|
40
69
|
bs.proposeSize(srcSize + 4);
|
|
@@ -43,7 +72,6 @@ export function serializeString_SIMD(src: string): void {
|
|
|
43
72
|
|
|
44
73
|
while (srcStart < srcEnd16) {
|
|
45
74
|
const block = load<v128>(srcStart);
|
|
46
|
-
store<v128>(bs.offset, block);
|
|
47
75
|
|
|
48
76
|
const eq22 = i16x8.eq(block, SPLAT_0022);
|
|
49
77
|
const eq5C = i16x8.eq(block, SPLAT_005C);
|
|
@@ -58,12 +86,13 @@ export function serializeString_SIMD(src: string): void {
|
|
|
58
86
|
let mask = i8x16.bitmask(v128.or(eq22, v128.or(eq5C, v128.or(lt20, gteD8))));
|
|
59
87
|
|
|
60
88
|
if (mask == 0) {
|
|
89
|
+
store<v128>(bs.offset, block);
|
|
61
90
|
bs.offset += 16;
|
|
62
91
|
srcStart += 16;
|
|
63
92
|
continue;
|
|
64
93
|
}
|
|
65
94
|
|
|
66
|
-
bs.
|
|
95
|
+
store<v128>(bs.offset, block);
|
|
67
96
|
|
|
68
97
|
do {
|
|
69
98
|
const laneIdx = ctz(mask);
|
|
@@ -173,8 +202,6 @@ export function serializeString_SIMD(src: string): void {
|
|
|
173
202
|
|
|
174
203
|
store<u16>(bs.offset, 34); // "
|
|
175
204
|
bs.offset += 2;
|
|
176
|
-
|
|
177
|
-
if (isDefined(JSON_CACHE)) sc.insertCached(changetype<usize>(src), srcStart, srcSize);
|
|
178
205
|
}
|
|
179
206
|
|
|
180
207
|
// @ts-expect-error: @inline is a valid decorator
|
|
@@ -1,13 +1,53 @@
|
|
|
1
1
|
import { bs } from "../../../lib/as-bs";
|
|
2
2
|
import { COMMA, BRACKET_RIGHT, BRACKET_LEFT } from "../../custom/chars";
|
|
3
3
|
import { JSON } from "../..";
|
|
4
|
-
import {
|
|
4
|
+
import { serializeBoolUnsafe } from "./bool";
|
|
5
|
+
import { serializeFloat32Unsafe, serializeFloat64Unsafe } from "./float";
|
|
6
|
+
import { serializeIntegerUnsafe } from "./integer";
|
|
7
|
+
import { serializeString } from "../index/string";
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@inline
|
|
11
|
+
function maxIntegerBytes<T extends number>(): u32 {
|
|
12
|
+
if (sizeof<T>() == 1) return isSigned<T>() ? 8 : 6;
|
|
13
|
+
if (sizeof<T>() == 2) return isSigned<T>() ? 12 : 10;
|
|
14
|
+
if (sizeof<T>() == 4) return isSigned<T>() ? 22 : 20;
|
|
15
|
+
return isSigned<T>() ? 42 : 40;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@inline
|
|
20
|
+
function reservePrimitiveArray<T>(len: i32): void {
|
|
21
|
+
if (len <= 0) return;
|
|
22
|
+
if (isBoolean<T>()) {
|
|
23
|
+
bs.proposeSize(4 + <u32>len * 12);
|
|
24
|
+
} else if (isInteger<T>()) {
|
|
25
|
+
bs.proposeSize(4 + <u32>len * (maxIntegerBytes<T>() + 2));
|
|
26
|
+
} else if (isFloat<T>()) {
|
|
27
|
+
bs.proposeSize(4 + <u32>len * (sizeof<T>() == 4 ? 34 : 66));
|
|
28
|
+
} else {
|
|
29
|
+
bs.proposeSize(4 + <u32>(len - 1) * 2);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
5
33
|
|
|
6
34
|
@inline
|
|
7
35
|
function serializeArrayElement<T>(value: T): void {
|
|
36
|
+
if (isString<T>()) {
|
|
37
|
+
serializeString(value as string);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
if (isBoolean<T>()) {
|
|
41
|
+
serializeBoolUnsafe(<bool>value);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
if (isInteger<T>()) {
|
|
45
|
+
serializeIntegerUnsafe<T>(value);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
8
48
|
if (isFloat<T>()) {
|
|
9
|
-
if (sizeof<T>() == 4)
|
|
10
|
-
else
|
|
49
|
+
if (sizeof<T>() == 4) serializeFloat32Unsafe(<f32>value);
|
|
50
|
+
else serializeFloat64Unsafe(<f64>value);
|
|
11
51
|
return;
|
|
12
52
|
}
|
|
13
53
|
JSON.__serialize<T>(value);
|
|
@@ -23,9 +63,11 @@ export function serializeArray<T extends any[]>(src: T): void {
|
|
|
23
63
|
bs.offset += 4;
|
|
24
64
|
return;
|
|
25
65
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
66
|
+
if (isBoolean<valueof<T>>() || isInteger<valueof<T>>() || isFloat<valueof<T>>() || isString<valueof<T>>()) {
|
|
67
|
+
reservePrimitiveArray<valueof<T>>(len);
|
|
68
|
+
} else {
|
|
69
|
+
bs.proposeSize(4 + <u32>(len - 1) * 2);
|
|
70
|
+
}
|
|
29
71
|
|
|
30
72
|
store<u16>(bs.offset, BRACKET_LEFT);
|
|
31
73
|
bs.offset += 2;
|
|
@@ -4,6 +4,18 @@ import { bs } from "../../../lib/as-bs";
|
|
|
4
4
|
* @param data data to serialize
|
|
5
5
|
* @returns void
|
|
6
6
|
*/
|
|
7
|
+
@inline
|
|
8
|
+
export function serializeBoolUnsafe(data: bool): void {
|
|
9
|
+
if (data === true) {
|
|
10
|
+
store<u64>(bs.offset, 28429475166421108);
|
|
11
|
+
bs.offset += 8;
|
|
12
|
+
} else {
|
|
13
|
+
store<u64>(bs.offset, 32370086184550502);
|
|
14
|
+
store<u64>(bs.offset, 101, 8);
|
|
15
|
+
bs.offset += 10;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
7
19
|
export function serializeBool(data: bool): void {
|
|
8
20
|
if (data === true) {
|
|
9
21
|
bs.proposeSize(8);
|
|
@@ -1,6 +1,21 @@
|
|
|
1
1
|
import { bs } from "../../../lib/as-bs";
|
|
2
2
|
import { dragonbox_f32_buffered, dragonbox_f64_buffered } from "../../util/dragonbox";
|
|
3
3
|
|
|
4
|
+
|
|
5
|
+
@inline
|
|
6
|
+
export function serializeFloat32Unsafe(data: f32): void {
|
|
7
|
+
const size = dragonbox_f32_buffered(bs.offset, data) << 1;
|
|
8
|
+
bs.offset += size;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@inline
|
|
13
|
+
export function serializeFloat64Unsafe(data: f64): void {
|
|
14
|
+
const size = dragonbox_f64_buffered(bs.offset, data) << 1;
|
|
15
|
+
bs.offset += size;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
|
|
4
19
|
@inline
|
|
5
20
|
export function serializeFloat32(data: f32): void {
|
|
6
21
|
bs.ensureSize(64);
|
|
@@ -9,6 +24,7 @@ export function serializeFloat32(data: f32): void {
|
|
|
9
24
|
bs.offset += size;
|
|
10
25
|
}
|
|
11
26
|
|
|
27
|
+
|
|
12
28
|
@inline
|
|
13
29
|
export function serializeFloat64(data: f64): void {
|
|
14
30
|
bs.ensureSize(64);
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { bs } from "../../../lib/as-bs";
|
|
2
2
|
import { itoa_buffered } from "util/number";
|
|
3
3
|
|
|
4
|
+
@inline
|
|
5
|
+
export function serializeIntegerUnsafe<T extends number>(data: T): void {
|
|
6
|
+
const bytesWritten = itoa_buffered(bs.offset, data) << 1;
|
|
7
|
+
bs.offset += bytesWritten;
|
|
8
|
+
}
|
|
9
|
+
|
|
4
10
|
// @ts-ignore: inline
|
|
5
11
|
@inline export function serializeInteger<T extends number>(data: T): void {
|
|
6
12
|
bs.ensureSize(sizeof<T>() << 3);
|