json-as 1.3.1 → 1.3.3

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 (55) hide show
  1. package/CHANGELOG.md +9 -26
  2. package/README.md +43 -19
  3. package/assembly/deserialize/index/arbitrary.ts +1 -1
  4. package/assembly/deserialize/index/array.ts +6 -1
  5. package/assembly/deserialize/index/float.ts +1 -1
  6. package/assembly/deserialize/index/integer.ts +1 -1
  7. package/assembly/deserialize/index/unsigned.ts +1 -1
  8. package/assembly/deserialize/simd/string.ts +17 -13
  9. package/assembly/deserialize/simple/arbitrary.ts +1 -1
  10. package/assembly/deserialize/simple/array/generic.ts +42 -0
  11. package/assembly/deserialize/simple/array.ts +8 -1
  12. package/assembly/deserialize/{float.ts → simple/float.ts} +22 -2
  13. package/assembly/deserialize/{integer.ts → simple/integer.ts} +3 -2
  14. package/assembly/deserialize/simple/map.ts +62 -12
  15. package/assembly/deserialize/simple/object.ts +1 -1
  16. package/assembly/deserialize/simple/set.ts +119 -134
  17. package/assembly/deserialize/simple/staticarray.ts +13 -1
  18. package/assembly/deserialize/simple/string.ts +15 -10
  19. package/assembly/deserialize/simple/struct.ts +9 -157
  20. package/assembly/deserialize/simple/typedarray.ts +1 -1
  21. package/assembly/deserialize/{unsigned.ts → simple/unsigned.ts} +3 -2
  22. package/assembly/deserialize/swar/array/array.ts +42 -7
  23. package/assembly/deserialize/swar/array/bool.ts +6 -2
  24. package/assembly/deserialize/swar/array/float.ts +7 -3
  25. package/assembly/deserialize/swar/array/generic.ts +41 -0
  26. package/assembly/deserialize/swar/array/integer.ts +8 -4
  27. package/assembly/deserialize/swar/array/object.ts +21 -4
  28. package/assembly/deserialize/swar/array/shared.ts +19 -4
  29. package/assembly/deserialize/swar/array/string.ts +6 -2
  30. package/assembly/deserialize/swar/array/struct.ts +21 -4
  31. package/assembly/deserialize/swar/array.ts +57 -2
  32. package/assembly/deserialize/swar/string.ts +248 -372
  33. package/assembly/index.d.ts +1 -0
  34. package/assembly/index.ts +77 -19
  35. package/assembly/serialize/index/arbitrary.ts +3 -3
  36. package/assembly/serialize/index/float.ts +1 -1
  37. package/assembly/serialize/index/object.ts +1 -5
  38. package/assembly/serialize/simd/string.ts +4 -5
  39. package/assembly/serialize/simple/arbitrary.ts +3 -3
  40. package/assembly/serialize/simple/array.ts +18 -6
  41. package/assembly/serialize/simple/float.ts +20 -4
  42. package/assembly/serialize/simple/map.ts +10 -27
  43. package/assembly/serialize/simple/object.ts +1 -5
  44. package/assembly/serialize/simple/set.ts +3 -4
  45. package/assembly/serialize/simple/staticarray.ts +4 -3
  46. package/assembly/serialize/simple/typedarray.ts +9 -7
  47. package/assembly/serialize/swar/string.ts +0 -1
  48. package/assembly/tsconfig.json +1 -0
  49. package/assembly/util/dragonbox-cache.ts +4 -0
  50. package/assembly/util/dragonbox.ts +624 -0
  51. package/lib/as-bs.ts +35 -24
  52. package/package.json +26 -18
  53. package/transform/lib/index.d.ts.map +1 -1
  54. package/transform/lib/index.js +508 -148
  55. package/transform/lib/index.js.map +1 -1
@@ -1,5 +1,6 @@
1
1
  import { bs } from "../../../lib/as-bs";
2
2
  import { OBJECT, TOTAL_OVERHEAD } from "rt/common";
3
+ import { __heap_base } from "memory";
3
4
  import { BACK_SLASH, QUOTE } from "../../custom/chars";
4
5
  import { DESERIALIZE_ESCAPE_TABLE } from "../../globals/tables";
5
6
  import { hex4_to_u16_swar } from "../../util/swar";
@@ -184,106 +185,7 @@ export function deserializeString_SWAR(srcStart: usize, srcEnd: usize): string {
184
185
  return copyStringFromSource(payloadStart, srcEnd - payloadStart);
185
186
  }
186
187
 
