json-as 1.3.6 → 1.3.8

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 (155) hide show
  1. package/CHANGELOG.md +45 -0
  2. package/README.md +1 -1
  3. package/assembly/deserialize/helpers/uint.ts +4 -1
  4. package/assembly/deserialize/index/arbitrary.ts +7 -3
  5. package/assembly/deserialize/index/array.ts +42 -17
  6. package/assembly/deserialize/index/bool.ts +1 -1
  7. package/assembly/deserialize/index/date.ts +1 -1
  8. package/assembly/deserialize/index/float.ts +40 -1
  9. package/assembly/deserialize/index/integer.ts +68 -1
  10. package/assembly/deserialize/index/map.ts +1 -1
  11. package/assembly/deserialize/index/object.ts +1 -1
  12. package/assembly/deserialize/index/raw.ts +1 -1
  13. package/assembly/deserialize/index/set.ts +1 -1
  14. package/assembly/deserialize/index/staticarray.ts +4 -1
  15. package/assembly/deserialize/index/string.ts +32 -4
  16. package/assembly/deserialize/index/struct.ts +1 -1
  17. package/assembly/deserialize/index/typedarray.ts +30 -10
  18. package/assembly/deserialize/index/unsigned.ts +78 -1
  19. package/assembly/deserialize/index.ts +1 -0
  20. package/assembly/deserialize/{simple → naive}/array/arbitrary.ts +24 -5
  21. package/assembly/deserialize/{simple → naive}/array/array.ts +8 -2
  22. package/assembly/deserialize/naive/array/bool.ts +68 -0
  23. package/assembly/deserialize/{simple → naive}/array/box.ts +8 -2
  24. package/assembly/deserialize/naive/array/float.ts +63 -0
  25. package/assembly/deserialize/{simple → naive}/array/generic.ts +14 -7
  26. package/assembly/deserialize/naive/array/integer.ts +86 -0
  27. package/assembly/deserialize/naive/array/map.ts +47 -0
  28. package/assembly/deserialize/naive/array/object.ts +47 -0
  29. package/assembly/deserialize/{simple → naive}/array/raw.ts +34 -7
  30. package/assembly/deserialize/naive/array/string.ts +69 -0
  31. package/assembly/deserialize/naive/array/struct.ts +47 -0
  32. package/assembly/deserialize/{simple → naive}/array.ts +15 -10
  33. package/assembly/deserialize/{simple → naive}/bool.ts +6 -2
  34. package/assembly/deserialize/naive/float.ts +135 -0
  35. package/assembly/deserialize/{simple → naive}/integer.ts +10 -2
  36. package/assembly/deserialize/{simple → naive}/map.ts +106 -27
  37. package/assembly/deserialize/{simple → naive}/object.ts +65 -19
  38. package/assembly/deserialize/{simple → naive}/raw.ts +4 -1
  39. package/assembly/deserialize/{simple → naive}/set.ts +49 -19
  40. package/assembly/deserialize/{simple → naive}/staticarray/array.ts +1 -1
  41. package/assembly/deserialize/{simple → naive}/staticarray/bool.ts +1 -1
  42. package/assembly/deserialize/{simple → naive}/staticarray/float.ts +1 -1
  43. package/assembly/deserialize/{simple → naive}/staticarray/integer.ts +1 -1
  44. package/assembly/deserialize/{simple → naive}/staticarray/string.ts +11 -3
  45. package/assembly/deserialize/{simple → naive}/staticarray/struct.ts +1 -2
  46. package/assembly/deserialize/{simple → naive}/staticarray.ts +68 -18
  47. package/assembly/deserialize/naive/string.ts +199 -0
  48. package/assembly/deserialize/{simple → naive}/struct.ts +5 -1
  49. package/assembly/deserialize/{simple → naive}/typedarray.ts +17 -4
  50. package/assembly/deserialize/{simple → naive}/unsigned.ts +10 -15
  51. package/assembly/deserialize/simd/array/integer.ts +339 -62
  52. package/assembly/deserialize/simd/float.ts +303 -0
  53. package/assembly/deserialize/simd/integer.ts +233 -0
  54. package/assembly/deserialize/simd/string.ts +266 -107
  55. package/assembly/deserialize/swar/array/arbitrary.ts +11 -3
  56. package/assembly/deserialize/swar/array/array.ts +40 -9
  57. package/assembly/deserialize/swar/array/bool.ts +28 -5
  58. package/assembly/deserialize/swar/array/box.ts +11 -3
  59. package/assembly/deserialize/swar/array/float.ts +295 -7
  60. package/assembly/deserialize/swar/array/generic.ts +28 -7
  61. package/assembly/deserialize/swar/array/integer.ts +363 -112
  62. package/assembly/deserialize/swar/array/map.ts +11 -3
  63. package/assembly/deserialize/swar/array/object.ts +37 -25
  64. package/assembly/deserialize/swar/array/raw.ts +11 -3
  65. package/assembly/deserialize/swar/array/shared.ts +63 -14
  66. package/assembly/deserialize/swar/array/string.ts +140 -7
  67. package/assembly/deserialize/swar/array/struct.ts +66 -12
  68. package/assembly/deserialize/swar/array.ts +12 -51
  69. package/assembly/deserialize/swar/float.ts +304 -0
  70. package/assembly/deserialize/swar/integer.ts +246 -0
  71. package/assembly/deserialize/swar/string.ts +213 -294
  72. package/assembly/deserialize/swar/typedarray.ts +224 -0
  73. package/assembly/index.d.ts +3 -1
  74. package/assembly/index.ts +402 -261
  75. package/assembly/serialize/index/array.ts +1 -1
  76. package/assembly/serialize/index/bool.ts +1 -1
  77. package/assembly/serialize/index/date.ts +1 -1
  78. package/assembly/serialize/index/float.ts +5 -1
  79. package/assembly/serialize/index/integer.ts +1 -1
  80. package/assembly/serialize/index/map.ts +1 -1
  81. package/assembly/serialize/index/raw.ts +1 -1
  82. package/assembly/serialize/index/set.ts +1 -1
  83. package/assembly/serialize/index/staticarray.ts +1 -1
  84. package/assembly/serialize/index/string.ts +1 -1
  85. package/assembly/serialize/index/struct.ts +1 -1
  86. package/assembly/serialize/index/typedarray.ts +21 -12
  87. package/assembly/serialize/index.ts +1 -0
  88. package/assembly/serialize/naive/array.ts +351 -0
  89. package/assembly/serialize/{simple → naive}/float.ts +4 -1
  90. package/assembly/serialize/naive/integer.ts +19 -0
  91. package/assembly/serialize/{simple → naive}/map.ts +6 -2
  92. package/assembly/serialize/{simple → naive}/raw.ts +5 -1
  93. package/assembly/serialize/{simple → naive}/set.ts +6 -1
  94. package/assembly/serialize/{simple → naive}/staticarray.ts +6 -1
  95. package/assembly/serialize/{simple → naive}/string.ts +1 -2
  96. package/assembly/serialize/{simple → naive}/typedarray.ts +10 -3
  97. package/assembly/serialize/simd/string.ts +6 -2
  98. package/assembly/serialize/swar/string.ts +15 -141
  99. package/assembly/util/atoi-fast.ts +81 -0
  100. package/assembly/util/concat.ts +5 -1
  101. package/assembly/util/dragonbox-cache.ts +443 -2
  102. package/assembly/util/dragonbox.ts +53 -17
  103. package/assembly/util/itoa-fast.ts +241 -0
  104. package/assembly/util/masks.ts +18 -1
  105. package/assembly/util/parsefloat-fast.ts +167 -0
  106. package/assembly/util/scanValueEnd.ts +78 -0
  107. package/assembly/util/scientific.ts +132 -0
  108. package/assembly/util/simd-int.ts +191 -0
  109. package/assembly/util/snp.ts +4 -1
  110. package/assembly/util/swar-int.ts +248 -0
  111. package/assembly/util/swar.ts +13 -3
  112. package/lib/as-bs.ts +27 -6
  113. package/package.json +15 -11
  114. package/transform/lib/builder.d.ts.map +1 -1
  115. package/transform/lib/builder.js +13 -5
  116. package/transform/lib/builder.js.map +1 -1
  117. package/transform/lib/index.d.ts +5 -0
  118. package/transform/lib/index.d.ts.map +1 -1
  119. package/transform/lib/index.js +1046 -340
  120. package/transform/lib/index.js.map +1 -1
  121. package/transform/lib/linkers/alias.d.ts.map +1 -1
  122. package/transform/lib/linkers/alias.js.map +1 -1
  123. package/transform/lib/linkers/custom.d.ts.map +1 -1
  124. package/transform/lib/linkers/custom.js +3 -2
  125. package/transform/lib/linkers/custom.js.map +1 -1
  126. package/transform/lib/linkers/imports.d.ts.map +1 -1
  127. package/transform/lib/linkers/imports.js.map +1 -1
  128. package/transform/lib/types.d.ts.map +1 -1
  129. package/transform/lib/types.js +54 -16
  130. package/transform/lib/types.js.map +1 -1
  131. package/transform/lib/util.d.ts.map +1 -1
  132. package/transform/lib/util.js +1 -1
  133. package/transform/lib/util.js.map +1 -1
  134. package/transform/lib/visitor.d.ts.map +1 -1
  135. package/transform/lib/visitor.js +2 -1
  136. package/transform/lib/visitor.js.map +1 -1
  137. package/assembly/custom/util.ts +0 -310
  138. package/assembly/deserialize/simple/arbitrary.ts +0 -23
  139. package/assembly/deserialize/simple/array/bool.ts +0 -17
  140. package/assembly/deserialize/simple/array/float.ts +0 -28
  141. package/assembly/deserialize/simple/array/integer.ts +0 -27
  142. package/assembly/deserialize/simple/array/map.ts +0 -28
  143. package/assembly/deserialize/simple/array/object.ts +0 -28
  144. package/assembly/deserialize/simple/array/string.ts +0 -23
  145. package/assembly/deserialize/simple/array/struct.ts +0 -28
  146. package/assembly/deserialize/simple/float.ts +0 -201
  147. package/assembly/deserialize/simple/string.ts +0 -132
  148. package/assembly/serialize/simple/arbitrary.ts +0 -79
  149. package/assembly/serialize/simple/array.ts +0 -86
  150. package/assembly/serialize/simple/integer.ts +0 -20
  151. package/assembly/serialize/simple/object.ts +0 -42
  152. /package/assembly/deserialize/{simple → naive}/date.ts +0 -0
  153. /package/assembly/serialize/{simple → naive}/bool.ts +0 -0
  154. /package/assembly/serialize/{simple → naive}/date.ts +0 -0
  155. /package/assembly/serialize/{simple → naive}/struct.ts +0 -0
