package net.glowstone.util; import org.apache.commons.lang3.Validate; import java.util.Arrays; /** * An array of nibbles (4-bit values) stored efficiently as a byte array of * half the size. The even indices are stored in the least significant nibble * and the odd indices in the most significant bits. * * For example, [1 5 8 15] is stored as [0x51 0xf8]. */ public final class NibbleArray { private final byte[] data; /** * Construct a new NibbleArray with the given size in nibbles. * @param size The number of nibbles in the array. * @throws IllegalArgumentException If size is not positive and even. */ public NibbleArray(int size) { Validate.isTrue(size > 0 && size % 2 == 0, "size must be positive even number, not " + size); data = new byte[size / 2]; } /** * Construct a new NibbleArray using the given underlying bytes. No copy * is created. * @param data The raw data to use. */ public NibbleArray(byte[] data) { this.data = data; } /** * Get the size in nibbles. * @return The size in nibbles. */ public int size() { return 2 * data.length; } /** * Get the size in bytes, one-half the size in nibbles. * @return The size in bytes. */ public int byteSize() { return data.length; } /** * Get the nibble at the given index. * @param index The nibble index. * @return The value of the nibble at that index. */ public byte get(int index) { byte val = data[index / 2]; if (index % 2 == 0) { return (byte) (val & 0x0f); } else { return (byte) ((val & 0xf0) >> 4); } } /** * Set the nibble at the given index to the given value. * @param index The nibble index. * @param value The new value to store. */ public void set(int index, byte value) { value &= 0xf; int half = index / 2; byte previous = data[half]; if (index % 2 == 0) { data[half] = (byte) ((previous & 0xf0) | value); } else { data[half] = (byte) ((previous & 0x0f) | (value << 4)); } } /** * Fill the nibble array with the specified value. * @param value The value nibble to fill with. */ public void fill(byte value) { value &= 0xf; Arrays.fill(data, (byte) ((value << 4) | value)); } /** * Get the raw bytes of this nibble array. Modifying the returned array * will modify the internal representation of this nibble array. * @return The raw bytes. */ public byte[] getRawData() { return data; } /** * Copies into the raw bytes of this nibble array from the given source. * @param source The array to copy from. * @throws IllegalArgumentException If source is not the correct length. */ public void setRawData(byte[] source) { Validate.isTrue(source.length == data.length, "expected byte array of length " + data.length + ", not " + source.length); System.arraycopy(source, 0, data, 0, source.length); } /** * Take a snapshot of this NibbleArray which will not reflect changes. * @return The snapshot NibbleArray. */ public NibbleArray snapshot() { return new NibbleArray(data.clone()); } }