mirror of
https://github.com/collinsmith/riiablo.git
synced 2025-03-06 07:30:41 +07:00
Replaced com.riiablo.io classes with com.riiablo.io.nio classes
This commit is contained in:
parent
c8b74fccbf
commit
7ed92b1f49
@ -1,19 +1,20 @@
|
||||
package com.riiablo.io;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.riiablo.util.DebugUtils;
|
||||
|
||||
/**
|
||||
* Wraps a {@link ByteInput} to support reading sequences of bits and
|
||||
* supporting {@link #align() re-aligning} the byte stream to read sequences of
|
||||
* {@link ByteInput bytes}. All read functions will return results in little
|
||||
* endian byte order.
|
||||
*
|
||||
* @see BitInput
|
||||
* @see #align()
|
||||
*/
|
||||
// TODO: improve placeholder documentation
|
||||
public class BitInput {
|
||||
private static final BitInput EMPTY_BITINPUT = new BitInput(new byte[0]);
|
||||
public static BitInput emptyBitInput() {
|
||||
return EMPTY_BITINPUT;
|
||||
}
|
||||
|
||||
public static BitInput wrap(byte[] bytes) {
|
||||
return bytes == null ? emptyBitInput() : new BitInput(bytes);
|
||||
return ByteInput.wrap(bytes).unalign();
|
||||
}
|
||||
|
||||
private static final int MAX_ULONG_BITS = Long.SIZE - 1;
|
||||
@ -31,75 +32,147 @@ public class BitInput {
|
||||
}
|
||||
}
|
||||
|
||||
private final ByteBuf buffer;
|
||||
private final ByteInput byteInput;
|
||||
private final long numBits;
|
||||
private long bitsRead;
|
||||
private int bitsCached;
|
||||
private long cache;
|
||||
|
||||
private BitInput(byte[] b) {
|
||||
buffer = Unpooled.wrappedBuffer(b);
|
||||
numBits = (long) b.length * Byte.SIZE;
|
||||
/**
|
||||
* Constructs a BitInput instance at the start of the byteInput with
|
||||
* {@code numBits} including all bits within {@code byteInput}.
|
||||
*/
|
||||
BitInput(ByteInput byteInput) {
|
||||
this(byteInput, 0, 0L, (long) byteInput.bytesRemaining() * Byte.SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears the overflow bits of the previously read byte.
|
||||
* Constructs a BitInput instance with an initial state. This is typically
|
||||
* done when the BitInput is created as the child of another BitInput.
|
||||
*/
|
||||
public void clearCache() {
|
||||
private BitInput(ByteInput byteInput, int bitsCached, long cache, long numBits) {
|
||||
this.byteInput = byteInput;
|
||||
this.bitsCached = bitsCached;
|
||||
this.cache = cache;
|
||||
this.numBits = numBits;
|
||||
}
|
||||
|
||||
ByteInput byteInput() {
|
||||
return byteInput;
|
||||
}
|
||||
|
||||
public int bytesRead() {
|
||||
return byteInput.bytesRead();
|
||||
}
|
||||
|
||||
public int bytesRemaining() {
|
||||
return byteInput.bytesRemaining();
|
||||
}
|
||||
|
||||
public int numBytes() {
|
||||
return byteInput.numBytes();
|
||||
}
|
||||
|
||||
public int bitsCached() {
|
||||
return bitsCached;
|
||||
}
|
||||
|
||||
public long cache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
void clearCache() {
|
||||
bitsCached = 0;
|
||||
cache = 0L;
|
||||
}
|
||||
|
||||
int bitsCached() {
|
||||
return bitsCached;
|
||||
}
|
||||
|
||||
long cache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
public long bitsRead() {
|
||||
return bitsRead;
|
||||
}
|
||||
|
||||
public long numBits() {
|
||||
return numBits;
|
||||
}
|
||||
|
||||
// TODO: include parent offset?
|
||||
public int bytePosition() {
|
||||
return buffer.readerIndex();
|
||||
}
|
||||
|
||||
public int bytesRemaining() {
|
||||
return buffer.readableBytes();
|
||||
}
|
||||
|
||||
// TODO: include parent offset?
|
||||
public long bitPosition() {
|
||||
assert bitsRead == ((Byte.SIZE - bitsCached) + ((long) bytePosition() * Byte.SIZE))
|
||||
: "actual(" + bitsRead + ") != expected(" + ((Byte.SIZE - bitsCached) + ((long) bytePosition() * Byte.SIZE)) + ")";
|
||||
return bitsRead;
|
||||
}
|
||||
|
||||
public long bitsRemaining() {
|
||||
assert (numBits - bitsRead) == (bitsCached + ((long) bytesRemaining() * Byte.SIZE))
|
||||
: "actual(" + (numBits - bitsRead) + ") != expected(" + (bitsCached + ((long) bytesRemaining() * Byte.SIZE)) + ")";
|
||||
return numBits - bitsRead;
|
||||
}
|
||||
|
||||
public long numBits() {
|
||||
return numBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether or not this bit stream's current bit is located on a
|
||||
* byte boundary.
|
||||
*/
|
||||
public boolean aligned() {
|
||||
assert bitsCached < Byte.SIZE : "bitsCached(" + bitsCached + ") > " + (Byte.SIZE - 1);
|
||||
return bitsCached == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a byte aligned view of this bit stream's content. This method
|
||||
* should be called when multiple successive byte aligned operations are
|
||||
* required. Returning to the bit stream state can be done via
|
||||
* {@link ByteInput#unalign()}.
|
||||
*
|
||||
* @see ByteInput#unalign()
|
||||
*/
|
||||
public ByteInput align() {
|
||||
// consume cache if bits remaining
|
||||
assert bitsCached < Byte.SIZE : "bitsCached(" + bitsCached + ") > " + (Byte.SIZE - 1);
|
||||
if (bitsCached > 0) {
|
||||
bitsRead = Math.min(numBits, bitsRead + bitsCached);
|
||||
clearCache();
|
||||
}
|
||||
|
||||
assert bitsRead <= numBits : "bitsRead(" + bitsRead + ") > numBits(" + numBits + ")";
|
||||
return byteInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a slice of this bit stream's sub-region starting at the current
|
||||
* bit and increases the bits read by the size of the new slice (= numBits).
|
||||
*/
|
||||
public BitInput readSlice(long numBits) {
|
||||
// since this shouldn't go more than 1 level deep, can also generate a new
|
||||
// ByteInput with a new BitInput if allowing align
|
||||
if (numBits == 0) return ByteInput.emptyByteInput().unalign();
|
||||
if (numBits < 0) throw new IllegalArgumentException("numBits(" + numBits + ") < " + 0);
|
||||
if (bitsRead + numBits > this.numBits) {
|
||||
throw new IllegalArgumentException(
|
||||
"bitsRead(" + bitsRead + ") + sliceBits(" + numBits + ") > numBits(" + this.numBits + ")");
|
||||
}
|
||||
|
||||
|
||||
assert bitsCached < Byte.SIZE : "bitsCached(" + bitsCached + ") > " + (Byte.SIZE - 1);
|
||||
|
||||
// length should include the last byte that bits belong (round to ceil)
|
||||
final long numBytes = (numBits - bitsCached + Byte.SIZE - 1) / Byte.SIZE;
|
||||
final ByteInput byteInput = this.byteInput.readSlice(numBytes);
|
||||
return byteInput.bitInput(new BitInput(byteInput, bitsCached, cache, numBits));
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips <i>n</i> bits by discarding them.
|
||||
*/
|
||||
public BitInput skip(long bits) {
|
||||
public BitInput skipBits(long bits) {
|
||||
if (bits < 0) throw new IllegalArgumentException("bits(" + bits + ") < " + 0);
|
||||
if (bits == 0) return this;
|
||||
|
||||
// aligns bit stream for multi-byte skipping
|
||||
assert bitsCached < Byte.SIZE : "bitsCached(" + bitsCached + ") > " + (Byte.SIZE - 1);
|
||||
if (bits >= bitsCached) {
|
||||
bits -= bitsCached;
|
||||
align();
|
||||
}
|
||||
|
||||
// skips multiple bytes
|
||||
final long startingBitsRead = bitsRead;
|
||||
final long bytes = bits / Byte.SIZE;
|
||||
assert bytes <= Integer.MAX_VALUE : "bytes(" + bytes + ") > Integer.MAX_VALUE";
|
||||
if (bytes > 0) align((int) bytes);
|
||||
if (bytes > 0) align().skipBytes((int) bytes);
|
||||
|
||||
// skips overflow bits
|
||||
final long overflowBits = (startingBitsRead + bits) - bitsRead;
|
||||
// checks single byte, multi-byte and expected max value
|
||||
assert bytes != 0 || overflowBits < Byte.SIZE : "overflowBits(" + overflowBits + ") > " + (Byte.SIZE - 1);
|
||||
@ -109,95 +182,128 @@ public class BitInput {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Aligns the bit stream to the nearest byte boundary, or not at all if it is
|
||||
* already at one.
|
||||
*
|
||||
* @see #align(int)
|
||||
*/
|
||||
public BitInput align() {
|
||||
return align(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Aligns the bit stream to the <i>nth</i> byte boundary. Alignment will
|
||||
* behave as follows:
|
||||
* <ul>
|
||||
* <li>cache will always be erased</li>
|
||||
* <li>{@code cached bits = 0} will consume {@code 0} bytes</li>
|
||||
* <li>{@code cached bits > 0} will consume {@code 1} byte</li>
|
||||
* </ul>
|
||||
*/
|
||||
public BitInput align(int bytes) {
|
||||
if (bytes < 0) throw new IllegalArgumentException("bytes(" + bytes + ") < " + 0);
|
||||
|
||||
// consume cache if bits remaining
|
||||
assert bitsCached < Byte.SIZE : "bitsCached(" + bitsCached + ") > " + (Byte.SIZE - 1);
|
||||
if (bitsCached > 0) {
|
||||
bitsRead = Math.min(numBits, bitsRead + bitsCached);
|
||||
if (bytes > 0) bytes--;
|
||||
clearCache();
|
||||
long incrementBitsRead(long bits) {
|
||||
if ((bitsRead += bits) > numBits) {
|
||||
bitsRead = numBits;
|
||||
throw new EndOfInput();
|
||||
}
|
||||
|
||||
if (bytes > 0) {
|
||||
bitsRead = Math.min(numBits, bitsRead + (bytes * Byte.SIZE));
|
||||
buffer.skipBytes(bytes);
|
||||
return bitsRead;
|
||||
}
|
||||
|
||||
long decrementBitsRead(long bits) {
|
||||
if ((bitsRead -= bits) < 0) {
|
||||
assert false : "bitsRead(" + bitsRead + ") < " + 0;
|
||||
bitsRead = 0;
|
||||
}
|
||||
|
||||
assert bitsRead <= numBits : "bitsRead(" + bitsRead + ") > numBits(" + numBits + ")";
|
||||
return this;
|
||||
return bitsRead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether or not the bit stream is aligned on a byte boundary.
|
||||
*/
|
||||
public boolean isAligned() {
|
||||
assert bitsCached < Byte.SIZE : "bitsCached(" + bitsCached + ") > " + (Byte.SIZE - 1);
|
||||
return bitsCached == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Consumes bits until the specified sequence of bytes are encountered. After
|
||||
* this method executes, position will be such that the next read operation
|
||||
* is at the first byte in the signature. This method will align the bit
|
||||
* stream to the byte boundary according to {@link #align()}.
|
||||
* Reads up to {@value #MAX_UNSIGNED_BITS} bits as unsigned and casts the
|
||||
* result into a {@code long}.
|
||||
* <p/>
|
||||
* <b>Precondition:</b> {@code signature.length == 2}.
|
||||
*
|
||||
* @see #align()
|
||||
* @see #skip(long)
|
||||
* <p>{@code bits} should be in [0, {@value #MAX_UNSIGNED_BITS}].
|
||||
* <p>Reading {@code 0} bits will always return {@code 0}.
|
||||
*/
|
||||
public BitInput skipUntil(byte[] signature) {
|
||||
if (signature.length != 2) {
|
||||
throw new IllegalArgumentException(
|
||||
"signature.length(" + signature.length + ") != " + 2 + ": " + DebugUtils.toByteArray(signature));
|
||||
long readUnsigned(int bits) {
|
||||
assert bits >= 0 : "bits(" + bits + ") < " + 0;
|
||||
assert bits < Long.SIZE : "bits(" + bits + ") > " + (Long.SIZE - 1);
|
||||
if (bits <= 0) return 0;
|
||||
incrementBitsRead(bits);
|
||||
ensureCache(bits);
|
||||
return bitsCached < bits
|
||||
? readCacheSafe(bits)
|
||||
: readCacheUnsafe(bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures {@link #cache} contains at least <i>n</i> bits, up to
|
||||
* {@value #MAX_SAFE_CACHED_BITS} bits due to overflow.
|
||||
*/
|
||||
private int ensureCache(int bits) {
|
||||
assert bits > 0 : "bits(" + bits + ") < " + 1;
|
||||
assert bits < Long.SIZE : "bits(" + bits + ") > " + (Long.SIZE - 1);
|
||||
while (bitsCached < bits && bitsCached <= MAX_SAFE_CACHED_BITS) {
|
||||
final long nextByte = byteInput._read8u();
|
||||
cache |= (nextByte << bitsCached);
|
||||
bitsCached += Byte.SIZE;
|
||||
}
|
||||
|
||||
align();
|
||||
final byte fb0 = signature[0];
|
||||
final byte fb1 = signature[1];
|
||||
byte b0, b1;
|
||||
b1 = read8();
|
||||
for (;;) {
|
||||
b0 = b1;
|
||||
b1 = read8();
|
||||
if (b0 == fb0 && b1 == fb1) {
|
||||
buffer.readerIndex(buffer.readerIndex() - signature.length);
|
||||
bitsRead -= ((long) signature.length * Byte.SIZE);
|
||||
assert bitsRead >= 0 : "bitsRead(" + bitsRead + ") < " + 0;
|
||||
assert bytesRemaining() >= signature.length
|
||||
: "bytesRemaining(" + bytesRemaining() + ") < signature.length(" + signature.length + ")";
|
||||
break;
|
||||
}
|
||||
}
|
||||
return bitsCached;
|
||||
}
|
||||
|
||||
// TODO: support dynamic signature lengths
|
||||
// create a byte[] of size signature.length
|
||||
// use as a circular buffer with each read byte incrementing index and then going back to
|
||||
// 0 when signature.length is reached. Comparisons will need to be index..length
|
||||
// and 0..index (and 0..length in case where index == 0)
|
||||
/**
|
||||
* Reads <i>n</i> bits from {@link #cache}, consuming the next byte in the
|
||||
* underlying byte stream.
|
||||
* <p/>
|
||||
* This function asserts that {@link #cache} would have overflowed if
|
||||
* <i>n</i> bits were read from the underlying byte stream and thus reads
|
||||
* the next byte accounting for this case.
|
||||
*/
|
||||
private long readCacheSafe(int bits) {
|
||||
assert bits > 0 : "bits(" + bits + ") < " + 1;
|
||||
assert bits < Long.SIZE : "bits(" + bits + ") > " + (Long.SIZE - 1);
|
||||
final int bitsToAddCount = bits - bitsCached;
|
||||
final int overflowBits = Byte.SIZE - bitsToAddCount;
|
||||
final long nextByte = byteInput._read8u();
|
||||
long bitsToAdd = nextByte & MASKS[bitsToAddCount];
|
||||
cache |= (bitsToAdd << bitsCached);
|
||||
final long overflow = (nextByte >>> bitsToAddCount) & MASKS[overflowBits];
|
||||
final long bitsOut = bitsCached & MASKS[bits];
|
||||
cache = overflow;
|
||||
bitsCached = overflowBits;
|
||||
return bitsOut;
|
||||
}
|
||||
|
||||
return this;
|
||||
/**
|
||||
* Reads <i>n</i> bits from {@link #cache}.
|
||||
* <p/>
|
||||
* This function asserts {@link #cache} contains at least <i>n</i> bits.
|
||||
*/
|
||||
private long readCacheUnsafe(int bits) {
|
||||
assert bits > 0 : "bits(" + bits + ") < " + 1;
|
||||
assert bits < Long.SIZE : "bits(" + bits + ") > " + (Long.SIZE - 1);
|
||||
final long bitsOut = cache & MASKS[bits];
|
||||
cache >>>= bits;
|
||||
bitsCached -= bits;
|
||||
return bitsOut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value Long#SIZE} bits and sign extending the result as a
|
||||
* {@code long}.
|
||||
* <p/>
|
||||
* <p>{@code bits} should be in [0, {@value Long#SIZE}].
|
||||
* <p>Reading {@code 0} bits will always return {@code 0}.
|
||||
*/
|
||||
long readSigned(int bits) {
|
||||
assert bits >= 0 : "bits(" + bits + ") < " + 0;
|
||||
assert bits <= Long.SIZE : "bits(" + bits + ") > " + Long.SIZE;
|
||||
if (bits <= 0) return 0;
|
||||
if (bits == Long.SIZE) return _readRaw(Long.SIZE);
|
||||
final int shift = Long.SIZE - bits;
|
||||
assert shift > 0;
|
||||
final long value = readUnsigned(bits);
|
||||
return value << shift >> shift;
|
||||
}
|
||||
|
||||
long _readRaw(int bits) {
|
||||
assert bits > 0 : "bits(" + bits + ") <= " + 0;
|
||||
assert bits <= Long.SIZE : "bits(" + bits + ") > " + Long.SIZE;
|
||||
long lo = readUnsigned(Math.min(Integer.SIZE, bits));
|
||||
long hi = readUnsigned(Math.max(bits - Integer.SIZE, 0));
|
||||
return (hi << Integer.SIZE) | lo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value Long#SIZE} bits as a {@code long}. This method is
|
||||
* intended to be used to read raw memory (i.e., flags).
|
||||
*/
|
||||
public long readRaw(int bits) {
|
||||
BitConstraints.validate64(bits);
|
||||
return _readRaw(bits);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -246,6 +352,8 @@ public class BitInput {
|
||||
|
||||
/**
|
||||
* Reads {@code 1} bit as a {@code boolean}.
|
||||
*
|
||||
* @see #read1()
|
||||
*/
|
||||
public boolean readBoolean() {
|
||||
return read1() != 0;
|
||||
@ -293,308 +401,100 @@ public class BitInput {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value Long#SIZE} bits as a {@code long}. This method is
|
||||
* intended to be used to read raw memory (i.e., flags).
|
||||
*/
|
||||
public long readRaw(int bits) {
|
||||
BitConstraints.validate64(bits);
|
||||
return _readRaw(bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* @see #readRaw
|
||||
*/
|
||||
private long _readRaw(int bits) {
|
||||
assert bits > 0 : "bits(" + bits + ") <= " + 0;
|
||||
assert bits <= Long.SIZE : "bits(" + bits + ") > " + Long.SIZE;
|
||||
long lo = readUnsigned(Math.min(Integer.SIZE, bits));
|
||||
long hi = readUnsigned(Math.max(bits - Integer.SIZE, 0));
|
||||
return (hi << Integer.SIZE) | lo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value Long#SIZE} bits and sign extending the result as a
|
||||
* {@code long}.
|
||||
* <p/>
|
||||
* <p>{@code bits} should be in [0, {@value Long#SIZE}].
|
||||
* <p>Reading {@code 0} bits will always return {@code 0}.
|
||||
*/
|
||||
long readSigned(int bits) {
|
||||
assert bits >= 0 : "bits(" + bits + ") < " + 0;
|
||||
assert bits <= Long.SIZE : "bits(" + bits + ") > " + Long.SIZE;
|
||||
if (bits <= 0) return 0;
|
||||
if (bits == Long.SIZE) return _readRaw(Long.SIZE);
|
||||
final int shift = Long.SIZE - bits;
|
||||
assert shift > 0;
|
||||
final long value = readUnsigned(bits);
|
||||
return value << shift >> shift;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the next byte from the underlying byte stream, ignoring alignment.
|
||||
*/
|
||||
private short _read8u() {
|
||||
try {
|
||||
final short octet = buffer.readUnsignedByte();
|
||||
assert 0 <= octet && octet < (1 << Byte.SIZE);
|
||||
return octet;
|
||||
} catch (IndexOutOfBoundsException t) {
|
||||
throw new EndOfInput(t);
|
||||
}
|
||||
}
|
||||
|
||||
long incrementBitsRead(long bits) {
|
||||
if ((bitsRead += bits) > numBits) {
|
||||
bitsRead = bits;
|
||||
throw new EndOfInput();
|
||||
}
|
||||
|
||||
return bitsRead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value #MAX_UNSIGNED_BITS} bits as unsigned and casts the
|
||||
* result into a {@code long}.
|
||||
* <p/>
|
||||
* <p>{@code bits} should be in [0, {@value #MAX_UNSIGNED_BITS}].
|
||||
* <p>Reading {@code 0} bits will always return {@code 0}.
|
||||
*/
|
||||
long readUnsigned(int bits) {
|
||||
assert bits >= 0 : "bits(" + bits + ") < " + 0;
|
||||
assert bits < Long.SIZE : "bits(" + bits + ") > " + (Long.SIZE - 1);
|
||||
if (bits <= 0) return 0;
|
||||
incrementBitsRead(bits);
|
||||
ensureCache(bits);
|
||||
return bitsCached < bits
|
||||
? readCacheSafe(bits)
|
||||
: readCacheUnsafe(bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures {@link #cache} contains at least <i>n</i> bits, up to
|
||||
* {@value #MAX_SAFE_CACHED_BITS} bits due to overflow.
|
||||
*/
|
||||
private int ensureCache(int bits) {
|
||||
assert bits > 0 : "bits(" + bits + ") < " + 1;
|
||||
assert bits < Long.SIZE : "bits(" + bits + ") > " + (Long.SIZE - 1);
|
||||
while (bitsCached < bits && bitsCached <= MAX_SAFE_CACHED_BITS) {
|
||||
final long nextByte = _read8u();
|
||||
cache |= (nextByte << bitsCached);
|
||||
bitsCached += Byte.SIZE;
|
||||
}
|
||||
|
||||
return bitsCached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <i>n</i> bits from {@link #cache}, consuming the next byte in the
|
||||
* underlying byte stream.
|
||||
* <p/>
|
||||
* This function asserts that {@link #cache} would have overflowed if
|
||||
* <i>n</i> bits were read from the underlying byte stream and thus reads
|
||||
* the next byte accounting for this case.
|
||||
*/
|
||||
private long readCacheSafe(int bits) {
|
||||
assert bits > 0 : "bits(" + bits + ") < " + 1;
|
||||
assert bits < Long.SIZE : "bits(" + bits + ") > " + (Long.SIZE - 1);
|
||||
final int bitsToAddCount = bits - bitsCached;
|
||||
final int overflowBits = Byte.SIZE - bitsToAddCount;
|
||||
final long nextByte = _read8u();
|
||||
long bitsToAdd = nextByte & MASKS[bitsToAddCount];
|
||||
cache |= (bitsToAdd << bitsCached);
|
||||
final long overflow = (nextByte >>> bitsToAddCount) & MASKS[overflowBits];
|
||||
final long bitsOut = bitsCached & MASKS[bits];
|
||||
cache = overflow;
|
||||
bitsCached = overflowBits;
|
||||
return bitsOut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <i>n</i> bits from {@link #cache}.
|
||||
* <p/>
|
||||
* This function asserts {@link #cache} contains at least <i>n</i> bits.
|
||||
*/
|
||||
private long readCacheUnsafe(int bits) {
|
||||
assert bits > 0 : "bits(" + bits + ") < " + 1;
|
||||
assert bits < Long.SIZE : "bits(" + bits + ") > " + (Long.SIZE - 1);
|
||||
final long bitsOut = cache & MASKS[bits];
|
||||
cache >>>= bits;
|
||||
bitsCached -= bits;
|
||||
return bitsOut;
|
||||
}
|
||||
|
||||
private void validateAlignment() {
|
||||
if (!isAligned()) {
|
||||
throw new IllegalStateException(
|
||||
"no-args method called on unaligned stream!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned byte from the bit stream.
|
||||
* <p/>
|
||||
* <b>Precondition:</b> This stream must be byte aligned to use this method.
|
||||
* Reads an unsigned byte.
|
||||
*/
|
||||
public short read8u() {
|
||||
validateAlignment();
|
||||
try {
|
||||
incrementBitsRead(Byte.SIZE);
|
||||
return buffer.readUnsignedByte();
|
||||
} catch (IndexOutOfBoundsException t) {
|
||||
throw new EndOfInput(t);
|
||||
}
|
||||
return read15u(Byte.SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned 16-bit short integer from the bit stream.
|
||||
* <p/>
|
||||
* <b>Precondition:</b> This stream must be byte aligned to use this method.
|
||||
* Reads an unsigned 16-bit short integer.
|
||||
*/
|
||||
public int read16u() {
|
||||
validateAlignment();
|
||||
try {
|
||||
incrementBitsRead(Short.SIZE);
|
||||
return buffer.readUnsignedShortLE();
|
||||
} catch (IndexOutOfBoundsException t) {
|
||||
throw new EndOfInput(t);
|
||||
}
|
||||
return read31u(Short.SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned 32-bit integer from the bit stream.
|
||||
* <p/>
|
||||
* <b>Precondition:</b> This stream must be byte aligned to use this method.
|
||||
* Reads an unsigned 32-bit integer.
|
||||
*/
|
||||
public long read32u() {
|
||||
validateAlignment();
|
||||
try {
|
||||
incrementBitsRead(Integer.SIZE);
|
||||
return buffer.readUnsignedIntLE();
|
||||
} catch (IndexOutOfBoundsException t) {
|
||||
throw new EndOfInput(t);
|
||||
}
|
||||
return read63u(Integer.SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a byte from the bit stream.
|
||||
* <p/>
|
||||
* <b>Precondition:</b> This stream must be byte aligned to use this method.
|
||||
* Reads a byte.
|
||||
*/
|
||||
public byte read8() {
|
||||
validateAlignment();
|
||||
try {
|
||||
incrementBitsRead(Byte.SIZE);
|
||||
return buffer.readByte();
|
||||
} catch (IndexOutOfBoundsException t) {
|
||||
throw new EndOfInput(t);
|
||||
}
|
||||
return read8(Byte.SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 16-bit short integer from the bit stream.
|
||||
* <p/>
|
||||
* <b>Precondition:</b> This stream must be byte aligned to use this method.
|
||||
* Reads a 16-bit short integer.
|
||||
*/
|
||||
public short read16() {
|
||||
validateAlignment();
|
||||
try {
|
||||
incrementBitsRead(Short.SIZE);
|
||||
return buffer.readShortLE();
|
||||
} catch (IndexOutOfBoundsException t) {
|
||||
throw new EndOfInput(t);
|
||||
}
|
||||
return read16(Short.SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 32-bit integer from the bit stream.
|
||||
* <p/>
|
||||
* <b>Precondition:</b> This stream must be byte aligned to use this method.
|
||||
* Reads a 32-bit integer.
|
||||
*/
|
||||
public int read32() {
|
||||
validateAlignment();
|
||||
try {
|
||||
incrementBitsRead(Integer.SIZE);
|
||||
return buffer.readIntLE();
|
||||
} catch (IndexOutOfBoundsException t) {
|
||||
throw new EndOfInput(t);
|
||||
}
|
||||
return read32(Integer.SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 64-bit long integer from the bit stream.
|
||||
* <p/>
|
||||
* <b>Precondition:</b> This stream must be byte aligned to use this method.
|
||||
* Reads a 64-bit long integer.
|
||||
*/
|
||||
public long read64() {
|
||||
validateAlignment();
|
||||
try {
|
||||
incrementBitsRead(Long.SIZE);
|
||||
return buffer.readLongLE();
|
||||
} catch (IndexOutOfBoundsException t) {
|
||||
throw new EndOfInput(t);
|
||||
}
|
||||
return read64(Long.SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <i>n</i> bytes from the bit stream into a created byte array.
|
||||
* <p/>
|
||||
* <b>Precondition:</b> This stream must be byte aligned to use this method.
|
||||
*
|
||||
* @see #readBytes(byte[])
|
||||
* @see #readBytes(byte[], int, int)
|
||||
*/
|
||||
public byte[] readBytes(int len) {
|
||||
validateAlignment();
|
||||
byte[] dst = new byte[len];
|
||||
readBytes(dst);
|
||||
return dst;
|
||||
}
|
||||
// /**
|
||||
// * @deprecated unaligned reads not supported!
|
||||
// * use {@code align().readBytes(int)} instead!
|
||||
// * <pre>{@link #align()} {@link ByteInput#readBytes(int)}</pre>
|
||||
// */
|
||||
// @Deprecated
|
||||
// public byte[] readBytes(int len) {
|
||||
// throw new UnsupportedOperationException("use align().readBytes(int) instead!");
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * @deprecated unaligned reads not supported!
|
||||
// * use {@code align().readBytes(byte[])} instead!
|
||||
// * <pre>{@link #align()} {@link ByteInput#readBytes(byte[])}</pre>
|
||||
// */
|
||||
// @Deprecated
|
||||
// public byte[] readBytes(byte[] dst) {
|
||||
// throw new UnsupportedOperationException("use align().readBytes(byte[]) instead!");
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * @deprecated unaligned reads not supported!
|
||||
// * use {@code align().readBytes(byte[], int, int)} instead!
|
||||
// * <pre>{@link #align()} {@link ByteInput#readBytes(byte[], int, int)}</pre>
|
||||
// */
|
||||
// @Deprecated
|
||||
// public byte[] readBytes(byte[] dst, int dstOffset, int len) {
|
||||
// throw new UnsupportedOperationException("use align().readBytes(byte[],int,int) instead!");
|
||||
// }
|
||||
|
||||
/**
|
||||
* Reads <i>n</i> bytes from the bit stream into the specified byte array.
|
||||
* <p/>
|
||||
* <b>Precondition:</b> This stream must be byte aligned to use this method.
|
||||
*
|
||||
* @see #readBytes(int)
|
||||
* @see #readBytes(byte[], int, int)
|
||||
*/
|
||||
public void readBytes(byte[] dst) {
|
||||
validateAlignment();
|
||||
readBytes(dst, 0, dst.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <i>n</i> bytes from the bit stream into the specified byte array.
|
||||
* <p/>
|
||||
* <b>Precondition:</b> This stream must be byte aligned to use this method.
|
||||
*
|
||||
* @see #readBytes(int)
|
||||
* @see #readBytes(byte[])
|
||||
*/
|
||||
public void readBytes(byte[] dst, int dstOffset, int len) {
|
||||
validateAlignment();
|
||||
align();
|
||||
assert bitsCached == 0;
|
||||
incrementBitsRead((long) len * Byte.SIZE);
|
||||
buffer.readBytes(dst, dstOffset, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <i>n</i> bytes from the bit stream and constructs a String.
|
||||
* Reads <i>n</i> bytes from the bit stream and constructs a string.
|
||||
*/
|
||||
public String readString(int len) {
|
||||
return readString(len, Byte.SIZE, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <i>n</i> characters of size {@code bits} and constructs a String.
|
||||
* Reads <i>n</i> characters of size {@code bits} and constructs a string.
|
||||
*
|
||||
* @param len number of characters to read
|
||||
* @param bits size of each character ({@code 7} or {@code 8})
|
||||
* @param nullTerminate {@code true} to stop reading at {@code \0}, otherwise
|
||||
* {@code len} characters will be read
|
||||
* @param nullTerminated {@code true} to stop reading at {@code \0}, otherwise
|
||||
* {@code len} characters will be read (variable-width string)
|
||||
*/
|
||||
public String readString(int len, int bits, boolean nullTerminate) {
|
||||
public String readString(int len, int bits, boolean nullTerminated) {
|
||||
if (len < 0) throw new IllegalArgumentException("len(" + len + ") < " + 0);
|
||||
BitConstraints.validateAscii(bits);
|
||||
|
||||
@ -602,7 +502,7 @@ public class BitInput {
|
||||
final byte[] dst = new byte[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
final byte b = dst[i] = (byte) readUnsigned(bits);
|
||||
if (nullTerminate && b == '\0') break;
|
||||
if (nullTerminated && b == '\0') break;
|
||||
}
|
||||
|
||||
return new String(dst);
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.riiablo.io.nio;
|
||||
package com.riiablo.io;
|
||||
|
||||
import io.netty.buffer.ByteBuf;
|
||||
import io.netty.buffer.Unpooled;
|
@ -1,67 +0,0 @@
|
||||
package com.riiablo.io.nio;
|
||||
|
||||
class BitConstraints {
|
||||
private BitConstraints() {}
|
||||
|
||||
private static int _validateSize(int min, int max, int bits) {
|
||||
assert min >= 0 : "min(" + min + ") < " + 0 + "!";
|
||||
assert max > 0 : "max(" + max + ") <= " + 0;
|
||||
assert min <= max : "min(" + min + ") > max(" + max + ")";
|
||||
if (bits < min) {
|
||||
throw new IllegalArgumentException("bits(" + bits + ") < " + min);
|
||||
}
|
||||
if (bits > max) {
|
||||
throw new IllegalArgumentException("bits(" + bits + ") > " + max);
|
||||
}
|
||||
return bits;
|
||||
}
|
||||
|
||||
private static int validateSizeU(int max, int bits) {
|
||||
return _validateSize(0, max - 1, bits);
|
||||
}
|
||||
|
||||
public static int validate7u(int bits) {
|
||||
return validateSizeU(Byte.SIZE, bits);
|
||||
}
|
||||
|
||||
public static int validate15u(int bits) {
|
||||
return validateSizeU(Short.SIZE, bits);
|
||||
}
|
||||
|
||||
public static int validate31u(int bits) {
|
||||
return validateSizeU(Integer.SIZE, bits);
|
||||
}
|
||||
|
||||
public static int validate63u(int bits) {
|
||||
return validateSizeU(Long.SIZE, bits);
|
||||
}
|
||||
|
||||
private static int validateSize(int max, int bits) {
|
||||
return _validateSize(0, max, bits);
|
||||
}
|
||||
|
||||
public static int validate8(int bits) {
|
||||
return validateSize(Byte.SIZE, bits);
|
||||
}
|
||||
|
||||
public static int validate16(int bits) {
|
||||
return validateSize(Short.SIZE, bits);
|
||||
}
|
||||
|
||||
public static int validate32(int bits) {
|
||||
return validateSize(Integer.SIZE, bits);
|
||||
}
|
||||
|
||||
public static int validate64(int bits) {
|
||||
return validateSize(Long.SIZE, bits);
|
||||
}
|
||||
|
||||
public static boolean isUnsigned(long value, int size) {
|
||||
assert 0 < size && size <= Long.SIZE;
|
||||
return (value & (1 << (size - 1))) == 0;
|
||||
}
|
||||
|
||||
public static int validateAscii(int bits) {
|
||||
return _validateSize(Byte.SIZE - 1, Byte.SIZE, bits);
|
||||
}
|
||||
}
|
@ -1,510 +0,0 @@
|
||||
package com.riiablo.io.nio;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
/**
|
||||
* Wraps a {@link ByteInput} to support reading sequences of bits and
|
||||
* supporting {@link #align() re-aligning} the byte stream to read sequences of
|
||||
* {@link ByteInput bytes}. All read functions will return results in little
|
||||
* endian byte order.
|
||||
*
|
||||
* @see BitInput
|
||||
* @see #align()
|
||||
*/
|
||||
// TODO: improve placeholder documentation
|
||||
public class BitInput {
|
||||
public static BitInput wrap(byte[] bytes) {
|
||||
return ByteInput.wrap(bytes).unalign();
|
||||
}
|
||||
|
||||
private static final int MAX_ULONG_BITS = Long.SIZE - 1;
|
||||
private static final int MAX_UINT_BITS = Integer.SIZE - 1;
|
||||
private static final int MAX_USHORT_BITS = Short.SIZE - 1;
|
||||
private static final int MAX_UBYTE_BITS = Byte.SIZE - 1;
|
||||
private static final int MAX_UNSIGNED_BITS = MAX_ULONG_BITS;
|
||||
|
||||
private static final int MAX_SAFE_CACHED_BITS = Long.SIZE - Byte.SIZE;
|
||||
|
||||
private static final long[] MASKS = new long[Long.SIZE];
|
||||
static {
|
||||
for (int i = 1; i < Long.SIZE; i++) {
|
||||
MASKS[i] = (MASKS[i - 1] << 1) + 1;
|
||||
}
|
||||
}
|
||||
|
||||
private final ByteInput byteInput;
|
||||
private final long numBits;
|
||||
private long bitsRead;
|
||||
private int bitsCached;
|
||||
private long cache;
|
||||
|
||||
/**
|
||||
* Constructs a BitInput instance at the start of the byteInput with
|
||||
* {@code numBits} including all bits within {@code byteInput}.
|
||||
*/
|
||||
BitInput(ByteInput byteInput) {
|
||||
this(byteInput, 0, 0L, (long) byteInput.bytesRemaining() * Byte.SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructs a BitInput instance with an initial state. This is typically
|
||||
* done when the BitInput is created as the child of another BitInput.
|
||||
*/
|
||||
private BitInput(ByteInput byteInput, int bitsCached, long cache, long numBits) {
|
||||
this.byteInput = byteInput;
|
||||
this.bitsCached = bitsCached;
|
||||
this.cache = cache;
|
||||
this.numBits = numBits;
|
||||
}
|
||||
|
||||
ByteInput byteInput() {
|
||||
return byteInput;
|
||||
}
|
||||
|
||||
public int bytesRead() {
|
||||
return byteInput.bytesRead();
|
||||
}
|
||||
|
||||
public int bytesRemaining() {
|
||||
return byteInput.bytesRemaining();
|
||||
}
|
||||
|
||||
public int numBytes() {
|
||||
return byteInput.numBytes();
|
||||
}
|
||||
|
||||
public int bitsCached() {
|
||||
return bitsCached;
|
||||
}
|
||||
|
||||
public long cache() {
|
||||
return cache;
|
||||
}
|
||||
|
||||
void clearCache() {
|
||||
bitsCached = 0;
|
||||
cache = 0L;
|
||||
}
|
||||
|
||||
public long bitsRead() {
|
||||
return bitsRead;
|
||||
}
|
||||
|
||||
public long bitsRemaining() {
|
||||
assert (numBits - bitsRead) == (bitsCached + ((long) bytesRemaining() * Byte.SIZE))
|
||||
: "actual(" + (numBits - bitsRead) + ") != expected(" + (bitsCached + ((long) bytesRemaining() * Byte.SIZE)) + ")";
|
||||
return numBits - bitsRead;
|
||||
}
|
||||
|
||||
public long numBits() {
|
||||
return numBits;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates whether or not this bit stream's current bit is located on a
|
||||
* byte boundary.
|
||||
*/
|
||||
public boolean aligned() {
|
||||
assert bitsCached < Byte.SIZE : "bitsCached(" + bitsCached + ") > " + (Byte.SIZE - 1);
|
||||
return bitsCached == 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a byte aligned view of this bit stream's content. This method
|
||||
* should be called when multiple successive byte aligned operations are
|
||||
* required. Returning to the bit stream state can be done via
|
||||
* {@link ByteInput#unalign()}.
|
||||
*
|
||||
* @see ByteInput#unalign()
|
||||
*/
|
||||
public ByteInput align() {
|
||||
// consume cache if bits remaining
|
||||
assert bitsCached < Byte.SIZE : "bitsCached(" + bitsCached + ") > " + (Byte.SIZE - 1);
|
||||
if (bitsCached > 0) {
|
||||
bitsRead = Math.min(numBits, bitsRead + bitsCached);
|
||||
clearCache();
|
||||
}
|
||||
|
||||
assert bitsRead <= numBits : "bitsRead(" + bitsRead + ") > numBits(" + numBits + ")";
|
||||
return byteInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a slice of this bit stream's sub-region starting at the current
|
||||
* bit and increases the bits read by the size of the new slice (= numBits).
|
||||
*/
|
||||
public BitInput readSlice(long numBits) {
|
||||
// since this shouldn't go more than 1 level deep, can also generate a new
|
||||
// ByteInput with a new BitInput if allowing align
|
||||
if (numBits == 0) return ByteInput.emptyByteInput().unalign();
|
||||
if (numBits < 0) throw new IllegalArgumentException("numBits(" + numBits + ") < " + 0);
|
||||
if (bitsRead + numBits > this.numBits) {
|
||||
throw new IllegalArgumentException(
|
||||
"bitsRead(" + bitsRead + ") + sliceBits(" + numBits + ") > numBits(" + this.numBits + ")");
|
||||
}
|
||||
|
||||
|
||||
assert bitsCached < Byte.SIZE : "bitsCached(" + bitsCached + ") > " + (Byte.SIZE - 1);
|
||||
|
||||
// length should include the last byte that bits belong (round to ceil)
|
||||
final long numBytes = (numBits - bitsCached + Byte.SIZE - 1) / Byte.SIZE;
|
||||
final ByteInput byteInput = this.byteInput.readSlice(numBytes);
|
||||
return byteInput.bitInput(new BitInput(byteInput, bitsCached, cache, numBits));
|
||||
}
|
||||
|
||||
/**
|
||||
* Skips <i>n</i> bits by discarding them.
|
||||
*/
|
||||
public BitInput skipBits(long bits) {
|
||||
if (bits < 0) throw new IllegalArgumentException("bits(" + bits + ") < " + 0);
|
||||
if (bits == 0) return this;
|
||||
|
||||
// aligns bit stream for multi-byte skipping
|
||||
assert bitsCached < Byte.SIZE : "bitsCached(" + bitsCached + ") > " + (Byte.SIZE - 1);
|
||||
if (bits >= bitsCached) {
|
||||
bits -= bitsCached;
|
||||
align();
|
||||
}
|
||||
|
||||
// skips multiple bytes
|
||||
final long startingBitsRead = bitsRead;
|
||||
final long bytes = bits / Byte.SIZE;
|
||||
assert bytes <= Integer.MAX_VALUE : "bytes(" + bytes + ") > Integer.MAX_VALUE";
|
||||
if (bytes > 0) align().skipBytes((int) bytes);
|
||||
|
||||
// skips overflow bits
|
||||
final long overflowBits = (startingBitsRead + bits) - bitsRead;
|
||||
// checks single byte, multi-byte and expected max value
|
||||
assert bytes != 0 || overflowBits < Byte.SIZE : "overflowBits(" + overflowBits + ") > " + (Byte.SIZE - 1);
|
||||
assert bytes == 0 || overflowBits < Short.SIZE : "overflowBits(" + overflowBits + ") > " + (Short.SIZE - 1);
|
||||
assert overflowBits < Short.SIZE : "overflowBits(" + overflowBits + ") > " + (Short.SIZE - 1);
|
||||
if (overflowBits > 0) _readRaw((int) overflowBits);
|
||||
return this;
|
||||
}
|
||||
|
||||
long incrementBitsRead(long bits) {
|
||||
if ((bitsRead += bits) > numBits) {
|
||||
bitsRead = numBits;
|
||||
throw new EndOfInput();
|
||||
}
|
||||
|
||||
return bitsRead;
|
||||
}
|
||||
|
||||
long decrementBitsRead(long bits) {
|
||||
if ((bitsRead -= bits) < 0) {
|
||||
assert false : "bitsRead(" + bitsRead + ") < " + 0;
|
||||
bitsRead = 0;
|
||||
}
|
||||
|
||||
return bitsRead;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value #MAX_UNSIGNED_BITS} bits as unsigned and casts the
|
||||
* result into a {@code long}.
|
||||
* <p/>
|
||||
* <p>{@code bits} should be in [0, {@value #MAX_UNSIGNED_BITS}].
|
||||
* <p>Reading {@code 0} bits will always return {@code 0}.
|
||||
*/
|
||||
long readUnsigned(int bits) {
|
||||
assert bits >= 0 : "bits(" + bits + ") < " + 0;
|
||||
assert bits < Long.SIZE : "bits(" + bits + ") > " + (Long.SIZE - 1);
|
||||
if (bits <= 0) return 0;
|
||||
incrementBitsRead(bits);
|
||||
ensureCache(bits);
|
||||
return bitsCached < bits
|
||||
? readCacheSafe(bits)
|
||||
: readCacheUnsafe(bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures {@link #cache} contains at least <i>n</i> bits, up to
|
||||
* {@value #MAX_SAFE_CACHED_BITS} bits due to overflow.
|
||||
*/
|
||||
private int ensureCache(int bits) {
|
||||
assert bits > 0 : "bits(" + bits + ") < " + 1;
|
||||
assert bits < Long.SIZE : "bits(" + bits + ") > " + (Long.SIZE - 1);
|
||||
while (bitsCached < bits && bitsCached <= MAX_SAFE_CACHED_BITS) {
|
||||
final long nextByte = byteInput._read8u();
|
||||
cache |= (nextByte << bitsCached);
|
||||
bitsCached += Byte.SIZE;
|
||||
}
|
||||
|
||||
return bitsCached;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <i>n</i> bits from {@link #cache}, consuming the next byte in the
|
||||
* underlying byte stream.
|
||||
* <p/>
|
||||
* This function asserts that {@link #cache} would have overflowed if
|
||||
* <i>n</i> bits were read from the underlying byte stream and thus reads
|
||||
* the next byte accounting for this case.
|
||||
*/
|
||||
private long readCacheSafe(int bits) {
|
||||
assert bits > 0 : "bits(" + bits + ") < " + 1;
|
||||
assert bits < Long.SIZE : "bits(" + bits + ") > " + (Long.SIZE - 1);
|
||||
final int bitsToAddCount = bits - bitsCached;
|
||||
final int overflowBits = Byte.SIZE - bitsToAddCount;
|
||||
final long nextByte = byteInput._read8u();
|
||||
long bitsToAdd = nextByte & MASKS[bitsToAddCount];
|
||||
cache |= (bitsToAdd << bitsCached);
|
||||
final long overflow = (nextByte >>> bitsToAddCount) & MASKS[overflowBits];
|
||||
final long bitsOut = bitsCached & MASKS[bits];
|
||||
cache = overflow;
|
||||
bitsCached = overflowBits;
|
||||
return bitsOut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <i>n</i> bits from {@link #cache}.
|
||||
* <p/>
|
||||
* This function asserts {@link #cache} contains at least <i>n</i> bits.
|
||||
*/
|
||||
private long readCacheUnsafe(int bits) {
|
||||
assert bits > 0 : "bits(" + bits + ") < " + 1;
|
||||
assert bits < Long.SIZE : "bits(" + bits + ") > " + (Long.SIZE - 1);
|
||||
final long bitsOut = cache & MASKS[bits];
|
||||
cache >>>= bits;
|
||||
bitsCached -= bits;
|
||||
return bitsOut;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value Long#SIZE} bits and sign extending the result as a
|
||||
* {@code long}.
|
||||
* <p/>
|
||||
* <p>{@code bits} should be in [0, {@value Long#SIZE}].
|
||||
* <p>Reading {@code 0} bits will always return {@code 0}.
|
||||
*/
|
||||
long readSigned(int bits) {
|
||||
assert bits >= 0 : "bits(" + bits + ") < " + 0;
|
||||
assert bits <= Long.SIZE : "bits(" + bits + ") > " + Long.SIZE;
|
||||
if (bits <= 0) return 0;
|
||||
if (bits == Long.SIZE) return _readRaw(Long.SIZE);
|
||||
final int shift = Long.SIZE - bits;
|
||||
assert shift > 0;
|
||||
final long value = readUnsigned(bits);
|
||||
return value << shift >> shift;
|
||||
}
|
||||
|
||||
long _readRaw(int bits) {
|
||||
assert bits > 0 : "bits(" + bits + ") <= " + 0;
|
||||
assert bits <= Long.SIZE : "bits(" + bits + ") > " + Long.SIZE;
|
||||
long lo = readUnsigned(Math.min(Integer.SIZE, bits));
|
||||
long hi = readUnsigned(Math.max(bits - Integer.SIZE, 0));
|
||||
return (hi << Integer.SIZE) | lo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value Long#SIZE} bits as a {@code long}. This method is
|
||||
* intended to be used to read raw memory (i.e., flags).
|
||||
*/
|
||||
public long readRaw(int bits) {
|
||||
BitConstraints.validate64(bits);
|
||||
return _readRaw(bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value #MAX_UBYTE_BITS} bits as unsigned and casts the result
|
||||
* into a {@code byte}.
|
||||
*/
|
||||
public byte read7u(int bits) {
|
||||
BitConstraints.validate7u(bits);
|
||||
final byte value = (byte) readUnsigned(bits);
|
||||
assert BitConstraints.isUnsigned(value, Byte.SIZE);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value #MAX_USHORT_BITS} bits as unsigned and casts the
|
||||
* result into a {@code short}.
|
||||
*/
|
||||
public short read15u(int bits) {
|
||||
BitConstraints.validate15u(bits);
|
||||
final short value = (short) readUnsigned(bits);
|
||||
assert BitConstraints.isUnsigned(value, Short.SIZE);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value #MAX_UINT_BITS} bits as unsigned and casts the result
|
||||
* into a {@code int}.
|
||||
*/
|
||||
public int read31u(int bits) {
|
||||
BitConstraints.validate31u(bits);
|
||||
final int value = (int) readUnsigned(bits);
|
||||
assert BitConstraints.isUnsigned(value, Integer.SIZE);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value #MAX_ULONG_BITS} bits as unsigned and casts the result
|
||||
* into a {@code long}.
|
||||
*/
|
||||
public long read63u(int bits) {
|
||||
BitConstraints.validate63u(bits);
|
||||
final long value = (long) readUnsigned(bits);
|
||||
assert BitConstraints.isUnsigned(value, Long.SIZE);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads {@code 1} bit as a {@code boolean}.
|
||||
*
|
||||
* @see #read1()
|
||||
*/
|
||||
public boolean readBoolean() {
|
||||
return read1() != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads {@code 1} bit as a {@code byte}.
|
||||
*/
|
||||
public byte read1() {
|
||||
final byte value = read7u(1);
|
||||
assert (value & ~1) == 0;
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value Byte#SIZE} bits as a sign-extended {@code byte}.
|
||||
*/
|
||||
public byte read8(int bits) {
|
||||
BitConstraints.validate8(bits);
|
||||
return (byte) readSigned(bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value Short#SIZE} bits as a sign-extended {@code short}.
|
||||
*/
|
||||
public short read16(int bits) {
|
||||
BitConstraints.validate16(bits);
|
||||
return (short) readSigned(bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value Integer#SIZE} bits as a sign-extended {@code int}.
|
||||
*/
|
||||
public int read32(int bits) {
|
||||
BitConstraints.validate32(bits);
|
||||
return (int) readSigned(bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads up to {@value Long#SIZE} bits as a sign-extended {@code long}.
|
||||
*/
|
||||
public long read64(int bits) {
|
||||
BitConstraints.validate64(bits);
|
||||
return readSigned(bits);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned byte.
|
||||
*/
|
||||
public short read8u() {
|
||||
return read15u(Byte.SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned 16-bit short integer.
|
||||
*/
|
||||
public int read16u() {
|
||||
return read31u(Short.SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads an unsigned 32-bit integer.
|
||||
*/
|
||||
public long read32u() {
|
||||
return read63u(Integer.SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a byte.
|
||||
*/
|
||||
public byte read8() {
|
||||
return read8(Byte.SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 16-bit short integer.
|
||||
*/
|
||||
public short read16() {
|
||||
return read16(Short.SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 32-bit integer.
|
||||
*/
|
||||
public int read32() {
|
||||
return read32(Integer.SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a 64-bit long integer.
|
||||
*/
|
||||
public long read64() {
|
||||
return read64(Long.SIZE);
|
||||
}
|
||||
|
||||
// /**
|
||||
// * @deprecated unaligned reads not supported!
|
||||
// * use {@code align().readBytes(int)} instead!
|
||||
// * <pre>{@link #align()} {@link ByteInput#readBytes(int)}</pre>
|
||||
// */
|
||||
// @Deprecated
|
||||
// public byte[] readBytes(int len) {
|
||||
// throw new UnsupportedOperationException("use align().readBytes(int) instead!");
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * @deprecated unaligned reads not supported!
|
||||
// * use {@code align().readBytes(byte[])} instead!
|
||||
// * <pre>{@link #align()} {@link ByteInput#readBytes(byte[])}</pre>
|
||||
// */
|
||||
// @Deprecated
|
||||
// public byte[] readBytes(byte[] dst) {
|
||||
// throw new UnsupportedOperationException("use align().readBytes(byte[]) instead!");
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * @deprecated unaligned reads not supported!
|
||||
// * use {@code align().readBytes(byte[], int, int)} instead!
|
||||
// * <pre>{@link #align()} {@link ByteInput#readBytes(byte[], int, int)}</pre>
|
||||
// */
|
||||
// @Deprecated
|
||||
// public byte[] readBytes(byte[] dst, int dstOffset, int len) {
|
||||
// throw new UnsupportedOperationException("use align().readBytes(byte[],int,int) instead!");
|
||||
// }
|
||||
|
||||
/**
|
||||
* Reads <i>n</i> bytes from the bit stream and constructs a string.
|
||||
*/
|
||||
public String readString(int len) {
|
||||
return readString(len, Byte.SIZE, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <i>n</i> characters of size {@code bits} and constructs a string.
|
||||
*
|
||||
* @param len number of characters to read
|
||||
* @param bits size of each character ({@code 7} or {@code 8})
|
||||
* @param nullTerminated {@code true} to stop reading at {@code \0}, otherwise
|
||||
* {@code len} characters will be read (variable-width string)
|
||||
*/
|
||||
public String readString(int len, int bits, boolean nullTerminated) {
|
||||
if (len < 0) throw new IllegalArgumentException("len(" + len + ") < " + 0);
|
||||
BitConstraints.validateAscii(bits);
|
||||
|
||||
if (len == 0) return StringUtils.EMPTY;
|
||||
final byte[] dst = new byte[len];
|
||||
for (int i = 0; i < len; i++) {
|
||||
final byte b = dst[i] = (byte) readUnsigned(bits);
|
||||
if (nullTerminated && b == '\0') break;
|
||||
}
|
||||
|
||||
return new String(dst);
|
||||
}
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
package com.riiablo.io.nio;
|
||||
|
||||
public class EndOfInput extends RuntimeException {
|
||||
private static final String DEFAULT_MESSAGE = "The end of the input has been reached!";
|
||||
|
||||
EndOfInput() {
|
||||
super(DEFAULT_MESSAGE);
|
||||
}
|
||||
|
||||
EndOfInput(Throwable cause) {
|
||||
super(DEFAULT_MESSAGE, cause);
|
||||
}
|
||||
}
|
@ -11,9 +11,15 @@ public class BitInputTest {
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void align_matches_unalign() {
|
||||
BitInput b = newInstance();
|
||||
Assert.assertEquals(b, b.align().unalign());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void empty_bit_input_is_empty() {
|
||||
BitInput b = BitInput.emptyBitInput();
|
||||
BitInput b = ByteInput.emptyByteInput().unalign();
|
||||
Assert.assertEquals(0, b.bitsRemaining());
|
||||
Assert.assertEquals(0, b.bytesRemaining());
|
||||
}
|
||||
@ -23,7 +29,7 @@ public class BitInputTest {
|
||||
BitInput b = newInstance();
|
||||
try {
|
||||
assert b.bitsRead() == 0;
|
||||
b.skip(-1);
|
||||
b.skipBits(-1);
|
||||
} finally {
|
||||
Assert.assertEquals(0L, b.bitsRead());
|
||||
}
|
||||
@ -33,9 +39,9 @@ public class BitInputTest {
|
||||
public void skip_0_bits_aligned() {
|
||||
BitInput b = newInstance();
|
||||
long bitsRead = b.bitsRead();
|
||||
assert b.isAligned();
|
||||
b.skip(0);
|
||||
Assert.assertTrue(b.isAligned());
|
||||
assert b.aligned();
|
||||
b.skipBits(0);
|
||||
Assert.assertTrue(b.aligned());
|
||||
Assert.assertEquals(bitsRead, b.bitsRead());
|
||||
Assert.assertEquals(0, b.bitsCached());
|
||||
Assert.assertEquals(0, b.cache());
|
||||
@ -44,13 +50,13 @@ public class BitInputTest {
|
||||
@Test
|
||||
public void skip_0_bits_unaligned() {
|
||||
BitInput b = newInstance();
|
||||
b.skip(4);
|
||||
b.skipBits(4);
|
||||
long bitsRead = b.bitsRead();
|
||||
int bitsCached = b.bitsCached();
|
||||
long cache = b.cache();
|
||||
assert !b.isAligned();
|
||||
b.skip(0);
|
||||
Assert.assertTrue(!b.isAligned());
|
||||
assert !b.aligned();
|
||||
b.skipBits(0);
|
||||
Assert.assertTrue(!b.aligned());
|
||||
Assert.assertEquals(bitsRead, b.bitsRead());
|
||||
Assert.assertEquals(bitsCached, b.bitsCached());
|
||||
Assert.assertEquals(cache, b.cache());
|
||||
@ -59,8 +65,8 @@ public class BitInputTest {
|
||||
@Test
|
||||
public void skip_n_bits_aligned_to_aligned() {
|
||||
BitInput b = newInstance();
|
||||
assert b.isAligned();
|
||||
b.skip(Byte.SIZE);
|
||||
assert b.aligned();
|
||||
b.skipBits(Byte.SIZE);
|
||||
Assert.assertEquals(Byte.SIZE, b.bitsRead());
|
||||
Assert.assertEquals(0, b.bitsCached());
|
||||
Assert.assertEquals(0, b.cache());
|
||||
@ -69,9 +75,9 @@ public class BitInputTest {
|
||||
@Test
|
||||
public void skip_n_bits_aligned_to_unaligned() {
|
||||
BitInput b = newInstance();
|
||||
assert b.isAligned();
|
||||
b.skip(Byte.SIZE - 1);
|
||||
Assert.assertTrue(!b.isAligned());
|
||||
assert b.aligned();
|
||||
b.skipBits(Byte.SIZE - 1);
|
||||
Assert.assertTrue(!b.aligned());
|
||||
Assert.assertEquals(Byte.SIZE - 1, b.bitsRead());
|
||||
Assert.assertEquals(1, b.bitsCached());
|
||||
Assert.assertEquals(0b1, b.cache());
|
||||
@ -80,11 +86,11 @@ public class BitInputTest {
|
||||
@Test
|
||||
public void skip_n_bits_unaligned_to_aligned() {
|
||||
BitInput b = newInstance();
|
||||
b.skip(1);
|
||||
b.skipBits(1);
|
||||
assert b.bitsRead() == 1;
|
||||
assert !b.isAligned();
|
||||
b.skip(Byte.SIZE - 1);
|
||||
Assert.assertTrue(b.isAligned());
|
||||
assert !b.aligned();
|
||||
b.skipBits(Byte.SIZE - 1);
|
||||
Assert.assertTrue(b.aligned());
|
||||
Assert.assertEquals(Byte.SIZE, b.bitsRead());
|
||||
Assert.assertEquals(0, b.bitsCached());
|
||||
Assert.assertEquals(0, b.cache());
|
||||
@ -93,10 +99,10 @@ public class BitInputTest {
|
||||
@Test
|
||||
public void skip_n_bits_unaligned_to_unaligned() {
|
||||
BitInput b = newInstance();
|
||||
b.skip(1);
|
||||
assert !b.isAligned();
|
||||
b.skip(Byte.SIZE - 2);
|
||||
Assert.assertTrue(!b.isAligned());
|
||||
b.skipBits(1);
|
||||
assert !b.aligned();
|
||||
b.skipBits(Byte.SIZE - 2);
|
||||
Assert.assertTrue(!b.aligned());
|
||||
Assert.assertEquals(Byte.SIZE - 1, b.bitsRead());
|
||||
Assert.assertEquals(1, b.bitsCached());
|
||||
Assert.assertEquals(0b1, b.cache());
|
||||
@ -105,8 +111,8 @@ public class BitInputTest {
|
||||
@Test
|
||||
public void skip_n_bits_aligned_to_aligned_multibyte() {
|
||||
BitInput b = newInstance();
|
||||
assert b.isAligned();
|
||||
b.skip(Byte.SIZE + Byte.SIZE);
|
||||
assert b.aligned();
|
||||
b.skipBits(Byte.SIZE + Byte.SIZE);
|
||||
Assert.assertEquals(Byte.SIZE + Byte.SIZE, b.bitsRead());
|
||||
Assert.assertEquals(0, b.bitsCached());
|
||||
Assert.assertEquals(0, b.cache());
|
||||
@ -115,9 +121,9 @@ public class BitInputTest {
|
||||
@Test
|
||||
public void skip_n_bits_aligned_to_unaligned_multibyte() {
|
||||
BitInput b = newInstance();
|
||||
assert b.isAligned();
|
||||
b.skip(Byte.SIZE + Byte.SIZE - 1);
|
||||
Assert.assertTrue(!b.isAligned());
|
||||
assert b.aligned();
|
||||
b.skipBits(Byte.SIZE + Byte.SIZE - 1);
|
||||
Assert.assertTrue(!b.aligned());
|
||||
Assert.assertEquals(Byte.SIZE + Byte.SIZE - 1, b.bitsRead());
|
||||
Assert.assertEquals(1, b.bitsCached());
|
||||
Assert.assertEquals(0b1, b.cache());
|
||||
@ -126,11 +132,11 @@ public class BitInputTest {
|
||||
@Test
|
||||
public void skip_n_bits_unaligned_to_aligned_multibyte() {
|
||||
BitInput b = newInstance();
|
||||
b.skip(1);
|
||||
b.skipBits(1);
|
||||
assert b.bitsRead() == 1;
|
||||
assert !b.isAligned();
|
||||
b.skip(Byte.SIZE + Byte.SIZE - 1);
|
||||
Assert.assertTrue(b.isAligned());
|
||||
assert !b.aligned();
|
||||
b.skipBits(Byte.SIZE + Byte.SIZE - 1);
|
||||
Assert.assertTrue(b.aligned());
|
||||
Assert.assertEquals(Byte.SIZE + Byte.SIZE, b.bitsRead());
|
||||
Assert.assertEquals(0, b.bitsCached());
|
||||
Assert.assertEquals(0, b.cache());
|
||||
@ -139,93 +145,15 @@ public class BitInputTest {
|
||||
@Test
|
||||
public void skip_n_bits_unaligned_to_unaligned_multibyte() {
|
||||
BitInput b = newInstance();
|
||||
b.skip(1);
|
||||
assert !b.isAligned();
|
||||
b.skip(Byte.SIZE);
|
||||
Assert.assertTrue(!b.isAligned());
|
||||
b.skipBits(1);
|
||||
assert !b.aligned();
|
||||
b.skipBits(Byte.SIZE);
|
||||
Assert.assertTrue(!b.aligned());
|
||||
Assert.assertEquals(Byte.SIZE + 1, b.bitsRead());
|
||||
Assert.assertEquals(7, b.bitsCached());
|
||||
Assert.assertEquals(0b1011111, b.cache());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void align_neg_bytes_throws_IllegalArgumentException() {
|
||||
BitInput b = newInstance();
|
||||
try {
|
||||
assert b.bitsRead() == 0;
|
||||
b.align(-1);
|
||||
} finally {
|
||||
Assert.assertEquals(0L, b.bitsRead());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void align_0_bytes_aligned() {
|
||||
BitInput b = newInstance();
|
||||
assert b.isAligned();
|
||||
assert b.bitsCached() == 0;
|
||||
b.align(0);
|
||||
Assert.assertTrue(b.isAligned());
|
||||
Assert.assertEquals(0, b.bitsRead());
|
||||
Assert.assertEquals(0, b.bitsCached());
|
||||
Assert.assertEquals(0, b.cache());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void align_0_bytes_unaligned() {
|
||||
BitInput b = newInstance();
|
||||
b.skip(4);
|
||||
assert !b.isAligned();
|
||||
assert b.bitsCached() > 0;
|
||||
b.align(0);
|
||||
Assert.assertTrue(b.isAligned());
|
||||
Assert.assertEquals(Byte.SIZE, b.bitsRead());
|
||||
Assert.assertEquals(0, b.bitsCached());
|
||||
Assert.assertEquals(0, b.cache());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void align_1_byte_aligned() {
|
||||
BitInput b = newInstance();
|
||||
assert b.isAligned();
|
||||
assert b.bitsCached() == 0;
|
||||
b.align(1);
|
||||
Assert.assertTrue(b.isAligned());
|
||||
Assert.assertEquals(Byte.SIZE, b.bitsRead());
|
||||
Assert.assertEquals(0, b.bitsCached());
|
||||
Assert.assertEquals(0, b.cache());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void align_1_byte_unaligned() {
|
||||
BitInput b = newInstance();
|
||||
b.skip(4);
|
||||
assert !b.isAligned();
|
||||
assert b.bitsCached() > 0;
|
||||
b.align(1);
|
||||
Assert.assertTrue(b.isAligned());
|
||||
Assert.assertEquals(Byte.SIZE, b.bitsRead());
|
||||
Assert.assertEquals(0, b.bitsCached());
|
||||
Assert.assertEquals(0, b.cache());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void readBytes_aligned() {
|
||||
final byte[] signature = new byte[] {0x4A, 0x4D};
|
||||
BitInput b = BitInput.wrap(new byte[] {
|
||||
signature[0], signature[1], 0x10, 0x00, (byte) 0x80, 0x00, 0x65, 0x00, 0x04,
|
||||
(byte) 0x82, 0x26, 0x76, 0x07, (byte) 0x82, 0x09, (byte) 0xD4,
|
||||
(byte) 0xAA, 0x12, 0x03, 0x01, (byte) 0x80, 0x70, 0x01, 0x01,
|
||||
(byte) 0x91, 0x03, 0x01, 0x04, 0x64, (byte) 0xFC, 0x07});
|
||||
assert b.isAligned();
|
||||
final byte[] bytesRead = b.readBytes(signature.length);
|
||||
Assert.assertTrue(b.isAligned());
|
||||
Assert.assertArrayEquals(signature, bytesRead); // signature
|
||||
Assert.assertEquals(signature.length * Byte.SIZE, b.bitsRead());
|
||||
Assert.assertEquals(0, b.bitsCached());
|
||||
Assert.assertEquals(0, b.cache());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void read_npc_data() {
|
||||
BitInput b = BitInput.wrap(new byte[] {
|
||||
@ -237,7 +165,7 @@ public class BitInputTest {
|
||||
(byte) 0xFA, (byte) 0x7B, (byte) 0xE7, (byte) 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
(byte) 0xDA, (byte) 0x79, (byte) 0xC7, (byte) 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
(byte) 0x10, (byte) 0x51, (byte) 0xE3, (byte) 0x18, 0x00, 0x00, 0x00, 0x00});
|
||||
Assert.assertArrayEquals(new byte[] {0x01, 0x77}, b.readBytes(2)); // signature
|
||||
Assert.assertArrayEquals(new byte[] {0x01, 0x77}, b.align().readBytes(2)); // signature
|
||||
Assert.assertEquals(52, b.readUnsigned(16)); // size
|
||||
Assert.assertEquals(0x00000002_89A5AEACL, b.readRaw(64));
|
||||
Assert.assertEquals(0x00000002_89A4BEACL, b.readRaw(64));
|
||||
@ -259,7 +187,7 @@ public class BitInputTest {
|
||||
(byte) 0xFA, (byte) 0x7B, (byte) 0xE7, (byte) 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
(byte) 0xDA, (byte) 0x79, (byte) 0xC7, (byte) 0x18, 0x00, 0x00, 0x00, 0x00,
|
||||
(byte) 0x10, (byte) 0x51, (byte) 0xE3, (byte) 0x18, 0x00, 0x00, 0x00, 0x00});
|
||||
Assert.assertArrayEquals(new byte[]{0x01, 0x77}, b.readBytes(2)); // signature
|
||||
Assert.assertArrayEquals(new byte[]{0x01, 0x77}, b.align().readBytes(2)); // signature
|
||||
Assert.assertEquals(52, b.read16u()); // size
|
||||
Assert.assertEquals(0x00000002_89A5AEACL, b.readRaw(64));
|
||||
Assert.assertEquals(0x00000002_89A4BEACL, b.readRaw(64));
|
||||
@ -277,10 +205,10 @@ public class BitInputTest {
|
||||
(byte) 0x82, 0x26, 0x76, 0x07, (byte) 0x82, 0x09, (byte) 0xD4,
|
||||
(byte) 0xAA, 0x12, 0x03, 0x01, (byte) 0x80, 0x70, 0x01, 0x01,
|
||||
(byte) 0x91, 0x03, 0x01, 0x04, 0x64, (byte) 0xFC, 0x07});
|
||||
Assert.assertArrayEquals(new byte[] {0x4A, 0x4D}, b.readBytes(2)); // signature
|
||||
Assert.assertArrayEquals(new byte[] {0x4A, 0x4D}, b.align().readBytes(2)); // signature
|
||||
Assert.assertEquals(0x00800010, b.readUnsigned(Integer.SIZE)); // flags
|
||||
Assert.assertEquals(101, b.readUnsigned(8)); // version
|
||||
b.skip(2); // unknown
|
||||
b.skipBits(2); // unknown
|
||||
Assert.assertEquals(0, b.readUnsigned(3)); // location
|
||||
Assert.assertEquals(0, b.readUnsigned(4)); // body location
|
||||
Assert.assertEquals(2, b.readUnsigned(4)); // grid x
|
||||
@ -295,7 +223,7 @@ public class BitInputTest {
|
||||
Assert.assertEquals(false, b.readBoolean()); // class only
|
||||
Assert.assertEquals(0, b.readUnsigned(11)); // magic prefix
|
||||
Assert.assertEquals(737, b.readUnsigned(11)); // magic suffix
|
||||
b.skip(1); // unknown
|
||||
b.skipBits(1); // unknown
|
||||
Assert.assertEquals(32, b.readUnsigned(8)); // max durability
|
||||
Assert.assertEquals(32, b.readUnsigned(9)); // durability
|
||||
Assert.assertEquals(57, b.readUnsigned(9)); // poisonmindam
|
||||
@ -316,10 +244,10 @@ public class BitInputTest {
|
||||
(byte) 0x82, 0x26, 0x76, 0x07, (byte) 0x82, 0x09, (byte) 0xD4,
|
||||
(byte) 0xAA, 0x12, 0x03, 0x01, (byte) 0x80, 0x70, 0x01, 0x01,
|
||||
(byte) 0x91, 0x03, 0x01, 0x04, 0x64, (byte) 0xFC, 0x07});
|
||||
Assert.assertArrayEquals(new byte[] {0x4A, 0x4D}, b.readBytes(2)); // signature
|
||||
Assert.assertArrayEquals(new byte[] {0x4A, 0x4D}, b.align().readBytes(2)); // signature
|
||||
Assert.assertEquals(0x00800010, b.readRaw(32)); // flags
|
||||
Assert.assertEquals(101, b.read8u()); // version
|
||||
b.skip(2); // unknown
|
||||
b.skipBits(2); // unknown
|
||||
Assert.assertEquals(0, b.read7u(3)); // location
|
||||
Assert.assertEquals(0, b.read7u(4)); // body location
|
||||
Assert.assertEquals(2, b.read7u(4)); // grid x
|
||||
@ -334,7 +262,7 @@ public class BitInputTest {
|
||||
Assert.assertEquals(false, b.readBoolean()); // class only
|
||||
Assert.assertEquals(0, b.read15u(11)); // magic prefix
|
||||
Assert.assertEquals(737, b.read15u(11)); // magic suffix
|
||||
b.skip(1); // unknown
|
||||
b.skipBits(1); // unknown
|
||||
Assert.assertEquals(32, b.read15u(8)); // max durability
|
||||
Assert.assertEquals(32, b.read15u(9)); // durability
|
||||
Assert.assertEquals(57, b.read15u(9)); // poisonmindam
|
||||
|
Loading…
Reference in New Issue
Block a user