| /* Inflater.java - Decompress a data stream | |
| Copyright (C) 1999, 2000, 2001, 2003, 2005 Free Software Foundation, Inc. | |
| This file is part of GNU Classpath. | |
| GNU Classpath 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, or (at your option) | |
| any later version. | |
| GNU Classpath 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 GNU Classpath; see the file COPYING. If not, write to the | |
| Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | |
| 02110-1301 USA. | |
| Linking this library statically or dynamically with other modules is | |
| making a combined work based on this library. Thus, the terms and | |
| conditions of the GNU General Public License cover the whole | |
| combination. | |
| As a special exception, the copyright holders of this library give you | |
| permission to link this library with independent modules to produce an | |
| executable, regardless of the license terms of these independent | |
| modules, and to copy and distribute the resulting executable under | |
| terms of your choice, provided that you also meet, for each linked | |
| independent module, the terms and conditions of the license of that | |
| module. An independent module is a module which is not derived from | |
| or based on this library. If you modify this library, you may extend | |
| this exception to your version of the library, but you are not | |
| obligated to do so. If you do not wish to do so, delete this | |
| exception statement from your version. */ | |
| package java.util.zip; | |
| /* Written using on-line Java Platform 1.2 API Specification | |
| * and JCL book. | |
| * Believed complete and correct. | |
| */ | |
| /** | |
| * Inflater is used to decompress data that has been compressed according | |
| * to the "deflate" standard described in rfc1950. | |
| * | |
| * The usage is as following. First you have to set some input with | |
| * <code>setInput()</code>, then inflate() it. If inflate doesn't | |
| * inflate any bytes there may be three reasons: | |
| * <ul> | |
| * <li>needsInput() returns true because the input buffer is empty. | |
| * You have to provide more input with <code>setInput()</code>. | |
| * NOTE: needsInput() also returns true when, the stream is finished. | |
| * </li> | |
| * <li>needsDictionary() returns true, you have to provide a preset | |
| * dictionary with <code>setDictionary()</code>.</li> | |
| * <li>finished() returns true, the inflater has finished.</li> | |
| * </ul> | |
| * Once the first output byte is produced, a dictionary will not be | |
| * needed at a later stage. | |
| * | |
| * @author John Leuner, Jochen Hoenicke | |
| * @author Tom Tromey | |
| * @date May 17, 1999 | |
| * @since JDK 1.1 | |
| */ | |
| public class Inflater | |
| { | |
| /* Copy lengths for literal codes 257..285 */ | |
| private static final int CPLENS[] = | |
| { | |
| 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31, | |
| 35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258 | |
| }; | |
| /* Extra bits for literal codes 257..285 */ | |
| private static final int CPLEXT[] = | |
| { | |
| 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, | |
| 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0 | |
| }; | |
| /* Copy offsets for distance codes 0..29 */ | |
| private static final int CPDIST[] = { | |
| 1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, | |
| 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, | |
| 8193, 12289, 16385, 24577 | |
| }; | |
| /* Extra bits for distance codes */ | |
| private static final int CPDEXT[] = { | |
| 0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, | |
| 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, | |
| 12, 12, 13, 13 | |
| }; | |
| /* This are the state in which the inflater can be. */ | |
| private static final int DECODE_HEADER = 0; | |
| private static final int DECODE_DICT = 1; | |
| private static final int DECODE_BLOCKS = 2; | |
| private static final int DECODE_STORED_LEN1 = 3; | |
| private static final int DECODE_STORED_LEN2 = 4; | |
| private static final int DECODE_STORED = 5; | |
| private static final int DECODE_DYN_HEADER = 6; | |
| private static final int DECODE_HUFFMAN = 7; | |
| private static final int DECODE_HUFFMAN_LENBITS = 8; | |
| private static final int DECODE_HUFFMAN_DIST = 9; | |
| private static final int DECODE_HUFFMAN_DISTBITS = 10; | |
| private static final int DECODE_CHKSUM = 11; | |
| private static final int FINISHED = 12; | |
| /** This variable contains the current state. */ | |
| private int mode; | |
| /** | |
| * The adler checksum of the dictionary or of the decompressed | |
| * stream, as it is written in the header resp. footer of the | |
| * compressed stream. <br> | |
| * | |
| * Only valid if mode is DECODE_DICT or DECODE_CHKSUM. | |
| */ | |
| private int readAdler; | |
| /** | |
| * The number of bits needed to complete the current state. This | |
| * is valid, if mode is DECODE_DICT, DECODE_CHKSUM, | |
| * DECODE_HUFFMAN_LENBITS or DECODE_HUFFMAN_DISTBITS. | |
| */ | |
| private int neededBits; | |
| private int repLength, repDist; | |
| private int uncomprLen; | |
| /** | |
| * True, if the last block flag was set in the last block of the | |
| * inflated stream. This means that the stream ends after the | |
| * current block. | |
| */ | |
| private boolean isLastBlock; | |
| /** | |
| * The total number of inflated bytes. | |
| */ | |
| private int totalOut; | |
| /** | |
| * The total number of bytes set with setInput(). This is not the | |
| * value returned by getTotalIn(), since this also includes the | |
| * unprocessed input. | |
| */ | |
| private int totalIn; | |
| /** | |
| * This variable stores the nowrap flag that was given to the constructor. | |
| * True means, that the inflated stream doesn't contain a header nor the | |
| * checksum in the footer. | |
| */ | |
| private boolean nowrap; | |
| private StreamManipulator input; | |
| private OutputWindow outputWindow; | |
| private InflaterDynHeader dynHeader; | |
| private InflaterHuffmanTree litlenTree, distTree; | |
| private Adler32 adler; | |
| /** | |
| * Creates a new inflater. | |
| */ | |
| public Inflater () | |
| { | |
| this (false); | |
| } | |
| /** | |
| * Creates a new inflater. | |
| * @param nowrap true if no header and checksum field appears in the | |
| * stream. This is used for GZIPed input. For compatibility with | |
| * Sun JDK you should provide one byte of input more than needed in | |
| * this case. | |
| */ | |
| public Inflater (boolean nowrap) | |
| { | |
| this.nowrap = nowrap; | |
| this.adler = new Adler32(); | |
| input = new StreamManipulator(); | |
| outputWindow = new OutputWindow(); | |
| mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER; | |
| } | |
| /** | |
| * Finalizes this object. | |
| */ | |
| protected void finalize () | |
| { | |
| /* Exists only for compatibility */ | |
| } | |
| /** | |
| * Frees all objects allocated by the inflater. There's no reason | |
| * to call this, since you can just rely on garbage collection (even | |
| * for the Sun implementation). Exists only for compatibility | |
| * with Sun's JDK, where the compressor allocates native memory. | |
| * If you call any method (even reset) afterwards the behaviour is | |
| * <i>undefined</i>. | |
| */ | |
| public void end () | |
| { | |
| outputWindow = null; | |
| input = null; | |
| dynHeader = null; | |
| litlenTree = null; | |
| distTree = null; | |
| adler = null; | |
| } | |
| /** | |
| * Returns true, if the inflater has finished. This means, that no | |
| * input is needed and no output can be produced. | |
| */ | |
| public boolean finished() | |
| { | |
| return mode == FINISHED && outputWindow.getAvailable() == 0; | |
| } | |
| /** | |
| * Gets the adler checksum. This is either the checksum of all | |
| * uncompressed bytes returned by inflate(), or if needsDictionary() | |
| * returns true (and thus no output was yet produced) this is the | |
| * adler checksum of the expected dictionary. | |
| * @returns the adler checksum. | |
| */ | |
| public int getAdler() | |
| { | |
| return needsDictionary() ? readAdler : (int) adler.getValue(); | |
| } | |
| /** | |
| * Gets the number of unprocessed input. Useful, if the end of the | |
| * stream is reached and you want to further process the bytes after | |
| * the deflate stream. | |
| * @return the number of bytes of the input which were not processed. | |
| */ | |
| public int getRemaining() | |
| { | |
| return input.getAvailableBytes(); | |
| } | |
| /** | |
| * Gets the total number of processed compressed input bytes. | |
| * @return the total number of bytes of processed input bytes. | |
| */ | |
| public int getTotalIn() | |
| { | |
| return totalIn - getRemaining(); | |
| } | |
| /** | |
| * Gets the total number of output bytes returned by inflate(). | |
| * @return the total number of output bytes. | |
| */ | |
| public int getTotalOut() | |
| { | |
| return totalOut; | |
| } | |
| /** | |
| * Inflates the compressed stream to the output buffer. If this | |
| * returns 0, you should check, whether needsDictionary(), | |
| * needsInput() or finished() returns true, to determine why no | |
| * further output is produced. | |
| * @param buf the output buffer. | |
| * @return the number of bytes written to the buffer, 0 if no further | |
| * output can be produced. | |
| * @exception DataFormatException if deflated stream is invalid. | |
| * @exception IllegalArgumentException if buf has length 0. | |
| */ | |
| public int inflate (byte[] buf) throws DataFormatException | |
| { | |
| return inflate (buf, 0, buf.length); | |
| } | |
| /** | |
| * Inflates the compressed stream to the output buffer. If this | |
| * returns 0, you should check, whether needsDictionary(), | |
| * needsInput() or finished() returns true, to determine why no | |
| * further output is produced. | |
| * @param buf the output buffer. | |
| * @param off the offset into buffer where the output should start. | |
| * @param len the maximum length of the output. | |
| * @return the number of bytes written to the buffer, 0 if no further | |
| * output can be produced. | |
| * @exception DataFormatException if deflated stream is invalid. | |
| * @exception IndexOutOfBoundsException if the off and/or len are wrong. | |
| */ | |
| public int inflate (byte[] buf, int off, int len) throws DataFormatException | |
| { | |
| /* Special case: len may be zero */ | |
| if (len == 0) | |
| return 0; | |
| /* Check for correct buff, off, len triple */ | |
| if (0 > off || off > off + len || off + len > buf.length) | |
| throw new ArrayIndexOutOfBoundsException(); | |
| int count = 0; | |
| int more; | |
| do | |
| { | |
| if (mode != DECODE_CHKSUM) | |
| { | |
| /* Don't give away any output, if we are waiting for the | |
| * checksum in the input stream. | |
| * | |
| * With this trick we have always: | |
| * needsInput() and not finished() | |
| * implies more output can be produced. | |
| */ | |
| more = outputWindow.copyOutput(buf, off, len); | |
| adler.update(buf, off, more); | |
| off += more; | |
| count += more; | |
| totalOut += more; | |
| len -= more; | |
| if (len == 0) | |
| return count; | |
| } | |
| } | |
| while (decode() || (outputWindow.getAvailable() > 0 | |
| && mode != DECODE_CHKSUM)); | |
| return count; | |
| } | |
| /** | |
| * Returns true, if a preset dictionary is needed to inflate the input. | |
| */ | |
| public boolean needsDictionary () | |
| { | |
| return mode == DECODE_DICT && neededBits == 0; | |
| } | |
| /** | |
| * Returns true, if the input buffer is empty. | |
| * You should then call setInput(). <br> | |
| * | |
| * <em>NOTE</em>: This method also returns true when the stream is finished. | |
| */ | |
| public boolean needsInput () | |
| { | |
| return input.needsInput (); | |
| } | |
| /** | |
| * Resets the inflater so that a new stream can be decompressed. All | |
| * pending input and output will be discarded. | |
| */ | |
| public void reset () | |
| { | |
| mode = nowrap ? DECODE_BLOCKS : DECODE_HEADER; | |
| totalIn = totalOut = 0; | |
| input.reset(); | |
| outputWindow.reset(); | |
| dynHeader = null; | |
| litlenTree = null; | |
| distTree = null; | |
| isLastBlock = false; | |
| adler.reset(); | |
| } | |
| /** | |
| * Sets the preset dictionary. This should only be called, if | |
| * needsDictionary() returns true and it should set the same | |
| * dictionary, that was used for deflating. The getAdler() | |
| * function returns the checksum of the dictionary needed. | |
| * @param buffer the dictionary. | |
| * @exception IllegalStateException if no dictionary is needed. | |
| * @exception IllegalArgumentException if the dictionary checksum is | |
| * wrong. | |
| */ | |
| public void setDictionary (byte[] buffer) | |
| { | |
| setDictionary(buffer, 0, buffer.length); | |
| } | |
| /** | |
| * Sets the preset dictionary. This should only be called, if | |
| * needsDictionary() returns true and it should set the same | |
| * dictionary, that was used for deflating. The getAdler() | |
| * function returns the checksum of the dictionary needed. | |
| * @param buffer the dictionary. | |
| * @param off the offset into buffer where the dictionary starts. | |
| * @param len the length of the dictionary. | |
| * @exception IllegalStateException if no dictionary is needed. | |
| * @exception IllegalArgumentException if the dictionary checksum is | |
| * wrong. | |
| * @exception IndexOutOfBoundsException if the off and/or len are wrong. | |
| */ | |
| public void setDictionary (byte[] buffer, int off, int len) | |
| { | |
| if (!needsDictionary()) | |
| throw new IllegalStateException(); | |
| adler.update(buffer, off, len); | |
| if ((int) adler.getValue() != readAdler) | |
| throw new IllegalArgumentException("Wrong adler checksum"); | |
| adler.reset(); | |
| outputWindow.copyDict(buffer, off, len); | |
| mode = DECODE_BLOCKS; | |
| } | |
| /** | |
| * Sets the input. This should only be called, if needsInput() | |
| * returns true. | |
| * @param buf the input. | |
| * @exception IllegalStateException if no input is needed. | |
| */ | |
| public void setInput (byte[] buf) | |
| { | |
| setInput (buf, 0, buf.length); | |
| } | |
| /** | |
| * Sets the input. This should only be called, if needsInput() | |
| * returns true. | |
| * @param buf the input. | |
| * @param off the offset into buffer where the input starts. | |
| * @param len the length of the input. | |
| * @exception IllegalStateException if no input is needed. | |
| * @exception IndexOutOfBoundsException if the off and/or len are wrong. | |
| */ | |
| public void setInput (byte[] buf, int off, int len) | |
| { | |
| input.setInput (buf, off, len); | |
| totalIn += len; | |
| } | |
| /** | |
| * Decodes the deflate header. | |
| * @return false if more input is needed. | |
| * @exception DataFormatException if header is invalid. | |
| */ | |
| private boolean decodeHeader () throws DataFormatException | |
| { | |
| int header = input.peekBits(16); | |
| if (header < 0) | |
| return false; | |
| input.dropBits(16); | |
| /* The header is written in "wrong" byte order */ | |
| header = ((header << 8) | (header >> 8)) & 0xffff; | |
| if (header % 31 != 0) | |
| throw new DataFormatException("Header checksum illegal"); | |
| if ((header & 0x0f00) != (Deflater.DEFLATED << 8)) | |
| throw new DataFormatException("Compression Method unknown"); | |
| /* Maximum size of the backwards window in bits. | |
| * We currently ignore this, but we could use it to make the | |
| * inflater window more space efficient. On the other hand the | |
| * full window (15 bits) is needed most times, anyway. | |
| int max_wbits = ((header & 0x7000) >> 12) + 8; | |
| */ | |
| if ((header & 0x0020) == 0) // Dictionary flag? | |
| { | |
| mode = DECODE_BLOCKS; | |
| } | |
| else | |
| { | |
| mode = DECODE_DICT; | |
| neededBits = 32; | |
| } | |
| return true; | |
| } | |
| /** | |
| * Decodes the dictionary checksum after the deflate header. | |
| * @return false if more input is needed. | |
| */ | |
| private boolean decodeDict () | |
| { | |
| while (neededBits > 0) | |
| { | |
| int dictByte = input.peekBits(8); | |
| if (dictByte < 0) | |
| return false; | |
| input.dropBits(8); | |
| readAdler = (readAdler << 8) | dictByte; | |
| neededBits -= 8; | |
| } | |
| return false; | |
| } | |
| /** | |
| * Decodes the huffman encoded symbols in the input stream. | |
| * @return false if more input is needed, true if output window is | |
| * full or the current block ends. | |
| * @exception DataFormatException if deflated stream is invalid. | |
| */ | |
| private boolean decodeHuffman () throws DataFormatException | |
| { | |
| int free = outputWindow.getFreeSpace(); | |
| while (free >= 258) | |
| { | |
| int symbol; | |
| switch (mode) | |
| { | |
| case DECODE_HUFFMAN: | |
| /* This is the inner loop so it is optimized a bit */ | |
| while (((symbol = litlenTree.getSymbol(input)) & ~0xff) == 0) | |
| { | |
| outputWindow.write(symbol); | |
| if (--free < 258) | |
| return true; | |
| } | |
| if (symbol < 257) | |
| { | |
| if (symbol < 0) | |
| return false; | |
| else | |
| { | |
| /* symbol == 256: end of block */ | |
| distTree = null; | |
| litlenTree = null; | |
| mode = DECODE_BLOCKS; | |
| return true; | |
| } | |
| } | |
| try | |
| { | |
| repLength = CPLENS[symbol - 257]; | |
| neededBits = CPLEXT[symbol - 257]; | |
| } | |
| catch (ArrayIndexOutOfBoundsException ex) | |
| { | |
| throw new DataFormatException("Illegal rep length code"); | |
| } | |
| /* fall through */ | |
| case DECODE_HUFFMAN_LENBITS: | |
| if (neededBits > 0) | |
| { | |
| mode = DECODE_HUFFMAN_LENBITS; | |
| int i = input.peekBits(neededBits); | |
| if (i < 0) | |
| return false; | |
| input.dropBits(neededBits); | |
| repLength += i; | |
| } | |
| mode = DECODE_HUFFMAN_DIST; | |
| /* fall through */ | |
| case DECODE_HUFFMAN_DIST: | |
| symbol = distTree.getSymbol(input); | |
| if (symbol < 0) | |
| return false; | |
| try | |
| { | |
| repDist = CPDIST[symbol]; | |
| neededBits = CPDEXT[symbol]; | |
| } | |
| catch (ArrayIndexOutOfBoundsException ex) | |
| { | |
| throw new DataFormatException("Illegal rep dist code"); | |
| } | |
| /* fall through */ | |
| case DECODE_HUFFMAN_DISTBITS: | |
| if (neededBits > 0) | |
| { | |
| mode = DECODE_HUFFMAN_DISTBITS; | |
| int i = input.peekBits(neededBits); | |
| if (i < 0) | |
| return false; | |
| input.dropBits(neededBits); | |
| repDist += i; | |
| } | |
| outputWindow.repeat(repLength, repDist); | |
| free -= repLength; | |
| mode = DECODE_HUFFMAN; | |
| break; | |
| default: | |
| throw new IllegalStateException(); | |
| } | |
| } | |
| return true; | |
| } | |
| /** | |
| * Decodes the adler checksum after the deflate stream. | |
| * @return false if more input is needed. | |
| * @exception DataFormatException if checksum doesn't match. | |
| */ | |
| private boolean decodeChksum () throws DataFormatException | |
| { | |
| while (neededBits > 0) | |
| { | |
| int chkByte = input.peekBits(8); | |
| if (chkByte < 0) | |
| return false; | |
| input.dropBits(8); | |
| readAdler = (readAdler << 8) | chkByte; | |
| neededBits -= 8; | |
| } | |
| if ((int) adler.getValue() != readAdler) | |
| throw new DataFormatException("Adler chksum doesn't match: " | |
| +Integer.toHexString((int)adler.getValue()) | |
| +" vs. "+Integer.toHexString(readAdler)); | |
| mode = FINISHED; | |
| return false; | |
| } | |
| /** | |
| * Decodes the deflated stream. | |
| * @return false if more input is needed, or if finished. | |
| * @exception DataFormatException if deflated stream is invalid. | |
| */ | |
| private boolean decode () throws DataFormatException | |
| { | |
| switch (mode) | |
| { | |
| case DECODE_HEADER: | |
| return decodeHeader(); | |
| case DECODE_DICT: | |
| return decodeDict(); | |
| case DECODE_CHKSUM: | |
| return decodeChksum(); | |
| case DECODE_BLOCKS: | |
| if (isLastBlock) | |
| { | |
| if (nowrap) | |
| { | |
| mode = FINISHED; | |
| return false; | |
| } | |
| else | |
| { | |
| input.skipToByteBoundary(); | |
| neededBits = 32; | |
| mode = DECODE_CHKSUM; | |
| return true; | |
| } | |
| } | |
| int type = input.peekBits(3); | |
| if (type < 0) | |
| return false; | |
| input.dropBits(3); | |
| if ((type & 1) != 0) | |
| isLastBlock = true; | |
| switch (type >> 1) | |
| { | |
| case DeflaterConstants.STORED_BLOCK: | |
| input.skipToByteBoundary(); | |
| mode = DECODE_STORED_LEN1; | |
| break; | |
| case DeflaterConstants.STATIC_TREES: | |
| litlenTree = InflaterHuffmanTree.defLitLenTree; | |
| distTree = InflaterHuffmanTree.defDistTree; | |
| mode = DECODE_HUFFMAN; | |
| break; | |
| case DeflaterConstants.DYN_TREES: | |
| dynHeader = new InflaterDynHeader(); | |
| mode = DECODE_DYN_HEADER; | |
| break; | |
| default: | |
| throw new DataFormatException("Unknown block type "+type); | |
| } | |
| return true; | |
| case DECODE_STORED_LEN1: | |
| { | |
| if ((uncomprLen = input.peekBits(16)) < 0) | |
| return false; | |
| input.dropBits(16); | |
| mode = DECODE_STORED_LEN2; | |
| } | |
| /* fall through */ | |
| case DECODE_STORED_LEN2: | |
| { | |
| int nlen = input.peekBits(16); | |
| if (nlen < 0) | |
| return false; | |
| input.dropBits(16); | |
| if (nlen != (uncomprLen ^ 0xffff)) | |
| throw new DataFormatException("broken uncompressed block"); | |
| mode = DECODE_STORED; | |
| } | |
| /* fall through */ | |
| case DECODE_STORED: | |
| { | |
| int more = outputWindow.copyStored(input, uncomprLen); | |
| uncomprLen -= more; | |
| if (uncomprLen == 0) | |
| { | |
| mode = DECODE_BLOCKS; | |
| return true; | |
| } | |
| return !input.needsInput(); | |
| } | |
| case DECODE_DYN_HEADER: | |
| if (!dynHeader.decode(input)) | |
| return false; | |
| litlenTree = dynHeader.buildLitLenTree(); | |
| distTree = dynHeader.buildDistTree(); | |
| mode = DECODE_HUFFMAN; | |
| /* fall through */ | |
| case DECODE_HUFFMAN: | |
| case DECODE_HUFFMAN_LENBITS: | |
| case DECODE_HUFFMAN_DIST: | |
| case DECODE_HUFFMAN_DISTBITS: | |
| return decodeHuffman(); | |
| case FINISHED: | |
| return false; | |
| default: | |
| throw new IllegalStateException(); | |
| } | |
| } | |
| } |