//
package net.sf.zipme;
/**
* This is the DeflaterHuffman class.
* This class is <i>not</i> thread safe. This is inherent in the API, due
* to the split of deflate and setInput.
* @author Jochen Hoenicke
* @date Jan 6, 2000
*/
class DeflaterHuffman {
private static final int BUFSIZE=1 << (DeflaterConstants.DEFAULT_MEM_LEVEL + 6);
private static final int LITERAL_NUM=286;
private static final int DIST_NUM=30;
private static final int BITLEN_NUM=19;
private static final int REP_3_6=16;
private static final int REP_3_10=17;
private static final int REP_11_138=18;
private static final int EOF_SYMBOL=256;
private static final int[] BL_ORDER={16,17,18,0,8,7,9,6,10,5,11,4,12,3,13,2,14,1,15};
private static final String bit4Reverse="\000\010\004\014\002\012\006\016\001\011\005\015\003\013\007\017";
class Tree {
short[] freqs;
short[] codes;
byte[] length;
int[] bl_counts;
int minNumCodes, numCodes;
int maxLength;
Tree( int elems, int minCodes, int maxLength){
this.minNumCodes=minCodes;
this.maxLength=maxLength;
freqs=new short[elems];
bl_counts=new int[maxLength];
}
void reset(){
for (int i=0; i < freqs.length; i++) freqs[i]=0;
codes=null;
length=null;
}
final void writeSymbol( int code){
if (DeflaterConstants.DEBUGGING) {
freqs[code]--;
}
pending.writeBits(codes[code] & 0xffff,length[code]);
}
final void checkEmpty(){
boolean empty=true;
for (int i=0; i < freqs.length; i++) if (freqs[i] != 0) {
System.err.println("freqs[" + i + "] == "+ freqs[i]);
empty=false;
}
if (!empty) throw new Error();
System.err.println("checkEmpty suceeded!");
}
void setStaticCodes( short[] stCodes, byte[] stLength){
codes=stCodes;
length=stLength;
}
public void buildCodes(){
int[] nextCode=new int[maxLength];
int code=0;
codes=new short[freqs.length];
if (DeflaterConstants.DEBUGGING) System.err.println("buildCodes: " + freqs.length);
for (int bits=0; bits < maxLength; bits++) {
nextCode[bits]=code;
code+=bl_counts[bits] << (15 - bits);
if (DeflaterConstants.DEBUGGING) System.err.println("bits: " + (bits + 1) + " count: "+ bl_counts[bits]+ " nextCode: "+ Integer.toHexString(code));
}
if (DeflaterConstants.DEBUGGING && code != 65536) throw new RuntimeException("Inconsistent bl_counts!");
for (int i=0; i < numCodes; i++) {
int bits=length[i];
if (bits > 0) {
if (DeflaterConstants.DEBUGGING) System.err.println("codes[" + i + "] = rev("+ Integer.toHexString(nextCode[bits - 1])+ "),"+ bits);
codes[i]=bitReverse(nextCode[bits - 1]);
nextCode[bits - 1]+=1 << (16 - bits);
}
}
}
private void buildLength( int childs[]){
this.length=new byte[freqs.length];
int numNodes=childs.length / 2;
int numLeafs=(numNodes + 1) / 2;
int overflow=0;
for (int i=0; i < maxLength; i++) bl_counts[i]=0;
int lengths[]=new int[numNodes];
lengths[numNodes - 1]=0;
for (int i=numNodes - 1; i >= 0; i--) {
if (childs[2 * i + 1] != -1) {
int bitLength=lengths[i] + 1;
if (bitLength > maxLength) {
bitLength=maxLength;
overflow++;
}
lengths[childs[2 * i]]=lengths[childs[2 * i + 1]]=bitLength;
}
else {
int bitLength=lengths[i];
bl_counts[bitLength - 1]++;
this.length[childs[2 * i]]=(byte)lengths[i];
}
}
if (DeflaterConstants.DEBUGGING) {
System.err.println("Tree " + freqs.length + " lengths:");
for (int i=0; i < numLeafs; i++) System.err.println("Node " + childs[2 * i] + " freq: "+ freqs[childs[2 * i]]+ " len: "+ length[childs[2 * i]]);
}
if (overflow == 0) return;
int incrBitLen=maxLength - 1;
do {
while (bl_counts[--incrBitLen] == 0) ;
do {
bl_counts[incrBitLen]--;
bl_counts[++incrBitLen]++;
overflow-=1 << (maxLength - 1 - incrBitLen);
}
while (overflow > 0 && incrBitLen < maxLength - 1);
}
while (overflow > 0);
bl_counts[maxLength - 1]+=overflow;
bl_counts[maxLength - 2]-=overflow;
int nodePtr=2 * numLeafs;
for (int bits=maxLength; bits != 0; bits--) {
int n=bl_counts[bits - 1];
while (n > 0) {
int childPtr=2 * childs[nodePtr++];
if (childs[childPtr + 1] == -1) {
length[childs[childPtr]]=(byte)bits;
n--;
}
}
}
if (DeflaterConstants.DEBUGGING) {
System.err.println("*** After overflow elimination. ***");
for (int i=0; i < numLeafs; i++) System.err.println("Node " + childs[2 * i] + " freq: "+ freqs[childs[2 * i]]+ " len: "+ length[childs[2 * i]]);
}
}
void buildTree(){
int numSymbols=freqs.length;
int[] heap=new int[numSymbols];
int heapLen=0;
int maxCode=0;
for (int n=0; n < numSymbols; n++) {
int freq=freqs[n];
if (freq != 0) {
int pos=heapLen++;
int ppos;
while (pos > 0 && freqs[heap[ppos=(pos - 1) / 2]] > freq) {
heap[pos]=heap[ppos];
pos=ppos;
}
heap[pos]=n;
maxCode=n;
}
}
while (heapLen < 2) {
int node=maxCode < 2 ? ++maxCode : 0;
heap[heapLen++]=node;
}
numCodes=Math.max(maxCode + 1,minNumCodes);
int numLeafs=heapLen;
int[] childs=new int[4 * heapLen - 2];
int[] values=new int[2 * heapLen - 1];
int numNodes=numLeafs;
for (int i=0; i < heapLen; i++) {
int node=heap[i];
childs[2 * i]=node;
childs[2 * i + 1]=-1;
values[i]=freqs[node] << 8;
heap[i]=i;
}
do {
int first=heap[0];
int last=heap[--heapLen];
int ppos=0;
int path=1;
while (path < heapLen) {
if (path + 1 < heapLen && values[heap[path]] > values[heap[path + 1]]) path++;
heap[ppos]=heap[path];
ppos=path;
path=path * 2 + 1;
}
int lastVal=values[last];
while ((path=ppos) > 0 && values[heap[ppos=(path - 1) / 2]] > lastVal) heap[path]=heap[ppos];
heap[path]=last;
int second=heap[0];
last=numNodes++;
childs[2 * last]=first;
childs[2 * last + 1]=second;
int mindepth=Math.min(values[first] & 0xff,values[second] & 0xff);
values[last]=lastVal=values[first] + values[second] - mindepth + 1;
ppos=0;
path=1;
while (path < heapLen) {
if (path + 1 < heapLen && values[heap[path]] > values[heap[path + 1]]) path++;
heap[ppos]=heap[path];
ppos=path;
path=ppos * 2 + 1;
}
while ((path=ppos) > 0 && values[heap[ppos=(path - 1) / 2]] > lastVal) heap[path]=heap[ppos];
heap[path]=last;
}
while (heapLen > 1);
if (heap[0] != childs.length / 2 - 1) throw new RuntimeException("Weird!");
buildLength(childs);
}
int getEncodedLength(){
int len=0;
for (int i=0; i < freqs.length; i++) len+=freqs[i] * length[i];
return len;
}
void calcBLFreq( Tree blTree){
int max_count;
int min_count;
int count;
int curlen=-1;
int i=0;
while (i < numCodes) {
count=1;
int nextlen=length[i];
if (nextlen == 0) {
max_count=138;
min_count=3;
}
else {
max_count=6;
min_count=3;
if (curlen != nextlen) {
blTree.freqs[nextlen]++;
count=0;
}
}
curlen=nextlen;
i++;
while (i < numCodes && curlen == length[i]) {
i++;
if (++count >= max_count) break;
}
if (count < min_count) blTree.freqs[curlen]+=count;
else if (curlen != 0) blTree.freqs[REP_3_6]++;
else if (count <= 10) blTree.freqs[REP_3_10]++;
else blTree.freqs[REP_11_138]++;
}
}
void writeTree( Tree blTree){
int max_count;
int min_count;
int count;
int curlen=-1;
int i=0;
while (i < numCodes) {
count=1;
int nextlen=length[i];
if (nextlen == 0) {
max_count=138;
min_count=3;
}
else {
max_count=6;
min_count=3;
if (curlen != nextlen) {
blTree.writeSymbol(nextlen);
count=0;
}
}
curlen=nextlen;
i++;
while (i < numCodes && curlen == length[i]) {
i++;
if (++count >= max_count) break;
}
if (count < min_count) {
while (count-- > 0) blTree.writeSymbol(curlen);
}
else if (curlen != 0) {
blTree.writeSymbol(REP_3_6);
pending.writeBits(count - 3,2);
}
else if (count <= 10) {
blTree.writeSymbol(REP_3_10);
pending.writeBits(count - 3,3);
}
else {
blTree.writeSymbol(REP_11_138);
pending.writeBits(count - 11,7);
}
}
}
}
DeflaterPending pending;
private Tree literalTree, distTree, blTree;
private short d_buf[];
private byte l_buf[];
private int last_lit;
private int extra_bits;
private static short staticLCodes[];
private static byte staticLLength[];
private static short staticDCodes[];
private static byte staticDLength[];
/**
* Reverse the bits of a 16 bit value.
*/
static short bitReverse( int value){
return (short)(bit4Reverse.charAt(value & 0xf) << 12 | bit4Reverse.charAt((value >> 4) & 0xf) << 8 | bit4Reverse.charAt((value >> 8) & 0xf) << 4 | bit4Reverse.charAt(value >> 12));
}
static {
staticLCodes=new short[LITERAL_NUM];
staticLLength=new byte[LITERAL_NUM];
int i=0;
while (i < 144) {
staticLCodes[i]=bitReverse((0x030 + i) << 8);
staticLLength[i++]=8;
}
while (i < 256) {
staticLCodes[i]=bitReverse((0x190 - 144 + i) << 7);
staticLLength[i++]=9;
}
while (i < 280) {
staticLCodes[i]=bitReverse((0x000 - 256 + i) << 9);
staticLLength[i++]=7;
}
while (i < LITERAL_NUM) {
staticLCodes[i]=bitReverse((0x0c0 - 280 + i) << 8);
staticLLength[i++]=8;
}
staticDCodes=new short[DIST_NUM];
staticDLength=new byte[DIST_NUM];
for (i=0; i < DIST_NUM; i++) {
staticDCodes[i]=bitReverse(i << 11);
staticDLength[i]=5;
}
}
public DeflaterHuffman( DeflaterPending pending){
this.pending=pending;
literalTree=new Tree(LITERAL_NUM,257,15);
distTree=new Tree(DIST_NUM,1,15);
blTree=new Tree(BITLEN_NUM,4,7);
d_buf=new short[BUFSIZE];
l_buf=new byte[BUFSIZE];
}
public final void reset(){
last_lit=0;
extra_bits=0;
literalTree.reset();
distTree.reset();
blTree.reset();
}
private int l_code( int len){
if (len == 255) return 285;
int code=257;
while (len >= 8) {
code+=4;
len>>=1;
}
return code + len;
}
private int d_code( int distance){
int code=0;
while (distance >= 4) {
code+=2;
distance>>=1;
}
return code + distance;
}
public void sendAllTrees( int blTreeCodes){
blTree.buildCodes();
literalTree.buildCodes();
distTree.buildCodes();
pending.writeBits(literalTree.numCodes - 257,5);
pending.writeBits(distTree.numCodes - 1,5);
pending.writeBits(blTreeCodes - 4,4);
for (int rank=0; rank < blTreeCodes; rank++) pending.writeBits(blTree.length[BL_ORDER[rank]],3);
literalTree.writeTree(blTree);
distTree.writeTree(blTree);
if (DeflaterConstants.DEBUGGING) blTree.checkEmpty();
}
public void compressBlock(){
for (int i=0; i < last_lit; i++) {
int litlen=l_buf[i] & 0xff;
int dist=d_buf[i];
if (dist-- != 0) {
if (DeflaterConstants.DEBUGGING) System.err.print("[" + (dist + 1) + ","+ (litlen + 3)+ "]: ");
int lc=l_code(litlen);
literalTree.writeSymbol(lc);
int bits=(lc - 261) / 4;
if (bits > 0 && bits <= 5) pending.writeBits(litlen & ((1 << bits) - 1),bits);
int dc=d_code(dist);
distTree.writeSymbol(dc);
bits=dc / 2 - 1;
if (bits > 0) pending.writeBits(dist & ((1 << bits) - 1),bits);
}
else {
if (DeflaterConstants.DEBUGGING) {
if (litlen > 32 && litlen < 127) System.err.print("(" + (char)litlen + "): ");
else System.err.print("{" + litlen + "}: ");
}
literalTree.writeSymbol(litlen);
}
}
if (DeflaterConstants.DEBUGGING) System.err.print("EOF: ");
literalTree.writeSymbol(EOF_SYMBOL);
if (DeflaterConstants.DEBUGGING) {
literalTree.checkEmpty();
distTree.checkEmpty();
}
}
public void flushStoredBlock( byte[] stored, int stored_offset, int stored_len, boolean lastBlock){
if (DeflaterConstants.DEBUGGING) System.err.println("Flushing stored block " + stored_len);
pending.writeBits((DeflaterConstants.STORED_BLOCK << 1) + (lastBlock ? 1 : 0),3);
pending.alignToByte();
pending.writeShort(stored_len);
pending.writeShort(~stored_len);
pending.writeBlock(stored,stored_offset,stored_len);
reset();
}
public void flushBlock( byte[] stored, int stored_offset, int stored_len, boolean lastBlock){
literalTree.freqs[EOF_SYMBOL]++;
literalTree.buildTree();
distTree.buildTree();
literalTree.calcBLFreq(blTree);
distTree.calcBLFreq(blTree);
blTree.buildTree();
int blTreeCodes=4;
for (int i=18; i > blTreeCodes; i--) {
if (blTree.length[BL_ORDER[i]] > 0) blTreeCodes=i + 1;
}
int opt_len=14 + blTreeCodes * 3 + blTree.getEncodedLength() + literalTree.getEncodedLength() + distTree.getEncodedLength() + extra_bits;
int static_len=extra_bits;
for (int i=0; i < LITERAL_NUM; i++) static_len+=literalTree.freqs[i] * staticLLength[i];
for (int i=0; i < DIST_NUM; i++) static_len+=distTree.freqs[i] * staticDLength[i];
if (opt_len >= static_len) {
opt_len=static_len;
}
if (stored_offset >= 0 && stored_len + 4 < opt_len >> 3) {
if (DeflaterConstants.DEBUGGING) System.err.println("Storing, since " + stored_len + " < "+ opt_len+ " <= "+ static_len);
flushStoredBlock(stored,stored_offset,stored_len,lastBlock);
}
else if (opt_len == static_len) {
pending.writeBits((DeflaterConstants.STATIC_TREES << 1) + (lastBlock ? 1 : 0),3);
literalTree.setStaticCodes(staticLCodes,staticLLength);
distTree.setStaticCodes(staticDCodes,staticDLength);
compressBlock();
reset();
}
else {
pending.writeBits((DeflaterConstants.DYN_TREES << 1) + (lastBlock ? 1 : 0),3);
sendAllTrees(blTreeCodes);
compressBlock();
reset();
}
}
public final boolean isFull(){
return last_lit == BUFSIZE;
}
public final boolean tallyLit( int lit){
if (DeflaterConstants.DEBUGGING) {
if (lit > 32 && lit < 127) System.err.println("(" + (char)lit + ")");
else System.err.println("{" + lit + "}");
}
d_buf[last_lit]=0;
l_buf[last_lit++]=(byte)lit;
literalTree.freqs[lit]++;
return last_lit == BUFSIZE;
}
public final boolean tallyDist( int dist, int len){
if (DeflaterConstants.DEBUGGING) System.err.println("[" + dist + ","+ len+ "]");
d_buf[last_lit]=(short)dist;
l_buf[last_lit++]=(byte)(len - 3);
int lc=l_code(len - 3);
literalTree.freqs[lc]++;
if (lc >= 265 && lc < 285) extra_bits+=(lc - 261) / 4;
int dc=d_code(dist - 1);
distTree.freqs[dc]++;
if (dc >= 4) extra_bits+=dc / 2 - 1;
return last_lit == BUFSIZE;
}
}