UNPKG

@struktoai/mirage-core

Version:

A Unified Virtual Filesystem For AI Agents

86 lines • 3.02 kB
import { ByteSpool, MEMORY_LIMIT } from "./spool.js"; /** Replay variable-length records without retaining their byte offsets in RAM. */ export class RecordSpool { data; index; count = 0; constructor(memoryLimit = MEMORY_LIMIT) { this.data = new ByteSpool(memoryLimit); this.index = new ByteSpool(memoryLimit); } async append(record) { const offset = this.data.size; await this.data.append(record); await this.addEntry(offset, record.length); } async addEntry(offset, size) { const entry = new Uint8Array(16); const view = new DataView(entry.buffer); view.setBigUint64(0, BigInt(offset), true); view.setBigUint64(8, BigInt(size), true); await this.index.append(entry); this.count++; } async indexDelimited(separator) { if (this.count !== 0 || !Number.isInteger(separator) || separator < 0 || separator > 255) throw new RangeError('record index requires an empty index and one-byte separator'); let start = 0; let offset = 0; for await (const chunk of this.data.stream()) { let position = 0; for (;;) { const found = chunk.indexOf(separator, position); if (found < 0) break; const end = offset + found + 1; await this.addEntry(start, end - start); start = end; position = found + 1; } offset += chunk.length; } if (start < this.data.size) await this.addEntry(start, this.data.size - start); } async window(index) { if (!Number.isSafeInteger(index) || index < 0 || index >= this.count) throw new RangeError('record index out of range'); const entry = await this.index.read(index * 16, 16); const view = new DataView(entry.buffer, entry.byteOffset, entry.byteLength); return [Number(view.getBigUint64(0, true)), Number(view.getBigUint64(8, true))]; } async read(index) { const [offset, size] = await this.window(index); return this.data.read(offset, size); } async *stream(index) { const [offset, size] = await this.window(index); yield* this.data.stream(offset, size); } async close() { try { await this.data.close(); } finally { await this.index.close(); } } } /** * The records `separator` ends across `sources` read back to back, byte for * byte; the last one may lack it. Mirrors Python's delimited. */ export async function delimited(sources, separator) { const records = new RecordSpool(); try { for (const source of sources) await records.data.fill(source); await records.indexDelimited(separator); } catch (error) { await records.close(); throw error; } return records; } //# sourceMappingURL=record_spool.js.map