Splitting byte array into multiple binary chunks

Viewed 68

I have a binary file that I want to read. Let's say its "foo.bin". I want to read all the bytes into an byte array.
byte[] data = File.ReadAllBytes("foo.bin")
I want to split these bytes into binary numbers of length x.
(Using a custom class BinaryChunk, but any type of binary / bool[] / byte[] will work fine.)

struct BinaryChunk { 
    bool[] data,
    int size
} 

BinaryChunk[] binaryChunks = SplitDataIntoBinaryChunksOfSize(data, x)

*Note that "x" is in bits, not bytes.

Examples in case you still have no idea what I'm talking about:
(Shortened "SplitDataIntoBinaryChunksOfSize" into "SplitData" for obvious reasons)

bool[] foo = new bool[]{0b11100110, 0b00010110};
SplitData(foo, 3) //-> 111, 001, 100, 001, 011, 000
//   1 1 1 0 0 1 1 0;0 0 0 1 0 1 1 0XXXXXXXXXXXXXXXX
//-> 1 1 1;0 0 1;1 0 0;0 0 1;0 1 1;0 0 0
SplitData(foo, 12) //-> 111001100001, 011000000000
//   1 1 1 0 0 1 1 0;0 0 0 1 0 1 1 0XXXXXXXXXXXXXXXX
//-> 1 1 1 0 0 1 1 0 0 0 0 1;0 1 1 0 0 0 0 0 0 0 0 0

Thinking about using BitArray, but I haven't used it enough to know how to use it. Anyone got any ideas?

EDIT: heres a quick solution if anyone needs one i guess

class BinaryStream {
            //binary data in bytes
            byte[] data;
            //length of data
            int size;

            public BinaryStream(byte[] data) {
                this.data = data;
                this.size = data.Length;
            }

            public void MoveBytes(int splicesize) {
                //moves the binarystream to the left (<<) in bytes
                size -= splicesize;
                byte[] cutdata = new byte[size];
                for (int i = 0; i < size; i++) { cutdata[i] = data[i + splicesize]; }
                data = cutdata;
            }

            public byte this[int membytes] {
                //gets the byte at index unless its not in the array then its 0
                get { return membytes < size ? data[membytes] : byte.MinValue; }
                set { if (membytes < size) data[membytes] = value; }
            }

            public static BinaryStream operator <<(BinaryStream binstream, int membits) {
                //a left shift operator (in bits)
                binstream.MoveBytes(membits / 8);
                membits %= 8;
                if (membits == 0) return binstream;

                for (int i = 0; i < binstream.size; i++) {
                    //moves the byte at index i, then grabs the next item and appends it
                    binstream[i] <<= membits;
                    binstream[i] |= (byte) (binstream[i + 1] >> (8 - membits));
                }
                return binstream;
            }
        }

Above is a simple BinaryStream class (a bit similar to a Queue<bool>), doesn't have much functionality yet since its just for helping to solve the problem.

Temporary Solution:

byte[][] SplitData(byte[] data, int bits) {
        //setup
        byte[][] split = new byte[data.Length * 8 / bits][];
        int chunkSize = bits / 8 + 1; //chunksize in bytes
        BinaryStream bstream = new BinaryStream(data);

        for (int i = 0; i < split.Length; i++) {
            split[i] = new byte[chunkSize];
            int copy = bits; //number of bits to copy
            int j = 0; //index at split[] to copy to
            while (copy > 0) {
                if (copy < 8) {
                    //copy less than a byte
                    split[i][j] = (byte) ((bstream[0] & (255 << (8 - copy))) >> (8 - copy));
                    bstream <<= copy;
                } else {
                    //copy a full byte
                    split[i][j] = bstream[0];
                    bstream.MoveBytes(1);
                }
                copy -= 8;
                j++;
            }
        }
        return split;
    }

Time Complexity: O(n^2) where n is the data length. Space Complexity: O(n) where n is the data length.

Not the best solution nor elegant, but it works for now.

1 Answers

Solution using IEnumerables:

IEnumerable<bool[]> SplitDataIntoBinaryChunksOfSize(byte[] data, int x)
{
    var currentId = 0;
    var currentArray = new bool[x];
    foreach (var bit in EnumerateBits(data))
    {
        currentArray[currentId] = bit;
        currentId++;
        if(currentId == x)
        {
            yield return currentArray;
            currentArray = new bool[x];
            currentId = 0;
        }
    }
    if(currentId > 0)
        yield return currentArray;
    
}

private static readonly byte[] bits = { 128, 64, 32, 16, 8, 4, 2, 1 };

IEnumerable<bool> EnumerateBits(byte[] data)
{
    foreach (var d in data)
        foreach (var b in bits)
            yield return Convert.ToBoolean(d & b);
}

And in .Net 6 it's even simpler:

IEnumerable<bool[]> SplitDataIntoBinaryChunksOfSize(byte[] data, int x)
{
    return Enumerable.Chunk(EnumerateBits(data), x);
}

private static readonly byte[] bits = { 128, 64, 32, 16, 8, 4, 2, 1 };

IEnumerable<bool> EnumerateBits(byte[] data)
{
    foreach (var d in data)
        foreach (var b in bits)
            yield return Convert.ToBoolean(d & b);
}

But in that solution the last array will be smaller if there's leftover data.

Related