sabcom 0.1.91 → 0.1.95
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/README.md +91 -0
- package/build/index.cjs +1 -1
- package/build/index.cjs.map +1 -1
- package/build/index.d.ts +148 -1
- package/build/index.js +1 -1
- package/build/index.js.map +1 -1
- package/package.json +6 -6
- package/src/index.ts +149 -1
package/README.md
CHANGED
|
@@ -8,6 +8,18 @@
|
|
|
8
8
|
|
|
9
9
|
A TypeScript/Node.js library for high-performance inter-thread communication using `SharedArrayBuffer` with atomic operations. It provides both synchronous and asynchronous APIs to transfer byte data between threads (e.g., Main thread and Worker threads) without the overhead of structured cloning or memory copying.
|
|
10
10
|
|
|
11
|
+
## What sabcom Does
|
|
12
|
+
|
|
13
|
+
sabcom is a **protocol layer** for SharedArrayBuffer. It handles:
|
|
14
|
+
- Synchronization between reader and writer via Atomics
|
|
15
|
+
- Chunking large data that exceeds buffer size
|
|
16
|
+
- Timeout detection to prevent deadlocks
|
|
17
|
+
|
|
18
|
+
sabcom does **NOT**:
|
|
19
|
+
- Create worker threads (you create them with `worker_threads` or `new Worker()`)
|
|
20
|
+
- Transfer the SharedArrayBuffer between threads (you pass it via `workerData` or `postMessage`)
|
|
21
|
+
- Serialize data (you encode to `Uint8Array` before calling write, e.g., with `TextEncoder` or `JSON.stringify`)
|
|
22
|
+
|
|
11
23
|
## Features
|
|
12
24
|
|
|
13
25
|
- **Thread-safe communication** using `Atomics` for synchronization.
|
|
@@ -175,6 +187,85 @@ The communication follows a strict handshake:
|
|
|
175
187
|
|
|
176
188
|
*Note: The `SharedArrayBuffer` is reusable after a successful transfer.*
|
|
177
189
|
|
|
190
|
+
## FAQ
|
|
191
|
+
|
|
192
|
+
### What is the minimum buffer size?
|
|
193
|
+
|
|
194
|
+
`HEADER_SIZE` is 16 bytes. Your buffer must be larger to have usable payload space:
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
import { HEADER_SIZE } from 'sabcom';
|
|
198
|
+
|
|
199
|
+
// Minimum: HEADER_SIZE + at least 1 byte for payload
|
|
200
|
+
// Practical minimum: 1024 bytes (1KB)
|
|
201
|
+
const buffer = new SharedArrayBuffer(1024);
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### How do I send JSON or objects?
|
|
205
|
+
|
|
206
|
+
sabcom transfers raw bytes only. Serialize before sending:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
// Writer
|
|
210
|
+
const obj = { hello: 'world', count: 42 };
|
|
211
|
+
const json = JSON.stringify(obj);
|
|
212
|
+
await write(new TextEncoder().encode(json), buffer);
|
|
213
|
+
|
|
214
|
+
// Reader
|
|
215
|
+
const data = await read(buffer);
|
|
216
|
+
const obj = JSON.parse(new TextDecoder().decode(data));
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Does sabcom work in browsers?
|
|
220
|
+
|
|
221
|
+
Yes, with Web Workers. However, `SharedArrayBuffer` requires cross-origin isolation headers on your server:
|
|
222
|
+
|
|
223
|
+
```
|
|
224
|
+
Cross-Origin-Opener-Policy: same-origin
|
|
225
|
+
Cross-Origin-Embedder-Policy: require-corp
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
### How do I handle errors?
|
|
229
|
+
|
|
230
|
+
```typescript
|
|
231
|
+
try {
|
|
232
|
+
await write(data, buffer, { timeout: 5000 });
|
|
233
|
+
} catch (err) {
|
|
234
|
+
if (err.message.includes('timeout')) {
|
|
235
|
+
console.error('Reader did not respond in time');
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Can I cancel a transfer mid-way?
|
|
241
|
+
|
|
242
|
+
Use generators with `for...of` - breaking out automatically triggers cleanup:
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
const gen = writeGenerator(data, buffer);
|
|
246
|
+
for (const request of gen) {
|
|
247
|
+
if (shouldCancel) break; // finally block resets buffer to READY
|
|
248
|
+
const result = Atomics.wait(request.target, request.index, request.value, request.timeout);
|
|
249
|
+
if (result === 'timed-out') break;
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Can I reuse the buffer after a transfer?
|
|
254
|
+
|
|
255
|
+
Yes. After a successful transfer (or error), the buffer resets to `READY` state and can be used again:
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
const buffer = new SharedArrayBuffer(4096);
|
|
259
|
+
|
|
260
|
+
// First transfer
|
|
261
|
+
await write(data1, buffer);
|
|
262
|
+
const result1 = await read(buffer);
|
|
263
|
+
|
|
264
|
+
// Second transfer - same buffer
|
|
265
|
+
await write(data2, buffer);
|
|
266
|
+
const result2 = await read(buffer);
|
|
267
|
+
```
|
|
268
|
+
|
|
178
269
|
## Development
|
|
179
270
|
|
|
180
271
|
```bash
|
package/build/index.cjs
CHANGED
|
@@ -64,7 +64,7 @@ var Header = /*#__PURE__*/ function(Header) {
|
|
|
64
64
|
Header[Header["CHUNK_SIZE"] = 3] = "CHUNK_SIZE";
|
|
65
65
|
return Header;
|
|
66
66
|
}({});
|
|
67
|
-
const HEADER_VALUES =
|
|
67
|
+
const HEADER_VALUES = 4;
|
|
68
68
|
const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;
|
|
69
69
|
function* writeGenerator(data, buffer, { timeout = 5000 } = {}) {
|
|
70
70
|
if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {
|
package/build/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export const SEMAPHORE = 0;\n\nexport enum Semaphore {\n READY,\n HANDSHAKE,\n PAYLOAD,\n}\n\nexport enum Handshake {\n TOTAL_SIZE = 1,\n TOTAL_CHUNKS,\n}\n\nexport enum Header {\n CHUNK_INDEX = 1,\n CHUNK_OFFSET,\n CHUNK_SIZE,\n}\n\nexport const HEADER_VALUES = 1 + Math.max(Object.values(Handshake).length, Object.values(Header).length) / 2;\nexport const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;\n\nexport interface Options {\n timeout?: number;\n}\n\nexport interface WaitRequest {\n target: Int32Array;\n index: number;\n value: number;\n timeout?: number;\n}\n\nexport type WaitResponse = ReturnType<typeof Atomics.wait>;\n\nexport function* writeGenerator(data: Uint8Array, buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, void, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const totalSize = data.length;\n const totalChunks = Math.ceil(totalSize / chunkSize);\n const header = new Int32Array(buffer);\n\n header[Handshake.TOTAL_SIZE] = totalSize;\n header[Handshake.TOTAL_CHUNKS] = totalChunks;\n Atomics.store(header, SEMAPHORE, Semaphore.HANDSHAKE);\n Atomics.notify(header, SEMAPHORE);\n\n try {\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.HANDSHAKE,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Reader handshake timeout');\n }\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const start = i * chunkSize;\n const end = Math.min(start + chunkSize, totalSize);\n const size = end - start;\n payload.set(data.subarray(start, end), 0);\n header[Header.CHUNK_INDEX] = i;\n header[Header.CHUNK_OFFSET] = start;\n header[Header.CHUNK_SIZE] = size;\n Atomics.store(header, SEMAPHORE, Semaphore.PAYLOAD);\n Atomics.notify(header, SEMAPHORE);\n\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.PAYLOAD,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Reader timeout on chunk ${i}/${totalChunks - 1}`);\n }\n }\n } finally {\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n}\n\nexport function* readGenerator(buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, Uint8Array, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const header = new Int32Array(buffer);\n\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Handshake timeout');\n }\n if (header[SEMAPHORE] !== Semaphore.HANDSHAKE) {\n throw new Error('Invalid handshake state');\n }\n\n const totalSize = header[Handshake.TOTAL_SIZE];\n const totalChunks = header[Handshake.TOTAL_CHUNKS];\n if (totalSize < 0 || totalChunks < 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize === 0 && totalChunks !== 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize > totalChunks * chunkSize) {\n throw new Error('Invalid handshake values');\n }\n const data = new Uint8Array(totalSize);\n\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Writer timeout waiting for chunk ${i}`);\n }\n // @ts-expect-error does not infer number\n if (header[SEMAPHORE] !== Semaphore.PAYLOAD) {\n throw new Error(`Expected payload header, received ${Semaphore[header[SEMAPHORE]]}`);\n }\n const chunkIndex = header[Header.CHUNK_INDEX];\n if (i !== chunkIndex) {\n throw new Error(`Reader integrity failure for chunk ${chunkIndex} expected ${i}`);\n }\n const offset = header[Header.CHUNK_OFFSET];\n const size = header[Header.CHUNK_SIZE];\n if (offset < 0 || size <= 0 || size > chunkSize || offset + size > totalSize) {\n throw new Error(`Invalid chunk metadata for chunk ${chunkIndex}`);\n }\n data.set(payload.subarray(0, size), offset);\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n return data;\n}\n\nexport const writeSync = (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): void => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n};\n\nexport const write = async (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): Promise<void> => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n};\n\nexport const readSync = (buffer: SharedArrayBuffer, options?: Options): Uint8Array => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n return result.value;\n};\n\nexport const read = async (buffer: SharedArrayBuffer, options?: Options): Promise<Uint8Array> => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n return result.value;\n};\n"],"names":["HEADER_SIZE","HEADER_VALUES","Handshake","Header","SEMAPHORE","Semaphore","read","readGenerator","readSync","write","writeGenerator","writeSync","Math","max","Object","values","length","Uint32Array","BYTES_PER_ELEMENT","data","buffer","timeout","byteLength","Int32Array","Error","chunkSize","totalSize","totalChunks","ceil","header","Atomics","store","notify","handshakeResult","target","index","value","payload","Uint8Array","i","start","end","min","size","set","subarray","chunkResult","chunkIndex","offset","options","gen","result","next","done","waitResult","wait","request","waitAsync"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export const SEMAPHORE = 0;\n\nexport enum Semaphore {\n READY,\n HANDSHAKE,\n PAYLOAD,\n}\n\nexport enum Handshake {\n TOTAL_SIZE = 1,\n TOTAL_CHUNKS,\n}\n\nexport enum Header {\n CHUNK_INDEX = 1,\n CHUNK_OFFSET,\n CHUNK_SIZE,\n}\n\nexport const HEADER_VALUES = 4;\n\n/**\n * Size in bytes reserved for protocol header in the SharedArrayBuffer.\n * The usable payload size is `buffer.byteLength - HEADER_SIZE`.\n */\nexport const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;\n\nexport interface Options {\n /** Max wait time in milliseconds before timeout error. Default: 5000 */\n timeout?: number;\n}\n\n/**\n * Request yielded by generator functions for Atomics.wait/waitAsync.\n * Pass to Atomics.wait() for sync or Atomics.waitAsync() for async waiting.\n */\nexport interface WaitRequest {\n /** Int32Array view of the SharedArrayBuffer header */\n target: Int32Array;\n /** Index in the array to wait on (always SEMAPHORE = 0) */\n index: number;\n /** Value to compare against before waiting */\n value: number;\n /** Timeout in milliseconds */\n timeout?: number;\n}\n\nexport type WaitResponse = ReturnType<typeof Atomics.wait>;\n\n/**\n * Low-level generator for writing data with custom flow control.\n * Use for progress tracking, cancellation, or custom scheduling.\n * @param data - Bytes to send (can be larger than buffer, will be chunked automatically)\n * @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @yields {WaitRequest} Request to wait for reader acknowledgment\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Reader handshake timeout\" - reader didn't respond in time\n * @throws {Error} \"Reader timeout on chunk N/M\" - reader stopped responding mid-transfer\n * @example\n * ```typescript\n * import { writeGenerator } from 'sabcom';\n *\n * const gen = writeGenerator(data, buffer);\n * let chunks = 0;\n * for (const request of gen) {\n * const result = Atomics.wait(request.target, request.index, request.value, request.timeout);\n * if (result === 'timed-out') throw new Error('Timeout');\n * console.log(`Chunk ${++chunks} sent`);\n * }\n * ```\n */\nexport function* writeGenerator(data: Uint8Array, buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, void, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const totalSize = data.length;\n const totalChunks = Math.ceil(totalSize / chunkSize);\n const header = new Int32Array(buffer);\n\n header[Handshake.TOTAL_SIZE] = totalSize;\n header[Handshake.TOTAL_CHUNKS] = totalChunks;\n Atomics.store(header, SEMAPHORE, Semaphore.HANDSHAKE);\n Atomics.notify(header, SEMAPHORE);\n\n try {\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.HANDSHAKE,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Reader handshake timeout');\n }\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const start = i * chunkSize;\n const end = Math.min(start + chunkSize, totalSize);\n const size = end - start;\n payload.set(data.subarray(start, end), 0);\n header[Header.CHUNK_INDEX] = i;\n header[Header.CHUNK_OFFSET] = start;\n header[Header.CHUNK_SIZE] = size;\n Atomics.store(header, SEMAPHORE, Semaphore.PAYLOAD);\n Atomics.notify(header, SEMAPHORE);\n\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.PAYLOAD,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Reader timeout on chunk ${i}/${totalChunks - 1}`);\n }\n }\n } finally {\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n}\n\n/**\n * Low-level generator for reading data with custom flow control.\n * Use for progress tracking, cancellation, or custom scheduling.\n * @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @yields {WaitRequest} Request to wait for writer data\n * @returns Complete data as Uint8Array\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Handshake timeout\" - writer didn't send data in time\n * @throws {Error} \"Invalid handshake state\" - protocol error\n * @throws {Error} \"Writer timeout waiting for chunk N\" - writer stopped responding mid-transfer\n * @example\n * ```typescript\n * import { readGenerator } from 'sabcom';\n *\n * const gen = readGenerator(buffer);\n * let result = gen.next();\n * while (!result.done) {\n * const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n * result = gen.next(waitResult);\n * }\n * const data = result.value; // Uint8Array\n * ```\n */\nexport function* readGenerator(buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, Uint8Array, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const header = new Int32Array(buffer);\n\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Handshake timeout');\n }\n if (header[SEMAPHORE] !== Semaphore.HANDSHAKE) {\n throw new Error('Invalid handshake state');\n }\n\n const totalSize = header[Handshake.TOTAL_SIZE];\n const totalChunks = header[Handshake.TOTAL_CHUNKS];\n if (totalSize < 0 || totalChunks < 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize === 0 && totalChunks !== 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize > totalChunks * chunkSize) {\n throw new Error('Invalid handshake values');\n }\n const data = new Uint8Array(totalSize);\n\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Writer timeout waiting for chunk ${i}`);\n }\n // @ts-expect-error does not infer number\n if (header[SEMAPHORE] !== Semaphore.PAYLOAD) {\n throw new Error(`Expected payload header, received ${Semaphore[header[SEMAPHORE]]}`);\n }\n const chunkIndex = header[Header.CHUNK_INDEX];\n if (i !== chunkIndex) {\n throw new Error(`Reader integrity failure for chunk ${chunkIndex} expected ${i}`);\n }\n const offset = header[Header.CHUNK_OFFSET];\n const size = header[Header.CHUNK_SIZE];\n if (offset < 0 || size <= 0 || size > chunkSize || offset + size > totalSize) {\n throw new Error(`Invalid chunk metadata for chunk ${chunkIndex}`);\n }\n data.set(payload.subarray(0, size), offset);\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n return data;\n}\n\n/**\n * Synchronously writes data to a SharedArrayBuffer for cross-thread transfer.\n * Blocks until the reader receives all data. Use in workers where blocking is acceptable.\n * @param data - Bytes to send (can be larger than buffer, will be chunked automatically)\n * @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Reader handshake timeout\" - reader didn't respond in time\n * @throws {Error} \"Reader timeout on chunk N/M\" - reader stopped responding mid-transfer\n * @example\n * ```typescript\n * import { workerData } from 'worker_threads';\n * import { writeSync } from 'sabcom';\n *\n * const buffer = workerData as SharedArrayBuffer;\n * const data = new TextEncoder().encode('Hello from worker');\n * writeSync(data, buffer);\n * ```\n */\nexport const writeSync = (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): void => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n};\n\n/**\n * Asynchronously writes data to a SharedArrayBuffer for cross-thread transfer.\n * Non-blocking, suitable for main thread. Resolves when the reader receives all data.\n * @param data - Bytes to send (can be larger than buffer, will be chunked automatically)\n * @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Reader handshake timeout\" - reader didn't respond in time\n * @throws {Error} \"Reader timeout on chunk N/M\" - reader stopped responding mid-transfer\n * @example\n * ```typescript\n * import { Worker } from 'worker_threads';\n * import { write } from 'sabcom';\n *\n * const buffer = new SharedArrayBuffer(4096);\n * const worker = new Worker('./worker.js', { workerData: buffer });\n * const data = new TextEncoder().encode('Hello from main');\n * await write(data, buffer);\n * ```\n */\nexport const write = async (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): Promise<void> => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n};\n\n/**\n * Synchronously reads data from a SharedArrayBuffer written by another thread.\n * Blocks until all data is received. Use in workers where blocking is acceptable.\n * @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @returns Complete data as Uint8Array\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Handshake timeout\" - writer didn't send data in time\n * @throws {Error} \"Invalid handshake state\" - protocol error\n * @throws {Error} \"Writer timeout waiting for chunk N\" - writer stopped responding mid-transfer\n * @example\n * ```typescript\n * import { workerData } from 'worker_threads';\n * import { readSync } from 'sabcom';\n *\n * const buffer = workerData as SharedArrayBuffer;\n * const data = readSync(buffer);\n * const message = new TextDecoder().decode(data);\n * ```\n */\nexport const readSync = (buffer: SharedArrayBuffer, options?: Options): Uint8Array => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n return result.value;\n};\n\n/**\n * Asynchronously reads data from a SharedArrayBuffer written by another thread.\n * Non-blocking, suitable for main thread. Resolves when all data is received.\n * @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @returns Complete data as Uint8Array\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Handshake timeout\" - writer didn't send data in time\n * @throws {Error} \"Invalid handshake state\" - protocol error\n * @throws {Error} \"Writer timeout waiting for chunk N\" - writer stopped responding mid-transfer\n * @example\n * ```typescript\n * import { Worker } from 'worker_threads';\n * import { read } from 'sabcom';\n *\n * const buffer = new SharedArrayBuffer(4096);\n * const worker = new Worker('./worker.js', { workerData: buffer });\n * // Worker writes data...\n * const data = await read(buffer);\n * const message = new TextDecoder().decode(data);\n * ```\n */\nexport const read = async (buffer: SharedArrayBuffer, options?: Options): Promise<Uint8Array> => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n return result.value;\n};\n"],"names":["HEADER_SIZE","HEADER_VALUES","Handshake","Header","SEMAPHORE","Semaphore","read","readGenerator","readSync","write","writeGenerator","writeSync","Uint32Array","BYTES_PER_ELEMENT","data","buffer","timeout","byteLength","Int32Array","Error","chunkSize","totalSize","length","totalChunks","Math","ceil","header","Atomics","store","notify","handshakeResult","target","index","value","payload","Uint8Array","i","start","end","min","size","set","subarray","chunkResult","chunkIndex","offset","options","gen","result","next","done","waitResult","wait","request","waitAsync"],"mappings":"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"}
|
package/build/index.d.ts
CHANGED
|
@@ -13,21 +13,168 @@ export declare enum Header {
|
|
|
13
13
|
CHUNK_OFFSET = 2,
|
|
14
14
|
CHUNK_SIZE = 3
|
|
15
15
|
}
|
|
16
|
-
export declare const HEADER_VALUES
|
|
16
|
+
export declare const HEADER_VALUES = 4;
|
|
17
|
+
/**
|
|
18
|
+
* Size in bytes reserved for protocol header in the SharedArrayBuffer.
|
|
19
|
+
* The usable payload size is `buffer.byteLength - HEADER_SIZE`.
|
|
20
|
+
*/
|
|
17
21
|
export declare const HEADER_SIZE: number;
|
|
18
22
|
export interface Options {
|
|
23
|
+
/** Max wait time in milliseconds before timeout error. Default: 5000 */
|
|
19
24
|
timeout?: number;
|
|
20
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Request yielded by generator functions for Atomics.wait/waitAsync.
|
|
28
|
+
* Pass to Atomics.wait() for sync or Atomics.waitAsync() for async waiting.
|
|
29
|
+
*/
|
|
21
30
|
export interface WaitRequest {
|
|
31
|
+
/** Int32Array view of the SharedArrayBuffer header */
|
|
22
32
|
target: Int32Array;
|
|
33
|
+
/** Index in the array to wait on (always SEMAPHORE = 0) */
|
|
23
34
|
index: number;
|
|
35
|
+
/** Value to compare against before waiting */
|
|
24
36
|
value: number;
|
|
37
|
+
/** Timeout in milliseconds */
|
|
25
38
|
timeout?: number;
|
|
26
39
|
}
|
|
27
40
|
export type WaitResponse = ReturnType<typeof Atomics.wait>;
|
|
41
|
+
/**
|
|
42
|
+
* Low-level generator for writing data with custom flow control.
|
|
43
|
+
* Use for progress tracking, cancellation, or custom scheduling.
|
|
44
|
+
* @param data - Bytes to send (can be larger than buffer, will be chunked automatically)
|
|
45
|
+
* @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
|
|
46
|
+
* @param options - Optional configuration
|
|
47
|
+
* @yields {WaitRequest} Request to wait for reader acknowledgment
|
|
48
|
+
* @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
|
|
49
|
+
* @throws {Error} "SharedArrayBuffer too small for header"
|
|
50
|
+
* @throws {Error} "Reader handshake timeout" - reader didn't respond in time
|
|
51
|
+
* @throws {Error} "Reader timeout on chunk N/M" - reader stopped responding mid-transfer
|
|
52
|
+
* @example
|
|
53
|
+
* ```typescript
|
|
54
|
+
* import { writeGenerator } from 'sabcom';
|
|
55
|
+
*
|
|
56
|
+
* const gen = writeGenerator(data, buffer);
|
|
57
|
+
* let chunks = 0;
|
|
58
|
+
* for (const request of gen) {
|
|
59
|
+
* const result = Atomics.wait(request.target, request.index, request.value, request.timeout);
|
|
60
|
+
* if (result === 'timed-out') throw new Error('Timeout');
|
|
61
|
+
* console.log(`Chunk ${++chunks} sent`);
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
28
65
|
export declare function writeGenerator(data: Uint8Array, buffer: SharedArrayBuffer, { timeout }?: Options): Generator<WaitRequest, void, WaitResponse>;
|
|
66
|
+
/**
|
|
67
|
+
* Low-level generator for reading data with custom flow control.
|
|
68
|
+
* Use for progress tracking, cancellation, or custom scheduling.
|
|
69
|
+
* @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
|
|
70
|
+
* @param options - Optional configuration
|
|
71
|
+
* @yields {WaitRequest} Request to wait for writer data
|
|
72
|
+
* @returns Complete data as Uint8Array
|
|
73
|
+
* @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
|
|
74
|
+
* @throws {Error} "SharedArrayBuffer too small for header"
|
|
75
|
+
* @throws {Error} "Handshake timeout" - writer didn't send data in time
|
|
76
|
+
* @throws {Error} "Invalid handshake state" - protocol error
|
|
77
|
+
* @throws {Error} "Writer timeout waiting for chunk N" - writer stopped responding mid-transfer
|
|
78
|
+
* @example
|
|
79
|
+
* ```typescript
|
|
80
|
+
* import { readGenerator } from 'sabcom';
|
|
81
|
+
*
|
|
82
|
+
* const gen = readGenerator(buffer);
|
|
83
|
+
* let result = gen.next();
|
|
84
|
+
* while (!result.done) {
|
|
85
|
+
* const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);
|
|
86
|
+
* result = gen.next(waitResult);
|
|
87
|
+
* }
|
|
88
|
+
* const data = result.value; // Uint8Array
|
|
89
|
+
* ```
|
|
90
|
+
*/
|
|
29
91
|
export declare function readGenerator(buffer: SharedArrayBuffer, { timeout }?: Options): Generator<WaitRequest, Uint8Array, WaitResponse>;
|
|
92
|
+
/**
|
|
93
|
+
* Synchronously writes data to a SharedArrayBuffer for cross-thread transfer.
|
|
94
|
+
* Blocks until the reader receives all data. Use in workers where blocking is acceptable.
|
|
95
|
+
* @param data - Bytes to send (can be larger than buffer, will be chunked automatically)
|
|
96
|
+
* @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
|
|
97
|
+
* @param options - Optional configuration
|
|
98
|
+
* @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
|
|
99
|
+
* @throws {Error} "SharedArrayBuffer too small for header"
|
|
100
|
+
* @throws {Error} "Reader handshake timeout" - reader didn't respond in time
|
|
101
|
+
* @throws {Error} "Reader timeout on chunk N/M" - reader stopped responding mid-transfer
|
|
102
|
+
* @example
|
|
103
|
+
* ```typescript
|
|
104
|
+
* import { workerData } from 'worker_threads';
|
|
105
|
+
* import { writeSync } from 'sabcom';
|
|
106
|
+
*
|
|
107
|
+
* const buffer = workerData as SharedArrayBuffer;
|
|
108
|
+
* const data = new TextEncoder().encode('Hello from worker');
|
|
109
|
+
* writeSync(data, buffer);
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
30
112
|
export declare const writeSync: (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options) => void;
|
|
113
|
+
/**
|
|
114
|
+
* Asynchronously writes data to a SharedArrayBuffer for cross-thread transfer.
|
|
115
|
+
* Non-blocking, suitable for main thread. Resolves when the reader receives all data.
|
|
116
|
+
* @param data - Bytes to send (can be larger than buffer, will be chunked automatically)
|
|
117
|
+
* @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
|
|
118
|
+
* @param options - Optional configuration
|
|
119
|
+
* @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
|
|
120
|
+
* @throws {Error} "SharedArrayBuffer too small for header"
|
|
121
|
+
* @throws {Error} "Reader handshake timeout" - reader didn't respond in time
|
|
122
|
+
* @throws {Error} "Reader timeout on chunk N/M" - reader stopped responding mid-transfer
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* import { Worker } from 'worker_threads';
|
|
126
|
+
* import { write } from 'sabcom';
|
|
127
|
+
*
|
|
128
|
+
* const buffer = new SharedArrayBuffer(4096);
|
|
129
|
+
* const worker = new Worker('./worker.js', { workerData: buffer });
|
|
130
|
+
* const data = new TextEncoder().encode('Hello from main');
|
|
131
|
+
* await write(data, buffer);
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
31
134
|
export declare const write: (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options) => Promise<void>;
|
|
135
|
+
/**
|
|
136
|
+
* Synchronously reads data from a SharedArrayBuffer written by another thread.
|
|
137
|
+
* Blocks until all data is received. Use in workers where blocking is acceptable.
|
|
138
|
+
* @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
|
|
139
|
+
* @param options - Optional configuration
|
|
140
|
+
* @returns Complete data as Uint8Array
|
|
141
|
+
* @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
|
|
142
|
+
* @throws {Error} "SharedArrayBuffer too small for header"
|
|
143
|
+
* @throws {Error} "Handshake timeout" - writer didn't send data in time
|
|
144
|
+
* @throws {Error} "Invalid handshake state" - protocol error
|
|
145
|
+
* @throws {Error} "Writer timeout waiting for chunk N" - writer stopped responding mid-transfer
|
|
146
|
+
* @example
|
|
147
|
+
* ```typescript
|
|
148
|
+
* import { workerData } from 'worker_threads';
|
|
149
|
+
* import { readSync } from 'sabcom';
|
|
150
|
+
*
|
|
151
|
+
* const buffer = workerData as SharedArrayBuffer;
|
|
152
|
+
* const data = readSync(buffer);
|
|
153
|
+
* const message = new TextDecoder().decode(data);
|
|
154
|
+
* ```
|
|
155
|
+
*/
|
|
32
156
|
export declare const readSync: (buffer: SharedArrayBuffer, options?: Options) => Uint8Array;
|
|
157
|
+
/**
|
|
158
|
+
* Asynchronously reads data from a SharedArrayBuffer written by another thread.
|
|
159
|
+
* Non-blocking, suitable for main thread. Resolves when all data is received.
|
|
160
|
+
* @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
|
|
161
|
+
* @param options - Optional configuration
|
|
162
|
+
* @returns Complete data as Uint8Array
|
|
163
|
+
* @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
|
|
164
|
+
* @throws {Error} "SharedArrayBuffer too small for header"
|
|
165
|
+
* @throws {Error} "Handshake timeout" - writer didn't send data in time
|
|
166
|
+
* @throws {Error} "Invalid handshake state" - protocol error
|
|
167
|
+
* @throws {Error} "Writer timeout waiting for chunk N" - writer stopped responding mid-transfer
|
|
168
|
+
* @example
|
|
169
|
+
* ```typescript
|
|
170
|
+
* import { Worker } from 'worker_threads';
|
|
171
|
+
* import { read } from 'sabcom';
|
|
172
|
+
*
|
|
173
|
+
* const buffer = new SharedArrayBuffer(4096);
|
|
174
|
+
* const worker = new Worker('./worker.js', { workerData: buffer });
|
|
175
|
+
* // Worker writes data...
|
|
176
|
+
* const data = await read(buffer);
|
|
177
|
+
* const message = new TextDecoder().decode(data);
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
33
180
|
export declare const read: (buffer: SharedArrayBuffer, options?: Options) => Promise<Uint8Array>;
|
package/build/index.js
CHANGED
|
@@ -16,7 +16,7 @@ export var Header = /*#__PURE__*/ function(Header) {
|
|
|
16
16
|
Header[Header["CHUNK_SIZE"] = 3] = "CHUNK_SIZE";
|
|
17
17
|
return Header;
|
|
18
18
|
}({});
|
|
19
|
-
export const HEADER_VALUES =
|
|
19
|
+
export const HEADER_VALUES = 4;
|
|
20
20
|
export const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;
|
|
21
21
|
export function* writeGenerator(data, buffer, { timeout = 5000 } = {}) {
|
|
22
22
|
if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export const SEMAPHORE = 0;\n\nexport enum Semaphore {\n READY,\n HANDSHAKE,\n PAYLOAD,\n}\n\nexport enum Handshake {\n TOTAL_SIZE = 1,\n TOTAL_CHUNKS,\n}\n\nexport enum Header {\n CHUNK_INDEX = 1,\n CHUNK_OFFSET,\n CHUNK_SIZE,\n}\n\nexport const HEADER_VALUES = 1 + Math.max(Object.values(Handshake).length, Object.values(Header).length) / 2;\nexport const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;\n\nexport interface Options {\n timeout?: number;\n}\n\nexport interface WaitRequest {\n target: Int32Array;\n index: number;\n value: number;\n timeout?: number;\n}\n\nexport type WaitResponse = ReturnType<typeof Atomics.wait>;\n\nexport function* writeGenerator(data: Uint8Array, buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, void, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const totalSize = data.length;\n const totalChunks = Math.ceil(totalSize / chunkSize);\n const header = new Int32Array(buffer);\n\n header[Handshake.TOTAL_SIZE] = totalSize;\n header[Handshake.TOTAL_CHUNKS] = totalChunks;\n Atomics.store(header, SEMAPHORE, Semaphore.HANDSHAKE);\n Atomics.notify(header, SEMAPHORE);\n\n try {\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.HANDSHAKE,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Reader handshake timeout');\n }\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const start = i * chunkSize;\n const end = Math.min(start + chunkSize, totalSize);\n const size = end - start;\n payload.set(data.subarray(start, end), 0);\n header[Header.CHUNK_INDEX] = i;\n header[Header.CHUNK_OFFSET] = start;\n header[Header.CHUNK_SIZE] = size;\n Atomics.store(header, SEMAPHORE, Semaphore.PAYLOAD);\n Atomics.notify(header, SEMAPHORE);\n\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.PAYLOAD,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Reader timeout on chunk ${i}/${totalChunks - 1}`);\n }\n }\n } finally {\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n}\n\nexport function* readGenerator(buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, Uint8Array, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const header = new Int32Array(buffer);\n\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Handshake timeout');\n }\n if (header[SEMAPHORE] !== Semaphore.HANDSHAKE) {\n throw new Error('Invalid handshake state');\n }\n\n const totalSize = header[Handshake.TOTAL_SIZE];\n const totalChunks = header[Handshake.TOTAL_CHUNKS];\n if (totalSize < 0 || totalChunks < 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize === 0 && totalChunks !== 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize > totalChunks * chunkSize) {\n throw new Error('Invalid handshake values');\n }\n const data = new Uint8Array(totalSize);\n\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Writer timeout waiting for chunk ${i}`);\n }\n // @ts-expect-error does not infer number\n if (header[SEMAPHORE] !== Semaphore.PAYLOAD) {\n throw new Error(`Expected payload header, received ${Semaphore[header[SEMAPHORE]]}`);\n }\n const chunkIndex = header[Header.CHUNK_INDEX];\n if (i !== chunkIndex) {\n throw new Error(`Reader integrity failure for chunk ${chunkIndex} expected ${i}`);\n }\n const offset = header[Header.CHUNK_OFFSET];\n const size = header[Header.CHUNK_SIZE];\n if (offset < 0 || size <= 0 || size > chunkSize || offset + size > totalSize) {\n throw new Error(`Invalid chunk metadata for chunk ${chunkIndex}`);\n }\n data.set(payload.subarray(0, size), offset);\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n return data;\n}\n\nexport const writeSync = (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): void => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n};\n\nexport const write = async (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): Promise<void> => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n};\n\nexport const readSync = (buffer: SharedArrayBuffer, options?: Options): Uint8Array => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n return result.value;\n};\n\nexport const read = async (buffer: SharedArrayBuffer, options?: Options): Promise<Uint8Array> => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n return result.value;\n};\n"],"names":["SEMAPHORE","Semaphore","Handshake","Header","HEADER_VALUES","Math","max","Object","values","length","HEADER_SIZE","Uint32Array","BYTES_PER_ELEMENT","writeGenerator","data","buffer","timeout","byteLength","Int32Array","Error","chunkSize","totalSize","totalChunks","ceil","header","Atomics","store","notify","handshakeResult","target","index","value","payload","Uint8Array","i","start","end","min","size","set","subarray","chunkResult","readGenerator","chunkIndex","offset","writeSync","options","gen","result","next","done","waitResult","wait","write","request","waitAsync","readSync","read"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"sourcesContent":["export const SEMAPHORE = 0;\n\nexport enum Semaphore {\n READY,\n HANDSHAKE,\n PAYLOAD,\n}\n\nexport enum Handshake {\n TOTAL_SIZE = 1,\n TOTAL_CHUNKS,\n}\n\nexport enum Header {\n CHUNK_INDEX = 1,\n CHUNK_OFFSET,\n CHUNK_SIZE,\n}\n\nexport const HEADER_VALUES = 4;\n\n/**\n * Size in bytes reserved for protocol header in the SharedArrayBuffer.\n * The usable payload size is `buffer.byteLength - HEADER_SIZE`.\n */\nexport const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;\n\nexport interface Options {\n /** Max wait time in milliseconds before timeout error. Default: 5000 */\n timeout?: number;\n}\n\n/**\n * Request yielded by generator functions for Atomics.wait/waitAsync.\n * Pass to Atomics.wait() for sync or Atomics.waitAsync() for async waiting.\n */\nexport interface WaitRequest {\n /** Int32Array view of the SharedArrayBuffer header */\n target: Int32Array;\n /** Index in the array to wait on (always SEMAPHORE = 0) */\n index: number;\n /** Value to compare against before waiting */\n value: number;\n /** Timeout in milliseconds */\n timeout?: number;\n}\n\nexport type WaitResponse = ReturnType<typeof Atomics.wait>;\n\n/**\n * Low-level generator for writing data with custom flow control.\n * Use for progress tracking, cancellation, or custom scheduling.\n * @param data - Bytes to send (can be larger than buffer, will be chunked automatically)\n * @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @yields {WaitRequest} Request to wait for reader acknowledgment\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Reader handshake timeout\" - reader didn't respond in time\n * @throws {Error} \"Reader timeout on chunk N/M\" - reader stopped responding mid-transfer\n * @example\n * ```typescript\n * import { writeGenerator } from 'sabcom';\n *\n * const gen = writeGenerator(data, buffer);\n * let chunks = 0;\n * for (const request of gen) {\n * const result = Atomics.wait(request.target, request.index, request.value, request.timeout);\n * if (result === 'timed-out') throw new Error('Timeout');\n * console.log(`Chunk ${++chunks} sent`);\n * }\n * ```\n */\nexport function* writeGenerator(data: Uint8Array, buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, void, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const totalSize = data.length;\n const totalChunks = Math.ceil(totalSize / chunkSize);\n const header = new Int32Array(buffer);\n\n header[Handshake.TOTAL_SIZE] = totalSize;\n header[Handshake.TOTAL_CHUNKS] = totalChunks;\n Atomics.store(header, SEMAPHORE, Semaphore.HANDSHAKE);\n Atomics.notify(header, SEMAPHORE);\n\n try {\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.HANDSHAKE,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Reader handshake timeout');\n }\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const start = i * chunkSize;\n const end = Math.min(start + chunkSize, totalSize);\n const size = end - start;\n payload.set(data.subarray(start, end), 0);\n header[Header.CHUNK_INDEX] = i;\n header[Header.CHUNK_OFFSET] = start;\n header[Header.CHUNK_SIZE] = size;\n Atomics.store(header, SEMAPHORE, Semaphore.PAYLOAD);\n Atomics.notify(header, SEMAPHORE);\n\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.PAYLOAD,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Reader timeout on chunk ${i}/${totalChunks - 1}`);\n }\n }\n } finally {\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n}\n\n/**\n * Low-level generator for reading data with custom flow control.\n * Use for progress tracking, cancellation, or custom scheduling.\n * @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @yields {WaitRequest} Request to wait for writer data\n * @returns Complete data as Uint8Array\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Handshake timeout\" - writer didn't send data in time\n * @throws {Error} \"Invalid handshake state\" - protocol error\n * @throws {Error} \"Writer timeout waiting for chunk N\" - writer stopped responding mid-transfer\n * @example\n * ```typescript\n * import { readGenerator } from 'sabcom';\n *\n * const gen = readGenerator(buffer);\n * let result = gen.next();\n * while (!result.done) {\n * const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n * result = gen.next(waitResult);\n * }\n * const data = result.value; // Uint8Array\n * ```\n */\nexport function* readGenerator(buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, Uint8Array, WaitResponse> {\n if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {\n throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');\n }\n const chunkSize = buffer.byteLength - HEADER_SIZE;\n if (chunkSize <= 0) {\n throw new Error('SharedArrayBuffer too small for header');\n }\n const header = new Int32Array(buffer);\n\n const handshakeResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (handshakeResult === 'timed-out') {\n throw new Error('Handshake timeout');\n }\n if (header[SEMAPHORE] !== Semaphore.HANDSHAKE) {\n throw new Error('Invalid handshake state');\n }\n\n const totalSize = header[Handshake.TOTAL_SIZE];\n const totalChunks = header[Handshake.TOTAL_CHUNKS];\n if (totalSize < 0 || totalChunks < 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize === 0 && totalChunks !== 0) {\n throw new Error('Invalid handshake values');\n }\n if (totalSize > totalChunks * chunkSize) {\n throw new Error('Invalid handshake values');\n }\n const data = new Uint8Array(totalSize);\n\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n\n const payload = new Uint8Array(buffer, HEADER_SIZE);\n for (let i = 0; i < totalChunks; i++) {\n const chunkResult: WaitResponse = yield {\n target: header,\n index: SEMAPHORE,\n value: Semaphore.READY,\n timeout,\n };\n if (chunkResult === 'timed-out') {\n throw new Error(`Writer timeout waiting for chunk ${i}`);\n }\n // @ts-expect-error does not infer number\n if (header[SEMAPHORE] !== Semaphore.PAYLOAD) {\n throw new Error(`Expected payload header, received ${Semaphore[header[SEMAPHORE]]}`);\n }\n const chunkIndex = header[Header.CHUNK_INDEX];\n if (i !== chunkIndex) {\n throw new Error(`Reader integrity failure for chunk ${chunkIndex} expected ${i}`);\n }\n const offset = header[Header.CHUNK_OFFSET];\n const size = header[Header.CHUNK_SIZE];\n if (offset < 0 || size <= 0 || size > chunkSize || offset + size > totalSize) {\n throw new Error(`Invalid chunk metadata for chunk ${chunkIndex}`);\n }\n data.set(payload.subarray(0, size), offset);\n Atomics.store(header, SEMAPHORE, Semaphore.READY);\n Atomics.notify(header, SEMAPHORE);\n }\n return data;\n}\n\n/**\n * Synchronously writes data to a SharedArrayBuffer for cross-thread transfer.\n * Blocks until the reader receives all data. Use in workers where blocking is acceptable.\n * @param data - Bytes to send (can be larger than buffer, will be chunked automatically)\n * @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Reader handshake timeout\" - reader didn't respond in time\n * @throws {Error} \"Reader timeout on chunk N/M\" - reader stopped responding mid-transfer\n * @example\n * ```typescript\n * import { workerData } from 'worker_threads';\n * import { writeSync } from 'sabcom';\n *\n * const buffer = workerData as SharedArrayBuffer;\n * const data = new TextEncoder().encode('Hello from worker');\n * writeSync(data, buffer);\n * ```\n */\nexport const writeSync = (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): void => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n};\n\n/**\n * Asynchronously writes data to a SharedArrayBuffer for cross-thread transfer.\n * Non-blocking, suitable for main thread. Resolves when the reader receives all data.\n * @param data - Bytes to send (can be larger than buffer, will be chunked automatically)\n * @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Reader handshake timeout\" - reader didn't respond in time\n * @throws {Error} \"Reader timeout on chunk N/M\" - reader stopped responding mid-transfer\n * @example\n * ```typescript\n * import { Worker } from 'worker_threads';\n * import { write } from 'sabcom';\n *\n * const buffer = new SharedArrayBuffer(4096);\n * const worker = new Worker('./worker.js', { workerData: buffer });\n * const data = new TextEncoder().encode('Hello from main');\n * await write(data, buffer);\n * ```\n */\nexport const write = async (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): Promise<void> => {\n const gen = writeGenerator(data, buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n};\n\n/**\n * Synchronously reads data from a SharedArrayBuffer written by another thread.\n * Blocks until all data is received. Use in workers where blocking is acceptable.\n * @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @returns Complete data as Uint8Array\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Handshake timeout\" - writer didn't send data in time\n * @throws {Error} \"Invalid handshake state\" - protocol error\n * @throws {Error} \"Writer timeout waiting for chunk N\" - writer stopped responding mid-transfer\n * @example\n * ```typescript\n * import { workerData } from 'worker_threads';\n * import { readSync } from 'sabcom';\n *\n * const buffer = workerData as SharedArrayBuffer;\n * const data = readSync(buffer);\n * const message = new TextDecoder().decode(data);\n * ```\n */\nexport const readSync = (buffer: SharedArrayBuffer, options?: Options): Uint8Array => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);\n result = gen.next(waitResult);\n }\n return result.value;\n};\n\n/**\n * Asynchronously reads data from a SharedArrayBuffer written by another thread.\n * Non-blocking, suitable for main thread. Resolves when all data is received.\n * @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)\n * @param options - Optional configuration\n * @returns Complete data as Uint8Array\n * @throws {Error} \"SharedArrayBuffer byteLength must be a multiple of 4\"\n * @throws {Error} \"SharedArrayBuffer too small for header\"\n * @throws {Error} \"Handshake timeout\" - writer didn't send data in time\n * @throws {Error} \"Invalid handshake state\" - protocol error\n * @throws {Error} \"Writer timeout waiting for chunk N\" - writer stopped responding mid-transfer\n * @example\n * ```typescript\n * import { Worker } from 'worker_threads';\n * import { read } from 'sabcom';\n *\n * const buffer = new SharedArrayBuffer(4096);\n * const worker = new Worker('./worker.js', { workerData: buffer });\n * // Worker writes data...\n * const data = await read(buffer);\n * const message = new TextDecoder().decode(data);\n * ```\n */\nexport const read = async (buffer: SharedArrayBuffer, options?: Options): Promise<Uint8Array> => {\n const gen = readGenerator(buffer, options);\n let result = gen.next();\n while (!result.done) {\n const request = result.value;\n const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;\n result = gen.next(waitResult);\n }\n return result.value;\n};\n"],"names":["SEMAPHORE","Semaphore","Handshake","Header","HEADER_VALUES","HEADER_SIZE","Uint32Array","BYTES_PER_ELEMENT","writeGenerator","data","buffer","timeout","byteLength","Int32Array","Error","chunkSize","totalSize","length","totalChunks","Math","ceil","header","Atomics","store","notify","handshakeResult","target","index","value","payload","Uint8Array","i","start","end","min","size","set","subarray","chunkResult","readGenerator","chunkIndex","offset","writeSync","options","gen","result","next","done","waitResult","wait","write","request","waitAsync","readSync","read"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sabcom",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.95",
|
|
4
4
|
"description": "A TypeScript/Node.js library for inter-thread communication using SharedArrayBuffer with atomic operations for raw buffer data transfer",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"types": "build/index.d.ts",
|
|
@@ -42,18 +42,18 @@
|
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
44
|
"@eslint/js": "^9.39.2",
|
|
45
|
-
"@types/node": "^25.0.
|
|
46
|
-
"@typescript-eslint/eslint-plugin": "^8.53.
|
|
47
|
-
"@typescript-eslint/parser": "^8.53.
|
|
45
|
+
"@types/node": "^25.0.10",
|
|
46
|
+
"@typescript-eslint/eslint-plugin": "^8.53.1",
|
|
47
|
+
"@typescript-eslint/parser": "^8.53.1",
|
|
48
48
|
"@vitest/coverage-v8": "^4.0.17",
|
|
49
49
|
"eslint": "^9.39.2",
|
|
50
50
|
"eslint-config-prettier": "^10.1.8",
|
|
51
51
|
"eslint-plugin-prettier": "^5.5.5",
|
|
52
52
|
"husky": "^9.1.7",
|
|
53
53
|
"inop": "^0.8.10",
|
|
54
|
-
"prettier": "^3.8.
|
|
54
|
+
"prettier": "^3.8.1",
|
|
55
55
|
"typescript": "^5.9.3",
|
|
56
|
-
"typescript-eslint": "^8.53.
|
|
56
|
+
"typescript-eslint": "^8.53.1",
|
|
57
57
|
"vitest": "^4.0.17"
|
|
58
58
|
},
|
|
59
59
|
"scripts": {
|
package/src/index.ts
CHANGED
|
@@ -17,22 +17,60 @@ export enum Header {
|
|
|
17
17
|
CHUNK_SIZE,
|
|
18
18
|
}
|
|
19
19
|
|
|
20
|
-
export const HEADER_VALUES =
|
|
20
|
+
export const HEADER_VALUES = 4;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Size in bytes reserved for protocol header in the SharedArrayBuffer.
|
|
24
|
+
* The usable payload size is `buffer.byteLength - HEADER_SIZE`.
|
|
25
|
+
*/
|
|
21
26
|
export const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;
|
|
22
27
|
|
|
23
28
|
export interface Options {
|
|
29
|
+
/** Max wait time in milliseconds before timeout error. Default: 5000 */
|
|
24
30
|
timeout?: number;
|
|
25
31
|
}
|
|
26
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Request yielded by generator functions for Atomics.wait/waitAsync.
|
|
35
|
+
* Pass to Atomics.wait() for sync or Atomics.waitAsync() for async waiting.
|
|
36
|
+
*/
|
|
27
37
|
export interface WaitRequest {
|
|
38
|
+
/** Int32Array view of the SharedArrayBuffer header */
|
|
28
39
|
target: Int32Array;
|
|
40
|
+
/** Index in the array to wait on (always SEMAPHORE = 0) */
|
|
29
41
|
index: number;
|
|
42
|
+
/** Value to compare against before waiting */
|
|
30
43
|
value: number;
|
|
44
|
+
/** Timeout in milliseconds */
|
|
31
45
|
timeout?: number;
|
|
32
46
|
}
|
|
33
47
|
|
|
34
48
|
export type WaitResponse = ReturnType<typeof Atomics.wait>;
|
|
35
49
|
|
|
50
|
+
/**
|
|
51
|
+
* Low-level generator for writing data with custom flow control.
|
|
52
|
+
* Use for progress tracking, cancellation, or custom scheduling.
|
|
53
|
+
* @param data - Bytes to send (can be larger than buffer, will be chunked automatically)
|
|
54
|
+
* @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
|
|
55
|
+
* @param options - Optional configuration
|
|
56
|
+
* @yields {WaitRequest} Request to wait for reader acknowledgment
|
|
57
|
+
* @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
|
|
58
|
+
* @throws {Error} "SharedArrayBuffer too small for header"
|
|
59
|
+
* @throws {Error} "Reader handshake timeout" - reader didn't respond in time
|
|
60
|
+
* @throws {Error} "Reader timeout on chunk N/M" - reader stopped responding mid-transfer
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* import { writeGenerator } from 'sabcom';
|
|
64
|
+
*
|
|
65
|
+
* const gen = writeGenerator(data, buffer);
|
|
66
|
+
* let chunks = 0;
|
|
67
|
+
* for (const request of gen) {
|
|
68
|
+
* const result = Atomics.wait(request.target, request.index, request.value, request.timeout);
|
|
69
|
+
* if (result === 'timed-out') throw new Error('Timeout');
|
|
70
|
+
* console.log(`Chunk ${++chunks} sent`);
|
|
71
|
+
* }
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
36
74
|
export function* writeGenerator(data: Uint8Array, buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, void, WaitResponse> {
|
|
37
75
|
if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {
|
|
38
76
|
throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');
|
|
@@ -89,6 +127,31 @@ export function* writeGenerator(data: Uint8Array, buffer: SharedArrayBuffer, { t
|
|
|
89
127
|
}
|
|
90
128
|
}
|
|
91
129
|
|
|
130
|
+
/**
|
|
131
|
+
* Low-level generator for reading data with custom flow control.
|
|
132
|
+
* Use for progress tracking, cancellation, or custom scheduling.
|
|
133
|
+
* @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
|
|
134
|
+
* @param options - Optional configuration
|
|
135
|
+
* @yields {WaitRequest} Request to wait for writer data
|
|
136
|
+
* @returns Complete data as Uint8Array
|
|
137
|
+
* @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
|
|
138
|
+
* @throws {Error} "SharedArrayBuffer too small for header"
|
|
139
|
+
* @throws {Error} "Handshake timeout" - writer didn't send data in time
|
|
140
|
+
* @throws {Error} "Invalid handshake state" - protocol error
|
|
141
|
+
* @throws {Error} "Writer timeout waiting for chunk N" - writer stopped responding mid-transfer
|
|
142
|
+
* @example
|
|
143
|
+
* ```typescript
|
|
144
|
+
* import { readGenerator } from 'sabcom';
|
|
145
|
+
*
|
|
146
|
+
* const gen = readGenerator(buffer);
|
|
147
|
+
* let result = gen.next();
|
|
148
|
+
* while (!result.done) {
|
|
149
|
+
* const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);
|
|
150
|
+
* result = gen.next(waitResult);
|
|
151
|
+
* }
|
|
152
|
+
* const data = result.value; // Uint8Array
|
|
153
|
+
* ```
|
|
154
|
+
*/
|
|
92
155
|
export function* readGenerator(buffer: SharedArrayBuffer, { timeout = 5000 }: Options = {}): Generator<WaitRequest, Uint8Array, WaitResponse> {
|
|
93
156
|
if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {
|
|
94
157
|
throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');
|
|
@@ -159,6 +222,26 @@ export function* readGenerator(buffer: SharedArrayBuffer, { timeout = 5000 }: Op
|
|
|
159
222
|
return data;
|
|
160
223
|
}
|
|
161
224
|
|
|
225
|
+
/**
|
|
226
|
+
* Synchronously writes data to a SharedArrayBuffer for cross-thread transfer.
|
|
227
|
+
* Blocks until the reader receives all data. Use in workers where blocking is acceptable.
|
|
228
|
+
* @param data - Bytes to send (can be larger than buffer, will be chunked automatically)
|
|
229
|
+
* @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
|
|
230
|
+
* @param options - Optional configuration
|
|
231
|
+
* @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
|
|
232
|
+
* @throws {Error} "SharedArrayBuffer too small for header"
|
|
233
|
+
* @throws {Error} "Reader handshake timeout" - reader didn't respond in time
|
|
234
|
+
* @throws {Error} "Reader timeout on chunk N/M" - reader stopped responding mid-transfer
|
|
235
|
+
* @example
|
|
236
|
+
* ```typescript
|
|
237
|
+
* import { workerData } from 'worker_threads';
|
|
238
|
+
* import { writeSync } from 'sabcom';
|
|
239
|
+
*
|
|
240
|
+
* const buffer = workerData as SharedArrayBuffer;
|
|
241
|
+
* const data = new TextEncoder().encode('Hello from worker');
|
|
242
|
+
* writeSync(data, buffer);
|
|
243
|
+
* ```
|
|
244
|
+
*/
|
|
162
245
|
export const writeSync = (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): void => {
|
|
163
246
|
const gen = writeGenerator(data, buffer, options);
|
|
164
247
|
let result = gen.next();
|
|
@@ -168,6 +251,27 @@ export const writeSync = (data: Uint8Array, buffer: SharedArrayBuffer, options?:
|
|
|
168
251
|
}
|
|
169
252
|
};
|
|
170
253
|
|
|
254
|
+
/**
|
|
255
|
+
* Asynchronously writes data to a SharedArrayBuffer for cross-thread transfer.
|
|
256
|
+
* Non-blocking, suitable for main thread. Resolves when the reader receives all data.
|
|
257
|
+
* @param data - Bytes to send (can be larger than buffer, will be chunked automatically)
|
|
258
|
+
* @param buffer - SharedArrayBuffer shared with the reader thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
|
|
259
|
+
* @param options - Optional configuration
|
|
260
|
+
* @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
|
|
261
|
+
* @throws {Error} "SharedArrayBuffer too small for header"
|
|
262
|
+
* @throws {Error} "Reader handshake timeout" - reader didn't respond in time
|
|
263
|
+
* @throws {Error} "Reader timeout on chunk N/M" - reader stopped responding mid-transfer
|
|
264
|
+
* @example
|
|
265
|
+
* ```typescript
|
|
266
|
+
* import { Worker } from 'worker_threads';
|
|
267
|
+
* import { write } from 'sabcom';
|
|
268
|
+
*
|
|
269
|
+
* const buffer = new SharedArrayBuffer(4096);
|
|
270
|
+
* const worker = new Worker('./worker.js', { workerData: buffer });
|
|
271
|
+
* const data = new TextEncoder().encode('Hello from main');
|
|
272
|
+
* await write(data, buffer);
|
|
273
|
+
* ```
|
|
274
|
+
*/
|
|
171
275
|
export const write = async (data: Uint8Array, buffer: SharedArrayBuffer, options?: Options): Promise<void> => {
|
|
172
276
|
const gen = writeGenerator(data, buffer, options);
|
|
173
277
|
let result = gen.next();
|
|
@@ -178,6 +282,27 @@ export const write = async (data: Uint8Array, buffer: SharedArrayBuffer, options
|
|
|
178
282
|
}
|
|
179
283
|
};
|
|
180
284
|
|
|
285
|
+
/**
|
|
286
|
+
* Synchronously reads data from a SharedArrayBuffer written by another thread.
|
|
287
|
+
* Blocks until all data is received. Use in workers where blocking is acceptable.
|
|
288
|
+
* @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
|
|
289
|
+
* @param options - Optional configuration
|
|
290
|
+
* @returns Complete data as Uint8Array
|
|
291
|
+
* @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
|
|
292
|
+
* @throws {Error} "SharedArrayBuffer too small for header"
|
|
293
|
+
* @throws {Error} "Handshake timeout" - writer didn't send data in time
|
|
294
|
+
* @throws {Error} "Invalid handshake state" - protocol error
|
|
295
|
+
* @throws {Error} "Writer timeout waiting for chunk N" - writer stopped responding mid-transfer
|
|
296
|
+
* @example
|
|
297
|
+
* ```typescript
|
|
298
|
+
* import { workerData } from 'worker_threads';
|
|
299
|
+
* import { readSync } from 'sabcom';
|
|
300
|
+
*
|
|
301
|
+
* const buffer = workerData as SharedArrayBuffer;
|
|
302
|
+
* const data = readSync(buffer);
|
|
303
|
+
* const message = new TextDecoder().decode(data);
|
|
304
|
+
* ```
|
|
305
|
+
*/
|
|
181
306
|
export const readSync = (buffer: SharedArrayBuffer, options?: Options): Uint8Array => {
|
|
182
307
|
const gen = readGenerator(buffer, options);
|
|
183
308
|
let result = gen.next();
|
|
@@ -188,6 +313,29 @@ export const readSync = (buffer: SharedArrayBuffer, options?: Options): Uint8Arr
|
|
|
188
313
|
return result.value;
|
|
189
314
|
};
|
|
190
315
|
|
|
316
|
+
/**
|
|
317
|
+
* Asynchronously reads data from a SharedArrayBuffer written by another thread.
|
|
318
|
+
* Non-blocking, suitable for main thread. Resolves when all data is received.
|
|
319
|
+
* @param buffer - SharedArrayBuffer shared with the writer thread (byteLength must be multiple of 4 and larger than HEADER_SIZE)
|
|
320
|
+
* @param options - Optional configuration
|
|
321
|
+
* @returns Complete data as Uint8Array
|
|
322
|
+
* @throws {Error} "SharedArrayBuffer byteLength must be a multiple of 4"
|
|
323
|
+
* @throws {Error} "SharedArrayBuffer too small for header"
|
|
324
|
+
* @throws {Error} "Handshake timeout" - writer didn't send data in time
|
|
325
|
+
* @throws {Error} "Invalid handshake state" - protocol error
|
|
326
|
+
* @throws {Error} "Writer timeout waiting for chunk N" - writer stopped responding mid-transfer
|
|
327
|
+
* @example
|
|
328
|
+
* ```typescript
|
|
329
|
+
* import { Worker } from 'worker_threads';
|
|
330
|
+
* import { read } from 'sabcom';
|
|
331
|
+
*
|
|
332
|
+
* const buffer = new SharedArrayBuffer(4096);
|
|
333
|
+
* const worker = new Worker('./worker.js', { workerData: buffer });
|
|
334
|
+
* // Worker writes data...
|
|
335
|
+
* const data = await read(buffer);
|
|
336
|
+
* const message = new TextDecoder().decode(data);
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
191
339
|
export const read = async (buffer: SharedArrayBuffer, options?: Options): Promise<Uint8Array> => {
|
|
192
340
|
const gen = readGenerator(buffer, options);
|
|
193
341
|
let result = gen.next();
|