json-as 1.4.0 → 1.6.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.
Files changed (108) hide show
  1. package/CHANGELOG.md +54 -29
  2. package/README.md +126 -33
  3. package/assembly/custom/chars.ts +39 -78
  4. package/assembly/deserialize/error.ts +21 -0
  5. package/assembly/deserialize/index/arbitrary.ts +50 -11
  6. package/assembly/deserialize/index/array.ts +2 -1
  7. package/assembly/deserialize/index/bool.ts +1 -1
  8. package/assembly/deserialize/index/float.ts +2 -4
  9. package/assembly/deserialize/index/integer.ts +2 -4
  10. package/assembly/deserialize/index/object.ts +9 -1
  11. package/assembly/deserialize/index/string.ts +39 -7
  12. package/assembly/deserialize/index/unsigned.ts +2 -4
  13. package/assembly/deserialize/index.ts +0 -1
  14. package/assembly/deserialize/naive/array/arbitrary.ts +3 -2
  15. package/assembly/deserialize/naive/array/array.ts +1 -0
  16. package/assembly/deserialize/naive/array/bool.ts +21 -11
  17. package/assembly/deserialize/naive/array/box.ts +13 -33
  18. package/assembly/deserialize/naive/array/float.ts +23 -13
  19. package/assembly/deserialize/naive/array/integer.ts +34 -22
  20. package/assembly/deserialize/naive/array/map.ts +1 -1
  21. package/assembly/deserialize/naive/array/object.ts +2 -1
  22. package/assembly/deserialize/naive/array/string.ts +29 -16
  23. package/assembly/deserialize/naive/array/struct.ts +19 -1
  24. package/assembly/deserialize/naive/bool.ts +18 -10
  25. package/assembly/deserialize/naive/date.ts +1 -2
  26. package/assembly/deserialize/naive/float.ts +3 -9
  27. package/assembly/deserialize/naive/integer.ts +1 -2
  28. package/assembly/deserialize/naive/map.ts +342 -43
  29. package/assembly/deserialize/naive/object.ts +250 -55
  30. package/assembly/deserialize/naive/raw.ts +10 -2
  31. package/assembly/deserialize/naive/set.ts +2 -5
  32. package/assembly/deserialize/naive/staticarray.ts +4 -4
  33. package/assembly/deserialize/naive/string.ts +46 -60
  34. package/assembly/deserialize/naive/unsigned.ts +1 -2
  35. package/assembly/deserialize/parseMode.ts +216 -0
  36. package/assembly/deserialize/simd/array/integer.ts +8 -10
  37. package/assembly/deserialize/simd/float.ts +38 -34
  38. package/assembly/deserialize/simd/integer.ts +2 -7
  39. package/assembly/deserialize/simd/string.ts +84 -39
  40. package/assembly/deserialize/string-validation.ts +34 -0
  41. package/assembly/deserialize/swar/array/arbitrary.ts +1 -2
  42. package/assembly/deserialize/swar/array/array.ts +64 -15
  43. package/assembly/deserialize/swar/array/bool.ts +15 -5
  44. package/assembly/deserialize/swar/array/box.ts +1 -2
  45. package/assembly/deserialize/swar/array/float.ts +399 -30
  46. package/assembly/deserialize/swar/array/generic.ts +13 -4
  47. package/assembly/deserialize/swar/array/integer.ts +29 -78
  48. package/assembly/deserialize/swar/array/map.ts +1 -2
  49. package/assembly/deserialize/swar/array/object.ts +12 -4
  50. package/assembly/deserialize/swar/array/raw.ts +1 -2
  51. package/assembly/deserialize/swar/array/shared.ts +7 -14
  52. package/assembly/deserialize/swar/array/string.ts +37 -13
  53. package/assembly/deserialize/swar/array/struct.ts +109 -21
  54. package/assembly/deserialize/swar/array.ts +1 -3
  55. package/assembly/deserialize/swar/float.ts +23 -21
  56. package/assembly/deserialize/swar/integer.ts +2 -7
  57. package/assembly/deserialize/swar/string.ts +81 -124
  58. package/assembly/deserialize/swar/typedarray.ts +4 -4
  59. package/assembly/index.d.ts +35 -24
  60. package/assembly/index.ts +1775 -281
  61. package/assembly/serialize/index/arbitrary.ts +70 -4
  62. package/assembly/serialize/index/bool.ts +1 -1
  63. package/assembly/serialize/index/float.ts +1 -5
  64. package/assembly/serialize/index/integer.ts +1 -1
  65. package/assembly/serialize/index/jsonarray.ts +51 -0
  66. package/assembly/serialize/index/object.ts +42 -3
  67. package/assembly/serialize/index/string.ts +1 -2
  68. package/assembly/serialize/index/typedarray.ts +1 -2
  69. package/assembly/serialize/index.ts +1 -0
  70. package/assembly/serialize/naive/array.ts +87 -34
  71. package/assembly/serialize/naive/bool.ts +0 -1
  72. package/assembly/serialize/naive/float.ts +15 -29
  73. package/assembly/serialize/naive/integer.ts +1 -5
  74. package/assembly/serialize/naive/map.ts +37 -0
  75. package/assembly/serialize/naive/raw.ts +1 -2
  76. package/assembly/serialize/naive/set.ts +0 -5
  77. package/assembly/serialize/naive/staticarray.ts +0 -6
  78. package/assembly/serialize/naive/string.ts +1 -10
  79. package/assembly/serialize/naive/typedarray.ts +0 -7
  80. package/assembly/serialize/simd/string.ts +1 -3
  81. package/assembly/serialize/swar/string.ts +1 -2
  82. package/assembly/util/atoi-fast.ts +4 -14
  83. package/assembly/util/bytes.ts +1 -2
  84. package/assembly/util/eisel-lemire.ts +179 -0
  85. package/assembly/util/idofd.ts +1 -2
  86. package/assembly/util/isSpace.ts +1 -2
  87. package/assembly/util/itoa-fast.ts +6 -9
  88. package/assembly/util/nextPowerOf2.ts +1 -2
  89. package/assembly/util/parsefloat-fast.ts +3 -5
  90. package/assembly/util/prettyWhitespaceSimd.ts +69 -0
  91. package/assembly/util/ptrToStr.ts +1 -2
  92. package/assembly/util/scanValueEndSimd.ts +83 -22
  93. package/assembly/util/scanValueEndSwar.ts +67 -25
  94. package/assembly/util/scientific.ts +5 -8
  95. package/assembly/util/snp.ts +1 -2
  96. package/assembly/util/swar-int.ts +5 -10
  97. package/assembly/util/swar.ts +2 -4
  98. package/assembly/util/validateJson.ts +338 -0
  99. package/lib/as-bs.ts +51 -51
  100. package/package.json +25 -8
  101. package/transform/lib/index.d.ts +6 -0
  102. package/transform/lib/index.js +874 -171
  103. package/transform/lib/types.d.ts +3 -1
  104. package/transform/lib/types.js +6 -0
  105. package/assembly/deserialize/index/struct.ts +0 -1
  106. package/assembly/deserialize/naive/struct.ts +0 -21
  107. package/assembly/util/dragonbox-cache.ts +0 -445
  108. package/assembly/util/dragonbox.ts +0 -652
