/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package java.util.zip; /** * This class compresses data using the <i>DEFLATE</i> algorithm (see <a * href="http://www.gzip.org/algorithm.txt">specification</a>). * <p> * Basically this class is part of the API to the stream based ZLIB compression * library and is used as such by {@code DeflaterOutputStream} and its * descendants. * <p> * The typical usage of a {@code Deflater} instance outside this package * consists of a specific call to one of its constructors before being passed to * an instance of {@code DeflaterOutputStream}. * * @see DeflaterOutputStream * @see Inflater */ public class Deflater { /** * Upper bound for the compression level range. */ public static final int BEST_COMPRESSION = 9; /** * Lower bound for compression level range. */ public static final int BEST_SPEED = 1; /** * Usage of the default compression level. */ public static final int DEFAULT_COMPRESSION = -1; /** * Default value for compression strategy. */ public static final int DEFAULT_STRATEGY = 0; /** * Default value for compression method. */ public static final int DEFLATED = 8; /** * Possible value for compression strategy. */ public static final int FILTERED = 1; /** * Possible value for compression strategy. */ public static final int HUFFMAN_ONLY = 2; /** * Possible value for compression level. */ public static final int NO_COMPRESSION = 0; /** * Use buffering for best compression. * * @hide * @since 1.7 */ public static final int NO_FLUSH = 0; /** * Flush buffers so recipients can immediately decode the data sent thus * far. This mode may degrade compression. * * @hide * @since 1.7 */ public static final int SYNC_FLUSH = 2; /** * Flush buffers so recipients can immediately decode the data sent thus * far. The compression state is also reset to permit random access and * recovery for clients who have discarded or damaged their own copy. This * mode may degrade compression. * * @hide * @since 1.7 */ public static final int FULL_FLUSH = 3; /** * Flush buffers and mark the end of the datastream. */ private static final int FINISH = 4; // Fill in the JNI id caches private static native void oneTimeInitialization(); // A stub buffer used when deflate() called while inputBuffer has not been // set. private static final byte[] STUB_INPUT_BUFFER = new byte[0]; static { oneTimeInitialization(); } private int flushParm = NO_FLUSH; private boolean finished; private int compressLevel = DEFAULT_COMPRESSION; private int strategy = DEFAULT_STRATEGY; private long streamHandle = -1; private byte[] inputBuffer; private int inRead; private int inLength; /** * Constructs a new {@code Deflater} instance with default compression * level. The strategy can be specified with {@link #setStrategy}, only. A * header is added to the output by default; use constructor {@code * Deflater(level, boolean)} if you need to omit the header. */ public Deflater() { this(DEFAULT_COMPRESSION, false); } /** * Constructs a new {@code Deflater} instance with a specific compression * level. The strategy can be specified with {@code setStrategy}, only. A * header is added to the output by default; use * {@code Deflater(level, boolean)} if you need to omit the header. * * @param level * the compression level in the range between 0 and 9. */ public Deflater(int level) { this(level, false); } /** * Constructs a new {@code Deflater} instance with a specific compression * level. If noHeader is passed as true no ZLib header is added to the * output. In a ZIP archive every entry (compressed file) comes with such a * header. The strategy can be specified with the setStrategy method, only. * * @param level * the compression level in the range between 0 and 9. * @param noHeader * {@code true} indicates that no ZLIB header should be written. */ public Deflater(int level, boolean noHeader) { super(); if (level < DEFAULT_COMPRESSION || level > BEST_COMPRESSION) { throw new IllegalArgumentException(); } compressLevel = level; streamHandle = createStream(compressLevel, strategy, noHeader); } /** * Deflates the data (previously passed to {@code setInput}) into the * supplied buffer. * * @param buf * buffer to write compressed data to. * @return number of bytes of compressed data written to {@code buf}. * @see #deflate(byte[], int, int) */ public int deflate(byte[] buf) { return deflate(buf, 0, buf.length); } /** * Deflates data (previously passed to {@code setInput}) into a specific * region within the supplied buffer. * * @param buf * the buffer to write compressed data to. * @param off * the offset within {@code buf} at which to start writing to. * @param nbytes * maximum number of bytes of compressed data to be written. * @return the number of bytes of compressed data written to {@code buf}. */ public synchronized int deflate(byte[] buf, int off, int nbytes) { return deflateImpl(buf, off, nbytes, flushParm); } /** * Deflates data (previously passed to {@code setInput}) into a specific * region within the supplied buffer, optionally flushing the input buffer. * * @param buf the buffer to write compressed data to. * @param off the offset within {@code buf} at which to start writing to. * @param nbytes maximum number of bytes of compressed data to be written. * @param flush one of {@link #NO_FLUSH}, {@link #SYNC_FLUSH} or * {@link #FULL_FLUSH}. * @return the number of compressed bytes written to {@code buf}. If this * equals {@code nbytes}, the number of bytes of input to be flushed * may have exceeded the output buffer's capacity. In this case, * finishing a flush will require the output buffer to be drained * and additional calls to {@link #deflate} to be made. * @hide * @since 1.7 */ public synchronized int deflate(byte[] buf, int off, int nbytes, int flush) { if (flush != NO_FLUSH && flush != SYNC_FLUSH && flush != FULL_FLUSH) { throw new IllegalArgumentException(); } return deflateImpl(buf, off, nbytes, flush); } private synchronized int deflateImpl( byte[] buf, int off, int nbytes, int flush) { if (streamHandle == -1) { throw new IllegalStateException(); } if (off > buf.length || nbytes < 0 || off < 0 || buf.length - off < nbytes) { throw new ArrayIndexOutOfBoundsException(); } if (inputBuffer == null) { setInput(STUB_INPUT_BUFFER); } return deflateImpl(buf, off, nbytes, streamHandle, flush); } private synchronized native int deflateImpl(byte[] buf, int off, int nbytes, long handle, int flushParm1); private synchronized native void endImpl(long handle); /** * Frees all resources held onto by this deflating algorithm. Any unused * input or output is discarded. While this method is used by {@code * finalize()}, it can be called explicitly in order to free native * resources before the next GC cycle. After {@code end()} was called other * methods will typically throw an {@code IllegalStateException}. */ public synchronized void end() { endImpl(); } private void endImpl() { if (streamHandle != -1) { endImpl(streamHandle); inputBuffer = null; streamHandle = -1; } } @Override protected void finalize() { synchronized (this) { end(); // to allow overriding classes to clean up endImpl(); // in case those classes don't call super.end() } } /** * Indicates to the {@code Deflater} that all uncompressed input has been provided * to it. * * @see #finished */ public synchronized void finish() { flushParm = FINISH; } /** * Returns whether or not all provided data has been successfully * compressed. * * @return true if all data has been compressed, false otherwise. */ public synchronized boolean finished() { return finished; } /** * Returns the Adler32 checksum of uncompressed data currently read. If a * preset dictionary is used getAdler() will return the Adler32 checksum of * the dictionary used. * * @return the Adler32 checksum of uncompressed data or preset dictionary if * used. * @see #setDictionary(byte[]) * @see #setDictionary(byte[], int, int) */ public synchronized int getAdler() { if (streamHandle == -1) { throw new IllegalStateException(); } return getAdlerImpl(streamHandle); } private synchronized native int getAdlerImpl(long handle); /** * Returns the total number of bytes of input consumed by the {@code Deflater}. * * @return number of bytes of input read. */ public synchronized int getTotalIn() { if (streamHandle == -1) { throw new IllegalStateException(); } return (int) getTotalInImpl(streamHandle); } private synchronized native long getTotalInImpl(long handle); /** * Returns the total number of compressed bytes output by this {@code Deflater}. * * @return number of compressed bytes output. */ public synchronized int getTotalOut() { if (streamHandle == -1) { throw new IllegalStateException(); } return (int) getTotalOutImpl(streamHandle); } private synchronized native long getTotalOutImpl(long handle); /** * Counterpart to setInput(). Indicates whether or not all bytes of * uncompressed input have been consumed by the {@code Deflater}. If needsInput() * returns true setInput() must be called before deflation can continue. If * all bytes of uncompressed data have been provided to the {@code Deflater} * finish() must be called to ensure the compressed data is output. * * @return {@code true} if input is required for deflation to continue, * {@code false} otherwise. * @see #finished() * @see #setInput(byte[]) * @see #setInput(byte[], int, int) */ public synchronized boolean needsInput() { if (inputBuffer == null) { return true; } return inRead == inLength; } /** * Resets the {@code Deflater} to accept new input without affecting any * previously made settings for the compression strategy or level. This * operation <i>must</i> be called after {@code finished()} returns * {@code true} if the {@code Deflater} is to be reused. * * @see #finished */ public synchronized void reset() { if (streamHandle == -1) { throw new NullPointerException(); } flushParm = NO_FLUSH; finished = false; resetImpl(streamHandle); inputBuffer = null; } private synchronized native void resetImpl(long handle); /** * Sets the dictionary to be used for compression by this {@code Deflater}. * setDictionary() can only be called if this {@code Deflater} supports the writing * of ZLIB headers. This is the default behaviour but can be overridden * using {@code Deflater(int, boolean)}. * * @param buf * the buffer containing the dictionary data bytes. * @see Deflater#Deflater(int, boolean) */ public void setDictionary(byte[] buf) { setDictionary(buf, 0, buf.length); } /** * Sets the dictionary to be used for compression by this {@code Deflater}. * setDictionary() can only be called if this {@code Deflater} supports the writing * of ZLIB headers. This is the default behaviour but can be overridden * using {@code Deflater(int, boolean)}. * * @param buf * the buffer containing the dictionary data bytes. * @param off * the offset of the data. * @param nbytes * the length of the data. * @see Deflater#Deflater(int, boolean) */ public synchronized void setDictionary(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { throw new IllegalStateException(); } // avoid int overflow, check null buf if (off <= buf.length && nbytes >= 0 && off >= 0 && buf.length - off >= nbytes) { setDictionaryImpl(buf, off, nbytes, streamHandle); } else { throw new ArrayIndexOutOfBoundsException(); } } private synchronized native void setDictionaryImpl(byte[] buf, int off, int nbytes, long handle); /** * Sets the input buffer the {@code Deflater} will use to extract uncompressed bytes * for later compression. * * @param buf * the buffer. */ public void setInput(byte[] buf) { setInput(buf, 0, buf.length); } /** * Sets the input buffer the {@code Deflater} will use to extract uncompressed bytes * for later compression. Input will be taken from the buffer region * starting at off and ending at nbytes - 1. * * @param buf * the buffer containing the input data bytes. * @param off * the offset of the data. * @param nbytes * the length of the data. */ public synchronized void setInput(byte[] buf, int off, int nbytes) { if (streamHandle == -1) { throw new IllegalStateException(); } // avoid int overflow, check null buf if (off <= buf.length && nbytes >= 0 && off >= 0 && buf.length - off >= nbytes) { inLength = nbytes; inRead = 0; if (inputBuffer == null) { setLevelsImpl(compressLevel, strategy, streamHandle); } inputBuffer = buf; setInputImpl(buf, off, nbytes, streamHandle); } else { throw new ArrayIndexOutOfBoundsException(); } } private synchronized native void setLevelsImpl(int level, int strategy, long handle); private synchronized native void setInputImpl(byte[] buf, int off, int nbytes, long handle); /** * Sets the compression level to be used when compressing data. The * compression level must be a value between 0 and 9. This value must be set * prior to calling setInput(). * * @param level * compression level to use * @exception IllegalArgumentException * If the compression level is invalid. */ public synchronized void setLevel(int level) { if (level < DEFAULT_COMPRESSION || level > BEST_COMPRESSION) { throw new IllegalArgumentException(); } if (inputBuffer != null) { throw new IllegalStateException(); } compressLevel = level; } /** * Sets the compression strategy to be used. The strategy must be one of * FILTERED, HUFFMAN_ONLY or DEFAULT_STRATEGY.This value must be set prior * to calling setInput(). * * @param strategy * compression strategy to use * @exception IllegalArgumentException * If the strategy specified is not one of FILTERED, * HUFFMAN_ONLY or DEFAULT_STRATEGY. */ public synchronized void setStrategy(int strategy) { if (strategy < DEFAULT_STRATEGY || strategy > HUFFMAN_ONLY) { throw new IllegalArgumentException(); } if (inputBuffer != null) { throw new IllegalStateException(); } this.strategy = strategy; } /** * Returns a long int of total number of bytes read by the {@code Deflater}. This * method performs the same as {@code getTotalIn} except it returns a long value * instead of an integer * * @see #getTotalIn() * @return total number of bytes read by {@code Deflater}. */ public synchronized long getBytesRead() { // Throw NPE here if (streamHandle == -1) { throw new NullPointerException(); } return getTotalInImpl(streamHandle); } /** * Returns a long int of total number of bytes of read by the {@code Deflater}. This * method performs the same as {@code getTotalOut} except it returns a long * value instead of an integer * * @see #getTotalOut() * @return bytes exactly write by {@code Deflater} */ public synchronized long getBytesWritten() { // Throw NPE here if (streamHandle == -1) { throw new NullPointerException(); } return getTotalOutImpl(streamHandle); } private native long createStream(int level, int strategy1, boolean noHeader1); }