import { BitstreamReader as br, BitstreamWriter as bw } from '@astronautlabs/bitstream'; export const revbyte = (n: number, len = 8) => { let acc = 0; let n2 = n; let len2 = len; while (len2) { // can't use bitshifts or binray ops or else becomes negative acc = (acc * 2) + (n2 & 1); n2 >>= 1; len2--; } return acc; }; export class BitstreamReader { private buffers: Uint8Array[] = []; private bufferedLength = 0; private _offsetIntoBuffer = 0; private _bufferIndex = 0; private _offset = 0; /** * Get the index of the buffer currently being read. This will always be zero unless retainBuffers=true */ get bufferIndex() { return this._bufferIndex; } /** * Get the current offset in bits, starting from the very first bit read by this reader (across all * buffers added) */ get offset() { return this._offset; } /** * The number of bits that are currently available. */ get available() { return this.bufferedLength - this.skippedLength; } private skippedLength = 0; /** * Read an unsigned integer of the given bit length synchronously. If there are not enough * bits available, an error is thrown. * * @param length The number of bits to read * @returns The unsigned integer that was read */ getBit(offset: number) { const byte = this.buffers[0][offset >> 3]; return +!!(byte & (1 << (offset & 7))); } readSync(length: number): number { let value = 0; //console.log(this.buffers[0]) if (this._offset >> 3 > this.buffers[0].byteLength) { throw "Out of data"; } //const byte = this.buffers[0][this._offset >> 3]; for (let i = length - 1; i >= 0; --i) { value = value * 2 + this.getBit(this._offset + i); } this._offset += length; this.bufferedLength -= length; return value; } /** * Add a buffer onto the end of the bitstream. * @param buffer The buffer to add to the bitstream */ addBuffer(buffer: Uint8Array) { this.buffers.push(buffer); this.bufferedLength += buffer.length * 8; } } export type Writable = { write: (chunk: Uint8Array) => void; }; export class BitstreamWriter { /** * Create a new writer * @param stream The writable stream to write to * @param bufferSize The number of bytes to buffer before flushing onto the writable */ constructor(public stream: Writable, bufferSize = 1) { bufferSize = 1; this.buffer = new Uint8Array(bufferSize); } private pendingBits = 0; private buffer: Uint8Array; bufferoffset = 0; private _offset = 0; /** * How many bits have been written via this writer in total */ get offset() { return this._offset; } /** * How many bits into the current byte is the write cursor. * If this value is zero, then we are currently byte-aligned. * A value of 7 means we are 1 bit away from the byte boundary. */ get byteOffset() { return this.pendingBits; } /** * Finish the current byte (assuming zeros for the remaining bits, if necessary) * and flushes the output. */ end() { //this.finishByte(); this.flush(); } flush() { this.stream.write(new Uint8Array(this.buffer)); this.bufferoffset = 0; this.buffer.fill(0); } setBit(b: number) { if (b) debugger; let byte = this.buffer[0]; byte |= b << (this._offset & 7); this.buffer[0] = byte; this._offset += 1; if (++this.bufferoffset == this.buffer.length * 8) { this.flush(); } } /** * Write the given number to the bitstream with the given bitlength. If the number is too large for the * number of bits specified, the lower-order bits are written and the higher-order bits are ignored. * @param length The number of bits to write * @param value The number to write */ write(length: number, value: number) { while (length--) { this.setBit(value & 1); value >>= 1; } } }