@@ -1,28 +1,28 @@
1
1
  import { atoi, isSpace } from "../../../util";
2
2
  import { COMMA, BRACKET_LEFT, BRACKET_RIGHT } from "../../../custom/chars";
3
+ import { markProductionParseError } from "../../error";
3
4
 
4
5
  // Strict RFC 8259 integer-token check over [start, end): optional minus (signed
5
- // types only), then a lone `0` or [1-9] digits no leading zeros, fraction,
6
- // exponent, or trailing garbage. Throws otherwise.
7
- function validateJSONInteger(start: usize, end: usize, signed: bool): void {
6
+ // types only), then a lone `0` or [1-9] digits - no leading zeros, fraction,
7
+ // exponent, or trailing garbage. Returns false otherwise.
8
+ function validateJSONInteger(start: usize, end: usize, signed: bool): bool {
8
9
  let ptr = start;
9
10
  if (ptr < end && load<u16>(ptr) == 45) {
10
- if (!signed) throw new Error("Invalid JSON number: minus on unsigned");
11
+ if (!signed) return false;
11
12
  ptr += 2;
12
13
  }
13
- if (ptr >= end) throw new Error("Invalid JSON number: expected digit");
14
+ if (ptr >= end) return false;
14
15
  const first = load<u16>(ptr);
15
16
  if (first == 48) {
16
17
  ptr += 2;
17
- if (ptr < end && <u32>(load<u16>(ptr) - 48) <= 9)
18
- throw new Error("Invalid JSON number: leading zero");
18
+ if (ptr < end && <u32>(load<u16>(ptr) - 48) <= 9) return false;
19
19
  } else if (<u32>(first - 48) <= 9) {
20
20
  ptr += 2;
21
21
  while (ptr < end && <u32>(load<u16>(ptr) - 48) <= 9) ptr += 2;
22
22
  } else {
23
- throw new Error("Invalid JSON number: expected digit");
23
+ return false;
24
24
  }
25
- if (ptr != end) throw new Error("Invalid JSON number: not an integer");
25
+ return ptr == end;
26
26
  }
27
27
 
28
28
  /**
@@ -30,7 +30,8 @@ function validateJSONInteger(start: usize, end: usize, signed: bool): void {
30
30
  *
31
31
  * Enforces RFC 8259 array structure: `[`-framed, single-comma separated, no
32
32
  * leading / trailing / doubled commas, and each element must be a valid JSON
33
- * integer (no leading zeros, fraction, or exponent). Throws on any deviation.
33
+ * integer (no leading zeros, fraction, or exponent). Returns the shared failure
34
+ * sentinel on any deviation.
34
35
  */
35
36
  export function deserializeIntegerArray_NAIVE<T extends number[]>(
36
37
  srcStart: usize,
@@ -42,12 +43,15 @@ export function deserializeIntegerArray_NAIVE<T extends number[]>(
42
43
  );
43
44
  out.length = 0; // dst may arrive pre-sized; re-parse from empty via push
44
45
 
45
- while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
46
46
  while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
47
- if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT)
48
- throw new Error("Invalid JSON array: expected '['");
49
- if (load<u16>(srcEnd - 2) != BRACKET_RIGHT)
50
- throw new Error("Invalid JSON array: expected ']'");
47
+ if (
48
+ srcStart >= srcEnd ||
49
+ load<u16>(srcStart) != BRACKET_LEFT ||
50
+ load<u16>(srcEnd - 2) != BRACKET_RIGHT
51
+ ) {
52
+ markProductionParseError();
53
+ return changetype<T>(0);
54
+ }
51
55
  srcStart += 2; // past '['
52
56
  srcEnd -= 2; // before ']'
53
57
 
@@ -62,19 +66,27 @@ export function deserializeIntegerArray_NAIVE<T extends number[]>(
62
66
  if (c == COMMA || isSpace(c)) break;
63
67
  srcStart += 2;
64
68
  }
65
- if (srcStart == tokenStart)
66
- throw new Error("Invalid JSON array: missing value");
67
- validateJSONInteger(tokenStart, srcStart, signed);
69
+ if (
70
+ srcStart == tokenStart ||
71
+ !validateJSONInteger(tokenStart, srcStart, signed)
72
+ ) {
73
+ markProductionParseError();
74
+ return changetype<T>(0);
75
+ }
68
76
  out.push(atoi<valueof<T>>(tokenStart, srcStart));
69
77
 
70
78
  while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
71
79
  if (srcStart >= srcEnd) break;
72
- if (load<u16>(srcStart) != COMMA)
73
- throw new Error("Invalid JSON array: expected ',' or ']'");
80
+ if (load<u16>(srcStart) != COMMA) {
81
+ markProductionParseError();
82
+ return changetype<T>(0);
83
+ }
74
84
  srcStart += 2;
75
85
  while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
76
- if (srcStart >= srcEnd)
77
- throw new Error("Invalid JSON array: trailing comma");
86
+ if (srcStart >= srcEnd) {
87
+ markProductionParseError();
88
+ return changetype<T>(0);
89
+ }
78
90
  }
