import { InvalidParametersError } from '@libp2p/interface';
import * as varint from 'uint8-varint';
import { Uint8ArrayList } from 'uint8arraylist';
import { InvalidMessageLengthError } from "./stream-utils.js";
const DEFAULT_MAX_BUFFER_SIZE = 1024 * 1024 * 4;
const DEFAULT_MAX_DATA_LENGTH = 1024 * 1024 * 4;
/**
 * Decode length-prefixed data from a buffer
 */
export class LengthPrefixedDecoder {
    buffer;
    maxBufferSize;
    lengthDecoder;
    maxDataLength;
    encodingLength;
    constructor(init = {}) {
        this.buffer = new Uint8ArrayList();
        this.maxBufferSize = init.maxBufferSize ?? DEFAULT_MAX_BUFFER_SIZE;
        this.maxDataLength = init.maxDataLength ?? DEFAULT_MAX_DATA_LENGTH;
        this.lengthDecoder = init.lengthDecoder ?? varint.decode;
        this.encodingLength = init.encodingLength ?? varint.encodingLength;
    }
    /**
     * Decodes length-prefixed data
     */
    *decode(buf) {
        this.buffer.append(buf);
        if (this.buffer.byteLength > this.maxBufferSize) {
            throw new InvalidParametersError(`Buffer length limit exceeded - ${this.buffer.byteLength}/${this.maxBufferSize}`);
        }
        // Loop to consume as many bytes from the buffer as possible
        // Eg: when a single chunk contains several frames
        while (true) {
            let dataLength;
            try {
                dataLength = this.lengthDecoder(this.buffer);
            }
            catch (err) {
                if (err instanceof RangeError) {
                    // ignore errors where we don't have enough data to read the length
                    // prefix
                    break;
                }
                throw err;
            }
            if (dataLength < 0 || dataLength > this.maxDataLength) {
                throw new InvalidMessageLengthError('Invalid message length');
            }
            const lengthLength = this.encodingLength(dataLength);
            const chunkLength = lengthLength + dataLength;
            if (this.buffer.byteLength >= chunkLength) {
                const buf = this.buffer.sublist(lengthLength, chunkLength);
                this.buffer.consume(chunkLength);
                if (buf.byteLength > 0) {
                    yield buf;
                }
            }
            else {
                break;
            }
        }
    }
}
//# sourceMappingURL=length-prefixed-decoder.js.map