json-as 1.2.6 → 1.3.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.
Files changed (135) hide show
  1. package/CHANGELOG.md +417 -0
  2. package/README.md +135 -36
  3. package/assembly/custom/util.ts +24 -70
  4. package/assembly/deserialize/float.ts +181 -0
  5. package/assembly/deserialize/helpers/uint.ts +12 -0
  6. package/assembly/deserialize/index/arbitrary.ts +25 -0
  7. package/assembly/deserialize/index/array.ts +61 -0
  8. package/assembly/deserialize/index/bool.ts +1 -0
  9. package/assembly/deserialize/index/date.ts +1 -0
  10. package/assembly/deserialize/index/float.ts +1 -0
  11. package/assembly/deserialize/index/integer.ts +1 -0
  12. package/assembly/deserialize/index/map.ts +1 -0
  13. package/assembly/deserialize/index/object.ts +1 -0
  14. package/assembly/deserialize/index/raw.ts +1 -0
  15. package/assembly/deserialize/index/set.ts +1 -0
  16. package/assembly/deserialize/index/staticarray.ts +1 -0
  17. package/assembly/deserialize/index/string.ts +15 -0
  18. package/assembly/deserialize/index/struct.ts +1 -0
  19. package/assembly/deserialize/index/typedarray.ts +15 -0
  20. package/assembly/deserialize/index/unsigned.ts +1 -0
  21. package/assembly/deserialize/index.ts +14 -0
  22. package/assembly/deserialize/integer.ts +42 -0
  23. package/assembly/deserialize/simd/array/integer.ts +307 -0
  24. package/assembly/deserialize/simd/string.ts +130 -11
  25. package/assembly/deserialize/simple/arbitrary.ts +5 -12
  26. package/assembly/deserialize/simple/array/arbitrary.ts +12 -36
  27. package/assembly/deserialize/simple/array/array.ts +2 -8
  28. package/assembly/deserialize/simple/array/bool.ts +2 -8
  29. package/assembly/deserialize/simple/array/box.ts +2 -8
  30. package/assembly/deserialize/simple/array/float.ts +2 -8
  31. package/assembly/deserialize/simple/array/integer.ts +2 -8
  32. package/assembly/deserialize/simple/array/map.ts +6 -26
  33. package/assembly/deserialize/simple/array/object.ts +6 -26
  34. package/assembly/deserialize/simple/array/raw.ts +18 -61
  35. package/assembly/deserialize/simple/array/string.ts +5 -10
  36. package/assembly/deserialize/simple/array/struct.ts +6 -26
  37. package/assembly/deserialize/simple/array.ts +2 -5
  38. package/assembly/deserialize/simple/bool.ts +2 -6
  39. package/assembly/deserialize/simple/map.ts +29 -102
  40. package/assembly/deserialize/simple/object.ts +24 -81
  41. package/assembly/deserialize/simple/raw.ts +1 -4
  42. package/assembly/deserialize/simple/set.ts +11 -37
  43. package/assembly/deserialize/simple/staticarray/array.ts +1 -1
  44. package/assembly/deserialize/simple/staticarray/bool.ts +1 -1
  45. package/assembly/deserialize/simple/staticarray/float.ts +1 -1
  46. package/assembly/deserialize/simple/staticarray/integer.ts +1 -1
  47. package/assembly/deserialize/simple/staticarray/string.ts +7 -14
  48. package/assembly/deserialize/simple/staticarray/struct.ts +1 -1
  49. package/assembly/deserialize/simple/staticarray.ts +57 -21
  50. package/assembly/deserialize/simple/string.ts +90 -10
  51. package/assembly/deserialize/simple/struct.ts +25 -121
  52. package/assembly/deserialize/simple/typedarray.ts +94 -0
  53. package/assembly/deserialize/swar/array/arbitrary.ts +8 -0
  54. package/assembly/deserialize/swar/array/array.ts +39 -0
  55. package/assembly/deserialize/swar/array/bool.ts +47 -0
  56. package/assembly/deserialize/swar/array/box.ts +8 -0
  57. package/assembly/deserialize/swar/array/float.ts +39 -0
  58. package/assembly/deserialize/swar/array/integer.ts +461 -0
  59. package/assembly/deserialize/swar/array/map.ts +7 -0
  60. package/assembly/deserialize/swar/array/object.ts +44 -0
  61. package/assembly/deserialize/swar/array/raw.ts +8 -0
  62. package/assembly/deserialize/swar/array/shared.ts +96 -0
  63. package/assembly/deserialize/swar/array/string.ts +39 -0
  64. package/assembly/deserialize/swar/array/struct.ts +44 -0
  65. package/assembly/deserialize/swar/array.ts +49 -0
  66. package/assembly/deserialize/swar/string.ts +648 -15
  67. package/assembly/deserialize/unsigned.ts +75 -0
  68. package/assembly/index.d.ts +1 -3
  69. package/assembly/index.ts +316 -374
  70. package/assembly/serialize/index/arbitrary.ts +75 -0
  71. package/assembly/serialize/index/array.ts +1 -0
  72. package/assembly/serialize/index/bool.ts +1 -0
  73. package/assembly/serialize/index/date.ts +1 -0
  74. package/assembly/serialize/index/float.ts +1 -0
  75. package/assembly/serialize/index/integer.ts +1 -0
  76. package/assembly/serialize/index/map.ts +1 -0
  77. package/assembly/serialize/index/object.ts +46 -0
  78. package/assembly/serialize/index/raw.ts +1 -0
  79. package/assembly/serialize/index/set.ts +1 -0
  80. package/assembly/serialize/index/staticarray.ts +1 -0
  81. package/assembly/serialize/index/string.ts +15 -0
  82. package/assembly/serialize/index/struct.ts +1 -0
  83. package/assembly/serialize/index/typedarray.ts +66 -0
  84. package/assembly/serialize/index.ts +13 -0
  85. package/assembly/serialize/simd/string.ts +4 -13
  86. package/assembly/serialize/simple/arbitrary.ts +6 -0
  87. package/assembly/serialize/simple/raw.ts +1 -5
  88. package/assembly/serialize/simple/string.ts +3 -11
  89. package/assembly/serialize/simple/typedarray.ts +63 -0
  90. package/assembly/serialize/swar/string.ts +6 -21
  91. package/assembly/util/concat.ts +1 -5
  92. package/assembly/util/index.ts +1 -0
  93. package/assembly/util/masks.ts +12 -18
  94. package/assembly/util/memory.ts +0 -0
  95. package/assembly/util/snp.ts +1 -4
  96. package/assembly/util/stringScan.ts +24 -0
  97. package/assembly/util/swar.ts +50 -6
  98. package/lib/as-bs.ts +137 -127
  99. package/package.json +26 -5
  100. package/transform/lib/builder.d.ts.map +1 -1
  101. package/transform/lib/builder.js +5 -13
  102. package/transform/lib/builder.js.map +1 -1
  103. package/transform/lib/index.d.ts +1 -0
  104. package/transform/lib/index.d.ts.map +1 -1
  105. package/transform/lib/index.js +672 -757
  106. package/transform/lib/index.js.map +1 -1
  107. package/transform/lib/linkers/alias.d.ts.map +1 -1
  108. package/transform/lib/linkers/alias.js.map +1 -1
  109. package/transform/lib/linkers/custom.d.ts.map +1 -1
  110. package/transform/lib/linkers/custom.js +8 -9
  111. package/transform/lib/linkers/custom.js.map +1 -1
  112. package/transform/lib/linkers/imports.d.ts.map +1 -1
  113. package/transform/lib/linkers/imports.js.map +1 -1
  114. package/transform/lib/types.d.ts +6 -0
  115. package/transform/lib/types.d.ts.map +1 -1
  116. package/transform/lib/types.js +83 -21
  117. package/transform/lib/types.js.map +1 -1
  118. package/transform/lib/util.d.ts.map +1 -1
  119. package/transform/lib/util.js +1 -1
  120. package/transform/lib/util.js.map +1 -1
  121. package/transform/lib/visitor.d.ts.map +1 -1
  122. package/transform/lib/visitor.js +1 -2
  123. package/transform/lib/visitor.js.map +1 -1
  124. package/.prettierrc +0 -3
  125. package/ARCHITECTURE.md +0 -320
  126. package/CONTRIBUTING.md +0 -238
  127. package/TODO +0 -1
  128. package/assembly/deserialize/simple/float.ts +0 -11
  129. package/assembly/deserialize/simple/integer.ts +0 -9
  130. package/assembly/test.ts +0 -30
  131. package/eslint.config.js +0 -60
  132. package/lib/tsconfig.json +0 -8
  133. package/tools/assemblyscript-eslint-local.js +0 -29
  134. package/tools/assemblyscript-eslint.js +0 -29
  135. package/transform/tsconfig.json +0 -35
