jaroslav@609: /* jaroslav@609: * Copyright (c) 1996, 2011, Oracle and/or its affiliates. All rights reserved. jaroslav@609: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jaroslav@609: * jaroslav@609: * This code is free software; you can redistribute it and/or modify it jaroslav@609: * under the terms of the GNU General Public License version 2 only, as jaroslav@609: * published by the Free Software Foundation. Oracle designates this jaroslav@609: * particular file as subject to the "Classpath" exception as provided jaroslav@609: * by Oracle in the LICENSE file that accompanied this code. jaroslav@609: * jaroslav@609: * This code is distributed in the hope that it will be useful, but WITHOUT jaroslav@609: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jaroslav@609: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jaroslav@609: * version 2 for more details (a copy is included in the LICENSE file that jaroslav@609: * accompanied this code). jaroslav@609: * jaroslav@609: * You should have received a copy of the GNU General Public License version jaroslav@609: * 2 along with this work; if not, write to the Free Software Foundation, jaroslav@609: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jaroslav@609: * jaroslav@609: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA jaroslav@609: * or visit www.oracle.com if you need additional information or have any jaroslav@609: * questions. jaroslav@609: */ jaroslav@609: jaroslav@609: package java.util.zip; jaroslav@609: jaroslav@609: /** jaroslav@609: * This class provides support for general purpose decompression using the jaroslav@609: * popular ZLIB compression library. The ZLIB compression library was jaroslav@609: * initially developed as part of the PNG graphics standard and is not jaroslav@609: * protected by patents. It is fully described in the specifications at jaroslav@609: * the java.util.zip jaroslav@609: * package description. jaroslav@609: * jaroslav@609: *

The following code fragment demonstrates a trivial compression jaroslav@609: * and decompression of a string using Deflater and jaroslav@609: * Inflater. jaroslav@609: * jaroslav@609: *

jaroslav@609:  * try {
jaroslav@609:  *     // Encode a String into bytes
jaroslav@609:  *     String inputString = "blahblahblah\u20AC\u20AC";
jaroslav@609:  *     byte[] input = inputString.getBytes("UTF-8");
jaroslav@609:  *
jaroslav@609:  *     // Compress the bytes
jaroslav@609:  *     byte[] output = new byte[100];
jaroslav@609:  *     Deflater compresser = new Deflater();
jaroslav@609:  *     compresser.setInput(input);
jaroslav@609:  *     compresser.finish();
jaroslav@609:  *     int compressedDataLength = compresser.deflate(output);
jaroslav@609:  *
jaroslav@609:  *     // Decompress the bytes
jaroslav@609:  *     Inflater decompresser = new Inflater();
jaroslav@609:  *     decompresser.setInput(output, 0, compressedDataLength);
jaroslav@609:  *     byte[] result = new byte[100];
jaroslav@609:  *     int resultLength = decompresser.inflate(result);
jaroslav@609:  *     decompresser.end();
jaroslav@609:  *
jaroslav@609:  *     // Decode the bytes into a String
jaroslav@609:  *     String outputString = new String(result, 0, resultLength, "UTF-8");
jaroslav@609:  * } catch(java.io.UnsupportedEncodingException ex) {
jaroslav@609:  *     // handle
jaroslav@609:  * } catch (java.util.zip.DataFormatException ex) {
jaroslav@609:  *     // handle
jaroslav@609:  * }
jaroslav@609:  * 
jaroslav@609: * jaroslav@609: * @see Deflater jaroslav@609: * @author David Connelly jaroslav@609: * jaroslav@609: */ jaroslav@609: public jaroslav@609: class Inflater { jaroslav@609: jaroslav@609: private final ZStreamRef zsRef; jaroslav@609: private byte[] buf = defaultBuf; jaroslav@609: private int off, len; jaroslav@609: private boolean finished; jaroslav@609: private boolean needDict; jaroslav@609: jaroslav@609: private static final byte[] defaultBuf = new byte[0]; jaroslav@609: jaroslav@609: static { jaroslav@609: /* Zip library is loaded from System.initializeSystemClass */ jaroslav@609: initIDs(); jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Creates a new decompressor. If the parameter 'nowrap' is true then jaroslav@609: * the ZLIB header and checksum fields will not be used. This provides jaroslav@609: * compatibility with the compression format used by both GZIP and PKZIP. jaroslav@609: *

jaroslav@609: * Note: When using the 'nowrap' option it is also necessary to provide jaroslav@609: * an extra "dummy" byte as input. This is required by the ZLIB native jaroslav@609: * library in order to support certain optimizations. jaroslav@609: * jaroslav@609: * @param nowrap if true then support GZIP compatible compression jaroslav@609: */ jaroslav@609: public Inflater(boolean nowrap) { jaroslav@609: zsRef = new ZStreamRef(init(nowrap)); jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Creates a new decompressor. jaroslav@609: */ jaroslav@609: public Inflater() { jaroslav@609: this(false); jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Sets input data for decompression. Should be called whenever jaroslav@609: * needsInput() returns true indicating that more input data is jaroslav@609: * required. jaroslav@609: * @param b the input data bytes jaroslav@609: * @param off the start offset of the input data jaroslav@609: * @param len the length of the input data jaroslav@609: * @see Inflater#needsInput jaroslav@609: */ jaroslav@609: public void setInput(byte[] b, int off, int len) { jaroslav@609: if (b == null) { jaroslav@609: throw new NullPointerException(); jaroslav@609: } jaroslav@609: if (off < 0 || len < 0 || off > b.length - len) { jaroslav@609: throw new ArrayIndexOutOfBoundsException(); jaroslav@609: } jaroslav@609: synchronized (zsRef) { jaroslav@609: this.buf = b; jaroslav@609: this.off = off; jaroslav@609: this.len = len; jaroslav@609: } jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Sets input data for decompression. Should be called whenever jaroslav@609: * needsInput() returns true indicating that more input data is jaroslav@609: * required. jaroslav@609: * @param b the input data bytes jaroslav@609: * @see Inflater#needsInput jaroslav@609: */ jaroslav@609: public void setInput(byte[] b) { jaroslav@609: setInput(b, 0, b.length); jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Sets the preset dictionary to the given array of bytes. Should be jaroslav@609: * called when inflate() returns 0 and needsDictionary() returns true jaroslav@609: * indicating that a preset dictionary is required. The method getAdler() jaroslav@609: * can be used to get the Adler-32 value of the dictionary needed. jaroslav@609: * @param b the dictionary data bytes jaroslav@609: * @param off the start offset of the data jaroslav@609: * @param len the length of the data jaroslav@609: * @see Inflater#needsDictionary jaroslav@609: * @see Inflater#getAdler jaroslav@609: */ jaroslav@609: public void setDictionary(byte[] b, int off, int len) { jaroslav@609: if (b == null) { jaroslav@609: throw new NullPointerException(); jaroslav@609: } jaroslav@609: if (off < 0 || len < 0 || off > b.length - len) { jaroslav@609: throw new ArrayIndexOutOfBoundsException(); jaroslav@609: } jaroslav@609: synchronized (zsRef) { jaroslav@609: ensureOpen(); jaroslav@609: setDictionary(zsRef.address(), b, off, len); jaroslav@609: needDict = false; jaroslav@609: } jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Sets the preset dictionary to the given array of bytes. Should be jaroslav@609: * called when inflate() returns 0 and needsDictionary() returns true jaroslav@609: * indicating that a preset dictionary is required. The method getAdler() jaroslav@609: * can be used to get the Adler-32 value of the dictionary needed. jaroslav@609: * @param b the dictionary data bytes jaroslav@609: * @see Inflater#needsDictionary jaroslav@609: * @see Inflater#getAdler jaroslav@609: */ jaroslav@609: public void setDictionary(byte[] b) { jaroslav@609: setDictionary(b, 0, b.length); jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Returns the total number of bytes remaining in the input buffer. jaroslav@609: * This can be used to find out what bytes still remain in the input jaroslav@609: * buffer after decompression has finished. jaroslav@609: * @return the total number of bytes remaining in the input buffer jaroslav@609: */ jaroslav@609: public int getRemaining() { jaroslav@609: synchronized (zsRef) { jaroslav@609: return len; jaroslav@609: } jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Returns true if no data remains in the input buffer. This can jaroslav@609: * be used to determine if #setInput should be called in order jaroslav@609: * to provide more input. jaroslav@609: * @return true if no data remains in the input buffer jaroslav@609: */ jaroslav@609: public boolean needsInput() { jaroslav@609: synchronized (zsRef) { jaroslav@609: return len <= 0; jaroslav@609: } jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Returns true if a preset dictionary is needed for decompression. jaroslav@609: * @return true if a preset dictionary is needed for decompression jaroslav@609: * @see Inflater#setDictionary jaroslav@609: */ jaroslav@609: public boolean needsDictionary() { jaroslav@609: synchronized (zsRef) { jaroslav@609: return needDict; jaroslav@609: } jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Returns true if the end of the compressed data stream has been jaroslav@609: * reached. jaroslav@609: * @return true if the end of the compressed data stream has been jaroslav@609: * reached jaroslav@609: */ jaroslav@609: public boolean finished() { jaroslav@609: synchronized (zsRef) { jaroslav@609: return finished; jaroslav@609: } jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Uncompresses bytes into specified buffer. Returns actual number jaroslav@609: * of bytes uncompressed. A return value of 0 indicates that jaroslav@609: * needsInput() or needsDictionary() should be called in order to jaroslav@609: * determine if more input data or a preset dictionary is required. jaroslav@609: * In the latter case, getAdler() can be used to get the Adler-32 jaroslav@609: * value of the dictionary required. jaroslav@609: * @param b the buffer for the uncompressed data jaroslav@609: * @param off the start offset of the data jaroslav@609: * @param len the maximum number of uncompressed bytes jaroslav@609: * @return the actual number of uncompressed bytes jaroslav@609: * @exception DataFormatException if the compressed data format is invalid jaroslav@609: * @see Inflater#needsInput jaroslav@609: * @see Inflater#needsDictionary jaroslav@609: */ jaroslav@609: public int inflate(byte[] b, int off, int len) jaroslav@609: throws DataFormatException jaroslav@609: { jaroslav@609: if (b == null) { jaroslav@609: throw new NullPointerException(); jaroslav@609: } jaroslav@609: if (off < 0 || len < 0 || off > b.length - len) { jaroslav@609: throw new ArrayIndexOutOfBoundsException(); jaroslav@609: } jaroslav@609: synchronized (zsRef) { jaroslav@609: ensureOpen(); jaroslav@609: return inflateBytes(zsRef.address(), b, off, len); jaroslav@609: } jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Uncompresses bytes into specified buffer. Returns actual number jaroslav@609: * of bytes uncompressed. A return value of 0 indicates that jaroslav@609: * needsInput() or needsDictionary() should be called in order to jaroslav@609: * determine if more input data or a preset dictionary is required. jaroslav@609: * In the latter case, getAdler() can be used to get the Adler-32 jaroslav@609: * value of the dictionary required. jaroslav@609: * @param b the buffer for the uncompressed data jaroslav@609: * @return the actual number of uncompressed bytes jaroslav@609: * @exception DataFormatException if the compressed data format is invalid jaroslav@609: * @see Inflater#needsInput jaroslav@609: * @see Inflater#needsDictionary jaroslav@609: */ jaroslav@609: public int inflate(byte[] b) throws DataFormatException { jaroslav@609: return inflate(b, 0, b.length); jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Returns the ADLER-32 value of the uncompressed data. jaroslav@609: * @return the ADLER-32 value of the uncompressed data jaroslav@609: */ jaroslav@609: public int getAdler() { jaroslav@609: synchronized (zsRef) { jaroslav@609: ensureOpen(); jaroslav@609: return getAdler(zsRef.address()); jaroslav@609: } jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Returns the total number of compressed bytes input so far. jaroslav@609: * jaroslav@609: *

Since the number of bytes may be greater than jaroslav@609: * Integer.MAX_VALUE, the {@link #getBytesRead()} method is now jaroslav@609: * the preferred means of obtaining this information.

jaroslav@609: * jaroslav@609: * @return the total number of compressed bytes input so far jaroslav@609: */ jaroslav@609: public int getTotalIn() { jaroslav@609: return (int) getBytesRead(); jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Returns the total number of compressed bytes input so far.

jaroslav@609: * jaroslav@609: * @return the total (non-negative) number of compressed bytes input so far jaroslav@609: * @since 1.5 jaroslav@609: */ jaroslav@609: public long getBytesRead() { jaroslav@609: synchronized (zsRef) { jaroslav@609: ensureOpen(); jaroslav@609: return getBytesRead(zsRef.address()); jaroslav@609: } jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Returns the total number of uncompressed bytes output so far. jaroslav@609: * jaroslav@609: *

Since the number of bytes may be greater than jaroslav@609: * Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now jaroslav@609: * the preferred means of obtaining this information.

jaroslav@609: * jaroslav@609: * @return the total number of uncompressed bytes output so far jaroslav@609: */ jaroslav@609: public int getTotalOut() { jaroslav@609: return (int) getBytesWritten(); jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Returns the total number of uncompressed bytes output so far.

jaroslav@609: * jaroslav@609: * @return the total (non-negative) number of uncompressed bytes output so far jaroslav@609: * @since 1.5 jaroslav@609: */ jaroslav@609: public long getBytesWritten() { jaroslav@609: synchronized (zsRef) { jaroslav@609: ensureOpen(); jaroslav@609: return getBytesWritten(zsRef.address()); jaroslav@609: } jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Resets inflater so that a new set of input data can be processed. jaroslav@609: */ jaroslav@609: public void reset() { jaroslav@609: synchronized (zsRef) { jaroslav@609: ensureOpen(); jaroslav@609: reset(zsRef.address()); jaroslav@609: buf = defaultBuf; jaroslav@609: finished = false; jaroslav@609: needDict = false; jaroslav@609: off = len = 0; jaroslav@609: } jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Closes the decompressor and discards any unprocessed input. jaroslav@609: * This method should be called when the decompressor is no longer jaroslav@609: * being used, but will also be called automatically by the finalize() jaroslav@609: * method. Once this method is called, the behavior of the Inflater jaroslav@609: * object is undefined. jaroslav@609: */ jaroslav@609: public void end() { jaroslav@609: synchronized (zsRef) { jaroslav@609: long addr = zsRef.address(); jaroslav@609: zsRef.clear(); jaroslav@609: if (addr != 0) { jaroslav@609: end(addr); jaroslav@609: buf = null; jaroslav@609: } jaroslav@609: } jaroslav@609: } jaroslav@609: jaroslav@609: /** jaroslav@609: * Closes the decompressor when garbage is collected. jaroslav@609: */ jaroslav@609: protected void finalize() { jaroslav@609: end(); jaroslav@609: } jaroslav@609: jaroslav@609: private void ensureOpen () { jaroslav@609: assert Thread.holdsLock(zsRef); jaroslav@609: if (zsRef.address() == 0) jaroslav@609: throw new NullPointerException("Inflater has been closed"); jaroslav@609: } jaroslav@609: jaroslav@609: boolean ended() { jaroslav@609: synchronized (zsRef) { jaroslav@609: return zsRef.address() == 0; jaroslav@609: } jaroslav@609: } jaroslav@609: jaroslav@609: private native static void initIDs(); jaroslav@609: private native static long init(boolean nowrap); jaroslav@609: private native static void setDictionary(long addr, byte[] b, int off, jaroslav@609: int len); jaroslav@609: private native int inflateBytes(long addr, byte[] b, int off, int len) jaroslav@609: throws DataFormatException; jaroslav@609: private native static int getAdler(long addr); jaroslav@609: private native static long getBytesRead(long addr); jaroslav@609: private native static long getBytesWritten(long addr); jaroslav@609: private native static void reset(long addr); jaroslav@609: private native static void end(long addr); jaroslav@609: }