/************************************************************************** * Copyright (c) 2001 by Punch Telematix. All rights reserved. * * * * Redistribution and use in source and binary forms, with or without * * modification, are permitted provided that the following conditions * * are met: * * 1. Redistributions of source code must retain the above copyright * * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * * notice, this list of conditions and the following disclaimer in the * * documentation and/or other materials provided with the distribution. * * 3. Neither the name of Punch Telematix nor the names of * * other contributors may be used to endorse or promote products * * derived from this software without specific prior written permission.* * * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED * * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * * IN NO EVENT SHALL PUNCH TELEMATIX OR OTHER CONTRIBUTORS BE LIABLE * * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * **************************************************************************/ /** * $Id: Inflater.java,v 1.3 2006/10/04 14:24:15 cvsroot Exp $ */ package java.util.zip; /** ** this class is the base class of inflating done in java.util.zip. ** this class is used by InflaterInputStream, GZIPInputStream, ZipInputStream, ** ZipFile, JarFile and JarInputStream ... ** ** this class is mapped directly onto the ZLIB library and should be used very carefully. ** Only give input (setInput) if needsInput returns true. Start Inflating (inflate) until ** new Input is required or the inflater is finished. ** ** If you use the inflater with noHeader = true then the inflater will insert a fake header. ** You can pass the inflater the deflated data (deflated using the noHeader=true options). ** It will inflate the data correctly, however it will expect 4 trailing bytes wich represent the ** Adler32 checksum. finished will not return true unless it could verify the checksum. If you pass an ** extra 4 bytes as input it the inflater will return finished = true and say getRemaining() is 4. The Inflater- ** InputStream is build in that way that no bytes will get lost. Since Zip, JAR and GZIP provide trailing data, ** we can be sure that the inflater will finish it job ... ** ** NOTE: for some reason some deflaters generate more bytes than needed by this inflater. So the inflater might say ** it was finished but not have used all provided bytes. The ZipInputStream just skip such bytes till it reaches it next ** header and verify if everything is ok. But this will lead to Format exceptions in the GZIPInputStream ... ** Altough it might not use all bytes it still generates all bytes passed to the deflating algorithm ... */ public class Inflater { private Adler32 adler = new Adler32(); private boolean finished; private boolean needsInput=true; private boolean needsDict; private boolean noHeader; private boolean skip; private int totalIn; private int totalOut; private int remain; private byte[] header; private int dictAdler; public Inflater() { this(false); } public Inflater(boolean noHeader) { create(); this.noHeader = noHeader; skip = !noHeader; if (skip){ header = new byte[6]; } } //CALLS TO OWN PUBLIC INTERFACE public int inflate(byte [] buf) throws DataFormatException { return inflate(buf, 0, buf.length); } public void setInput(byte[] buf) { setInput(buf, 0, buf.length); } public void setDictionary(byte [] buf){ setDictionary(buf , 0, buf.length); } //CALLS WHO RETURN MEMBER VARIABLES public synchronized int getAdler(){ return (dictAdler == 0 ? (int) adler.getValue() : dictAdler); } public synchronized boolean needsDictionary(){ return needsDict; } public synchronized boolean finished() { return finished; } public synchronized boolean needsInput(){ return needsInput; } public synchronized int getTotalIn(){ return totalIn; } public synchronized int getTotalOut(){ return totalOut; } //REAL WORKERS ... public synchronized int inflate(byte [] buf, int off, int len) throws DataFormatException { if(finished){ return 0; } len = _inflate(buf, off, len); adler.update(buf,off,len); totalOut += len; return len; } /** ** if a Header is expected we check if dictionary is needed ** |4 bits cinfo|4 bits cm|2 bits Flevel|1 bit FDICT|5bits checksum| ** cinfo = compression info if CM=8 then cinfo = log2(LZ77 window size) - 8 ** else cinfo is not defined (0) ** cm = compression method ... ** FLevel = compression level --> pure info, not needed to decompress ** FDict = set to 0 (if 1 then extra 4 bytes added to header ...) ** checksum = these five bits are set the 2 bytes are divisable by 31 */ public synchronized void setInput(byte[] buf, int off, int len) { if (off < 0 || len < 0 || buf.length - len < off) { throw new ArrayIndexOutOfBoundsException("offset = " + off + ", length = " + len + ", buffer length = " + buf.length); } if (len != 0) { if(skip){ /** ** this case can only occur if we have a ZLIB header !!! ** this means we need at least 2 bytes as header and 4 bytes as trailer so we can ask for more input ** until we at least recieved 6 bytes ... ** ** first fill up the header array (we have to make sure nothing strange ** happens if we set the input byte per byte. (don't do that though) */ int put = (6 > len ? len : 6) - totalIn; System.arraycopy(buf,off, header, totalIn, put); totalIn += len; if (totalIn >= 6){ /** ** header is full ... */ //System.out.println("DEBUG - header is full"); if (!((header[1] & 0x020) == 1)){ //System.out.println("DEBUG - header is full - no dictionary needed"); _setInput(header,2,4); remain = 6; } else { //System.out.println("DEBUG - header is full - dictionary needed"); dictAdler = (int)ZipFile.bytesToLong(header,2); remain = 10; } _setInput(buf, off+put, len-put); skip = false; header = null; } } else { //System.out.println("DEBUG - passing input straight to native"); totalIn += len; _setInput(buf,off,len); } needsInput=false; } } public synchronized void reset(){ needsInput = true; finished = false; needsDict = false; totalIn = 0; totalOut = 0; dictAdler = 0; adler.reset(); nativeReset(); } protected native void finalize(); //native stuff ... private native void nativeReset(); private native void create(); private native int _inflate(byte[] buf, int off, int len) throws DataFormatException; private native void _setInput(byte[] buf, int off, int len); public native synchronized void end(); public native synchronized void setDictionary(byte [] buf, int off, int len); public native synchronized int getRemaining(); }