package/README.md CHANGED
@@ -14,6 +14,7 @@
14
14
  - [Using Raw JSON Strings](#using-raw-json-strings)
15
15
  - [Working with Enums](#working-with-enums)
16
16
  - [Using Custom Serializers or Deserializers](#using-custom-serializers-or-deserializers)
17
+ - [Overriding built-in Container Types](#overriding-built-in-container-types)
17
18
  - [Performance](#performance)
18
19
  - [Comparison to JavaScript](#comparison-to-javascript)
19
20
  - [Performance Tuning](#performance-tuning)
@@ -36,7 +37,7 @@ npm install json-as
36
37
  Add the `--transform` to your `asc` command (e.g. in package.json)
37
38
 
38
39
  ```bash
39
- --transform json-as/transform
40
+ --transform json-as
40
41
  ```
41
42
 
42
43
  Optionally, for additional performance, also add:
@@ -50,7 +51,7 @@ Alternatively, add it to your `asconfig.json`
50
51
  ```typescript
51
52
  {
52
53
  "options": {
53
- "transform": ["json-as/transform"]
54
+ "transform": ["json-as"]
54
55
  }
55
56
  }
56
57
  ```
@@ -334,11 +335,11 @@ const serialized = JSON.stringify<Foo>(Foo.bar);
334
335
 
335
336
  This library supports custom serialization and deserialization methods, which can be defined using the `@serializer` and `@deserializer` decorators.
336
337
 
337
- Here's an example of creating a custom data type called `Point` which serializes to `(x,y)`
338
+ Custom serializers and deserializers must always speak valid JSON. You can optionally provide the JSON value shape they operate on using one of: `"any"`, `"string"`, `"number"`, `"object"`, `"array"`, `"boolean"`, or `"null"`. If omitted, the shape defaults to `"any"`.
338
339
 
339
- ```typescript
340
- import { bytes } from "json-as/assembly/util";
340
+ Here's an example of creating a custom data type called `Point` which serializes to a JSON string:
341
341
 
342
+ ```typescript
342
343
  @json
343
344
  class Point {
344
345
  x: f64 = 0.0;
@@ -348,21 +349,21 @@ class Point {
348
349
  this.y = y;
349
350
  }
350
351
 
351
- @serializer
352
+ @serializer("string")
352
353
  serializer(self: Point): string {
353
- return `(${self.x},${self.y})`;
354
+ return JSON.stringify(`${self.x},${self.y}`);
354
355
  }
355
356
 
356
- @deserializer
357
+ @deserializer("string")
357
358
  deserializer(data: string): Point {
358
- const dataSize = bytes(data);
359
- if (dataSize <= 2) throw new Error("Could not deserialize provided data as type Point");
359
+ const raw = JSON.parse<string>(data);
360
+ if (!raw.length) throw new Error("Could not deserialize provided data as type Point");
360
361
 
361
- const c = data.indexOf(",");
362
- const x = data.slice(1, c);
363
- const y = data.slice(c + 1, data.length - 1);
362
+ const c = raw.indexOf(",");
363
+ const x = raw.slice(0, c);
364
+ const y = raw.slice(c + 1);
364
365
 
365
- return new Point(f64.parse(x), f64.parse(y));
366
+ return new Point(f64.parse(x), f64.parse(y)); // NEVER use this in deserializers. Always return a new instance
366
367
  }
367
368
  }
368
369
 
@@ -375,9 +376,11 @@ console.log("Serialized " + serialized);
375
376
  console.log("Deserialized " + JSON.stringify(deserialized));
376
377
  ```
377
378
 
378
- The serializer function converts a `Point` instance into a string format `(x,y)`.
379
+ The serializer function converts a `Point` instance into a valid JSON string value.
380
+
381
+ The deserializer function parses that JSON string back into a `Point` instance.
379
382
 
380
- The deserializer function parses the string `(x,y)` back into a `Point` instance.
383
+ Custom deserializers should always instantiate and return a new object. They should not assume an existing destination instance will be passed in or reused.
381
384
 
382
385
  These functions are then wrapped before being consumed by the json-as library:
383
386
 
@@ -396,19 +399,98 @@ These functions are then wrapped before being consumed by the json-as library:
396
399
 
397
400
  This allows custom serialization while maintaining a generic interface for the library to access.
398
401
 
399
- ## Performance
402
+ ### Overriding built-in container types
400
403
 
401
- The `json-as` library is engineered for **multi-GB/s processing speeds**, leveraging SIMD and SWAR optimizations along with highly efficient transformations. The charts below highlight key performance metrics such as build time, operations-per-second, and throughput.
404
+ Undecorated subclasses of built-in container types keep the built-in JSON behavior.
405
+
406
+ This rule applies consistently across:
407
+
408
+ - `JSON.stringify(...)`
409
+ - `JSON.parse<T>(...)`
410
+ - `JSON.Value.from(...)`
411
+ - `JSON.internal.stringify(...)`
412
+ - `JSON.internal.parse(...)`
413
+
414
+ For example:
415
+
416
+ - `class MyBytes extends Uint8Array {}` still serializes like a normal `Uint8Array`
417
+ - `class MyMap extends Map<string, i32> {}` still serializes like a normal `Map<string, i32>`
418
+ - the same applies to subclassable built-ins such as `Array`, `Set`, and typed arrays
419
+
420
+ If you decorate that subclass with `@json`, it is treated as a normal generated class instead of inheriting the built-in container behavior. That means generated `__SERIALIZE` / `__DESERIALIZE` logic and custom serializer/deserializer hooks can take over.
421
+
422
+ If you want a different wire format, decorate the subclass with `@json` and provide custom `@serializer(...)` / `@deserializer(...)` methods:
423
+
424
+ ```typescript
425
+ function hexDigit(value: u8): string {
426
+ return String.fromCharCode(value < 10 ? 48 + value : 87 + value);
427
+ }
428
+
429
+ function parseHexNibble(code: u16): u8 {
430
+ if (code >= 48 && code <= 57) return <u8>(code - 48);
431
+ if (code >= 97 && code <= 102) return <u8>(code - 87);
432
+ return <u8>(code - 55);
433
+ }
434
+
435
+ @json
436
+ class HexBytes extends Uint8Array {
437
+ constructor(length: i32 = 0) {
438
+ super(length);
439
+ }
440
+
441
+ @serializer("string")
442
+ serializer(self: HexBytes): string {
443
+ let out = "";
444
+ for (let i = 0; i < self.length; i++) {
445
+ const value = unchecked(self[i]);
446
+ out += hexDigit(value >> 4);
447
+ out += hexDigit(value & 0x0f);
448
+ }
449
+ return JSON.stringify(out);
450
+ }
451
+
452
+ @deserializer("string")
453
+ deserializer(data: string): HexBytes {
454
+ const raw = JSON.parse<string>(data);
455
+ const out = new HexBytes(raw.length >> 1);
456
+
457
+ for (let i = 0, j = 0; i < raw.length; i += 2, j++) {
458
+ const hi = parseHexNibble(<u16>raw.charCodeAt(i));
459
+ const lo = parseHexNibble(<u16>raw.charCodeAt(i + 1));
460
+ unchecked((out[j] = <u8>((hi << 4) | lo)));
461
+ }
402
462
 
403
- You can **re-run the benchmarks** at any time by clicking the button below. After the workflow completes, refresh this page to view updated results.
463
+ return out;
464
+ }
465
+ }
466
+
467
+ const bytes = new HexBytes(4);
468
+ bytes[0] = 10;
469
+ bytes[1] = 20;
470
+ bytes[2] = 30;
471
+ bytes[3] = 40;
472
+
473
+ JSON.stringify(bytes); // "\"0a141e28\""
474
+ JSON.parse<HexBytes>("\"0a141e28\"");
475
+ ```
476
+
477
+ This same pattern works for subclassable built-ins like `Array`, `Map`, `Set`, and typed arrays.
404
478
 
405
- [![Run Benchmarks](https://img.shields.io/badge/Run_Benchmark-blue)](https://github.com/JairusSW/json-as/actions/workflows/benchmark.yml)
479
+ `ArrayBuffer` and `String` are `@final` in AssemblyScript, so they cannot be subclassed there.
480
+
481
+ ## Performance
482
+
483
+ The `json-as` library is engineered for **multi-GB/s processing speeds**, leveraging SIMD and SWAR optimizations along with highly efficient transformations. The charts below highlight key performance metrics such as build time, operations-per-second, and throughput.
406
484
 
407
485
  ### Comparison to JavaScript
408
486
 
409
- The following charts compare JSON-AS (both SWAR and SIMD variants) against JavaScript's native `JSON` implementation. Benchmarks were conducted in a GitHub Actions environment. On modern hardware, you may see even higher throughput.
487
+ The following charts compare JSON-AS (both SWAR and SIMD variants) against JavaScript's native `JSON` implementation. The published charts are generated locally and pushed to the `docs` branch.
410
488
 
411
489
  > Note: Benchmarks reflect the **latest version**. Older versions may show different performance.
490
+ >
491
+ > Current local benchmark machine: AMD Ryzen 7 7800X3D (8 cores, 8 threads), 96 MB L3 cache, 32 GB RAM.
492
+ >
493
+ > Benchmark results include normal end-to-end work such as allocating the destination object or array before deserializing into it. Raw parser throughput is higher than the published figures because these numbers intentionally include that allocation/setup cost.
412
494
 
413
495
  <img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart01.svg" alt="Performance Chart 1">
414
496
 
@@ -418,9 +500,9 @@ The following charts compare JSON-AS (both SWAR and SIMD variants) against JavaS
418
500
 
419
501
  <img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart04.png" alt="Performance Chart 4">
420
502
 
421
- > 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.
503
+ <img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart05.png" alt="Performance Chart 5">
422
504
 
423
- > Note on Token: I am currently working on optimizing the token deserialization process. This reflects the *future* performance of this library.
505
+ <img src="https://raw.githubusercontent.com/JairusSW/json-as/refs/heads/docs/charts/chart06.png" alt="Performance Chart 6">
424
506
 
425
507
  ### Performance Tuning
426
508
 
@@ -433,46 +515,63 @@ Here's a short list:
433
515
 
434
516
  **JSON_MODE** (default: SWAR) - Selects which mode should be used. Can be `NAIVE,SWAR,SIMD`. Note that `--enable simd` may be required.
435
517
 
518
+ **JSON_USE_FAST_PATH** (default: 0) - When set to `1`, the transform emits the fast `__DESERIALIZE` implementation for generated structs. When unset or `0`, it emits only the slow path.
519
+
436
520
  **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`
437
521
 
438
- ### Running benchmarks locally
522
+ ### Running Benchmarks Locally
439
523
 
440
- Benchmarks are run directly on top of v8 for more tailored control
524
+ Benchmarks are run directly on top of `v8` for tighter control over the engine configuration.
441
525
 
442
- 1. Download JSVU off of npm
526
+ 1. Install the local benchmark prerequisites:
443
527
 
444
528
  ```bash
445
- npm install jsvu -g
529
+ npm install -g jsvu
530
+ jsvu --engines=v8
446
531
  ```
447
532
 
448
- 2. Modify your dotfiles to add `~/.jsvu/bin` to `PATH`
533
+ 2. Add `~/.jsvu/bin` to your `PATH` and make sure `wasm-opt` is installed:
449
534
 
450
535
  ```bash
451
536
  export PATH="${HOME}/.jsvu/bin:${PATH}"
537
+ sudo apt-get install -y binaryen
452
538
  ```
453
539
 
454
- 3. Clone the repository
540
+ 3. Install project dependencies:
455
541
 
456
542
  ```bash
457
- git clone https://github.com/JairusSW/json-as
543
+ npm install
458
544
  ```
459
545
 
460
- 4. Install dependencies
546
+ 4. Run either benchmark suite directly:
461
547
 
462
548
  ```bash
463
- npm i
549
+ ./run-bench.as.sh
550
+ ./run-bench.js.sh
464
551
  ```
465
552
 
466
- 5. Run benchmarks for either AssemblyScript or JavaScript
553
+ 5. Build charts from the latest local logs:
467
554
 
468
555
  ```bash
469
- ./run-bench.as.sh
556
+ ./build-charts.sh
470
557
  ```
471
558
 
472
- or
559
+ 6. Publish benchmark charts to the `docs` branch:
473
560
 
474
561
  ```bash
475
- ./run-bench.js.sh
562
+ ./publish-benchmarks.sh
563
+ ```
564
+
565
+ If you already have fresh logs and only want to rebuild charts and push them:
566
+
567
+ ```bash
568
+ ./publish-benchmarks.sh --no-run
569
+ ```
570
+
571
+ Or run the full local publish flow in one step:
572
+
573
+ ```bash
574
+ npm run bench:publish
476
575
  ```
477
576
 
478
577
  ## Debugging
@@ -15,11 +15,7 @@
15
15
  * @param str - Any number. Can include scientific notation.
16
16
  */
17
17
  // @ts-ignore: Decorator
18
- @inline export function snip_fast<T extends number>(
19
- str: string,
20
- len: u32 = 0,
21
- offset: u32 = 0,
22
- ): T {
18
+ @inline export function snip_fast<T extends number>(str: string, len: u32 = 0, offset: u32 = 0): T {
23
19
  if (isSigned<T>()) {
24
20
  const firstChar: u32 = load<u16>(changetype<usize>(str));
25
21
  if (firstChar == 48) return 0 as T;
@@ -39,31 +35,19 @@
39
35
  // The first char (f) is E or e
40
36
  // We push the offset up by two and apply the notation.
41
37
  if (load<u16>(changetype<usize>(str) + <usize>offset + 2) == 45) {
42
- return -(
43
- val /
44
- 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)
45
- ) as T;
38
+ return -(val / 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)) as T;
46
39
  } else {
47
40
  // Inlined this operation instead of using a loop
48
- return -(
49
- val *
50
- 10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1)
51
- ) as T;
41
+ return -(val * 10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1)) as T;
52
42
  }
53
43
  } else if (high > 57) {
54
44
  // The first char (f) is E or e
55
45
  // We push the offset up by two and apply the notation.
56
46
  if (load<u16>(changetype<usize>(str) + <usize>offset + 4) == 45) {
57
- return -(
58
- val /
59
- 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)
60
- ) as T;
47
+ return -(val / 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)) as T;
61
48
  } else {
62
49
  // Inlined this operation instead of using a loop
63
- return -(
64
- val *
65
- 10 ** (__atoi_fast<u32>(str, offset + 4, offset + 6) + 1)
66
- ) as T;
50
+ return -(val * 10 ** (__atoi_fast<u32>(str, offset + 4, offset + 6) + 1)) as T;
67
51
  }
68
52
  } else {
69
53
  val = (val * 100 + (low - 48) * 10 + (high - 48)) as T;
@@ -78,16 +62,10 @@
78
62
  // The first char (f) is E or e
79
63
  // We push the offset up by two and apply the notation.
80
64
  if (load<u16>(changetype<usize>(str) + <usize>offset + 2) == 45) {
81
- return -(
82
- val /
83
- 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)
84
- ) as T;
65
+ return -(val / 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)) as T;
85
66
  } else {
86
67
  // Inlined this operation instead of using a loop
87
- return -(
88
- val *
89
- 10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1)
90
- ) as T;
68
+ return -(val * 10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1)) as T;
91
69
  }
