package org.andengine.util.adt.bit;
import org.andengine.util.adt.DataConstants;
/**
* (c) Zynga 2011
*
* @author Nicolas Gramlich <ngramlich@zynga.com>
* @since 15:56:32 - 30.10.2011
*/
public final class BitVector {
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
private final int mCapacity;
private final long[] mData;
// ===========================================================
// Constructors
// ===========================================================
private BitVector(final int pCapacity) {
if(pCapacity <= 0) {
throw new IllegalArgumentException("pCapacity must be > 0.");
}
this.mCapacity = pCapacity;
/* Check if bytes perfectly fit into the data fields or if there are some overflow bytes that need special treatment. */
final boolean perfectDataFit = pCapacity % DataConstants.BITS_PER_LONG == 0;
final int dataCapacity;
if(perfectDataFit) {
dataCapacity = pCapacity / DataConstants.BITS_PER_LONG;
} else {
/* Extra field for overflow bytes. */
dataCapacity = (pCapacity / DataConstants.BITS_PER_LONG) + 1;
}
this.mData = new long[dataCapacity];
}
public BitVector(final byte[] pBytes) {
this(pBytes.length * DataConstants.BITS_PER_BYTE);
final long[] data = this.mData;
/* Check if bytes perfectly fit into the data fields or if there are some overflow bytes that need special treatment. */
final boolean perfectDataFit = pBytes.length % DataConstants.BYTES_PER_LONG == 0;
final int dataCapacity = data.length;
final int lastCompleteDataIndex = (perfectDataFit) ? dataCapacity - 1 : dataCapacity - 2;
for(int i = lastCompleteDataIndex; i >= 0; i--) {
final int bytesOffset = i * DataConstants.BYTES_PER_LONG;
data[i] = ((((long)pBytes[bytesOffset + 0]) << 56) & 0xFF00000000000000L)
| ((((long)pBytes[bytesOffset + 1]) << 48) & 0xFF000000000000L)
| ((((long)pBytes[bytesOffset + 2]) << 40) & 0xFF0000000000L)
| ((((long)pBytes[bytesOffset + 3]) << 32) & 0xFF00000000L)
| ((((long)pBytes[bytesOffset + 4]) << 24) & 0xFF000000L)
| ((((long)pBytes[bytesOffset + 5]) << 16) & 0xFF0000L)
| ((((long)pBytes[bytesOffset + 6]) << 8) & 0xFF00L)
| ((((long)pBytes[bytesOffset + 7]) << 0) & 0xFFL);
}
/* Put overflow bytes into last data field. */
if(!perfectDataFit) {
long overflowData = 0;
final int overflowDataIndex = dataCapacity - 1;
final int overflowBytesOffset = overflowDataIndex * DataConstants.BYTES_PER_LONG;
final int overflowByteCount = pBytes.length - overflowBytesOffset;
switch(overflowByteCount) {
case 7:
overflowData = overflowData | ((((long)pBytes[overflowBytesOffset + 6]) << 8) & 0xFF00L);
case 6:
overflowData = overflowData | ((((long)pBytes[overflowBytesOffset + 5]) << 16) & 0xFF0000L);
case 5:
overflowData = overflowData | ((((long)pBytes[overflowBytesOffset + 4]) << 24) & 0xFF000000L);
case 4:
overflowData = overflowData | ((((long)pBytes[overflowBytesOffset + 3]) << 32) & 0xFF00000000L);
case 3:
overflowData = overflowData | ((((long)pBytes[overflowBytesOffset + 2]) << 40) & 0xFF0000000000L);
case 2:
overflowData = overflowData | ((((long)pBytes[overflowBytesOffset + 1]) << 48) & 0xFF000000000000L);
case 1:
overflowData = overflowData | ((((long)pBytes[overflowBytesOffset + 0]) << 56) & 0xFF00000000000000L);
}
data[overflowDataIndex] = overflowData;
}
}
// ===========================================================
// Getter & Setter
// ===========================================================
public int getCapacity() {
return this.mCapacity;
}
public boolean getBit(final int pPosition) {
if(pPosition < 0) {
throw new IllegalArgumentException("pPosition must be >= 0.");
}
if(pPosition >= this.mCapacity) {
throw new IllegalArgumentException("pPosition must be < capacity.");
}
final int dataIndex = pPosition / DataConstants.BITS_PER_LONG;
final int dataOffset = pPosition % DataConstants.BITS_PER_LONG;
final long dataField = this.mData[dataIndex];
final int rightShift = DataConstants.BITS_PER_LONG - dataOffset - 1;
final long bit = (dataField >> rightShift) & 0x01;
return bit != 0;
}
public byte getByte(final int pPosition) {
return (byte) this.getBits(pPosition, DataConstants.BITS_PER_BYTE);
}
public short getShort(final int pPosition) {
return (short) this.getBits(pPosition, DataConstants.BITS_PER_SHORT);
}
public int getInt(final int pPosition) {
return (int) this.getBits(pPosition, DataConstants.BITS_PER_INT);
}
public long getLong(final int pPosition) {
return this.getBits(pPosition, DataConstants.BITS_PER_LONG);
}
public long getBits(final int pPosition, final int pLength) {
/* Sanity checks. */
if(pPosition < 0) {
throw new IllegalArgumentException("pPosition must be >= 0.");
}
if(pLength < 0) {
throw new IllegalArgumentException("pLength must be >= 0.");
}
if(pPosition + pLength > this.mCapacity) {
throw new IllegalArgumentException("pPosition + pLength must be <= capacity.");
}
/* Early exit. */
if(pLength == 0) {
return 0L;
}
final int dataIndex = pPosition / DataConstants.BITS_PER_LONG;
final int offset = pPosition % DataConstants.BITS_PER_LONG;
final long data;
if(offset == 0) {
data = this.mData[dataIndex];
} else if(offset + pLength <= DataConstants.BITS_PER_LONG) {
data = this.mData[dataIndex] << offset;
} else {
/* Join bits from adjacent data fields. */
data = (this.mData[dataIndex] << offset) | (this.mData[dataIndex + 1] >>> (DataConstants.BITS_PER_LONG - offset));
}
if(pLength == DataConstants.BITS_PER_LONG) {
return data;
} else {
final int rightShift = DataConstants.BITS_PER_LONG - pLength;
final long mask = 0xFFFFFFFFFFFFFFFFL >>> rightShift;
final long unmaskedBits = data >> rightShift;
return unmaskedBits & mask;
}
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public String toString() {
final StringBuilder sb = new StringBuilder();
sb.append('[');
for(int i = 0; i < this.mCapacity; i++) {
sb.append(this.getBit(i) ? '1' : '0');
if(i % 8 == 7 && i < this.mCapacity -1) {
sb.append(' ');
}
}
sb.append(']');
return sb.toString();
}
// ===========================================================
// Methods
// ===========================================================
public byte[] toByteArray() {
final int byteArrayLength;
if(this.mCapacity % DataConstants.BITS_PER_BYTE == 0) {
byteArrayLength = this.mCapacity / DataConstants.BITS_PER_BYTE;
} else {
byteArrayLength = (this.mCapacity / DataConstants.BITS_PER_BYTE) + 1;
}
final byte[] bytes = new byte[byteArrayLength];
/* Check if bytes perfectly fit into the data fields or if there are some overflow bytes that need special treatment. */
final boolean perfectDataFit = this.mCapacity % DataConstants.BITS_PER_LONG == 0;
final long[] data = this.mData;
final int dataCapacity = data.length;
final int lastCompleteDataIndex = (perfectDataFit) ? dataCapacity - 1 : dataCapacity - 2;
int bytesOffset = lastCompleteDataIndex * DataConstants.BYTES_PER_LONG + (DataConstants.BYTES_PER_LONG - 1);
for(int i = lastCompleteDataIndex; i >= 0; i--) {
final long dataField = data[i];
bytes[bytesOffset--] = (byte) ((dataField >> 0) & 0xFF);
bytes[bytesOffset--] = (byte) ((dataField >> 8) & 0xFF);
bytes[bytesOffset--] = (byte) ((dataField >> 16) & 0xFF);
bytes[bytesOffset--] = (byte) ((dataField >> 24) & 0xFF);
bytes[bytesOffset--] = (byte) ((dataField >> 32) & 0xFF);
bytes[bytesOffset--] = (byte) ((dataField >> 40) & 0xFF);
bytes[bytesOffset--] = (byte) ((dataField >> 48) & 0xFF);
bytes[bytesOffset--] = (byte) ((dataField >> 56) & 0xFF);
}
/* Put overflow bytes into last data field. */
if(!perfectDataFit) {
final int overflowDataIndex = dataCapacity - 1;
final long overflowDataField = data[overflowDataIndex];
final int overflowBytesOffset = overflowDataIndex * DataConstants.BYTES_PER_LONG;
final int overflowByteCount = bytes.length % DataConstants.BYTES_PER_LONG;
switch(overflowByteCount) {
case 7:
bytes[overflowBytesOffset + 6] = (byte) ((overflowDataField >> 8) & 0xFF);
case 6:
bytes[overflowBytesOffset + 5] = (byte) ((overflowDataField >> 16) & 0xFF);
case 5:
bytes[overflowBytesOffset + 4] = (byte) ((overflowDataField >> 24) & 0xFF);
case 4:
bytes[overflowBytesOffset + 3] = (byte) ((overflowDataField >> 32) & 0xFF);
case 3:
bytes[overflowBytesOffset + 2] = (byte) ((overflowDataField >> 40) & 0xFF);
case 2:
bytes[overflowBytesOffset + 1] = (byte) ((overflowDataField >> 48) & 0xFF);
case 1:
bytes[overflowBytesOffset + 0] = (byte) ((overflowDataField >> 56) & 0xFF);
}
}
return bytes;
}
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}