blob: 4ac5c470d54019e8a80c40da88291879b47974a8 [file] [log] [blame]
/*
* 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;
// A stub buffer used when deflate() called while inputBuffer has not been
// set.
private static final byte[] STUB_INPUT_BUFFER = new byte[0];
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() {
try {
synchronized (this) {
end(); // to allow overriding classes to clean up
endImpl(); // in case those classes don't call super.end()
}
} finally {
try {
super.finalize();
} catch (Throwable t) {
throw new AssertionError(t);
}
}
}
/**
* 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);
}