92
70
  } else {
93
71
  val = (val * 10 + (ch - 48)) as T;
@@ -106,21 +84,17 @@
106
84
  // The first char (f) is E or e
107
85
  // We push the offset up by two and apply the notation.
108
86
  if (load<u16>(changetype<usize>(str) + <usize>offset + 2) == 45) {
109
- return (val /
110
- 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)) as T;
87
+ return (val / 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)) as T;
111
88
  } else {
112
89
  // Inlined this operation instead of using a loop
113
- return (val *
114
- 10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1)) as T;
90
+ return (val * 10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1)) as T;
115
91
  }
116
92
  } else if (high > 57) {
117
93
  if (load<u16>(changetype<usize>(str) + <usize>offset + 4) == 45) {
118
- return (val /
119
- 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)) as T;
94
+ return (val / 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)) as T;
120
95
  } else {
121
96
  // Inlined this operation instead of using a loop
122
- return (val *
123
- 10 ** (__atoi_fast<u32>(str, offset + 4, offset + 6) + 1)) as T;
97
+ return (val * 10 ** (__atoi_fast<u32>(str, offset + 4, offset + 6) + 1)) as T;
124
98
  }
125
99
  } else {
126
100
  // Optimized with multiplications and shifts.
@@ -135,12 +109,10 @@
135
109
  // e is 101 and E is 69.
136
110
  if (ch > 57) {
137
111
  if (load<u16>(changetype<usize>(str) + <usize>offset + 2) == 45) {
138
- val = (val /
139
- 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)) as T;
112
+ val = (val / 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)) as T;
140
113
  } else {
141
114
  // Inlined this operation instead of using a loop
142
- val = (val *
143
- 10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1)) as T;
115
+ val = (val * 10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1)) as T;
144
116
  }
