Home Reference Source Test

src/main/generic/utils/buffer/SerialBuffer.js

class SerialBuffer extends Uint8Array {
    /**
     * @param {*} bufferOrArrayOrLength
     */
    constructor(bufferOrArrayOrLength) {
        super(bufferOrArrayOrLength);
        this._view = new DataView(this.buffer);
        this._readPos = 0;
        this._writePos = 0;
    }

    /**
     * @param {number} start
     * @param {number} end
     * @return {Uint8Array}
     */
    subarray(start, end) {
        return ArrayUtils.subarray(this, start, end);
    }

    /** @type {number} */
    get readPos() {
        return this._readPos;
    }

    /** @type {number} */
    set readPos(value) {
        if (value < 0 || value > this.byteLength) throw `Invalid readPos ${value}`;
        this._readPos = value;
    }

    /** @type {number} */
    get writePos() {
        return this._writePos;
    }

    /** @type {number} */
    set writePos(value) {
        if (value < 0 || value > this.byteLength) throw `Invalid writePos ${value}`;
        this._writePos = value;
    }

    /**
     * Resets the read and write position of the buffer to zero.
     * @returns {void}
     */
    reset() {
        this._readPos = 0;
        this._writePos = 0;
    }

    /**
     * @param {number} length
     * @return {Uint8Array}
     */
    read(length) {
        const value = this.subarray(this._readPos, this._readPos + length);
        this._readPos += length;
        return new Uint8Array(value);
    }

    /**
     * @param {*} array
     */
    write(array) {
        this.set(array, this._writePos);
        this._writePos += array.byteLength;
    }

    /**
     * @return {number}
     */
    readUint8() {
        return this._view.getUint8(this._readPos++);
    }

    /**
     * @param {number} value
     */
    writeUint8(value) {
        this._view.setUint8(this._writePos++, value);
    }

    /**
     * @return {number}
     */
    readUint16() {
        const value = this._view.getUint16(this._readPos);
        this._readPos += 2;
        return value;
    }

    /**
     * @param {number} value
     */
    writeUint16(value) {
        this._view.setUint16(this._writePos, value);
        this._writePos += 2;
    }

    /**
     * @return {number}
     */
    readUint32() {
        const value = this._view.getUint32(this._readPos);
        this._readPos += 4;
        return value;
    }

    /**
     * @param {number} value
     */
    writeUint32(value) {
        this._view.setUint32(this._writePos, value);
        this._writePos += 4;
    }

    /**
     * @return {number}
     */
    readUint64() {
        const value = this._view.getUint32(this._readPos) * Math.pow(2, 32) + this._view.getUint32(this._readPos + 4);
        if (!NumberUtils.isUint64(value)) throw new Error('Malformed value');
        this._readPos += 8;
        return value;
    }

    /**
     * @param {number} value
     */
    writeUint64(value) {
        if (!NumberUtils.isUint64(value)) throw new Error('Malformed value');
        this._view.setUint32(this._writePos, Math.floor(value / Math.pow(2, 32)));
        this._view.setUint32(this._writePos + 4, value);
        this._writePos += 8;
    }

    /**
     * @return {number}
     */
    readVarUint() {
        const value = this.readUint8();
        if (value < 0xFD) {
            return value;
        } else if (value === 0xFD) {
            return this.readUint16();
        } else if (value === 0xFE) {
            return this.readUint32();
        } else /*if (value === 0xFF)*/ {
            return this.readUint64();
        }
    }

    /**
     * @param {number} value
     */
    writeVarUint(value) {
        if (!NumberUtils.isUint64(value)) throw new Error('Malformed value');
        if (value < 0xFD) {
            this.writeUint8(value);
        } else if (value <= 0xFFFF) {
            this.writeUint8(0xFD);
            this.writeUint16(value);
        } else if (value <= 0xFFFFFFFF) {
            this.writeUint8(0xFE);
            this.writeUint32(value);
        } else {
            this.writeUint8(0xFF);
            this.writeUint64(value);
        }
    }

    /**
     * @param {number} value
     * @returns {number}
     */
    static varUintSize(value) {
        if (!NumberUtils.isUint64(value)) throw new Error('Malformed value');
        if (value < 0xFD) {
            return 1;
        } else if (value <= 0xFFFF) {
            return 3;
        } else if (value <= 0xFFFFFFFF) {
            return 5;
        } else {
            return 9;
        }
    }

    /**
     * @return {number}
     */
    readFloat64() {
        const value = this._view.getFloat64(this._readPos);
        this._readPos += 8;
        return value;
    }

    /**
     * @param {number} value
     */
    writeFloat64(value) {
        this._view.setFloat64(this._writePos, value);
        this._writePos += 8;
    }

    /**
     * @param {number} length
     * @return {string}
     */
    readString(length) {
        const bytes = this.read(length);
        return BufferUtils.toAscii(bytes);
    }

    /**
     * @param {string} value
     * @param {number} length
     */
    writeString(value, length) {
        if (StringUtils.isMultibyte(value) || value.length !== length) throw 'Malformed value/length';
        const bytes = BufferUtils.fromAscii(value);
        this.write(bytes);
    }

    /**
     * @param {number} length
     * @return {string}
     */
    readPaddedString(length) {
        const bytes = this.read(length);
        let i = 0;
        while (i < length && bytes[i] !== 0x0) i++;
        const view = new Uint8Array(bytes.buffer, bytes.byteOffset, i);
        return BufferUtils.toAscii(view);
    }

    /**
     * @param {string} value
     * @param {number} length
     */
    writePaddedString(value, length) {
        if (StringUtils.isMultibyte(value) || value.length > length) throw 'Malformed value/length';
        const bytes = BufferUtils.fromAscii(value);
        this.write(bytes);
        const padding = length - bytes.byteLength;
        this.write(new Uint8Array(padding));
    }

    /**
     * @return {string}
     */
    readVarLengthString() {
        const length = this.readUint8();
        if (this._readPos + length > this.length) throw 'Malformed length';
        const bytes = this.read(length);
        return BufferUtils.toAscii(bytes);
    }

    /**
     * @param {string} value
     */
    writeVarLengthString(value) {
        if (StringUtils.isMultibyte(value) || !NumberUtils.isUint8(value.length)) throw new Error('Malformed value');
        const bytes = BufferUtils.fromAscii(value);
        this.writeUint8(bytes.byteLength);
        this.write(bytes);
    }

    /**
     * @param {string} value
     * @returns {number}
     */
    static varLengthStringSize(value) {
        if (StringUtils.isMultibyte(value) || !NumberUtils.isUint8(value.length)) throw new Error('Malformed value');
        return /*length*/ 1 + value.length;
    }
}
Class.register(SerialBuffer);