import { ProtoError } from "../../errors.js"; import { VARINT, FIXED_64, LENGTH_DELIMITED, FIXED_32 } from "./util.js"; class MessageReader { #array; #view; #pos; constructor(array) { this.#array = array; this.#view = new DataView(array.buffer, array.byteOffset, array.byteLength); this.#pos = 0; } varint() { let value = 0; for (let shift = 0;; shift += 7) { const byte = this.#array[this.#pos++]; value |= (byte & 0x7f) << shift; if (!(byte & 0x80)) { break; } } return value; } varintBig() { let value = 0n; for (let shift = 0n;; shift += 7n) { const byte = this.#array[this.#pos++]; value |= BigInt(byte & 0x7f) << shift; if (!(byte & 0x80)) { break; } } return value; } bytes(length) { const array = new Uint8Array(this.#array.buffer, this.#array.byteOffset + this.#pos, length); this.#pos += length; return array; } double() { const value = this.#view.getFloat64(this.#pos, true); this.#pos += 8; return value; } skipVarint() { for (;;) { const byte = this.#array[this.#pos++]; if (!(byte & 0x80)) { break; } } } skip(count) { this.#pos += count; } eof() { return this.#pos >= this.#array.byteLength; } } export class FieldReader { #reader; #wireType; constructor(reader) { this.#reader = reader; this.#wireType = -1; } setup(wireType) { this.#wireType = wireType; } #expect(expectedWireType) { if (this.#wireType !== expectedWireType) { throw new ProtoError(`Expected wire type ${expectedWireType}, got ${this.#wireType}`); } this.#wireType = -1; } bytes() { this.#expect(LENGTH_DELIMITED); const length = this.#reader.varint(); return this.#reader.bytes(length); } string() { return new TextDecoder().decode(this.bytes()); } message(def) { return readProtobufMessage(this.bytes(), def); } int32() { this.#expect(VARINT); return this.#reader.varint(); } uint32() { return this.int32(); } bool() { return this.int32() !== 0; } uint64() { this.#expect(VARINT); return this.#reader.varintBig(); } sint64() { const value = this.uint64(); return (value >> 1n) ^ (-(value & 1n)); } double() { this.#expect(FIXED_64); return this.#reader.double(); } maybeSkip() { if (this.#wireType < 0) { return; } else if (this.#wireType === VARINT) { this.#reader.skipVarint(); } else if (this.#wireType === FIXED_64) { this.#reader.skip(8); } else if (this.#wireType === LENGTH_DELIMITED) { const length = this.#reader.varint(); this.#reader.skip(length); } else if (this.#wireType === FIXED_32) { this.#reader.skip(4); } else { throw new ProtoError(`Unexpected wire type ${this.#wireType}`); } this.#wireType = -1; } } export function readProtobufMessage(data, def) { const msgReader = new MessageReader(data); const fieldReader = new FieldReader(msgReader); let value = def.default(); while (!msgReader.eof()) { const key = msgReader.varint(); const tag = key >> 3; const wireType = key & 0x7; fieldReader.setup(wireType); const tagFun = def[tag]; if (tagFun !== undefined) { const returnedValue = tagFun(fieldReader, value); if (returnedValue !== undefined) { value = returnedValue; } } fieldReader.maybeSkip(); } return value; }