145
117
  return val as T;
146
118
  } else {
@@ -165,21 +137,17 @@
165
137
  // The first char (f) is E or e
166
138
  // We push the offset up by two and apply the notation.
167
139
  if (load<u16>(changetype<usize>(str) + <usize>offset + 2) == 45) {
168
- return (val /
169
- 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)) as T;
140
+ return (val / 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)) as T;
170
141
  } else {
171
142
  // Inlined this operation instead of using a loop
172
- return (val *
173
- 10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1)) as T;
143
+ return (val * 10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1)) as T;
174
144
  }
175
145
  } else if (high > 57) {
176
146
  if (load<u16>(changetype<usize>(str) + <usize>offset + 4) == 45) {
177
- return (val /
178
- 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)) as T;
147
+ return (val / 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)) as T;
179
148
  } else {
180
149
  // Inlined this operation instead of using a loop
181
- return (val *
182
- 10 ** (__atoi_fast<u32>(str, offset + 4, offset + 6) + 1)) as T;
150
+ return (val * 10 ** (__atoi_fast<u32>(str, offset + 4, offset + 6) + 1)) as T;
183
151
  }
184
152
  } else {
185
153
  // Optimized with multiplications and shifts.
@@ -194,12 +162,10 @@
194
162
  // e is 101 and E is 69.
195
163
  if (ch > 57) {
196
164
  if (load<u16>(changetype<usize>(str) + <usize>offset + 2) == 45) {
197
- return (val /
198
- 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)) as T;
165
+ return (val / 10 ** (__atoi_fast<u32>(str, offset + 6, offset + 8) - 1)) as T;
199
166
  } else {
200
167
  // Inlined this operation instead of using a loop
201
- return (val *
202
- 10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1)) as T;
168
+ return (val * 10 ** (__atoi_fast<u32>(str, offset + 2, offset + 4) + 1)) as T;
203
169
  }
