bun-shared-memory 0.1.7 → 0.1.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "bun-shared-memory",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Share data between Bun workers/threads using SharedArrayBuffer (zero-copy, 4-buffer architecture, Atomics-based synchronization).",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -102,7 +102,13 @@ export class GrowableBuffer {
102
102
  /** log2(chunkSize), used for O(1) offset resolution via bit shifts. */
103
103
  private readonly chunkShift: number;
104
104
  private readonly budget?: SharedBudget;
105
- private used = 0;
105
+ /**
106
+ * Shared append cursor (used length). Stored in a SharedArrayBuffer so all
107
+ * workers that reconstruct this buffer from `fromBuffers()` see the same
108
+ * used length and append at the correct offset instead of overwriting
109
+ * existing data at offset 0.
110
+ */
111
+ private readonly cursor: BigInt64Array;
106
112
  /** Smallest chunk index that is not yet active (see `activate`). */
107
113
  private nextChunkToActivate = 0;
108
114
 
@@ -113,9 +119,13 @@ export class GrowableBuffer {
113
119
  chunkSize = 32 * 1024 * 1024, // 32 MiB per chunk
114
120
  singleBuffer = false,
115
121
  chargeInitial = true,
122
+ cursorBuffer?: SharedArrayBuffer,
116
123
  ) {
117
124
  this.singleBuffer = singleBuffer;
118
125
  this.budget = budget;
126
+ this.cursor = new BigInt64Array(
127
+ cursorBuffer ?? new SharedArrayBuffer(BigInt64Array.BYTES_PER_ELEMENT),
128
+ );
119
129
  if (singleBuffer && typeof initialSizeOrBuffer !== "number") {
120
130
  const buffer = Array.isArray(initialSizeOrBuffer)
121
131
  ? initialSizeOrBuffer[0]
@@ -267,7 +277,12 @@ export class GrowableBuffer {
267
277
 
268
278
  /** Number of bytes written so far (used length, not buffer capacity). */
269
279
  get byteLength(): number {
270
- return this.used;
280
+ return Number(Atomics.load(this.cursor, 0));
281
+ }
282
+
283
+ /** The shared cursor buffer (for export to workers). */
284
+ get cursorBuffer(): SharedArrayBuffer {
285
+ return this.cursor.buffer as SharedArrayBuffer;
271
286
  }
272
287
 
273
288
  /** Total logical capacity across all chunks (active or not). */
@@ -373,7 +388,7 @@ export class GrowableBuffer {
373
388
 
374
389
  /** Mark `n` bytes as used, after writing them at the current offset. */
375
390
  advance(n: number): void {
376
- this.used += n;
391
+ Atomics.add(this.cursor, 0, BigInt(n));
377
392
  }
378
393
 
379
394
  /**
@@ -383,7 +398,7 @@ export class GrowableBuffer {
383
398
  * `releaseInactiveChunks()`.
384
399
  */
385
400
  reset(n: number): void {
386
- this.used = n;
401
+ Atomics.store(this.cursor, 0, BigInt(n));
387
402
  if (this.singleBuffer) return;
388
403
  const neededChunks = Math.ceil(n / this.chunkSize);
389
404
  for (let i = 0; i < this.chunks.length; i++) {
package/src/memory.ts CHANGED
@@ -211,6 +211,12 @@ export interface SharedMemoryOptions {
211
211
  layoutVersion?: SharedArrayBuffer;
212
212
  refCount?: SharedArrayBuffer;
213
213
  budget?: SharedArrayBuffer;
214
+ cursors?: {
215
+ indexTable?: SharedArrayBuffer;
216
+ dataTable?: SharedArrayBuffer;
217
+ stringTable?: SharedArrayBuffer;
218
+ arrayTable?: SharedArrayBuffer;
219
+ };
214
220
  };
215
221
  }
216
222
 
@@ -223,6 +229,7 @@ function makeBuffer(
223
229
  chunkSize?: number,
224
230
  singleBuffer = false,
225
231
  chargeInitial = true,
232
+ cursorBuffer?: SharedArrayBuffer,
226
233
  ): GrowableBuffer {
227
234
  if (buf === undefined) {
228
235
  return new GrowableBuffer(
@@ -232,6 +239,7 @@ function makeBuffer(
232
239
  chunkSize,
233
240
  singleBuffer,
234
241
  chargeInitial,
242
+ cursorBuffer,
235
243
  );
236
244
  }
237
245
  return new GrowableBuffer(
@@ -241,6 +249,7 @@ function makeBuffer(
241
249
  chunkSize,
242
250
  singleBuffer,
243
251
  chargeInitial,
252
+ cursorBuffer,
244
253
  );
245
254
  }
246
255
 
@@ -359,6 +368,8 @@ export class SharedMemory {
359
368
  undefined,
360
369
  chunkSize,
361
370
  true,
371
+ true,
372
+ buffers?.cursors?.indexTable,
362
373
  );
363
374
  this.dataTable = makeBuffer(
364
375
  buffers?.dataTable,
@@ -368,6 +379,7 @@ export class SharedMemory {
368
379
  chunkSize,
369
380
  false,
370
381
  chargeInitial,
382
+ buffers?.cursors?.dataTable,
371
383
  );
372
384
  this.stringTable = makeBuffer(
373
385
  buffers?.stringTable,
@@ -377,6 +389,7 @@ export class SharedMemory {
377
389
  chunkSize,
378
390
  false,
379
391
  chargeInitial,
392
+ buffers?.cursors?.stringTable,
380
393
  );
381
394
  this.arrayTable = makeBuffer(
382
395
  buffers?.arrayTable,
@@ -386,6 +399,7 @@ export class SharedMemory {
386
399
  chunkSize,
387
400
  false,
388
401
  chargeInitial,
402
+ buffers?.cursors?.arrayTable,
389
403
  );
390
404
  this.versionView = new Int32Array(this.indexTable.buffers[0]!);
391
405
  this.layoutVersion = new Int32Array(
@@ -453,6 +467,12 @@ export class SharedMemory {
453
467
  indexTableMaxByteLength: number;
454
468
  maxTotalBytes: number | null;
455
469
  budget: SharedArrayBuffer | null;
470
+ cursors: {
471
+ indexTable: SharedArrayBuffer;
472
+ dataTable: SharedArrayBuffer;
473
+ stringTable: SharedArrayBuffer;
474
+ arrayTable: SharedArrayBuffer;
475
+ };
456
476
  } {
457
477
  return {
458
478
  indexTable: this.indexTable.buffers,
@@ -466,6 +486,12 @@ export class SharedMemory {
466
486
  indexTableMaxByteLength: this.indexTableMaxByteLength,
467
487
  maxTotalBytes: this.maxTotalBytes,
468
488
  budget: this.budgetBuffer,
489
+ cursors: {
490
+ indexTable: this.indexTable.cursorBuffer,
491
+ dataTable: this.dataTable.cursorBuffer,
492
+ stringTable: this.stringTable.cursorBuffer,
493
+ arrayTable: this.arrayTable.cursorBuffer,
494
+ },
469
495
  };
470
496
  }
471
497
 
@@ -485,6 +511,12 @@ export class SharedMemory {
485
511
  indexTableMaxByteLength?: number;
486
512
  maxTotalBytes?: number | null;
487
513
  budget?: SharedArrayBuffer | null;
514
+ cursors?: {
515
+ indexTable?: SharedArrayBuffer;
516
+ dataTable?: SharedArrayBuffer;
517
+ stringTable?: SharedArrayBuffer;
518
+ arrayTable?: SharedArrayBuffer;
519
+ };
488
520
  }): SharedMemory {
489
521
  const indexBuffer = Array.isArray(buffers.indexTable)
490
522
  ? buffers.indexTable[0]
@@ -505,6 +537,7 @@ export class SharedMemory {
505
537
  layoutVersion: buffers.layoutVersion,
506
538
  refCount: buffers.refCount ?? undefined,
507
539
  budget: buffers.budget ?? undefined,
540
+ cursors: buffers.cursors,
508
541
  },
509
542
  lock: new Mutex(buffers.lock),
510
543
  });