@nxtedition/shared 4.0.1 → 4.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -17
- package/lib/index.d.ts +14 -11
- package/lib/index.js +29 -39
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -23,10 +23,10 @@ npm install @nxtedition/shared
|
|
|
23
23
|
## Usage
|
|
24
24
|
|
|
25
25
|
```js
|
|
26
|
-
import {
|
|
26
|
+
import { SharedStateBuffer, Reader, Writer } from '@nxtedition/shared'
|
|
27
27
|
|
|
28
|
-
// Allocate shared memory (pass state
|
|
29
|
-
const state = new
|
|
28
|
+
// Allocate shared memory (pass state to a worker thread)
|
|
29
|
+
const state = new SharedStateBuffer(1024 * 1024) // 1 MB ring buffer
|
|
30
30
|
|
|
31
31
|
// --- Writer side (e.g. main thread) ---
|
|
32
32
|
const w = new Writer(state)
|
|
@@ -87,12 +87,12 @@ if (!ok) {
|
|
|
87
87
|
|
|
88
88
|
```js
|
|
89
89
|
// main.js
|
|
90
|
-
import {
|
|
90
|
+
import { SharedStateBuffer, Writer } from '@nxtedition/shared'
|
|
91
91
|
import { Worker } from 'node:worker_threads'
|
|
92
92
|
|
|
93
|
-
const state = new
|
|
93
|
+
const state = new SharedStateBuffer(1024 * 1024)
|
|
94
94
|
const worker = new Worker('./reader-worker.js', {
|
|
95
|
-
workerData: state
|
|
95
|
+
workerData: state,
|
|
96
96
|
})
|
|
97
97
|
|
|
98
98
|
const w = new Writer(state)
|
|
@@ -117,20 +117,17 @@ poll()
|
|
|
117
117
|
|
|
118
118
|
## API
|
|
119
119
|
|
|
120
|
-
### `new
|
|
120
|
+
### `new SharedStateBuffer(size: number)`
|
|
121
121
|
|
|
122
|
-
|
|
122
|
+
Extends `SharedArrayBuffer`. Allocates a shared memory buffer for the ring buffer. The first 128 bytes are reserved for state (read/write pointers); the rest is the data region. Overhead (length header + alignment + next-header slot) is added automatically so that a single write of `size` bytes is always guaranteed to fit.
|
|
123
123
|
|
|
124
|
-
- **size** —
|
|
125
|
-
- **buffer** — An existing `SharedArrayBuffer` to wrap
|
|
124
|
+
- **size** — Maximum payload size in bytes for a single write (must be a positive integer, max ~2 GB)
|
|
126
125
|
|
|
127
|
-
|
|
126
|
+
Since `SharedStateBuffer` extends `SharedArrayBuffer`, pass it directly to a worker thread.
|
|
128
127
|
|
|
129
|
-
|
|
128
|
+
### `new Reader(sharedBuffer: SharedArrayBuffer)`
|
|
130
129
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
Creates a reader for the ring buffer. Accepts a `State` instance or a `SharedArrayBuffer` directly (shorthand for `new Reader(new State(buf))`).
|
|
130
|
+
Creates a reader for the ring buffer.
|
|
134
131
|
|
|
135
132
|
#### `reader.readSome(next, opaque?)`
|
|
136
133
|
|
|
@@ -147,9 +144,9 @@ Return `false` from the callback to stop reading early. Returns the number of me
|
|
|
147
144
|
|
|
148
145
|
Messages are batched: up to 1024 items or 256 KiB per call.
|
|
149
146
|
|
|
150
|
-
### `new Writer(
|
|
147
|
+
### `new Writer(sharedBuffer: SharedArrayBuffer, options?)`
|
|
151
148
|
|
|
152
|
-
Creates a writer for the ring buffer.
|
|
149
|
+
Creates a writer for the ring buffer.
|
|
153
150
|
|
|
154
151
|
**Options:**
|
|
155
152
|
|
package/lib/index.d.ts
CHANGED
|
@@ -11,21 +11,21 @@ export interface WriterOptions {
|
|
|
11
11
|
};
|
|
12
12
|
}
|
|
13
13
|
/**
|
|
14
|
-
* Shared ring buffer state.
|
|
15
|
-
*
|
|
16
|
-
*
|
|
14
|
+
* Shared ring buffer state. Extends SharedArrayBuffer where the first 128
|
|
15
|
+
* bytes are reserved for read/write pointers and the rest is the data region.
|
|
16
|
+
*
|
|
17
|
+
* The `size` parameter is the guaranteed max payload for a single write.
|
|
18
|
+
* Overhead (length header + alignment + next-header slot) is added automatically.
|
|
17
19
|
*/
|
|
18
|
-
export declare class
|
|
19
|
-
readonly buffer: SharedArrayBuffer;
|
|
20
|
+
export declare class SharedStateBuffer extends SharedArrayBuffer {
|
|
20
21
|
constructor(size: number);
|
|
21
|
-
constructor(buffer: SharedArrayBuffer);
|
|
22
22
|
}
|
|
23
23
|
/**
|
|
24
24
|
* Reader for the ring buffer.
|
|
25
25
|
*/
|
|
26
26
|
export declare class Reader {
|
|
27
27
|
#private;
|
|
28
|
-
constructor(
|
|
28
|
+
constructor(sharedBuffer: SharedArrayBuffer);
|
|
29
29
|
readSome<U>(next: (data: BufferRegion, opaque?: U) => void | boolean, opaque?: U): number;
|
|
30
30
|
}
|
|
31
31
|
/**
|
|
@@ -33,22 +33,25 @@ export declare class Reader {
|
|
|
33
33
|
*/
|
|
34
34
|
export declare class Writer {
|
|
35
35
|
#private;
|
|
36
|
-
constructor(
|
|
36
|
+
constructor(sharedBuffer: SharedArrayBuffer, { yield: onYield, logger }?: WriterOptions);
|
|
37
37
|
/**
|
|
38
38
|
* Synchronously writes a message. Blocks (via `Atomics.wait`) until buffer space is available.
|
|
39
39
|
* Writing more than "len" bytes in the callback will cause undefined behavior.
|
|
40
40
|
*/
|
|
41
|
-
writeSync
|
|
41
|
+
writeSync(len: number, fn: (data: BufferRegion) => number): void;
|
|
42
|
+
writeSync<U>(len: number, fn: (data: BufferRegion, opaque: U) => number, opaque: U): void;
|
|
42
43
|
/**
|
|
43
44
|
* Non-blocking write attempt. Returns `false` if the buffer is full.
|
|
44
45
|
* Writing more than "len" bytes in the callback will cause undefined behavior.
|
|
45
46
|
*/
|
|
46
|
-
tryWrite
|
|
47
|
+
tryWrite(len: number, fn: (data: BufferRegion) => number): boolean;
|
|
48
|
+
tryWrite<U>(len: number, fn: (data: BufferRegion, opaque: U) => number, opaque: U): boolean;
|
|
47
49
|
/**
|
|
48
50
|
* Batches multiple writes within the callback. The write pointer is only
|
|
49
51
|
* published to the reader when cork returns, reducing atomic operation overhead.
|
|
50
52
|
*/
|
|
51
|
-
cork
|
|
53
|
+
cork(): void;
|
|
54
|
+
cork<T>(callback: () => T): T;
|
|
52
55
|
/**
|
|
53
56
|
* Publishes the pending write position to the reader.
|
|
54
57
|
*/
|
package/lib/index.js
CHANGED
|
@@ -29,38 +29,25 @@ const HWM_COUNT = 1024 // 1024 items
|
|
|
29
29
|
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
|
-
* Shared ring buffer state.
|
|
33
|
-
*
|
|
34
|
-
*
|
|
32
|
+
* Shared ring buffer state. Extends SharedArrayBuffer where the first 128
|
|
33
|
+
* bytes are reserved for read/write pointers and the rest is the data region.
|
|
34
|
+
*
|
|
35
|
+
* The `size` parameter is the guaranteed max payload for a single write.
|
|
36
|
+
* Overhead (length header + alignment + next-header slot) is added automatically.
|
|
35
37
|
*/
|
|
36
|
-
export class
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (sizeOrBuffer.byteLength >= 2 ** 31) {
|
|
47
|
-
throw new RangeError('Shared buffer size exceeds maximum of 2GB')
|
|
48
|
-
}
|
|
49
|
-
this.buffer = sizeOrBuffer
|
|
50
|
-
} else {
|
|
51
|
-
const size = sizeOrBuffer
|
|
52
|
-
if (!Number.isInteger(size)) {
|
|
53
|
-
throw new TypeError('size must be a positive integer')
|
|
54
|
-
}
|
|
55
|
-
if (size <= 0) {
|
|
56
|
-
throw new RangeError('size must be a positive integer')
|
|
57
|
-
}
|
|
58
|
-
if (size >= 2 ** 31 - 11) {
|
|
59
|
-
throw new RangeError('size exceeds maximum of 2GB minus header size')
|
|
60
|
-
}
|
|
61
|
-
// 128 bytes for state + data region (rounded up to 4-byte boundary for Int32Array).
|
|
62
|
-
this.buffer = new SharedArrayBuffer(STATE_BYTES + ((size + 8 + 3) & ~3))
|
|
38
|
+
export class SharedStateBuffer extends SharedArrayBuffer {
|
|
39
|
+
constructor(size ) {
|
|
40
|
+
if (!Number.isInteger(size)) {
|
|
41
|
+
throw new TypeError('size must be a positive integer')
|
|
42
|
+
}
|
|
43
|
+
if (size <= 0) {
|
|
44
|
+
throw new RangeError('size must be a positive integer')
|
|
45
|
+
}
|
|
46
|
+
if (size >= 2 ** 31) {
|
|
47
|
+
throw new RangeError('size exceeds maximum of 2GB')
|
|
63
48
|
}
|
|
49
|
+
// Data region: aligned payload + 4-byte length header + 4-byte next-header slot.
|
|
50
|
+
super(STATE_BYTES + ((size + 3) & ~3) + 8)
|
|
64
51
|
}
|
|
65
52
|
}
|
|
66
53
|
|
|
@@ -74,8 +61,7 @@ export class Reader {
|
|
|
74
61
|
#data
|
|
75
62
|
#readPos
|
|
76
63
|
|
|
77
|
-
constructor(
|
|
78
|
-
const sharedBuffer = state instanceof SharedArrayBuffer ? state : state.buffer
|
|
64
|
+
constructor(sharedBuffer ) {
|
|
79
65
|
const size = sharedBuffer.byteLength - STATE_BYTES
|
|
80
66
|
|
|
81
67
|
this.#state = new Int32Array(sharedBuffer, 0, STATE_BYTES >> 2)
|
|
@@ -183,9 +169,7 @@ export class Writer {
|
|
|
183
169
|
#logger
|
|
184
170
|
#uncorkBound
|
|
185
171
|
|
|
186
|
-
constructor(
|
|
187
|
-
const sharedBuffer = state instanceof SharedArrayBuffer ? state : state.buffer
|
|
188
|
-
|
|
172
|
+
constructor(sharedBuffer , { yield: onYield, logger } = {}) {
|
|
189
173
|
if (onYield != null && typeof onYield !== 'function') {
|
|
190
174
|
throw new TypeError('onYield must be a function')
|
|
191
175
|
}
|
|
@@ -384,7 +368,9 @@ export class Writer {
|
|
|
384
368
|
* Synchronously writes a message. Blocks (via `Atomics.wait`) until buffer space is available.
|
|
385
369
|
* Writing more than "len" bytes in the callback will cause undefined behavior.
|
|
386
370
|
*/
|
|
387
|
-
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
writeSync (len , fn , opaque ) {
|
|
388
374
|
if (typeof len !== 'number') {
|
|
389
375
|
throw new TypeError('"len" must be a non-negative number')
|
|
390
376
|
}
|
|
@@ -438,7 +424,9 @@ export class Writer {
|
|
|
438
424
|
* Non-blocking write attempt. Returns `false` if the buffer is full.
|
|
439
425
|
* Writing more than "len" bytes in the callback will cause undefined behavior.
|
|
440
426
|
*/
|
|
441
|
-
|
|
427
|
+
|
|
428
|
+
|
|
429
|
+
tryWrite (len , fn , opaque ) {
|
|
442
430
|
if (typeof len !== 'number') {
|
|
443
431
|
throw new TypeError('"len" must be a non-negative number')
|
|
444
432
|
}
|
|
@@ -472,6 +460,8 @@ export class Writer {
|
|
|
472
460
|
* Batches multiple writes within the callback. The write pointer is only
|
|
473
461
|
* published to the reader when cork returns, reducing atomic operation overhead.
|
|
474
462
|
*/
|
|
463
|
+
|
|
464
|
+
|
|
475
465
|
cork (callback ) {
|
|
476
466
|
this.#corked += 1
|
|
477
467
|
if (callback != null) {
|
|
@@ -486,7 +476,7 @@ export class Writer {
|
|
|
486
476
|
/**
|
|
487
477
|
* Publishes the pending write position to the reader.
|
|
488
478
|
*/
|
|
489
|
-
uncork()
|
|
479
|
+
uncork() {
|
|
490
480
|
if (this.#corked === 0) {
|
|
491
481
|
return
|
|
492
482
|
}
|
|
@@ -497,7 +487,7 @@ export class Writer {
|
|
|
497
487
|
}
|
|
498
488
|
}
|
|
499
489
|
|
|
500
|
-
flushSync()
|
|
490
|
+
flushSync() {
|
|
501
491
|
if (this.#pending > 0) {
|
|
502
492
|
Atomics.store(this.#state, WRITE_INDEX, this.#writePos)
|
|
503
493
|
this.#pending = 0
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nxtedition/shared",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"types": "lib/index.d.ts",
|
|
@@ -26,5 +26,6 @@
|
|
|
26
26
|
"oxlint-tsgolint": "^0.13.0",
|
|
27
27
|
"rimraf": "^6.1.3",
|
|
28
28
|
"typescript": "^5.9.3"
|
|
29
|
-
}
|
|
29
|
+
},
|
|
30
|
+
"gitHead": "239c3eeff1f228d4d71ff6f8c884642f359065b5"
|
|
30
31
|
}
|