diff -r a9e506a27b55 -r 0d277415ed02 emul/mini/src/main/java/java/util/zip/Inflater.java
--- a/emul/mini/src/main/java/java/util/zip/Inflater.java Wed Feb 06 15:47:06 2013 +0100
+++ b/emul/mini/src/main/java/java/util/zip/Inflater.java Thu Feb 07 12:58:12 2013 +0100
@@ -1,44 +1,30 @@
-/* Inflater.java - Decompress a data stream
- Copyright (C) 1999, 2000, 2001, 2003 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., 59 Temple Place, Suite 330, Boston, MA
-02111-1307 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. */
+/*
+ * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code 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
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
package java.util.zip;
-import org.apidesign.bck2brwsr.emul.lang.System;
-
/**
* This class provides support for general purpose decompression using the
* popular ZLIB compression library. The ZLIB compression library was
@@ -84,1399 +70,241 @@
* @author David Connelly
*
*/
-
-/* 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
- * setInput()
, then inflate() it. If inflate doesn't
- * inflate any bytes there may be three reasons:
- *
setInput()
.
- * NOTE: needsInput() also returns true when, the stream is finished.
- * setDictionary()
.+ * Note: When using the 'nowrap' option it is also necessary to provide + * an extra "dummy" byte as input. This is required by the ZLIB native + * library in order to support certain optimizations. + * + * @param nowrap if true then support GZIP compatible compression */ - - 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(); - } - } - - - interface DeflaterConstants { - final static boolean DEBUGGING = false; - - final static int STORED_BLOCK = 0; - final static int STATIC_TREES = 1; - final static int DYN_TREES = 2; - final static int PRESET_DICT = 0x20; - - final static int DEFAULT_MEM_LEVEL = 8; - - final static int MAX_MATCH = 258; - final static int MIN_MATCH = 3; - - final static int MAX_WBITS = 15; - final static int WSIZE = 1 << MAX_WBITS; - final static int WMASK = WSIZE - 1; - - final static int HASH_BITS = DEFAULT_MEM_LEVEL + 7; - final static int HASH_SIZE = 1 << HASH_BITS; - final static int HASH_MASK = HASH_SIZE - 1; - final static int HASH_SHIFT = (HASH_BITS + MIN_MATCH - 1) / MIN_MATCH; - - final static int MIN_LOOKAHEAD = MAX_MATCH + MIN_MATCH + 1; - final static int MAX_DIST = WSIZE - MIN_LOOKAHEAD; - - final static int PENDING_BUF_SIZE = 1 << (DEFAULT_MEM_LEVEL + 8); - final static int MAX_BLOCK_SIZE = Math.min(65535, PENDING_BUF_SIZE-5); - - final static int DEFLATE_STORED = 0; - final static int DEFLATE_FAST = 1; - final static int DEFLATE_SLOW = 2; - - final static int GOOD_LENGTH[] = { 0,4, 4, 4, 4, 8, 8, 8, 32, 32 }; - final static int MAX_LAZY[] = { 0,4, 5, 6, 4,16, 16, 32, 128, 258 }; - final static int NICE_LENGTH[] = { 0,8,16,32,16,32,128,128, 258, 258 }; - final static int MAX_CHAIN[] = { 0,4, 8,32,16,32,128,256,1024,4096 }; - final static int COMPR_FUNC[] = { 0,1, 1, 1, 1, 2, 2, 2, 2, 2 }; - } - private static class InflaterHuffmanTree { - private final static int MAX_BITLEN = 15; - private short[] tree; - - public static InflaterHuffmanTree defLitLenTree, defDistTree; - - static - { - try - { - byte[] codeLengths = new byte[288]; - int i = 0; - while (i < 144) - codeLengths[i++] = 8; - while (i < 256) - codeLengths[i++] = 9; - while (i < 280) - codeLengths[i++] = 7; - while (i < 288) - codeLengths[i++] = 8; - defLitLenTree = new InflaterHuffmanTree(codeLengths); - - codeLengths = new byte[32]; - i = 0; - while (i < 32) - codeLengths[i++] = 5; - defDistTree = new InflaterHuffmanTree(codeLengths); - } - catch (DataFormatException ex) - { - throw new IllegalStateException - ("InflaterHuffmanTree: static tree length illegal"); - } - } - - /** - * Constructs a Huffman tree from the array of code lengths. - * - * @param codeLengths the array of code lengths - */ - public InflaterHuffmanTree(byte[] codeLengths) throws DataFormatException - { - buildTree(codeLengths); - } - - private void buildTree(byte[] codeLengths) throws DataFormatException - { - int[] blCount = new int[MAX_BITLEN+1]; - int[] nextCode = new int[MAX_BITLEN+1]; - for (int i = 0; i < codeLengths.length; i++) - { - int bits = codeLengths[i]; - if (bits > 0) - blCount[bits]++; - } - - int code = 0; - int treeSize = 512; - for (int bits = 1; bits <= MAX_BITLEN; bits++) - { - nextCode[bits] = code; - code += blCount[bits] << (16 - bits); - if (bits >= 10) - { - /* We need an extra table for bit lengths >= 10. */ - int start = nextCode[bits] & 0x1ff80; - int end = code & 0x1ff80; - treeSize += (end - start) >> (16 - bits); - } - } - if (code != 65536) - throw new DataFormatException("Code lengths don't add up properly."); - - fillTable1(treeSize, code, blCount); - - for (int i = 0; i < codeLengths.length; i++) - { - int bits = codeLengths[i]; - if (bits == 0) - continue; - code = nextCode[bits]; - int revcode = bitReverse(code); - if (bits <= 9) - { - do - { - tree[revcode] = (short) ((i << 4) | bits); - revcode += 1 << bits; - } - while (revcode < 512); - } - else - { - int subTree = tree[revcode & 511]; - int treeLen = 1 << (subTree & 15); - subTree = -(subTree >> 4); - do - { - tree[subTree | (revcode >> 9)] = (short) ((i << 4) | bits); - revcode += 1 << bits; - } - while (revcode < treeLen); - } - nextCode[bits] = code + (1 << (16 - bits)); - } - } - private final static String bit4Reverse = - "\000\010\004\014\002\012\006\016\001\011\005\015\003\013\007\017"; - 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)); - } - - /** - * Reads the next symbol from input. The symbol is encoded using the - * huffman tree. - * @param input the input source. - * @return the next symbol, or -1 if not enough input is available. - */ - public int getSymbol(StreamManipulator input) throws DataFormatException - { - int lookahead, symbol; - if ((lookahead = input.peekBits(9)) >= 0) - { - if ((symbol = tree[lookahead]) >= 0) - { - input.dropBits(symbol & 15); - return symbol >> 4; - } - int subtree = -(symbol >> 4); - int bitlen = symbol & 15; - if ((lookahead = input.peekBits(bitlen)) >= 0) - { - symbol = tree[subtree | (lookahead >> 9)]; - input.dropBits(symbol & 15); - return symbol >> 4; - } - else - { - int bits = input.getAvailableBits(); - lookahead = input.peekBits(bits); - symbol = tree[subtree | (lookahead >> 9)]; - if ((symbol & 15) <= bits) - { - input.dropBits(symbol & 15); - return symbol >> 4; - } - else - return -1; - } - } - else - { - int bits = input.getAvailableBits(); - lookahead = input.peekBits(bits); - symbol = tree[lookahead]; - if (symbol >= 0 && (symbol & 15) <= bits) - { - input.dropBits(symbol & 15); - return symbol >> 4; - } - else - return -1; - } - } - - private void fillTable1(int treeSize, int code, int[] blCount) { - /* Now create and fill the extra tables from longest to shortest - * bit len. This way the sub trees will be aligned. - */ - tree = new short[treeSize]; - int treePtr = 512; - for (int bits = MAX_BITLEN; bits >= 10; bits--) { - int end = code & 0x1ff80; - code -= blCount[bits] << (16 - bits); - int start = code & 0x1ff80; - final int inc = 1 << 7; - fillTable2(start, end, inc, treePtr, bits); - } - } - - private void fillTable2(int start, int end, final int inc, int treePtr, int bits) { - for (int i = start; i < end; i += inc) { - final short br = bitReverse(i); - tree[br] = (short) ((-treePtr << 4) | bits); - treePtr += 1 << (bits - 9); - } + public Inflater(boolean nowrap) { + if (getClass() == org.apidesign.bck2brwsr.emul.zip.Inflater.class) { + impl = null; + } else { + impl = new org.apidesign.bck2brwsr.emul.zip.Inflater(nowrap); } } - private static class InflaterDynHeader + + /** + * Creates a new decompressor. + */ + public Inflater() { + this(false); + } + + /** + * Sets input data for decompression. Should be called whenever + * needsInput() returns true indicating that more input data is + * required. + * @param b the input data bytes + * @param off the start offset of the input data + * @param len the length of the input data + * @see Inflater#needsInput + */ + public void setInput(byte[] b, int off, int len) { + impl.setInput(b, off, len); + } + + /** + * Sets input data for decompression. Should be called whenever + * needsInput() returns true indicating that more input data is + * required. + * @param b the input data bytes + * @see Inflater#needsInput + */ + public void setInput(byte[] b) { + impl.setInput(b); + } + + /** + * Sets the preset dictionary to the given array of bytes. Should be + * called when inflate() returns 0 and needsDictionary() returns true + * indicating that a preset dictionary is required. The method getAdler() + * can be used to get the Adler-32 value of the dictionary needed. + * @param b the dictionary data bytes + * @param off the start offset of the data + * @param len the length of the data + * @see Inflater#needsDictionary + * @see Inflater#getAdler + */ + public void setDictionary(byte[] b, int off, int len) { + impl.setDictionary(b, off, len); + } + + /** + * Sets the preset dictionary to the given array of bytes. Should be + * called when inflate() returns 0 and needsDictionary() returns true + * indicating that a preset dictionary is required. The method getAdler() + * can be used to get the Adler-32 value of the dictionary needed. + * @param b the dictionary data bytes + * @see Inflater#needsDictionary + * @see Inflater#getAdler + */ + public void setDictionary(byte[] b) { + impl.setDictionary(b); + } + + /** + * Returns the total number of bytes remaining in the input buffer. + * This can be used to find out what bytes still remain in the input + * buffer after decompression has finished. + * @return the total number of bytes remaining in the input buffer + */ + public int getRemaining() { + return impl.getRemaining(); + } + + /** + * Returns true if no data remains in the input buffer. This can + * be used to determine if #setInput should be called in order + * to provide more input. + * @return true if no data remains in the input buffer + */ + public boolean needsInput() { + return impl.needsInput(); + } + + /** + * Returns true if a preset dictionary is needed for decompression. + * @return true if a preset dictionary is needed for decompression + * @see Inflater#setDictionary + */ + public boolean needsDictionary() { + return impl.needsDictionary(); + } + + /** + * Returns true if the end of the compressed data stream has been + * reached. + * @return true if the end of the compressed data stream has been + * reached + */ + public boolean finished() { + return impl.finished(); + } + + /** + * Uncompresses bytes into specified buffer. Returns actual number + * of bytes uncompressed. A return value of 0 indicates that + * needsInput() or needsDictionary() should be called in order to + * determine if more input data or a preset dictionary is required. + * In the latter case, getAdler() can be used to get the Adler-32 + * value of the dictionary required. + * @param b the buffer for the uncompressed data + * @param off the start offset of the data + * @param len the maximum number of uncompressed bytes + * @return the actual number of uncompressed bytes + * @exception DataFormatException if the compressed data format is invalid + * @see Inflater#needsInput + * @see Inflater#needsDictionary + */ + public int inflate(byte[] b, int off, int len) + throws DataFormatException { - private static final int LNUM = 0; - private static final int DNUM = 1; - private static final int BLNUM = 2; - private static final int BLLENS = 3; - private static final int LENS = 4; - private static final int REPS = 5; + return impl.inflate(b, off, len); + } - private static final int repMin[] = { 3, 3, 11 }; - private static final int repBits[] = { 2, 3, 7 }; + /** + * Uncompresses bytes into specified buffer. Returns actual number + * of bytes uncompressed. A return value of 0 indicates that + * needsInput() or needsDictionary() should be called in order to + * determine if more input data or a preset dictionary is required. + * In the latter case, getAdler() can be used to get the Adler-32 + * value of the dictionary required. + * @param b the buffer for the uncompressed data + * @return the actual number of uncompressed bytes + * @exception DataFormatException if the compressed data format is invalid + * @see Inflater#needsInput + * @see Inflater#needsDictionary + */ + public int inflate(byte[] b) throws DataFormatException { + return impl.inflate(b); + } + /** + * Returns the ADLER-32 value of the uncompressed data. + * @return the ADLER-32 value of the uncompressed data + */ + public int getAdler() { + return impl.getAdler(); + } - private byte[] blLens; - private byte[] litdistLens; + /** + * Returns the total number of compressed bytes input so far. + * + *
Since the number of bytes may be greater than + * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now + * the preferred means of obtaining this information.
+ * + * @return the total number of compressed bytes input so far + */ + public int getTotalIn() { + return impl.getTotalIn(); + } - private InflaterHuffmanTree blTree; + /** + * Returns the total number of compressed bytes input so far. + * + * @return the total (non-negative) number of compressed bytes input so far + * @since 1.5 + */ + public long getBytesRead() { + return impl.getBytesRead(); + } - private int mode; - private int lnum, dnum, blnum, num; - private int repSymbol; - private byte lastLen; - private int ptr; + /** + * Returns the total number of uncompressed bytes output so far. + * + *Since the number of bytes may be greater than + * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now + * the preferred means of obtaining this information.
+ * + * @return the total number of uncompressed bytes output so far + */ + public int getTotalOut() { + return impl.getTotalOut(); + } - 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 }; + /** + * Returns the total number of uncompressed bytes output so far. + * + * @return the total (non-negative) number of uncompressed bytes output so far + * @since 1.5 + */ + public long getBytesWritten() { + return impl.getBytesWritten(); + } - public InflaterDynHeader() - { - } + /** + * Resets inflater so that a new set of input data can be processed. + */ + public void reset() { + impl.reset(); + } - public boolean decode(StreamManipulator input) throws DataFormatException - { - decode_loop: - for (;;) - { - switch (mode) - { - case LNUM: - lnum = input.peekBits(5); - if (lnum < 0) - return false; - lnum += 257; - input.dropBits(5); - // System.err.println("LNUM: "+lnum); - mode = DNUM; - /* fall through */ - case DNUM: - dnum = input.peekBits(5); - if (dnum < 0) - return false; - dnum++; - input.dropBits(5); - // System.err.println("DNUM: "+dnum); - num = lnum+dnum; - litdistLens = new byte[num]; - mode = BLNUM; - /* fall through */ - case BLNUM: - blnum = input.peekBits(4); - if (blnum < 0) - return false; - blnum += 4; - input.dropBits(4); - blLens = new byte[19]; - ptr = 0; - // System.err.println("BLNUM: "+blnum); - mode = BLLENS; - /* fall through */ - case BLLENS: - while (ptr < blnum) - { - int len = input.peekBits(3); - if (len < 0) - return false; - input.dropBits(3); - // System.err.println("blLens["+BL_ORDER[ptr]+"]: "+len); - blLens[BL_ORDER[ptr]] = (byte) len; - ptr++; - } - blTree = new InflaterHuffmanTree(blLens); - blLens = null; - ptr = 0; - mode = LENS; - /* fall through */ - case LENS: - { - int symbol; - while (((symbol = blTree.getSymbol(input)) & ~15) == 0) - { - /* Normal case: symbol in [0..15] */ + /** + * Closes the decompressor and discards any unprocessed input. + * This method should be called when the decompressor is no longer + * being used, but will also be called automatically by the finalize() + * method. Once this method is called, the behavior of the Inflater + * object is undefined. + */ + public void end() { + impl.end(); + } - // System.err.println("litdistLens["+ptr+"]: "+symbol); - litdistLens[ptr++] = lastLen = (byte) symbol; - - if (ptr == num) - { - /* Finished */ - return true; - } - } - - /* need more input ? */ - if (symbol < 0) - return false; - - /* otherwise repeat code */ - if (symbol >= 17) - { - /* repeat zero */ - // System.err.println("repeating zero"); - lastLen = 0; - } - else - { - if (ptr == 0) - throw new DataFormatException(); - } - repSymbol = symbol-16; - mode = REPS; - } - /* fall through */ - - case REPS: - { - int bits = repBits[repSymbol]; - int count = input.peekBits(bits); - if (count < 0) - return false; - input.dropBits(bits); - count += repMin[repSymbol]; - // System.err.println("litdistLens repeated: "+count); - - if (ptr + count > num) - throw new DataFormatException(); - while (count-- > 0) - litdistLens[ptr++] = lastLen; - - if (ptr == num) - { - /* Finished */ - return true; - } - } - mode = LENS; - continue decode_loop; - } - } - } - - public InflaterHuffmanTree buildLitLenTree() throws DataFormatException - { - byte[] litlenLens = new byte[lnum]; - System.arraycopy(litdistLens, 0, litlenLens, 0, lnum); - return new InflaterHuffmanTree(litlenLens); - } - - public InflaterHuffmanTree buildDistTree() throws DataFormatException - { - byte[] distLens = new byte[dnum]; - System.arraycopy(litdistLens, lnum, distLens, 0, dnum); - return new InflaterHuffmanTree(distLens); - } + /** + * Closes the decompressor when garbage is collected. + */ + protected void finalize() { + end(); } - /** - * This class allows us to retrieve a specified amount of bits from - * the input buffer, as well as copy big byte blocks. - * - * It uses an int buffer to store up to 31 bits for direct - * manipulation. This guarantees that we can get at least 16 bits, - * but we only need at most 15, so this is all safe. - * - * There are some optimizations in this class, for example, you must - * never peek more then 8 bits more than needed, and you must first - * peek bits before you may drop them. This is not a general purpose - * class but optimized for the behaviour of the Inflater. - * - * @author John Leuner, Jochen Hoenicke - */ - - private static class StreamManipulator - { - private byte[] window; - private int window_start = 0; - private int window_end = 0; - - private int buffer = 0; - private int bits_in_buffer = 0; - - /** - * Get the next n bits but don't increase input pointer. n must be - * less or equal 16 and if you if this call succeeds, you must drop - * at least n-8 bits in the next call. - * - * @return the value of the bits, or -1 if not enough bits available. */ - public final int peekBits(int n) - { - if (bits_in_buffer < n) - { - if (window_start == window_end) - return -1; - buffer |= (window[window_start++] & 0xff - | (window[window_start++] & 0xff) << 8) << bits_in_buffer; - bits_in_buffer += 16; - } - return buffer & ((1 << n) - 1); - } - - /* Drops the next n bits from the input. You should have called peekBits - * with a bigger or equal n before, to make sure that enough bits are in - * the bit buffer. - */ - public final void dropBits(int n) - { - buffer >>>= n; - bits_in_buffer -= n; - } - - /** - * Gets the next n bits and increases input pointer. This is equivalent - * to peekBits followed by dropBits, except for correct error handling. - * @return the value of the bits, or -1 if not enough bits available. - */ - public final int getBits(int n) - { - int bits = peekBits(n); - if (bits >= 0) - dropBits(n); - return bits; - } - /** - * Gets the number of bits available in the bit buffer. This must be - * only called when a previous peekBits() returned -1. - * @return the number of bits available. - */ - public final int getAvailableBits() - { - return bits_in_buffer; - } - - /** - * Gets the number of bytes available. - * @return the number of bytes available. - */ - public final int getAvailableBytes() - { - return window_end - window_start + (bits_in_buffer >> 3); - } - - /** - * Skips to the next byte boundary. - */ - public void skipToByteBoundary() - { - buffer >>= (bits_in_buffer & 7); - bits_in_buffer &= ~7; - } - - public final boolean needsInput() { - return window_start == window_end; - } - - - /* Copies length bytes from input buffer to output buffer starting - * at output[offset]. You have to make sure, that the buffer is - * byte aligned. If not enough bytes are available, copies fewer - * bytes. - * @param length the length to copy, 0 is allowed. - * @return the number of bytes copied, 0 if no byte is available. - */ - public int copyBytes(byte[] output, int offset, int length) - { - if (length < 0) - throw new IllegalArgumentException("length negative"); - if ((bits_in_buffer & 7) != 0) - /* bits_in_buffer may only be 0 or 8 */ - throw new IllegalStateException("Bit buffer is not aligned!"); - - int count = 0; - while (bits_in_buffer > 0 && length > 0) - { - output[offset++] = (byte) buffer; - buffer >>>= 8; - bits_in_buffer -= 8; - length--; - count++; - } - if (length == 0) - return count; - - int avail = window_end - window_start; - if (length > avail) - length = avail; - System.arraycopy(window, window_start, output, offset, length); - window_start += length; - - if (((window_start - window_end) & 1) != 0) - { - /* We always want an even number of bytes in input, see peekBits */ - buffer = (window[window_start++] & 0xff); - bits_in_buffer = 8; - } - return count + length; - } - - public StreamManipulator() - { - } - - public void reset() - { - window_start = window_end = buffer = bits_in_buffer = 0; - } - - public void setInput(byte[] buf, int off, int len) - { - if (window_start < window_end) - throw new IllegalStateException - ("Old input was not completely processed"); - - int end = off + len; - - /* We want to throw an ArrayIndexOutOfBoundsException early. The - * check is very tricky: it also handles integer wrap around. - */ - if (0 > off || off > end || end > buf.length) - throw new ArrayIndexOutOfBoundsException(); - - if ((len & 1) != 0) - { - /* We always want an even number of bytes in input, see peekBits */ - buffer |= (buf[off++] & 0xff) << bits_in_buffer; - bits_in_buffer += 8; - } - - window = buf; - window_start = off; - window_end = end; - } - } - /* - * Contains the output from the Inflation process. - * - * We need to have a window so that we can refer backwards into the output stream - * to repeat stuff. - * - * @author John Leuner - * @since JDK 1.1 - */ - - private static class OutputWindow - { - private final int WINDOW_SIZE = 1 << 15; - private final int WINDOW_MASK = WINDOW_SIZE - 1; - - private byte[] window = new byte[WINDOW_SIZE]; //The window is 2^15 bytes - private int window_end = 0; - private int window_filled = 0; - - public void write(int abyte) - { - if (window_filled++ == WINDOW_SIZE) - throw new IllegalStateException("Window full"); - window[window_end++] = (byte) abyte; - window_end &= WINDOW_MASK; - } - - - private final void slowRepeat(int rep_start, int len, int dist) - { - while (len-- > 0) - { - window[window_end++] = window[rep_start++]; - window_end &= WINDOW_MASK; - rep_start &= WINDOW_MASK; - } - } - - public void repeat(int len, int dist) - { - if ((window_filled += len) > WINDOW_SIZE) - throw new IllegalStateException("Window full"); - - int rep_start = (window_end - dist) & WINDOW_MASK; - int border = WINDOW_SIZE - len; - if (rep_start <= border && window_end < border) - { - if (len <= dist) - { - System.arraycopy(window, rep_start, window, window_end, len); - window_end += len; - } - else - { - /* We have to copy manually, since the repeat pattern overlaps. - */ - while (len-- > 0) - window[window_end++] = window[rep_start++]; - } - } - else - slowRepeat(rep_start, len, dist); - } - - public int copyStored(StreamManipulator input, int len) - { - len = Math.min(Math.min(len, WINDOW_SIZE - window_filled), - input.getAvailableBytes()); - int copied; - - int tailLen = WINDOW_SIZE - window_end; - if (len > tailLen) - { - copied = input.copyBytes(window, window_end, tailLen); - if (copied == tailLen) - copied += input.copyBytes(window, 0, len - tailLen); - } - else - copied = input.copyBytes(window, window_end, len); - - window_end = (window_end + copied) & WINDOW_MASK; - window_filled += copied; - return copied; - } - - public void copyDict(byte[] dict, int offset, int len) - { - if (window_filled > 0) - throw new IllegalStateException(); - - if (len > WINDOW_SIZE) - { - offset += len - WINDOW_SIZE; - len = WINDOW_SIZE; - } - System.arraycopy(dict, offset, window, 0, len); - window_end = len & WINDOW_MASK; - } - - public int getFreeSpace() - { - return WINDOW_SIZE - window_filled; - } - - public int getAvailable() - { - return window_filled; - } - - public int copyOutput(byte[] output, int offset, int len) - { - int copy_end = window_end; - if (len > window_filled) - len = window_filled; - else - copy_end = (window_end - window_filled + len) & WINDOW_MASK; - - int copied = len; - int tailLen = len - copy_end; - - if (tailLen > 0) - { - System.arraycopy(window, WINDOW_SIZE - tailLen, - output, offset, tailLen); - offset += tailLen; - len = copy_end; - } - System.arraycopy(window, copy_end - len, output, offset, len); - window_filled -= copied; - if (window_filled < 0) - throw new IllegalStateException(); - return copied; - } - - public void reset() { - window_filled = window_end = 0; - } - } - }