@@ -1,5 +1,6 @@
1
1
  import { DRAGONBOX_F32_CACHE, DRAGONBOX_F64_CACHE } from "./dragonbox-cache";
2
- import { decimalCount32, itoa_buffered, utoa32_dec_core } from "util/number";
2
+ import { decimalCount32, utoa32_dec_core } from "util/number";
3
+ import { ensureItoaPairs, itoaU32, itoaU64 } from "./itoa-fast";
3
4
 
4
5
  const CHAR_MINUS: u16 = 45;
5
6
  const CHAR_DOT: u16 = 46;
@@ -188,7 +189,12 @@ function computeMul64(u: u64, cacheHigh: u64, cacheLow: u64): void {
188
189
 
189
190
 
190
191
  @inline
191
- function computeMulParity64(twoF: u64, cacheHigh: u64, cacheLow: u64, beta: i32): void {
192
+ function computeMulParity64(
193
+ twoF: u64,
194
+ cacheHigh: u64,
195
+ cacheLow: u64,
196
+ beta: i32,
197
+ ): void {
192
198
  const high = twoF * cacheHigh;
193
199
 
194
200
  const a = <u32>(twoF >>> 32);
@@ -205,7 +211,8 @@ function computeMulParity64(twoF: u64, cacheHigh: u64, cacheLow: u64, beta: i32)
205
211
 
206
212
  const rHigh = high + lowHigh;
207
213
  _dbParity = ((rHigh >>> (64 - beta)) & 1) != 0;
208
- _dbMulIsInteger = (((rHigh << beta) & 0xffffffffffffffff) | (lowLow >>> (64 - beta))) == 0;
214
+ _dbMulIsInteger =
215
+ (((rHigh << beta) & 0xffffffffffffffff) | (lowLow >>> (64 - beta))) == 0;
209
216
  }
210
217
 
211
218
 
@@ -298,7 +305,8 @@ function prettify(buffer: usize, length: i32, k: i32): i32 {
298
305
 
299
306
  const kk = length + k;
300
307
  if (length <= kk && kk <= 21) {
301
- for (let i = length; i < kk; ++i) store<u16>(buffer + ((<usize>i) << 1), CHAR_0);
308
+ for (let i = length; i < kk; ++i)
309
+ store<u16>(buffer + ((<usize>i) << 1), CHAR_0);
302
310
  const tail = buffer + ((<usize>kk) << 1);
303
311
  store<u16>(tail, CHAR_DOT);
304
312
  store<u16>(tail, CHAR_0, 2);
@@ -313,7 +321,8 @@ function prettify(buffer: usize, length: i32, k: i32): i32 {
313
321
  memory.copy(buffer + ((<usize>offset) << 1), buffer, (<usize>length) << 1);
314
322
  store<u16>(buffer, CHAR_0);
315
323
  store<u16>(buffer, CHAR_DOT, 2);
316
- for (let i = 2; i < offset; ++i) store<u16>(buffer + ((<usize>i) << 1), CHAR_0);
324
+ for (let i = 2; i < offset; ++i)
325
+ store<u16>(buffer + ((<usize>i) << 1), CHAR_0);
317
326
  return length + offset;
318
327
  } else if (length == 1) {
319
328
  store<u16>(buffer, CHAR_E, 2);
@@ -365,7 +374,10 @@ function prettifyFast(buffer: usize, length: i32, k: i32): i32 {
365
374
  return -1;
366
375
  }
367
376
 
368
- function dragonboxToDecimalF32(binarySignificand: u32, binaryExponent: i32): u32 {
377
+ function dragonboxToDecimalF32(
378
+ binarySignificand: u32,
379
+ binaryExponent: i32,
380
+ ): u32 {
369
381
  const isEven = (binarySignificand & 1) == 0;
370
382
  let twoFc = binarySignificand << 1;
371
383
 
@@ -374,7 +386,9 @@ function dragonboxToDecimalF32(binarySignificand: u32, binaryExponent: i32): u32
374
386
  if (twoFc == 0) {
375
387
  const minusK = floor_log10_pow2_minus_log10_4_over_3(binaryExponent);
376
388
  const beta = binaryExponent + floor_log2_pow10(-minusK);
377
- const cache = load<u64>(DRAGONBOX_F32_CACHE + ((<usize>(31 - minusK)) << 3));
389
+ const cache = load<u64>(
390
+ DRAGONBOX_F32_CACHE + ((<usize>(31 - minusK)) << 3),
391
+ );
378
392
  let xi = computeLeftEndpointShorter32(cache, beta);
379
393
  const zi = computeRightEndpointShorter32(cache, beta);
380
394
  if (!(binaryExponent >= 2 && binaryExponent <= 3)) ++xi;
@@ -387,7 +401,8 @@ function dragonboxToDecimalF32(binarySignificand: u32, binaryExponent: i32): u32
387
401
  return decimalSignificand;
388
402
  }
389
403
  decimalSignificand = computeRoundUpShorter32(cache, beta);
390
- if ((decimalSignificand & 1) != 0 && binaryExponent == -35) --decimalSignificand;
404
+ if ((decimalSignificand & 1) != 0 && binaryExponent == -35)
405
+ --decimalSignificand;
391
406
  else if (decimalSignificand < xi) ++decimalSignificand;
392
407
  _dbK = minusK;
393
408
  return decimalSignificand;
@@ -444,14 +459,18 @@ function dragonboxToDecimalF32(binarySignificand: u32, binaryExponent: i32): u32
444
459
  if ((packedDiv & 1) != 0) {
445
460
  computeMulParity32(twoFc, cache, beta);
446
461
  if (_dbParity != approxYParity) --decimalSignificand;
447
- else if ((decimalSignificand & 1) != 0 && _dbMulIsInteger) --decimalSignificand;
462
+ else if ((decimalSignificand & 1) != 0 && _dbMulIsInteger)
463
+ --decimalSignificand;
448
464
  }
449
465
 
450
466
  _dbK = minusK + 1;
451
467
  return decimalSignificand;
452
468
  }
453
469
 
454
- function dragonboxToDecimalF64(binarySignificand: u64, binaryExponent: i32): u64 {
470
+ function dragonboxToDecimalF64(
471
+ binarySignificand: u64,
472
+ binaryExponent: i32,
473
+ ): u64 {
455
474
  const isEven = (binarySignificand & 1) == 0;
456
475
  let twoFc = binarySignificand << 1;
457
476
 
@@ -475,7 +494,8 @@ function dragonboxToDecimalF64(binarySignificand: u64, binaryExponent: i32): u64
475
494
  return decimalSignificand;
476
495
  }
477
496
  decimalSignificand = computeRoundUpShorter64(cacheHigh, beta);
478
- if ((decimalSignificand & 1) != 0 && binaryExponent == -77) --decimalSignificand;
497
+ if ((decimalSignificand & 1) != 0 && binaryExponent == -77)
498
+ --decimalSignificand;
479
499
  else if (decimalSignificand < xi) ++decimalSignificand;
480
500
  _dbK = minusK;
481
501
  return decimalSignificand;
@@ -534,7 +554,8 @@ function dragonboxToDecimalF64(binarySignificand: u64, binaryExponent: i32): u64
534
554
  if ((packedDiv & 1) != 0) {
535
555
  computeMulParity64(twoFc, cacheHigh, cacheLow, beta);
536
556
  if (_dbParity != approxYParity) --decimalSignificand;
537
- else if ((decimalSignificand & 1) != 0 && _dbMulIsInteger) --decimalSignificand;
557
+ else if ((decimalSignificand & 1) != 0 && _dbMulIsInteger)
558
+ --decimalSignificand;
538
559
  }
539
560
 
540
561
  _dbK = minusK + 2;
@@ -548,8 +569,15 @@ function dragonboxCoreF32(buffer: usize, value: f32): u32 {
548
569
  value = -value;
549
570
  store<u16>(buffer, CHAR_MINUS);
550
571
  }
551
- const digits = dragonboxToDecimalF32(reinterpret<u32>(value) & 0x7fffff, (reinterpret<u32>(value) >>> 23) & 0xff);
552
- let len = itoa_buffered<u32>(buffer + ((<usize>sign) << 1), digits);
572
+ const digits = dragonboxToDecimalF32(
573
+ reinterpret<u32>(value) & 0x7fffff,
574
+ (reinterpret<u32>(value) >>> 23) & 0xff,
575
+ );
576
+ // Use the jeaiii-style itoa (forward write + 2-digit-pair LUT) instead
577
+ // of AS stdlib's `itoa_buffered`, which runs a separate width classifier
578
+ // + backward div-by-10000 loop. Saves a function call and ~half the
579
+ // per-digit work for the typical 7-17 digit dragonbox output.
580
+ let len = itoaU32(buffer + ((<usize>sign) << 1), digits);
553
581
  return <u32>(prettify(buffer + ((<usize>sign) << 1), len, _dbK) + sign);
554
582
  }
555
583
 
@@ -561,8 +589,11 @@ function dragonboxCoreF64(buffer: usize, value: f64): u32 {
561
589
  store<u16>(buffer, CHAR_MINUS);
562
590
  }
563
591
  const bits = reinterpret<u64>(value);
564
- const digits = dragonboxToDecimalF64(bits & 0x000fffffffffffff, <i32>((bits >>> 52) & 0x7ff));
565
- let len = itoa_buffered<u64>(buffer + ((<usize>sign) << 1), digits);
592
+ const digits = dragonboxToDecimalF64(
593
+ bits & 0x000fffffffffffff,
594
+ <i32>((bits >>> 52) & 0x7ff),
595
+ );
596
+ let len = itoaU64(buffer + ((<usize>sign) << 1), digits);
566
597
  return <u32>(prettify(buffer + ((<usize>sign) << 1), len, _dbK) + sign);
567
598
  }
568
599
 
@@ -589,6 +620,7 @@ export function dragonbox_f32_buffered(buffer: usize, value: f32): u32 {
589
620
  store<u64>(buffer + 8, 0x7900740069006e);
590
621
  return 8 + (sign ? 1 : 0);
591
622
  }
623
+ ensureItoaPairs();
592
624
  return dragonboxCoreF32(buffer, value);
593
625
  }
594
626
 
@@ -615,10 +647,14 @@ export function dragonbox_f64_buffered(buffer: usize, value: f64): u32 {
615
647
  store<u64>(buffer + 8, 0x7900740069006e);
616
648
  return 8 + (sign ? 1 : 0);
617
649
  }
650
+ ensureItoaPairs();
618
651
  return dragonboxCoreF64(buffer, value);
619
652
  }
620
653
 
621
- export function dragonbox_buffered<T extends number>(buffer: usize, value: T): u32 {
654
+ export function dragonbox_buffered<T extends number>(
655
+ buffer: usize,
656
+ value: T,
657
+ ): u32 {
622
658
  if (sizeof<T>() == 4) return dragonbox_f32_buffered(buffer, <f32>value);
623
659
  return dragonbox_f64_buffered(buffer, <f64>value);
624
660
  }
@@ -0,0 +1,241 @@
1
+ // Fast integer -> UTF-16 stringification.
2
+ //
3
+ // We tried the "real" jeaiii algorithm (fixed-point magic-multiplication
4
+ // per bucket; `f0 -> f2 -> f4 -> f6` chained fractional-part extractions)
5
+ // and it ran ~5-7% slower on V8/wasm than the div-by-constant variant
6
+ // below. Two reasons:
7
+ //
8
+ // 1. V8/wasm lowers `v / 100` (and other `/ <const>`s) to a single
9
+ // multiply-shift, so jeaiii's main selling point — avoiding division
10
+ // hardware — gives no win on this target. The op counts come out
11
+ // roughly equal.
12
+ //
13
+ // 2. The div-by-const variant computes each digit pair independently
14
+ // from `v` (`h = v / 100`, `l = v - h*100`, etc), so V8 schedules
15
+ // the LUT loads + stores for all pairs in parallel. The jeaiii
16
+ // chain forces them serial.
17
+ //
18
+ // What we keep from jeaiii here:
19
+ //
20
+ // - Width-ladder dispatch (`if v < 100 / 10_000 / 1_000_000 / ...`) so
21
+ // the same comparisons that would drive a separate `decimalCount`
22
+ // pass become the bucket pick.
23
+ //
24
+ // - A 100-entry digit-pair LUT keyed on `value % 100`. One `store<u32>`
25
+ // emits a UTF-16 pair.
26
+ //
27
+ // - Forward write in one pass — no `decimalCount32` precomputation, no
28
+ // backward write.
29
+ //
30
+ // Reference H2H bench: `__benches__/custom/itoa-h2h.bench.ts`.
31
+
32
+ // 100-entry pair LUT: index `i` -> u32 holding UTF-16 chars for the
33
+ // zero-padded two-digit string "DD". One `store<u32>` writes the pair.
34
+ const DIGIT_PAIRS_UTF16: usize = memory.data(100 * 4);
35
+ let _pairsInited: bool = false;
36
+
37
+ function initPairs(): void {
38
+ for (let i: i32 = 0; i < 100; i++) {
39
+ const tens = u32(0x30 + i / 10);
40
+ const units = u32(0x30 + (i % 10));
41
+ store<u32>(DIGIT_PAIRS_UTF16 + ((<usize>i) << 2), tens | (units << 16));
42
+ }
43
+ _pairsInited = true;
44
+ }
45
+
46
+
47
+ @inline export function ensureItoaPairs(): void {
48
+ if (!_pairsInited) initPairs();
49
+ }
50
+
51
+ // @ts-expect-error: @inline is a valid decorator
52
+ @inline function pair(i: u32): u32 {
53
+ return load<u32>(DIGIT_PAIRS_UTF16 + ((<usize>i) << 2));
54
+ }
55
+
56
+ /**
57
+ * u32 -> UTF-16 stringification, forward write.
58
+ * Returns the number of UTF-16 chars written (caller multiplies by 2 for
59
+ * a byte offset). Caller must ensure the buffer has at least 20 bytes
60
+ * available (max 10 chars).
61
+ */
62
+ // @ts-expect-error: @inline is a valid decorator
63
+ @inline export function itoaU32(buf: usize, v: u32): u32 {
64
+ if (v < 10) {
65
+ store<u16>(buf, <u16>(v + 0x30));
66
+ return 1;
67
+ }
68
+ if (v < 100) {
69
+ store<u32>(buf, pair(v));
70
+ return 2;
71
+ }
72
+ if (v < 1_000_000) {
73
+ if (v < 10_000) {
74
+ if (v < 1_000) {
75
+ const h = v / 100;
76
+ const l = v - h * 100;
77
+ store<u16>(buf, <u16>(h + 0x30));
78
+ store<u32>(buf, pair(l), 2);
79
+ return 3;
80
+ }
81
+ const h = v / 100;
82
+ const l = v - h * 100;
83
+ store<u32>(buf, pair(h));
84
+ store<u32>(buf, pair(l), 4);
85
+ return 4;
86
+ }
87
+ if (v < 100_000) {
88
+ const hi = v / 10_000;
89
+ const rest = v - hi * 10_000;
90
+ const m = rest / 100;
91
+ const l = rest - m * 100;
92
+ store<u16>(buf, <u16>(hi + 0x30));
93
+ store<u32>(buf, pair(m), 2);
94
+ store<u32>(buf, pair(l), 6);
95
+ return 5;
96
+ }
97
+ const hi = v / 10_000;
98
+ const rest = v - hi * 10_000;
99
+ const m = rest / 100;
100
+ const l = rest - m * 100;
101
+ store<u32>(buf, pair(hi));
102
+ store<u32>(buf, pair(m), 4);
103
+ store<u32>(buf, pair(l), 8);
104
+ return 6;
105
+ }
106
+ if (v < 100_000_000) {
107
+ if (v < 10_000_000) {
108
+ const top = v / 1_000_000;
109
+ let rest = v - top * 1_000_000;
110
+ const m = rest / 10_000;
111
+ rest = rest - m * 10_000;
112
+ const n = rest / 100;
113
+ const l = rest - n * 100;
114
+ store<u16>(buf, <u16>(top + 0x30));
115
+ store<u32>(buf, pair(m), 2);
116
+ store<u32>(buf, pair(n), 6);
117
+ store<u32>(buf, pair(l), 10);
118
+ return 7;
119
+ }
120
+ const top = v / 1_000_000;
121
+ let rest = v - top * 1_000_000;
122
+ const m = rest / 10_000;
123
+ rest = rest - m * 10_000;
124
+ const n = rest / 100;
125
+ const l = rest - n * 100;
126
+ store<u32>(buf, pair(top));
127
+ store<u32>(buf, pair(m), 4);
128
+ store<u32>(buf, pair(n), 8);
129
+ store<u32>(buf, pair(l), 12);
130
+ return 8;
131
+ }
132
+ if (v < 1_000_000_000) {
133
+ const top = v / 100_000_000;
134
+ let rest = v - top * 100_000_000;
135
+ const a = rest / 1_000_000;
136
+ rest = rest - a * 1_000_000;
137
+ const b = rest / 10_000;
138
+ rest = rest - b * 10_000;
139
+ const c = rest / 100;
140
+ const d = rest - c * 100;
141
+ store<u16>(buf, <u16>(top + 0x30));
142
+ store<u32>(buf, pair(a), 2);
143
+ store<u32>(buf, pair(b), 6);
144
+ store<u32>(buf, pair(c), 10);
145
+ store<u32>(buf, pair(d), 14);
146
+ return 9;
147
+ }
148
+ const top = v / 100_000_000;
149
+ let rest = v - top * 100_000_000;
150
+ const a = rest / 1_000_000;
151
+ rest = rest - a * 1_000_000;
152
+ const b = rest / 10_000;
153
+ rest = rest - b * 10_000;
154
+ const c = rest / 100;
155
+ const d = rest - c * 100;
156
+ store<u32>(buf, pair(top));
157
+ store<u32>(buf, pair(a), 4);
158
+ store<u32>(buf, pair(b), 8);
159
+ store<u32>(buf, pair(c), 12);
160
+ store<u32>(buf, pair(d), 16);
161
+ return 10;
162
+ }
163
+
164
+ /**
165
+ * Writes a u32 in the range 0..99_999_999 as exactly 8 UTF-16 chars with
166
+ * leading zeros. Used by the u64 path to emit trailing groups of 8 digits.
167
+ */
168
+ // @ts-expect-error: @inline is a valid decorator
169
+ @inline function writeU32Padded8(buf: usize, v: u32): void {
170
+ const a = v / 1_000_000;
171
+ let rest = v - a * 1_000_000;
172
+ const b = rest / 10_000;
173
+ rest = rest - b * 10_000;
174
+ const c = rest / 100;
175
+ const d = rest - c * 100;
176
+ store<u32>(buf, pair(a));
177
+ store<u32>(buf, pair(b), 4);
178
+ store<u32>(buf, pair(c), 8);
179
+ store<u32>(buf, pair(d), 12);
180
+ }
181
+
182
+ /**
183
+ * u64 -> UTF-16 stringification.
184
+ * Small values delegate to `itoaU32`. For 11+ digit values, peel 8 digits
185
+ * from the bottom (always fits in u32), emit the remaining top via the
186
+ * u32 path, then emit the 8 trailing digits with leading-zero padding.
187
+ * For 17+ digit values (which still fit in u64 < 1.8e19), repeat.
188
+ * Caller must ensure the buffer has at least 40 bytes available.
189
+ */
190
+ // @ts-expect-error: @inline is a valid decorator
191
+ @inline export function itoaU64(buf: usize, v: u64): u32 {
192
+ if (v <= <u64>u32.MAX_VALUE) {
193
+ return itoaU32(buf, <u32>v);
194
+ }
195
+ const lo8 = <u32>(v % 100_000_000);
196
+ const hi = v / 100_000_000;
197
+ if (hi <= <u64>u32.MAX_VALUE) {
198
+ const written = itoaU32(buf, <u32>hi);
199
+ writeU32Padded8(buf + ((<usize>written) << 1), lo8);
200
+ return written + 8;
201
+ }
202
+ // 17-20 digit case: peel a second group of 8.
203
+ const mid8 = <u32>(hi % 100_000_000);
204
+ const top = <u32>(hi / 100_000_000);
205
+ const written = itoaU32(buf, top);
206
+ writeU32Padded8(buf + ((<usize>written) << 1), mid8);
207
+ writeU32Padded8(buf + ((<usize>written) << 1) + 16, lo8);
208
+ return written + 16;
209
+ }
210
+
211
+ /**
212
+ * Generic integer -> UTF-16 entry point. Signed types peel `-` and pass
213
+ * the absolute value (via two's complement negation, which works for the
214
+ * minimum-value edge case because `u32(-i32.MIN_VALUE) == 2147483648`
215
+ * and likewise for i64).
216
+ *
217
+ * Returns the number of UTF-16 chars written.
218
+ */
219
+ // @ts-expect-error: @inline is a valid decorator
220
+ @inline export function itoaFast<T extends number>(buf: usize, value: T): u32 {
221
+ if (sizeof<T>() <= 4) {
222
+ if (isSigned<T>()) {
223
+ let v = <i32>value;
224
+ if (v < 0) {
225
+ store<u16>(buf, 0x2d); // '-'
226
+ return 1 + itoaU32(buf + 2, <u32>-v);
227
+ }
228
+ return itoaU32(buf, <u32>v);
229
+ }
230
+ return itoaU32(buf, <u32>value);
231
+ }
232
+ if (isSigned<T>()) {
233
+ let v = <i64>value;
234
+ if (v < 0) {
235
+ store<u16>(buf, 0x2d); // '-'
236
+ return 1 + itoaU64(buf + 2, <u64>-v);
237
+ }
238
+ return itoaU64(buf, <u64>value);
239
+ }
240
+ return itoaU64(buf, <u64>value);
241
+ }
@@ -25,7 +25,24 @@ export function block_to_string(block: u64): string {
25
25
  export function mask_to_string_v128(vec: v128): string {
26
26
  let result = "0x";
27
27
 
28
- const lanes: i8[] = [i8x16.extract_lane_s(vec, 0), i8x16.extract_lane_s(vec, 1), i8x16.extract_lane_s(vec, 2), i8x16.extract_lane_s(vec, 3), i8x16.extract_lane_s(vec, 4), i8x16.extract_lane_s(vec, 5), i8x16.extract_lane_s(vec, 6), i8x16.extract_lane_s(vec, 7), i8x16.extract_lane_s(vec, 8), i8x16.extract_lane_s(vec, 9), i8x16.extract_lane_s(vec, 10), i8x16.extract_lane_s(vec, 11), i8x16.extract_lane_s(vec, 12), i8x16.extract_lane_s(vec, 13), i8x16.extract_lane_s(vec, 14), i8x16.extract_lane_s(vec, 15)];
28
+ const lanes: i8[] = [
29
+ i8x16.extract_lane_s(vec, 0),
30
+ i8x16.extract_lane_s(vec, 1),
31
+ i8x16.extract_lane_s(vec, 2),
32
+ i8x16.extract_lane_s(vec, 3),
33
+ i8x16.extract_lane_s(vec, 4),
34
+ i8x16.extract_lane_s(vec, 5),
35
+ i8x16.extract_lane_s(vec, 6),
36
+ i8x16.extract_lane_s(vec, 7),
37
+ i8x16.extract_lane_s(vec, 8),
38
+ i8x16.extract_lane_s(vec, 9),
39
+ i8x16.extract_lane_s(vec, 10),
40
+ i8x16.extract_lane_s(vec, 11),
41
+ i8x16.extract_lane_s(vec, 12),
42
+ i8x16.extract_lane_s(vec, 13),
43
+ i8x16.extract_lane_s(vec, 14),
44
+ i8x16.extract_lane_s(vec, 15),
45
+ ];
29
46
 
30
47
  for (let i = 15; i >= 0; i--) {
31
48
  const byte = lanes[i];
@@ -0,0 +1,167 @@
1
+ import { ptrToStr } from "./ptrToStr";
2
+
3
+ // Lemire-style fast float parser.
4
+ //
5
+ // Reference: Daniel Lemire, "Number parsing at a gigabyte per second"
6
+ // (2021). https://arxiv.org/abs/2101.11408 — implemented in
7
+ // https://github.com/fastfloat/fast_float.
8
+ //
9
+ // The "fast path" applies when:
10
+ // - the mantissa fits in a u64 (<=19 decimal digits), and
11
+ // - the total decimal exponent is in [-22, 22], so the matching
12
+ // `1e<exp>` power-of-ten is representable exactly in f64.
13
+ //
14
+ // In that regime `value = mantissa * 10^exp` rounds correctly under
15
+ // IEEE-754: both operands are exact in f64 and the single fmul is
16
+ // correctly rounded, so the result is the same as the strictly-rounded
17
+ // reference. This covers the overwhelming majority of JSON float
18
+ // payloads (most fields are <20 significant digits and modest
19
+ // exponents). Out-of-range inputs delegate to AS std's `f64.parse`
20
+ // (Grisu-based; correctly rounded for all f64).
21
+ //
22
+ // Compared to the original digit-by-digit accumulator (`value = value *
23
+ // 10.0 + digit`) this saves both wall-time (fewer fmul/fdiv) and
24
+ // precision (one rounding instead of N).
25
+
26
+ // 23-entry table: 10^0 .. 10^22, all exact in f64. f32 fast-paths can
27
+ // reuse the same table (since 10^k for k <= 22 fits in f32 only up to
28
+ // 10^7, but the multiplication is done in f64 and narrowed at the end).
29
+ const POW10_F64_POS: usize = memory.data<f64>([
30
+ 1, 1e1, 1e2, 1e3, 1e4, 1e5, 1e6, 1e7, 1e8, 1e9, 1e10, 1e11, 1e12, 1e13, 1e14,
31
+ 1e15, 1e16, 1e17, 1e18, 1e19, 1e20, 1e21, 1e22,
32
+ ]);
33
+
34
+ const MAX_EXACT_POW10: i32 = 22;
35
+ // 2^53 = 9_007_199_254_740_992. Any u64 <= this is exact in f64.
36
+ const MAX_EXACT_MANTISSA: u64 = 1 << 53;
37
+
38
+
39
+ @inline function loadPow10(exp: u32): f64 {
40
+ return load<f64>(POW10_F64_POS + ((<usize>exp) << 3));
41
+ }
42
+
43
+
44
+ @inline function fallback<T>(srcStart: usize, srcEnd: usize): T {
45
+ const s = ptrToStr(srcStart, srcEnd);
46
+ // @ts-ignore: type
47
+ const type: T = 0;
48
+ // @ts-ignore: type
49
+ if (type instanceof f64) return <T>f64.parse(s);
50
+ // @ts-ignore: cast
51
+ return <T>(<f32>f32.parse(s));
52
+ }
53
+
54
+ /**
55
+ * Fast path for `deserializeFloat`. `srcStart..srcEnd` must contain only
56
+ * the float content (no surrounding whitespace, no `null`). Returns the
57
+ * parsed value; on the slow path falls back to `f64.parse` /
58
+ * `f32.parse` over the same range so behavior is preserved for every
59
+ * input the previous parser accepted.
60
+ *
61
+ * Structure mirrors the existing parser's split integer/fraction loops
62
+ * (TurboFan schedules these tighter than a single fused loop) but uses
63
+ * u64 accumulators throughout so a 17-digit "3.141592653589793" stays
64
+ * exact through accumulation and only loses precision at the final
65
+ * `<f64>` cast.
66
+ */
67
+ // @ts-expect-error: @inline is a valid decorator
68
+ @inline export function parseFloatFast<T>(srcStart: usize, srcEnd: usize): T {
69
+ const origStart = srcStart;
70
+ let p = srcStart;
71
+ let negative = false;
72
+ if (p < srcEnd && load<u16>(p) == 45) {
73
+ negative = true;
74
+ p += 2;
75
+ }
76
+
77
+ // Integer part.
78
+ let mantissa: u64 = 0;
79
+ let intDigits: i32 = 0;
80
+ while (p < srcEnd) {
81
+ const d = <u32>load<u16>(p) - 48;
82
+ if (d > 9) break;
83
+ mantissa = mantissa * 10 + <u64>d;
84
+ intDigits++;
85
+ p += 2;
86
+ }
87
+
88
+ // Optional fractional part.
89
+ let fracDigits: i32 = 0;
90
+ if (p < srcEnd && load<u16>(p) == 46) {
91
+ p += 2;
92
+ while (p < srcEnd) {
93
+ const d = <u32>load<u16>(p) - 48;
94
+ if (d > 9) break;
95
+ mantissa = mantissa * 10 + <u64>d;
96
+ fracDigits++;
97
+ p += 2;
98
+ }
99
+ }
100
+
101
+ const mantDigits = intDigits + fracDigits;
102
+ if (mantDigits == 0) {
103
+ // No digits seen (e.g. `.5`, `NaN`, `Infinity`) - defer to AS std.
104
+ return fallback<T>(origStart, srcEnd);
105
+ }
106
+
107
+ let exponent: i32 = -fracDigits;
108
+
109
+ // Optional `e[+-]NNN` suffix.
110
+ if (p < srcEnd) {
111
+ const c = load<u16>(p);
112
+ if (c == 101 || c == 69) {
113
+ p += 2;
114
+ if (p >= srcEnd) return fallback<T>(origStart, srcEnd);
115
+ let expNeg = false;
116
+ const sc = load<u16>(p);
117
+ if (sc == 45) {
118
+ expNeg = true;
119
+ p += 2;
120
+ } else if (sc == 43) {
121
+ p += 2;
122
+ }
123
+ if (p >= srcEnd) return fallback<T>(origStart, srcEnd);
124
+ let exp: i32 = 0;
125
+ let expDigits: i32 = 0;
126
+ while (p < srcEnd) {
127
+ const d = <u32>load<u16>(p) - 48;
128
+ if (d > 9) break;
129
+ exp = exp * 10 + <i32>d;
130
+ expDigits++;
131
+ if (expDigits > 4) {
132
+ // Pathological exponent - fall back for safety.
133
+ return fallback<T>(origStart, srcEnd);
134
+ }
135
+ p += 2;
136
+ }
137
+ if (expDigits == 0) return fallback<T>(origStart, srcEnd);
138
+ exponent += expNeg ? -exp : exp;
139
+ }
140
+ }
141
+
142
+ // Fast path eligibility: mantissa fits exactly in an f64 and exponent
143
+ // is in the exactly-representable pow10 range. Both halves are needed
144
+ // for the result to be correctly rounded. Capping `mantDigits` at 19
145
+ // is a cheaper proxy for "didn't overflow u64".
146
+ if (mantDigits > 19 || mantissa > MAX_EXACT_MANTISSA) {
147
+ return fallback<T>(origStart, srcEnd);
148
+ }
149
+ if (exponent > MAX_EXACT_POW10 || exponent < -MAX_EXACT_POW10) {
150
+ return fallback<T>(origStart, srcEnd);
151
+ }
152
+
153
+ let result = <f64>mantissa;
154
+ if (exponent > 0) {
155
+ result *= loadPow10(<u32>exponent);
156
+ } else if (exponent < 0) {
157
+ result /= loadPow10(<u32>-exponent);
158
+ }
159
+ if (negative) result = -result;
160
+
161
+ // @ts-ignore: type
162
+ const type: T = 0;
163
+ // @ts-ignore: type
164
+ if (type instanceof f64) return <T>result;
165
+ // @ts-ignore: cast
166
+ return <T>(<f32>result);
167
+ }
@@ -0,0 +1,78 @@
1
+ import {
2
+ BRACE_LEFT,
3
+ BRACE_RIGHT,
4
+ BRACKET_LEFT,
5
+ BRACKET_RIGHT,
6
+ COMMA,
7
+ QUOTE,
8
+ } from "../custom/chars";
9
+ import { isSpace } from "./isSpace";
10
+ import { scanStringEnd } from "./stringScan";
11
+
12
+ /**
13
+ * Pure-scalar value-end scanner used by the NAIVE container deserializers.
14
+ *
15
+ * Returns the position immediately after the value that begins at `srcStart`:
16
+ *
17
+ * - For a quoted string: position past the closing `"` (uses scalar
18
+ * {@link scanStringEnd}).
19
+ * - For an object/array: position past the matching `}`/`]`, tracking depth
20
+ * and skipping nested quoted strings.
21
+ * - For anything else: position of the first `,`, `]`, or `}` (the value's
22
+ * structural terminator).
23
+ *
24
+ * Returns `0` when the input is empty or no terminator is found.
25
+ *
26
+ * Mirrors the semantics of `deserialize/swar/array/shared.ts:scanValueEnd`
27
+ * but stays scalar so `naive/` callers don't pull SWAR into the correctness
28
+ * baseline.
29
+ */
30
+ // @ts-ignore: inline
31
+ @inline export function scanValueEnd(srcStart: usize, srcEnd: usize): usize {
32
+ if (srcStart >= srcEnd) return 0;
33
+ const first = load<u16>(srcStart);
34
+
35
+ if (first == QUOTE) {
36
+ const end = scanStringEnd(srcStart, srcEnd);
37
+ return end >= srcEnd ? 0 : end + 2;
38
+ }
39
+
40
+ if (first == BRACE_LEFT || first == BRACKET_LEFT) {
41
+ let depth: i32 = 1;
42
+ let ptr = srcStart + 2;
43
+ while (ptr < srcEnd) {
44
+ const code = load<u16>(ptr);
45
+ if (code == QUOTE) {
46
+ const end = scanStringEnd(ptr, srcEnd);
47
+ if (end >= srcEnd) return 0;
48
+ ptr = end + 2;
49
+ continue;
50
+ }
51
+ if (code == BRACE_LEFT || code == BRACKET_LEFT) {
52
+ depth++;
53
+ } else if (code == BRACE_RIGHT || code == BRACKET_RIGHT) {
54
+ if (--depth == 0) return ptr + 2;
55
+ }
56
+ ptr += 2;
57
+ }
58
+ return 0;
59
+ }
60
+
61
+ while (srcStart < srcEnd) {
62
+ const code = load<u16>(srcStart);
63
+ // Stop at the structural terminator OR trailing whitespace, so the returned
64
+ // range is the exact value (scalar parsers assume [srcStart,srcEnd) is the
65
+ // value with no trailing whitespace). Callers skip whitespace to reach the
66
+ // following `,`/`]`/`}`.
67
+ if (
68
+ code == COMMA ||
69
+ code == BRACKET_RIGHT ||
70
+ code == BRACE_RIGHT ||
71
+ isSpace(code)
72
+ )
73
+ return srcStart;
74
+ srcStart += 2;
75
+ }
76
+
77
+ return 0;
78
+ }