"use strict";
//
//  Copyright (c) Microsoft Corporation. All rights reserved.
//
Object.defineProperty(exports, "__esModule", { value: true });
exports.formatBuffer = exports.SshDataWriter = exports.SshDataReader = void 0;
const buffer_1 = require("buffer");
const sshAlgorithms_1 = require("../algorithms/sshAlgorithms");
const bigInt_1 = require("./bigInt");
class SshDataReader {
    constructor(buffer) {
        this.buffer = buffer;
        this.position = 0;
    }
    get available() {
        return this.buffer.length - this.position;
    }
    read(length) {
        if (this.available < length) {
            throw new Error('Attempted to read past end of buffer.');
        }
        const data = this.buffer.slice(this.position, this.position + length);
        this.position += length;
        return data;
    }
    readByte() {
        if (this.available === 0) {
            throw new Error('Attempted to read past end of buffer.');
        }
        const value = this.buffer[this.position];
        this.position++;
        return value;
    }
    readBinary() {
        const length = this.readUInt32();
        if (this.available < length) {
            throw new Error('Attempted to read past end of buffer.');
        }
        const data = this.buffer.slice(this.position, this.position + length);
        this.position += length;
        return data;
    }
    readString(encoding) {
        const bytes = this.readBinary();
        return bytes.toString();
    }
    readList(encoding) {
        const stringList = this.readString(encoding);
        return stringList.length === 0 ? [] : stringList.split(',');
    }
    readBoolean() {
        return this.readByte() !== 0;
    }
    readUInt32() {
        if (this.available < 4) {
            throw new Error('Attempted to read past end of buffer.');
        }
        // Big-endian encoding
        const value0 = this.buffer[this.position + 0];
        const value1 = this.buffer[this.position + 1];
        const value2 = this.buffer[this.position + 2];
        const value3 = this.buffer[this.position + 3];
        this.position += 4;
        const value = ((value0 << 24) | (value1 << 16) | (value2 << 8) | value3) >>> 0;
        return value;
    }
    readUInt64() {
        if (this.available < 8) {
            throw new Error('Attempted to read past end of buffer.');
        }
        // Big-endian encoding
        const value0 = this.buffer[this.position + 0];
        const value1 = this.buffer[this.position + 1];
        const value2 = this.buffer[this.position + 2];
        const value3 = this.buffer[this.position + 3];
        const value4 = this.buffer[this.position + 4];
        const value5 = this.buffer[this.position + 5];
        const value6 = this.buffer[this.position + 6];
        const value7 = this.buffer[this.position + 7];
        this.position += 8;
        const high = ((value0 << 24) | (value1 << 16) | (value2 << 8) | value3) >>> 0;
        const low = ((value4 << 24) | (value5 << 16) | (value6 << 8) | value7) >>> 0;
        return high * 0x100000000 + low;
    }
    readBigInt() {
        const data = this.readBinary();
        if (data.length === 0) {
            return bigInt_1.BigInt.zero;
        }
        return bigInt_1.BigInt.fromBytes(data);
    }
}
exports.SshDataReader = SshDataReader;
SshDataReader.mpintZero = buffer_1.Buffer.alloc(1);
class SshDataWriter {
    constructor(buffer) {
        this.buffer = buffer;
        this.position = 0;
    }
    write(data) {
        this.ensureCapacity(this.position + data.length);
        data.copy(this.buffer, this.position);
        this.position += data.length;
    }
    writeByte(value) {
        this.ensureCapacity(this.position + 1);
        this.buffer[this.position] = value;
        this.position++;
    }
    writeBinary(data) {
        this.ensureCapacity(this.position + 4 + data.length);
        this.writeUInt32(data.length);
        data.copy(this.buffer, this.position);
        this.position += data.length;
    }
    writeString(value, encoding) {
        this.writeBinary(buffer_1.Buffer.from(value));
    }
    writeList(value, encoding) {
        this.writeString(value ? value.join(',') : '', encoding);
    }
    writeBoolean(value) {
        this.writeByte(value ? 1 : 0);
    }
    writeUInt32(value) {
        this.ensureCapacity(this.position + 4);
        // Big-endian encoding
        this.buffer[this.position + 0] = value >>> 24;
        this.buffer[this.position + 1] = value >>> 16;
        this.buffer[this.position + 2] = value >>> 8;
        this.buffer[this.position + 3] = value >>> 0;
        this.position += 4;
    }
    /* @internal */
    static writeUInt32(buffer, offset, value) {
        buffer[offset + 0] = value >>> 24;
        buffer[offset + 1] = value >>> 16;
        buffer[offset + 2] = value >>> 8;
        buffer[offset + 3] = value >>> 0;
    }
    writeUInt64(value) {
        this.ensureCapacity(this.position + 8);
        const low = value & 0xffffffff;
        const high = (value - low) / 0x100000000;
        // Big-endian encoding
        this.buffer[this.position + 0] = high >>> 24;
        this.buffer[this.position + 1] = high >>> 16;
        this.buffer[this.position + 2] = high >>> 8;
        this.buffer[this.position + 3] = high >>> 0;
        this.buffer[this.position + 4] = low >>> 24;
        this.buffer[this.position + 5] = low >>> 16;
        this.buffer[this.position + 6] = low >>> 8;
        this.buffer[this.position + 7] = low >>> 0;
        this.position += 8;
    }
    writeBigInt(value) {
        const data = value.toBytes();
        if (data.length === 1 && data[0] === 0) {
            this.writeUInt32(0);
        }
        else {
            this.writeBinary(data);
        }
    }
    writeRandom(length) {
        this.ensureCapacity(this.position + length);
        const randomBuffer = this.buffer.slice(this.position, this.position + length);
        sshAlgorithms_1.SshAlgorithms.random.getBytes(randomBuffer);
        this.position += length;
    }
    skip(length) {
        this.ensureCapacity(this.position + length);
        this.position += length;
    }
    ensureCapacity(capacity) {
        if (this.buffer.length < capacity) {
            let newLength = Math.max(512, this.buffer.length * 2);
            while (newLength < capacity)
                newLength *= 2;
            const newBuffer = buffer_1.Buffer.alloc(newLength);
            this.buffer.copy(newBuffer, 0, 0, this.position);
            this.buffer = newBuffer;
        }
    }
    toBuffer() {
        return this.buffer.slice(0, this.position);
    }
}
exports.SshDataWriter = SshDataWriter;
function makeCrcTable() {
    let c;
    const table = [];
    for (let n = 0; n < 256; n++) {
        c = n;
        for (let k = 0; k < 8; k++) {
            c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
        }
        table[n] = c;
    }
    return table;
}
let crcTable;
function crc32(data) {
    if (!crcTable) {
        crcTable = makeCrcTable();
    }
    let crc = 0 ^ -1;
    for (let i = 0; i < data.length; i++) {
        crc = (crc >>> 8) ^ crcTable[(crc ^ data[i]) & 0xff];
    }
    const result = (crc ^ -1) >>> 0;
    return (result + 0x100000000)
        .toString(16)
        .substr(-8)
        .toUpperCase();
}
/**
 * Formats a byte buffer using the same format as OpenSSH,
 * useful for debugging and comparison in logs.
 */
