export default class Stream { private stream: Uint8Array; private readPointer = 0; // bits private writePointer = 0; // bits constructor(stream?: Uint8Array | number[]) { if (!stream) { this.stream = new Uint8Array(0); } else { const src = stream instanceof Uint8Array ? stream : Uint8Array.from(stream); this.stream = src; this.writePointer = this.stream.length << 3; } } getStream(): Uint8Array { return this.stream.slice(0, this.length()); } setStream(stream?: Uint8Array | number[]) { if (!stream) { this.stream = new Uint8Array(0); this.readPointer = 0; this.writePointer = 0; return; } const src = stream instanceof Uint8Array ? stream : Uint8Array.from(stream); this.stream = src; this.readPointer = 0; this.writePointer = this.stream.length << 3; } getBuffer(): Uint8Array { return this.getStream(); } isEmpty(): boolean { return this.writePointer === 0; } length(): number { return (this.writePointer + 7) >> 3; } // ---------- bit / boolean ---------- writeBit(value: number) { this.writeBits(BigInt(value & 1), 1); } readBit(): number { return Number(this.readBits(1)); } writeBoolean(value: boolean) { this.writeBit(value ? 1 : 0); } readBoolean(): boolean { return this.readBit() === 1; } // ---------- byte ---------- writeByte(b: number) { this.writeUInt8(b & 0xff); } readByte(): number { const v = this.readUInt8(); return (v << 24) >> 24; // signed byte } // ---------- UInt / Int 8 ---------- writeUInt8(value: number) { const v = value & 0xff; if ((this.writePointer & 7) === 0) { this.reserveBits(8); this.stream[this.writePointer >> 3] = v; this.writePointer += 8; return; } this.writeBits(BigInt(v), 8); } readUInt8(): number { if (this.remainingBits() < 8n) { throw new Error("Not enough bits to read UInt8"); } if ((this.readPointer & 7) === 0) { const v = this.stream[this.readPointer >> 3] & 0xff; this.readPointer += 8; return v; } return Number(this.readBits(8)); } writeInt8(value: number) { this.writeUInt8(value); } readInt8(): number { const u = this.readUInt8(); return (u << 24) >> 24; } // ---------- UInt / Int 16 ---------- writeUInt16(value: number) { const v = value & 0xffff; this.writeUInt8((v >>> 8) & 0xff); this.writeUInt8(v & 0xff); } readUInt16(): number { const hi = this.readUInt8(); const lo = this.readUInt8(); return (hi << 8) | lo; } writeInt16(value: number) { this.writeUInt16(value); } readInt16(): number { const u = this.readUInt16(); return (u << 16) >> 16; } // ---------- UInt / Int 32 ---------- writeUInt32(value: number) { if (!Number.isFinite(value) || value < 0 || value > 0xffffffff) { throw new Error(`UInt32 out of range: ${value}`); } const v = Math.floor(value); this.writeUInt8((v >>> 24) & 0xff); this.writeUInt8((v >>> 16) & 0xff); this.writeUInt8((v >>> 8) & 0xff); this.writeUInt8(v & 0xff); } readUInt32(): number { const b1 = this.readUInt8(); const b2 = this.readUInt8(); const b3 = this.readUInt8(); const b4 = this.readUInt8(); return (((b1 * 0x1000000) + (b2 << 16) + (b3 << 8) + b4) >>> 0); } writeInt32(value: number) { this.writeUInt32(value >>> 0); } readInt32(): number { return this.readUInt32() | 0; } // ---------- UInt / Int 64 ---------- writeUInt64(value: bigint) { if (value < 0n || value > 0xffff_ffff_ffff_ffffn) { throw new Error(`UInt64 out of range: ${value.toString()}`); } this.writeUInt8(Number((value >> 56n) & 0xffn)); this.writeUInt8(Number((value >> 48n) & 0xffn)); this.writeUInt8(Number((value >> 40n) & 0xffn)); this.writeUInt8(Number((value >> 32n) & 0xffn)); this.writeUInt8(Number((value >> 24n) & 0xffn)); this.writeUInt8(Number((value >> 16n) & 0xffn)); this.writeUInt8(Number((value >> 8n) & 0xffn)); this.writeUInt8(Number(value & 0xffn)); } readUInt64(): bigint { const high = BigInt(this.readUInt32() >>> 0); const low = BigInt(this.readUInt32() >>> 0); return (high << 32n) | low; } writeInt64(value: bigint) { const u = BigInt.asUintN(64, value); this.writeUInt64(u); } readInt64(): bigint { return BigInt.asIntN(64, this.readUInt64()); } // ---------- float ---------- writeFloat32(value: number) { const ab = new ArrayBuffer(4); const dv = new DataView(ab); dv.setFloat32(0, value, false); // big-endian this.writeUInt8(dv.getUint8(0)); this.writeUInt8(dv.getUint8(1)); this.writeUInt8(dv.getUint8(2)); this.writeUInt8(dv.getUint8(3)); } readFloat32(): number { const ab = new ArrayBuffer(4); const dv = new DataView(ab); dv.setUint8(0, this.readUInt8()); dv.setUint8(1, this.readUInt8()); dv.setUint8(2, this.readUInt8()); dv.setUint8(3, this.readUInt8()); return dv.getFloat32(0, false); // big-endian } // ---------- string / bytes ---------- // String: length(UInt32) + chars(UInt16), как в Java charAt() writeString(value: string | null | undefined) { const s = value ?? ""; this.writeUInt32(s.length); if (s.length === 0) return; this.reserveBits(BigInt(s.length) * 16n); for (let i = 0; i < s.length; i++) { this.writeUInt16(s.charCodeAt(i) & 0xffff); } } readString(): string { const len = this.readUInt32(); if (len > 0x7fffffff) { throw new Error(`String length too large: ${len}`); } const requiredBits = BigInt(len) * 16n; if (requiredBits > this.remainingBits()) { throw new Error("Not enough bits to read string"); } const chars = new Array(len); for (let i = 0; i < len; i++) { chars[i] = this.readUInt16(); } return String.fromCharCode(...chars); } // byte[]: length(UInt32) + payload writeBytes(value: Uint8Array | number[] | null | undefined) { const arr = value == null ? new Uint8Array(0) : (value instanceof Uint8Array ? value : Uint8Array.from(value)); this.writeUInt32(arr.length); if (arr.length === 0) return; this.reserveBits(BigInt(arr.length) * 8n); if ((this.writePointer & 7) === 0) { const byteIndex = this.writePointer >> 3; this.ensureCapacity(byteIndex + arr.length - 1); this.stream.set(arr, byteIndex); this.writePointer += arr.length << 3; return; } for (let i = 0; i < arr.length; i++) { this.writeUInt8(arr[i]); } } readBytes(): Uint8Array { const len = this.readUInt32(); if (len === 0) return new Uint8Array(0); const requiredBits = BigInt(len) * 8n; if (requiredBits > this.remainingBits()) { return new Uint8Array(0); } const out = new Uint8Array(len); if ((this.readPointer & 7) === 0) { const byteIndex = this.readPointer >> 3; out.set(this.stream.slice(byteIndex, byteIndex + len)); this.readPointer += len << 3; return out; } for (let i = 0; i < len; i++) { out[i] = this.readUInt8(); } return out; } // ---------- internals ---------- private remainingBits(): bigint { return BigInt(this.writePointer - this.readPointer); } private writeBits(value: bigint, bits: number) { if (bits <= 0) return; this.reserveBits(bits); for (let i = bits - 1; i >= 0; i--) { const bit = Number((value >> BigInt(i)) & 1n); const byteIndex = this.writePointer >> 3; const shift = 7 - (this.writePointer & 7); if (bit === 1) { this.stream[byteIndex] = this.stream[byteIndex] | (1 << shift); } else { this.stream[byteIndex] = this.stream[byteIndex] & ~(1 << shift); } this.writePointer++; } } private readBits(bits: number): bigint { if (bits <= 0) return 0n; if (this.remainingBits() < BigInt(bits)) { throw new Error("Not enough bits to read"); } let value = 0n; for (let i = 0; i < bits; i++) { const bit = (this.stream[this.readPointer >> 3] >> (7 - (this.readPointer & 7))) & 1; value = (value << 1n) | BigInt(bit); this.readPointer++; } return value; } private reserveBits(bitsToWrite: number | bigint) { const bits = typeof bitsToWrite === "number" ? BigInt(bitsToWrite) : bitsToWrite; if (bits <= 0n) return; const lastBitIndex = BigInt(this.writePointer) + bits - 1n; if (lastBitIndex < 0n) throw new Error("Bit index overflow"); const byteIndex = lastBitIndex >> 3n; if (byteIndex > BigInt(Number.MAX_SAFE_INTEGER)) { throw new Error("Stream too large"); } this.ensureCapacity(Number(byteIndex)); } private ensureCapacity(byteIndex: number) { const requiredSize = byteIndex + 1; if (requiredSize <= this.stream.length) return; let newSize = this.stream.length === 0 ? 32 : this.stream.length; while (newSize < requiredSize) { if (newSize > (0x7fffffff >> 1)) { newSize = requiredSize; break; } newSize <<= 1; } const next = new Uint8Array(newSize); next.set(this.stream); this.stream = next; } }