204
170
  } else {
205
171
  val = (val * 10 + (ch - 48)) as T;
@@ -214,11 +180,7 @@
214
180
  */
215
181
 
216
182
  // @ts-ignore
217
- @inline export function __atoi_fast<T extends number>(
218
- str: string,
219
- start: u32 = 0,
220
- end: u32 = 0,
221
- ): T {
183
+ @inline export function __atoi_fast<T extends number>(str: string, start: u32 = 0, end: u32 = 0): T {
222
184
  // @ts-ignore
223
185
  let val: T = 0;
224
186
  if (!end) end = start + u32(str.length << 1);
@@ -227,21 +189,18 @@
227
189
  if (load<u16>(changetype<usize>(str) + <usize>start) == 45) {
228
190
  start += 2;
229
191
  for (; start < end; start += 2) {
230
- val = (val * 10 +
231
- (load<u16>(changetype<usize>(str) + <usize>start) - 48)) as T;
192
+ val = (val * 10 + (load<u16>(changetype<usize>(str) + <usize>start) - 48)) as T;
232
193
  }
233
194
  return -val as T;
234
195
  } else {
235
196
  for (; start < end; start += 2) {
236
- val = (val * 10 +
237
- (load<u16>(changetype<usize>(str) + <usize>start) - 48)) as T;
197
+ val = (val * 10 + (load<u16>(changetype<usize>(str) + <usize>start) - 48)) as T;
238
198
  }
239
199
  return val as T;
240
200
  }
241
201
  } else {
242
202
  for (; start < end; start += 2) {
243
- val = (val * 10 +
244
- (load<u16>(changetype<usize>(str) + <usize>start) - 48)) as T;
203
+ val = (val * 10 + (load<u16>(changetype<usize>(str) + <usize>start) - 48)) as T;
245
204
  }
246
205
  return val as T;
247
206
  }
@@ -308,12 +267,7 @@
308
267
  }
309
268
 
310
269
  // @ts-ignore
311
- @inline export function containsCodePoint(
312
- str: string,
313
- code: u32,
314
- start: i32,
315
- end: i32,
316
- ): bool {
270
+ @inline export function containsCodePoint(str: string, code: u32, start: i32, end: i32): bool {
317
271
  for (let i = start; i <= end; i++) {
318
272
  if (unsafeCharCodeAt(str, i) == code) return true;
319
273
  }
@@ -0,0 +1,181 @@
1
+ import { ptrToStr } from "../util/ptrToStr";
2
+
3
+ // @ts-ignore: inline
4
+ @inline function pow10Fast(exponent: u32): f64 {
5
+ let result = 1.0;
6
+ if (exponent & 1) result *= 1e1;
7
+ if (exponent & 2) result *= 1e2;
8
+ if (exponent & 4) result *= 1e4;
9
+ if (exponent & 8) result *= 1e8;
10
+ if (exponent & 16) result *= 1e16;
11
+ if (exponent & 32) result *= 1e32;
12
+ if (exponent & 64) result *= 1e64;
13
+ if (exponent & 128) result *= 1e128;
14
+ if (exponent & 256) result *= 1e256;
15
+ return result;
16
+ }
17
+
18
+ // @ts-ignore: inline
19
+ @inline export function deserializeFloat<T>(srcStart: usize, srcEnd: usize): T {
20
+ let negative = false;
21
+ if (load<u16>(srcStart) == 45) {
22
+ negative = true;
23
+ srcStart += 2;
24
+ if (srcStart >= srcEnd) unreachable();
25
+ }
26
+
27
+ let value: f64 = 0.0;
28
+ let seenDigit = false;
29
+
30
+ while (srcStart < srcEnd) {
31
+ const digit = <u32>load<u16>(srcStart) - 48;
32
+ if (digit > 9) break;
33
+ value = value * 10.0 + <f64>digit;
34
+ seenDigit = true;
35
+ srcStart += 2;
36
+ }
37
+
38
+ if (srcStart < srcEnd && load<u16>(srcStart) == 46) {
39
+ srcStart += 2;
40
+ let fraction: u64 = 0;
41
+ let digits: u32 = 0;
42
+ while (srcStart < srcEnd) {
43
+ const digit = <u32>load<u16>(srcStart) - 48;
44
+ if (digit > 9) break;
45
+ fraction = fraction * 10 + digit;
46
+ digits += 1;
47
+ seenDigit = true;
48
+ srcStart += 2;
49
+ }
50
+ if (digits != 0) value += <f64>fraction / pow10Fast(digits);
51
+ }
52
+
53
+ if (!seenDigit) {
54
+ // @ts-ignore
55
+ const type: T = 0;
56
+ // @ts-ignore
57
+ if (type instanceof f64) return f64.parse(ptrToStr(srcStart, srcEnd));
58
+ // @ts-ignore
59
+ return f32.parse(ptrToStr(srcStart, srcEnd));
60
+ }
61
+
62
+ if (srcStart < srcEnd) {
63
+ const code = load<u16>(srcStart);
64
+ if (code == 101 || code == 69) {
65
+ srcStart += 2;
66
+ if (srcStart >= srcEnd) unreachable();
67
+
68
+ let exponentNegative = false;
69
+ let exponentCode = load<u16>(srcStart);
70
+ if (exponentCode == 45 || exponentCode == 43) {
71
+ exponentNegative = exponentCode == 45;
72
+ srcStart += 2;
73
+ if (srcStart >= srcEnd) unreachable();
74
+ exponentCode = load<u16>(srcStart);
75
+ }
76
+
77
+ let exponent = <u32>exponentCode - 48;
78
+ if (exponent > 9) unreachable();
79
+ srcStart += 2;
80
+ while (srcStart < srcEnd) {
81
+ const digit = <u32>load<u16>(srcStart) - 48;
82
+ if (digit > 9) break;
83
+ exponent = exponent * 10 + digit;
84
+ srcStart += 2;
85
+ }
86
+
87
+ const power = pow10Fast(exponent);
88
+ value = exponentNegative ? value / power : value * power;
89
+ }
90
+ }
91
+
92
+ if (negative) value = -value;
93
+
94
+ // @ts-ignore
95
+ const type: T = 0;
96
+ // @ts-ignore
97
+ if (type instanceof f64) return <T>value;
98
+ // @ts-ignore
99
+ return <T>(<f32>value);
100
+ }
101
+
102
+ // @ts-ignore: inline
103
+ @inline export function deserializeFloatField<T extends number>(srcStart: usize, srcEnd: usize, fieldPtr: usize): usize {
104
+ let negative = false;
105
+ if (load<u16>(srcStart) == 45) {
106
+ negative = true;
107
+ srcStart += 2;
108
+ if (srcStart >= srcEnd) unreachable();
109
+ }
110
+
111
+ let value: f64 = 0.0;
112
+ let seenDigit = false;
113
+
114
+ while (srcStart < srcEnd) {
115
+ const code = load<u16>(srcStart);
116
+ const digit = <u32>code - 48;
117
+ if (digit > 9) break;
118
+ value = value * 10.0 + <f64>digit;
119
+ seenDigit = true;
120
+ srcStart += 2;
121
+ }
122
+
123
+ if (srcStart < srcEnd && load<u16>(srcStart) == 46) {
124
+ srcStart += 2;
125
+ let fraction: u64 = 0;
126
+ let digits: u32 = 0;
127
+ while (srcStart < srcEnd) {
128
+ const code = load<u16>(srcStart);
129
+ const digit = <u32>code - 48;
130
+ if (digit > 9) break;
131
+ fraction = fraction * 10 + digit;
132
+ digits += 1;
133
+ seenDigit = true;
134
+ srcStart += 2;
135
+ }
136
+ if (digits != 0) value += <f64>fraction / pow10Fast(digits);
137
+ }
138
+
139
+ if (!seenDigit) unreachable();
140
+
141
+ if (srcStart < srcEnd) {
142
+ const code = load<u16>(srcStart);
143
+ if (code == 101 || code == 69) {
144
+ srcStart += 2;
145
+ if (srcStart >= srcEnd) unreachable();
146
+
147
+ let exponentNegative = false;
148
+ let exponentCode = load<u16>(srcStart);
149
+ if (exponentCode == 45 || exponentCode == 43) {
150
+ exponentNegative = exponentCode == 45;
151
+ srcStart += 2;
152
+ if (srcStart >= srcEnd) unreachable();
153
+ exponentCode = load<u16>(srcStart);
154
+ }
155
+
156
+ let exponent = <u32>exponentCode - 48;
157
+ if (exponent > 9) unreachable();
158
+ srcStart += 2;
159
+ while (srcStart < srcEnd) {
160
+ const code = load<u16>(srcStart);
161
+ const digit = <u32>code - 48;
162
+ if (digit > 9) break;
163
+ exponent = exponent * 10 + digit;
164
+ srcStart += 2;
165
+ }
166
+
167
+ const power = pow10Fast(exponent);
168
+ value = exponentNegative ? value / power : value * power;
169
+ }
170
+ }
171
+
172
+ if (negative) value = -value;
173
+
174
+ if (sizeof<T>() == sizeof<f32>()) {
175
+ store<f32>(fieldPtr, <f32>value);
176
+ } else {
177
+ store<f64>(fieldPtr, value);
178
+ }
179
+
180
+ return srcStart;
181
+ }
@@ -0,0 +1,12 @@
1
+ export function deserializeUintScan<T extends number>(src: usize, dst: usize): usize {
2
+ let digit = <T>load<u16>(src) - 48;
3
+ if (digit > 9) abort("Found invalid digit");
4
+ let val = digit;
5
+ src += 2;
6
+ while ((digit = <u32>load<u16>(src) - 48) < 10) {
7
+ val = val * 10 + digit;
8
+ src += 2;
9
+ }
10
+ store<T>(dst, val);
11
+ return src;
12
+ }