321 lines
7.2 KiB
Java
321 lines
7.2 KiB
Java
package btools.util;
|
|
|
|
|
|
public class BitCoderContext {
|
|
private byte[] ab;
|
|
private int idxMax;
|
|
private int idx = -1;
|
|
private int bits; // bits left in buffer
|
|
private int b; // buffer word
|
|
|
|
public static final int[] vl_values = new int[4096];
|
|
public static final int[] vl_length = new int[4096];
|
|
|
|
private static final int[] vc_values = new int[4096];
|
|
private static final int[] vc_length = new int[4096];
|
|
|
|
private static final int[] reverse_byte = new int[256];
|
|
|
|
private static final int[] bm2bits = new int[256];
|
|
|
|
static {
|
|
// fill varbits lookup table
|
|
|
|
BitCoderContext bc = new BitCoderContext(new byte[4]);
|
|
for (int i = 0; i < 4096; i++) {
|
|
bc.reset();
|
|
bc.bits = 14;
|
|
bc.b = 0x1000 + i;
|
|
|
|
int b0 = bc.getReadingBitPosition();
|
|
vl_values[i] = bc.decodeVarBits2();
|
|
vl_length[i] = bc.getReadingBitPosition() - b0;
|
|
}
|
|
for (int i = 0; i < 4096; i++) {
|
|
bc.reset();
|
|
int b0 = bc.getWritingBitPosition();
|
|
bc.encodeVarBits2(i);
|
|
vc_values[i] = bc.b;
|
|
vc_length[i] = bc.getWritingBitPosition() - b0;
|
|
}
|
|
for (int i = 0; i < 1024; i++) {
|
|
bc.reset();
|
|
bc.bits = 14;
|
|
bc.b = 0x1000 + i;
|
|
|
|
int b0 = bc.getReadingBitPosition();
|
|
vl_values[i] = bc.decodeVarBits2();
|
|
vl_length[i] = bc.getReadingBitPosition() - b0;
|
|
}
|
|
for (int b = 0; b < 256; b++) {
|
|
int r = 0;
|
|
for (int i = 0; i < 8; i++) {
|
|
if ((b & (1 << i)) != 0) r |= 1 << (7 - i);
|
|
}
|
|
reverse_byte[b] = r;
|
|
}
|
|
for (int b = 0; b < 8; b++) {
|
|
bm2bits[1 << b] = b;
|
|
}
|
|
}
|
|
|
|
|
|
public BitCoderContext(byte[] ab) {
|
|
this.ab = ab;
|
|
idxMax = ab.length - 1;
|
|
}
|
|
|
|
public final void reset(byte[] ab) {
|
|
this.ab = ab;
|
|
idxMax = ab.length - 1;
|
|
reset();
|
|
}
|
|
|
|
public final void reset() {
|
|
idx = -1;
|
|
bits = 0;
|
|
b = 0;
|
|
}
|
|
|
|
/**
|
|
* encode a distance with a variable bit length
|
|
* (poor mans huffman tree)
|
|
* {@code 1 -> 0}
|
|
* {@code 01 -> 1} + following 1-bit word ( 1..2 )
|
|
* {@code 001 -> 3} + following 2-bit word ( 3..6 )
|
|
* {@code 0001 -> 7} + following 3-bit word ( 7..14 ) etc.
|
|
*
|
|
* @see #decodeVarBits
|
|
*/
|
|
public final void encodeVarBits2(int value) {
|
|
int range = 0;
|
|
while (value > range) {
|
|
encodeBit(false);
|
|
value -= range + 1;
|
|
range = 2 * range + 1;
|
|
}
|
|
encodeBit(true);
|
|
encodeBounded(range, value);
|
|
}
|
|
|
|
public final void encodeVarBits(int value) {
|
|
if ((value & 0xfff) == value) {
|
|
flushBuffer();
|
|
b |= vc_values[value] << bits;
|
|
bits += vc_length[value];
|
|
} else {
|
|
encodeVarBits2(value); // slow fallback for large values
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @see #encodeVarBits
|
|
*/
|
|
public final int decodeVarBits2() {
|
|
int range = 0;
|
|
while (!decodeBit()) {
|
|
range = 2 * range + 1;
|
|
}
|
|
return range + decodeBounded(range);
|
|
}
|
|
|
|
public final int decodeVarBits() {
|
|
fillBuffer();
|
|
int b12 = b & 0xfff;
|
|
int len = vl_length[b12];
|
|
if (len <= 12) {
|
|
b >>>= len;
|
|
bits -= len;
|
|
return vl_values[b12]; // full value lookup
|
|
}
|
|
if (len <= 23) // // only length lookup
|
|
{
|
|
int len2 = len >> 1;
|
|
b >>>= (len2 + 1);
|
|
int mask = 0xffffffff >>> (32 - len2);
|
|
mask += b & mask;
|
|
b >>>= len2;
|
|
bits -= len;
|
|
return mask;
|
|
}
|
|
if ((b & 0xffffff) != 0) {
|
|
// here we just know len in [25..47]
|
|
// ( fillBuffer guarantees only 24 bits! )
|
|
b >>>= 12;
|
|
int len3 = 1 + (vl_length[b & 0xfff] >> 1);
|
|
b >>>= len3;
|
|
int len2 = 11 + len3;
|
|
bits -= len2 + 1;
|
|
fillBuffer();
|
|
int mask = 0xffffffff >>> (32 - len2);
|
|
mask += b & mask;
|
|
b >>>= len2;
|
|
bits -= len2;
|
|
return mask;
|
|
}
|
|
return decodeVarBits2(); // no chance, use the slow one
|
|
}
|
|
|
|
|
|
public final void encodeBit(boolean value) {
|
|
if (bits > 31) {
|
|
ab[++idx] = (byte) (b & 0xff);
|
|
b >>>= 8;
|
|
bits -= 8;
|
|
}
|
|
if (value) {
|
|
b |= 1 << bits;
|
|
}
|
|
bits++;
|
|
}
|
|
|
|
public final boolean decodeBit() {
|
|
if (bits == 0) {
|
|
bits = 8;
|
|
b = ab[++idx] & 0xff;
|
|
}
|
|
boolean value = ((b & 1) != 0);
|
|
b >>>= 1;
|
|
bits--;
|
|
return value;
|
|
}
|
|
|
|
/**
|
|
* encode an integer in the range 0..max (inclusive).
|
|
* For max = 2^n-1, this just encodes n bits, but in general
|
|
* this is variable length encoding, with the shorter codes
|
|
* for the central value range
|
|
*/
|
|
public final void encodeBounded(int max, int value) {
|
|
int im = 1; // integer mask
|
|
while (im <= max) {
|
|
if ((value & im) != 0) {
|
|
encodeBit(true);
|
|
max -= im;
|
|
} else {
|
|
encodeBit(false);
|
|
}
|
|
im <<= 1;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* decode an integer in the range 0..max (inclusive).
|
|
*
|
|
* @see #encodeBounded
|
|
*/
|
|
public final int decodeBounded(int max) {
|
|
int value = 0;
|
|
int im = 1; // integer mask
|
|
while ((value | im) <= max) {
|
|
if (bits == 0) {
|
|
bits = 8;
|
|
b = ab[++idx] & 0xff;
|
|
}
|
|
if ((b & 1) != 0)
|
|
value |= im;
|
|
b >>>= 1;
|
|
bits--;
|
|
im <<= 1;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
public final int decodeBits(int count) {
|
|
fillBuffer();
|
|
int mask = 0xffffffff >>> (32 - count);
|
|
int value = b & mask;
|
|
b >>>= count;
|
|
bits -= count;
|
|
return value;
|
|
}
|
|
|
|
public final int decodeBitsReverse(int count) {
|
|
fillBuffer();
|
|
int value = 0;
|
|
while (count > 8) {
|
|
value = (value << 8) | reverse_byte[b & 0xff];
|
|
b >>= 8;
|
|
count -= 8;
|
|
bits -= 8;
|
|
fillBuffer();
|
|
}
|
|
value = (value << count) | reverse_byte[b & 0xff] >> (8 - count);
|
|
bits -= count;
|
|
b >>= count;
|
|
return value;
|
|
}
|
|
|
|
private void fillBuffer() {
|
|
while (bits < 24) {
|
|
if (idx++ < idxMax) {
|
|
b |= (ab[idx] & 0xff) << bits;
|
|
}
|
|
bits += 8;
|
|
}
|
|
}
|
|
|
|
private void flushBuffer() {
|
|
while (bits > 7) {
|
|
ab[++idx] = (byte) (b & 0xff);
|
|
b >>>= 8;
|
|
bits -= 8;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* flushes and closes the (write-mode) context
|
|
*
|
|
* @return the encoded length in bytes
|
|
*/
|
|
public final int closeAndGetEncodedLength() {
|
|
flushBuffer();
|
|
if (bits > 0) {
|
|
ab[++idx] = (byte) (b & 0xff);
|
|
}
|
|
return idx + 1;
|
|
}
|
|
|
|
/**
|
|
* @return the encoded length in bits
|
|
*/
|
|
public final int getWritingBitPosition() {
|
|
return (idx << 3) + 8 + bits;
|
|
}
|
|
|
|
public final int getReadingBitPosition() {
|
|
return (idx << 3) + 8 - bits;
|
|
}
|
|
|
|
public final void setReadingBitPosition(int pos) {
|
|
idx = pos >>> 3;
|
|
bits = (idx << 3) + 8 - pos;
|
|
b = ab[idx] & 0xff;
|
|
b >>>= (8 - bits);
|
|
}
|
|
|
|
public static void main(String[] args) {
|
|
byte[] ab = new byte[581969];
|
|
BitCoderContext ctx = new BitCoderContext(ab);
|
|
for (int i = 0; i < 31; i++) {
|
|
ctx.encodeVarBits((1 << i) + 3);
|
|
}
|
|
for (int i = 0; i < 100000; i += 13) {
|
|
ctx.encodeVarBits(i);
|
|
}
|
|
ctx.closeAndGetEncodedLength();
|
|
ctx = new BitCoderContext(ab);
|
|
|
|
for (int i = 0; i < 31; i++) {
|
|
int value = ctx.decodeVarBits();
|
|
int v0 = (1 << i) + 3;
|
|
if (v0 != value)
|
|
throw new RuntimeException("value mismatch value=" + value + "v0=" + v0);
|
|
}
|
|
for (int i = 0; i < 100000; i += 13) {
|
|
int value = ctx.decodeVarBits();
|
|
if (value != i)
|
|
throw new RuntimeException("value mismatch i=" + i + "v=" + value);
|
|
}
|
|
}
|
|
}
|