import { reader } from 'it-reader' import { isUint8ArrayList, Uint8ArrayList } from 'uint8arraylist' import type { Source } from 'it-stream-types' export interface LteReader extends AsyncIterator { nextLte(bytes: number): Promise> return(): Promise> } export function lteReader (source: Source): LteReader { const input = reader(source) let overflow: Uint8ArrayList | null const lteReader = { [Symbol.asyncIterator]: () => lteReader, async next (bytes?: number): Promise> { if (overflow != null) { let value if (bytes == null || overflow.length === bytes) { value = overflow overflow = null } else if (overflow.length > bytes) { value = overflow.sublist(0, bytes) overflow = overflow.sublist(bytes) } else if (overflow.length < bytes) { const { value: nextValue, done } = await input.next(bytes - overflow.length) if (done === true) { throw Object.assign( new Error(`stream ended before ${bytes - overflow.length} bytes became available`), { code: 'ERR_UNDER_READ' } ) } value = new Uint8ArrayList(overflow, nextValue) overflow = null } if (value == null) { const result: IteratorResult = { done: true, value: undefined } return result } const result: IteratorResult = { done: false, value } return result } return input.next(bytes) }, async nextLte (bytes: number): Promise> { const { done, value } = await lteReader.next() if (done === true) { return { done: true, value: undefined } } if (value.length <= bytes) { return { done: false, value } } const list = isUint8ArrayList(value) ? value : new Uint8ArrayList(value) if (overflow != null) { overflow.append(list.sublist(bytes)) } else { overflow = list.sublist(bytes) } return { done: false, value: list.sublist(0, bytes) } }, async return () { return input.return() } } return lteReader }