/** * A range's chunks on the wire (REBUILD.md, prefetch): one streaming * response, a frame per chunk: from (8 bytes), to (8 bytes), length * (4 bytes), then the chunk as the news server sent it (compressed XOVER). */ export interface Frame { from: number to: number data: Buffer } const HEADER = 20 export function encodeFrame(frame: Frame): Buffer { const head = Buffer.alloc(HEADER) head.writeBigUInt64BE(BigInt(frame.from), 0) head.writeBigUInt64BE(BigInt(frame.to), 8) head.writeUInt32BE(frame.data.length, 16) return Buffer.concat([head, frame.data]) } /** The frames of a byte stream, as they complete. */ export async function* decodeFrames(stream: AsyncIterable | Iterable): AsyncGenerator { let buffer = Buffer.alloc(0) for await (const piece of stream) { buffer = buffer.length === 0 ? Buffer.from(piece) : Buffer.concat([buffer, piece]) while (buffer.length >= HEADER) { const length = buffer.readUInt32BE(16) if (buffer.length < HEADER + length) break yield { from: Number(buffer.readBigUInt64BE(0)), to: Number(buffer.readBigUInt64BE(8)), data: buffer.subarray(HEADER, HEADER + length) } buffer = buffer.subarray(HEADER + length) } } if (buffer.length > 0) throw new Error(`Stream ended inside a frame (${buffer.length} bytes left)`) }