brouter/brouter-util/src/main/java/btools/util/BitCoderContext.java
2019-09-28 12:59:29 +02:00

374 lines
7.4 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 );
}
}
}