package org.jcodec.common.io;
import org.jcodec.common.IntArrayList;
import java.io.PrintStream;
import java.lang.StringBuilder;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* Table-based prefix VLC reader
*
* @author The JCodec project
*
*/
public class VLC {
/**
* @param arguments
* vlc codes
* @return
*/
public static VLC createVLC(String... arguments) {
IntArrayList _codes = IntArrayList.createIntArrayList();
IntArrayList _codeSizes = IntArrayList.createIntArrayList();
for (int i = 0; i < arguments.length; i++) {
String string = arguments[i];
_codes.add(Integer.parseInt(string, 2) << (32 - string.length()));
_codeSizes.add(string.length());
}
VLC vlc = new VLC(_codes.toArray(), _codeSizes.toArray());
return vlc;
}
private int[] codes;
private int[] codeSizes;
private int[] values;
private int[] valueSizes;
public VLC(int[] codes, int[] codeSizes) {
this.codes = codes;
this.codeSizes = codeSizes;
_invert();
}
private void _invert() {
IntArrayList values = IntArrayList.createIntArrayList();
IntArrayList valueSizes = IntArrayList.createIntArrayList();
invert(0, 0, 0, values, valueSizes);
this.values = values.toArray();
this.valueSizes = valueSizes.toArray();
}
private int invert(int startOff, int level, int prefix, IntArrayList values, IntArrayList valueSizes) {
int tableEnd = startOff + 256;
values.fill(startOff, tableEnd, -1);
valueSizes.fill(startOff, tableEnd, 0);
int prefLen = level << 3;
for (int i = 0; i < codeSizes.length; i++) {
if ((codeSizes[i] <= prefLen) || (level > 0 && (codes[i] >>> (32 - prefLen)) != prefix))
continue;
int pref = codes[i] >>> (32 - prefLen - 8);
int code = pref & 0xff;
int len = codeSizes[i] - prefLen;
if (len <= 8) {
for (int k = 0; k < (1 << (8 - len)); k++) {
values.set(startOff + code + k, i);
valueSizes.set(startOff + code + k, len);
}
} else {
if (values.get(startOff + code) == -1) {
values.set(startOff + code, tableEnd);
tableEnd = invert(tableEnd, level + 1, pref, values, valueSizes);
}
}
}
return tableEnd;
}
public int readVLC16(BitReader _in) {
int string = _in.check16Bits();
int b = string >>> 8;
int code = values[b];
int len = valueSizes[b];
if (len == 0) {
b = (string & 0xff) + code;
code = values[b];
_in.skipFast(8 + valueSizes[b]);
} else
_in.skipFast(len);
return code;
}
public int readVLC(BitReader _in) {
int code = 0, len = 0, overall = 0, total = 0;
for (int i = 0; len == 0; i++) {
int string = _in.checkNBit(8);
int ind = string + code;
code = values[ind];
len = valueSizes[ind];
int bits = len != 0 ? len : 8;
total += bits;
overall = (overall << bits) | (string >> (8 - bits));
_in.skip(bits);
if (code == -1)
throw new RuntimeException("Invalid code prefix " + binary(overall, (i << 3) + bits));
}
// System.out.println("VLC: " + binary(overall, total));
return code;
}
private String binary(int string, int len) {
char[] symb = new char[len];
for (int i = 0; i < len; i++) {
symb[i] = (string & (1 << (len - i - 1))) != 0 ? '1' : '0';
}
return new String(symb);
}
public void writeVLC(BitWriter out, int code) {
out.writeNBit(codes[code] >>> (32 - codeSizes[code]), codeSizes[code]);
}
public void printTable(PrintStream ps) {
for (int i = 0; i < values.length; i++) {
ps.println(i + ": " + extracted(i) + " (" + valueSizes[i] + ") -> " + values[i]);
}
}
private String extracted(int num) {
String str = Integer.toString(num & 0xff, 2);
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 8 - str.length(); i++)
builder.append('0');
builder.append(str);
return builder.toString();
}
public int[] getCodes() {
return codes;
}
public int[] getCodeSizes() {
return codeSizes;
}
}