json-as 1.2.1 → 1.2.2

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 (92) hide show
  1. package/README.md +31 -1
  2. package/assembly/deserialize/simd/string.ts +62 -63
  3. package/assembly/deserialize/simple/arbitrary.ts +4 -4
  4. package/assembly/deserialize/simple/array/arbitrary.ts +1 -1
  5. package/assembly/index.d.ts +9 -0
  6. package/assembly/index.ts +211 -112
  7. package/assembly/serialize/simd/string.ts +16 -3
  8. package/assembly/serialize/simple/arbitrary.ts +16 -2
  9. package/assembly/serialize/swar/string.ts +41 -15
  10. package/assembly/test.ts +38 -0
  11. package/lib/as-bs.ts +113 -22
  12. package/package.json +4 -3
  13. package/transform/lib/index.js +34 -1
  14. package/transform/lib/index.js.map +1 -1
  15. package/.github/FUNDING.yml +0 -1
  16. package/.github/dependabot.yml +0 -11
  17. package/.github/workflows/benchmark.yml +0 -72
  18. package/.github/workflows/release-package.yml +0 -47
  19. package/.github/workflows/tests.yml +0 -25
  20. package/.prettierignore +0 -9
  21. package/.prettierrc.json +0 -7
  22. package/.trunk/configs/.markdownlint.yaml +0 -2
  23. package/.trunk/configs/.shellcheckrc +0 -7
  24. package/.trunk/configs/.yamllint.yaml +0 -7
  25. package/.trunk/trunk.yaml +0 -37
  26. package/CHANGELOG.md +0 -334
  27. package/SECURITY.md +0 -32
  28. package/asconfig.json +0 -7
  29. package/assembly/__benches__/abc.bench.ts +0 -28
  30. package/assembly/__benches__/large.bench.ts +0 -238
  31. package/assembly/__benches__/lib/bench.ts +0 -85
  32. package/assembly/__benches__/medium.bench.ts +0 -128
  33. package/assembly/__benches__/small.bench.ts +0 -46
  34. package/assembly/__benches__/throughput.ts +0 -172
  35. package/assembly/__benches__/vec3.bench.ts +0 -37
  36. package/assembly/__tests__/arbitrary.spec.ts +0 -35
  37. package/assembly/__tests__/array.spec.ts +0 -145
  38. package/assembly/__tests__/bool.spec.ts +0 -12
  39. package/assembly/__tests__/box.spec.ts +0 -27
  40. package/assembly/__tests__/custom.spec.ts +0 -56
  41. package/assembly/__tests__/date.spec.ts +0 -36
  42. package/assembly/__tests__/enum.spec.ts +0 -35
  43. package/assembly/__tests__/float.spec.ts +0 -42
  44. package/assembly/__tests__/generics.spec.ts +0 -49
  45. package/assembly/__tests__/hierarchy.spec.ts +0 -61
  46. package/assembly/__tests__/integer.spec.ts +0 -26
  47. package/assembly/__tests__/lib/index.ts +0 -41
  48. package/assembly/__tests__/map.spec.ts +0 -7
  49. package/assembly/__tests__/namespace.spec.ts +0 -63
  50. package/assembly/__tests__/null.spec.ts +0 -12
  51. package/assembly/__tests__/raw.spec.ts +0 -23
  52. package/assembly/__tests__/resolving.spec.ts +0 -55
  53. package/assembly/__tests__/staticarray.spec.ts +0 -12
  54. package/assembly/__tests__/string.spec.ts +0 -30
  55. package/assembly/__tests__/struct.spec.ts +0 -163
  56. package/assembly/__tests__/test.spec.ts +0 -3
  57. package/assembly/__tests__/types.spec.ts +0 -27
  58. package/assembly/__tests__/types.ts +0 -98
  59. package/assembly/test.tmp.ts +0 -133
  60. package/bench/abc.bench.ts +0 -25
  61. package/bench/large.bench.ts +0 -127
  62. package/bench/lib/bench.d.ts +0 -27
  63. package/bench/lib/bench.js +0 -53
  64. package/bench/lib/chart.ts +0 -217
  65. package/bench/medium.bench.ts +0 -68
  66. package/bench/runners/assemblyscript.js +0 -34
  67. package/bench/small.bench.ts +0 -34
  68. package/bench/throughput.ts +0 -87
  69. package/bench/tsconfig.json +0 -13
  70. package/bench/vec3.bench.ts +0 -30
  71. package/bench.ts +0 -18
  72. package/ci/bench/lib/bench.ts +0 -42
  73. package/ci/bench/runners/assemblyscript.js +0 -29
  74. package/ci/run-bench.as.sh +0 -63
  75. package/publish.sh +0 -78
  76. package/run-bench.as.sh +0 -60
  77. package/run-bench.js.sh +0 -36
  78. package/run-tests.sh +0 -51
  79. package/scripts/build-chart01.ts +0 -38
  80. package/scripts/build-chart02.ts +0 -38
  81. package/scripts/build-chart03.ts +0 -139
  82. package/scripts/build-chart05.ts +0 -47
  83. package/scripts/generate-as-class.ts +0 -50
  84. package/scripts/lib/bench-utils.ts +0 -308
  85. package/transform/src/builder.ts +0 -1375
  86. package/transform/src/index.ts +0 -1486
  87. package/transform/src/linkers/alias.ts +0 -58
  88. package/transform/src/linkers/custom.ts +0 -32
  89. package/transform/src/linkers/imports.ts +0 -22
  90. package/transform/src/types.ts +0 -300
  91. package/transform/src/util.ts +0 -128
  92. package/transform/src/visitor.ts +0 -530
package/README.md CHANGED
@@ -16,8 +16,8 @@
16
16
  - [Using Custom Serializers or Deserializers](#using-custom-serializers-or-deserializers)
17
17
  - [Performance](#performance)
18
18
  - [Comparison to JavaScript](#comparison-to-javascript)
19
+ - [Performance Tuning](#performance-tuning)
19
20
  - [Running Benchmarks Locally](#running-benchmarks-locally)
20
- - [Re-run Benchmarks](#re-run-benchmarks)
21
21
  - [Debugging](#debugging)
22
22
  - [License](#license)
23
23
  - [Contact](#contact)
@@ -269,6 +269,21 @@ obj.data = JSON.Value.from("a string"); // Changing to a string
269
269
  console.log(JSON.stringify(obj)); // {"id":1,"name":"Example","data":"a string"}
270
270
  ```
271
271
 
272
+ **Working with nullable primitives and dynamic data**
273
+
274
+ ```ts
275
+ const box = JSON.Box.from<i32>(123);
276
+ const value = JSON.Value.from<JSON.Box<i32> | null>(box);
277
+ const reboxed = JSON.Box.fromValue<i32>(value); // Box<i32> | null
278
+ console.log(reboxed !== null ? reboxed!.toString() : "null");
279
+ // 123
280
+
281
+ const value = JSON.parse<JSON.Value>("123");
282
+ const boxed = JSON.Box.fromValue<i32>(value);
283
+ console.log(boxed !== null ? boxed!.toString() : "null");
284
+ // 123
285
+ ```
286
+
272
287
  ### Using Raw JSON strings
273
288
 
274
289
  Sometimes its necessary to simply copy a string instead of serializing it.
@@ -396,8 +411,23 @@ The following charts compare JSON-AS (both SWAR and SIMD variants) against JavaS
396
411
 
397
412
  <img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart02.svg" alt="Performance Chart 2">
398
413
 
414
+ <img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart03.png" alt="Performance Chart 3">
415
+
399
416
  > Note: I have focused on extensively optimizing serialization. I used to have deserialization be highly unsafe and extremely fast, but I've since doubled down on safety for deserialization which has negatively affected performance. I will be optimizing soon.
400
417
 
418
+ ### Performance Tuning
419
+
420
+ Instead of using flags for setting options, `json-as` is configured by environmental variables.
421
+ Here's a short list:
422
+
423
+ **JSON_CACHE** (default: 0) - Enables caching costly strings based on hit frequency. May boost string serialization in excess of 22 GB/s.
424
+
425
+ **JSON_DEBUG** (default: 0) - Sets the debug level. May be within range `0-3`
426
+
427
+ **JSON_MODE** (default: SWAR) - Selects which mode should be used. Can be `NAIVE,SWAR,SIMD`. Note that `--enable simd` may be required.
428
+
429
+ **JSON_WRITE** (default: "") - Select a series of files to output after transform and optimization passes have completed for easy inspection. Usage: `JSON_WRITE=.path-to-file-a.ts,./path-to-file-b.ts`
430
+
401
431
  ### Running benchmarks locally
402
432
 
403
433
  Benchmarks are run directly on top of v8 for more tailored control
@@ -1,3 +1,4 @@
1
+ import { bs } from "../../../lib/as-bs";
1
2
  import { BACK_SLASH } from "../../custom/chars";
2
3
  import { DESERIALIZE_ESCAPE_TABLE, ESCAPE_HEX_TABLE } from "../../globals/tables";
3
4
 
@@ -8,94 +9,92 @@ import { DESERIALIZE_ESCAPE_TABLE, ESCAPE_HEX_TABLE } from "../../globals/tables
8
9
  * @returns number of bytes written
9
10
  */
10
11
  // todo: optimize and stuff. it works, its not pretty. ideally, i'd like this to be (nearly) branchless
11
- export function deserializeString_SIMD(srcStart: usize, srcEnd: usize, dst: usize): usize {
12
+ export function deserializeString_SIMD(srcStart: usize, srcEnd: usize): void {
12
13
  const SPLAT_92 = i16x8.splat(92); /* \ */
13
14
  srcStart += 2;
14
15
  srcEnd -= 2;
15
- let dst_ptr = changetype<usize>(dst);
16
+ bs.ensureSize(u32(srcEnd - srcStart));
16
17
  const src_end_15 = srcEnd - 15;
17
18
 
18
- while (srcStart < src_end_15) {
19
- const block = v128.load(srcStart);
20
- v128.store(dst_ptr, block);
19
+ // while (srcStart < src_end_15) {
20
+ // const block = v128.load(srcStart);
21
+ // v128.store(bs.offset, block);
21
22
 
22
- const backslash_indices = i16x8.eq(block, SPLAT_92);
23
- let mask = i16x8.bitmask(backslash_indices);
23
+ // const backslash_indices = i16x8.eq(block, SPLAT_92);
24
+ // let mask = i16x8.bitmask(backslash_indices);
24
25
 
25
- while (mask != 0) {
26
- const lane_index = ctz(mask) << 1;
27
- const dst_offset = dst_ptr + lane_index;
28
- const src_offset = srcStart + lane_index;
29
- const code = load<u16>(src_offset, 2);
26
+ // while (mask != 0) {
27
+ // const lane_index = ctz(mask) << 1;
28
+ // const dst_offset = bs.offset + lane_index;
29
+ // const src_offset = srcStart + lane_index;
30
+ // const code = load<u16>(src_offset, 2);
30
31
 
31
- mask &= mask - 1;
32
- if (code == 117 && load<u32>(src_offset, 4) == 3145776) {
33
- const block = load<u32>(src_offset, 8);
34
- const codeA = block & 0xffff;
35
- const codeB = (block >> 16) & 0xffff;
36
- const escapedA = load<u8>(ESCAPE_HEX_TABLE + codeA);
37
- const escapedB = load<u8>(ESCAPE_HEX_TABLE + codeB);
38
- const escaped = (escapedA << 4) + escapedB;
39
- // console.log("Escaped:");
40
- // console.log(" a: " + escapedA.toString())
41
- // console.log(" b: " + escapedB.toString());
42
- // console.log(" c: " + escaped.toString());
43
- // console.log(" o: " + (dst_ptr - dst).toString());
44
- // console.log(" d: " + (dst_offset - dst).toString())
45
- // console.log(" l: " + (lane_index).toString())
46
- store<u16>(dst_offset, escaped);
47
- v128.store(dst_offset, v128.load(src_offset, 4), 2);
48
- if (lane_index >= 6) {
49
- const bytes_left = lane_index - 4;
50
- srcStart += bytes_left;
51
- dst_ptr += bytes_left;
52
- // console.log(" e: " + (bytes_left).toString())
53
- }
54
- dst_ptr -= 10;
55
- } else {
56
- const escaped = load<u8>(DESERIALIZE_ESCAPE_TABLE + code);
57
- store<u16>(dst_offset, escaped);
58
- v128.store(dst_offset, v128.load(src_offset, 4), 2);
59
- // console.log("Escaped:");
60
- if (lane_index == 14) {
61
- srcStart += 2;
62
- } else {
63
- dst_ptr -= 2;
64
- }
65
- }
66
- }
32
+ // mask &= mask - 1;
33
+ // if (code == 117 && load<u32>(src_offset, 4) == 3145776) {
34
+ // const block = load<u32>(src_offset, 8);
35
+ // const codeA = block & 0xffff;
36
+ // const codeB = (block >> 16) & 0xffff;
37
+ // const escapedA = load<u8>(ESCAPE_HEX_TABLE + codeA);
38
+ // const escapedB = load<u8>(ESCAPE_HEX_TABLE + codeB);
39
+ // const escaped = (escapedA << 4) + escapedB;
40
+ // // console.log("Escaped:");
41
+ // console.log(" a: " + escapedA.toString())
42
+ // console.log(" b: " + escapedB.toString());
43
+ // console.log(" c: " + escaped.toString());
44
+ // console.log(" o: " + (bs.offset - dst).toString());
45
+ // console.log(" d: " + (dst_offset - dst).toString())
46
+ // console.log(" l: " + (lane_index).toString())
47
+ // store<u16>(dst_offset, escaped);
48
+ // v128.store(dst_offset, v128.load(src_offset, 4), 2);
49
+ // if (lane_index >= 6) {
50
+ // const bytes_left = lane_index - 4;
51
+ // srcStart += bytes_left;
52
+ // bs.offset += bytes_left;
53
+ // // console.log(" e: " + (bytes_left).toString())
54
+ // }
55
+ // bs.offset -= 10;
56
+ // } else {
57
+ // const escaped = load<u8>(DESERIALIZE_ESCAPE_TABLE + code);
58
+ // store<u16>(dst_offset, escaped);
59
+ // v128.store(dst_offset, v128.load(src_offset, 4), 2);
60
+ // // console.log("Escaped:");
61
+ // if (lane_index == 14) {
62
+ // srcStart += 2;
63
+ // } else {
64
+ // bs.offset -= 2;
65
+ // }
66
+ // }
67
+ // }
67
68
 
68
- srcStart += 16;
69
- dst_ptr += 16;
69
+ // srcStart += 16;
70
+ // bs.offset += 16;
70
71
 
71
- // console.log("src: " + (srcStart - changetype<usize>(src)).toString());
72
- // console.log("dst: " + (dst_ptr - dst).toString());
73
- }
72
+ // // console.log("src: " + (srcStart - changetype<usize>(src)).toString());
73
+ // // console.log("dst: " + (dst_ptr - dst).toString());
74
+ // }
74
75
  while (srcStart < srcEnd) {
75
76
  let code = load<u16>(srcStart);
76
- if (code == BACK_SLASH) {
77
+ if (code === BACK_SLASH) {
77
78
  code = load<u16>(DESERIALIZE_ESCAPE_TABLE + load<u8>(srcStart, 2));
78
- if (code == 117 && load<u32>(srcStart, 4) == 3145776) {
79
+ if (code === 117 && load<u32>(srcStart, 4) === 3145776) {
79
80
  const block = load<u32>(srcStart, 8);
80
81
  const codeA = block & 0xffff;
81
82
  const codeB = (block >> 16) & 0xffff;
82
83
  const escapedA = load<u8>(ESCAPE_HEX_TABLE + codeA);
83
84
  const escapedB = load<u8>(ESCAPE_HEX_TABLE + codeB);
84
85
  const escaped = (escapedA << 4) + escapedB;
85
- store<u16>(dst_ptr, escaped);
86
- dst_ptr += 2;
86
+ store<u16>(bs.offset, escaped);
87
+ bs.offset += 2;
87
88
  srcStart += 12;
88
89
  } else {
89
- store<u16>(dst_ptr, code);
90
- dst_ptr += 2;
90
+ store<u16>(bs.offset, code);
91
+ bs.offset += 2;
91
92
  srcStart += 4;
92
93
  }
93
94
  } else {
94
- store<u16>(dst_ptr, code);
95
- dst_ptr += 2;
95
+ store<u16>(bs.offset, code);
96
+ bs.offset += 2;
96
97
  srcStart += 2;
97
98
  }
98
99
  }
99
-
100
- return dst_ptr - dst;
101
100
  }
@@ -5,8 +5,6 @@ import { deserializeFloat } from "./float";
5
5
  import { deserializeString } from "./string";
6
6
  import { deserializeObject } from "./object";
7
7
  import { BRACE_LEFT, BRACKET_LEFT, CHAR_N, QUOTE } from "../../custom/chars";
8
- import { bs } from "../../../lib/as-bs";
9
- import { deserializeString_SWAR } from "../swar/string";
10
8
 
11
9
  export function deserializeArbitrary(srcStart: usize, srcEnd: usize, dst: usize): JSON.Value {
12
10
  const firstChar = load<u16>(srcStart);
@@ -18,7 +16,9 @@ export function deserializeArbitrary(srcStart: usize, srcEnd: usize, dst: usize)
18
16
  return JSON.Value.from(deserializeArray<JSON.Value[]>(srcStart, srcEnd, 0));
19
17
  } else if (firstChar == 116 || firstChar == 102) return JSON.Value.from(deserializeBoolean(srcStart, srcEnd));
20
18
  else if (firstChar == CHAR_N) {
21
- return JSON.Value.from<usize>(0);
19
+ const value = JSON.Value.from<usize>(0);
20
+ value.isNull = true;
21
+ return value;
22
22
  }
23
23
  return unreachable();
24
- }
24
+ }
@@ -121,7 +121,7 @@ export function deserializeArbitraryArray(srcStart: usize, srcEnd: usize, dst: u
121
121
  }
122
122
  } else if (code == CHAR_N) {
123
123
  if (load<u64>(srcStart) == 30399761348886638) {
124
- console.log("Value (null): " + ptrToStr(srcStart, srcStart + 8));
124
+ // console.log("Value (null): " + ptrToStr(srcStart, srcStart + 8));
125
125
  // @ts-ignore: type
126
126
  out.push(JSON.__deserialize<JSON.Value>(srcStart, (srcStart += 8)));
127
127
  // while (isSpace(load<u16>((srcStart += 2)))) {
@@ -43,3 +43,12 @@ declare function serializer(..._): any;
43
43
  */
44
44
  // @ts-ignore: type
45
45
  declare function deserializer(..._): any;
46
+
47
+ declare enum JSONMode {
48
+ SWAR = 0,
49
+ SIMD = 1,
50
+ NAIVE = 2
51
+ }
52
+
53
+ declare const JSON_MODE: JSONMode;
54
+ declare const JSON_CACHE: bool;