package org.jcodec.common;
import org.jcodec.common.io.BitWriter;
import org.jcodec.common.io.VLC;
import org.jcodec.common.tools.MathUtil;
import org.jcodec.platform.Platform;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
* This class is part of JCodec ( www.jcodec.org ) This software is distributed
* under FreeBSD License
*
* Compresses a set of long's creating a dictionary and then outputting it using
* a huffman table
*
* @author The JCodec project
*
*/
public class DictionaryCompressor {
protected VLC buildCodes(int[] counts, int esc) {
int[] codes = new int[counts.length];
int[] codeSizes = new int[counts.length];
int code = 0;
for (; code < Math.min(codes.length, esc); code++) {
int max = 0;
for (int i = 0; i < counts.length; i++) {
if (counts[i] > counts[max])
max = i;
}
codes[max] = code;
codeSizes[max] = Math.max(1, MathUtil.log2(code));
counts[max] = Integer.MIN_VALUE;
}
int escSize = MathUtil.log2(esc);
for (int i = 0; i < counts.length; i++)
if (counts[i] >= 0) {
codes[i] = esc;
codeSizes[i] = escSize;
}
return new VLC(codes, codeSizes);
}
public static class Long extends DictionaryCompressor {
public void compress(long[] values, ByteBuffer bb) {
RunLength.Long rl = getValueStats(values);
int[] counts = rl.getCounts();
long[] keys = rl.getValues();
VLC vlc = buildCodes(counts, values.length / 10);
int[] codes = vlc.getCodes();
int[] codeSizes = vlc.getCodeSizes();
bb.putInt(codes.length);
for (int i = 0; i < codes.length; i++) {
bb.put((byte) codeSizes[i]);
bb.putShort((short) (codes[i] >>> 16));
bb.putLong(keys[i]);
}
BitWriter br = new BitWriter(bb);
for (int j = 0; j < values.length; j++) {
long l = values[j];
for (int i = 0; i < keys.length; i++)
if (keys[i] == l) {
vlc.writeVLC(br, i);
if (codes[i] == 0xf)
br.writeNBit(16, i);
}
}
br.flush();
}
private RunLength.Long getValueStats(long[] values) {
long[] copy = Platform.copyOfLong(values, values.length);
Arrays.sort(copy);
RunLength.Long rl = new RunLength.Long();
for (int i = 0; i < copy.length; i++) {
long l = copy[i];
rl.add(l);
}
return rl;
}
}
public static class Int extends DictionaryCompressor {
public void compress(int[] values, ByteBuffer bb) {
RunLength.Integer rl = getValueStats(values);
int[] counts = rl.getCounts();
int[] keys = rl.getValues();
int esc = Math.max(1, (1 << (MathUtil.log2(counts.length) - 2)) - 1);
VLC vlc = buildCodes(counts, esc);
int[] codes = vlc.getCodes();
int[] codeSizes = vlc.getCodeSizes();
bb.putInt(codes.length);
for (int i = 0; i < codes.length; i++) {
bb.put((byte) codeSizes[i]);
bb.putShort((short) (codes[i] >>> 16));
bb.putInt(keys[i]);
}
BitWriter br = new BitWriter(bb);
for (int j = 0; j < values.length; j++) {
int l = values[j];
for (int i = 0; i < keys.length; i++)
if (keys[i] == l) {
vlc.writeVLC(br, i);
if (codes[i] == esc)
br.writeNBit(i, 16);
}
}
br.flush();
}
private RunLength.Integer getValueStats(int[] values) {
int[] copy = Platform.copyOfInt(values, values.length);
Arrays.sort(copy);
RunLength.Integer rl = new RunLength.Integer();
for (int i = 0; i < copy.length; i++) {
int l = copy[i];
rl.add(l);
}
return rl;
}
}
}