json-as 1.5.0 → 1.6.1
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 +10 -1
- package/README.md +42 -0
- package/assembly/deserialize/error.ts +21 -0
- package/assembly/deserialize/index/arbitrary.ts +29 -8
- package/assembly/deserialize/index/array.ts +2 -1
- package/assembly/deserialize/index/bool.ts +1 -1
- package/assembly/deserialize/index/object.ts +3 -0
- package/assembly/deserialize/index/string.ts +37 -0
- package/assembly/deserialize/index.ts +0 -1
- package/assembly/deserialize/naive/array/arbitrary.ts +3 -2
- package/assembly/deserialize/naive/array/array.ts +1 -0
- package/assembly/deserialize/naive/array/bool.ts +21 -11
- package/assembly/deserialize/naive/array/box.ts +13 -33
- package/assembly/deserialize/naive/array/float.ts +23 -13
- package/assembly/deserialize/naive/array/integer.ts +33 -21
- package/assembly/deserialize/naive/array/object.ts +1 -0
- package/assembly/deserialize/naive/array/string.ts +29 -16
- package/assembly/deserialize/naive/bool.ts +17 -5
- package/assembly/deserialize/naive/float.ts +1 -2
- package/assembly/deserialize/naive/map.ts +339 -39
- package/assembly/deserialize/naive/object.ts +122 -65
- package/assembly/deserialize/naive/raw.ts +13 -1
- package/assembly/deserialize/naive/set.ts +0 -1
- package/assembly/deserialize/naive/staticarray.ts +3 -2
- package/assembly/deserialize/naive/string.ts +41 -52
- package/assembly/deserialize/parseMode.ts +216 -0
- package/assembly/deserialize/simd/array/integer.ts +6 -3
- package/assembly/deserialize/simd/float.ts +35 -29
- package/assembly/deserialize/simd/string.ts +79 -17
- package/assembly/deserialize/string-validation.ts +34 -0
- package/assembly/deserialize/swar/array/array.ts +63 -13
- package/assembly/deserialize/swar/array/bool.ts +14 -3
- package/assembly/deserialize/swar/array/float.ts +394 -13
- package/assembly/deserialize/swar/array/generic.ts +12 -2
- package/assembly/deserialize/swar/array/integer.ts +22 -62
- package/assembly/deserialize/swar/array/object.ts +11 -2
- package/assembly/deserialize/swar/array/shared.ts +1 -1
- package/assembly/deserialize/swar/array/string.ts +34 -5
- package/assembly/deserialize/swar/array/struct.ts +109 -15
- package/assembly/deserialize/swar/float.ts +19 -12
- package/assembly/deserialize/swar/string.ts +70 -111
- package/assembly/index.d.ts +6 -0
- package/assembly/index.ts +411 -33
- package/assembly/serialize/index/bool.ts +1 -1
- package/assembly/serialize/index/float.ts +1 -5
- package/assembly/serialize/index/integer.ts +1 -1
- package/assembly/serialize/index/object.ts +17 -0
- package/assembly/serialize/naive/array.ts +64 -0
- package/assembly/serialize/naive/float.ts +0 -5
- package/assembly/serialize/naive/map.ts +37 -0
- package/assembly/serialize/naive/set.ts +0 -1
- package/assembly/serialize/naive/staticarray.ts +0 -1
- package/assembly/serialize/naive/string.ts +0 -6
- package/assembly/serialize/naive/typedarray.ts +0 -1
- package/assembly/util/eisel-lemire.ts +179 -0
- package/assembly/util/prettyWhitespaceSimd.ts +69 -0
- package/assembly/util/scanValueEndSimd.ts +59 -36
- package/assembly/util/validateJson.ts +338 -0
- package/lib/as-bs.ts +28 -6
- package/package.json +17 -7
- package/transform/lib/index.d.ts +6 -0
- package/transform/lib/index.js +773 -112
- package/transform/lib/types.d.ts +1 -0
- package/transform/lib/types.js +3 -0
- package/assembly/deserialize/index/struct.ts +0 -1
- package/assembly/deserialize/naive/struct.ts +0 -21
|
@@ -2,6 +2,27 @@ import { JSON } from "../../..";
|
|
|
2
2
|
import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
|
|
3
3
|
import { deserializeFloatArrayBody } from "./float";
|
|
4
4
|
import { ensureArrayField, scanValueEnd, skipWhitespace } from "./shared";
|
|
5
|
+
import { skipPrettyWhitespace_SIMD } from "../../../util/prettyWhitespaceSimd";
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@inline
|
|
9
|
+
function skipNestedArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
|
|
10
|
+
const code = load<u16>(srcStart);
|
|
11
|
+
if (ASC_FEATURE_SIMD && code == 10) {
|
|
12
|
+
// Common JSON.stringify(..., null, 2) shape at Canada's coordinate depth:
|
|
13
|
+
// LF + twelve spaces + the next nested array. Two fixed compares avoid a
|
|
14
|
+
// general whitespace mask, bitmask, and ctz on every coordinate pair.
|
|
15
|
+
if (
|
|
16
|
+
srcStart + 28 <= srcEnd &&
|
|
17
|
+
!v128.any_true(v128.xor(load<v128>(srcStart, 2), i16x8.splat(0x20))) &&
|
|
18
|
+
load<u64>(srcStart, 18) == 0x0020_0020_0020_0020 &&
|
|
19
|
+
load<u16>(srcStart, 26) == BRACKET_LEFT
|
|
20
|
+
)
|
|
21
|
+
return srcStart + 26;
|
|
22
|
+
return skipPrettyWhitespace_SIMD(srcStart, srcEnd);
|
|
23
|
+
}
|
|
24
|
+
return skipWhitespace(srcStart, srcEnd);
|
|
25
|
+
}
|
|
5
26
|
|
|
6
27
|
export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
7
28
|
srcStart: usize,
|
|
@@ -9,11 +30,21 @@ export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
|
9
30
|
out: T,
|
|
10
31
|
): usize {
|
|
11
32
|
let index = 0;
|
|
33
|
+
const reusableLength = load<i32>(
|
|
34
|
+
changetype<usize>(out),
|
|
35
|
+
offsetof<T>("length_"),
|
|
36
|
+
);
|
|
37
|
+
const reusableDataStart = out.dataStart;
|
|
38
|
+
const elementSize = sizeof<valueof<T>>();
|
|
12
39
|
|
|
13
40
|
do {
|
|
14
41
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
|
|
15
42
|
srcStart += 2;
|
|
16
|
-
|
|
43
|
+
if (srcStart < srcEnd) {
|
|
44
|
+
const code = load<u16>(srcStart);
|
|
45
|
+
if (code != BRACKET_LEFT)
|
|
46
|
+
srcStart = skipNestedArrayWhitespace(srcStart, srcEnd);
|
|
47
|
+
}
|
|
17
48
|
if (srcStart >= srcEnd) break;
|
|
18
49
|
if (load<u16>(srcStart) == BRACKET_RIGHT) {
|
|
19
50
|
out.length = 0;
|
|
@@ -23,8 +54,10 @@ export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
|
23
54
|
while (srcStart < srcEnd) {
|
|
24
55
|
if (isFloat<valueof<valueof<T>>>()) {
|
|
25
56
|
let value: valueof<T>;
|
|
26
|
-
if (index <
|
|
27
|
-
value =
|
|
57
|
+
if (index < reusableLength) {
|
|
58
|
+
value = load<valueof<T>>(
|
|
59
|
+
reusableDataStart + <usize>index * elementSize,
|
|
60
|
+
);
|
|
28
61
|
} else {
|
|
29
62
|
value = changetype<valueof<T>>(instantiate<valueof<T>>());
|
|
30
63
|
out.push(value);
|
|
@@ -37,8 +70,10 @@ export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
|
37
70
|
if (!srcStart || srcStart >= srcEnd) break;
|
|
38
71
|
} else if (isArray<valueof<valueof<T>>>()) {
|
|
39
72
|
let value: valueof<T>;
|
|
40
|
-
if (index <
|
|
41
|
-
value =
|
|
73
|
+
if (index < reusableLength) {
|
|
74
|
+
value = load<valueof<T>>(
|
|
75
|
+
reusableDataStart + <usize>index * elementSize,
|
|
76
|
+
);
|
|
42
77
|
} else {
|
|
43
78
|
value = changetype<valueof<T>>(instantiate<valueof<T>>());
|
|
44
79
|
out.push(value);
|
|
@@ -54,24 +89,39 @@ export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
|
54
89
|
if (!valueEnd || valueEnd <= srcStart) break;
|
|
55
90
|
|
|
56
91
|
let valuePtr: usize = 0;
|
|
57
|
-
if (index <
|
|
58
|
-
valuePtr = changetype<usize>(
|
|
92
|
+
if (index < reusableLength) {
|
|
93
|
+
valuePtr = changetype<usize>(
|
|
94
|
+
load<valueof<T>>(reusableDataStart + <usize>index * elementSize),
|
|
95
|
+
);
|
|
59
96
|
}
|
|
60
97
|
const value = JSON.__deserialize<valueof<T>>(
|
|
61
98
|
srcStart,
|
|
62
99
|
valueEnd,
|
|
63
100
|
valuePtr,
|
|
64
101
|
);
|
|
65
|
-
if (index <
|
|
66
|
-
|
|
102
|
+
if (index < reusableLength) {
|
|
103
|
+
store<valueof<T>>(
|
|
104
|
+
reusableDataStart + <usize>index * elementSize,
|
|
105
|
+
value,
|
|
106
|
+
);
|
|
107
|
+
} else {
|
|
108
|
+
out.push(value);
|
|
109
|
+
}
|
|
67
110
|
srcStart = valueEnd;
|
|
68
111
|
}
|
|
69
112
|
|
|
70
|
-
|
|
71
|
-
|
|
113
|
+
let code = load<u16>(srcStart);
|
|
114
|
+
if (code != COMMA && code != BRACKET_RIGHT) {
|
|
115
|
+
srcStart = skipWhitespace(srcStart, srcEnd);
|
|
116
|
+
code = load<u16>(srcStart);
|
|
117
|
+
}
|
|
72
118
|
if (code == COMMA) {
|
|
73
119
|
srcStart += 2;
|
|
74
|
-
|
|
120
|
+
if (srcStart < srcEnd) {
|
|
121
|
+
code = load<u16>(srcStart);
|
|
122
|
+
if (code != BRACKET_LEFT)
|
|
123
|
+
srcStart = skipNestedArrayWhitespace(srcStart, srcEnd);
|
|
124
|
+
}
|
|
75
125
|
index++;
|
|
76
126
|
continue;
|
|
77
127
|
}
|
|
@@ -80,7 +130,7 @@ export function deserializeArrayArrayBody<T extends unknown[][]>(
|
|
|
80
130
|
// correct (the array is being reused with the same shape, e.g.
|
|
81
131
|
// canada's geometry rings across repeated parses).
|
|
82
132
|
const nextLen = index + 1;
|
|
83
|
-
if (
|
|
133
|
+
if (reusableLength != nextLen) out.length = nextLen;
|
|
84
134
|
return srcStart + 2;
|
|
85
135
|
}
|
|
86
136
|
break;
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from "../../../custom/chars";
|
|
8
8
|
import { isSpace } from "../../../util";
|
|
9
9
|
import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
10
|
+
import { markProductionParseError } from "../../error";
|
|
10
11
|
|
|
11
12
|
function deserializeBooleanArrayBody<T extends boolean[]>(
|
|
12
13
|
srcStart: usize,
|
|
@@ -14,6 +15,12 @@ function deserializeBooleanArrayBody<T extends boolean[]>(
|
|
|
14
15
|
out: T,
|
|
15
16
|
): usize {
|
|
16
17
|
let index = 0;
|
|
18
|
+
const reusableLength = load<i32>(
|
|
19
|
+
changetype<usize>(out),
|
|
20
|
+
offsetof<T>("length_"),
|
|
21
|
+
);
|
|
22
|
+
const reusableDataStart = out.dataStart;
|
|
23
|
+
const elementSize = sizeof<valueof<T>>();
|
|
17
24
|
|
|
18
25
|
do {
|
|
19
26
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
|
|
@@ -26,7 +33,10 @@ function deserializeBooleanArrayBody<T extends boolean[]>(
|
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
while (srcStart < srcEnd) {
|
|
29
|
-
const slot =
|
|
36
|
+
const slot =
|
|
37
|
+
index < reusableLength
|
|
38
|
+
? reusableDataStart + <usize>index * elementSize
|
|
39
|
+
: ensureArrayElementSlot<T>(out, index);
|
|
30
40
|
const block = load<u64>(srcStart);
|
|
31
41
|
if (block == TRUE_WORD_U64) {
|
|
32
42
|
store<valueof<T>>(slot, true);
|
|
@@ -49,14 +59,15 @@ function deserializeBooleanArrayBody<T extends boolean[]>(
|
|
|
49
59
|
}
|
|
50
60
|
if (code == BRACKET_RIGHT) {
|
|
51
61
|
const nextLen = index + 1;
|
|
52
|
-
if (
|
|
62
|
+
if (reusableLength != nextLen) out.length = nextLen;
|
|
53
63
|
return srcStart + 2;
|
|
54
64
|
}
|
|
55
65
|
break;
|
|
56
66
|
}
|
|
57
67
|
} while (false);
|
|
58
68
|
|
|
59
|
-
|
|
69
|
+
markProductionParseError();
|
|
70
|
+
return 0;
|
|
60
71
|
}
|
|
61
72
|
|
|
62
73
|
export function deserializeBooleanArrayField<T extends boolean[]>(
|
|
@@ -4,14 +4,24 @@ import { deserializeFloatField_NAIVE } from "../../naive/float";
|
|
|
4
4
|
import { deserializeFloatArray_NAIVE } from "../../naive/array/float";
|
|
5
5
|
import { BRACKET_LEFT, BRACKET_RIGHT, COMMA } from "../../../custom/chars";
|
|
6
6
|
import { ensureArrayElementSlot, ensureArrayField } from "./shared";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
parse4Digits_PairMul,
|
|
9
|
+
parse8Digits_PairMul,
|
|
10
|
+
} from "../../../util/swar-int";
|
|
8
11
|
import { loadPow10, MAX_EXACT_MANTISSA, MAX_EXACT_POW10 } from "../float";
|
|
9
12
|
import { isSpace } from "../../../util";
|
|
13
|
+
import {
|
|
14
|
+
eiselLemire22,
|
|
15
|
+
eiselLemireMinus14,
|
|
16
|
+
eiselLemireMinus14_54Bit,
|
|
17
|
+
} from "../../../util/eisel-lemire";
|
|
18
|
+
import { markProductionParseError } from "../../error";
|
|
10
19
|
|
|
11
20
|
function skipFloatArrayWhitespace(srcStart: usize, srcEnd: usize): usize {
|
|
12
21
|
while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
|
|
13
22
|
return srcStart;
|
|
14
23
|
}
|
|
24
|
+
|
|
15
25
|
function fallbackStore<E>(origStart: usize, end: usize, slot: usize): void {
|
|
16
26
|
const s = ptrToStr(origStart, end);
|
|
17
27
|
if (sizeof<E>() == sizeof<f32>()) {
|
|
@@ -21,6 +31,228 @@ function fallbackStore<E>(origStart: usize, end: usize, slot: usize): void {
|
|
|
21
31
|
}
|
|
22
32
|
}
|
|
23
33
|
|
|
34
|
+
// Cold companion to the 14-digit GeoJSON hot path. Keeping the less common
|
|
35
|
+
// fixed widths out of `parseFloatElementSWAR` preserves its compact branch
|
|
36
|
+
// layout while avoiding the generic digit loops for the remaining coordinate
|
|
37
|
+
// shapes seen in real GeoJSON payloads.
|
|
38
|
+
function parseGeoAlternateFixed<E>(
|
|
39
|
+
p: usize,
|
|
40
|
+
srcEnd: usize,
|
|
41
|
+
slot: usize,
|
|
42
|
+
negative: bool,
|
|
43
|
+
firstDigit: u32,
|
|
44
|
+
): usize {
|
|
45
|
+
if (p + 20 >= srcEnd) return 0;
|
|
46
|
+
let intDigits = 0;
|
|
47
|
+
if (load<u16>(p, 4) == 46) {
|
|
48
|
+
intDigits = 2;
|
|
49
|
+
} else if (load<u16>(p, 6) == 46) {
|
|
50
|
+
intDigits = 3;
|
|
51
|
+
} else {
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const frac = p + <usize>((intDigits + 1) << 1);
|
|
56
|
+
let fracDigits = 0;
|
|
57
|
+
let term = frac + 12;
|
|
58
|
+
let termCode = load<u16>(term);
|
|
59
|
+
if (termCode == COMMA || termCode == BRACKET_RIGHT || isSpace(termCode)) {
|
|
60
|
+
fracDigits = 6;
|
|
61
|
+
} else {
|
|
62
|
+
term = frac + 30;
|
|
63
|
+
if (term >= srcEnd) return 0;
|
|
64
|
+
termCode = load<u16>(term);
|
|
65
|
+
if (termCode == COMMA || termCode == BRACKET_RIGHT || isSpace(termCode)) {
|
|
66
|
+
fracDigits = 15;
|
|
67
|
+
} else {
|
|
68
|
+
term = frac + 26;
|
|
69
|
+
termCode = load<u16>(term);
|
|
70
|
+
if (termCode == COMMA || termCode == BRACKET_RIGHT || isSpace(termCode)) {
|
|
71
|
+
fracDigits = 13;
|
|
72
|
+
} else {
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const d1 = <u32>load<u16>(p, 2) - 48;
|
|
79
|
+
const d2 = intDigits == 3 ? <u32>load<u16>(p, 4) - 48 : <u32>0;
|
|
80
|
+
if (d1 > 9 || d2 > 9) return 0;
|
|
81
|
+
const whole =
|
|
82
|
+
intDigits == 2
|
|
83
|
+
? <u64>(firstDigit * 10 + d1)
|
|
84
|
+
: <u64>(firstDigit * 100 + d1 * 10 + d2);
|
|
85
|
+
|
|
86
|
+
let mantissa: u64;
|
|
87
|
+
if (fracDigits == 6) {
|
|
88
|
+
const a = parse4Digits_PairMul(load<u64>(frac));
|
|
89
|
+
const d4 = <u32>load<u16>(frac, 8) - 48;
|
|
90
|
+
const d5 = <u32>load<u16>(frac, 10) - 48;
|
|
91
|
+
if (a == U32.MAX_VALUE || d4 > 9 || d5 > 9) return 0;
|
|
92
|
+
mantissa = whole * 1_000_000 + <u64>a * 100 + <u64>(d4 * 10 + d5);
|
|
93
|
+
} else {
|
|
94
|
+
const a = parse8Digits_PairMul(load<u64>(frac), load<u64>(frac, 8));
|
|
95
|
+
const c = parse4Digits_PairMul(load<u64>(frac, 16));
|
|
96
|
+
const d12 = <u32>load<u16>(frac, 24) - 48;
|
|
97
|
+
if (a == U32.MAX_VALUE || c == U32.MAX_VALUE || d12 > 9) return 0;
|
|
98
|
+
if (fracDigits == 13) {
|
|
99
|
+
mantissa =
|
|
100
|
+
whole * 10_000_000_000_000 + <u64>a * 100_000 + <u64>c * 10 + <u64>d12;
|
|
101
|
+
} else {
|
|
102
|
+
const d13 = <u32>load<u16>(frac, 26) - 48;
|
|
103
|
+
const d14 = <u32>load<u16>(frac, 28) - 48;
|
|
104
|
+
if (d13 > 9 || d14 > 9) return 0;
|
|
105
|
+
mantissa =
|
|
106
|
+
whole * 1_000_000_000_000_000 +
|
|
107
|
+
<u64>a * 10_000_000 +
|
|
108
|
+
<u64>c * 1_000 +
|
|
109
|
+
<u64>(d12 * 100 + d13 * 10 + d14);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
let result: f64;
|
|
114
|
+
if (fracDigits == 6) {
|
|
115
|
+
result = <f64>mantissa / loadPow10(6);
|
|
116
|
+
} else if (fracDigits == 15) {
|
|
117
|
+
result = eiselLemire22(mantissa, -15);
|
|
118
|
+
} else {
|
|
119
|
+
result =
|
|
120
|
+
mantissa <= MAX_EXACT_MANTISSA
|
|
121
|
+
? <f64>mantissa / loadPow10(13)
|
|
122
|
+
: eiselLemire22(mantissa, -13);
|
|
123
|
+
}
|
|
124
|
+
if (negative) result = -result;
|
|
125
|
+
if (sizeof<E>() == sizeof<f32>()) {
|
|
126
|
+
store<f32>(slot, <f32>result);
|
|
127
|
+
} else {
|
|
128
|
+
store<f64>(slot, result);
|
|
129
|
+
}
|
|
130
|
+
return term;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function parseFixed14Pair<E>(
|
|
134
|
+
srcStart: usize,
|
|
135
|
+
srcEnd: usize,
|
|
136
|
+
dataStart: usize,
|
|
137
|
+
): usize {
|
|
138
|
+
let p0 = srcStart;
|
|
139
|
+
let negative0 = false;
|
|
140
|
+
if (load<u16>(p0) == 45) {
|
|
141
|
+
negative0 = true;
|
|
142
|
+
p0 += 2;
|
|
143
|
+
}
|
|
144
|
+
if (p0 + 36 >= srcEnd) return 0;
|
|
145
|
+
const first0 = <u32>load<u16>(p0) - 48;
|
|
146
|
+
if (first0 > 9) return 0;
|
|
147
|
+
let intDigits0 = 0;
|
|
148
|
+
if (load<u16>(p0, 4) == 46) {
|
|
149
|
+
intDigits0 = 2;
|
|
150
|
+
} else if (load<u16>(p0, 6) == 46) {
|
|
151
|
+
intDigits0 = 3;
|
|
152
|
+
} else {
|
|
153
|
+
return 0;
|
|
154
|
+
}
|
|
155
|
+
const frac0 = p0 + <usize>((intDigits0 + 1) << 1);
|
|
156
|
+
const term0 = frac0 + 28;
|
|
157
|
+
if (load<u16>(term0) != COMMA) return 0;
|
|
158
|
+
|
|
159
|
+
let p1 = term0 + 2;
|
|
160
|
+
if (load<u16>(p1) == 32) p1 += 2;
|
|
161
|
+
let negative1 = false;
|
|
162
|
+
if (load<u16>(p1) == 45) {
|
|
163
|
+
negative1 = true;
|
|
164
|
+
p1 += 2;
|
|
165
|
+
}
|
|
166
|
+
if (p1 + 36 >= srcEnd) return 0;
|
|
167
|
+
const first1 = <u32>load<u16>(p1) - 48;
|
|
168
|
+
if (first1 > 9) return 0;
|
|
169
|
+
let intDigits1 = 0;
|
|
170
|
+
if (load<u16>(p1, 4) == 46) {
|
|
171
|
+
intDigits1 = 2;
|
|
172
|
+
} else if (load<u16>(p1, 6) == 46) {
|
|
173
|
+
intDigits1 = 3;
|
|
174
|
+
} else {
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
const frac1 = p1 + <usize>((intDigits1 + 1) << 1);
|
|
178
|
+
const term1 = frac1 + 28;
|
|
179
|
+
if (load<u16>(term1) != BRACKET_RIGHT) return 0;
|
|
180
|
+
|
|
181
|
+
const a0 = parse8Digits_PairMul(load<u64>(frac0), load<u64>(frac0, 8));
|
|
182
|
+
const c0 = parse4Digits_PairMul(load<u64>(frac0, 16));
|
|
183
|
+
const d012 = <u32>load<u16>(frac0, 24) - 48;
|
|
184
|
+
const d013 = <u32>load<u16>(frac0, 26) - 48;
|
|
185
|
+
const d01 = <u32>load<u16>(p0, 2) - 48;
|
|
186
|
+
const d02 = intDigits0 == 3 ? <u32>load<u16>(p0, 4) - 48 : <u32>0;
|
|
187
|
+
if (
|
|
188
|
+
a0 == U32.MAX_VALUE ||
|
|
189
|
+
c0 == U32.MAX_VALUE ||
|
|
190
|
+
d012 > 9 ||
|
|
191
|
+
d013 > 9 ||
|
|
192
|
+
d01 > 9 ||
|
|
193
|
+
d02 > 9
|
|
194
|
+
)
|
|
195
|
+
return 0;
|
|
196
|
+
|
|
197
|
+
const a1 = parse8Digits_PairMul(load<u64>(frac1), load<u64>(frac1, 8));
|
|
198
|
+
const c1 = parse4Digits_PairMul(load<u64>(frac1, 16));
|
|
199
|
+
const d112 = <u32>load<u16>(frac1, 24) - 48;
|
|
200
|
+
const d113 = <u32>load<u16>(frac1, 26) - 48;
|
|
201
|
+
const d11 = <u32>load<u16>(p1, 2) - 48;
|
|
202
|
+
const d12 = intDigits1 == 3 ? <u32>load<u16>(p1, 4) - 48 : <u32>0;
|
|
203
|
+
if (
|
|
204
|
+
a1 == U32.MAX_VALUE ||
|
|
205
|
+
c1 == U32.MAX_VALUE ||
|
|
206
|
+
d112 > 9 ||
|
|
207
|
+
d113 > 9 ||
|
|
208
|
+
d11 > 9 ||
|
|
209
|
+
d12 > 9
|
|
210
|
+
)
|
|
211
|
+
return 0;
|
|
212
|
+
|
|
213
|
+
const whole0 =
|
|
214
|
+
intDigits0 == 2
|
|
215
|
+
? <u64>(first0 * 10 + d01)
|
|
216
|
+
: <u64>(first0 * 100 + d01 * 10 + d02);
|
|
217
|
+
const whole1 =
|
|
218
|
+
intDigits1 == 2
|
|
219
|
+
? <u64>(first1 * 10 + d11)
|
|
220
|
+
: <u64>(first1 * 100 + d11 * 10 + d12);
|
|
221
|
+
const mantissa0 =
|
|
222
|
+
whole0 * 100_000_000_000_000 +
|
|
223
|
+
<u64>a0 * 1_000_000 +
|
|
224
|
+
<u64>c0 * 100 +
|
|
225
|
+
<u64>(d012 * 10 + d013);
|
|
226
|
+
const mantissa1 =
|
|
227
|
+
whole1 * 100_000_000_000_000 +
|
|
228
|
+
<u64>a1 * 1_000_000 +
|
|
229
|
+
<u64>c1 * 100 +
|
|
230
|
+
<u64>(d112 * 10 + d113);
|
|
231
|
+
|
|
232
|
+
let value0 =
|
|
233
|
+
mantissa0 <= MAX_EXACT_MANTISSA
|
|
234
|
+
? <f64>mantissa0 / loadPow10(14)
|
|
235
|
+
: mantissa0 < (<u64>1) << 54
|
|
236
|
+
? eiselLemireMinus14_54Bit(mantissa0)
|
|
237
|
+
: eiselLemireMinus14(mantissa0);
|
|
238
|
+
let value1 =
|
|
239
|
+
mantissa1 <= MAX_EXACT_MANTISSA
|
|
240
|
+
? <f64>mantissa1 / loadPow10(14)
|
|
241
|
+
: mantissa1 < (<u64>1) << 54
|
|
242
|
+
? eiselLemireMinus14_54Bit(mantissa1)
|
|
243
|
+
: eiselLemireMinus14(mantissa1);
|
|
244
|
+
if (negative0) value0 = -value0;
|
|
245
|
+
if (negative1) value1 = -value1;
|
|
246
|
+
if (sizeof<E>() == sizeof<f32>()) {
|
|
247
|
+
store<f32>(dataStart, <f32>value0);
|
|
248
|
+
store<f32>(dataStart + sizeof<E>(), <f32>value1);
|
|
249
|
+
} else {
|
|
250
|
+
store<f64>(dataStart, value0);
|
|
251
|
+
store<f64>(dataStart + sizeof<E>(), value1);
|
|
252
|
+
}
|
|
253
|
+
return term1 + 2;
|
|
254
|
+
}
|
|
255
|
+
|
|
24
256
|
/**
|
|
25
257
|
* Inline single-pass Lemire-style float element parser for arrays.
|
|
26
258
|
*
|
|
@@ -55,37 +287,128 @@ export function parseFloatElementSWAR<E>(
|
|
|
55
287
|
let firstDigit = <u32>code - 48;
|
|
56
288
|
if (firstDigit > 9) return 0;
|
|
57
289
|
|
|
290
|
+
// GeoJSON coordinates overwhelmingly use one of these two fixed layouts:
|
|
291
|
+
// `DD.ffffffffffffff` or `DDD.ffffffffffffff`. Recognize them before the
|
|
292
|
+
// generic scan so the common case avoids four loop exits (including the
|
|
293
|
+
// guaranteed failing parse4 probe at the fraction terminator). Each parse4
|
|
294
|
+
// still validates its lanes; malformed or exponent-bearing values fall
|
|
295
|
+
// through unchanged to the fully general parser below.
|
|
296
|
+
if (ASC_FEATURE_SIMD && p + 36 < srcEnd) {
|
|
297
|
+
let intDigitsFast = 0;
|
|
298
|
+
if (load<u16>(p, 4) == 46) {
|
|
299
|
+
intDigitsFast = 2;
|
|
300
|
+
} else if (load<u16>(p, 6) == 46) {
|
|
301
|
+
intDigitsFast = 3;
|
|
302
|
+
}
|
|
303
|
+
if (intDigitsFast != 0) {
|
|
304
|
+
const frac = p + <usize>((intDigitsFast + 1) << 1);
|
|
305
|
+
const term = frac + 28;
|
|
306
|
+
const termCode = load<u16>(term);
|
|
307
|
+
if (termCode == COMMA || termCode == BRACKET_RIGHT || isSpace(termCode)) {
|
|
308
|
+
const a = parse8Digits_PairMul(load<u64>(frac), load<u64>(frac, 8));
|
|
309
|
+
const c = parse4Digits_PairMul(load<u64>(frac, 16));
|
|
310
|
+
const d12 = <u32>load<u16>(frac, 24) - 48;
|
|
311
|
+
const d13 = <u32>load<u16>(frac, 26) - 48;
|
|
312
|
+
const d1 = <u32>load<u16>(p, 2) - 48;
|
|
313
|
+
const d2 = intDigitsFast == 3 ? <u32>load<u16>(p, 4) - 48 : <u32>0;
|
|
314
|
+
if (
|
|
315
|
+
a != U32.MAX_VALUE &&
|
|
316
|
+
c != U32.MAX_VALUE &&
|
|
317
|
+
d1 <= 9 &&
|
|
318
|
+
d2 <= 9 &&
|
|
319
|
+
d12 <= 9 &&
|
|
320
|
+
d13 <= 9
|
|
321
|
+
) {
|
|
322
|
+
const whole =
|
|
323
|
+
intDigitsFast == 2
|
|
324
|
+
? <u64>(firstDigit * 10 + d1)
|
|
325
|
+
: <u64>(firstDigit * 100 + d1 * 10 + d2);
|
|
326
|
+
const mantissa =
|
|
327
|
+
whole * 100_000_000_000_000 +
|
|
328
|
+
<u64>a * 1_000_000 +
|
|
329
|
+
<u64>c * 100 +
|
|
330
|
+
<u64>(d12 * 10 + d13);
|
|
331
|
+
let result =
|
|
332
|
+
mantissa <= MAX_EXACT_MANTISSA
|
|
333
|
+
? <f64>mantissa / loadPow10(14)
|
|
334
|
+
: mantissa < (<u64>1) << 54
|
|
335
|
+
? eiselLemireMinus14_54Bit(mantissa)
|
|
336
|
+
: eiselLemireMinus14(mantissa);
|
|
337
|
+
if (negative) result = -result;
|
|
338
|
+
if (sizeof<E>() == sizeof<f32>()) {
|
|
339
|
+
store<f32>(slot, <f32>result);
|
|
340
|
+
} else {
|
|
341
|
+
store<f64>(slot, result);
|
|
342
|
+
}
|
|
343
|
+
return term;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (ASC_FEATURE_SIMD) {
|
|
350
|
+
const alternateEnd = parseGeoAlternateFixed<E>(
|
|
351
|
+
p,
|
|
352
|
+
srcEnd,
|
|
353
|
+
slot,
|
|
354
|
+
negative,
|
|
355
|
+
firstDigit,
|
|
356
|
+
);
|
|
357
|
+
if (alternateEnd) return alternateEnd;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return parseFloatElementGeneric<E>(origStart, srcEnd, slot);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
function parseFloatElementGeneric<E>(
|
|
364
|
+
srcStart: usize,
|
|
365
|
+
srcEnd: usize,
|
|
366
|
+
slot: usize,
|
|
367
|
+
): usize {
|
|
368
|
+
const origStart = srcStart;
|
|
369
|
+
let p = srcStart;
|
|
370
|
+
let negative = false;
|
|
371
|
+
let code = load<u16>(p);
|
|
372
|
+
if (code == 45) {
|
|
373
|
+
negative = true;
|
|
374
|
+
p += 2;
|
|
375
|
+
if (p >= srcEnd) return 0;
|
|
376
|
+
code = load<u16>(p);
|
|
377
|
+
}
|
|
378
|
+
const firstDigit = <u32>code - 48;
|
|
379
|
+
if (firstDigit > 9) return 0;
|
|
380
|
+
|
|
58
381
|
// Integer mantissa: scalar (most JSON integers are 1-3 digits).
|
|
59
382
|
let mantissa: u64 = 0;
|
|
60
|
-
|
|
383
|
+
const intStart = p;
|
|
61
384
|
while (p < srcEnd) {
|
|
62
385
|
const d = <u32>load<u16>(p) - 48;
|
|
63
386
|
if (d > 9) break;
|
|
64
387
|
mantissa = mantissa * 10 + <u64>d;
|
|
65
|
-
intDigits++;
|
|
66
388
|
p += 2;
|
|
67
389
|
}
|
|
390
|
+
const intDigits = <i32>((p - intStart) >> 1);
|
|
68
391
|
|
|
69
392
|
// Fractional mantissa: parse4 SWAR stride + scalar tail. Same u64
|
|
70
393
|
// accumulator as the integer part - exponent compensates for fracDigits.
|
|
71
|
-
let
|
|
394
|
+
let fracStart = p;
|
|
72
395
|
if (p < srcEnd && load<u16>(p) == 46) {
|
|
73
396
|
p += 2;
|
|
397
|
+
fracStart = p;
|
|
74
398
|
while (p + 6 < srcEnd) {
|
|
75
399
|
const parsed = parse4Digits_PairMul(load<u64>(p));
|
|
76
400
|
if (parsed == U32.MAX_VALUE) break;
|
|
77
401
|
mantissa = mantissa * 10_000 + <u64>parsed;
|
|
78
|
-
fracDigits += 4;
|
|
79
402
|
p += 8;
|
|
80
403
|
}
|
|
81
404
|
while (p < srcEnd) {
|
|
82
405
|
const d = <u32>load<u16>(p) - 48;
|
|
83
406
|
if (d > 9) break;
|
|
84
407
|
mantissa = mantissa * 10 + <u64>d;
|
|
85
|
-
fracDigits++;
|
|
86
408
|
p += 2;
|
|
87
409
|
}
|
|
88
410
|
}
|
|
411
|
+
const fracDigits = <i32>((p - fracStart) >> 1);
|
|
89
412
|
|
|
90
413
|
const mantDigits = intDigits + fracDigits;
|
|
91
414
|
let exponent: i32 = -fracDigits;
|
|
@@ -149,10 +472,12 @@ export function parseFloatElementSWAR<E>(
|
|
|
149
472
|
result /= loadPow10(<u32>-exponent);
|
|
150
473
|
}
|
|
151
474
|
} else if (mantDigits <= 19) {
|
|
152
|
-
//
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
|
|
475
|
+
// Eisel-Lemire handles the overwhelmingly common medium exponent range
|
|
476
|
+
// without the u64 division/modulo in `scientific`'s scaledown path.
|
|
477
|
+
result =
|
|
478
|
+
exponent >= -22 && exponent <= 22
|
|
479
|
+
? eiselLemire22(mantissa, exponent)
|
|
480
|
+
: scientific(mantissa, exponent);
|
|
156
481
|
} else {
|
|
157
482
|
// >19 mantissa digits - beyond u64 capacity, may need strtod's sticky-bit
|
|
158
483
|
// pattern. Hand off to f*.parse on the float's substring.
|
|
@@ -254,12 +579,19 @@ export function deserializeFloatArray_SWAR<T extends number[]>(
|
|
|
254
579
|
* `[lon,lat]` would over-allocate megabytes of f64 capacity. Instead we
|
|
255
580
|
* use `ensureArrayElementSlot`'s grow-or-reuse strategy.
|
|
256
581
|
*/
|
|
582
|
+
@inline
|
|
257
583
|
export function deserializeFloatArrayBody<T extends number[]>(
|
|
258
584
|
srcStart: usize,
|
|
259
585
|
srcEnd: usize,
|
|
260
586
|
out: T,
|
|
261
587
|
): usize {
|
|
262
588
|
let index = 0;
|
|
589
|
+
const reusableLength = load<i32>(
|
|
590
|
+
changetype<usize>(out),
|
|
591
|
+
offsetof<T>("length_"),
|
|
592
|
+
);
|
|
593
|
+
const reusableDataStart = out.dataStart;
|
|
594
|
+
const elementSize = sizeof<valueof<T>>();
|
|
263
595
|
|
|
264
596
|
do {
|
|
265
597
|
if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT) break;
|
|
@@ -271,8 +603,56 @@ export function deserializeFloatArrayBody<T extends number[]>(
|
|
|
271
603
|
return srcStart + 2;
|
|
272
604
|
}
|
|
273
605
|
|
|
606
|
+
// GeoJSON's innermost coordinate arrays are overwhelmingly reused
|
|
607
|
+
// `[longitude,latitude]` pairs. Unroll that stable-shape case so neither
|
|
608
|
+
// element pays the generic index/capacity selection or loop backedge. A
|
|
609
|
+
// mismatch restores the first-value cursor and falls through unchanged.
|
|
610
|
+
if (ASC_FEATURE_SIMD && reusableLength == 2) {
|
|
611
|
+
const firstStart = srcStart;
|
|
612
|
+
const pairEnd = parseFixed14Pair<valueof<T>>(
|
|
613
|
+
srcStart,
|
|
614
|
+
srcEnd,
|
|
615
|
+
reusableDataStart,
|
|
616
|
+
);
|
|
617
|
+
if (pairEnd) return pairEnd;
|
|
618
|
+
let next = parseFloatElementSWAR<valueof<T>>(
|
|
619
|
+
srcStart,
|
|
620
|
+
srcEnd,
|
|
621
|
+
reusableDataStart,
|
|
622
|
+
);
|
|
623
|
+
if (next) {
|
|
624
|
+
srcStart = next;
|
|
625
|
+
if (srcStart < srcEnd && load<u16>(srcStart) != COMMA)
|
|
626
|
+
srcStart = skipFloatArrayWhitespace(srcStart, srcEnd);
|
|
627
|
+
if (srcStart < srcEnd && load<u16>(srcStart) == COMMA) {
|
|
628
|
+
srcStart += 2;
|
|
629
|
+
if (srcStart < srcEnd && load<u16>(srcStart) == 32) {
|
|
630
|
+
srcStart += 2;
|
|
631
|
+
} else {
|
|
632
|
+
srcStart = skipFloatArrayWhitespace(srcStart, srcEnd);
|
|
633
|
+
}
|
|
634
|
+
next = parseFloatElementSWAR<valueof<T>>(
|
|
635
|
+
srcStart,
|
|
636
|
+
srcEnd,
|
|
637
|
+
reusableDataStart + elementSize,
|
|
638
|
+
);
|
|
639
|
+
if (next) {
|
|
640
|
+
srcStart = next;
|
|
641
|
+
if (srcStart < srcEnd && load<u16>(srcStart) != BRACKET_RIGHT)
|
|
642
|
+
srcStart = skipFloatArrayWhitespace(srcStart, srcEnd);
|
|
643
|
+
if (srcStart < srcEnd && load<u16>(srcStart) == BRACKET_RIGHT)
|
|
644
|
+
return srcStart + 2;
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
srcStart = firstStart;
|
|
649
|
+
}
|
|
650
|
+
|
|
274
651
|
while (srcStart < srcEnd) {
|
|
275
|
-
const slot =
|
|
652
|
+
const slot =
|
|
653
|
+
index < reusableLength
|
|
654
|
+
? reusableDataStart + <usize>index * elementSize
|
|
655
|
+
: ensureArrayElementSlot<T>(out, index);
|
|
276
656
|
let next = parseFloatElementSWAR<valueof<T>>(srcStart, srcEnd, slot);
|
|
277
657
|
if (!next) {
|
|
278
658
|
next = deserializeFloatField_NAIVE<valueof<T>>(srcStart, srcEnd, slot);
|
|
@@ -296,14 +676,15 @@ export function deserializeFloatArrayBody<T extends number[]>(
|
|
|
296
676
|
// pairs across thousands of geometries) the length is unchanged
|
|
297
677
|
// and the AS runtime's `ensureCapacity` call is pure overhead.
|
|
298
678
|
const nextLen = index + 1;
|
|
299
|
-
if (
|
|
679
|
+
if (reusableLength != nextLen) out.length = nextLen;
|
|
300
680
|
return srcStart + 2;
|
|
301
681
|
}
|
|
302
682
|
break;
|
|
303
683
|
}
|
|
304
684
|
} while (false);
|
|
305
685
|
|
|
306
|
-
|
|
686
|
+
markProductionParseError();
|
|
687
|
+
return 0;
|
|
307
688
|
}
|
|
308
689
|
export function deserializeFloatArrayField<T extends number[]>(
|
|
309
690
|
srcStart: usize,
|