Skip to content

Rewrite the data access using a pointer instead of popping from array #10

@MarkJeronimus

Description

@MarkJeronimus

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));
	}
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions