@nxtedition/shared
Advanced tools
+6
-10
@@ -22,10 +22,2 @@ export interface BufferRegion { | ||
| /** | ||
| * Allocates a ring buffer handle. The first 128 bytes are reserved for | ||
| * read/write pointers; the rest is the data region. | ||
| * | ||
| * The `size` parameter is the guaranteed max payload for a single write. | ||
| * Overhead (length header + alignment + next-header slot) is added automatically. | ||
| */ | ||
| export declare function alloc(size: number): SharedHandle; | ||
| /** | ||
| * Reader for the ring buffer. | ||
@@ -40,3 +32,5 @@ */ | ||
| }; | ||
| constructor(handle: SharedHandle); | ||
| get handle(): SharedHandle; | ||
| constructor(handleOrSize: SharedHandle | number); | ||
| [Symbol.dispose](): void; | ||
| readSome(next: (data: BufferRegion) => void | boolean): number; | ||
@@ -58,3 +52,5 @@ readSome<U>(next: (data: BufferRegion, opaque: U) => void | boolean, opaque: U): number; | ||
| }; | ||
| constructor(handle: SharedHandle, { yield: onYield, logger }?: WriterOptions); | ||
| get handle(): SharedHandle; | ||
| constructor(handleOrSize: SharedHandle | number, { yield: onYield, logger }?: WriterOptions); | ||
| [Symbol.dispose](): void; | ||
| /** | ||
@@ -61,0 +57,0 @@ * Synchronously writes a message. Blocks (via `Atomics.wait`) until buffer space is available. |
+39
-5
@@ -49,3 +49,3 @@ // By placing the read and write indices far apart (multiples of a common | ||
| */ | ||
| export function alloc(size ) { | ||
| function alloc(size ) { | ||
| if (!Number.isInteger(size)) { | ||
@@ -74,2 +74,3 @@ throw new TypeError('size must be a positive integer') | ||
| #data | ||
| #handle | ||
@@ -86,4 +87,14 @@ #stats = { | ||
| constructor(handle ) { | ||
| const sharedBuffer = handle | ||
| get handle() { | ||
| return this.#handle | ||
| } | ||
| constructor(handleOrSize ) { | ||
| if (typeof handleOrSize === 'number') { | ||
| this.#handle = alloc(handleOrSize) | ||
| } else { | ||
| this.#handle = handleOrSize | ||
| } | ||
| const sharedBuffer = this.#handle | ||
| const size = sharedBuffer.byteLength - STATE_BYTES | ||
@@ -106,2 +117,8 @@ | ||
| [Symbol.dispose]() { | ||
| // No resources to clean up in this implementation, but this method is defined | ||
| // to allow for future enhancements (e.g. if we add event listeners or other | ||
| // resources that need explicit cleanup). | ||
| } | ||
@@ -191,2 +208,3 @@ | ||
| #uncorkBound | ||
| #handle | ||
@@ -206,5 +224,13 @@ #stats = { | ||
| constructor(handle , { yield: onYield, logger } = {}) { | ||
| const sharedBuffer = handle | ||
| get handle() { | ||
| return this.#handle | ||
| } | ||
| constructor(handleOrSize , { yield: onYield, logger } = {}) { | ||
| if (typeof handleOrSize === 'number') { | ||
| this.#handle = alloc(handleOrSize) | ||
| } else { | ||
| this.#handle = handleOrSize | ||
| } | ||
| if (onYield != null && typeof onYield !== 'function') { | ||
@@ -214,2 +240,3 @@ throw new TypeError('onYield must be a function') | ||
| const sharedBuffer = this.#handle | ||
| const size = sharedBuffer.byteLength - STATE_BYTES | ||
@@ -244,2 +271,9 @@ | ||
| [Symbol.dispose]() { | ||
| this.flushSync() | ||
| // No resources to clean up in this implementation, but this method is defined | ||
| // to allow for future enhancements (e.g. if we add event listeners or other | ||
| // resources that need explicit cleanup). | ||
| } | ||
| /** | ||
@@ -246,0 +280,0 @@ * Pauses the writer thread to wait for the reader to catch up. |
+2
-2
| { | ||
| "name": "@nxtedition/shared", | ||
| "version": "5.0.1", | ||
| "version": "5.0.2", | ||
| "type": "module", | ||
@@ -32,3 +32,3 @@ "main": "lib/index.js", | ||
| }, | ||
| "gitHead": "55b4a0e0bf5b52d7516759ad182f5f49f094c662" | ||
| "gitHead": "9547032a3fa363bb353777a1b19b0852013459c5" | ||
| } |
+29
-22
@@ -26,10 +26,7 @@ # @nxtedition/shared | ||
| ```js | ||
| import { alloc, Reader, Writer } from '@nxtedition/shared' | ||
| import { Reader, Writer } from '@nxtedition/shared' | ||
| // Allocate shared memory (pass handle to a worker thread) | ||
| const state = alloc(1024 * 1024) // 1 MB ring buffer | ||
| // Create writer — allocates the ring buffer internally | ||
| using w = new Writer(1024 * 1024) // 1 MB ring buffer | ||
| // --- Writer side (e.g. main thread) --- | ||
| const w = new Writer(state) | ||
| const payload = Buffer.from('hello world') | ||
@@ -41,4 +38,4 @@ w.writeSync(payload.length, (data) => { | ||
| // --- Reader side (e.g. worker thread) --- | ||
| const r = new Reader(state) | ||
| // Create reader from the same handle (pass w.handle to the other thread) | ||
| using r = new Reader(w.handle) | ||
@@ -92,11 +89,9 @@ r.readSome((data) => { | ||
| // main.js | ||
| import { alloc, Writer } from '@nxtedition/shared' | ||
| import { Writer } from '@nxtedition/shared' | ||
| import { Worker } from 'node:worker_threads' | ||
| const state = alloc(1024 * 1024) | ||
| using w = new Writer(1024 * 1024) | ||
| const worker = new Worker('./reader-worker.js', { | ||
| workerData: state, | ||
| workerData: w.handle, | ||
| }) | ||
| const w = new Writer(state) | ||
| // ... write messages | ||
@@ -110,3 +105,3 @@ ``` | ||
| const r = new Reader(workerData) | ||
| using r = new Reader(workerData) | ||
@@ -124,14 +119,12 @@ function poll() { | ||
| ### `alloc(size)` | ||
| ### `new Reader(handleOrSize)` | ||
| Allocates a shared ring buffer. Returns a handle that can be passed directly to `Reader`, `Writer`, and worker threads via `workerData`. | ||
| Creates a reader for the ring buffer. | ||
| - **size** — Maximum payload size in bytes for a single write (must be a positive integer, max ~2 GB) | ||
| - **handleOrSize** — Either a `SharedHandle` obtained from `writer.handle` (to connect to an existing ring buffer), or a positive integer to allocate a new ring buffer of that payload capacity in bytes (max ~2 GB). Overhead is added automatically. | ||
| Overhead (state header + length header + alignment) is added automatically on top of `size`. | ||
| #### `reader.handle` | ||
| ### `new Reader(handle)` | ||
| The underlying `SharedHandle` (`SharedArrayBuffer`). Pass this to another thread via `workerData` or to `new Writer(handle)` / `new Reader(handle)` to share the buffer. | ||
| Creates a reader for the ring buffer. | ||
| #### `reader.readSome(next, opaque?)` | ||
@@ -152,6 +145,12 @@ | ||
| ### `new Writer(handle, options?)` | ||
| #### `reader[Symbol.dispose]()` | ||
| Implements the [explicit resource management](https://github.com/tc39/proposal-explicit-resource-management) protocol. Currently a no-op; enables use with `using` declarations for forward compatibility. | ||
| ### `new Writer(handleOrSize, options?)` | ||
| Creates a writer for the ring buffer. | ||
| - **handleOrSize** — Either a `SharedHandle` obtained from `writer.handle` (to connect to an existing ring buffer), or a positive integer to allocate a new ring buffer of that payload capacity in bytes (max ~2 GB). Overhead is added automatically. | ||
| **Options:** | ||
@@ -162,2 +161,6 @@ | ||
| #### `writer.handle` | ||
| The underlying `SharedHandle` (`SharedArrayBuffer`). Pass this to another thread via `workerData` or to `new Reader(handle)` to share the buffer. | ||
| #### `writer.writeSync(len, fn, opaque?)` | ||
@@ -191,2 +194,6 @@ | ||
| #### `writer[Symbol.dispose]()` | ||
| Implements the [explicit resource management](https://github.com/tc39/proposal-explicit-resource-management) protocol. Calls `flushSync()` to publish any pending writes, then releases held resources. Enables use with `using` declarations. | ||
| ## Benchmarks | ||
@@ -193,0 +200,0 @@ |
33017
5.44%551
4.75%249
2.89%