blob: 7a75280334f008e2df207c309ff8efc54a91d6b9 [file] [log] [blame] [raw]
package net.glowstone.util;
import lombok.Getter;
import java.util.Arrays;
import static com.google.common.base.Preconditions.checkArgument;
/**
* An array of nibbles (4-bit values) stored efficiently as a byte array of half the size.
*
* <p>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 {
/**
* 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.
*/
@Getter
private final byte[] rawData;
/**
* 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) {
this(size, (byte) 0);
}
/**
* Construct a new NibbleArray with the given size in nibble, filled with the specified nibble
* value.
*
* @param size The number of nibbles in the array.
* @param value The value to fill the array with.
* @throws IllegalArgumentException If size is not positive and even.
*/
public NibbleArray(int size, byte value) {
checkArgument(size > 0 && size % 2 == 0, "size must be positive even number, not " + size);
rawData = new byte[size / 2];
if (value != 0) {
fill(value);
}
}
/**
* Construct a new NibbleArray using the given underlying bytes. No copy is created.
*
* @param rawData The raw data to use.
*/
public NibbleArray(byte... rawData) {
this.rawData = rawData;
}
/**
* Get the size in nibbles.
*
* @return The size in nibbles.
*/
public int size() {
return 2 * rawData.length;
}
/**
* Get the size in bytes, one-half the size in nibbles.
*
* @return The size in bytes.
*/
public int byteSize() {
return rawData.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 = rawData[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 = rawData[half];
if (index % 2 == 0) {
rawData[half] = (byte) (previous & 0xf0 | value);
} else {
rawData[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(rawData, (byte) (value << 4 | value));
}
/**
* 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) {
checkArgument(
source.length == rawData.length,
"expected byte array of length " + rawData.length + ", not " + source.length);
System.arraycopy(source, 0, rawData, 0, source.length);
}
/**
* Take a snapshot of this NibbleArray which will not reflect changes.
*
* @return The snapshot NibbleArray.
*/
public NibbleArray snapshot() {
return new NibbleArray(rawData.clone());
}
}