sabcom 0.1.140 → 0.1.142

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/build/index.cjs CHANGED
@@ -9,213 +9,546 @@ function _export(target, all) {
9
9
  });
10
10
  }
11
11
  _export(exports, {
12
- get HEADER_SIZE () {
13
- return HEADER_SIZE;
12
+ get createBuffer () {
13
+ return createBuffer;
14
14
  },
15
- get HEADER_VALUES () {
16
- return HEADER_VALUES;
15
+ get open () {
16
+ return open;
17
17
  },
18
- get Handshake () {
19
- return Handshake;
20
- },
21
- get Header () {
22
- return Header;
23
- },
24
- get SEMAPHORE () {
25
- return SEMAPHORE;
26
- },
27
- get Semaphore () {
28
- return Semaphore;
29
- },
30
- get read () {
31
- return read;
32
- },
33
- get readGenerator () {
34
- return readGenerator;
35
- },
36
- get readSync () {
37
- return readSync;
38
- },
39
- get write () {
40
- return write;
41
- },
42
- get writeGenerator () {
43
- return writeGenerator;
44
- },
45
- get writeSync () {
46
- return writeSync;
18
+ get reset () {
19
+ return reset;
47
20
  }
48
21
  });
49
- const SEMAPHORE = 0;
50
- var Semaphore = /*#__PURE__*/ function(Semaphore) {
51
- Semaphore[Semaphore["READY"] = 0] = "READY";
52
- Semaphore[Semaphore["HANDSHAKE"] = 1] = "HANDSHAKE";
53
- Semaphore[Semaphore["PAYLOAD"] = 2] = "PAYLOAD";
54
- return Semaphore;
55
- }({});
56
- var Handshake = /*#__PURE__*/ function(Handshake) {
57
- Handshake[Handshake["TOTAL_SIZE"] = 1] = "TOTAL_SIZE";
58
- Handshake[Handshake["TOTAL_CHUNKS"] = 2] = "TOTAL_CHUNKS";
59
- return Handshake;
60
- }({});
61
- var Header = /*#__PURE__*/ function(Header) {
62
- Header[Header["CHUNK_INDEX"] = 1] = "CHUNK_INDEX";
63
- Header[Header["CHUNK_OFFSET"] = 2] = "CHUNK_OFFSET";
64
- Header[Header["CHUNK_SIZE"] = 3] = "CHUNK_SIZE";
65
- return Header;
66
- }({});
67
- const HEADER_VALUES = 4;
68
- const HEADER_SIZE = Uint32Array.BYTES_PER_ELEMENT * HEADER_VALUES;
69
- function* writeGenerator(data, buffer, { timeout = 5000 } = {}) {
70
- if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {
71
- throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');
72
- }
73
- const chunkSize = buffer.byteLength - HEADER_SIZE;
74
- if (chunkSize <= 0) {
75
- throw new Error('SharedArrayBuffer too small for header');
76
- }
77
- const totalSize = data.length;
78
- const totalChunks = Math.ceil(totalSize / chunkSize);
79
- const header = new Int32Array(buffer);
80
- header[1] = totalSize;
81
- header[2] = totalChunks;
82
- Atomics.store(header, SEMAPHORE, 1);
83
- Atomics.notify(header, SEMAPHORE);
84
- try {
85
- const handshakeResult = yield {
86
- target: header,
87
- index: SEMAPHORE,
88
- value: 1,
89
- timeout
90
- };
91
- if (handshakeResult === 'timed-out') {
92
- throw new Error('Reader handshake timeout');
93
- }
94
- const payload = new Uint8Array(buffer, HEADER_SIZE);
95
- for(let i = 0; i < totalChunks; i++){
96
- const start = i * chunkSize;
97
- const end = Math.min(start + chunkSize, totalSize);
98
- const size = end - start;
99
- payload.set(data.subarray(start, end), 0);
100
- header[1] = i;
101
- header[2] = start;
102
- header[3] = size;
103
- Atomics.store(header, SEMAPHORE, 2);
104
- Atomics.notify(header, SEMAPHORE);
105
- const chunkResult = yield {
106
- target: header,
107
- index: SEMAPHORE,
108
- value: 2,
109
- timeout
22
+ const MAGIC = 0x53424332;
23
+ const INIT_SENTINEL = -1;
24
+ const MIN_BUFFER_BYTES = 4096;
25
+ const VERSION = 2;
26
+ const HEADER_WORDS = 5;
27
+ const RING_CONTROL_WORDS = 4;
28
+ const DESCRIPTOR_WORDS = 3;
29
+ const CONTINUATION = -1;
30
+ const SLOT_OPTIONS = [
31
+ 256,
32
+ 128,
33
+ 64,
34
+ 32,
35
+ 16,
36
+ 8
37
+ ];
38
+ const MIN_DATA_BYTES = 256;
39
+ const FLAG_CLOSED_A = 1;
40
+ const FLAG_CLOSED_B = 2;
41
+ const IDX_MAGIC = 0;
42
+ const IDX_VERSION = 1;
43
+ const IDX_OWNER_A = 2;
44
+ const IDX_OWNER_B = 3;
45
+ const IDX_FLAGS = 4;
46
+ const IDX_WRITE_MSG = 0;
47
+ const IDX_READ_MSG = 1;
48
+ const IDX_READ_BYTE = 3;
49
+ const IDX_DESC_OFFSET = 0;
50
+ const IDX_DESC_SIZE = 1;
51
+ const IDX_DESC_TOTAL = 2;
52
+ const layoutCache = new Map();
53
+ const stateCache = new WeakMap();
54
+ const handleCache = new WeakMap();
55
+ const resolvedTail = Promise.resolve();
56
+ const roundDownToWord = (value)=>value & ~3;
57
+ const computeLayout = (byteLength)=>{
58
+ const cached = layoutCache.get(byteLength);
59
+ if (cached !== undefined) return cached;
60
+ if (byteLength % Int32Array.BYTES_PER_ELEMENT !== 0 || byteLength < MIN_BUFFER_BYTES) {
61
+ layoutCache.set(byteLength, null);
62
+ return null;
63
+ }
64
+ const headerBytes = HEADER_WORDS * Int32Array.BYTES_PER_ELEMENT;
65
+ const perDirectionBytes = roundDownToWord(byteLength - headerBytes >> 1);
66
+ const controlBytes = RING_CONTROL_WORDS * Int32Array.BYTES_PER_ELEMENT;
67
+ for (const ringSlots of SLOT_OPTIONS){
68
+ const descriptorBytes = ringSlots * DESCRIPTOR_WORDS * Int32Array.BYTES_PER_ELEMENT;
69
+ const dataBytes = perDirectionBytes - controlBytes - descriptorBytes;
70
+ if (dataBytes >= MIN_DATA_BYTES) {
71
+ const layout = {
72
+ controlBytes,
73
+ descriptorBytes,
74
+ dataBytes,
75
+ perDirectionBytes,
76
+ ringSlots
110
77
  };
111
- if (chunkResult === 'timed-out') {
112
- throw new Error(`Reader timeout on chunk ${i}/${totalChunks - 1}`);
113
- }
78
+ layoutCache.set(byteLength, layout);
79
+ return layout;
114
80
  }
115
- } finally{
116
- Atomics.store(header, SEMAPHORE, 0);
117
- Atomics.notify(header, SEMAPHORE);
118
81
  }
119
- }
120
- function* readGenerator(buffer, { timeout = 5000 } = {}) {
121
- if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) {
122
- throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');
123
- }
124
- const chunkSize = buffer.byteLength - HEADER_SIZE;
125
- if (chunkSize <= 0) {
126
- throw new Error('SharedArrayBuffer too small for header');
127
- }
128
- const header = new Int32Array(buffer);
129
- const handshakeResult = yield {
130
- target: header,
131
- index: SEMAPHORE,
132
- value: 0,
133
- timeout
82
+ layoutCache.set(byteLength, null);
83
+ return null;
84
+ };
85
+ const createRingViews = (buffer, byteOffset, layout)=>{
86
+ const descriptorOffset = byteOffset + layout.controlBytes;
87
+ const dataOffset = descriptorOffset + layout.descriptorBytes;
88
+ return {
89
+ control: new Int32Array(buffer, byteOffset, RING_CONTROL_WORDS),
90
+ descriptors: new Int32Array(buffer, descriptorOffset, layout.ringSlots * DESCRIPTOR_WORDS),
91
+ bytes: new Uint8Array(buffer, dataOffset, layout.dataBytes),
92
+ dataBytes: layout.dataBytes,
93
+ ringSlots: layout.ringSlots,
94
+ ringMask: layout.ringSlots - 1
134
95
  };
135
- if (handshakeResult === 'timed-out') {
136
- throw new Error('Handshake timeout');
137
- }
138
- if (header[SEMAPHORE] !== 1) {
139
- throw new Error('Invalid handshake state');
140
- }
141
- const totalSize = header[1];
142
- const totalChunks = header[2];
143
- if (totalSize < 0 || totalChunks < 0) {
144
- throw new Error('Invalid handshake values');
145
- }
146
- if (totalSize === 0 && totalChunks !== 0) {
147
- throw new Error('Invalid handshake values');
148
- }
149
- if (totalSize > totalChunks * chunkSize) {
150
- throw new Error('Invalid handshake values');
151
- }
152
- const data = new Uint8Array(totalSize);
153
- Atomics.store(header, SEMAPHORE, 0);
154
- Atomics.notify(header, SEMAPHORE);
155
- const payload = new Uint8Array(buffer, HEADER_SIZE);
156
- for(let i = 0; i < totalChunks; i++){
157
- const chunkResult = yield {
158
- target: header,
159
- index: SEMAPHORE,
160
- value: 0,
161
- timeout
162
- };
163
- if (chunkResult === 'timed-out') {
164
- throw new Error(`Writer timeout waiting for chunk ${i}`);
96
+ };
97
+ const createEndpointToken = ()=>{
98
+ if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {
99
+ const buf = new Int32Array(1);
100
+ do {
101
+ crypto.getRandomValues(buf);
102
+ }while (buf[0] === 0 || buf[0] === INIT_SENTINEL || buf[0] === MAGIC)
103
+ return buf[0];
104
+ }
105
+ let token = 0;
106
+ while(token === 0 || token === INIT_SENTINEL || token === MAGIC){
107
+ token = Math.random() * 0x7fffffff | 0;
108
+ }
109
+ return token;
110
+ };
111
+ const ENDPOINT_TOKEN = createEndpointToken();
112
+ const finalizeHeader = (buffer, header)=>{
113
+ const bodyOffset = HEADER_WORDS * Int32Array.BYTES_PER_ELEMENT;
114
+ if (bodyOffset < buffer.byteLength) {
115
+ new Int32Array(buffer, bodyOffset, (buffer.byteLength - bodyOffset) / Int32Array.BYTES_PER_ELEMENT).fill(0);
116
+ }
117
+ Atomics.store(header, IDX_VERSION, VERSION);
118
+ Atomics.store(header, IDX_OWNER_A, 0);
119
+ Atomics.store(header, IDX_OWNER_B, 0);
120
+ Atomics.store(header, IDX_FLAGS, 0);
121
+ Atomics.store(header, IDX_MAGIC, MAGIC);
122
+ Atomics.notify(header, IDX_MAGIC);
123
+ };
124
+ const initializeHeaderSync = (buffer, header)=>{
125
+ for(;;){
126
+ const magic = Atomics.load(header, IDX_MAGIC);
127
+ if (magic === MAGIC) {
128
+ const version = Atomics.load(header, IDX_VERSION);
129
+ if (version !== VERSION) throw new Error(`Unsupported channel version ${version}`);
130
+ return;
165
131
  }
166
- if (header[SEMAPHORE] !== 2) {
167
- throw new Error(`Expected payload header, received ${Semaphore[header[SEMAPHORE]]}`);
132
+ if (magic === INIT_SENTINEL) {
133
+ Atomics.wait(header, IDX_MAGIC, INIT_SENTINEL);
134
+ continue;
168
135
  }
169
- const chunkIndex = header[1];
170
- if (i !== chunkIndex) {
171
- throw new Error(`Reader integrity failure for chunk ${chunkIndex} expected ${i}`);
136
+ if (magic !== 0) throw new Error('SharedArrayBuffer does not contain a supported channel');
137
+ if (Atomics.compareExchange(header, IDX_MAGIC, 0, INIT_SENTINEL) !== 0) continue;
138
+ finalizeHeader(buffer, header);
139
+ return;
140
+ }
141
+ };
142
+ const resetClosedHeaderSync = (buffer, header)=>{
143
+ for(;;){
144
+ const magic = Atomics.load(header, IDX_MAGIC);
145
+ if (magic === INIT_SENTINEL) {
146
+ Atomics.wait(header, IDX_MAGIC, INIT_SENTINEL);
147
+ continue;
148
+ }
149
+ if (magic !== MAGIC) return;
150
+ const version = Atomics.load(header, IDX_VERSION);
151
+ if (version !== VERSION) throw new Error(`Unsupported channel version ${version}`);
152
+ if (Atomics.load(header, IDX_OWNER_A) !== 0 || Atomics.load(header, IDX_OWNER_B) !== 0 || Atomics.load(header, IDX_FLAGS) === 0) return;
153
+ if (Atomics.compareExchange(header, IDX_MAGIC, MAGIC, INIT_SENTINEL) !== MAGIC) continue;
154
+ finalizeHeader(buffer, header);
155
+ return;
156
+ }
157
+ };
158
+ const resetHeaderSync = (buffer, header)=>{
159
+ initializeHeaderSync(buffer, header);
160
+ for(;;){
161
+ if (Atomics.load(header, IDX_OWNER_A) !== 0 || Atomics.load(header, IDX_OWNER_B) !== 0) {
162
+ throw new Error('Channel is still open');
172
163
  }
173
- const offset = header[2];
174
- const size = header[3];
175
- if (offset < 0 || size <= 0 || size > chunkSize || offset + size > totalSize) {
176
- throw new Error(`Invalid chunk metadata for chunk ${chunkIndex}`);
164
+ if (Atomics.compareExchange(header, IDX_MAGIC, MAGIC, INIT_SENTINEL) !== MAGIC) {
165
+ initializeHeaderSync(buffer, header);
166
+ continue;
177
167
  }
178
- data.set(payload.subarray(0, size), offset);
179
- Atomics.store(header, SEMAPHORE, 0);
180
- Atomics.notify(header, SEMAPHORE);
168
+ finalizeHeader(buffer, header);
169
+ return;
170
+ }
171
+ };
172
+ const claimEndpoint = (header)=>{
173
+ const ownerA = Atomics.load(header, IDX_OWNER_A);
174
+ if (ownerA === ENDPOINT_TOKEN) {
175
+ Atomics.and(header, IDX_FLAGS, ~FLAG_CLOSED_A);
176
+ return 0;
177
+ }
178
+ if (ownerA === 0 && Atomics.compareExchange(header, IDX_OWNER_A, 0, ENDPOINT_TOKEN) === 0) {
179
+ Atomics.and(header, IDX_FLAGS, ~FLAG_CLOSED_A);
180
+ return 0;
181
+ }
182
+ const ownerB = Atomics.load(header, IDX_OWNER_B);
183
+ if (ownerB === ENDPOINT_TOKEN) {
184
+ Atomics.and(header, IDX_FLAGS, ~FLAG_CLOSED_B);
185
+ return 1;
186
+ }
187
+ if (ownerB === 0 && Atomics.compareExchange(header, IDX_OWNER_B, 0, ENDPOINT_TOKEN) === 0) {
188
+ Atomics.and(header, IDX_FLAGS, ~FLAG_CLOSED_B);
189
+ return 1;
190
+ }
191
+ throw new Error('SharedArrayBuffer channel already claimed by two endpoints');
192
+ };
193
+ const createState = (buffer, layout)=>{
194
+ const cached = stateCache.get(buffer);
195
+ if (cached !== undefined) return cached;
196
+ const byteOffset = HEADER_WORDS * Int32Array.BYTES_PER_ELEMENT;
197
+ const directionA = createRingViews(buffer, byteOffset, layout);
198
+ const directionB = createRingViews(buffer, byteOffset + layout.perDirectionBytes, layout);
199
+ const header = new Int32Array(buffer, 0, HEADER_WORDS);
200
+ const endpoint = claimEndpoint(header);
201
+ const outbound = endpoint === 0 ? directionA : directionB;
202
+ const inbound = endpoint === 0 ? directionB : directionA;
203
+ const state = {
204
+ header,
205
+ inbound,
206
+ outbound,
207
+ ownerIndex: endpoint === 0 ? IDX_OWNER_A : IDX_OWNER_B,
208
+ localClosedMask: endpoint === 0 ? FLAG_CLOSED_A : FLAG_CLOSED_B,
209
+ peerClosedMask: endpoint === 0 ? FLAG_CLOSED_B : FLAG_CLOSED_A,
210
+ localClosed: false,
211
+ writeLocked: false,
212
+ readLocked: false,
213
+ readTail: resolvedTail,
214
+ writeTail: resolvedTail,
215
+ wMsg: Atomics.load(outbound.control, IDX_WRITE_MSG),
216
+ wByte: 0,
217
+ rMsg: Atomics.load(inbound.control, IDX_READ_MSG),
218
+ rByte: 0,
219
+ pendingAckMsg: 0,
220
+ pendingAckByte: 0,
221
+ pendingAckSize: 0
222
+ };
223
+ stateCache.set(buffer, state);
224
+ return state;
225
+ };
226
+ const getStateSync = (buffer)=>{
227
+ const cached = stateCache.get(buffer);
228
+ if (cached !== undefined) return cached;
229
+ const layout = computeLayout(buffer.byteLength);
230
+ if (layout === null) throw new Error(`SharedArrayBuffer too small for channel (minimum ${MIN_BUFFER_BYTES} bytes)`);
231
+ const header = new Int32Array(buffer, 0, HEADER_WORDS);
232
+ initializeHeaderSync(buffer, header);
233
+ resetClosedHeaderSync(buffer, header);
234
+ return createState(buffer, layout);
235
+ };
236
+ const copyToRing = (ring, ringOffset, source, sourceOffset, size)=>{
237
+ const first = Math.min(size, ring.byteLength - ringOffset);
238
+ ring.set(source.subarray(sourceOffset, sourceOffset + first), ringOffset);
239
+ if (first < size) ring.set(source.subarray(sourceOffset + first, sourceOffset + size), 0);
240
+ };
241
+ const copyFromRing = (target, targetOffset, ring, ringOffset, size)=>{
242
+ const first = Math.min(size, ring.byteLength - ringOffset);
243
+ target.set(ring.subarray(ringOffset, ringOffset + first), targetOffset);
244
+ if (first < size) target.set(ring.subarray(0, size - first), targetOffset + first);
245
+ };
246
+ const freeWriteSpace = (ring, writeMsg, writeByte)=>{
247
+ const readMsg = Atomics.load(ring.control, IDX_READ_MSG);
248
+ const pending = writeMsg - readMsg | 0;
249
+ if (pending >= ring.ringSlots) return 0;
250
+ if (pending === 0) return ring.dataBytes;
251
+ const readByte = Atomics.load(ring.control, IDX_READ_BYTE);
252
+ const free = ring.dataBytes - (writeByte - readByte | 0);
253
+ return free > 0 ? free : 0;
254
+ };
255
+ const publishSegment = (ring, offset, size, total, writeMsg)=>{
256
+ const di = (writeMsg & ring.ringMask) * DESCRIPTOR_WORDS;
257
+ ring.descriptors[di + IDX_DESC_OFFSET] = offset;
258
+ ring.descriptors[di + IDX_DESC_SIZE] = size;
259
+ ring.descriptors[di + IDX_DESC_TOTAL] = total;
260
+ Atomics.store(ring.control, IDX_WRITE_MSG, writeMsg + 1 | 0);
261
+ Atomics.notify(ring.control, IDX_WRITE_MSG, 1);
262
+ };
263
+ const ackSegment = (ring, readMsg, readByte, size)=>{
264
+ Atomics.store(ring.control, IDX_READ_BYTE, readByte + size | 0);
265
+ Atomics.store(ring.control, IDX_READ_MSG, readMsg + 1 | 0);
266
+ Atomics.notify(ring.control, IDX_READ_MSG, 1);
267
+ };
268
+ const flushPendingAck = (state)=>{
269
+ if (state.pendingAckSize > 0) {
270
+ ackSegment(state.inbound, state.pendingAckMsg, state.pendingAckByte, state.pendingAckSize);
271
+ state.pendingAckSize = 0;
272
+ }
273
+ };
274
+ const isPeerClosed = (state)=>(Atomics.load(state.header, IDX_FLAGS) & state.peerClosedMask) !== 0;
275
+ const assertOpen = (state)=>{
276
+ if (state.localClosed) throw new Error('Channel is closed');
277
+ };
278
+ const assertWritable = (state)=>{
279
+ assertOpen(state);
280
+ if (isPeerClosed(state)) throw new Error('Peer channel is closed');
281
+ };
282
+ const assertReadable = (state, receiving)=>{
283
+ assertOpen(state);
284
+ if (isPeerClosed(state)) throw new Error(receiving ? 'Peer channel closed while receiving message' : 'Peer channel is closed');
285
+ };
286
+ const notifyWaiters = (state)=>{
287
+ Atomics.notify(state.inbound.control, IDX_WRITE_MSG);
288
+ Atomics.notify(state.inbound.control, IDX_READ_MSG);
289
+ Atomics.notify(state.outbound.control, IDX_WRITE_MSG);
290
+ Atomics.notify(state.outbound.control, IDX_READ_MSG);
291
+ };
292
+ const writeChannelSync = (data, state, timeout)=>{
293
+ if (state.writeLocked) throw new Error('Cannot writeSync while an async write is in progress');
294
+ flushPendingAck(state);
295
+ const ring = state.outbound;
296
+ assertWritable(state);
297
+ let writeMsg = state.wMsg;
298
+ let writeByte = state.wByte;
299
+ if (data.length === 0) {
300
+ while(freeWriteSpace(ring, writeMsg, writeByte) === 0){
301
+ assertWritable(state);
302
+ const readMsg = Atomics.load(ring.control, IDX_READ_MSG);
303
+ if (Atomics.wait(ring.control, IDX_READ_MSG, readMsg, timeout) === 'timed-out') throw new Error('Write timeout waiting for channel space');
304
+ }
305
+ publishSegment(ring, (writeByte >>> 0) % ring.dataBytes, 0, 0, writeMsg);
306
+ state.wMsg = writeMsg + 1 | 0;
307
+ return;
308
+ }
309
+ let sourceOffset = 0;
310
+ let firstSegment = true;
311
+ while(sourceOffset < data.length){
312
+ let free = freeWriteSpace(ring, writeMsg, writeByte);
313
+ while(free === 0){
314
+ assertWritable(state);
315
+ const readMsg = Atomics.load(ring.control, IDX_READ_MSG);
316
+ if (Atomics.wait(ring.control, IDX_READ_MSG, readMsg, timeout) === 'timed-out') throw new Error('Write timeout waiting for channel space');
317
+ free = freeWriteSpace(ring, writeMsg, writeByte);
318
+ }
319
+ const size = Math.min(data.length - sourceOffset, free);
320
+ const ringOffset = (writeByte >>> 0) % ring.dataBytes;
321
+ copyToRing(ring.bytes, ringOffset, data, sourceOffset, size);
322
+ publishSegment(ring, ringOffset, size, firstSegment ? data.length : CONTINUATION, writeMsg);
323
+ writeMsg = writeMsg + 1 | 0;
324
+ writeByte = writeByte + size | 0;
325
+ state.wMsg = writeMsg;
326
+ state.wByte = writeByte;
327
+ sourceOffset += size;
328
+ firstSegment = false;
329
+ }
330
+ };
331
+ const writeChannel = async (data, state, timeout)=>{
332
+ flushPendingAck(state);
333
+ const ring = state.outbound;
334
+ assertWritable(state);
335
+ let writeMsg = state.wMsg;
336
+ let writeByte = state.wByte;
337
+ if (data.length === 0) {
338
+ while(freeWriteSpace(ring, writeMsg, writeByte) === 0){
339
+ assertWritable(state);
340
+ const readMsg = Atomics.load(ring.control, IDX_READ_MSG);
341
+ if (await Atomics.waitAsync(ring.control, IDX_READ_MSG, readMsg, timeout).value === 'timed-out') throw new Error('Write timeout waiting for channel space');
342
+ }
343
+ publishSegment(ring, (writeByte >>> 0) % ring.dataBytes, 0, 0, writeMsg);
344
+ state.wMsg = writeMsg + 1 | 0;
345
+ return;
346
+ }
347
+ let sourceOffset = 0;
348
+ let firstSegment = true;
349
+ while(sourceOffset < data.length){
350
+ let free = freeWriteSpace(ring, writeMsg, writeByte);
351
+ while(free === 0){
352
+ assertWritable(state);
353
+ const readMsg = Atomics.load(ring.control, IDX_READ_MSG);
354
+ if (await Atomics.waitAsync(ring.control, IDX_READ_MSG, readMsg, timeout).value === 'timed-out') throw new Error('Write timeout waiting for channel space');
355
+ free = freeWriteSpace(ring, writeMsg, writeByte);
356
+ }
357
+ const size = Math.min(data.length - sourceOffset, free);
358
+ const ringOffset = (writeByte >>> 0) % ring.dataBytes;
359
+ copyToRing(ring.bytes, ringOffset, data, sourceOffset, size);
360
+ publishSegment(ring, ringOffset, size, firstSegment ? data.length : CONTINUATION, writeMsg);
361
+ writeMsg = writeMsg + 1 | 0;
362
+ writeByte = writeByte + size | 0;
363
+ state.wMsg = writeMsg;
364
+ state.wByte = writeByte;
365
+ sourceOffset += size;
366
+ firstSegment = false;
367
+ }
368
+ };
369
+ const readChannelSync = (state, timeout)=>{
370
+ if (state.readLocked) throw new Error('Cannot readSync while an async read is in progress');
371
+ const ring = state.inbound;
372
+ flushPendingAck(state);
373
+ let readMsg = state.rMsg;
374
+ let readByte = state.rByte;
375
+ while(readMsg === Atomics.load(ring.control, IDX_WRITE_MSG)){
376
+ assertReadable(state, false);
377
+ if (Atomics.wait(ring.control, IDX_WRITE_MSG, readMsg, timeout) === 'timed-out') throw new Error('Read timeout');
378
+ }
379
+ const di = (readMsg & ring.ringMask) * DESCRIPTOR_WORDS;
380
+ const offset = ring.descriptors[di + IDX_DESC_OFFSET];
381
+ const size = ring.descriptors[di + IDX_DESC_SIZE];
382
+ const total = ring.descriptors[di + IDX_DESC_TOTAL];
383
+ if (offset < 0 || offset >= ring.dataBytes || size < 0 || size > ring.dataBytes) throw new Error('Invalid descriptor');
384
+ if (total < 0) throw new Error('Invalid descriptor');
385
+ if (size === total && offset + size <= ring.dataBytes) {
386
+ state.pendingAckMsg = readMsg;
387
+ state.pendingAckByte = readByte;
388
+ state.pendingAckSize = size;
389
+ state.rMsg = readMsg + 1 | 0;
390
+ state.rByte = readByte + size | 0;
391
+ if (size === 0) return new Uint8Array(0);
392
+ return new Uint8Array(ring.bytes.buffer, ring.bytes.byteOffset + offset, size);
393
+ }
394
+ const data = new Uint8Array(total);
395
+ const targetSize = total;
396
+ let targetOffset = 0;
397
+ if (targetOffset + size > targetSize) throw new Error('Invalid descriptor');
398
+ if (size > 0) copyFromRing(data, targetOffset, ring.bytes, offset, size);
399
+ targetOffset += size;
400
+ ackSegment(ring, readMsg, readByte, size);
401
+ readMsg = readMsg + 1 | 0;
402
+ readByte = readByte + size | 0;
403
+ state.rMsg = readMsg;
404
+ state.rByte = readByte;
405
+ while(targetOffset < targetSize){
406
+ while(readMsg === Atomics.load(ring.control, IDX_WRITE_MSG)){
407
+ assertReadable(state, true);
408
+ if (Atomics.wait(ring.control, IDX_WRITE_MSG, readMsg, timeout) === 'timed-out') throw new Error('Read timeout waiting for segment');
409
+ }
410
+ const di2 = (readMsg & ring.ringMask) * DESCRIPTOR_WORDS;
411
+ const off2 = ring.descriptors[di2 + IDX_DESC_OFFSET];
412
+ const sz2 = ring.descriptors[di2 + IDX_DESC_SIZE];
413
+ const tot2 = ring.descriptors[di2 + IDX_DESC_TOTAL];
414
+ if (off2 < 0 || off2 >= ring.dataBytes || sz2 < 0 || sz2 > ring.dataBytes) throw new Error('Invalid descriptor');
415
+ if (tot2 !== CONTINUATION) throw new Error('Invalid descriptor');
416
+ if (targetOffset + sz2 > targetSize) throw new Error('Invalid descriptor');
417
+ if (sz2 > 0) copyFromRing(data, targetOffset, ring.bytes, off2, sz2);
418
+ targetOffset += sz2;
419
+ ackSegment(ring, readMsg, readByte, sz2);
420
+ readMsg = readMsg + 1 | 0;
421
+ readByte = readByte + sz2 | 0;
422
+ state.rMsg = readMsg;
423
+ state.rByte = readByte;
181
424
  }
182
425
  return data;
183
- }
184
- const writeSync = (data, buffer, options)=>{
185
- const gen = writeGenerator(data, buffer, options);
186
- let result = gen.next();
187
- while(!result.done){
188
- const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);
189
- result = gen.next(waitResult);
190
- }
191
- };
192
- const write = async (data, buffer, options)=>{
193
- const gen = writeGenerator(data, buffer, options);
194
- let result = gen.next();
195
- while(!result.done){
196
- const request = result.value;
197
- const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;
198
- result = gen.next(waitResult);
199
- }
200
- };
201
- const readSync = (buffer, options)=>{
202
- const gen = readGenerator(buffer, options);
203
- let result = gen.next();
204
- while(!result.done){
205
- const waitResult = Atomics.wait(result.value.target, result.value.index, result.value.value, result.value.timeout);
206
- result = gen.next(waitResult);
207
- }
208
- return result.value;
209
- };
210
- const read = async (buffer, options)=>{
211
- const gen = readGenerator(buffer, options);
212
- let result = gen.next();
213
- while(!result.done){
214
- const request = result.value;
215
- const waitResult = await Atomics.waitAsync(request.target, request.index, request.value, request.timeout).value;
216
- result = gen.next(waitResult);
217
- }
218
- return result.value;
426
+ };
427
+ const readChannel = async (state, timeout)=>{
428
+ const ring = state.inbound;
429
+ flushPendingAck(state);
430
+ let readMsg = state.rMsg;
431
+ let readByte = state.rByte;
432
+ while(readMsg === Atomics.load(ring.control, IDX_WRITE_MSG)){
433
+ assertReadable(state, false);
434
+ if (await Atomics.waitAsync(ring.control, IDX_WRITE_MSG, readMsg, timeout).value === 'timed-out') throw new Error('Read timeout');
435
+ }
436
+ const di = (readMsg & ring.ringMask) * DESCRIPTOR_WORDS;
437
+ const offset = ring.descriptors[di + IDX_DESC_OFFSET];
438
+ const size = ring.descriptors[di + IDX_DESC_SIZE];
439
+ const total = ring.descriptors[di + IDX_DESC_TOTAL];
440
+ if (offset < 0 || offset >= ring.dataBytes || size < 0 || size > ring.dataBytes) throw new Error('Invalid descriptor');
441
+ if (total < 0) throw new Error('Invalid descriptor');
442
+ if (size === total && offset + size <= ring.dataBytes) {
443
+ state.pendingAckMsg = readMsg;
444
+ state.pendingAckByte = readByte;
445
+ state.pendingAckSize = size;
446
+ state.rMsg = readMsg + 1 | 0;
447
+ state.rByte = readByte + size | 0;
448
+ if (size === 0) return new Uint8Array(0);
449
+ return new Uint8Array(ring.bytes.buffer, ring.bytes.byteOffset + offset, size);
450
+ }
451
+ const data = new Uint8Array(total);
452
+ const targetSize = total;
453
+ let targetOffset = 0;
454
+ if (targetOffset + size > targetSize) throw new Error('Invalid descriptor');
455
+ if (size > 0) copyFromRing(data, targetOffset, ring.bytes, offset, size);
456
+ targetOffset += size;
457
+ ackSegment(ring, readMsg, readByte, size);
458
+ readMsg = readMsg + 1 | 0;
459
+ readByte = readByte + size | 0;
460
+ state.rMsg = readMsg;
461
+ state.rByte = readByte;
462
+ while(targetOffset < targetSize){
463
+ while(readMsg === Atomics.load(ring.control, IDX_WRITE_MSG)){
464
+ assertReadable(state, true);
465
+ if (await Atomics.waitAsync(ring.control, IDX_WRITE_MSG, readMsg, timeout).value === 'timed-out') throw new Error('Read timeout waiting for segment');
466
+ }
467
+ const di2 = (readMsg & ring.ringMask) * DESCRIPTOR_WORDS;
468
+ const off2 = ring.descriptors[di2 + IDX_DESC_OFFSET];
469
+ const sz2 = ring.descriptors[di2 + IDX_DESC_SIZE];
470
+ const tot2 = ring.descriptors[di2 + IDX_DESC_TOTAL];
471
+ if (off2 < 0 || off2 >= ring.dataBytes || sz2 < 0 || sz2 > ring.dataBytes) throw new Error('Invalid descriptor');
472
+ if (tot2 !== CONTINUATION) throw new Error('Invalid descriptor');
473
+ if (targetOffset + sz2 > targetSize) throw new Error('Invalid descriptor');
474
+ if (sz2 > 0) copyFromRing(data, targetOffset, ring.bytes, off2, sz2);
475
+ targetOffset += sz2;
476
+ ackSegment(ring, readMsg, readByte, sz2);
477
+ readMsg = readMsg + 1 | 0;
478
+ readByte = readByte + sz2 | 0;
479
+ state.rMsg = readMsg;
480
+ state.rByte = readByte;
481
+ }
482
+ return data;
483
+ };
484
+ const runExclusive = async (state, key, task)=>{
485
+ const previous = state[key];
486
+ let release;
487
+ const next = new Promise((resolve)=>{
488
+ release = resolve;
489
+ });
490
+ state[key] = previous.then(()=>next, ()=>next);
491
+ await previous;
492
+ try {
493
+ return await task();
494
+ } finally{
495
+ release();
496
+ }
497
+ };
498
+ const closeState = (buffer, state)=>{
499
+ if (state.localClosed) return;
500
+ flushPendingAck(state);
501
+ state.localClosed = true;
502
+ Atomics.or(state.header, IDX_FLAGS, state.localClosedMask);
503
+ Atomics.compareExchange(state.header, state.ownerIndex, ENDPOINT_TOKEN, 0);
504
+ notifyWaiters(state);
505
+ handleCache.delete(buffer);
506
+ stateCache.delete(buffer);
507
+ };
508
+ const createBuffer = (byteLength)=>{
509
+ if (byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');
510
+ const buffer = new SharedArrayBuffer(byteLength);
511
+ if (computeLayout(buffer.byteLength) === null) throw new Error(`SharedArrayBuffer too small for channel (minimum ${MIN_BUFFER_BYTES} bytes)`);
512
+ initializeHeaderSync(buffer, new Int32Array(buffer, 0, HEADER_WORDS));
513
+ return buffer;
514
+ };
515
+ const open = (buffer)=>{
516
+ const cached = handleCache.get(buffer);
517
+ if (cached !== undefined) return cached;
518
+ if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');
519
+ const state = getStateSync(buffer);
520
+ const channel = {
521
+ write: (data, options)=>runExclusive(state, 'writeTail', async ()=>{
522
+ state.writeLocked = true;
523
+ try {
524
+ await writeChannel(data, state, options?.timeout ?? 5000);
525
+ } finally{
526
+ state.writeLocked = false;
527
+ }
528
+ }),
529
+ read: (options)=>runExclusive(state, 'readTail', async ()=>{
530
+ state.readLocked = true;
531
+ try {
532
+ return await readChannel(state, options?.timeout ?? 5000);
533
+ } finally{
534
+ state.readLocked = false;
535
+ }
536
+ }),
537
+ writeSync: (data, options)=>writeChannelSync(data, state, options?.timeout ?? 5000),
538
+ readSync: (options)=>readChannelSync(state, options?.timeout ?? 5000),
539
+ close: ()=>closeState(buffer, state)
540
+ };
541
+ handleCache.set(buffer, channel);
542
+ return channel;
543
+ };
544
+ const reset = (buffer)=>{
545
+ if (buffer.byteLength % Int32Array.BYTES_PER_ELEMENT !== 0) throw new Error('SharedArrayBuffer byteLength must be a multiple of 4');
546
+ if (computeLayout(buffer.byteLength) === null) throw new Error(`SharedArrayBuffer too small for channel (minimum ${MIN_BUFFER_BYTES} bytes)`);
547
+ const cached = stateCache.get(buffer);
548
+ if (cached !== undefined && !cached.localClosed) throw new Error('Channel is still open in this runtime');
549
+ resetHeaderSync(buffer, new Int32Array(buffer, 0, HEADER_WORDS));
550
+ handleCache.delete(buffer);
551
+ stateCache.delete(buffer);
219
552
  };
220
553
 
221
554
  //# sourceMappingURL=index.cjs.map