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 // =========================================================== }