Files

143 lines
4.5 KiB
C#
Raw Permalink Normal View History

2026-04-04 13:18:04 -04:00
using System;
namespace SJK.Collections;
/// <summary>
/// Stores an Array of bits that can be retrieved by index. Can be used to return multiple bits as an int or long.
/// </summary>
public sealed class BitBuffer
{
// Number of bits in a byte
private const int BitsInByte = 8;
private const float BitsInByteFloat = 8f;
private byte[] data;
/// <summary>
/// Length of the bit buffer in bits.
/// </summary>
public int Length
{
get { return length; }
set
{
AppendNewArray(value);
length = value;
}
}
/// <summary>
/// Capacity of the bit buffer.
/// </summary>
public int Capacity { get { return data.Length; } }
private int length;
/// <summary>
/// Initializes a new instance of the BitBuffer class with the specified length in bits.
/// </summary>
/// <param name="length">The length of the bit buffer in bits.</param>
public BitBuffer(int length)
{
data = new byte[length / BitsInByte + 5];
}
/// <summary>
/// Initializes a new instance of the BitBuffer class with the specified byte array.
/// </summary>
/// <param name="_data">The byte array to initialize the bit buffer.</param>
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;
}
/// <summary>
/// Gets the bit at the specified index.
/// </summary>
/// <param name="index">The index of the bit.</param>
/// <returns>The value of the bit at the specified index.</returns>
public bool GetBit(long index)
{
return GetBits(index, 1) > 0;
}
/// <summary>
/// Sets the bit at the specified index to the specified value.
/// </summary>
/// <param name="index">The index of the bit.</param>
/// <param name="value">The value to set the bit to.</param>
public void SetBit(long index, bool value)
{
SetBits(index, value ? 1 : 0, 1);
}
/// <summary>
/// Gets the specified number of bits starting from the specified index.
/// </summary>
/// <param name="index">The starting index of the bits.</param>
/// <param name="bits">The number of bits to retrieve.</param>
/// <returns>The retrieved bits as an integer.</returns>
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);
}
}
/// <summary>
/// Sets the specified number of bits starting from the specified index to the specified value.
/// </summary>
/// <param name="index">The starting index of the bits.</param>
/// <param name="value">The value to set the bits to.</param>
/// <param name="bits">The number of bits to set.</param>
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;
}
}
/// <summary>
/// Gets the underlying byte array of the BitBuffer.
/// </summary>
/// <returns>The underlying byte array.</returns>
public byte[] GetData()
{
return data;
}
}