0

I need to serialize integer values to a byte array Byte[] such that the most-significant-bit of the input value is also the most-significant-bit in the destination array. It also needs to allow for per-bit (not per-byte) offsets for storage locations.

Here's a simple example where I'm writing an integer (in both MSByte-first, MSbit-first-order) aligned to a byte-boundary:

Byte[] buffer = new Byte[99];
Encode( buffer, 0, 0x1234 ); // Encode the value 0x1234 inside buffer at bit-offset 0.
Assert.AreEqual( buffer[0], 0x12 ); // The integer value is stored most-significant-byte first
Assert.AreEqual( buffer[1], 0x34 );

Here's something similar, but offset by 1 bit:

Array.Initialize( buffer ); // Reset the buffer's bytes to 0.
Encode( buffer, 1, 0x0102 ); // Encode 0x0102 at bit-offset 1
// In binary 0x0102 is 00000001 00000010, but the final buffer will be this: 00000000 10000001 00000000
Assert.AreEqual( buffer[0], 0x00 ); // The first byte will be zero
Assert.AreEqual( buffer[1], 0x81 ); // 10000001 == 0x81
Assert.AreEqual( buffer[2], 0x00 );

I am currently using a BitArray instance, however BitArray reverses the bit-order within bytes, viz. bitArray[0] is the least-significant-bit of the first byte in its buffer, whereas I need it to be the most-significant-bit of the first byte in my buffer.

Dai
  • 141,631
  • 28
  • 261
  • 374

1 Answers1

2

The only way I could solve this was by implementing my own BitArray class which preserved most-to-least significant bit-order.

I felt I cheated because internally I used a Boolean[] to store bit values instead of Int32[] as BitArray does, but I'm happy with the performance I get.

Here's the logic I used:

/// <summary>A bit-array that works with bytes in big-endian order (that is, this[0] == big-endian-bit of the first byte).</summary>
public class BitArray2 : IEnumerable<Boolean> {

    private Boolean[] _bits;

    public BitArray2(Int32 byteCount, Boolean initialValue) {

        this._bits = new Boolean[ byteCount * 8 ];
        for(int i=0;i<this._bits.Length;i++) {
            this._bits[i] = initialValue;
        }
    }

    public BitArray2(Byte[] copyFrom) {

        this._bits = new Boolean[ copyFrom.Length * 8 ];

        for(int i=0;i<copyFrom.Length;i++) {
            Byte b = copyFrom[i];

            this._bits[ (i*8) + 0 ] = (b & 0x80) == 0x80;
            this._bits[ (i*8) + 1 ] = (b & 0x40) == 0x40;
            this._bits[ (i*8) + 2 ] = (b & 0x20) == 0x20;
            this._bits[ (i*8) + 3 ] = (b & 0x10) == 0x10;
            this._bits[ (i*8) + 4 ] = (b & 0x08) == 0x08;
            this._bits[ (i*8) + 5 ] = (b & 0x04) == 0x04;
            this._bits[ (i*8) + 6 ] = (b & 0x02) == 0x02;
            this._bits[ (i*8) + 7 ] = (b & 0x01) == 0x01;
        }
    }

    public Boolean this[Int32 bitIndex] {
        get { return this._bits[bitIndex]; }
        set { this._bits[bitIndex] = value; }
    }

    public void CopyTo(Byte[] buffer) {

        for(int i=0;i<_bits.Length;i+=8) {

            Int32 destIndex = i / 8;

            Byte b = 0;
            if( this._bits[ i + 0 ] ) b |= 0x80;
            if( this._bits[ i + 1 ] ) b |= 0x40;
            if( this._bits[ i + 2 ] ) b |= 0x20;
            if( this._bits[ i + 3 ] ) b |= 0x10;
            if( this._bits[ i + 4 ] ) b |= 0x08;
            if( this._bits[ i + 5 ] ) b |= 0x04;
            if( this._bits[ i + 6 ] ) b |= 0x02;
            if( this._bits[ i + 7 ] ) b |= 0x01;

            buffer[ destIndex ] = b;
        }
    }

    public IEnumerator<Boolean> GetEnumerator() {
        // See http://stackoverflow.com/questions/1272673/obtain-generic-enumerator-from-an-array
        return ((IEnumerable<Boolean>)this._bits).GetEnumerator();
    }
    IEnumerator IEnumerable.GetEnumerator() {
        return this.GetEnumerator();
    }
}
Dai
  • 141,631
  • 28
  • 261
  • 374
  • I just discovered the BitArray and was dismayed to find this to be an issue. I'd be curious to find someone who could explain why it should be this way. – Len White Dec 24 '20 at 01:02