UNPKG

it-length-prefixed

Version:

Streaming length prefixed buffers with async iterables

124 lines 4.54 kB
/* eslint max-depth: ["error", 6] */ import * as varint from 'uint8-varint'; import { Uint8ArrayList } from 'uint8arraylist'; import { MAX_DATA_LENGTH, MAX_LENGTH_LENGTH } from './constants.js'; import { InvalidDataLengthError, InvalidDataLengthLengthError, InvalidMessageLengthError, UnexpectedEOFError } from './errors.js'; import { isAsyncIterable } from './utils.js'; var ReadMode; (function (ReadMode) { ReadMode[ReadMode["LENGTH"] = 0] = "LENGTH"; ReadMode[ReadMode["DATA"] = 1] = "DATA"; })(ReadMode || (ReadMode = {})); const defaultDecoder = (buf) => { const length = varint.decode(buf); defaultDecoder.bytes = varint.encodingLength(length); return length; }; defaultDecoder.bytes = 0; export function decode(source, options) { const buffer = new Uint8ArrayList(); let mode = ReadMode.LENGTH; let dataLength = -1; const lengthDecoder = options?.lengthDecoder ?? defaultDecoder; const maxLengthLength = options?.maxLengthLength ?? MAX_LENGTH_LENGTH; const maxDataLength = options?.maxDataLength ?? MAX_DATA_LENGTH; function* maybeYield() { while (buffer.byteLength > 0) { if (mode === ReadMode.LENGTH) { // read length, ignore errors for short reads try { dataLength = lengthDecoder(buffer); if (dataLength < 0) { throw new InvalidMessageLengthError('Invalid message length'); } if (dataLength > maxDataLength) { throw new InvalidDataLengthError('Message length too long'); } const dataLengthLength = lengthDecoder.bytes; buffer.consume(dataLengthLength); if (options?.onLength != null) { options.onLength(dataLength); } mode = ReadMode.DATA; } catch (err) { if (err instanceof RangeError) { if (buffer.byteLength > maxLengthLength) { throw new InvalidDataLengthLengthError('Message length length too long'); } break; } throw err; } } if (mode === ReadMode.DATA) { if (buffer.byteLength < dataLength) { // not enough data, wait for more break; } const data = buffer.sublist(0, dataLength); buffer.consume(dataLength); if (options?.onData != null) { options.onData(data); } yield data; mode = ReadMode.LENGTH; } } } if (isAsyncIterable(source)) { return (async function* () { for await (const buf of source) { buffer.append(buf); yield* maybeYield(); } if (buffer.byteLength > 0) { throw new UnexpectedEOFError('Unexpected end of input'); } })(); } return (function* () { for (const buf of source) { buffer.append(buf); yield* maybeYield(); } if (buffer.byteLength > 0) { throw new UnexpectedEOFError('Unexpected end of input'); } })(); } decode.fromReader = (reader, options) => { let byteLength = 1; // Read single byte chunks until the length is known const varByteSource = (async function* () { while (true) { try { const { done, value } = await reader.next(byteLength); if (done === true) { return; } if (value != null) { yield value; } } catch (err) { if (err.code === 'ERR_UNDER_READ') { return { done: true, value: null }; } throw err; } finally { // Reset the byteLength so we continue to check for varints byteLength = 1; } } }()); /** * Once the length has been parsed, read chunk for that length */ const onLength = (l) => { byteLength = l; }; return decode(varByteSource, { ...(options ?? {}), onLength }); }; //# sourceMappingURL=decode.js.map