187
- // /**
188
- // * Deserializes a quoted JSON string into a reused/renewed destination string buffer.
189
- // * @param srcStart pointer to opening quote
190
- // * @param srcEnd pointer to closing quote
191
- // * @param outPtr existing destination string pointer (or 0)
192
- // * @returns next unread source pointer
193
- // */
194
- // export function deserializeString_SWAR_TO(srcStart: usize, srcEnd: usize, outPtr: usize): usize {
195
- // srcStart += 2;
196
- // let dst = outPtr;
197
- // const srcEnd8 = srcEnd - 8;
198
- // const byteSize = srcEnd - srcStart;
199
- // if (!dst) {
200
- // dst = __new(byteSize, idof<string>());
201
- // } else if (changetype<OBJECT>(dst - TOTAL_OVERHEAD).rtSize < <u32>byteSize) {
202
- // dst = __renew(dst, byteSize);
203
- // }
204
- // let offset = dst;
205
-
206
- // while (srcStart < srcEnd8) {
207
- // const block = load<u64>(srcStart);
208
- // store<u64>(offset, block);
209
-
210
- // let mask = inline.always(backslash_mask_unsafe(block));
211
-
212
- // if (mask === 0) {
213
- // srcStart += 8;
214
- // offset += 8;
215
- // continue;
216
- // }
217
-
218
- // do {
219
- // const laneIdx = usize(ctz(mask) >> 3); // 0 2 4 6
220
- // mask &= mask - 1;
221
- // const srcIdx = srcStart + laneIdx;
222
- // const dstIdx = offset + laneIdx;
223
- // const header = load<u32>(srcIdx);
224
- // const code = <u16>(header >> 16);
225
-
226
- // if ((header & 0xffff) !== 0x5c) continue;
227
-
228
- // if (code !== 0x75) {
229
- // const escaped = load<u16>(DESERIALIZE_ESCAPE_TABLE + code);
230
- // mask &= mask - usize(escaped === 0x5c);
231
- // store<u16>(dstIdx, escaped);
232
- // const copyStart = srcIdx + 4;
233
- // if (copyStart < srcEnd) {
234
- // const copyBytes = min<usize>(4, srcEnd - copyStart);
235
- // memory.copy(dstIdx + 2, copyStart, copyBytes);
236
- // }
237
-
238
- // const l6 = usize(laneIdx === 6);
239
- // offset -= (1 - l6) << 1;
240
- // srcStart += l6 << 1;
241
- // continue;
242
- // }
243
-
244
- // const block = load<u64>(srcIdx, 4); // XXXX
245
- // const escaped = hex4_to_u16_swar(block);
246
- // store<u16>(dstIdx, escaped);
247
- // srcStart += 4 + laneIdx;
248
- // offset -= 6 - laneIdx;
249
- // } while (mask !== 0);
250
-
251
- // offset += 8;
252
- // srcStart += 8;
253
- // }
254
-
255
- // while (srcStart < srcEnd) {
256
- // const block = load<u16>(srcStart);
257
- // store<u16>(offset, block);
258
- // srcStart += 2;
259
-
260
- // if (block !== 0x5c) {
261
- // offset += 2;
262
- // continue;
263
- // }
264
-
265
- // const code = load<u16>(srcStart);
266
- // if (code !== 0x75) {
267
- // const block = load<u16>(srcStart);
268
- // const escape = load<u16>(DESERIALIZE_ESCAPE_TABLE + block);
269
- // store<u16>(offset, escape);
270
- // srcStart += 2;
271
- // } else {
272
- // const block = load<u64>(srcStart, 2); // XXXX
273
- // const escaped = hex4_to_u16_swar(block);
274
- // store<u16>(offset, escaped);
275
- // srcStart += 10;
276
- // }
277
-
278
- // offset += 2;
279
- // }
280
- // if (offset - dst != byteSize) {
281
- // dst = __renew(dst, offset - dst);
282
- // }
283
- // return srcEnd + 2;
284
- // }
285
-
286
- // Scans a quoted string value, writes into the destination field, and returns next unread src pointer.
188
+ // Writes into the destination field, reusing or resizing the backing string.
287
189
  // @ts-expect-error: @inline is a valid decorator
