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
|
@@ -1,21 +1,77 @@
|
|
|
1
1
|
import { JSON } from "../..";
|
|
2
2
|
import { bs } from "../../../lib/as-bs";
|
|
3
|
-
import {
|
|
3
|
+
import { bytes } from "../../util/bytes";
|
|
4
|
+
import { QUOTE } from "../../custom/chars";
|
|
4
5
|
import { serializeBool } from "./bool";
|
|
5
6
|
import { serializeFloat32, serializeFloat64 } from "./float";
|
|
6
7
|
import { serializeInteger } from "./integer";
|
|
8
|
+
import { serializeJsonArray } from "./jsonarray";
|
|
7
9
|
import { serializeMap } from "./map";
|
|
8
10
|
import { serializeObject } from "./object";
|
|
11
|
+
import { serializeRaw } from "../naive/raw";
|
|
9
12
|
import { serializeString } from "./string";
|
|
10
13
|
import { serializeDynamic } from "./typedarray";
|
|
11
14
|
|
|
15
|
+
// True if any code unit would need JSON-escaping on serialize: a quote,
|
|
16
|
+
// backslash, control char (< 0x20), or a surrogate (handled conservatively -
|
|
17
|
+
// even valid pairs route to the full path, since they can't be bulk-copied as a
|
|
18
|
+
// plain run). A clean string is none of these, so it emits as a verbatim memcpy.
|
|
19
|
+
// @ts-ignore: decorator
|
|
20
|
+
@inline function stringNeedsEscape(src: string): bool {
|
|
21
|
+
let ptr = changetype<usize>(src);
|
|
22
|
+
const end = ptr + <usize>bytes(src);
|
|
23
|
+
while (ptr < end) {
|
|
24
|
+
const code = load<u16>(ptr);
|
|
25
|
+
if (
|
|
26
|
+
code == 0x22 ||
|
|
27
|
+
code == 0x5c ||
|
|
28
|
+
code < 0x20 ||
|
|
29
|
+
(code >= 0xd800 && code <= 0xdfff)
|
|
30
|
+
)
|
|
31
|
+
return true;
|
|
32
|
+
ptr += 2;
|
|
33
|
+
}
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Fast path for a string already known to need no escaping: quote + a single
|
|
38
|
+
// bulk copy of the UTF-16 bytes + quote. Skips the per-char escape scan that
|
|
39
|
+
// serializeString would otherwise do (and its trailing second pass).
|
|
40
|
+
function serializeStringClean(src: string): void {
|
|
41
|
+
const size = <usize>bytes(src);
|
|
42
|
+
bs.proposeSize(size + 4);
|
|
43
|
+
store<u16>(bs.offset, QUOTE);
|
|
44
|
+
bs.offset += 2;
|
|
45
|
+
memory.copy(bs.offset, changetype<usize>(src), size);
|
|
46
|
+
bs.offset += size;
|
|
47
|
+
store<u16>(bs.offset, QUOTE);
|
|
48
|
+
bs.offset += 2;
|
|
49
|
+
}
|
|
50
|
+
|
|
12
51
|
export function serializeArbitrary(src: JSON.Value): void {
|
|
52
|
+
// Verbatim passthrough: an untouched (still-deferred) value emits its original
|
|
53
|
+
// source bytes directly - no materialization, no re-encoding. Peek the slice
|
|
54
|
+
// without reading `src.type` (which would force materialization).
|
|
55
|
+
const lz = src.__lazySlice();
|
|
56
|
+
if (lz != 0) {
|
|
57
|
+
const start = <usize>(lz >>> 32);
|
|
58
|
+
const size = <usize>(<u32>lz) - start;
|
|
59
|
+
bs.proposeSize(size);
|
|
60
|
+
memory.copy(bs.offset, start, size);
|
|
61
|
+
bs.offset += size;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
13
64
|
switch (src.type) {
|
|
14
65
|
case JSON.Types.Null:
|
|
15
66
|
bs.proposeSize(8);
|
|
16
67
|
store<u64>(bs.offset, 30399761348886638);
|
|
17
68
|
bs.offset += 8;
|
|
18
69
|
break;
|
|
70
|
+
case JSON.Types.Raw:
|
|
71
|
+
// A materialized JSON.Raw value (e.g. set into a JSON.Obj/Arr) emits its
|
|
72
|
+
// pre-formatted bytes as-is - without this it fell into the struct default.
|
|
73
|
+
serializeRaw(src.get<JSON.Raw>());
|
|
74
|
+
break;
|
|
19
75
|
case JSON.Types.U8:
|
|
20
76
|
serializeInteger<u8>(src.get<u8>());
|
|
21
77
|
break;
|
|
@@ -46,14 +102,24 @@ export function serializeArbitrary(src: JSON.Value): void {
|
|
|
46
102
|
case JSON.Types.F64:
|
|
47
103
|
serializeFloat64(src.get<f64>());
|
|
48
104
|
break;
|
|
49
|
-
case JSON.Types.String:
|
|
50
|
-
|
|
105
|
+
case JSON.Types.String: {
|
|
106
|
+
const str = src.get<string>();
|
|
107
|
+
// Reuse the cached escape class; classify once on first serialize. Clean
|
|
108
|
+
// strings (the common case) then emit as a single memcpy on every reuse.
|
|
109
|
+
let cls = src.__strClass();
|
|
110
|
+
if (cls == 0) {
|
|
111
|
+
cls = stringNeedsEscape(str) ? 2 : 1;
|
|
112
|
+
src.__setStrClass(cls);
|
|
113
|
+
}
|
|
114
|
+
if (cls == 1) serializeStringClean(str);
|
|
115
|
+
else serializeString(str);
|
|
51
116
|
break;
|
|
117
|
+
}
|
|
52
118
|
case JSON.Types.Bool:
|
|
53
119
|
serializeBool(src.get<bool>());
|
|
54
120
|
break;
|
|
55
121
|
case JSON.Types.Array:
|
|
56
|
-
|
|
122
|
+
serializeJsonArray(src.get<JSON.Arr>());
|
|
57
123
|
break;
|
|
58
124
|
case JSON.Types.Object:
|
|
59
125
|
serializeObject(src.get<JSON.Obj>());
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { bs } from "../../../lib/as-bs";
|
|
2
|
+
import { JSON } from "../..";
|
|
3
|
+
import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../custom/chars";
|
|
4
|
+
import { serializeArbitrary } from "./arbitrary";
|
|
5
|
+
|
|
6
|
+
export function serializeJsonArray(src: JSON.Arr): void {
|
|
7
|
+
const n = src.length;
|
|
8
|
+
|
|
9
|
+
if (n == 0) {
|
|
10
|
+
bs.proposeSize(4);
|
|
11
|
+
store<u32>(bs.offset, 6094939); // "[]"
|
|
12
|
+
bs.offset += 4;
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const vals = src._vals;
|
|
17
|
+
// Source base + upper bound for resolving a deferred slot's value range.
|
|
18
|
+
const srcBase = changetype<usize>(src._src);
|
|
19
|
+
const srcEnd = srcBase + ((<usize>src._src.length) << 1);
|
|
20
|
+
|
|
21
|
+
bs.proposeSize(4 + <u32>(n - 1) * 2);
|
|
22
|
+
store<u16>(bs.offset, BRACKET_LEFT);
|
|
23
|
+
bs.offset += 2;
|
|
24
|
+
|
|
25
|
+
// A still-deferred slot is copied out verbatim from the source; an
|
|
26
|
+
// eager/materialized slot is serialized from its boxed bits.
|
|
27
|
+
for (let i = 0; i < n; i++) {
|
|
28
|
+
if (i != 0) {
|
|
29
|
+
store<u16>(bs.offset, COMMA);
|
|
30
|
+
bs.offset += 2;
|
|
31
|
+
}
|
|
32
|
+
const slot = unchecked(vals[i]);
|
|
33
|
+
if (JSON.Value.slotIsLazy(slot)) {
|
|
34
|
+
const start = JSON.Value.slotPtr(slot, srcBase);
|
|
35
|
+
const end = JSON.Value.slotEnd(slot, srcBase, srcEnd);
|
|
36
|
+
const size = end - start;
|
|
37
|
+
bs.proposeSize(size);
|
|
38
|
+
memory.copy(bs.offset, start, size);
|
|
39
|
+
bs.offset += size;
|
|
40
|
+
} else {
|
|
41
|
+
const v = JSON.Value.fromBits(slot);
|
|
42
|
+
serializeArbitrary(v);
|
|
43
|
+
// Persist a cached String escape-class back into the slot (see object.ts).
|
|
44
|
+
const nb = v.__bits();
|
|
45
|
+
if (nb != slot) unchecked((vals[i] = nb));
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
store<u16>(bs.offset, BRACKET_RIGHT);
|
|
50
|
+
bs.offset += 2;
|
|
51
|
+
}
|
|
@@ -17,13 +17,18 @@ export function serializeObject(src: JSON.Obj): void {
|
|
|
17
17
|
const vals = src._vals;
|
|
18
18
|
const kbuf = changetype<usize>(src._kbuf);
|
|
19
19
|
const kused = src._kused;
|
|
20
|
+
// Source base + upper bound for resolving a deferred slot's value range.
|
|
21
|
+
const srcBase = changetype<usize>(src._src);
|
|
22
|
+
const srcEnd = srcBase + ((<usize>src._src.length) << 1);
|
|
20
23
|
|
|
21
24
|
bs.proposeSize(4 + <u32>(srcSize - 1) * 2 + <u32>srcSize * 2);
|
|
22
25
|
store<u16>(bs.offset, BRACE_LEFT);
|
|
23
26
|
bs.offset += 2;
|
|
24
27
|
|
|
25
|
-
// Walk the length-prefixed key buffer in lockstep with the
|
|
26
|
-
// each key straight from its slice (no per-key string
|
|
28
|
+
// Walk the length-prefixed key buffer in lockstep with the value slots,
|
|
29
|
+
// emitting each key straight from its slice (no per-key string
|
|
30
|
+
// materialization). A still-deferred slot is copied out verbatim from the
|
|
31
|
+
// source; an eager/materialized slot is serialized from its boxed bits.
|
|
27
32
|
let pos = 0;
|
|
28
33
|
let i = 0;
|
|
29
34
|
while (pos < kused) {
|
|
@@ -35,7 +40,24 @@ export function serializeObject(src: JSON.Obj): void {
|
|
|
35
40
|
serializeStringRange(kbuf + ((<usize>(pos + 1)) << 1), (<usize>len) << 1);
|
|
36
41
|
store<u16>(bs.offset, COLON);
|
|
37
42
|
bs.offset += 2;
|
|
38
|
-
|
|
43
|
+
|
|
44
|
+
const slot = unchecked(vals[i]);
|
|
45
|
+
if (JSON.Value.slotIsLazy(slot)) {
|
|
46
|
+
const start = JSON.Value.slotPtr(slot, srcBase);
|
|
47
|
+
const end = JSON.Value.slotEnd(slot, srcBase, srcEnd);
|
|
48
|
+
const size = end - start;
|
|
49
|
+
bs.proposeSize(size);
|
|
50
|
+
memory.copy(bs.offset, start, size);
|
|
51
|
+
bs.offset += size;
|
|
52
|
+
} else {
|
|
53
|
+
const v = JSON.Value.fromBits(slot);
|
|
54
|
+
serializeArbitrary(v);
|
|
55
|
+
// Persist any escape-class the serializer cached on a materialized String
|
|
56
|
+
// slot back into the flat slot, so re-serializing this object reuses it
|
|
57
|
+
// (the memcpy fast path) instead of re-scanning. No-op for other types.
|
|
58
|
+
const nb = v.__bits();
|
|
59
|
+
if (nb != slot) unchecked((vals[i] = nb));
|
|
60
|
+
}
|
|
39
61
|
pos += 1 + len;
|
|
40
62
|
i++;
|
|
41
63
|
}
|
|
@@ -3,8 +3,7 @@ import { serializeString_NAIVE } from "../naive/string";
|
|
|
3
3
|
import { serializeString_SIMD } from "../simd/string";
|
|
4
4
|
import { serializeString_SWAR } from "../swar/string";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
@inline export function serializeString(src: string): void {
|
|
6
|
+
export function serializeString(src: string): void {
|
|
8
7
|
if (JSON_MODE == JSONMode.SIMD) {
|
|
9
8
|
serializeString_SIMD(src);
|
|
10
9
|
} else if (JSON_MODE == JSONMode.NAIVE) {
|
|
@@ -9,8 +9,7 @@ import {
|
|
|
9
9
|
serializeTypedArray,
|
|
10
10
|
} from "../naive/typedarray";
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
@inline export function serializeDynamic(type: u16, data: usize): void {
|
|
12
|
+
export function serializeDynamic(type: u16, data: usize): void {
|
|
14
13
|
if (type == JSON.Types.ArrayBuffer) {
|
|
15
14
|
serializeArrayBufferUnsafe(
|
|
16
15
|
data,
|
|
@@ -5,13 +5,8 @@ import { serializeBoolUnsafe } from "./bool";
|
|
|
5
5
|
import { serializeFloat32Unsafe, serializeFloat64Unsafe } from "./float";
|
|
6
6
|
import { serializeIntegerUnsafe } from "./integer";
|
|
7
7
|
import { serializeString } from "../index/string";
|
|
8
|
-
import {
|
|
9
|
-
dragonbox_f32_buffered,
|
|
10
|
-
dragonbox_f64_buffered,
|
|
11
|
-
} from "../../util/dragonbox";
|
|
8
|
+
import { dtoa_buffered, ftoa_buffered } from "xjb-as";
|
|
12
9
|
|
|
13
|
-
|
|
14
|
-
@inline
|
|
15
10
|
function maxIntegerBytes<T extends number>(): u32 {
|
|
16
11
|
if (sizeof<T>() == 1) return isSigned<T>() ? 8 : 6;
|
|
17
12
|
if (sizeof<T>() == 2) return isSigned<T>() ? 12 : 10;
|
|
@@ -19,8 +14,6 @@ function maxIntegerBytes<T extends number>(): u32 {
|
|
|
19
14
|
return isSigned<T>() ? 42 : 40;
|
|
20
15
|
}
|
|
21
16
|
|
|
22
|
-
|
|
23
|
-
@inline
|
|
24
17
|
function reservePrimitiveArray<T>(len: i32): void {
|
|
25
18
|
if (len <= 0) return;
|
|
26
19
|
if (isBoolean<T>()) {
|
|
@@ -34,8 +27,6 @@ function reservePrimitiveArray<T>(len: i32): void {
|
|
|
34
27
|
}
|
|
35
28
|
}
|
|
36
29
|
|
|
37
|
-
|
|
38
|
-
@inline
|
|
39
30
|
function serializeArrayElement<T>(value: T): void {
|
|
40
31
|
if (isString<T>()) {
|
|
41
32
|
serializeString(value as string);
|
|
@@ -78,15 +69,13 @@ function serializeArrayElement<T>(value: T): void {
|
|
|
78
69
|
// comma-store + advance per element that the generic `serializeArray` does.
|
|
79
70
|
|
|
80
71
|
// `"true,"` packed UTF-16: `t,r,u,e,,` lanes 0..4 = bytes 0..9.
|
|
81
|
-
|
|
72
|
+
const TRUE_COMMA_LO: u64 = 0x0065_0075_0072_0074;
|
|
82
73
|
|
|
83
|
-
|
|
84
|
-
@inline const TRUE_COMMA_HI: u16 = 0x002c;
|
|
74
|
+
const TRUE_COMMA_HI: u16 = 0x002c;
|
|
85
75
|
// `"false,"` packed UTF-16: `f,a,l,s,e,,` lanes 0..5 = bytes 0..11.
|
|
86
|
-
|
|
87
|
-
|
|
76
|
+
const FALSE_COMMA_LO: u64 = 0x0073_006c_0061_0066;
|
|
88
77
|
|
|
89
|
-
|
|
78
|
+
const FALSE_COMMA_HI: u32 = 0x002c_0065;
|
|
90
79
|
|
|
91
80
|
function serializeBoolArrayFast(src: bool[]): void {
|
|
92
81
|
const len = src.length;
|
|
@@ -152,8 +141,7 @@ function initU8Lut(): void {
|
|
|
152
141
|
_u8LutInited = true;
|
|
153
142
|
}
|
|
154
143
|
|
|
155
|
-
|
|
156
|
-
@inline function ensureU8Lut(): void {
|
|
144
|
+
function ensureU8Lut(): void {
|
|
157
145
|
if (!_u8LutInited) initU8Lut();
|
|
158
146
|
}
|
|
159
147
|
|
|
@@ -181,20 +169,20 @@ function serializeU8ArrayFast(src: u8[]): void {
|
|
|
181
169
|
store<u16>(bs.offset - 2, BRACKET_RIGHT);
|
|
182
170
|
}
|
|
183
171
|
|
|
184
|
-
// Specialized float-array serializer:
|
|
172
|
+
// Specialized float-array serializer: xjb writer + trailing comma in a
|
|
185
173
|
// uniform per-iteration body, then overwrite the final comma with `]`. The
|
|
186
174
|
// generic dispatcher splits the loop into "N-1 elements with comma, then
|
|
187
|
-
// last element without, then `]`"
|
|
175
|
+
// last element without, then `]`" - the branch on each `i < end` check
|
|
188
176
|
// stalls the loop's tight bs.offset advance pattern. This variant runs the
|
|
189
|
-
// same number of stores per iteration (
|
|
177
|
+
// same number of stores per iteration (xjb output + COMMA), but the
|
|
190
178
|
// uniform loop body inlines better and the trailing `]` is a single fixed
|
|
191
179
|
// overwrite outside the loop.
|
|
192
180
|
function serializeF64ArrayFast(src: f64[]): void {
|
|
193
181
|
const len = src.length;
|
|
194
|
-
// Worst case per element:
|
|
195
|
-
//
|
|
196
|
-
//
|
|
197
|
-
bs.proposeSize(4 + <u32>len *
|
|
182
|
+
// Worst case per element: a 21-char fixed integer (e.g. 1e20) or a ~24-char
|
|
183
|
+
// exponent form, + comma = ~52 bytes; plus the writer's SIMD stores can
|
|
184
|
+
// overshoot the logical end by up to 16 bytes. 80 covers both comfortably.
|
|
185
|
+
bs.proposeSize(4 + <u32>len * 80);
|
|
198
186
|
store<u16>(bs.offset, BRACKET_LEFT);
|
|
199
187
|
bs.offset += 2;
|
|
200
188
|
if (len == 0) {
|
|
@@ -210,9 +198,9 @@ function serializeF64ArrayFast(src: f64[]): void {
|
|
|
210
198
|
let offset = bs.offset;
|
|
211
199
|
for (let i: i32 = 0; i < len; i++) {
|
|
212
200
|
const v = load<f64>(dataStart + ((<usize>i) << 3));
|
|
213
|
-
|
|
214
|
-
store<u16>(offset
|
|
215
|
-
offset +=
|
|
201
|
+
offset += dtoa_buffered(offset, v) << 1;
|
|
202
|
+
store<u16>(offset, COMMA);
|
|
203
|
+
offset += 2;
|
|
216
204
|
}
|
|
217
205
|
// Overwrite the final trailing comma with `]`.
|
|
218
206
|
store<u16>(offset - 2, BRACKET_RIGHT);
|
|
@@ -221,9 +209,10 @@ function serializeF64ArrayFast(src: f64[]): void {
|
|
|
221
209
|
|
|
222
210
|
function serializeF32ArrayFast(src: f32[]): void {
|
|
223
211
|
const len = src.length;
|
|
224
|
-
// Worst case for f32 is
|
|
225
|
-
//
|
|
226
|
-
|
|
212
|
+
// Worst case for f32 is a 21-char fixed integer (e.g. 1e20 rounds to a
|
|
213
|
+
// 21-digit value), + comma = ~44 bytes, plus up to 16 bytes of SIMD store
|
|
214
|
+
// overshoot. 64 covers it.
|
|
215
|
+
bs.proposeSize(4 + <u32>len * 64);
|
|
227
216
|
store<u16>(bs.offset, BRACKET_LEFT);
|
|
228
217
|
bs.offset += 2;
|
|
229
218
|
if (len == 0) {
|
|
@@ -236,9 +225,9 @@ function serializeF32ArrayFast(src: f32[]): void {
|
|
|
236
225
|
let offset = bs.offset;
|
|
237
226
|
for (let i: i32 = 0; i < len; i++) {
|
|
238
227
|
const v = load<f32>(dataStart + ((<usize>i) << 2));
|
|
239
|
-
|
|
240
|
-
store<u16>(offset
|
|
241
|
-
offset +=
|
|
228
|
+
offset += ftoa_buffered(offset, v) << 1;
|
|
229
|
+
store<u16>(offset, COMMA);
|
|
230
|
+
offset += 2;
|
|
242
231
|
}
|
|
243
232
|
store<u16>(offset - 2, BRACKET_RIGHT);
|
|
244
233
|
bs.offset = offset;
|
|
@@ -1,43 +1,34 @@
|
|
|
1
1
|
import { bs } from "../../../lib/as-bs";
|
|
2
|
-
import {
|
|
3
|
-
dragonbox_f32_buffered,
|
|
4
|
-
dragonbox_f64_buffered,
|
|
5
|
-
} from "../../util/dragonbox";
|
|
2
|
+
import { dtoa_buffered, ftoa_buffered } from "xjb-as";
|
|
6
3
|
|
|
4
|
+
// Float serialization is backed by xjb-as (a Schubfach/xjb shortest-decimal
|
|
5
|
+
// writer with SWAR + WASM-SIMD digit kernels). The *_buffered writers emit
|
|
6
|
+
// UTF-16 straight into the `bs` buffer and return the number of characters
|
|
7
|
+
// written, so advancing the offset is `count << 1` bytes.
|
|
7
8
|
|
|
8
|
-
@inline
|
|
9
9
|
export function serializeFloat32Unsafe(data: f32): void {
|
|
10
|
-
|
|
11
|
-
bs.offset += size;
|
|
10
|
+
bs.offset += ftoa_buffered(bs.offset, data) << 1;
|
|
12
11
|
}
|
|
13
12
|
|
|
14
|
-
|
|
15
|
-
@inline
|
|
16
13
|
export function serializeFloat64Unsafe(data: f64): void {
|
|
17
|
-
|
|
18
|
-
bs.offset += size;
|
|
14
|
+
bs.offset += dtoa_buffered(bs.offset, data) << 1;
|
|
19
15
|
}
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
@inline
|
|
23
17
|
export function serializeFloat32(data: f32): void {
|
|
24
|
-
bs.ensureSize(
|
|
25
|
-
const
|
|
26
|
-
bs.stackSize +=
|
|
27
|
-
bs.offset +=
|
|
18
|
+
bs.ensureSize(128);
|
|
19
|
+
const bytes = ftoa_buffered(bs.offset, data) << 1;
|
|
20
|
+
bs.stackSize += bytes;
|
|
21
|
+
bs.offset += bytes;
|
|
28
22
|
}
|
|
29
23
|
|
|
30
|
-
|
|
31
|
-
@inline
|
|
32
24
|
export function serializeFloat64(data: f64): void {
|
|
33
|
-
bs.ensureSize(
|
|
34
|
-
const
|
|
35
|
-
bs.stackSize +=
|
|
36
|
-
bs.offset +=
|
|
25
|
+
bs.ensureSize(128);
|
|
26
|
+
const bytes = dtoa_buffered(bs.offset, data) << 1;
|
|
27
|
+
bs.stackSize += bytes;
|
|
28
|
+
bs.offset += bytes;
|
|
37
29
|
}
|
|
38
30
|
|
|
39
|
-
|
|
40
|
-
@inline export function serializeFloat<T extends number>(data: T): void {
|
|
31
|
+
export function serializeFloat<T extends number>(data: T): void {
|
|
41
32
|
if (sizeof<T>() == 4) serializeFloat32(<f32>data);
|
|
42
33
|
else serializeFloat64(<f64>data);
|
|
43
34
|
}
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
import { bs } from "../../../lib/as-bs";
|
|
2
2
|
import { ensureItoaPairs, itoaFast } from "../../util/itoa-fast";
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
@inline
|
|
6
4
|
export function serializeIntegerUnsafe<T extends number>(data: T): void {
|
|
7
5
|
ensureItoaPairs();
|
|
8
6
|
const charsWritten = itoaFast<T>(bs.offset, data);
|
|
9
7
|
bs.offset += (<usize>charsWritten) << 1;
|
|
10
8
|
}
|
|
11
|
-
|
|
12
|
-
// @ts-ignore: inline
|
|
13
|
-
@inline export function serializeInteger<T extends number>(data: T): void {
|
|
9
|
+
export function serializeInteger<T extends number>(data: T): void {
|
|
14
10
|
ensureItoaPairs();
|
|
15
11
|
bs.ensureSize(sizeof<T>() << 3);
|
|
16
12
|
const charsWritten = itoaFast<T>(bs.offset, data);
|
|
@@ -7,8 +7,7 @@ import { bytes } from "../../util";
|
|
|
7
7
|
* @param data data to serialize
|
|
8
8
|
* @returns void
|
|
9
9
|
*/
|
|
10
|
-
|
|
11
|
-
@inline export function serializeRaw(data: JSON.Raw): void {
|
|
10
|
+
export function serializeRaw(data: JSON.Raw): void {
|
|
12
11
|
const dataSize = bytes(data.data);
|
|
13
12
|
bs.proposeSize(dataSize);
|
|
14
13
|
memory.copy(
|
|
@@ -6,8 +6,6 @@ import { serializeFloat32Unsafe, serializeFloat64Unsafe } from "./float";
|
|
|
6
6
|
import { serializeIntegerUnsafe } from "./integer";
|
|
7
7
|
import { serializeString } from "../index/string";
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
@inline
|
|
11
9
|
function maxIntegerBytes<T extends number>(): u32 {
|
|
12
10
|
if (sizeof<T>() == 1) return isSigned<T>() ? 8 : 6;
|
|
13
11
|
if (sizeof<T>() == 2) return isSigned<T>() ? 12 : 10;
|
|
@@ -15,8 +13,6 @@ function maxIntegerBytes<T extends number>(): u32 {
|
|
|
15
13
|
return isSigned<T>() ? 42 : 40;
|
|
16
14
|
}
|
|
17
15
|
|
|
18
|
-
|
|
19
|
-
@inline
|
|
20
16
|
function reservePrimitiveSet<T>(len: i32): void {
|
|
21
17
|
if (len <= 0) return;
|
|
22
18
|
if (isBoolean<T>()) {
|
|
@@ -6,17 +6,12 @@ import { serializeFloat32Unsafe, serializeFloat64Unsafe } from "./float";
|
|
|
6
6
|
import { serializeIntegerUnsafe } from "./integer";
|
|
7
7
|
import { serializeString } from "../index/string";
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
@inline
|
|
11
9
|
function maxIntegerBytes<T extends number>(): u32 {
|
|
12
10
|
if (sizeof<T>() == 1) return isSigned<T>() ? 8 : 6;
|
|
13
11
|
if (sizeof<T>() == 2) return isSigned<T>() ? 12 : 10;
|
|
14
12
|
if (sizeof<T>() == 4) return isSigned<T>() ? 22 : 20;
|
|
15
13
|
return isSigned<T>() ? 42 : 40;
|
|
16
14
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
@inline
|
|
20
15
|
function reservePrimitiveStaticArray<T>(len: i32): void {
|
|
21
16
|
if (len <= 0) return;
|
|
22
17
|
if (isBoolean<T>()) {
|
|
@@ -15,8 +15,7 @@ import { serializeStruct } from "./struct";
|
|
|
15
15
|
* @param src string
|
|
16
16
|
* @returns void
|
|
17
17
|
*/
|
|
18
|
-
|
|
19
|
-
@inline export function serializeString_NAIVE(src: string): void {
|
|
18
|
+
export function serializeString_NAIVE(src: string): void {
|
|
20
19
|
serializeStringRange(changetype<usize>(src), bytes(src));
|
|
21
20
|
}
|
|
22
21
|
|
|
@@ -86,9 +85,7 @@ export function serializeStringRange(srcPtr: usize, srcSize: usize): void {
|
|
|
86
85
|
store<u16>(bs.offset, QUOTE);
|
|
87
86
|
bs.offset += 2;
|
|
88
87
|
}
|
|
89
|
-
|
|
90
|
-
// @ts-ignore: inline
|
|
91
|
-
@inline function write_u_escape(code: u16): void {
|
|
88
|
+
function write_u_escape(code: u16): void {
|
|
92
89
|
bs.growSize(10);
|
|
93
90
|
store<u32>(bs.offset, U_MARKER); // "\u"
|
|
94
91
|
store<u64>(bs.offset, u16_to_hex4_swar(code), 4);
|
|
@@ -3,8 +3,6 @@ import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../custom/chars";
|
|
|
3
3
|
import { serializeFloat32Unsafe, serializeFloat64Unsafe } from "./float";
|
|
4
4
|
import { serializeIntegerUnsafe } from "./integer";
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
@inline
|
|
8
6
|
function maxIntegerBytes<T extends number>(): u32 {
|
|
9
7
|
if (sizeof<T>() == 1) return isSigned<T>() ? 8 : 6;
|
|
10
8
|
if (sizeof<T>() == 2) return isSigned<T>() ? 12 : 10;
|
|
@@ -12,8 +10,6 @@ function maxIntegerBytes<T extends number>(): u32 {
|
|
|
12
10
|
return isSigned<T>() ? 42 : 40;
|
|
13
11
|
}
|
|
14
12
|
|
|
15
|
-
|
|
16
|
-
@inline
|
|
17
13
|
function reserveTypedArray<T extends ArrayLike<number>>(len: i32): void {
|
|
18
14
|
if (len <= 0) return;
|
|
19
15
|
if (isFloat<valueof<T>>()) {
|
|
@@ -23,8 +19,6 @@ function reserveTypedArray<T extends ArrayLike<number>>(len: i32): void {
|
|
|
23
19
|
}
|
|
24
20
|
}
|
|
25
21
|
|
|
26
|
-
|
|
27
|
-
@inline
|
|
28
22
|
function serializeTypedArrayElement<T extends ArrayLike<number>>(
|
|
29
23
|
src: T,
|
|
30
24
|
index: i32,
|
|
@@ -207,9 +207,7 @@ export function serializeString_SIMD(src: string): void {
|
|
|
207
207
|
store<u16>(bs.offset, 34); // "
|
|
208
208
|
bs.offset += 2;
|
|
209
209
|
}
|
|
210
|
-
|
|
211
|
-
// @ts-expect-error: @inline is a valid decorator
|
|
212
|
-
@inline function write_u_escape(code: u16): void {
|
|
210
|
+
function write_u_escape(code: u16): void {
|
|
213
211
|
bs.growSize(10);
|
|
214
212
|
store<u32>(bs.offset, U_MARKER); // "\u"
|
|
215
213
|
store<u64>(bs.offset, u16_to_hex4_swar(code), 4);
|
|
@@ -174,8 +174,7 @@ export function serializeString_SWAR(src: string): void {
|
|
|
174
174
|
bs.offset += 2;
|
|
175
175
|
}
|
|
176
176
|
|
|
177
|
-
|
|
178
|
-
@inline function write_u_escape(code: u16): void {
|
|
177
|
+
function write_u_escape(code: u16): void {
|
|
179
178
|
bs.growSize(10);
|
|
180
179
|
store<u32>(bs.offset, U_MARKER); // "\u"
|
|
181
180
|
store<u64>(bs.offset, u16_to_hex4_swar(code), 4);
|
|
@@ -18,11 +18,7 @@ import {
|
|
|
18
18
|
* @param srcEnd Pointer just past the last code unit.
|
|
19
19
|
* @returns The parsed value, truncated to `T`.
|
|
20
20
|
*/
|
|
21
|
-
|
|
22
|
-
@inline export function atou<T extends number>(
|
|
23
|
-
srcStart: usize,
|
|
24
|
-
srcEnd: usize,
|
|
25
|
-
): T {
|
|
21
|
+
export function atou<T extends number>(srcStart: usize, srcEnd: usize): T {
|
|
26
22
|
return deserializeUnsigned_SWAR<T>(srcStart, srcEnd);
|
|
27
23
|
}
|
|
28
24
|
|
|
@@ -34,11 +30,7 @@ import {
|
|
|
34
30
|
* @param srcEnd Pointer just past the last code unit.
|
|
35
31
|
* @returns The parsed value, truncated to `T`.
|
|
36
32
|
*/
|
|
37
|
-
|
|
38
|
-
@inline export function atoi<T extends number>(
|
|
39
|
-
srcStart: usize,
|
|
40
|
-
srcEnd: usize,
|
|
41
|
-
): T {
|
|
33
|
+
export function atoi<T extends number>(srcStart: usize, srcEnd: usize): T {
|
|
42
34
|
return deserializeInteger_SWAR<T>(srcStart, srcEnd);
|
|
43
35
|
}
|
|
44
36
|
|
|
@@ -52,8 +44,7 @@ import {
|
|
|
52
44
|
* @param dstPtr Destination pointer for the parsed value.
|
|
53
45
|
* @returns The source position immediately after the last digit consumed.
|
|
54
46
|
*/
|
|
55
|
-
|
|
56
|
-
@inline export function atouScan<T extends number>(
|
|
47
|
+
export function atouScan<T extends number>(
|
|
57
48
|
srcStart: usize,
|
|
58
49
|
srcEnd: usize,
|
|
59
50
|
dstPtr: usize,
|
|
@@ -71,8 +62,7 @@ import {
|
|
|
71
62
|
* @param dstPtr Destination pointer for the parsed value.
|
|
72
63
|
* @returns The source position immediately after the last digit consumed.
|
|
73
64
|
*/
|
|
74
|
-
|
|
75
|
-
@inline export function atoiScan<T extends number>(
|
|
65
|
+
export function atoiScan<T extends number>(
|
|
76
66
|
srcStart: usize,
|
|
77
67
|
srcEnd: usize,
|
|
78
68
|
dstPtr: usize,
|
package/assembly/util/bytes.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { OBJECT, TOTAL_OVERHEAD } from "rt/common";
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
@inline export function bytes<T>(o: T): i32 {
|
|
3
|
+
export function bytes<T>(o: T): i32 {
|
|
5
4
|
if (isInteger<T>() || isFloat<T>()) {
|
|
6
5
|
return sizeof<T>();
|
|
7
6
|
} else if (isManaged<T>() || isReference<T>()) {
|
package/assembly/util/idofd.ts
CHANGED
package/assembly/util/isSpace.ts
CHANGED