function formatBuffer(data, name, formatData) {
    let s = `${name === undefined ? 'Buffer' : name}[${data.length}] (${crc32(data)})\n`;
    if (formatData === false) {
        return s;
    }
    const max = Math.min(2048, data.length);
    for (let lineOffset = 0; lineOffset < max; lineOffset += 16) {
        if (lineOffset < 1000)
            s += '0';
        if (lineOffset < 100)
            s += '0';
        if (lineOffset < 10)
            s += '0';
        s += lineOffset + ':';
        for (let i = lineOffset; i < lineOffset + 16; i++) {
            if (i < max) {
                s += ' ' + data.slice(i, i + 1).toString('hex');
            }
            else {
                s += '   ';
            }
        }
        s += '  ';
        for (let i = lineOffset; i < lineOffset + 16; i++) {
            if (i < max) {
                const c = data[i];
                s += c > 32 && c <= 127 ? data.slice(i, i + 1).toString() : '.';
            }
            else {
                s += ' ';
            }
        }
        s += '\n';
    }
    if (max < data.length) {
        s += '...\n';
    }
    return s;
}
exports.formatBuffer = formatBuffer;
//# sourceMappingURL=https://main.vscode-cdn.net/sourcemaps/6a44c352bd24569c417e530095901b649960f9f8/node_modules/@microsoft/dev-tunnels-ssh/io/sshData.js.map