bun-shared-memory 0.1.4 → 0.1.6

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.
package/src/memory.ts CHANGED
@@ -175,19 +175,35 @@ export interface SharedMemoryOptions {
175
175
  maxByteLength?: number;
176
176
  /** Total memory budget shared across all buffers (see below). */
177
177
  maxTotalBytes?: number;
178
+ /** Size of each growable chunk (the minimum growth step). Default 32 MiB. */
179
+ chunkSize?: number;
178
180
  lock?: Mutex;
179
181
  internCapacity?: number;
180
182
  internMax?: number;
181
183
  buffers?: {
182
- indexTable?: SharedArrayBuffer;
183
- dataTable?: SharedArrayBuffer;
184
- stringTable?: SharedArrayBuffer;
185
- arrayTable?: SharedArrayBuffer;
184
+ indexTable?: SharedArrayBuffer | SharedArrayBuffer[];
185
+ dataTable?: SharedArrayBuffer | SharedArrayBuffer[];
186
+ stringTable?: SharedArrayBuffer | SharedArrayBuffer[];
187
+ arrayTable?: SharedArrayBuffer | SharedArrayBuffer[];
186
188
  layoutVersion?: SharedArrayBuffer;
187
189
  refCount?: SharedArrayBuffer;
188
190
  };
189
191
  }
190
192
 
193
+ /** Build a GrowableBuffer from an optional single SAB, chunk list, or nothing. */
194
+ function makeBuffer(
195
+ buf: SharedArrayBuffer | SharedArrayBuffer[] | undefined,
196
+ initial: number,
197
+ maxByteLength: number,
198
+ budget?: SharedBudget,
199
+ chunkSize?: number,
200
+ ): GrowableBuffer {
201
+ if (buf === undefined) {
202
+ return new GrowableBuffer(initial, maxByteLength, budget, chunkSize);
203
+ }
204
+ return new GrowableBuffer(buf, maxByteLength, budget, chunkSize);
205
+ }
206
+
191
207
  /**
192
208
  * Manages the four shared buffers and provides operations to store and read
193
209
  * strings, arrays, and objects.
@@ -246,44 +262,48 @@ export class SharedMemory {
246
262
 
247
263
  constructor(options?: SharedMemoryOptions) {
248
264
  const initial = options?.initialSize ?? DEFAULT_INITIAL;
249
- // A single SharedArrayBuffer can be at most 4 GiB. `maxTotalBytes` is a
250
- // *total* budget shared across all buffers: each buffer's own maxByteLength
251
- // is capped at min(4 GiB, maxTotalBytes), and the sum of all buffers'
252
- // physical sizes is additionally capped at maxTotalBytes. Without
253
- // maxTotalBytes, each buffer simply grows up to its own maxByteLength.
265
+ // `maxTotalBytes` is a *total* budget shared across all buffers: each
266
+ // buffer's own maxByteLength is capped at maxTotalBytes, and the sum of
267
+ // all buffers' physical sizes is additionally capped at maxTotalBytes.
268
+ // Without maxTotalBytes, each buffer grows up to its own maxByteLength.
269
+ // A single SharedArrayBuffer is limited to 4 GiB, but the chunked
270
+ // GrowableBuffer splits a buffer across multiple chunks, so a buffer can
271
+ // exceed 4 GiB.
254
272
  const maxTotal = options?.maxTotalBytes;
255
- const perBufferMax = Math.min(
256
- 4 * 1024 * 1024 * 1024,
257
- maxTotal ?? options?.maxByteLength ?? DEFAULT_MAX,
258
- );
273
+ const perBufferMax = maxTotal ?? options?.maxByteLength ?? DEFAULT_MAX;
259
274
  const budget =
260
275
  maxTotal !== undefined ? new SharedBudget(maxTotal) : undefined;
276
+ const chunkSize = options?.chunkSize;
261
277
  const buffers = options?.buffers;
262
- this.indexTable = new GrowableBuffer(
263
- buffers?.indexTable ??
264
- new SharedArrayBuffer(initial, { maxByteLength: perBufferMax }),
278
+ this.indexTable = makeBuffer(
279
+ buffers?.indexTable,
280
+ initial,
265
281
  perBufferMax,
266
282
  budget,
283
+ chunkSize,
267
284
  );
268
- this.dataTable = new GrowableBuffer(
269
- buffers?.dataTable ??
270
- new SharedArrayBuffer(initial, { maxByteLength: perBufferMax }),
285
+ this.dataTable = makeBuffer(
286
+ buffers?.dataTable,
287
+ initial,
271
288
  perBufferMax,
272
289
  budget,
290
+ chunkSize,
273
291
  );
274
- this.stringTable = new GrowableBuffer(
275
- buffers?.stringTable ??
276
- new SharedArrayBuffer(initial, { maxByteLength: perBufferMax }),
292
+ this.stringTable = makeBuffer(
293
+ buffers?.stringTable,
294
+ initial,
277
295
  perBufferMax,
278
296
  budget,
297
+ chunkSize,
279
298
  );
280
- this.arrayTable = new GrowableBuffer(
281
- buffers?.arrayTable ??
282
- new SharedArrayBuffer(initial, { maxByteLength: perBufferMax }),
299
+ this.arrayTable = makeBuffer(
300
+ buffers?.arrayTable,
301
+ initial,
283
302
  perBufferMax,
284
303
  budget,
304
+ chunkSize,
285
305
  );
286
- this.versionView = new Int32Array(this.indexTable.buffer);
306
+ this.versionView = new Int32Array(this.indexTable.buffers[0]!);
287
307
  this.layoutVersion = new Int32Array(
288
308
  buffers?.layoutVersion ??
289
309
  new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT),
@@ -313,21 +333,24 @@ export class SharedMemory {
313
333
  /**
314
334
  * Export all shared buffers (including the lock) so they can be sent to a
315
335
  * worker via `postMessage` and reconstructed there with `fromBuffers()`.
336
+ *
337
+ * Each table is exported as a list of SharedArrayBuffers (chunks), since a
338
+ * single SharedArrayBuffer is limited to 4 GiB.
316
339
  */
