Skip to main content
1 of 2
MrPaulch
  • 265
  • 2
  • 7

Another BitStreamer

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.

Here it goes:

/*
 * 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;
}
MrPaulch
  • 265
  • 2
  • 7