package org.andengine.util.adt.bit;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.util.Arrays;
import org.andengine.util.adt.data.constants.DataConstants;
import org.andengine.util.exception.MethodNotYetImplementedException;
/**
* (c) 2011 Zynga Inc.
*
* @author Nicolas Gramlich <ngramlich@zynga.com>
* @since 15:56:32 - 30.10.2011
*/
public class LongBackedBitVector extends BitVector {
// ===========================================================
// Constants
// ===========================================================
// ===========================================================
// Fields
// ===========================================================
private final int mSize;
private final long[] mData;
// ===========================================================
// Constructors
// ===========================================================
public LongBackedBitVector(final int pSize) throws IllegalArgumentException {
if (pSize <= 0) {
throw new IllegalArgumentException("pSize must be > 0.");
}
this.mSize = pSize;
/* Check if bytes perfectly fit into the data fields or if there are some overflow bytes that need special treatment. */
final boolean perfectDataFit = (pSize % DataConstants.BITS_PER_LONG) == 0;
final int dataLength;
if (perfectDataFit) {
dataLength = pSize / DataConstants.BITS_PER_LONG;
} else {
/* Extra field for overflow bytes. */
dataLength = (pSize / DataConstants.BITS_PER_LONG) + 1;
}
this.mData = new long[dataLength];
}
public LongBackedBitVector(final long[] pData) throws IllegalArgumentException {
throw new MethodNotYetImplementedException();
}
public LongBackedBitVector(final int pSize, final long[] pData) throws IllegalArgumentException {
if (pData == null) {
throw new IllegalArgumentException("pData must not be null");
}
if (pSize > (pData.length * Long.SIZE)) {
throw new IllegalArgumentException("pData is too short.");
}
if (BitVector.calculateLongSize(pSize) < pData.length) {
throw new IllegalArgumentException("pData is too long.");
}
throw new MethodNotYetImplementedException();
}
public LongBackedBitVector(final byte[] pData) throws IllegalArgumentException, NullPointerException {
this(pData.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 = (pData.length % DataConstants.BYTES_PER_LONG) == 0;
final int dataLength = data.length;
final int lastCompleteDataIndex = (perfectDataFit) ? dataLength - 1 : dataLength - 2;
for (int i = lastCompleteDataIndex; i >= 0; i--) {
final int bytesOffset = i * DataConstants.BYTES_PER_LONG;
data[i] = ((((long)pData[bytesOffset + 0]) << 56) & 0xFF00000000000000L)
| ((((long)pData[bytesOffset + 1]) << 48) & 0xFF000000000000L)
| ((((long)pData[bytesOffset + 2]) << 40) & 0xFF0000000000L)
| ((((long)pData[bytesOffset + 3]) << 32) & 0xFF00000000L)
| ((((long)pData[bytesOffset + 4]) << 24) & 0xFF000000L)
| ((((long)pData[bytesOffset + 5]) << 16) & 0xFF0000L)
| ((((long)pData[bytesOffset + 6]) << 8) & 0xFF00L)
| ((((long)pData[bytesOffset + 7]) << 0) & 0xFFL);
}
/* Put overflow bytes into last data field. */
if (!perfectDataFit) {
long overflowData = 0;
final int overflowDataIndex = dataLength - 1;
final int overflowBytesOffset = overflowDataIndex * DataConstants.BYTES_PER_LONG;
final int overflowByteCount = pData.length - overflowBytesOffset;
switch (overflowByteCount) {
case 7:
overflowData = overflowData | ((((long)pData[overflowBytesOffset + 6]) << 8) & 0xFF00L);
case 6:
overflowData = overflowData | ((((long)pData[overflowBytesOffset + 5]) << 16) & 0xFF0000L);
case 5:
overflowData = overflowData | ((((long)pData[overflowBytesOffset + 4]) << 24) & 0xFF000000L);
case 4:
overflowData = overflowData | ((((long)pData[overflowBytesOffset + 3]) << 32) & 0xFF00000000L);
case 3:
overflowData = overflowData | ((((long)pData[overflowBytesOffset + 2]) << 40) & 0xFF0000000000L);
case 2:
overflowData = overflowData | ((((long)pData[overflowBytesOffset + 1]) << 48) & 0xFF000000000000L);
case 1:
overflowData = overflowData | ((((long)pData[overflowBytesOffset + 0]) << 56) & 0xFF00000000000000L);
}
data[overflowDataIndex] = overflowData;
}
}
public LongBackedBitVector(final DataInputStream pDataInputStream) throws IOException {
this.mSize = pDataInputStream.readInt();
this.mData = new long[BitVector.calculateLongSize(this.mSize)];
throw new MethodNotYetImplementedException();
// pDataInputStream.readFully(this.mData);
}
public static LongBackedBitVector load(final DataInputStream pDataInputStream) throws IOException {
return new LongBackedBitVector(pDataInputStream);
}
// ===========================================================
// Getter & Setter
// ===========================================================
@Override
public int getSize() {
return this.mSize;
}
@Override
public int getBit(final int pIndex) throws IllegalArgumentException {
if (pIndex < 0) {
throw new IllegalArgumentException("pIndex must be >= 0.");
}
if (pIndex >= this.mSize) {
throw new IllegalArgumentException("pIndex must be < size.");
}
final int dataIndex = pIndex / DataConstants.BITS_PER_LONG;
final int dataOffset = pIndex % DataConstants.BITS_PER_LONG;
final long dataField = this.mData[dataIndex];
final int rightShift = DataConstants.BITS_PER_LONG - dataOffset - 1;
final long dataBit = (dataField >> rightShift) & 0x01;
final int bit = (int)dataBit;
return bit;
}
@Override
public boolean getBitAsBoolean(final int pIndex) throws IllegalArgumentException {
return this.getBit(pIndex) == BitVector.TRUE;
}
@Override
public void setBit(final int pIndex) throws IllegalArgumentException {
throw new MethodNotYetImplementedException();
}
@Override
public void clearBit(final int pIndex) throws IllegalArgumentException {
throw new MethodNotYetImplementedException();
}
@Override
public void setBit(final int pIndex, final boolean pTrue) throws IllegalArgumentException {
throw new MethodNotYetImplementedException();
}
@Override
public int getBits(final int pIndex, final int pLength) throws IllegalArgumentException {
throw new MethodNotYetImplementedException();
}
@Override
public long getLongBits(final int pIndex, final int pLength) throws IllegalArgumentException {
/* Sanity checks. */
if (pIndex < 0) {
throw new IllegalArgumentException("pIndex must be >= 0.");
}
if (pLength < 0) {
throw new IllegalArgumentException("pLength must be >= 0.");
}
if ((pIndex + pLength) > this.mSize) {
throw new IllegalArgumentException("pIndex + pLength must be <= size.");
}
/* Early exit. */
if (pLength == 0) {
return 0L;
}
final int dataIndex = pIndex / DataConstants.BITS_PER_LONG;
final int offset = pIndex % 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;
}
}
@Override
public void setBits(final int pIndex, final byte pBits, final int pBitIndex, final int pBitCount) throws IllegalArgumentException {
throw new MethodNotYetImplementedException();
}
@Override
public void setBits(final int pIndex, final short pBits, final int pBitIndex, final int pBitCount) throws IllegalArgumentException {
throw new MethodNotYetImplementedException();
}
@Override
public void setBits(final int pIndex, final int pBits, final int pBitIndex, final int pBitCount) throws IllegalArgumentException {
throw new MethodNotYetImplementedException();
}
@Override
public byte getByte(final int pIndex) throws IllegalArgumentException {
return (byte) this.getBits(pIndex, DataConstants.BITS_PER_BYTE);
}
@Override
public void setByte(final int pIndex, final byte pByte) throws IllegalArgumentException {
throw new MethodNotYetImplementedException();
}
@Override
public short getShort(final int pIndex) throws IllegalArgumentException {
return (short) this.getBits(pIndex, DataConstants.BITS_PER_SHORT);
}
@Override
public void setShort(final int pIndex, final short pShort) throws IllegalArgumentException {
throw new MethodNotYetImplementedException();
}
@Override
public int getInt(final int pIndex) throws IllegalArgumentException {
return this.getBits(pIndex, DataConstants.BITS_PER_INT);
}
@Override
public void setInt(final int pIndex, final int pInt) throws IllegalArgumentException {
throw new MethodNotYetImplementedException();
}
@Override
public long getLong(final int pIndex) throws IllegalArgumentException {
return this.getLongBits(pIndex, DataConstants.BITS_PER_LONG);
}
@Override
public void setLong(final int pIndex, final long pLong) throws IllegalArgumentException {
throw new MethodNotYetImplementedException();
}
@Override
public void clear() {
Arrays.fill(this.mData, 0x00);
}
@Override
public void fill(final byte pByte) {
throw new MethodNotYetImplementedException();
}
// ===========================================================
// Methods for/from SuperClass/Interfaces
// ===========================================================
@Override
public void save(final DataOutputStream pDataOutputStream) throws IOException {
throw new MethodNotYetImplementedException();
}
@Override
public byte[] toByteArray() {
final int byteArrayLength;
if ((this.mSize % DataConstants.BITS_PER_BYTE) == 0) {
byteArrayLength = this.mSize / DataConstants.BITS_PER_BYTE;
} else {
byteArrayLength = (this.mSize / 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.mSize % DataConstants.BITS_PER_LONG) == 0;
final long[] data = this.mData;
final int dataLength = data.length;
final int lastCompleteDataIndex = (perfectDataFit) ? dataLength - 1 : dataLength - 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 = dataLength - 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;
}
@Override
public String toString() {
final StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append('[');
for (int i = 0; i < this.mSize; i++) {
if (this.getBit(i) == BitVector.TRUE) {
stringBuilder.append('1');
} else {
stringBuilder.append('0');
}
if (((i % Byte.SIZE) == (Long.SIZE - 1)) && (i < (this.mSize - 1))) {
stringBuilder.append(',').append(' ');
}
}
stringBuilder.append(']');
return stringBuilder.toString();
}
// ===========================================================
// Methods
// ===========================================================
// ===========================================================
// Inner and Anonymous Classes
// ===========================================================
}