package com.oracle.truffle.llvm.parser.scanner;
import com.oracle.truffle.llvm.runtime.except.LLVMParserException;
import org.graalvm.polyglot.io.ByteSequence;
public final class BitStream {
private static final int BYTE_BITS_SHIFT = 3;
private static final int BYTE_BITS_MASK = 0x7;
private static final long BYTE_MASK = 0xffL;
private final ByteSequence bitstream;
private BitStream(ByteSequence bitstream) {
this.bitstream = bitstream;
}
public static BitStream create(ByteSequence bytes) {
return new BitStream(bytes);
}
public static BitStream createFromBlob(long[] args, int blobStartIndex) {
final byte[] blob = new byte[(args.length - blobStartIndex) * Long.BYTES];
int to = 0;
for (int from = blobStartIndex; from < args.length; from++) {
final long l = args[from];
for (int i = 0; i < Long.BYTES; i++) {
blob[to++] = (byte) ((l >> (Byte.SIZE * i)) & BYTE_MASK);
}
}
return new BitStream(ByteSequence.create(blob));
}
public static long widthVBR(long value, long width) {
long total = 0;
long v = value;
do {
total += width;
v >>>= (width - 1);
} while (v != 0);
return total;
}
public long read(long offset, int bits) {
if (Long.compareUnsigned(bits, Long.SIZE) > 0) {
throw new LLVMParserException(String.format("Cannot read more than %s bits (%s)", Long.SIZE, Integer.toUnsignedString(bits)));
}
int byteIndex = (int) (offset >> BYTE_BITS_SHIFT);
int bitOffsetInByte = (int) (offset & BYTE_BITS_MASK);
int availableBits = Byte.SIZE - bitOffsetInByte;
long value = (bitstream.byteAt(byteIndex++) & BYTE_MASK) >> bitOffsetInByte;
if (bits <= availableBits) {
return value & (BYTE_MASK >> (8 - bits));
}
int remainingBits = bits - availableBits;
int shift = availableBits;
while (true) {
byte byteValue = bitstream.byteAt(byteIndex++);
if (remainingBits > Byte.SIZE) {
value = value | ((byteValue & BYTE_MASK) << shift);
remainingBits -= Byte.SIZE;
shift += Byte.SIZE;
} else {
return value | ((byteValue & (BYTE_MASK >> (Byte.SIZE - remainingBits))) << shift);
}
}
}
public long readVBR(long offset, int width) {
long value = 0;
long shift = 0;
long datum;
long o = offset;
long dmask = 1 << (width - 1);
do {
datum = read(o, width);
o += width;
value += (datum & (dmask - 1)) << shift;
shift += width - 1;
} while ((datum & dmask) != 0);
return value;
}
public long size() {
return bitstream.length() * (long) Byte.SIZE;
}
}