2
\$\begingroup\$

This is a fraction of the binary writer I'm writing, and I'm trying find some way to improve it.

using System;
using System.Collections.Generic;

public class ByteBuffer
{
    // List used to hold all bytes that will be read
    private List<byte> buffer = new List<byte>(32);

    private int bitIndex = 0;

    /// <summary>
    /// Writes an n bits byte onto the buffer.
    /// </summary>
    public void Write(byte source, int n)
    {
        if ((n + bitIndex) / 8 > buffer.Count)
        {
            buffer.AddRange(new byte[(n + bitIndex) / 8 - buffer.Count]);
        }

        for (int i = 0; i < n; i++)
        {
            buffer[(bitIndex + i) / 8] |= (byte)(((source >> (n - 1 - i)) & 1) << (int)(7 - (bitIndex + i) % 8));
        }

        bitIndex += n;
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ For what you're doing here, working with data at the bit level is overkill IMHO. The intrinsic type is the byte so you should work at that level at a minimum. You won't gain much in terms of efficiency and maintainability, in fact, it's quite the opposite. You're making it harder on yourself unnecessarily. Besides, that's why there's the BitArray type. \$\endgroup\$ Commented Jul 21, 2012 at 22:45
  • 2
    \$\begingroup\$ @JeffMercado In some cases, this is exactly what you need to do. For example, when implementing Huffman coding or some other compression algorithm. \$\endgroup\$
    – svick
    Commented Jul 22, 2012 at 0:54

3 Answers 3

1
\$\begingroup\$

I'm going to go out on a limb, not knowing the full extent of the use cases of the class, but there's a pretty decent class, BitArray that might handle your needs as such:

using System;
using System.Collections;

public class ByteBuffer
{
    // List used to hold all bytes that will be read
    private BitArray buffer;

    /// <summary>
    /// Writes an n bits byte onto the buffer.
    /// </summary>
    public void Write(byte source, int n)
    {
        buffer = Append(buffer, source, n);
    }

    private static BitArray Append(BitArray current, byte source, int n)
    {
        var count = current == null ? 0 : current.Count;
        var bools = new bool[count + n];

        if (count > 0)
        {
            current.CopyTo(bools, 0);
        }

        if (n > 0)
        {
            var after = new BitArray(new[] { source });

            for (int i = 0; i < n; i++)
            {
                bools[count + i] = after[i];
            }
        }

        return new BitArray(bools);
    }
}
\$\endgroup\$
1
\$\begingroup\$

If you're after efficiency, you shouldn't write bit by bit. For example, if bitIndex is currently 3 and you're writing the full 8 bits, just two steps are necessary: writing 5 bits to one byte and then the remaining 3 bits to another byte.

And depending on what you do, writing directly to some Stream might make more sense than using a List<byte>.

Also, you should probably check the input and throw and exception if n is not between 1 and 8.

\$\endgroup\$
2
  • \$\begingroup\$ Well, about using a stream, I'm only using the List<byte> because of the automatic expansion and Add/AddRange, to exclude the need of a WriteIndex field. Would the stream overhead, in fact, worth it? \$\endgroup\$
    – user15111
    Commented Jul 21, 2012 at 21:42
  • \$\begingroup\$ I mean, you're most likely going to write the result to a file or network anyway in the end, no? If that's the case, it would be more efficient to write it there as soon as possible. \$\endgroup\$
    – svick
    Commented Jul 21, 2012 at 21:48
1
\$\begingroup\$

I ended up with an implementation that looks like this:

using System.Diagnostics;

public class ByteBuffer
{
    public byte[] tempBuffer = new byte[16];

    public int tempIndex = 0;

    /// <summary>
    /// Writes the given number of bits.
    /// </summary>
    public void Write(byte value, int bits)
    {
        Debug.Assert(bits > 0 && bits < 9, "Number of bits must be between 1 and 8.");

        int localBitLen = (tempIndex % 8);
        if (localBitLen == 0)
        {
            tempBuffer[tempIndex >> 3] = value;
            tempIndex += bits;
            return;
        }

        tempBuffer[tempIndex >> 3] &= (byte)(255 >> (8 - localBitLen)); // clear before writing
        tempBuffer[tempIndex >> 3] |= (byte)(value << localBitLen); // write first half


        // need write into next byte?
        if (localBitLen + bits > 8)
        {
            tempBuffer[(tempIndex >> 3) + 1] &= (byte)(255 << localBitLen); // clear before writing
            tempBuffer[(tempIndex >> 3) + 1] |= (byte)(value >> (8 - localBitLen)); // write second half
        }

        tempIndex += bits;
    }
}

I was able to do it with the help of some calculators (I just hate being bad with bitwise operations).

I had to use a byte[] rather than a stream since I can't write directly on the stream back-buffer. I might implement the Read, and exponential expansion for the buffer tomorrow.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.