import type { Loadable } from './loadable.ts'; const SHARED = Symbol.for('moroutine.shared'); const encoder = new TextEncoder(); const decoder = new TextDecoder(); /** A variable-length shared UTF-8 string with a max byte capacity, backed by SharedArrayBuffer. */ export class SharedString implements Loadable { readonly maxBytes: number; private readonly lengthView: Uint32Array; private readonly dataView: Uint8Array; static byteAlignment = 4; constructor(maxBytes: number, buffer?: SharedArrayBuffer, byteOffset?: number) { this.maxBytes = maxBytes; const totalSize = 4 + maxBytes; const buf = buffer ?? new SharedArrayBuffer(totalSize); const offset = byteOffset ?? 0; this.lengthView = new Uint32Array(buf, offset, 1); this.dataView = new Uint8Array(buf, offset + 4, maxBytes); } /** * Decodes and returns the stored string. * @returns The UTF-8 decoded string. */ load(): string { const len = this.lengthView[0]; if (len === 0) return ''; return decoder.decode(this.dataView.subarray(0, len)); } /** * Encodes and stores a string. Throws if the encoded bytes exceed the max capacity. * @param value - The string to encode and store. */ store(value: string): void { if (value === '') { this.lengthView[0] = 0; return; } const encoded = encoder.encode(value); if (encoded.length > this.maxBytes) { throw new RangeError(`Encoded string (${encoded.length} bytes) exceeds max ${this.maxBytes} bytes`); } this.dataView.set(encoded); this.lengthView[0] = encoded.length; } [SHARED](): { tag: string; buffer: SharedArrayBuffer; byteOffset: number; maxBytes: number } { return { tag: 'SharedString', buffer: this.lengthView.buffer as SharedArrayBuffer, byteOffset: this.lengthView.byteOffset, maxBytes: this.maxBytes, }; } }