I noticed the parser pops data from the array, effectively consuming it. This blocks #9 and in turn #8.
I suggest rewriting it to a bit-container with random-access and a walking 'current pointer'.
Here's my implementation in Java that you should be able to cannibalize.
public final class BitContainer {
private final byte[] buffer;
private final int bitCapacity;
private int pointer = 0; // byte pointer
private int remainingBits = 8; // Always in the range [1, 8]
private BitContainer(byte[] buffer, int bitCapacity) {
this.buffer = requireNonNull(buffer, "buffer");
this.bitCapacity = bitCapacity;
}
public static BitContainer allocate(int bitCapacity) {
requireAtLeast(1, bitCapacity, "bitCapacity");
int capacity = (bitCapacity + 7) / 8;
byte[] buffer = new byte[capacity];
return new BitContainer(buffer, bitCapacity);
}
public int bitCapacity() { return bitCapacity; }
public int capacity() { return (bitCapacity + 7) / 8; }
public int remainingBits() { return bitCapacity - buffer.position() * 8 + remainingBits - 8; }
public void setPointer(int bitPosition) {
pointer = bitPosition / 8;
remainingBits = 8 - (bitPosition & 7);
}
public byte[] getArray(int numBits) {
requireThat((numBits & 7) == 0, "numBits not an integral number of bytes: " + numBits);
if (numBits > remainingBits())
throw new IndexOutOfBoundsException(numBits + " > " + remainingBits());
byte[] array = new byte[numBits / 8];
// Use more efficient method for byte-aligned bytes
if (remainingBits == 8) {
for (int i = 0; i < array.length; i++)
array[i] = buffer[pointer++];
} else {
for (int i = 0; i < array.length; i++)
array[i] = (byte)getBits(8);
}
return array;
}
public void putArray(byte[] array) {
requireNonNull(array, "array");
if (array.length * 8 > remainingBits())
throw new IndexOutOfBoundsException(array.length * 8 + " > " + remainingBits());
// Use more efficient method for byte-aligned bytes
if (remainingBits == 8) {
for (byte element : array)
buffer[pointer++] = element;
} else {
for (byte element : array)
putBits(8, element);
}
}
public int getBits(int numBits) {
requireRange(1, 32, numBits, "numBits");
if (numBits > remainingBits())
throw new IndexOutOfBoundsException(numBits + " > " + remainingBits());
if (numBits < remainingBits) {
// Get middle bits of this byte and don't advance byte pointer
byte value = buffer[pointer];
int result = extractBits(value, remainingBits - numBits, numBits);
remainingBits -= numBits;
return result;
}
int result;
if (remainingBits != 8) {
// Get all remaining bits of this byte and advance byte pointer
byte value = buffer[pointer++];
result = extractBits(value, 0, remainingBits);
numBits -= remainingBits;
} else {
result = 0;
}
// Get middle bytes and advance byte pointer
while (numBits >= 8) {
result <<= 8;
result |= buffer[pointer++] & 0xFF;
numBits -= 8;
}
if (numBits > 0) {
// Get remaining bits of this byte and don't advance byte pointer
byte value = buffer[pointer];
result <<= numBits;
result |= extractBits(value, 8 - numBits, numBits);
}
remainingBits = 8 - numBits;
return result;
}
public void putBits(int numBits, int bits) {
requireRange(1, 32, numBits, "numBits");
if (numBits > remainingBits())
throw new IndexOutOfBoundsException(numBits + " > " + remainingBits());
if (numBits < remainingBits) {
// Put all bits in the middle of this byte and don't advance byte pointer
byte value = buffer[pointer];
byte result = injectBits(bits, value, remainingBits - numBits, numBits);
buffer[pointer] = result;
remainingBits -= numBits;
return;
}
if (remainingBits != 8) {
// Fill remaining bits of this byte and advance byte pointer
byte value = buffer[pointer];
int highBits = extractBits(bits, numBits - remainingBits, remainingBits);
byte result = injectBits(highBits, value, 0, remainingBits);
numBits -= remainingBits;
buffer[pointer++] = result;
}
// Fill middle bytes and advance byte pointer
while (numBits >= 8) {
int value = extractBits(bits, numBits - 8, 8);
buffer[pointer++] = (byte)value;
numBits -= 8;
}
if (numBits > 0) {
// Put remaining bits in this byte and don't advance byte pointer
byte value = buffer[pointer];
int lowBits = extractBits(bits, 0, numBits);
byte result = injectBits(lowBits, value, 8 - numBits, numBits);
buffer[pointer] = result;
}
remainingBits = 8 - numBits;
}
private static int extractBits(byte value, int firstBit, int numBits) {
int mask = (1 << numBits) - 1;
return value >> firstBit & mask;
}
private static int extractBits(int value, int firstBit, int numBits) {
int mask = (1 << numBits) - 1;
return value >> firstBit & mask;
}
private static byte injectBits(int bits, byte value, int firstBit, int numBits) {
int mask = ((1 << numBits) - 1) << firstBit;
return (byte)((value & ~mask) | (bits << firstBit & mask));
}
}