For a recent project I was in need of excessive bit manipulation. Specifically, I had to write to or read from a variable number of bits up from the least significant bit towards the most significant bit.
I created the following helper class since I had spare time and wanted to learn something. I made it work but feel very unsure about whether it's performance might be sub optimal.
/*
* This class provides functions to read or write 1 to 8 bits to a stream at a time
* The bits a read from the least significant to the most significant
*
*/
public class BitStreamer : IDisposable {
/*
* Initializes a BitStreamer on a source Stream with a bps of 1
*/
public BitStreamer(Stream source) : base(source, 1) { }
public BitStreamer(Stream source, int bps) {
if(bps <= 0 | bps > 8)
throw new ArgumentException("The bits per sample has to be in the range of a byte (1 to 8)");
m_strSource = source;
m_iBitsPerSample = bps;
m_iPosition = 0;
m_iLength = source.Length;
m_bEndOfStream = false;
}
/* Bits per sample */
private int m_iBitsPerSample;
/* Base stream */
private Stream m_strSource;
/* Binary stream helpers */
private BinaryReader m_bstrSrc;
private BinaryWriter m_bstwSrc;
/* Byte stream fields */
private bool m_bEndOfStream;
private long m_iPosition;
private long m_iLength;
/* Bit stream fields */
private byte m_bCurrentByte;
private byte m_bInBytePosition;
private byte m_bCurrentBits;
Methods for bit writing
/*
* Write up to 8 bits to the current buffer
*/
public void WriteBits(byte b) {
m_bCurrentBits = 0;
for(int i = 0; i < m_iBitsPerSample; i++) {
byte bit = (byte)(b & (1 << i));
m_bCurrentBits = (byte)(m_bCurrentBits | bit);
WriteBit(bit);
}
}
/*
* Write a bit to the current byte buffer
*/
public void WriteBit(byte bit) {
if(m_bInBytePosition > 7)
WriteCurrentByte();
byte tar = m_bCurrentByte;
byte nbt = (byte)((tar & ~(1 << m_bInBytePosition)) | (bit << m_bInBytePosition));
m_bCurrentByte = nbt;
m_bInBytePosition += 1;
}
/*
* Writes the current byte and starts a new one
*/
protected void WriteCurrentByte() {
if(m_bstwSrc == null) m_bstwSrc = new BinaryWriter(m_strSource);
m_bstwSrc.Write(m_bCurrentByte);
m_bCurrentByte = 0;
m_bInBytePosition = 0;
m_iPosition += 1;
m_iLength = m_iPosition;
}
Methods for bit reading
/*
* Get up to 8 bits at a time within a byte
*/
public byte GetNextBits() {
byte result = GetNextBit();
for(int i = 1; i < m_iBitsPerSample; i++) {
result = (byte)((1 << result) & GetNextBit());
}
m_bCurrentBits = result;
return result;
}
/*
* Get the next bit of the stream OR 0xFF on EndOfStreamException
* Advance in byte stream if current byte is exhausted
*/
public byte GetNextBit() {
if(m_bInBytePosition > 7) // ReadNextByte does have side effects!
if(!ReadNextByte()) return 0xFF;
m_bCurrentBits = (byte)((m_bCurrentByte & (1 << m_bInBytePosition)) >> m_bInBytePosition);
m_bInBytePosition += 1;
return m_bCurrentBits;
}
/*
* Gets next byte of the stream and put's it into the buffer.
* Returns false on EndOfStreamException
*/
protected bool ReadNextByte() {
if(m_bEndOfStream) return false;
try {
if(m_bstrSrc == null) m_bstrSrc = new BinaryReader(m_strSource);
m_bCurrentByte = m_bstrSrc.ReadByte();
} catch(EndOfStreamException) {
m_bEndOfStream = true;
return false;
}
m_bInBytePosition = 0;
if(++m_iPosition >= m_iLength) m_bEndOfStream = true;
return true;
}
Some Properties
public bool AtEndOfStream { get { return m_bEndOfStream; } }
public long Position { get { return m_iPosition; } }
public long Length { get { return m_iLength; } }
public byte InBytePosition { get { return m_bInBytePosition; } }
public byte CurrentByte { get { return m_bCurrentByte; } }
public int BitsPerSample { get { return m_iBitsPerSample; } }
public Stream BaseStream { get { return m_strSource; } }
public virtual string Status {
get {
return String.Format("Current byte (0x{0}) = {1,8} and current bit(s) ({2}): {3,8}",
m_iPosition.ToString("X4"),
Convert.ToString(m_bCurrentByte, 2),
m_bInBytePosition.ToString(),
Convert.ToString(m_bCurrentBits, 2)
);
}
}
Handle IDisposable
public void Close() {
this.Dispose();
}
public void Dispose() {
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing) {
if (disposed) return;
if (disposing) {
if(m_strSource != null) {
m_strSource.Close();
m_bstrSrc = null;
m_bstwSrc = null;
m_strSource = null;
}
}
disposed = true;
}
private bool disposed = false;
}