using System; namespace SJK.Collections; /// /// Stores an Array of bits that can be retrieved by index. Can be used to return multiple bits as an int or long. /// public sealed class BitBuffer { // Number of bits in a byte private const int BitsInByte = 8; private const float BitsInByteFloat = 8f; private byte[] data; /// /// Length of the bit buffer in bits. /// public int Length { get { return length; } set { AppendNewArray(value); length = value; } } /// /// Capacity of the bit buffer. /// public int Capacity { get { return data.Length; } } private int length; /// /// Initializes a new instance of the BitBuffer class with the specified length in bits. /// /// The length of the bit buffer in bits. public BitBuffer(int length) { data = new byte[length / BitsInByte + 5]; } /// /// Initializes a new instance of the BitBuffer class with the specified byte array. /// /// The byte array to initialize the bit buffer. public BitBuffer(byte[] _data) { data = _data; length = data.Length * BitsInByte; } private void AppendNewArray(int length) { int capacity = length / BitsInByte + 1; byte[] newData = new byte[capacity]; Array.Copy(data, newData, System.Math.Min(data.Length, newData.Length)); data = newData; } /// /// Gets the bit at the specified index. /// /// The index of the bit. /// The value of the bit at the specified index. public bool GetBit(long index) { return GetBits(index, 1) > 0; } /// /// Sets the bit at the specified index to the specified value. /// /// The index of the bit. /// The value to set the bit to. public void SetBit(long index, bool value) { SetBits(index, value ? 1 : 0, 1); } /// /// Gets the specified number of bits starting from the specified index. /// /// The starting index of the bits. /// The number of bits to retrieve. /// The retrieved bits as an integer. public unsafe int GetBits(long index, int bits) { #if DEBUG if (bits > 32) throw new ArgumentOutOfRangeException($"{nameof(BitBuffer)} does not support accessing more then size of int (32 bits) : {bits}"); if (bits <= 0) throw new ArgumentOutOfRangeException($"{nameof(BitBuffer)} cannot have zero or lower be the length of the bits : {bits}"); if (index <0) throw new IndexOutOfRangeException($"{nameof(BitBuffer)} cannot index a value lower then zero : {index}"); if (index >= length - bits) throw new IndexOutOfRangeException($"{nameof(BitBuffer)} cannot bit greater then the length of the bitBuffer : {index}, {bits}"); #endif long byteIndex = index / BitsInByte; int offset = (int)(index - byteIndex * BitsInByte); fixed (byte* d = &data[byteIndex]) { return (*((int*)d) >> offset) & ((1 << bits) - 1); } } /// /// Sets the specified number of bits starting from the specified index to the specified value. /// /// The starting index of the bits. /// The value to set the bits to. /// The number of bits to set. public unsafe void SetBits(long index, int value, int bits) { int mask = (1 << bits) - 1; int newValue = value; newValue &= mask; long byteIndex = index / BitsInByte; int offset = (int)(index - byteIndex * BitsInByte); fixed (byte* d = data) { int* l = (int*)(d + byteIndex); int v = *l; v = v & ~((mask << offset)); v = v | (newValue << offset); *(int*)(d + byteIndex) = v; } } /// /// Gets the underlying byte array of the BitBuffer. /// /// The underlying byte array. public byte[] GetData() { return data; } }