/**************************************************************************
* 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: Deflater.java,v 1.3 2006/03/14 15:00:36 cvs Exp $
*/
package java.util.zip;
/**
** This class is the core of all deflating done in java.util.zip.
** It is used by DeflaterOutputStream, ZipOutputStream, GZIPOutputStream
** and JarOutputStream.
** Before you use setInput you should always check if the deflater needs input (with needsInput)
** call deflate untill needsInput returns true (give more input and start over);
** Once finish is called you should not give new input anymore. just call deflate untill
** finished returns true ...
*/
public class Deflater {
private static final byte[] headerNoDict = { 0,0 };
private final byte[] headerDict = { 0,0x3e, 0, 0, 0 ,0};
public static final int BEST_COMPRESSION = 9;
public static final int BEST_SPEED = 1;
public static final int DEFAULT_COMPRESSION = -1;
public static final int DEFAULT_STRATEGY = 0;
public static final int DEFLATED = 8;
public static final int FILTERED = 1;
public static final int HUFFMAN_ONLY = 2;
public static final int NO_COMPRESSION = 0;
private boolean needsInput=true;
private int dictadler=0;
private boolean noHeader;
private Adler32 adler = new Adler32();
private int level;
private int strategy;
private int totalIn;
private int totalOut;
private int finished;
// 0 = not finished
// 1 = finish is called
// 2 or more = done deflating ...
// if a ZLIB needs to be suppiled finished will keep track of how many adler bytes are written already
public Deflater() {
this(DEFAULT_COMPRESSION , false);
}
public Deflater(int lvl) {
this(lvl, false);
}
public Deflater(int lvl, boolean noHeader) {
level = (9 < lvl || lvl < 0) ? DEFAULT_COMPRESSION : lvl;
create();
this.noHeader = noHeader;
}
//Straight calls to public API
public synchronized int getTotalIn(){
return totalIn;
}
public synchronized int getTotalOut(){
return totalOut;
}
public synchronized int getAdler(){
return (int)adler.getValue();
}
public int deflate(byte [] buf) {
return deflate(buf, 0, buf.length);
}
public void setInput(byte[] buf) {
setInput(buf, 0, buf.length);
}
public void setDictionary(byte [] buf){
setDictionary(buf , 0, buf.length);
}
public synchronized boolean finished() {
return (finished > 1);
}
public synchronized boolean needsInput(){
return (needsInput || (finished > 1));
}
public synchronized void setLevel(int lvl){
if (9 < lvl || lvl < 0) {
throw new IllegalArgumentException();
}
level=lvl;
updateLvl();
}
public synchronized void setStrategy(int strat){
if (DEFAULT_STRATEGY == strat && FILTERED == strat && HUFFMAN_ONLY == strat) {
throw new IllegalArgumentException();
}
strategy=strat;
}
public synchronized void setInput(byte[] buf, int off, int len){
adler.update(buf,off,len);
totalIn += len;
_setInput(buf, off, len);
needsInput = false;
}
/**
** if a Header is expected we check if dictionary is was passed
** |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 .int rdint rd..)
** checksum = these five bits are set the 2 bytes are divisable by 31
*/
public synchronized int deflate(byte[] buf, int off, int len){
int tot = 0;
if(len == 0){
return 0;
}
if (totalOut < 6 && !noHeader){
if(dictadler != 0){
tot = ( len > 6 ? 6 : len ) - totalOut;
//System.out.println("writing header (with adler)"+tot);
System.arraycopy(headerDict, totalOut, buf, off, tot);
len -= tot;
off += tot;
}
else if (totalOut < 2){
tot = ( len > 2 ? 2 : len ) - totalOut;
System.arraycopy(headerNoDict, totalOut, buf, off, tot);
//System.out.println("writing header (without adler)"+tot);
totalOut += tot;
len -= tot;
off += tot;
}
}
int rd = _deflate(buf, off, len);
tot += rd;
len -=rd;
if (finished > 0 && 0 < len){
off += rd;
rd = _deflate(buf, off, len);
if(!noHeader){
if(finished == 2){
intToBytes((int)adler.getValue(), headerDict, 2);
}
rd = (4 > len ? len : 4);
System.arraycopy(headerDict, finished, buf, off, rd);
tot+=rd;
}
}
totalOut += tot;
return tot;
}
public synchronized void reset(){
totalIn=0;
totalOut=0;
dictadler=0;
nativeReset();
}
public synchronized void setDictionary(byte [] buf, int off, int len){
if(finished < 2){
Adler32 ad = new Adler32();
ad.update(buf,off,len);
intToBytes((int)ad.getValue(), headerDict, 2);
_setDictionary(buf,off,len);
}
}
protected native void finalize();
//native stuff
private native void create();
private native void updateLvl();
private native void nativeReset();
private native void _setInput(byte[] buf, int off, int len);
private native void _setDictionary(byte [] buf, int off, int len);
private native int _deflate(byte[] buf, int off, int len);
public native synchronized void finish();
public native synchronized void end();
static void intToBytes(int val, byte[] bytes, int off){
bytes[off++] = (byte)(val>>>24);
bytes[off++] = (byte)(val>>>16);
bytes[off++] = (byte)(val>>>8);
bytes[off] = (byte) val;
}
}