Offload functions to worker threads with shared memory primitives for Node.js.
1import type { Loadable } from './loadable.ts';
2
3const SHARED = Symbol.for('moroutine.shared');
4const encoder = new TextEncoder();
5const decoder = new TextDecoder();
6
7/** A variable-length shared UTF-8 string with a max byte capacity, backed by SharedArrayBuffer. */
8export class SharedString implements Loadable<string> {
9 readonly maxBytes: number;
10 private readonly lengthView: Uint32Array;
11 private readonly dataView: Uint8Array;
12
13 static byteAlignment = 4;
14
15 constructor(maxBytes: number, buffer?: SharedArrayBuffer, byteOffset?: number) {
16 this.maxBytes = maxBytes;
17 const totalSize = 4 + maxBytes;
18 const buf = buffer ?? new SharedArrayBuffer(totalSize);
19 const offset = byteOffset ?? 0;
20 this.lengthView = new Uint32Array(buf, offset, 1);
21 this.dataView = new Uint8Array(buf, offset + 4, maxBytes);
22 }
23
24 /**
25 * Decodes and returns the stored string.
26 * @returns The UTF-8 decoded string.
27 */
28 load(): string {
29 const len = this.lengthView[0];
30 if (len === 0) return '';
31 return decoder.decode(this.dataView.subarray(0, len));
32 }
33
34 /**
35 * Encodes and stores a string. Throws if the encoded bytes exceed the max capacity.
36 * @param value - The string to encode and store.
37 */
38 store(value: string): void {
39 if (value === '') {
40 this.lengthView[0] = 0;
41 return;
42 }
43 const encoded = encoder.encode(value);
44 if (encoded.length > this.maxBytes) {
45 throw new RangeError(`Encoded string (${encoded.length} bytes) exceeds max ${this.maxBytes} bytes`);
46 }
47 this.dataView.set(encoded);
48 this.lengthView[0] = encoded.length;
49 }
50
51 [SHARED](): { tag: string; buffer: SharedArrayBuffer; byteOffset: number; maxBytes: number } {
52 return {
53 tag: 'SharedString',
54 buffer: this.lengthView.buffer as SharedArrayBuffer,
55 byteOffset: this.lengthView.byteOffset,
56 maxBytes: this.maxBytes,
57 };
58 }
59}