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/transform/lib/index.js
CHANGED
|
@@ -13,7 +13,59 @@ const WRITE = process.env["JSON_WRITE"]?.trim();
|
|
|
13
13
|
const rawValue = process.env["JSON_DEBUG"]?.trim();
|
|
14
14
|
const DEBUG = rawValue === "true" ? 1 : rawValue === "false" || rawValue === "" ? 0 : isNaN(Number(rawValue)) ? 0 : Number(rawValue);
|
|
15
15
|
const STRICT = process.env["JSON_STRICT"] && process.env["JSON_STRICT"] == "true";
|
|
16
|
-
const
|
|
16
|
+
const DEFAULT_JSON_CACHE_BYTES = 1 << 20;
|
|
17
|
+
function envFlagDefaultTrue(value) {
|
|
18
|
+
if (!value)
|
|
19
|
+
return true;
|
|
20
|
+
switch (value.trim().toLowerCase()) {
|
|
21
|
+
case "0":
|
|
22
|
+
case "false":
|
|
23
|
+
case "off":
|
|
24
|
+
case "no":
|
|
25
|
+
return false;
|
|
26
|
+
default:
|
|
27
|
+
return true;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const USE_FAST_PATH = envFlagDefaultTrue(process.env["JSON_USE_FAST_PATH"]);
|
|
31
|
+
const THROW_FAST_PATH = process.env["JSON_FAST_PATH_THROW"]?.trim() === "1";
|
|
32
|
+
function parseJSONCacheConfig(value) {
|
|
33
|
+
if (!value)
|
|
34
|
+
return { enabled: false, bytes: 0 };
|
|
35
|
+
const raw = value.trim();
|
|
36
|
+
if (!raw)
|
|
37
|
+
return { enabled: false, bytes: 0 };
|
|
38
|
+
const lower = raw.toLowerCase();
|
|
39
|
+
if (lower === "false" || lower === "off" || lower === "no" || lower === "none" || lower === "0") {
|
|
40
|
+
return { enabled: false, bytes: 0 };
|
|
41
|
+
}
|
|
42
|
+
if (lower === "true" || lower === "on" || lower === "yes") {
|
|
43
|
+
return { enabled: true, bytes: DEFAULT_JSON_CACHE_BYTES };
|
|
44
|
+
}
|
|
45
|
+
const match = /^(\d+)\s*([kKmMgG]?[bB])?$/.exec(raw);
|
|
46
|
+
if (!match) {
|
|
47
|
+
throw new Error(`Invalid JSON_CACHE value '${value}'. Expected true/false or <int>[kb|mb|gb|KB|MB|GB].`);
|
|
48
|
+
}
|
|
49
|
+
const amount = Number(match[1]);
|
|
50
|
+
const suffix = match[2] || "B";
|
|
51
|
+
if (!Number.isFinite(amount)) {
|
|
52
|
+
throw new Error(`Invalid JSON_CACHE value '${value}'.`);
|
|
53
|
+
}
|
|
54
|
+
const unit = suffix[0];
|
|
55
|
+
const scale = unit == "k" || unit == "K" ? 1_000 : unit == "m" || unit == "M" ? 1_000_000 : unit == "g" || unit == "G" ? 1_000_000_000 : 1;
|
|
56
|
+
let bytes = amount * scale;
|
|
57
|
+
if (suffix.endsWith("b")) {
|
|
58
|
+
bytes = Math.ceil(bytes / 8);
|
|
59
|
+
}
|
|
60
|
+
if (bytes <= 0) {
|
|
61
|
+
return { enabled: false, bytes: 0 };
|
|
62
|
+
}
|
|
63
|
+
if (bytes > 0xffff_ffff) {
|
|
64
|
+
throw new Error(`JSON_CACHE value '${value}' is too large (max 4GB).`);
|
|
65
|
+
}
|
|
66
|
+
return { enabled: true, bytes: Math.floor(bytes) };
|
|
67
|
+
}
|
|
68
|
+
const JSON_CACHE_CONFIG = parseJSONCacheConfig(process.env["JSON_CACHE"]);
|
|
17
69
|
function needsReferenceLoad(type) {
|
|
18
70
|
return type == "ArrayBuffer" || type == "Int8Array" || type == "Uint8Array" || type == "Uint8ClampedArray" || type == "Int16Array" || type == "Uint16Array" || type == "Int32Array" || type == "Uint32Array" || type == "Int64Array" || type == "Uint64Array" || type == "Float32Array" || type == "Float64Array";
|
|
19
71
|
}
|
|
@@ -23,6 +75,9 @@ function getSerializeCall(type, realName) {
|
|
|
23
75
|
}
|
|
24
76
|
return needsReferenceLoad(type) ? `JSON.__serialize<${type}>(changetype<${type}>(load<usize>(ptr, offsetof<this>(${JSON.stringify(realName)}))));\n` : `JSON.__serialize<${type}>(load<${type}>(ptr, offsetof<this>(${JSON.stringify(realName)})));\n`;
|
|
25
77
|
}
|
|
78
|
+
function isRawType(type) {
|
|
79
|
+
return type == "JSON.Raw" || type == "Raw";
|
|
80
|
+
}
|
|
26
81
|
const CUSTOM_JSON_KINDS = new Set(["any", "string", "number", "object", "array", "boolean", "null", "any | null", "string | null", "number | null", "object | null", "array | null", "boolean | null"]);
|
|
27
82
|
function parseCustomJsonKind(method, decoratorName) {
|
|
28
83
|
const decorator = method.decorators?.find((v) => v.name.text.toLowerCase() == decoratorName);
|
|
@@ -446,7 +501,7 @@ export class JSONTransform extends Visitor {
|
|
|
446
501
|
mem.type = type;
|
|
447
502
|
mem.value = value;
|
|
448
503
|
mem.node = member;
|
|
449
|
-
mem.byteSize =
|
|
504
|
+
mem.byteSize = estimatedSerializedByteSize(mem.type, source, this.parser);
|
|
450
505
|
mem.custom = schema.deps.some((dep) => dep?.name == stripNull(type) && dep.custom);
|
|
451
506
|
this.schema.byteSize += mem.byteSize;
|
|
452
507
|
if (member.decorators) {
|
|
@@ -630,8 +685,14 @@ export class JSONTransform extends Visitor {
|
|
|
630
685
|
sortedMembers.null.push(member);
|
|
631
686
|
if (isString(type) || type == "Date")
|
|
632
687
|
sortedMembers.string.push(member);
|
|
633
|
-
else if (type
|
|
688
|
+
else if (isRawType(type)) {
|
|
689
|
+
sortedMembers.string.push(member);
|
|
690
|
+
sortedMembers.number.push(member);
|
|
691
|
+
sortedMembers.boolean.push(member);
|
|
692
|
+
sortedMembers.null.push(member);
|
|
634
693
|
sortedMembers.object.push(member);
|
|
694
|
+
sortedMembers.array.push(member);
|
|
695
|
+
}
|
|
635
696
|
else if (isBoolean(type) || type.startsWith("JSON.Box<bool"))
|
|
636
697
|
sortedMembers.boolean.push(member);
|
|
637
698
|
else if (isPrimitive(type) || type.startsWith("JSON.Box<") || isEnum(type, this.sources.get(this.schema.node.range.source), this.parser))
|
|
@@ -671,6 +732,11 @@ export class JSONTransform extends Visitor {
|
|
|
671
732
|
const FLOAT_TYPES = ["f32", "f64"];
|
|
672
733
|
const INTEGER_TYPES = [...UNSIGNED_INTEGER_TYPES, ...SIGNED_INTEGER_TYPES];
|
|
673
734
|
const STRING_FIELD_DESERIALIZER = codegenMode === JSONMode.SIMD ? "deserializeStringField_SIMD" : "deserializeStringField_SWAR";
|
|
735
|
+
const getArrayValueType = (type) => {
|
|
736
|
+
if (!type.startsWith("Array<") && !type.startsWith("StaticArray<"))
|
|
737
|
+
return null;
|
|
738
|
+
return stripNull(type.slice(type.indexOf("<") + 1, -1).trim());
|
|
739
|
+
};
|
|
674
740
|
const getDeserializer = (type, srcPtr, outPtr, member, keyOffset = 0, fastPath = false) => {
|
|
675
741
|
const out = [];
|
|
676
742
|
const resolvedType = stripNull(type);
|
|
@@ -706,7 +772,7 @@ export class JSONTransform extends Visitor {
|
|
|
706
772
|
out.push(` if (load<u16>(${valuePtr}) != 0x22) break;`);
|
|
707
773
|
out.push(` let dateEnd = ${valuePtr} + 2;`);
|
|
708
774
|
out.push(` while (dateEnd < srcEnd) {`);
|
|
709
|
-
out.push(" if (load<u16>(dateEnd) == 0x22 &&
|
|
775
|
+
out.push(" if (load<u16>(dateEnd) == 0x22 && JSON.Util.isUnescapedQuote(dateEnd)) break;");
|
|
710
776
|
out.push(" dateEnd += 2;");
|
|
711
777
|
out.push(" }");
|
|
712
778
|
out.push(" if (dateEnd >= srcEnd) break;");
|
|
@@ -751,7 +817,7 @@ export class JSONTransform extends Visitor {
|
|
|
751
817
|
}
|
|
752
818
|
out.push("}");
|
|
753
819
|
}
|
|
754
|
-
else if (resolvedType
|
|
820
|
+
else if (isRawType(resolvedType)) {
|
|
755
821
|
out.push("{");
|
|
756
822
|
out.push(` const valueStart = ${srcPtr};`);
|
|
757
823
|
out.push(" let depth: i32 = 0;");
|
|
@@ -759,7 +825,7 @@ export class JSONTransform extends Visitor {
|
|
|
759
825
|
out.push(` while (${srcPtr} < srcEnd) {`);
|
|
760
826
|
out.push(` const code = load<u16>(${srcPtr});`);
|
|
761
827
|
out.push(" if (inString) {");
|
|
762
|
-
out.push(` if (code == 0x22 &&
|
|
828
|
+
out.push(` if (code == 0x22 && JSON.Util.isUnescapedQuote(${srcPtr})) inString = false;`);
|
|
763
829
|
out.push(` ${srcPtr} += 2;`);
|
|
764
830
|
out.push(" continue;");
|
|
765
831
|
out.push(" }");
|
|
@@ -800,7 +866,24 @@ export class JSONTransform extends Visitor {
|
|
|
800
866
|
}
|
|
801
867
|
else if (resolvedSchema && !resolvedSchema.custom) {
|
|
802
868
|
if (fastPath) {
|
|
803
|
-
out.push("
|
|
869
|
+
out.push("{");
|
|
870
|
+
if (member.node.type.isNullable) {
|
|
871
|
+
out.push(` if (load<u64>(${valuePtr}) == 30399761348886638) {`);
|
|
872
|
+
out.push(` store<${resolvedType}>(${outPtr}, changetype<${resolvedType}>(0), ${fieldOffset});`);
|
|
873
|
+
out.push(` ${srcPtr} = ${valuePtr} + 8;`);
|
|
874
|
+
out.push(" } else {");
|
|
875
|
+
}
|
|
876
|
+
out.push(` let value = load<${resolvedType}>(${outPtr}, ${fieldOffset});`);
|
|
877
|
+
out.push(` if (changetype<usize>(value) == 0) {`);
|
|
878
|
+
out.push(` value = changetype<${resolvedType}>(__new(offsetof<nonnull<${resolvedType}>>(), idof<nonnull<${resolvedType}>>()));`);
|
|
879
|
+
out.push(` store<${resolvedType}>(${outPtr}, value, ${fieldOffset});`);
|
|
880
|
+
out.push(" }");
|
|
881
|
+
out.push(` ${srcPtr} = changetype<nonnull<${resolvedType}>>(value).__DESERIALIZE_FAST<${resolvedType}>(${valuePtr}, srcEnd, value);`);
|
|
882
|
+
out.push(` if (!${srcPtr}) break;`);
|
|
883
|
+
if (member.node.type.isNullable) {
|
|
884
|
+
out.push(" }");
|
|
885
|
+
}
|
|
886
|
+
out.push("}");
|
|
804
887
|
return out;
|
|
805
888
|
}
|
|
806
889
|
out.push("{");
|
|
@@ -830,6 +913,7 @@ export class JSONTransform extends Visitor {
|
|
|
830
913
|
out.push("}");
|
|
831
914
|
}
|
|
832
915
|
else if (resolvedType.startsWith("Array<")) {
|
|
916
|
+
const valueType = getArrayValueType(resolvedType);
|
|
833
917
|
out.push("{");
|
|
834
918
|
if (member.node.type.isNullable) {
|
|
835
919
|
out.push(` if (load<u64>(${valuePtr}) == 30399761348886638) {`);
|
|
@@ -837,23 +921,96 @@ export class JSONTransform extends Visitor {
|
|
|
837
921
|
out.push(` ${srcPtr} = ${valuePtr} + 8;`);
|
|
838
922
|
out.push(" } else {");
|
|
839
923
|
}
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
out.push(
|
|
844
|
-
out.push(`
|
|
845
|
-
out.push(`
|
|
924
|
+
if (fastPath && valueType && ["string", "String"].includes(valueType)) {
|
|
925
|
+
out.push(` if (load<u16>(${valuePtr}) != 0x5b) break;`);
|
|
926
|
+
out.push(` let value = load<${resolvedType}>(${outPtr}, ${fieldOffset});`);
|
|
927
|
+
out.push(" if (changetype<usize>(value) == 0) {");
|
|
928
|
+
out.push(` value = instantiate<nonnull<${resolvedType}>>();`);
|
|
929
|
+
out.push(` store<${resolvedType}>(${outPtr}, value, ${fieldOffset});`);
|
|
930
|
+
out.push(" }");
|
|
931
|
+
out.push(" let index = 0;");
|
|
932
|
+
out.push(` ${srcPtr} = ${valuePtr} + 2;`);
|
|
933
|
+
out.push(` if (load<u16>(${srcPtr}) == 0x5d) {`);
|
|
934
|
+
out.push(" value.length = 0;");
|
|
935
|
+
out.push(` ${srcPtr} += 2;`);
|
|
936
|
+
out.push(" } else while (true) {");
|
|
937
|
+
out.push(' if (index >= value.length) value.push("");');
|
|
938
|
+
out.push(` ${srcPtr} = ${STRING_FIELD_DESERIALIZER}<${valueType}>(${srcPtr}, srcEnd, value.dataStart + ((<usize>index) << alignof<${valueType}>()));`);
|
|
939
|
+
out.push(" index++;");
|
|
940
|
+
out.push(` const code = load<u16>(${srcPtr});`);
|
|
941
|
+
out.push(" if (code == 0x2c) {");
|
|
942
|
+
out.push(` ${srcPtr} += 2;`);
|
|
943
|
+
out.push(" continue;");
|
|
944
|
+
out.push(" }");
|
|
945
|
+
out.push(" if (code == 0x5d) {");
|
|
946
|
+
out.push(" value.length = index;");
|
|
947
|
+
out.push(` ${srcPtr} += 2;`);
|
|
948
|
+
out.push(" break;");
|
|
846
949
|
out.push(" }");
|
|
950
|
+
out.push(" break;");
|
|
951
|
+
out.push(" }");
|
|
952
|
+
if (member.node.type.isNullable) {
|
|
953
|
+
out.push(" }");
|
|
954
|
+
}
|
|
955
|
+
out.push("}");
|
|
956
|
+
return out;
|
|
847
957
|
}
|
|
958
|
+
const valueSchema = valueType ? this.getSchema(valueType) : null;
|
|
959
|
+
if (fastPath && valueType && valueSchema && !valueSchema.custom) {
|
|
960
|
+
out.push(` if (load<u16>(${valuePtr}) != 0x5b) break;`);
|
|
961
|
+
out.push(` let value = load<${resolvedType}>(${outPtr}, ${fieldOffset});`);
|
|
962
|
+
out.push(" if (changetype<usize>(value) == 0) {");
|
|
963
|
+
out.push(` value = instantiate<nonnull<${resolvedType}>>();`);
|
|
964
|
+
out.push(` store<${resolvedType}>(${outPtr}, value, ${fieldOffset});`);
|
|
965
|
+
out.push(" }");
|
|
966
|
+
out.push(" let index = 0;");
|
|
967
|
+
out.push(` ${srcPtr} = ${valuePtr} + 2;`);
|
|
968
|
+
out.push(` if (load<u16>(${srcPtr}) == 0x5d) {`);
|
|
969
|
+
out.push(" value.length = 0;");
|
|
970
|
+
out.push(` ${srcPtr} += 2;`);
|
|
971
|
+
out.push(" } else while (true) {");
|
|
972
|
+
out.push(` let item: ${valueType};`);
|
|
973
|
+
out.push(" if (index < value.length) {");
|
|
974
|
+
out.push(" item = unchecked(value[index]);");
|
|
975
|
+
out.push(" if (changetype<usize>(item) == 0) {");
|
|
976
|
+
out.push(` item = changetype<${valueType}>(__new(offsetof<nonnull<${valueType}>>(), idof<nonnull<${valueType}>>()));`);
|
|
977
|
+
out.push(" unchecked((value[index] = item));");
|
|
978
|
+
out.push(" }");
|
|
979
|
+
out.push(" } else {");
|
|
980
|
+
out.push(` item = changetype<${valueType}>(__new(offsetof<nonnull<${valueType}>>(), idof<nonnull<${valueType}>>()));`);
|
|
981
|
+
out.push(" value.push(item);");
|
|
982
|
+
out.push(" }");
|
|
983
|
+
out.push(` ${srcPtr} = changetype<nonnull<${valueType}>>(item).__DESERIALIZE_FAST<${valueType}>(${srcPtr}, srcEnd, item);`);
|
|
984
|
+
out.push(` if (!${srcPtr}) break;`);
|
|
985
|
+
out.push(" index++;");
|
|
986
|
+
out.push(` const code = load<u16>(${srcPtr});`);
|
|
987
|
+
out.push(" if (code == 0x2c) {");
|
|
988
|
+
out.push(` ${srcPtr} += 2;`);
|
|
989
|
+
out.push(" continue;");
|
|
990
|
+
out.push(" }");
|
|
991
|
+
out.push(" if (code == 0x5d) {");
|
|
992
|
+
out.push(" value.length = index;");
|
|
993
|
+
out.push(` ${srcPtr} += 2;`);
|
|
994
|
+
out.push(" break;");
|
|
995
|
+
out.push(" }");
|
|
996
|
+
out.push(" break;");
|
|
997
|
+
out.push(" }");
|
|
998
|
+
if (member.node.type.isNullable) {
|
|
999
|
+
out.push(" }");
|
|
1000
|
+
}
|
|
1001
|
+
out.push("}");
|
|
1002
|
+
return out;
|
|
1003
|
+
}
|
|
1004
|
+
out.push(` let value = load<${resolvedType}>(${outPtr}, ${fieldOffset});`);
|
|
1005
|
+
out.push(` if (changetype<usize>(value) == 0) {`);
|
|
1006
|
+
out.push(` value = changetype<${resolvedType}>(instantiate<nonnull<${resolvedType}>>());`);
|
|
1007
|
+
out.push(` store<${resolvedType}>(${outPtr}, value, ${fieldOffset});`);
|
|
1008
|
+
out.push(" }");
|
|
1009
|
+
out.push(` if (load<u16>(${valuePtr}) == 0x5b && load<u16>(${valuePtr}, 2) == 0x5d) {`);
|
|
848
1010
|
out.push(" value.length = 0;");
|
|
849
1011
|
out.push(` ${srcPtr} = ${valuePtr} + 4;`);
|
|
850
1012
|
out.push(" } else {");
|
|
851
|
-
|
|
852
|
-
out.push(` ${srcPtr} = deserializeArrayField_SWAR<${resolvedType}>(${valuePtr}, srcEnd, ${outPtr}, ${fieldOffset});`);
|
|
853
|
-
}
|
|
854
|
-
else {
|
|
855
|
-
out.push(` ${srcPtr} = deserializeArrayInto_SWAR<${resolvedType}>(${valuePtr}, srcEnd, load<${resolvedType}>(${outPtr}, ${fieldOffset}));`);
|
|
856
|
-
}
|
|
1013
|
+
out.push(` ${srcPtr} = deserializeArrayInto_SWAR<${resolvedType}>(${valuePtr}, srcEnd, value);`);
|
|
857
1014
|
out.push(` if (!${srcPtr}) break;`);
|
|
858
1015
|
out.push(" }");
|
|
859
1016
|
if (member.node.type.isNullable) {
|
|
@@ -865,6 +1022,16 @@ export class JSONTransform extends Visitor {
|
|
|
865
1022
|
if (member.node.type.isNullable) {
|
|
866
1023
|
out.push(`${srcPtr} = deserializeMapField<${resolvedType}>(${srcPtr}, srcEnd, ${outPtr}, ${fieldOffset});`);
|
|
867
1024
|
}
|
|
1025
|
+
else if (fastPath) {
|
|
1026
|
+
out.push("{");
|
|
1027
|
+
out.push(` let value = load<${resolvedType}>(${outPtr}, ${fieldOffset});`);
|
|
1028
|
+
out.push(" if (changetype<usize>(value) == 0) {");
|
|
1029
|
+
out.push(` value = new ${resolvedType}();`);
|
|
1030
|
+
out.push(` store<${resolvedType}>(${outPtr}, value, ${fieldOffset});`);
|
|
1031
|
+
out.push(" }");
|
|
1032
|
+
out.push(` ${srcPtr} = deserializeMapInto<${resolvedType}>(${srcPtr}, srcEnd, value);`);
|
|
1033
|
+
out.push("}");
|
|
1034
|
+
}
|
|
868
1035
|
else {
|
|
869
1036
|
out.push(`${srcPtr} = deserializeMapInto<${resolvedType}>(${srcPtr}, srcEnd, load<${resolvedType}>(${outPtr}, ${fieldOffset}));`);
|
|
870
1037
|
}
|
|
@@ -874,6 +1041,16 @@ export class JSONTransform extends Visitor {
|
|
|
874
1041
|
if (member.node.type.isNullable) {
|
|
875
1042
|
out.push(`${srcPtr} = deserializeSetField<${resolvedType}>(${srcPtr}, srcEnd, ${outPtr}, ${fieldOffset});`);
|
|
876
1043
|
}
|
|
1044
|
+
else if (fastPath) {
|
|
1045
|
+
out.push("{");
|
|
1046
|
+
out.push(` let value = load<${resolvedType}>(${outPtr}, ${fieldOffset});`);
|
|
1047
|
+
out.push(" if (changetype<usize>(value) == 0) {");
|
|
1048
|
+
out.push(` value = new ${resolvedType}();`);
|
|
1049
|
+
out.push(` store<${resolvedType}>(${outPtr}, value, ${fieldOffset});`);
|
|
1050
|
+
out.push(" }");
|
|
1051
|
+
out.push(` ${srcPtr} = deserializeSetInto<${resolvedType}>(${srcPtr}, srcEnd, value);`);
|
|
1052
|
+
out.push("}");
|
|
1053
|
+
}
|
|
877
1054
|
else {
|
|
878
1055
|
out.push(`${srcPtr} = deserializeSetInto<${resolvedType}>(${srcPtr}, srcEnd, load<${resolvedType}>(${outPtr}, ${fieldOffset}));`);
|
|
879
1056
|
}
|
|
@@ -893,6 +1070,7 @@ export class JSONTransform extends Visitor {
|
|
|
893
1070
|
};
|
|
894
1071
|
indent = " ";
|
|
895
1072
|
DESERIALIZE_FAST += indent + "const start = srcStart;\n";
|
|
1073
|
+
DESERIALIZE_FAST += indent + "const dst = changetype<usize>(out);\n";
|
|
896
1074
|
DESERIALIZE_FAST += indent + "do {\n";
|
|
897
1075
|
indent += " ";
|
|
898
1076
|
if (supportsFastOptionalPath) {
|
|
@@ -910,8 +1088,8 @@ export class JSONTransform extends Visitor {
|
|
|
910
1088
|
const nextKeyOffset = nextKeySection.length << 1;
|
|
911
1089
|
const resolvedType = stripNull(member.type);
|
|
912
1090
|
const inlineStringValue = ["string", "String"].includes(resolvedType);
|
|
913
|
-
const deserializerFirst = getDeserializer(member.type, "srcStart", "
|
|
914
|
-
const deserializerNext = getDeserializer(member.type, "srcStart", "
|
|
1091
|
+
const deserializerFirst = getDeserializer(member.type, "srcStart", "dst", member, inlineStringValue ? firstKeyOffset : 0, true);
|
|
1092
|
+
const deserializerNext = getDeserializer(member.type, "srcStart", "dst", member, inlineStringValue ? nextKeyOffset : 0, true);
|
|
915
1093
|
const isOptional = member.flags.has(PropertyFlags.OmitNull) || member.flags.has(PropertyFlags.OmitIf);
|
|
916
1094
|
if (!deserializerFirst.length || !deserializerNext.length) {
|
|
917
1095
|
DESERIALIZE_FAST += indent + "break;\n\n";
|
|
@@ -973,7 +1151,7 @@ export class JSONTransform extends Visitor {
|
|
|
973
1151
|
if (!inlineStringValue) {
|
|
974
1152
|
DESERIALIZE_FAST += indent + `srcStart += ${keyOffset};\n\n`;
|
|
975
1153
|
}
|
|
976
|
-
const deserializer = getDeserializer(member.type, "srcStart", "
|
|
1154
|
+
const deserializer = getDeserializer(member.type, "srcStart", "dst", member, inlineStringValue ? keyOffset : 0, true);
|
|
977
1155
|
if (!deserializer.length) {
|
|
978
1156
|
DESERIALIZE_FAST += indent + "break;\n\n";
|
|
979
1157
|
continue;
|
|
@@ -986,9 +1164,14 @@ export class JSONTransform extends Visitor {
|
|
|
986
1164
|
DESERIALIZE_FAST += indent + "return srcStart;\n";
|
|
987
1165
|
indent = indent.slice(0, -2);
|
|
988
1166
|
DESERIALIZE_FAST += indent + "} while (false);\n\n";
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
1167
|
+
if (THROW_FAST_PATH) {
|
|
1168
|
+
DESERIALIZE_FAST += indent + "const failAt = srcStart ? srcStart : start;\n";
|
|
1169
|
+
DESERIALIZE_FAST += indent + "const failEnd = failAt + 160 < srcEnd ? failAt + 160 : srcEnd;\n";
|
|
1170
|
+
DESERIALIZE_FAST += indent + `throw new Error("Fast path failed for ${this.schema.name} at char offset " + ((failAt - start) >> 1).toString() + " near: " + JSON.Util.ptrToStr(failAt, failEnd));`;
|
|
1171
|
+
}
|
|
1172
|
+
else {
|
|
1173
|
+
DESERIALIZE_FAST += indent + "return 0;";
|
|
1174
|
+
}
|
|
992
1175
|
indent = indent.slice(0, -2);
|
|
993
1176
|
DESERIALIZE_FAST += indent + "}";
|
|
994
1177
|
DESERIALIZE += indent + " let keyStart: usize = 0;\n";
|
|
@@ -1007,7 +1190,7 @@ export class JSONTransform extends Visitor {
|
|
|
1007
1190
|
DESERIALIZE += indent + " let code = load<u16>(srcStart);\n";
|
|
1008
1191
|
DESERIALIZE += indent + " while (JSON.Util.isSpace(code)) code = load<u16>(srcStart += 2);\n";
|
|
1009
1192
|
DESERIALIZE += indent + " if (keyStart == 0) {\n";
|
|
1010
|
-
DESERIALIZE += indent + " if (code == 34 &&
|
|
1193
|
+
DESERIALIZE += indent + " if (code == 34 && JSON.Util.isUnescapedQuote(srcStart)) {\n";
|
|
1011
1194
|
DESERIALIZE += indent + " if (isKey) {\n";
|
|
1012
1195
|
DESERIALIZE += indent + " keyStart = lastIndex;\n";
|
|
1013
1196
|
DESERIALIZE += indent + " keyEnd = srcStart;\n";
|
|
@@ -1112,7 +1295,7 @@ export class JSONTransform extends Visitor {
|
|
|
1112
1295
|
DESERIALIZE += " srcStart += 2;\n";
|
|
1113
1296
|
DESERIALIZE += " while (srcStart < srcEnd) {\n";
|
|
1114
1297
|
DESERIALIZE += " const code = load<u16>(srcStart);\n";
|
|
1115
|
-
DESERIALIZE += " if (code == 34 &&
|
|
1298
|
+
DESERIALIZE += " if (code == 34 && JSON.Util.isUnescapedQuote(srcStart)) {\n";
|
|
1116
1299
|
if (DEBUG > 1)
|
|
1117
1300
|
DESERIALIZE += ' console.log("Value (string, ' + ++id + '): " + JSON.Util.ptrToStr(lastIndex, srcStart + 2));';
|
|
1118
1301
|
generateGroups(sortedMembers.string, (group) => {
|
|
@@ -1211,7 +1394,7 @@ export class JSONTransform extends Visitor {
|
|
|
1211
1394
|
DESERIALIZE += " const code = load<u16>(srcStart);\n";
|
|
1212
1395
|
DESERIALIZE += " if (code == 34) {\n";
|
|
1213
1396
|
DESERIALIZE += " srcStart += 2;\n";
|
|
1214
|
-
DESERIALIZE += " while (!(load<u16>(srcStart) == 34 &&
|
|
1397
|
+
DESERIALIZE += " while (!(load<u16>(srcStart) == 34 && JSON.Util.isUnescapedQuote(srcStart))) srcStart += 2;\n";
|
|
1215
1398
|
DESERIALIZE += " } else if (code == 125) {\n";
|
|
1216
1399
|
DESERIALIZE += " if (--depth == 0) {\n";
|
|
1217
1400
|
DESERIALIZE += " srcStart += 2;\n";
|
|
@@ -1265,7 +1448,7 @@ export class JSONTransform extends Visitor {
|
|
|
1265
1448
|
DESERIALIZE += " const code = load<u16>(srcStart);\n";
|
|
1266
1449
|
DESERIALIZE += " if (code == 34) {\n";
|
|
1267
1450
|
DESERIALIZE += " srcStart += 2;\n";
|
|
1268
|
-
DESERIALIZE += " while (!(load<u16>(srcStart) == 34 &&
|
|
1451
|
+
DESERIALIZE += " while (!(load<u16>(srcStart) == 34 && JSON.Util.isUnescapedQuote(srcStart))) srcStart += 2;\n";
|
|
1269
1452
|
DESERIALIZE += " } else if (code == 93) {\n";
|
|
1270
1453
|
DESERIALIZE += " if (--depth == 0) {\n";
|
|
1271
1454
|
DESERIALIZE += " srcStart += 2;\n";
|
|
@@ -1321,16 +1504,11 @@ export class JSONTransform extends Visitor {
|
|
|
1321
1504
|
const first = group[0];
|
|
1322
1505
|
const fName = first.alias || first.name;
|
|
1323
1506
|
DESERIALIZE += indent + " if (" + (first.generic ? "isBoolean<" + first.type + ">() && " : "") + getComparison(fName) + ") { // " + fName + "\n";
|
|
1324
|
-
if (
|
|
1325
|
-
DESERIALIZE +=
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
">(changetype<usize>(out), changetype<" +
|
|
1330
|
-
first.type +
|
|
1331
|
-
">(JSON.Box.from<bool>(true)), offsetof<this>(" +
|
|
1332
|
-
JSON.stringify(first.name) +
|
|
1333
|
-
"));\n";
|
|
1507
|
+
if (isRawType(first.type)) {
|
|
1508
|
+
DESERIALIZE += indent + " store<" + first.type + ">(changetype<usize>(out), JSON.__deserialize<" + first.type + ">(srcStart - 8, srcStart), offsetof<this>(" + JSON.stringify(first.name) + "));\n";
|
|
1509
|
+
}
|
|
1510
|
+
else if (first.type.startsWith("JSON.Box<bool") || first.type.startsWith("JSON.Box<boolean") || first.type.startsWith("Box<bool") || first.type.startsWith("Box<boolean")) {
|
|
1511
|
+
DESERIALIZE += indent + " store<" + first.type + ">(changetype<usize>(out), changetype<" + first.type + ">(JSON.Box.from<bool>(true)), offsetof<this>(" + JSON.stringify(first.name) + "));\n";
|
|
1334
1512
|
}
|
|
1335
1513
|
else {
|
|
1336
1514
|
DESERIALIZE += indent + " store<boolean>(changetype<usize>(out), true, offsetof<this>(" + JSON.stringify(first.name) + "));\n";
|
|
@@ -1343,16 +1521,11 @@ export class JSONTransform extends Visitor {
|
|
|
1343
1521
|
const mem = group[i];
|
|
1344
1522
|
const memName = mem.alias || mem.name;
|
|
1345
1523
|
DESERIALIZE += indent + " else if (" + (mem.generic ? "isBoolean<" + mem.type + ">() && " : "") + getComparison(memName) + ") { // " + memName + "\n";
|
|
1346
|
-
if (
|
|
1347
|
-
DESERIALIZE +=
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
">(changetype<usize>(out), changetype<" +
|
|
1352
|
-
mem.type +
|
|
1353
|
-
">(JSON.Box.from<bool>(true)), offsetof<this>(" +
|
|
1354
|
-
JSON.stringify(mem.name) +
|
|
1355
|
-
"));\n";
|
|
1524
|
+
if (isRawType(mem.type)) {
|
|
1525
|
+
DESERIALIZE += indent + " store<" + mem.type + ">(changetype<usize>(out), JSON.__deserialize<" + mem.type + ">(srcStart - 8, srcStart), offsetof<this>(" + JSON.stringify(mem.name) + "));\n";
|
|
1526
|
+
}
|
|
1527
|
+
else if (mem.type.startsWith("JSON.Box<bool") || mem.type.startsWith("JSON.Box<boolean") || mem.type.startsWith("Box<bool") || mem.type.startsWith("Box<boolean")) {
|
|
1528
|
+
DESERIALIZE += indent + " store<" + mem.type + ">(changetype<usize>(out), changetype<" + mem.type + ">(JSON.Box.from<bool>(true)), offsetof<this>(" + JSON.stringify(mem.name) + "));\n";
|
|
1356
1529
|
}
|
|
1357
1530
|
else {
|
|
1358
1531
|
DESERIALIZE += indent + " store<boolean>(changetype<usize>(out), true, offsetof<this>(" + JSON.stringify(mem.name) + "));\n";
|
|
@@ -1391,16 +1564,11 @@ export class JSONTransform extends Visitor {
|
|
|
1391
1564
|
const first = group[0];
|
|
1392
1565
|
const fName = first.alias || first.name;
|
|
1393
1566
|
DESERIALIZE += indent + " if (" + (first.generic ? "isBoolean<" + first.type + ">() && " : "") + getComparison(fName) + ") { // " + fName + "\n";
|
|
1394
|
-
if (
|
|
1395
|
-
DESERIALIZE +=
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
">(changetype<usize>(out), changetype<" +
|
|
1400
|
-
first.type +
|
|
1401
|
-
">(JSON.Box.from<bool>(false)), offsetof<this>(" +
|
|
1402
|
-
JSON.stringify(first.name) +
|
|
1403
|
-
"));\n";
|
|
1567
|
+
if (isRawType(first.type)) {
|
|
1568
|
+
DESERIALIZE += indent + " store<" + first.type + ">(changetype<usize>(out), JSON.__deserialize<" + first.type + ">(srcStart - 10, srcStart), offsetof<this>(" + JSON.stringify(first.name) + "));\n";
|
|
1569
|
+
}
|
|
1570
|
+
else if (first.type.startsWith("JSON.Box<bool") || first.type.startsWith("JSON.Box<boolean") || first.type.startsWith("Box<bool") || first.type.startsWith("Box<boolean")) {
|
|
1571
|
+
DESERIALIZE += indent + " store<" + first.type + ">(changetype<usize>(out), changetype<" + first.type + ">(JSON.Box.from<bool>(false)), offsetof<this>(" + JSON.stringify(first.name) + "));\n";
|
|
1404
1572
|
}
|
|
1405
1573
|
else {
|
|
1406
1574
|
DESERIALIZE += indent + " store<boolean>(changetype<usize>(out), false, offsetof<this>(" + JSON.stringify(first.name) + "));\n";
|
|
@@ -1413,16 +1581,11 @@ export class JSONTransform extends Visitor {
|
|
|
1413
1581
|
const mem = group[i];
|
|
1414
1582
|
const memName = mem.alias || mem.name;
|
|
1415
1583
|
DESERIALIZE += indent + " else if (" + (mem.generic ? "isBoolean<" + mem.type + ">() && " : "") + getComparison(memName) + ") { // " + memName + "\n";
|
|
1416
|
-
if (
|
|
1417
|
-
DESERIALIZE +=
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
">(changetype<usize>(out), changetype<" +
|
|
1422
|
-
mem.type +
|
|
1423
|
-
">(JSON.Box.from<bool>(false)), offsetof<this>(" +
|
|
1424
|
-
JSON.stringify(mem.name) +
|
|
1425
|
-
"));\n";
|
|
1584
|
+
if (isRawType(mem.type)) {
|
|
1585
|
+
DESERIALIZE += indent + " store<" + mem.type + ">(changetype<usize>(out), JSON.__deserialize<" + mem.type + ">(srcStart - 10, srcStart), offsetof<this>(" + JSON.stringify(mem.name) + "));\n";
|
|
1586
|
+
}
|
|
1587
|
+
else if (mem.type.startsWith("JSON.Box<bool") || mem.type.startsWith("JSON.Box<boolean") || mem.type.startsWith("Box<bool") || mem.type.startsWith("Box<boolean")) {
|
|
1588
|
+
DESERIALIZE += indent + " store<" + mem.type + ">(changetype<usize>(out), changetype<" + mem.type + ">(JSON.Box.from<bool>(false)), offsetof<this>(" + JSON.stringify(mem.name) + "));\n";
|
|
1426
1589
|
}
|
|
1427
1590
|
else {
|
|
1428
1591
|
DESERIALIZE += indent + " store<boolean>(changetype<usize>(out), false, offsetof<this>(" + JSON.stringify(mem.name) + "));\n";
|
|
@@ -1460,7 +1623,12 @@ export class JSONTransform extends Visitor {
|
|
|
1460
1623
|
const first = group[0];
|
|
1461
1624
|
const fName = first.alias || first.name;
|
|
1462
1625
|
DESERIALIZE += indent + " if (" + (first.generic ? "isNullable<" + first.type + ">() && " : "") + getComparison(fName) + ") { // " + fName + "\n";
|
|
1463
|
-
|
|
1626
|
+
if (isRawType(first.type) && !first.node.type.isNullable) {
|
|
1627
|
+
DESERIALIZE += indent + " store<" + first.type + ">(changetype<usize>(out), JSON.__deserialize<" + first.type + ">(srcStart - 8, srcStart), offsetof<this>(" + JSON.stringify(first.name) + "));\n";
|
|
1628
|
+
}
|
|
1629
|
+
else {
|
|
1630
|
+
DESERIALIZE += indent + " store<usize>(changetype<usize>(out), 0, offsetof<this>(" + JSON.stringify(first.name) + "));\n";
|
|
1631
|
+
}
|
|
1464
1632
|
DESERIALIZE += indent + " srcStart += 2;\n";
|
|
1465
1633
|
DESERIALIZE += indent + " keyStart = 0;\n";
|
|
1466
1634
|
DESERIALIZE += indent + " break;\n";
|
|
@@ -1469,7 +1637,12 @@ export class JSONTransform extends Visitor {
|
|
|
1469
1637
|
const mem = group[i];
|
|
1470
1638
|
const memName = mem.alias || mem.name;
|
|
1471
1639
|
DESERIALIZE += indent + " else if (" + (mem.generic ? "isNullable<" + mem.type + ">() && " : "") + getComparison(memName) + ") { // " + memName + "\n";
|
|
1472
|
-
|
|
1640
|
+
if (isRawType(mem.type) && !mem.node.type.isNullable) {
|
|
1641
|
+
DESERIALIZE += indent + " store<" + mem.type + ">(changetype<usize>(out), JSON.__deserialize<" + mem.type + ">(srcStart - 8, srcStart), offsetof<this>(" + JSON.stringify(mem.name) + "));\n";
|
|
1642
|
+
}
|
|
1643
|
+
else {
|
|
1644
|
+
DESERIALIZE += indent + " store<usize>(changetype<usize>(out), 0, offsetof<this>(" + JSON.stringify(mem.name) + "));\n";
|
|
1645
|
+
}
|
|
1473
1646
|
DESERIALIZE += indent + " srcStart += 2;\n";
|
|
1474
1647
|
DESERIALIZE += indent + " keyStart = 0;\n";
|
|
1475
1648
|
DESERIALIZE += indent + " break;\n";
|
|
@@ -1793,8 +1966,9 @@ export default class Transformer extends Transform {
|
|
|
1793
1966
|
}
|
|
1794
1967
|
}
|
|
1795
1968
|
program.registerConstantInteger("JSON_MODE", Type.i32, i64_new(MODE));
|
|
1796
|
-
if (
|
|
1969
|
+
if (JSON_CACHE_CONFIG.enabled) {
|
|
1797
1970
|
program.registerConstantInteger("JSON_CACHE", Type.bool, i64_one);
|
|
1971
|
+
program.registerConstantInteger("JSON_CACHE_SIZE", Type.u32, i64_new(JSON_CACHE_CONFIG.bytes));
|
|
1798
1972
|
}
|
|
1799
1973
|
}
|
|
1800
1974
|
afterParse(parser) {
|
|
@@ -1961,15 +2135,56 @@ function sizeof(type) {
|
|
|
1961
2135
|
return 20;
|
|
1962
2136
|
else if (type == "i32")
|
|
1963
2137
|
return 22;
|
|
2138
|
+
else if (type == "usize")
|
|
2139
|
+
return 40;
|
|
2140
|
+
else if (type == "isize")
|
|
2141
|
+
return 42;
|
|
1964
2142
|
else if (type == "u64")
|
|
1965
2143
|
return 40;
|
|
1966
2144
|
else if (type == "i64")
|
|
1967
|
-
return
|
|
2145
|
+
return 42;
|
|
2146
|
+
else if (type == "f32")
|
|
2147
|
+
return 34;
|
|
2148
|
+
else if (type == "f64")
|
|
2149
|
+
return 66;
|
|
1968
2150
|
else if (type == "bool" || type == "boolean")
|
|
1969
2151
|
return 10;
|
|
1970
2152
|
else
|
|
1971
2153
|
return 0;
|
|
1972
2154
|
}
|
|
2155
|
+
function estimatedSerializedByteSize(type, source, parser) {
|
|
2156
|
+
const trimmed = type.trim();
|
|
2157
|
+
const baseType = stripNull(trimmed);
|
|
2158
|
+
const nullable = trimmed != baseType;
|
|
2159
|
+
let estimated = sizeof(baseType);
|
|
2160
|
+
if (estimated == 0) {
|
|
2161
|
+
if (isEnum(baseType, source, parser)) {
|
|
2162
|
+
estimated = 22;
|
|
2163
|
+
}
|
|
2164
|
+
else if (baseType == "Date") {
|
|
2165
|
+
estimated = 52;
|
|
2166
|
+
}
|
|
2167
|
+
else if (isString(baseType)) {
|
|
2168
|
+
estimated = 4;
|
|
2169
|
+
}
|
|
2170
|
+
else if (isArray(baseType) || baseType.startsWith("Map<")) {
|
|
2171
|
+
estimated = 4;
|
|
2172
|
+
}
|
|
2173
|
+
else if (baseType == "JSON.Obj" || baseType == "Obj" || baseType == "JSON.Raw" || baseType == "Raw" || baseType == "JSON.Value" || baseType == "Value") {
|
|
2174
|
+
estimated = 4;
|
|
2175
|
+
}
|
|
2176
|
+
else if (baseType == "ArrayBuffer" || needsReferenceLoad(baseType)) {
|
|
2177
|
+
estimated = 4;
|
|
2178
|
+
}
|
|
2179
|
+
else {
|
|
2180
|
+
estimated = 4;
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
if (nullable) {
|
|
2184
|
+
estimated = Math.max(estimated, 8);
|
|
2185
|
+
}
|
|
2186
|
+
return estimated;
|
|
2187
|
+
}
|
|
1973
2188
|
function isPrimitive(type) {
|
|
1974
2189
|
const primitiveTypes = ["u8", "u16", "u32", "u64", "i8", "i16", "i32", "i64", "f32", "f64", "bool", "boolean"];
|
|
1975
2190
|
return primitiveTypes.some((v) => type.startsWith(v));
|