/** * Copyright (C) 2007 Rui Shen (rui.shen@gmail.com) All Right Reserved * File : LZXTree.java * Created : 2007-3-1 * **************************************************************************** * This program is free software; you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation; either version 2 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program; if not, write to the Free Software Foundation, Inc., 59 Temple * Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************** */ package org.geometerplus.fbreader.formats.chm; import java.io.IOException; /** * LZX Tables */ class LZXTree { public static final int LZX_LENTABLE_SAFETY = 64; // allow length table decoding overruns public static final int LZX_PRETREE_NUM_ELEMENTS = 20; int bits; int max_symbol; int[] symbols; byte[] lens; LZXTree(int bits, int max) { this.bits = bits; this.max_symbol = max; symbols = new int[ (1 << bits) + (max << 1) ]; lens = new byte[max + LZX_LENTABLE_SAFETY]; } /** * This function was coded by David Tritscher. It builds a fast huffman * decoding table out of just a canonical huffman code lengths table. */ void makeSymbolTable() throws DataFormatException { int bit_num = 1; int pos = 0; // the current position in the decode table int table_mask = 1 << bits; int bit_mask = table_mask >> 1; // don't do 0 length codes int next_symbol = bit_mask; // base of allocation for long codes // fill entries for codes short enough for a direct mapping while (bit_num <= bits) { for (int symbol = 0; symbol < max_symbol; symbol++) { if ( lens[symbol] == bit_num ) { int leaf = pos; if( (pos += bit_mask) > table_mask ) // ensure capacity throw new DataFormatException("symbol table overruns"); // fill all possible lookups of this symbol with the symbol itself while(leaf < pos) symbols[leaf ++] = symbol; } } bit_mask >>= 1; bit_num ++; } // if there are any codes longer than table.bits if (pos != table_mask) { // clear the remainder of the table for (int i = pos; i < table_mask; i ++ ) symbols[i] = 0; // give ourselves room for codes to grow by up to 16 more bits pos <<= 16; table_mask <<= 16; bit_mask = 1 << 15; while (bit_num <= 16) { for (int symbol = 0; symbol < max_symbol; symbol++) { if (lens[symbol] == bit_num) { int leaf = pos >> 16; for (int fill = 0; fill < bit_num - bits; fill++) { // if this path hasn't been taken yet, 'allocate' two entries if (symbols[leaf] == 0) { symbols[ next_symbol << 1 ] = 0; symbols[ (next_symbol << 1) + 1 ] = 0; symbols[leaf] = (next_symbol ++); } // follow the path and select either left or right for next bit leaf = symbols[leaf] << 1; if ( ((pos >> (15-fill)) & 1) > 0) // odd leaf ++; } symbols[leaf] = symbol; if ((pos += bit_mask) > table_mask) throw new DataFormatException("symbol table overflow"); } } bit_mask >>= 1; bit_num ++; } } // full table? if (pos == table_mask) return; // either erroneous table, or all elements are 0 - let's find out. for (short sym = 0; sym < max_symbol; sym++) if (lens[sym] != 0) throw new DataFormatException("erroneous symbol table"); } /** * reads in code lengths for symbols * first to last in the given table. The code lengths are stored in their * own special LZX way. */ void readLengthTable(BitsInputStream bin, int first, int last) throws DataFormatException, IOException { LZXTree preTree = new LZXTree(6, LZX_PRETREE_NUM_ELEMENTS); for (int i = 0; i < preTree.max_symbol; i++) preTree.lens[i] = (byte) bin.readLE(4); preTree.makeSymbolTable(); for (int pos = first; pos < last; ) { int symbol = preTree.readHuffmanSymbol(bin); if ( symbol == 0x11 ) { int pos2 = pos + bin.readLE(4) + 4; while (pos < pos2) lens[pos ++] = (byte) 0; } else if (symbol == 0x12 ) { int pos2 = pos + bin.readLE(5) + 20; while (pos < pos2) lens[pos ++] = (byte) 0; } else if (symbol == 0x13 ) { int pos2 = pos + bin.readLE(1) + 4; symbol = lens[pos] - preTree.readHuffmanSymbol(bin); if (symbol < 0) symbol += 0x11; while (pos < pos2) lens[pos ++] = (byte) symbol; } else { symbol = lens[pos] - symbol; if ( symbol < 0) symbol += 0x11; lens[pos ++] = (byte) symbol; } } } /** * decodes one huffman symbol from the bitstream using the * stated table and return it. * @throws IOException */ int readHuffmanSymbol(BitsInputStream bin) throws IOException { int next = bin.peekUnder(16); /* TODO: it's very strange that bin.peek(bits) will raise EOFException, * we have to use peekUnder(bits) here, but how should it happen like this? */ int symbol = symbols[bin.peekUnder(bits)]; if ( symbol >= max_symbol) { int j = 1 << (16 - bits); do { j >>= 1; symbol <<= 1; symbol |= (next & j) > 0 ? 1 : 0; symbol = symbols[symbol]; } while ( symbol >= max_symbol ); } bin.readLE(lens[symbol]); return symbol; } public void clear() { for( int i = 0; i < lens.length; i ++) lens[i] = 0; } }