79
91
 
80
92
  return out;
@@ -29,7 +29,7 @@ export function deserializeMapArray<T extends Map<any, any>[]>(
29
29
  );
30
30
 
31
31
  // Each `{...}` map element is parsed in a single pass via deserializeMapBody,
32
- // which reports where it ended no separate scan to find the closing brace.
32
+ // which reports where it ended - no separate scan to find the closing brace.
33
33
  while (srcStart < srcEnd) {
34
34
  if (load<u16>(srcStart) == BRACE_LEFT) {
35
35
  const m = instantiate<valueof<T>>();
@@ -29,11 +29,12 @@ export function deserializeObjectArray<T extends unknown[]>(
29
29
  );
30
30
 
31
31
  // Each `{...}` element is parsed in a single pass via parseObjectBody, which
32
- // reports where it ended no separate scan to find the closing brace.
32
+ // reports where it ended - no separate scan to find the closing brace.
33
33
  while (srcStart < srcEnd) {
34
34
  if (load<u16>(srcStart) == BRACE_LEFT) {
35
35
  const obj = new JSON.Obj();
36
36
  srcStart = parseObjectBody(obj, srcStart + 2, srcEnd);
37
+ if (srcStart == 0) return changetype<T>(0);
37
38
  out.push(changetype<valueof<T>>(changetype<usize>(obj)));
38
39
  } else {
39
40
  srcStart += 2;
@@ -1,4 +1,3 @@
1
- import { JSON } from "../../..";
2
1
  import {
3
2
  COMMA,
4
3
  BRACKET_LEFT,
@@ -7,6 +6,8 @@ import {
7
6
  QUOTE,
8
7
  } from "../../../custom/chars";
9
8
  import { isSpace, scanStringEnd } from "../../../util";
9
+ import { markProductionParseError } from "../../error";
10
+ import { deserializeString } from "../../index/string";
10
11
 
11
12
  /**
12
13
  * Strict string-array deserializer (`string[]` / `(string | null)[]`).
@@ -14,8 +15,8 @@ import { isSpace, scanStringEnd } from "../../../util";
14
15
  * Enforces RFC 8259 array structure: `[`-framed, single-comma separated, no
15
16
  * leading / trailing / doubled commas, and each element must be a quoted string
16
17
  * (or the literal `null` for a nullable element type). Element contents are
17
- * validated by `deserializeString_NAIVE` (via `JSON.__deserialize`). Throws on
18
- * any deviation.
18
+ * validated by the selected whole-string decoder. Returns the shared failure
19
+ * sentinel on any deviation so JSON.parse can throw at its public boundary.
19
20
  */
20
21
  export function deserializeStringArray_NAIVE(
21
22
  srcStart: usize,
@@ -27,12 +28,15 @@ export function deserializeStringArray_NAIVE(
27
28
  );
28
29
  out.length = 0; // dst may arrive pre-sized; re-parse from empty via push
29
30
 
30
- while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
31
31
  while (srcEnd > srcStart && isSpace(load<u16>(srcEnd - 2))) srcEnd -= 2;
32
- if (srcStart >= srcEnd || load<u16>(srcStart) != BRACKET_LEFT)
33
- throw new Error("Invalid JSON array: expected '['");
34
- if (load<u16>(srcEnd - 2) != BRACKET_RIGHT)
35
- throw new Error("Invalid JSON array: expected ']'");
32
+ if (
33
+ srcStart >= srcEnd ||
34
+ load<u16>(srcStart) != BRACKET_LEFT ||
35
+ load<u16>(srcEnd - 2) != BRACKET_RIGHT
36
+ ) {
37
+ markProductionParseError();
38
+ return changetype<string[]>(0);
39
+ }
36
40
  srcStart += 2; // past '['
37
41
  srcEnd -= 2; // before ']'
38
42
 
@@ -43,26 +47,35 @@ export function deserializeStringArray_NAIVE(
43
47
  const code = load<u16>(srcStart);
44
48
  if (code == QUOTE) {
45
49
  const closing = scanStringEnd(srcStart, srcEnd);
46
- if (closing >= srcEnd)
47
- throw new Error("Invalid JSON array: unterminated string");
48
- out.push(JSON.__deserialize<string>(srcStart, closing + 2));
50
+ if (closing >= srcEnd) {
51
+ markProductionParseError();
52
+ return changetype<string[]>(0);
53
+ }
54
+ const value = deserializeString(srcStart, closing + 2);
55
+ if (changetype<usize>(value) == 0) return changetype<string[]>(0);
56
+ out.push(value);
49
57
  srcStart = closing + 2;
50
58
  } else if (srcStart + 8 <= srcEnd && load<u64>(srcStart) == NULL_WORD_U64) {
51
59
  // `(string | null)[]` element
52
60
  out.push(changetype<string>(0));
53
61
  srcStart += 8;
54
62
  } else {
55
- throw new Error("Invalid JSON array: expected string");
63
+ markProductionParseError();
64
+ return changetype<string[]>(0);
56
65
  }
57
66
 
58
67
  while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
59
68
  if (srcStart >= srcEnd) break;
60
- if (load<u16>(srcStart) != COMMA)
61
- throw new Error("Invalid JSON array: expected ',' or ']'");
69
+ if (load<u16>(srcStart) != COMMA) {
70
+ markProductionParseError();
71
+ return changetype<string[]>(0);
72
+ }
62
73
  srcStart += 2;
63
74
  while (srcStart < srcEnd && isSpace(load<u16>(srcStart))) srcStart += 2;
64
- if (srcStart >= srcEnd)
65
- throw new Error("Invalid JSON array: trailing comma");
75
+ if (srcStart >= srcEnd) {
76
+ markProductionParseError();
77
+ return changetype<string[]>(0);
78
+ }
66
79
  }
67
80
 
68
81
  return out;
@@ -34,14 +34,32 @@ export function deserializeStructArray<T extends unknown[]>(
34
34
  (srcEnd - srcStart).toString(),
35
35
  );
36
36
 
37
+ // Reuse existing element slots when `dst` already holds elements (no per-call
38
+ // allocation on a reused array); fall back to push for fresh/extra slots and
39
+ // trim any leftovers from a previous, longer parse.
40
+ let index = 0;
37
41
  while (srcStart < srcEnd) {
38
42
  const code = load<u16>(srcStart);
39
43
  if (code == BRACE_LEFT && depth++ == 0) {
40
44
  lastIndex = srcStart;
41
45
  } else if (code == BRACE_RIGHT && --depth == 0) {
42
- out.push(JSON.__deserialize<valueof<T>>(lastIndex, (srcStart += 2)));
46
+ const valueEnd = (srcStart += 2);
47
+ if (<usize>index < <usize>out.length) {
48
+ const slot = changetype<usize>(unchecked(out[index]));
49
+ if (slot != 0) {
50
+ JSON.__deserialize<valueof<T>>(lastIndex, valueEnd, slot);
51
+ } else {
52
+ unchecked(
53
+ (out[index] = JSON.__deserialize<valueof<T>>(lastIndex, valueEnd)),
54
+ );
55
+ }
56
+ } else {
57
+ out.push(JSON.__deserialize<valueof<T>>(lastIndex, valueEnd));
58
+ }
59
+ index++;
43
60
  }
44
61
  srcStart += 2;
45
62
  }
63
+ if (<usize>index < <usize>out.length) out.length = index;
46
64
  return out;
47
65
  }
@@ -1,11 +1,19 @@
1
- // @ts-ignore: inline
2
- @inline export function deserializeBoolean(
3
- srcStart: usize,
4
- srcEnd: usize,
5
- ): boolean {
6
- const block = load<u64>(srcStart);
7
- if (block == 28429475166421108) return true;
8
- else if (block == 32370086184550502 && load<u16>(srcStart, 8) == 101)
9
- return false;
10
- return false; //throw new Error(`Expected to find boolean, but found "${data.slice(0, 100)}" instead!`);
1
+ // 0 = invalid, 1 = false, 2 = true. The status form lets public dispatchers
2
+ // raise malformed-input errors at their catchable boundary without rescanning.
3
+ export function deserializeBooleanCode(srcStart: usize, srcEnd: usize): u32 {
4
+ const length = srcEnd - srcStart;
5
+ if (length >= 8 && load<u64>(srcStart) == 28429475166421108) return 2;
6
+ if (
7
+ length >= 10 &&
8
+ load<u64>(srcStart) == 32370086184550502 &&
9
+ load<u16>(srcStart, 8) == 101
10
+ )
11
+ return 1;
12
+ return 0;
13
+ }
14
+
15
+ export function deserializeBoolean(srcStart: usize, srcEnd: usize): boolean {
16
+ const code = deserializeBooleanCode(srcStart, srcEnd);
17
+ if (code != 0) return code == 2;
18
+ throw new Error("Expected 'true' or 'false' in JSON");
11
19
  }
@@ -1,7 +1,6 @@
1
1
  import { ptrToStr } from "../../util/ptrToStr";
2
2
 
3
- // @ts-ignore: inline
4
- @inline export function deserializeDate(srcStart: usize, srcEnd: usize): Date {
3
+ export function deserializeDate(srcStart: usize, srcEnd: usize): Date {
5
4
  // Use AssemblyScript's date parser
6
5
  const d = Date.fromString(ptrToStr(srcStart + 2, srcEnd - 2));
7
6
 
@@ -9,7 +9,6 @@ import { isSpace } from "../../util";
9
9
  // numbers like `0e`, `-01`, `1.`, `2.e3`, `0x42`.
10
10
  function validateJSONNumber(srcStart: usize, srcEnd: usize): void {
11
11
  let ptr = srcStart;
12
- while (ptr < srcEnd && isSpace(load<u16>(ptr))) ptr += 2;
13
12
  let end = srcEnd;
14
13
  while (end > ptr && isSpace(load<u16>(end - 2))) end -= 2;
15
14
  if (ptr >= end) throw new Error("Invalid JSON number: empty");
@@ -61,12 +60,8 @@ function validateJSONNumber(srcStart: usize, srcEnd: usize): void {
61
60
  if (ptr != end) throw new Error("Invalid JSON number: trailing characters");
62
61
  }
63
62
 
64
- // @ts-ignore: inline
65
- @inline export function deserializeFloat_NAIVE<T>(
66
- srcStart: usize,
67
- srcEnd: usize,
68
- ): T {
69
- validateJSONNumber(srcStart, srcEnd);
63
+ export function deserializeFloat_NAIVE<T>(srcStart: usize, srcEnd: usize): T {
64
+ if (JSON_STRICT) validateJSONNumber(srcStart, srcEnd);
70
65
  // @ts-ignore
71
66
  const type: T = 0;
72
67
  // @ts-ignore
@@ -113,8 +108,7 @@ function scanFloatEnd(srcStart: usize, srcEnd: usize): usize {
113
108
  return ptr;
114
109
  }
115
110
 
116
- // @ts-ignore: inline
117
- @inline export function deserializeFloatField_NAIVE<T extends number>(
111
+ export function deserializeFloatField_NAIVE<T extends number>(
118
112
  srcStart: usize,
119
113
  srcEnd: usize,
120
114
  dstObj: usize,
@@ -1,7 +1,6 @@
1
1
  import { atoi } from "../../util/atoi";
2
2
 
3
- // @ts-ignore: inline
4
- @inline export function deserializeInteger_NAIVE<T extends number>(
3
+ export function deserializeInteger_NAIVE<T extends number>(
5
4
  srcStart: usize,
6
5
  srcEnd: usize,
7
6
  ): T {