317
340
  exportBuffers(): {
318
- indexTable: SharedArrayBuffer;
319
- dataTable: SharedArrayBuffer;
320
- stringTable: SharedArrayBuffer;
321
- arrayTable: SharedArrayBuffer;
341
+ indexTable: SharedArrayBuffer[];
342
+ dataTable: SharedArrayBuffer[];
343
+ stringTable: SharedArrayBuffer[];
344
+ arrayTable: SharedArrayBuffer[];
322
345
  layoutVersion: SharedArrayBuffer;
323
346
  lock: SharedArrayBuffer;
324
347
  refCount: SharedArrayBuffer | null;
325
348
  } {
326
349
  return {
327
- indexTable: this.indexTable.buffer,
328
- dataTable: this.dataTable.buffer,
329
- stringTable: this.stringTable.buffer,
330
- arrayTable: this.arrayTable.buffer,
350
+ indexTable: this.indexTable.buffers,
351
+ dataTable: this.dataTable.buffers,
352
+ stringTable: this.stringTable.buffers,
353
+ arrayTable: this.arrayTable.buffers,
331
354
  layoutVersion: this.layoutVersion.buffer as SharedArrayBuffer,
332
355
  lock: this.lock.buffer as SharedArrayBuffer,
333
356
  refCount: this.refCountBuffer,
@@ -339,10 +362,10 @@ export class SharedMemory {
339
362
  * `exportBuffers()` (e.g. received from the main thread in a worker).
340
363
  */
341
364
  static fromBuffers(buffers: {
342
- indexTable: SharedArrayBuffer;
343
- dataTable: SharedArrayBuffer;
344
- stringTable: SharedArrayBuffer;
345
- arrayTable: SharedArrayBuffer;
365
+ indexTable: SharedArrayBuffer | SharedArrayBuffer[];
366
+ dataTable: SharedArrayBuffer | SharedArrayBuffer[];
367
+ stringTable: SharedArrayBuffer | SharedArrayBuffer[];
368
+ arrayTable: SharedArrayBuffer | SharedArrayBuffer[];
346
369
  layoutVersion: SharedArrayBuffer;
347
370
  lock: SharedArrayBuffer;
348
371
  refCount?: SharedArrayBuffer | null;
@@ -393,10 +416,7 @@ export class SharedMemory {
393
416
  const bytes = this.encoder.encode(value);
394
417
  const offset = this.stringTable.byteLength;
395
418
  this.stringTable.ensureCapacity(offset + bytes.byteLength);
396
- const view = this.stringTable.getView();
397
- for (let i = 0; i < bytes.byteLength; i++) {
398
- view.setUint8(offset + i, bytes[i]!);
399
- }
419
+ this.stringTable.writeBytes(offset, bytes);
400
420
  this.stringTable.advance(bytes.byteLength);
401
421
  const ref: StringRef = { offset, length: bytes.byteLength };
402
422
 
@@ -431,12 +451,7 @@ export class SharedMemory {
431
451
 
432
452
  /** Read a string from the StringTable by reference. */
433
453
  readString(ref: StringRef): string {
434
- const view = this.stringTable.getView();
435
- const bytes = new Uint8Array(
436
- this.stringTable.buffer,
437
- ref.offset,
438
- ref.length,
439
- );
454
+ const bytes = this.stringTable.readBytes(ref.offset, ref.length);
440
455
  return this.decoder.decode(bytes);
441
456
  }
442
457
 
@@ -458,11 +473,14 @@ export class SharedMemory {
458
473
  const cap = Math.max(capacity ?? 0, count, 1);
459
474
  const offset = this.arrayTable.byteLength;
460
475
  this.arrayTable.ensureCapacity(offset + ARRAY_HEADER + cap * SLOT_SIZE);
461
- const view = this.arrayTable.getView();
462
476
  const objectId = this.nextObjectId++;
463
- view.setInt32(offset, objectId, true);
477
+ this.arrayTable.setInt32(offset, objectId);
464
478
  for (let i = 0; i < count; i++) {
465
- writeSlot(view, offset + ARRAY_HEADER + i * SLOT_SIZE, slots[i]!);
479
+ writeSlot(
480
+ this.arrayTable,
481
+ offset + ARRAY_HEADER + i * SLOT_SIZE,
482
+ slots[i]!,
483
+ );
466
484
  }
467
485
  this.arrayTable.advance(ARRAY_HEADER + cap * SLOT_SIZE);
468
486
  return { offset, count, capacity: cap };
@@ -470,10 +488,11 @@ export class SharedMemory {
470
488
 
471
489
  /** Read an array of value slots from the ArrayTable by reference. */
472
490
  readArray(ref: ArrayRef): ValueSlot[] {
473
- const view = this.arrayTable.getView();
474
491
  const slots: ValueSlot[] = [];
475
492
  for (let i = 0; i < ref.count; i++) {
476
- slots.push(readSlot(view, ref.offset + ARRAY_HEADER + i * SLOT_SIZE));
493
+ slots.push(
494
+ readSlot(this.arrayTable, ref.offset + ARRAY_HEADER + i * SLOT_SIZE),
495
+ );
477
496
  }
478
497
  return slots;
479
498
  }
@@ -481,7 +500,7 @@ export class SharedMemory {
481
500
  /** Update a single value slot of an array in the ArrayTable in place. */
482
501
  updateArraySlot(ref: ArrayRef, index: number, slot: ValueSlot): void {
483
502
  writeSlot(
484
- this.arrayTable.getView(),
503
+ this.arrayTable,
485
504
  ref.offset + ARRAY_HEADER + index * SLOT_SIZE,
486
505
  slot,
487
506
  );
@@ -490,14 +509,14 @@ export class SharedMemory {
490
509
  /** Read a single value slot of an array from the ArrayTable. */
491
510
  readArraySlot(ref: ArrayRef, index: number): ValueSlot {
492
511
  return readSlot(
493
- this.arrayTable.getView(),
512
+ this.arrayTable,
494
513
  ref.offset + ARRAY_HEADER + index * SLOT_SIZE,
495
514
  );
496
515
  }
497
516
 
498
517
  /** Read the stable objectId of an array block in the ArrayTable. */
499
518
  getArrayObjectId(offset: number): number {
500
- return this.arrayTable.getView().getInt32(offset, true);
519
+ return this.arrayTable.getInt32(offset);
501
520
  }
502
521
 
503
522
  /**
@@ -510,14 +529,13 @@ export class SharedMemory {
510
529
  this.arrayTable.ensureCapacity(
511
530
  newOffset + ARRAY_HEADER + newCapacity * SLOT_SIZE,
512
531
  );
513
- const view = this.arrayTable.getView();
514
532
  const objectId = this.getArrayObjectId(ref.offset);
515
- view.setInt32(newOffset, objectId, true);
533
+ this.arrayTable.setInt32(newOffset, objectId);
516
534
  for (let i = 0; i < ref.count; i++) {
517
535
  writeSlot(
518
- view,
536
+ this.arrayTable,
519
537
  newOffset + ARRAY_HEADER + i * SLOT_SIZE,
520
- readSlot(view, ref.offset + ARRAY_HEADER + i * SLOT_SIZE),
538
+ readSlot(this.arrayTable, ref.offset + ARRAY_HEADER + i * SLOT_SIZE),
521
539
  );
522
540
  }
523
541
  this.arrayTable.advance(ARRAY_HEADER + newCapacity * SLOT_SIZE);
@@ -533,7 +551,7 @@ export class SharedMemory {
533
551
  appendArraySlot(ref: ArrayRef, slot: ValueSlot): ArrayRef {
534
552
  if (ref.count < ref.capacity) {
535
553
  writeSlot(
536
- this.arrayTable.getView(),
554
+ this.arrayTable,
537
555
  ref.offset + ARRAY_HEADER + ref.count * SLOT_SIZE,
538
556
  slot,
539
557
  );
@@ -545,7 +563,7 @@ export class SharedMemory {
545
563
  }
546
564
  const moved = this.moveArray(ref, Math.max(ref.capacity * 2, 1));
547
565
  writeSlot(
548
- this.arrayTable.getView(),
566
+ this.arrayTable,
549
567
  moved.offset + ARRAY_HEADER + moved.count * SLOT_SIZE,
550
568
  slot,
551
569
  );
@@ -564,19 +582,19 @@ export class SharedMemory {
564
582
  storeValueSlot(slot: ValueSlot): number {
565
583
  const offset = this.dataTable.byteLength;
566
584
  this.dataTable.ensureCapacity(offset + SLOT_SIZE);
567
- writeSlot(this.dataTable.getView(), offset, slot);
585
+ writeSlot(this.dataTable, offset, slot);
568
586
  this.dataTable.advance(SLOT_SIZE);
569
587
  return offset;
570
588
  }
571
589
 
572
590
  /** Read a single value slot from the DataTable. */
573
591
  readValueSlot(offset: number): ValueSlot {
574
- return readSlot(this.dataTable.getView(), offset);
592
+ return readSlot(this.dataTable, offset);
575
593
  }
576
594
 
577
595
  /** Update a single value slot in the DataTable. */
578
596
  updateValueSlot(offset: number, slot: ValueSlot): void {
579
- writeSlot(this.dataTable.getView(), offset, slot);
597
+ writeSlot(this.dataTable, offset, slot);
580
598
  }
581
599
 
582
600
  /**
@@ -590,20 +608,19 @@ export class SharedMemory {
590
608
  OBJECT_HEADER + properties.length * (NAME_REF_SIZE + SLOT_SIZE);
591
609
  const offset = this.dataTable.byteLength;
592
610
  this.dataTable.ensureCapacity(offset + blockSize);
593
- const view = this.dataTable.getView();
594
611
 
595
612
  const objectId = this.nextObjectId++;
596
- view.setInt32(offset, TYPE_OBJECT, true);
597
- view.setInt32(offset + 4, properties.length, true);
598
- view.setInt32(offset + 8, objectId, true);
613
+ this.dataTable.setInt32(offset, TYPE_OBJECT);
614
+ this.dataTable.setInt32(offset + 4, properties.length);
615
+ this.dataTable.setInt32(offset + 8, objectId);
599
616
 
600
617
  let cursor = offset + OBJECT_HEADER;
601
618
  for (const [name, slot] of properties) {
602
619
  const nameRef = this.storeString(name);
603
- view.setInt32(cursor, nameRef.offset, true);
604
- view.setInt32(cursor + 4, nameRef.length, true);
620
+ this.dataTable.setInt32(cursor, nameRef.offset);
621
+ this.dataTable.setInt32(cursor + 4, nameRef.length);
605
622
  cursor += NAME_REF_SIZE;
606
- writeSlot(view, cursor, slot);
623
+ writeSlot(this.dataTable, cursor, slot);
607
624
  cursor += SLOT_SIZE;
608
625
  }
609
626
  this.dataTable.advance(blockSize);
@@ -612,16 +629,15 @@ export class SharedMemory {
612
629
 
613
630
  /** Read an object's properties from the DataTable. */
614
631
  readObject(offset: number): Array<[string, ValueSlot]> {
615
- const view = this.dataTable.getView();
616
- const count = view.getInt32(offset + 4, true);
632
+ const count = this.dataTable.getInt32(offset + 4);
617
633
  const properties: Array<[string, ValueSlot]> = [];
618
634
  let cursor = offset + OBJECT_HEADER;
619
635
  for (let i = 0; i < count; i++) {
620
- const nameOffset = view.getInt32(cursor, true);
621
- const nameLength = view.getInt32(cursor + 4, true);
636
+ const nameOffset = this.dataTable.getInt32(cursor);
637
+ const nameLength = this.dataTable.getInt32(cursor + 4);
622
638
  const name = this.readString({ offset: nameOffset, length: nameLength });
623
639
  cursor += NAME_REF_SIZE;
624
- const slot = readSlot(view, cursor);
640
+ const slot = readSlot(this.dataTable, cursor);
625
641
  cursor += SLOT_SIZE;
626
642
  properties.push([name, slot]);
627
643
  }
@@ -630,7 +646,7 @@ export class SharedMemory {
630
646
 
631
647
  /** Read the stable objectId of an object block in the DataTable. */
632
648
  getObjectId(offset: number): number {
633
- return this.dataTable.getView().getInt32(offset + 8, true);
649
+ return this.dataTable.getInt32(offset + 8);
634
650
  }
635
651
 
636
652
  /**
@@ -647,14 +663,13 @@ export class SharedMemory {
647
663
  getObjectLayout(key: string): ObjectLayout | null {
648
664
  const offset = this.lookup(key);
649
665
  if (offset === null) return null;
650
- const view = this.dataTable.getView();
651
- if (view.getInt32(offset, true) !== TYPE_OBJECT) return null;
652
- const count = view.getInt32(offset + 4, true);
666
+ if (this.dataTable.getInt32(offset) !== TYPE_OBJECT) return null;
667
+ const count = this.dataTable.getInt32(offset + 4);
653
668
  const properties = new Map<string, number>();
654
669
  let cursor = offset + OBJECT_HEADER;
655
670
  for (let i = 0; i < count; i++) {
656
- const nameOffset = view.getInt32(cursor, true);
657
- const nameLength = view.getInt32(cursor + 4, true);
671
+ const nameOffset = this.dataTable.getInt32(cursor);
672
+ const nameLength = this.dataTable.getInt32(cursor + 4);
658
673
  const name = this.readString({ offset: nameOffset, length: nameLength });
659
674
  cursor += NAME_REF_SIZE;
660
675
  properties.set(name, cursor);
@@ -669,13 +684,12 @@ export class SharedMemory {
669
684
  propertyIndex: number,
670
685
  slot: ValueSlot,
671
686
  ): void {
672
- const view = this.dataTable.getView();
673
687
  const slotOffset =
674
688
  offset +
675
689
  OBJECT_HEADER +
676
690
  propertyIndex * (NAME_REF_SIZE + SLOT_SIZE) +
677
691
  NAME_REF_SIZE;
678
- writeSlot(view, slotOffset, slot);
692
+ writeSlot(this.dataTable, slotOffset, slot);
679
693
  }
680
694
 
681
695
  // ---------------------------------------------------------------------------
@@ -691,11 +705,10 @@ export class SharedMemory {
691
705
 
692
706
  /** Read the DataTable offset for a known stable index, or null if absent. */
693
707
  lookupByIndex(index: number): number | null {
694
- const view = this.indexTable.getView();
695
- const count = view.getInt32(0, true);
708
+ const count = this.indexTable.getInt32(0);
696
709
  if (index >= count) return null;
697
710
  const entryOffset = INDEX_HEADER + index * INDEX_ENTRY_SIZE;
698
- return view.getInt32(entryOffset + 8, true);
711
+ return this.indexTable.getInt32(entryOffset + 8);
699
712
  }
700
713
 
701
714
  /**
@@ -706,12 +719,11 @@ export class SharedMemory {
706
719
  * used to address the key's version cell.
707
720
  */
708
721
  lookupIndex(key: string): number | null {
709
- const view = this.indexTable.getView();
710
- const count = view.getInt32(0, true);
722
+ const count = this.indexTable.getInt32(0);
711
723
  for (let i = 0; i < count; i++) {
712
724
  const entryOffset = INDEX_HEADER + i * INDEX_ENTRY_SIZE;
713
- const keyOffset = view.getInt32(entryOffset, true);
714
- const keyLength = view.getInt32(entryOffset + 4, true);
725
+ const keyOffset = this.indexTable.getInt32(entryOffset);
726
+ const keyLength = this.indexTable.getInt32(entryOffset + 4);
715
727
  const storedKey = this.readString({
716
728
  offset: keyOffset,
717
729
  length: keyLength,
@@ -736,21 +748,21 @@ export class SharedMemory {
736
748
  /** Insert a key → dataOffset mapping into the Index Table. */
737
749
  insert(key: string, dataOffset: number): void {
738
750
  const keyRef = this.storeString(key);
739
- const view = this.indexTable.getView();
740
- const count = view.getInt32(0, true);
751
+ const count = this.indexTable.getInt32(0);
741
752
  const entryOffset = INDEX_HEADER + count * INDEX_ENTRY_SIZE;
742
753
  this.indexTable.ensureCapacity(entryOffset + INDEX_ENTRY_SIZE);
743
- view.setInt32(entryOffset, keyRef.offset, true);
744
- view.setInt32(entryOffset + 4, keyRef.length, true);
745
- view.setInt32(entryOffset + 8, dataOffset, true);
754
+ this.indexTable.setInt32(entryOffset, keyRef.offset);
755
+ this.indexTable.setInt32(entryOffset + 4, keyRef.length);
756
+ this.indexTable.setInt32(entryOffset + 8, dataOffset);
746
757
  // The 4th field is the key's version cell, initialized to 0.
747
- view.setInt32(entryOffset + INDEX_VERSION_OFFSET, 0, true);
748
- view.setInt32(0, count + 1, true);
758
+ this.indexTable.setInt32(entryOffset + INDEX_VERSION_OFFSET, 0);
759
+ this.indexTable.setInt32(0, count + 1);
749
760
  this.indexTable.advance(INDEX_ENTRY_SIZE);
750
761
 
751
- // Rebuild the version view if the buffer grew (grow preserves the SAB in
752
- // place, but the Int32Array view must be recreated over the new buffer).
753
- this.versionView = new Int32Array(this.indexTable.buffer);
762
+ // Rebuild the version view over the first chunk (the Index Table is small
763
+ // and stays within the first chunk for typical workloads; version cells
764
+ // beyond the first chunk are not addressable via the single Int32Array).
765
+ this.versionView = new Int32Array(this.indexTable.buffers[0]!);
754
766
  }
755
767
 
756
768
  // ---------------------------------------------------------------------------
@@ -812,9 +824,10 @@ export class SharedMemory {
812
824
  const markArray = (offset: number, count: number) => {
813
825
  if (liveArrays.has(offset)) return;
814
826
  liveArrays.add(offset);
815
- const view = this.arrayTable.getView();
816
827
  for (let i = 0; i < count; i++) {
817
- markValueSlot(readSlot(view, offset + ARRAY_HEADER + i * SLOT_SIZE));
828
+ markValueSlot(
829
+ readSlot(this.arrayTable, offset + ARRAY_HEADER + i * SLOT_SIZE),
830
+ );
818
831
  }
819
832
  };
820
833
 
@@ -822,40 +835,37 @@ export class SharedMemory {
822
835
  if (liveObjects.has(offset)) return;
823
836
  liveObjects.add(offset);
824
837
  liveDataOffsets.add(offset);
825
- const view = this.dataTable.getView();
826
- const count = view.getInt32(offset + 4, true);
838
+ const count = this.dataTable.getInt32(offset + 4);
827
839
  let cursor = offset + OBJECT_HEADER;
828
840
  for (let i = 0; i < count; i++) {
829
- const nameOffset = view.getInt32(cursor, true);
830
- const nameLength = view.getInt32(cursor + 4, true);
841
+ const nameOffset = this.dataTable.getInt32(cursor);
842
+ const nameLength = this.dataTable.getInt32(cursor + 4);
831
843
  liveStrings.add(nameOffset);
832
844
  cursor += NAME_REF_SIZE;
833
- markValueSlot(readSlot(view, cursor));
845
+ markValueSlot(readSlot(this.dataTable, cursor));
834
846
  cursor += SLOT_SIZE;
835
847
  }
836
848
  };
837
849
 
838
850
  const markValue = (dataOffset: number) => {
839
- const view = this.dataTable.getView();
840
- if (view.getInt32(dataOffset, true) === TYPE_OBJECT) {
851
+ if (this.dataTable.getInt32(dataOffset) === TYPE_OBJECT) {
841
852
  markObject(dataOffset);
842
853
  } else {
843
854
  liveDataOffsets.add(dataOffset);
844
- markValueSlot(readSlot(view, dataOffset));
855
+ markValueSlot(readSlot(this.dataTable, dataOffset));
845
856
  }
846
857
  };
847
858
 
848
859
  // Phase 1: Mark live data by walking the Index Table.
849
- const indexView = this.indexTable.getView();
850
- const indexCount = indexView.getInt32(0, true);
860
+ const indexCount = this.indexTable.getInt32(0);
851
861
  const indexEntries: Array<{ keyRef: StringRef; dataOffset: number }> = [];
852
862
  for (let i = 0; i < indexCount; i++) {
853
863
  const entryOffset = INDEX_HEADER + i * INDEX_ENTRY_SIZE;
854
864
  const keyRef = {
855
- offset: indexView.getInt32(entryOffset, true),
856
- length: indexView.getInt32(entryOffset + 4, true),
865
+ offset: this.indexTable.getInt32(entryOffset),
866
+ length: this.indexTable.getInt32(entryOffset + 4),
857
867
  };
858
- const dataOffset = indexView.getInt32(entryOffset + 8, true);
868
+ const dataOffset = this.indexTable.getInt32(entryOffset + 8);
859
869
  indexEntries.push({ keyRef, dataOffset });
860
870
  liveStrings.add(keyRef.offset);
861
871
  markValue(dataOffset);
@@ -864,14 +874,13 @@ export class SharedMemory {
864
874
  // Phase 2: Compact the DataTable (objects and single value slots).
865
875
  const sortedData = [...liveDataOffsets].sort((a, b) => a - b);
866
876
  let newDataOffset = 0;
867
- const dataView = this.dataTable.getView();
868
877
  for (const oldOffset of sortedData) {
869
878
  const size =
870
- dataView.getInt32(oldOffset, true) === TYPE_OBJECT
879
+ this.dataTable.getInt32(oldOffset) === TYPE_OBJECT
871
880
  ? OBJECT_HEADER +
872
- dataView.getInt32(oldOffset + 4, true) * (NAME_REF_SIZE + SLOT_SIZE)
881
+ this.dataTable.getInt32(oldOffset + 4) * (NAME_REF_SIZE + SLOT_SIZE)
873
882
  : SLOT_SIZE;
874
- copyBytes(dataView, newDataOffset, oldOffset, size);
883
+ copyBytes(this.dataTable, newDataOffset, oldOffset, size);
875
884
  objectRemap.set(oldOffset, newDataOffset);
876
885
  newDataOffset += size;
877
886
  }
@@ -880,7 +889,6 @@ export class SharedMemory {
880
889
  // Phase 3: Compact the ArrayTable.
881
890
  const sortedArrays = [...liveArrays].sort((a, b) => a - b);
882
891
  let newArrayOffset = 0;
883
- const arrayView = this.arrayTable.getView();
884
892
  for (const oldOffset of sortedArrays) {
885
893
  // Determine the array's capacity by scanning forward to the next live
886
894
  // array or the end of the used region.
@@ -888,7 +896,7 @@ export class SharedMemory {
888
896
  const end = next !== undefined ? next : this.arrayTable.byteLength;
889
897
  const capacity = (end - oldOffset - ARRAY_HEADER) / SLOT_SIZE;
890
898
  copyBytes(
891
- arrayView,
899
+ this.arrayTable,
892
900
  newArrayOffset,
893
901
  oldOffset,
894
902
  ARRAY_HEADER + capacity * SLOT_SIZE,
@@ -901,12 +909,11 @@ export class SharedMemory {
901
909
  // Phase 4: Compact the StringTable.
902
910
  const sortedStrings = [...liveStrings].sort((a, b) => a - b);
903
911
  let newStringOffset = 0;
904
- const stringView = this.stringTable.getView();
905
912
  for (const oldOffset of sortedStrings) {
906
913
  const next = sortedStrings[sortedStrings.indexOf(oldOffset) + 1];
907
914
  const end = next !== undefined ? next : this.stringTable.byteLength;
908
915
  const length = end - oldOffset;
909
- copyBytes(stringView, newStringOffset, oldOffset, length);
916
+ copyBytes(this.stringTable, newStringOffset, oldOffset, length);
910
917
  stringRemap.set(oldOffset, newStringOffset);
911
918
  newStringOffset += length;
912
919
  }
@@ -916,29 +923,29 @@ export class SharedMemory {
916
923
  for (let i = 0; i < indexCount; i++) {
917
924
  const entryOffset = INDEX_HEADER + i * INDEX_ENTRY_SIZE;
918
925
  const { keyRef, dataOffset } = indexEntries[i]!;
919
- indexView.setInt32(entryOffset, stringRemap.get(keyRef.offset)!, true);
920
- indexView.setInt32(entryOffset + 4, keyRef.length, true);
921
- indexView.setInt32(entryOffset + 8, objectRemap.get(dataOffset)!, true);
926
+ this.indexTable.setInt32(entryOffset, stringRemap.get(keyRef.offset)!);
927
+ this.indexTable.setInt32(entryOffset + 4, keyRef.length);
928
+ this.indexTable.setInt32(entryOffset + 8, objectRemap.get(dataOffset)!);
922
929
  }
923
930
 
924
931
  // Phase 6: Rewrite object property names and value slots, and array slots.
925
932
  for (const oldOffset of sortedData) {
926
- if (dataView.getInt32(oldOffset, true) !== TYPE_OBJECT) continue;
933
+ if (this.dataTable.getInt32(oldOffset) !== TYPE_OBJECT) continue;
927
934
  const newOff = objectRemap.get(oldOffset)!;
928
- const count = dataView.getInt32(newOff + 4, true);
935
+ const count = this.dataTable.getInt32(newOff + 4);
929
936
  let cursor = newOff + OBJECT_HEADER;
930
937
  for (let i = 0; i < count; i++) {
931
- const nameOffset = dataView.getInt32(cursor, true);
932
- dataView.setInt32(cursor, stringRemap.get(nameOffset)!, true);
938
+ const nameOffset = this.dataTable.getInt32(cursor);
939
+ this.dataTable.setInt32(cursor, stringRemap.get(nameOffset)!);
933
940
  cursor += NAME_REF_SIZE;
934
- remapSlot(dataView, cursor, stringRemap, arrayRemap);
941
+ remapSlot(this.dataTable, cursor, stringRemap, arrayRemap);
935
942
  cursor += SLOT_SIZE;
936
943
  }
937
944
  }
938
945
  for (const oldOffset of sortedData) {
939
- if (dataView.getInt32(oldOffset, true) === TYPE_OBJECT) continue;
946
+ if (this.dataTable.getInt32(oldOffset) === TYPE_OBJECT) continue;
940
947
  const newOff = objectRemap.get(oldOffset)!;
941
- remapSlot(dataView, newOff, stringRemap, arrayRemap);
948
+ remapSlot(this.dataTable, newOff, stringRemap, arrayRemap);
942
949
  }
943
950
  for (const oldOffset of sortedArrays) {
944
951
  const newOff = arrayRemap.get(oldOffset)!;
@@ -947,7 +954,7 @@ export class SharedMemory {
947
954
  const capacity = (end - oldOffset - ARRAY_HEADER) / SLOT_SIZE;
948
955
  for (let i = 0; i < capacity; i++) {
949
956
  remapSlot(
950
- arrayView,
957
+ this.arrayTable,
951
958
  newOff + ARRAY_HEADER + i * SLOT_SIZE,
952
959
  stringRemap,
953
960
  arrayRemap,
@@ -960,31 +967,31 @@ export class SharedMemory {
960
967
  }
961
968
  }
962
969
 
963
- function writeSlot(view: DataView, offset: number, slot: ValueSlot): void {
964
- view.setInt32(offset, slot.type, true);
965
- view.setInt32(offset + 4, slot.a, true);
966
- view.setInt32(offset + 8, slot.b, true);
967
- view.setInt32(offset + 12, slot.c, true);
970
+ function writeSlot(buf: GrowableBuffer, offset: number, slot: ValueSlot): void {
971
+ buf.setInt32(offset, slot.type);
972
+ buf.setInt32(offset + 4, slot.a);
973
+ buf.setInt32(offset + 8, slot.b);
974
+ buf.setInt32(offset + 12, slot.c);
968
975
  }
969
976
 
970
- function readSlot(view: DataView, offset: number): ValueSlot {
977
+ function readSlot(buf: GrowableBuffer, offset: number): ValueSlot {
971
978
  return {
972
- type: view.getInt32(offset, true),
973
- a: view.getInt32(offset + 4, true),
974
- b: view.getInt32(offset + 8, true),
975
- c: view.getInt32(offset + 12, true),
979
+ type: buf.getInt32(offset),
980
+ a: buf.getInt32(offset + 4),
981
+ b: buf.getInt32(offset + 8),
982
+ c: buf.getInt32(offset + 12),
976
983
  };
977
984
  }
978
985
 
979
986
  /** Copy `len` bytes from `src` to `dst` in the same buffer (dst <= src). */
980
987
  function copyBytes(
981
- view: DataView,
988
+ buf: GrowableBuffer,
982
989
  dst: number,
983
990
  src: number,
984
991
  len: number,
985
992
  ): void {
986
993
  for (let i = 0; i < len; i++) {
987
- view.setUint8(dst + i, view.getUint8(src + i));
994
+ buf.setUint8(dst + i, buf.getUint8(src + i));
988
995
  }
989
996
  }
990
997
 
@@ -994,23 +1001,15 @@ function copyBytes(
994
1001
  * via `arrayRemap`.
995
1002
  */
996
1003
  function remapSlot(
997
- view: DataView,
1004
+ buf: GrowableBuffer,
998
1005
  offset: number,
999
1006
  stringRemap: Map<number, number>,
1000
1007
  arrayRemap: Map<number, number>,
1001
1008
  ): void {
1002
- const type = view.getInt32(offset, true);
1009
+ const type = buf.getInt32(offset);
1003
1010
  if (type === TYPE_STRING_REF || type === TYPE_BIGINT_STR) {
1004
- view.setInt32(
1005
- offset + 4,
1006
- stringRemap.get(view.getInt32(offset + 4, true))!,
1007
- true,
1008
- );
1011
+ buf.setInt32(offset + 4, stringRemap.get(buf.getInt32(offset + 4))!);
1009
1012
  } else if (type === TYPE_ARRAY_REF) {
1010
- view.setInt32(
1011
- offset + 4,
1012
- arrayRemap.get(view.getInt32(offset + 4, true))!,
1013
- true,
1014
- );
1013
+ buf.setInt32(offset + 4, arrayRemap.get(buf.getInt32(offset + 4))!);
1015
1014
  }
1016
1015
  }