using System; namespace CUETools.Codecs; public unsafe class BitReader { byte* bptr_m; int buffer_len_m; ulong cache_m; ushort crc16_m; int have_bits_m; public BitReader() { Buffer = null; bptr_m = null; buffer_len_m = 0; have_bits_m = 0; cache_m = 0; crc16_m = 0; } public BitReader(byte* _buffer, int _pos, int _len) { Reset(_buffer, _pos, _len); } public int Position => (int)(bptr_m - Buffer - (have_bits_m >> 3)); public byte* Buffer { get; private set; } public void Reset(byte* _buffer, int _pos, int _len) { Buffer = _buffer; bptr_m = _buffer + _pos; buffer_len_m = _len; have_bits_m = 0; cache_m = 0; crc16_m = 0; fill(); } public void fill() { while(have_bits_m < 56) { have_bits_m += 8; byte b = *bptr_m++; cache_m |= (ulong)b << 64 - have_bits_m; crc16_m = (ushort)(crc16_m << 8 ^ Crc16.table[crc16_m >> 8 ^ b]); } } /* skip any number of bits */ public void skipbits(int bits) { while(bits > have_bits_m) { bits -= have_bits_m; cache_m = 0; have_bits_m = 0; fill(); } cache_m <<= bits; have_bits_m -= bits; } public long read_long() => (long)readbits(32) << 32 | readbits(32); public ulong read_ulong() => (ulong)readbits(32) << 32 | readbits(32); public int read_int() => (int)readbits(sizeof(int)); public uint read_uint() => readbits(sizeof(uint)); public short read_short() => (short)readbits(16); public ushort read_ushort() => (ushort)readbits(16); /* supports reading 1 to 32 bits, in big endian format */ public uint readbits(int bits) { fill(); var result = (uint)(cache_m >> 64 - bits); skipbits(bits); return result; } /* supports reading 1 to 64 bits, in big endian format */ public ulong readbits64(int bits) { if(bits <= 56) return readbits(bits); return (ulong)readbits(32) << bits - 32 | readbits(bits - 32); } /* reads a single bit */ public uint readbit() => readbits(1); public uint read_unary() { fill(); uint val = 0; ulong result = cache_m >> 56; while(result == 0) { val += 8; cache_m <<= 8; byte b = *bptr_m++; cache_m |= (ulong)b << 64 - have_bits_m; crc16_m = (ushort)(crc16_m << 8 ^ Crc16.table[crc16_m >> 8 ^ b]); result = cache_m >> 56; } val += byte_to_unary_table[result]; skipbits((int)(val & 7) + 1); return val; } public void flush() { if((have_bits_m & 7) > 0) { cache_m <<= have_bits_m & 7; have_bits_m -= have_bits_m & 7; } } public ushort get_crc16() { if(have_bits_m == 0) return crc16_m; ushort crc = 0; int n = have_bits_m >> 3; for(var i = 0; i < n; i++) crc = (ushort)(crc << 8 ^ Crc16.table[crc >> 8 ^ (byte)(cache_m >> 56 - (i << 3))]); return Crc16.Subtract(crc16_m, crc, n); } public int readbits_signed(int bits) { var val = (int)readbits(bits); val <<= 32 - bits; val >>= 32 - bits; return val; } public uint read_utf8() { uint x = readbits(8); uint v; int i; if(0 == (x & 0x80)) { v = x; i = 0; } else if(0xC0 == (x & 0xE0)) /* 110xxxxx */ { v = x & 0x1F; i = 1; } else if(0xE0 == (x & 0xF0)) /* 1110xxxx */ { v = x & 0x0F; i = 2; } else if(0xF0 == (x & 0xF8)) /* 11110xxx */ { v = x & 0x07; i = 3; } else if(0xF8 == (x & 0xFC)) /* 111110xx */ { v = x & 0x03; i = 4; } else if(0xFC == (x & 0xFE)) /* 1111110x */ { v = x & 0x01; i = 5; } else if(0xFE == x) /* 11111110 */ { v = 0; i = 6; } else throw new Exception("invalid utf8 encoding"); for(; i > 0; i--) { x = readbits(8); if(0x80 != (x & 0xC0)) /* 10xxxxxx */ throw new Exception("invalid utf8 encoding"); v <<= 6; v |= x & 0x3F; } return v; } public void read_rice_block(int n, int k, int* r) { fill(); fixed(byte* unary_table = byte_to_unary_table) { fixed(ushort* t = Crc16.table) { uint mask = (1U << k) - 1; byte* bptr = bptr_m; int have_bits = have_bits_m; ulong cache = cache_m; ushort crc = crc16_m; for(int i = n; i > 0; i--) { uint bits; byte* orig_bptr = bptr; while((bits = unary_table[cache >> 56]) == 8) { cache <<= 8; byte b = *bptr++; cache |= (ulong)b << 64 - have_bits; crc = (ushort)(crc << 8 ^ t[crc >> 8 ^ b]); } uint msbs = bits + ((uint)(bptr - orig_bptr) << 3); // assumes k <= 41 (have_bits < 41 + 7 + 1 + 8 == 57, so we don't loose bits here) while(have_bits < 56) { have_bits += 8; byte b = *bptr++; cache |= (ulong)b << 64 - have_bits; crc = (ushort)(crc << 8 ^ t[crc >> 8 ^ b]); } int btsk = k + (int)bits + 1; uint uval = msbs << k | (uint)(cache >> 64 - btsk & mask); cache <<= btsk; have_bits -= btsk; *r++ = (int)(uval >> 1 ^ -(int)(uval & 1)); } have_bits_m = have_bits; cache_m = cache; bptr_m = bptr; crc16_m = crc; } } } #region Static Methods public static int log2i(int v) => log2i((uint)v); public static readonly byte[] MultiplyDeBruijnBitPosition = [ 0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31 ]; public static int log2i(ulong v) { v |= v >> 1; // first round down to one less than a power of 2 v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; if(v >> 32 == 0) return MultiplyDeBruijnBitPosition[(uint)v * 0x07C4ACDDU >> 27]; return 32 + MultiplyDeBruijnBitPosition[(uint)(v >> 32) * 0x07C4ACDDU >> 27]; } public static int log2i(uint v) { v |= v >> 1; // first round down to one less than a power of 2 v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; return MultiplyDeBruijnBitPosition[v * 0x07C4ACDDU >> 27]; } public static readonly byte[] byte_to_unary_table = [ 8, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ]; #endregion }