288
190
  @inline function writeStringToField(dstFieldPtr: usize, srcStart: usize, byteLength: u32): void {
289
191
  if (byteLength == 0) {
@@ -293,11 +195,13 @@ export function deserializeString_SWAR(srcStart: usize, srcEnd: usize): string {
293
195
 
294
196
  const current = load<usize>(dstFieldPtr);
295
197
  let stringPtr: usize;
296
- if (current != 0 && changetype<OBJECT>(current - TOTAL_OVERHEAD).rtSize == byteLength) {
297
- stringPtr = current;
298
- } else if (current != 0 && current != changetype<usize>("")) {
299
- stringPtr = __renew(current, byteLength);
300
- store<usize>(dstFieldPtr, stringPtr);
198
+ if (current >= __heap_base) {
199
+ if (changetype<OBJECT>(current - TOTAL_OVERHEAD).rtSize == byteLength) {
200
+ stringPtr = current;
201
+ } else {
202
+ stringPtr = __renew(current, byteLength);
203
+ store<usize>(dstFieldPtr, stringPtr);
204
+ }
301
205
  } else {
302
206
  stringPtr = __new(byteLength, idof<string>());
303
207
  store<usize>(dstFieldPtr, stringPtr);
@@ -305,17 +209,13 @@ export function deserializeString_SWAR(srcStart: usize, srcEnd: usize): string {
305
209
  memory.copy(stringPtr, srcStart, byteLength);
306
210
  }
307
211
 
308
- /*
309
- export function deserializeStringField_SWAR<T extends string | null>(srcStart: usize, srcEnd: usize, dstFieldPtr: usize): usize {
310
- if (srcStart + 2 > srcEnd || load<u16>(srcStart) != QUOTE) abort("Expected leading quote");
311
-
312
- const payloadStart = srcStart + 2;
313
- const srcEnd8 = srcEnd >= 8 ? srcEnd - 8 : 0;
314
- srcStart = payloadStart;
212
+ // @ts-expect-error: @inline is a valid decorator
213
+ @inline function deserializeEscapedStringContinuation_SWAR(lastPtr: usize, srcStart: usize, srcEnd: usize, dstFieldPtr: usize, outStart: usize): usize {
214
+ const srcEnd8 = srcEnd - 8;
315
215
 
316
216
  while (srcStart <= srcEnd8) {
217
+ const blockStart = srcStart;
317
218
  let mask = inline.always(backslash_or_quote_mask(load<u64>(srcStart)));
318
-
319
219
  if (mask === 0) {
320
220
  srcStart += 8;
321
221
  continue;
@@ -323,294 +223,93 @@ export function deserializeStringField_SWAR<T extends string | null>(srcStart: u
323
223
 
324
224
  do {
325
225
  const laneIdx = usize(ctz(mask) >> 3);
326
- mask &= ~(0xffff << (laneIdx << 3));
327
- // since we clear the entire byte, we can guarentee that any discovered lane where char == QUOTE is unescaped and a terminator.
226
+ mask &= mask - 1;
328
227
  const srcIdx = srcStart + laneIdx;
329
228
  const char = load<u16>(srcIdx);
330
-
331
229
  if (char == QUOTE) {
332
- writeStringToField(dstFieldPtr, payloadStart, <u32>(srcIdx - payloadStart));
230
+ const runLen = <u32>(srcIdx - lastPtr);
231
+ if (runLen != 0) {
232
+ memory.copy(bs.offset, lastPtr, runLen);
233
+ bs.offset += runLen;
234
+ }
235
+ bs.toField(outStart, dstFieldPtr);
333
236
  return srcIdx + 2;
334
237
  }
335
238
  if (char != BACK_SLASH) continue;
336
239
 
337
- bs.offset = bs.buffer;
338
- bs.ensureSize(<u32>(srcEnd - payloadStart));
339
- const prefixLen = <u32>(srcIdx - payloadStart);
340
- if (prefixLen != 0) {
341
- memory.copy(bs.buffer, payloadStart, prefixLen);
342
- bs.offset += prefixLen;
240
+ const runLen = <u32>(srcIdx - lastPtr);
241
+ if (runLen != 0) {
242
+ memory.copy(bs.offset, lastPtr, runLen);
243
+ bs.offset += runLen;
343
244
  }
344
245
 
345
246
  const chunk = load<u32>(srcIdx);
346
247
  const code = <u16>(chunk >> 16);
347
-
348
248
  if (code !== 0x75) {
349
249
  store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
350
250
  bs.offset += 2;
351
- let lastPtr = srcIdx + 4;
352
- srcStart = lastPtr;
353
- while (srcStart <= srcEnd8) {
354
- const blockStart = srcStart;
355
- let escapedMask = inline.always(backslash_or_quote_mask(load<u64>(srcStart)));
356
- if (escapedMask === 0) {
357
- srcStart += 8;
358
- continue;
359
- }
360
-
361
- do {
362
- const escapedLaneIdx = usize(ctz(escapedMask) >> 3);
363
- escapedMask &= escapedMask - 1;
364
- const escapedIdx = srcStart + escapedLaneIdx;
365
- const escapedChar = load<u16>(escapedIdx);
366
-
367
- if (escapedChar == QUOTE) {
368
- const runLen = <u32>(escapedIdx - lastPtr);
369
- if (runLen != 0) {
370
- memory.copy(bs.offset, lastPtr, runLen);
371
- bs.offset += runLen;
372
- }
373
- writeStringToField(dstFieldPtr, bs.buffer, <u32>(bs.offset - bs.buffer));
374
- bs.offset = bs.buffer;
375
- return escapedIdx + 2;
376
- }
377
- if (escapedChar != BACK_SLASH) continue;
378
-
379
- const runLen = <u32>(escapedIdx - lastPtr);
380
- if (runLen != 0) {
381
- memory.copy(bs.offset, lastPtr, runLen);
382
- bs.offset += runLen;
383
- }
384
-
385
- const escapedChunk = load<u32>(escapedIdx);
386
- const escapedCode = <u16>(escapedChunk >> 16);
387
- if (escapedCode !== 0x75) {
388
- store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + escapedCode));
389
- bs.offset += 2;
390
- lastPtr = escapedIdx + 4;
391
- } else {
392
- store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(escapedIdx, 4)));
393
- bs.offset += 2;
394
- lastPtr = escapedIdx + 12;
395
- }
396
- srcStart = lastPtr;
397
- break;
398
- } while (escapedMask !== 0);
399
-
400
- if (srcStart == blockStart) srcStart += 8;
401
- }
402
-
403
- while (srcStart < srcEnd) {
404
- const tailChar = load<u16>(srcStart);
405
- if (tailChar == QUOTE) {
406
- const runLen = <u32>(srcStart - lastPtr);
407
- if (runLen != 0) {
408
- memory.copy(bs.offset, lastPtr, runLen);
409
- bs.offset += runLen;
410
- }
411
- writeStringToField(dstFieldPtr, bs.buffer, <u32>(bs.offset - bs.buffer));
412
- bs.offset = bs.buffer;
413
- return srcStart + 2;
414
- }
415
- if (tailChar != BACK_SLASH) {
416
- srcStart += 2;
417
- continue;
418
- }
419
-
420
- const runLen = <u32>(srcStart - lastPtr);
421
- if (runLen != 0) {
422
- memory.copy(bs.offset, lastPtr, runLen);
423
- bs.offset += runLen;
424
- }
425
- const tailCode = load<u16>(srcStart, 2);
426
- if (tailCode !== 0x75) {
427
- store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + tailCode));
428
- bs.offset += 2;
429
- srcStart += 4;
430
- } else {
431
- store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(srcStart, 4)));
432
- bs.offset += 2;
433
- srcStart += 12;
434
- }
435
- lastPtr = srcStart;
436
- }
437
- bs.offset = bs.buffer;
438
- return srcStart;
251
+ lastPtr = srcIdx + 4;
439
252
  } else {
440
253
  store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(srcIdx, 4)));
441
254
  bs.offset += 2;
442
- let lastPtr = srcIdx + 12;
443
- srcStart = lastPtr;
444
- while (srcStart <= srcEnd8) {
445
- const blockStart = srcStart;
446
- let escapedMask = inline.always(backslash_or_quote_mask(load<u64>(srcStart)));
447
- if (escapedMask === 0) {
448
- srcStart += 8;
449
- continue;
450
- }
451
-
452
- do {
453
- const escapedLaneIdx = usize(ctz(escapedMask) >> 3);
454
- escapedMask &= escapedMask - 1;
455
- const escapedIdx = srcStart + escapedLaneIdx;
456
- const escapedChar = load<u16>(escapedIdx);
457
-
458
- if (escapedChar == QUOTE) {
459
- const runLen = <u32>(escapedIdx - lastPtr);
460
- if (runLen != 0) {
461
- memory.copy(bs.offset, lastPtr, runLen);
462
- bs.offset += runLen;
463
- }
464
- writeStringToField(dstFieldPtr, bs.buffer, <u32>(bs.offset - bs.buffer));
465
- bs.offset = bs.buffer;
466
- return escapedIdx + 2;
467
- }
468
- if (escapedChar != BACK_SLASH) continue;
469
-
470
- const runLen = <u32>(escapedIdx - lastPtr);
471
- if (runLen != 0) {
472
- memory.copy(bs.offset, lastPtr, runLen);
473
- bs.offset += runLen;
474
- }
475
-
476
- const escapedChunk = load<u32>(escapedIdx);
477
- const escapedCode = <u16>(escapedChunk >> 16);
478
- if (escapedCode !== 0x75) {
479
- store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + escapedCode));
480
- bs.offset += 2;
481
- lastPtr = escapedIdx + 4;
482
- } else {
483
- store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(escapedIdx, 4)));
484
- bs.offset += 2;
485
- lastPtr = escapedIdx + 12;
486
- }
487
- srcStart = lastPtr;
488
- break;
489
- } while (escapedMask !== 0);
490
-
491
- if (srcStart == blockStart) srcStart += 8;
492
- }
493
-
494
- while (srcStart < srcEnd) {
495
- const tailChar = load<u16>(srcStart);
496
- if (tailChar == QUOTE) {
497
- const runLen = <u32>(srcStart - lastPtr);
498
- if (runLen != 0) {
499
- memory.copy(bs.offset, lastPtr, runLen);
500
- bs.offset += runLen;
501
- }
502
- writeStringToField(dstFieldPtr, bs.buffer, <u32>(bs.offset - bs.buffer));
503
- bs.offset = bs.buffer;
504
- return srcStart + 2;
505
- }
506
- if (tailChar != BACK_SLASH) {
507
- srcStart += 2;
508
- continue;
509
- }
510
-
511
- const runLen = <u32>(srcStart - lastPtr);
512
- if (runLen != 0) {
513
- memory.copy(bs.offset, lastPtr, runLen);
514
- bs.offset += runLen;
515
- }
516
- const tailCode = load<u16>(srcStart, 2);
517
- if (tailCode !== 0x75) {
518
- store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + tailCode));
519
- bs.offset += 2;
520
- srcStart += 4;
521
- } else {
522
- store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(srcStart, 4)));
523
- bs.offset += 2;
524
- srcStart += 12;
525
- }
526
- lastPtr = srcStart;
527
- }
528
- bs.offset = bs.buffer;
529
- return srcStart;
255
+ lastPtr = srcIdx + 12;
530
256
  }
257
+ srcStart = lastPtr;
258
+ break;
531
259
  } while (mask !== 0);
260
+
261
+ if (srcStart == blockStart) srcStart += 8;
532
262
  }
533
263
 
534
264
  while (srcStart < srcEnd) {
535
265
  const char = load<u16>(srcStart);
536
266
  if (char == QUOTE) {
537
- writeStringToField(dstFieldPtr, payloadStart, <u32>(srcStart - payloadStart));
267
+ const runLen = <u32>(srcStart - lastPtr);
268
+ if (runLen != 0) {
269
+ memory.copy(bs.offset, lastPtr, runLen);
270
+ bs.offset += runLen;
271
+ }
272
+ bs.toField(outStart, dstFieldPtr);
538
273
  return srcStart + 2;
539
274
  }
540
- if (char == BACK_SLASH) {
541
- bs.offset = bs.buffer;
542
- bs.ensureSize(<u32>(srcEnd - payloadStart));
543
- const prefixLen = <u32>(srcStart - payloadStart);
544
- if (prefixLen != 0) {
545
- memory.copy(bs.buffer, payloadStart, prefixLen);
546
- bs.offset += prefixLen;
547
- }
275
+ if (char != BACK_SLASH) {
276
+ srcStart += 2;
277
+ continue;
278
+ }
548
279
 
549
- let lastPtr = srcStart;
550
- const code = load<u16>(srcStart, 2);
551
- if (code !== 0x75) {
552
- store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
553
- bs.offset += 2;
554
- srcStart += 4;
555
- } else {
556
- store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(srcStart, 4)));
557
- bs.offset += 2;
558
- srcStart += 12;
559
- }
560
- lastPtr = srcStart;
561
-
562
- while (srcStart < srcEnd) {
563
- const tailChar = load<u16>(srcStart);
564
- if (tailChar == QUOTE) {
565
- const runLen = <u32>(srcStart - lastPtr);
566
- if (runLen != 0) {
567
- memory.copy(bs.offset, lastPtr, runLen);
568
- bs.offset += runLen;
569
- }
570
- writeStringToField(dstFieldPtr, bs.buffer, <u32>(bs.offset - bs.buffer));
571
- bs.offset = bs.buffer;
572
- return srcStart + 2;
573
- }
574
- if (tailChar != BACK_SLASH) {
575
- srcStart += 2;
576
- continue;
577
- }
280
+ const runLen = <u32>(srcStart - lastPtr);
281
+ if (runLen != 0) {
282
+ memory.copy(bs.offset, lastPtr, runLen);
283
+ bs.offset += runLen;
284
+ }
578
285
 
579
- const runLen = <u32>(srcStart - lastPtr);
580
- if (runLen != 0) {
581
- memory.copy(bs.offset, lastPtr, runLen);
582
- bs.offset += runLen;
583
- }
584
- const tailCode = load<u16>(srcStart, 2);
585
- if (tailCode !== 0x75) {
586
- store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + tailCode));
587
- bs.offset += 2;
588
- srcStart += 4;
589
- } else {
590
- store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(srcStart, 4)));
591
- bs.offset += 2;
592
- srcStart += 12;
593
- }
594
- lastPtr = srcStart;
595
- }
596
- bs.offset = bs.buffer;
597
- return srcStart;
286
+ const code = load<u16>(srcStart, 2);
287
+ if (code !== 0x75) {
288
+ store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
289
+ bs.offset += 2;
290
+ srcStart += 4;
291
+ } else {
292
+ store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(srcStart, 4)));
293
+ bs.offset += 2;
294
+ srcStart += 12;
598
295
  }
599
- srcStart += 2;
296
+ lastPtr = srcStart;
600
297
  }
601
298
 
299
+ bs.offset = bs.buffer + outStart;
300
+ abort("Unterminated string literal");
602
301
  return srcStart;
603
302
  }
604
- */
605
303
 
304
+ // Scans a quoted string value, writes into the destination field, and returns next unread src pointer.
606
305
  // @ts-expect-error: @inline is a valid decorator
607
- @inline function deserializeEscapedStringScan_SWAR(payloadStart: usize, escapeStart: usize, srcEnd: usize, dstFieldPtr: usize): usize {
306
+ @inline function deserializeEscapedStringScan_SWAR_SplitTuned(payloadStart: usize, escapeStart: usize, srcEnd: usize, dstFieldPtr: usize): usize {
608
307
  const prefixLen = <u32>(escapeStart - payloadStart);
609
- const srcEnd8 = srcEnd >= 8 ? srcEnd - 8 : 0;
610
- const outStart = bs.offset - bs.buffer;
308
+ const srcEnd8 = srcEnd - 8;
309
+ bs.offset = bs.buffer;
611
310
  bs.ensureSize(<u32>(srcEnd - payloadStart));
612
311
  if (prefixLen != 0) {
613
- memory.copy(bs.offset, payloadStart, prefixLen);
312
+ memory.copy(bs.buffer, payloadStart, prefixLen);
614
313
  bs.offset += prefixLen;
615
314
  }
616
315
 
@@ -636,7 +335,8 @@ export function deserializeStringField_SWAR<T extends string | null>(srcStart: u
636
335
  memory.copy(bs.offset, lastPtr, runLen);
637
336
  bs.offset += runLen;
638
337
  }
639
- bs.toField(outStart, dstFieldPtr);
338
+ writeStringToField(dstFieldPtr, bs.buffer, <u32>(bs.offset - bs.buffer));
339
+ bs.offset = bs.buffer;
640
340
  return srcIdx + 2;
641
341
  }
642
342
  if (char != BACK_SLASH) continue;
@@ -672,7 +372,8 @@ export function deserializeStringField_SWAR<T extends string | null>(srcStart: u
672
372
  memory.copy(bs.offset, lastPtr, runLen);
673
373
  bs.offset += runLen;
674
374
  }
675
- bs.toField(outStart, dstFieldPtr);
375
+ writeStringToField(dstFieldPtr, bs.buffer, <u32>(bs.offset - bs.buffer));
376
+ bs.offset = bs.buffer;
676
377
  return srcStart + 2;
677
378
  }
678
379
  if (char != BACK_SLASH) {
@@ -696,21 +397,197 @@ export function deserializeStringField_SWAR<T extends string | null>(srcStart: u
696
397
  bs.offset += 2;
697
398
  srcStart += 12;
698
399
  }
699
-
700
400
  lastPtr = srcStart;
701
401
  }
702
402
 
703
- bs.offset = bs.buffer + outStart;
403
+ bs.offset = bs.buffer;
704
404
  abort("Unterminated string literal");
705
405
  return srcStart;
706
406
  }
707
407
 
708
- // Scans a quoted string value, writes into the destination field, and returns next unread src pointer.
408
+ // @ts-expect-error: @inline is a valid decorator
409
+ @inline function deserializeEscapedStringContinuation_SWAR_MergedTuned(lastPtr: usize, srcStart: usize, srcEnd: usize, dstFieldPtr: usize): usize {
410
+ const srcEnd8 = srcEnd - 8;
411
+
412
+ while (srcStart <= srcEnd8) {
413
+ const blockStart = srcStart;
414
+ let mask = inline.always(backslash_or_quote_mask(load<u64>(srcStart)));
415
+ if (mask === 0) {
416
+ srcStart += 8;
417
+ continue;
418
+ }
419
+
420
+ do {
421
+ const laneIdx = usize(ctz(mask) >> 3);
422
+ mask &= mask - 1;
423
+ const srcIdx = srcStart + laneIdx;
424
+ const char = load<u16>(srcIdx);
425
+ if (char == QUOTE) {
426
+ const runLen = <u32>(srcIdx - lastPtr);
427
+ if (runLen != 0) {
428
+ memory.copy(bs.offset, lastPtr, runLen);
429
+ bs.offset += runLen;
430
+ }
431
+ writeStringToField(dstFieldPtr, bs.buffer, <u32>(bs.offset - bs.buffer));
432
+ bs.offset = bs.buffer;
433
+ return srcIdx + 2;
434
+ }
435
+ if (char != BACK_SLASH) continue;
436
+
437
+ const runLen = <u32>(srcIdx - lastPtr);
438
+ if (runLen != 0) {
439
+ memory.copy(bs.offset, lastPtr, runLen);
440
+ bs.offset += runLen;
441
+ }
442
+
443
+ const chunk = load<u32>(srcIdx);
444
+ const code = <u16>(chunk >> 16);
445
+ if (code !== 0x75) {
446
+ store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
447
+ bs.offset += 2;
448
+ lastPtr = srcIdx + 4;
449
+ } else {
450
+ store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(srcIdx, 4)));
451
+ bs.offset += 2;
452
+ lastPtr = srcIdx + 12;
453
+ }
454
+ srcStart = lastPtr;
455
+ break;
456
+ } while (mask !== 0);
457
+
458
+ if (srcStart == blockStart) srcStart += 8;
459
+ }
460
+
461
+ while (srcStart < srcEnd) {
462
+ const tailChar = load<u16>(srcStart);
463
+ if (tailChar == QUOTE) {
464
+ const runLen = <u32>(srcStart - lastPtr);
465
+ if (runLen != 0) {
466
+ memory.copy(bs.offset, lastPtr, runLen);
467
+ bs.offset += runLen;
468
+ }
469
+ writeStringToField(dstFieldPtr, bs.buffer, <u32>(bs.offset - bs.buffer));
470
+ bs.offset = bs.buffer;
471
+ return srcStart + 2;
472
+ }
473
+ if (tailChar != BACK_SLASH) {
474
+ srcStart += 2;
475
+ continue;
476
+ }
477
+
478
+ const runLen = <u32>(srcStart - lastPtr);
479
+ if (runLen != 0) {
480
+ memory.copy(bs.offset, lastPtr, runLen);
481
+ bs.offset += runLen;
482
+ }
483
+ const tailCode = load<u16>(srcStart, 2);
484
+ if (tailCode !== 0x75) {
485
+ store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + tailCode));
486
+ bs.offset += 2;
487
+ srcStart += 4;
488
+ } else {
489
+ store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(srcStart, 4)));
490
+ bs.offset += 2;
491
+ srcStart += 12;
492
+ }
493
+ lastPtr = srcStart;
494
+ }
495
+
496
+ bs.offset = bs.buffer;
497
+ return srcStart;
498
+ }
499
+
500
+ function deserializeStringField_SWAR_MergedTuned(srcStart: usize, srcEnd: usize, dstFieldPtr: usize): usize {
501
+ if (srcStart + 2 > srcEnd || load<u16>(srcStart) != QUOTE) abort("Expected leading quote");
502
+
503
+ const payloadStart = srcStart + 2;
504
+ const srcEnd8 = srcEnd - 8;
505
+ srcStart = payloadStart;
506
+
507
+ while (srcStart <= srcEnd8) {
508
+ let mask = inline.always(backslash_or_quote_mask(load<u64>(srcStart)));
509
+ if (mask === 0) {
510
+ srcStart += 8;
511
+ continue;
512
+ }
513
+
514
+ do {
515
+ const laneIdx = usize(ctz(mask) >> 3);
516
+ mask &= ~(0xffff << (laneIdx << 3));
517
+ const srcIdx = srcStart + laneIdx;
518
+ const char = load<u16>(srcIdx);
519
+
520
+ if (char == QUOTE) {
521
+ writeStringToField(dstFieldPtr, payloadStart, <u32>(srcIdx - payloadStart));
522
+ return srcIdx + 2;
523
+ }
524
+ if (char != BACK_SLASH) continue;
525
+
526
+ bs.offset = bs.buffer;
527
+ bs.ensureSize(<u32>(srcEnd - payloadStart));
528
+ const prefixLen = <u32>(srcIdx - payloadStart);
529
+ if (prefixLen != 0) {
530
+ memory.copy(bs.buffer, payloadStart, prefixLen);
531
+ bs.offset += prefixLen;
532
+ }
533
+
534
+ const chunk = load<u32>(srcIdx);
535
+ const code = <u16>(chunk >> 16);
536
+ let lastPtr: usize;
537
+ if (code !== 0x75) {
538
+ store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
539
+ bs.offset += 2;
540
+ lastPtr = srcIdx + 4;
541
+ } else {
542
+ store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(srcIdx, 4)));
543
+ bs.offset += 2;
544
+ lastPtr = srcIdx + 12;
545
+ }
546
+ return inline.always(deserializeEscapedStringContinuation_SWAR_MergedTuned(lastPtr, lastPtr, srcEnd, dstFieldPtr));
547
+ } while (mask !== 0);
548
+
549
+ srcStart += 8;
550
+ }
551
+
552
+ while (srcStart < srcEnd) {
553
+ const char = load<u16>(srcStart);
554
+ if (char == QUOTE) {
555
+ writeStringToField(dstFieldPtr, payloadStart, <u32>(srcStart - payloadStart));
556
+ return srcStart + 2;
557
+ }
558
+ if (char == BACK_SLASH) {
559
+ bs.offset = bs.buffer;
560
+ bs.ensureSize(<u32>(srcEnd - payloadStart));
561
+ const prefixLen = <u32>(srcStart - payloadStart);
562
+ if (prefixLen != 0) {
563
+ memory.copy(bs.buffer, payloadStart, prefixLen);
564
+ bs.offset += prefixLen;
565
+ }
566
+
567
+ const code = load<u16>(srcStart, 2);
568
+ let lastPtr: usize;
569
+ if (code !== 0x75) {
570
+ store<u16>(bs.offset, load<u16>(DESERIALIZE_ESCAPE_TABLE + code));
571
+ bs.offset += 2;
572
+ lastPtr = srcStart + 4;
573
+ } else {
574
+ store<u16>(bs.offset, hex4_to_u16_swar(load<u64>(srcStart, 4)));
575
+ bs.offset += 2;
576
+ lastPtr = srcStart + 12;
577
+ }
578
+ return inline.always(deserializeEscapedStringContinuation_SWAR_MergedTuned(lastPtr, lastPtr, srcEnd, dstFieldPtr));
579
+ }
580
+ srcStart += 2;
581
+ }
582
+
583
+ return srcStart;
584
+ }
585
+
709
586
  export function deserializeStringField_SWAR<T extends string | null>(srcStart: usize, srcEnd: usize, dstFieldPtr: usize): usize {
710
587
  if (srcStart + 2 > srcEnd || load<u16>(srcStart) != QUOTE) abort("Expected leading quote");
711
588
 
712
589
  const payloadStart = srcStart + 2;
713
- const srcEnd8 = srcEnd >= 8 ? srcEnd - 8 : 0;
590
+ const srcEnd8 = srcEnd - 8;
714
591
  srcStart = payloadStart;
715
592
 
716
593
  while (srcStart <= srcEnd8) {
@@ -730,8 +607,7 @@ export function deserializeStringField_SWAR<T extends string | null>(srcStart: u
730
607
  return srcIdx + 2;
731
608
  }
732
609
  if (char != BACK_SLASH) continue;
733
-
734
- return deserializeEscapedStringScan_SWAR(payloadStart, srcIdx, srcEnd, dstFieldPtr);
610
+ return inline.always(deserializeEscapedStringScan_SWAR_SplitTuned(payloadStart, srcIdx, srcEnd, dstFieldPtr));
735
611
  } while (mask !== 0);
736
612
 
737
613
  srcStart += 8;
@@ -744,7 +620,7 @@ export function deserializeStringField_SWAR<T extends string | null>(srcStart: u
744
620
  return srcStart + 2;
745
621
  }
746
622
  if (char == BACK_SLASH) {
747
- return deserializeEscapedStringScan_SWAR(payloadStart, srcStart, srcEnd, dstFieldPtr);
623
+ return inline.always(deserializeEscapedStringScan_SWAR_SplitTuned(payloadStart, srcStart, srcEnd, dstFieldPtr));
748
624
  }
749
625
  srcStart += 2;
750
626
  }