blob: 9dd56f35a44d65dd8d8eec0d8b7741921dd095d2 [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;
import java.io.FileDescriptor;
/**
* This class uncompresses data that was compressed 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 InflaterInputStream} and its
* descendants.
* <p>
* The typical usage of a {@code Inflater} outside this package consists of a
* specific call to one of its constructors before being passed to an instance
* of {@code InflaterInputStream}.
*
* @see InflaterInputStream
* @see Deflater
*/
public class Inflater {
private boolean finished; // Set by the inflateImpl native
int inLength;
int inRead;
private boolean needsDictionary; // Set by the inflateImpl native
private long streamHandle = -1;
/**
* This constructor creates an inflater that expects a header from the input
* stream. Use {@code Inflater(boolean)} if the input comes without a ZLIB
* header.
*/
public Inflater() {
this(false);
}
/**
* This constructor allows to create an inflater that expects no header from
* the input stream.
*
* @param noHeader
* {@code true} indicates that no ZLIB header comes with the
* input.
*/
public Inflater(boolean noHeader) {
streamHandle = createStream(noHeader);
}
private native long createStream(boolean noHeader1);
/**
* Release any resources associated with this {@code Inflater}. Any unused
* input/output is discarded. This is also called by the finalize method.
*/
public synchronized void end() {
if (streamHandle != -1) {
endImpl(streamHandle);
inRead = 0;
inLength = 0;
streamHandle = -1;
}
}
private native synchronized void endImpl(long handle);
@Override protected void finalize() {
try {
end();
} finally {
try {
super.finalize();
} catch (Throwable t) {
throw new AssertionError(t);
}
}
}
/**
* Indicates if the {@code Inflater} has inflated the entire deflated
* stream. If deflated bytes remain and {@code needsInput()} returns {@code
* true} this method will return {@code false}. This method should be
* called after all deflated input is supplied to the {@code Inflater}.
*
* @return {@code true} if all input has been inflated, {@code false}
* otherwise.
*/
public synchronized boolean finished() {
return finished;
}
/**
* Returns the <i>Adler32</i> checksum of either all bytes inflated, or the
* checksum of the preset dictionary if one has been supplied.
*
* @return The <i>Adler32</i> checksum associated with this
* {@code Inflater}.
*/
public synchronized int getAdler() {
if (streamHandle == -1) {
throw new IllegalStateException();
}
return getAdlerImpl(streamHandle);
}
private native synchronized int getAdlerImpl(long handle);
/**
* Returns the total number of bytes read by the {@code Inflater}. This
* method performs the same as {@code getTotalIn()} except that it returns a
* {@code long} value instead of an integer.
*
* @return the total number of bytes read.
*/
public synchronized long getBytesRead() {
// Throw NPE here
if (streamHandle == -1) {
throw new NullPointerException();
}
return getTotalInImpl(streamHandle);
}
/**
* Returns a the total number of bytes read by the {@code Inflater}. This
* method performs the same as {@code getTotalOut} except it returns a
* {@code long} value instead of an integer.
*
* @return the total bytes written to the output buffer.
*/
public synchronized long getBytesWritten() {
// Throw NPE here
if (streamHandle == -1) {
throw new NullPointerException();
}
return getTotalOutImpl(streamHandle);
}
/**
* Returns the number of bytes of current input remaining to be read by the
* inflater.
*
* @return the number of bytes of unread input.
*/
public synchronized int getRemaining() {
return inLength - inRead;
}
/**
* Returns total number of bytes of input read by the {@code Inflater}. The
* result value is limited by {@code Integer.MAX_VALUE}.
*
* @return the total number of bytes read.
*/
public synchronized int getTotalIn() {
if (streamHandle == -1) {
throw new IllegalStateException();
}
long totalIn = getTotalInImpl(streamHandle);
return (totalIn <= Integer.MAX_VALUE ? (int) totalIn
: Integer.MAX_VALUE);
}
private synchronized native long getTotalInImpl(long handle);
/**
* Returns total number of bytes written to the output buffer by the {@code
* Inflater}. The result value is limited by {@code Integer.MAX_VALUE}.
*
* @return the total bytes of output data written.
*/
public synchronized int getTotalOut() {
if (streamHandle == -1) {
throw new IllegalStateException();
}
long totalOut = getTotalOutImpl(streamHandle);
return (totalOut <= Integer.MAX_VALUE ? (int) totalOut
: Integer.MAX_VALUE);
}
private native synchronized long getTotalOutImpl(long handle);
/**
* Inflates bytes from current input and stores them in {@code buf}.
*
* @param buf
* the buffer where decompressed data bytes are written.
* @return the number of bytes inflated.
* @throws DataFormatException
* if the underlying stream is corrupted or was not compressed
* using a {@code Deflater}.
*/
public int inflate(byte[] buf) throws DataFormatException {
return inflate(buf, 0, buf.length);
}
/**
* Inflates up to n bytes from the current input and stores them in {@code
* buf} starting at {@code off}.
*
* @param buf
* the buffer to write inflated bytes to.
* @param off
* the offset in buffer where to start writing decompressed data.
* @param nbytes
* the number of inflated bytes to write to {@code buf}.
* @throws DataFormatException
* if the underlying stream is corrupted or was not compressed
* using a {@code Deflater}.
* @return the number of bytes inflated.
*/
public synchronized int inflate(byte[] buf, int off, int nbytes)
throws DataFormatException {
// avoid int overflow, check null buf
if (off > buf.length || nbytes < 0 || off < 0
|| buf.length - off < nbytes) {
throw new ArrayIndexOutOfBoundsException();
}
if (nbytes == 0) {
return 0;
}
if (streamHandle == -1) {
throw new IllegalStateException();
}
if (needsInput()) {
return 0;
}
boolean neededDict = needsDictionary;
needsDictionary = false;
int result = inflateImpl(buf, off, nbytes, streamHandle);
if (needsDictionary && neededDict) {
throw new DataFormatException("Needs dictionary");
}
return result;
}
private native synchronized int inflateImpl(byte[] buf, int off,
int nbytes, long handle);
/**
* Indicates whether the input bytes were compressed with a preset
* dictionary. This method should be called prior to {@code inflate()} to
* determine whether a dictionary is required. If so {@code setDictionary()}
* should be called with the appropriate dictionary prior to calling {@code
* inflate()}.
*
* @return {@code true} if a preset dictionary is required for inflation.
* @see #setDictionary(byte[])
* @see #setDictionary(byte[], int, int)
*/
public synchronized boolean needsDictionary() {
return needsDictionary;
}
/**
* Indicates that input has to be passed to the inflater.
*
* @return {@code true} if {@code setInput} has to be called before
* inflation can proceed.
* @see #setInput(byte[])
*/
public synchronized boolean needsInput() {
return inRead == inLength;
}
/**
* Resets the {@code Inflater}. Should be called prior to inflating a new
* set of data.
*/
public synchronized void reset() {
if (streamHandle == -1) {
throw new NullPointerException();
}
finished = false;
needsDictionary = false;
inLength = inRead = 0;
resetImpl(streamHandle);
}
private native synchronized void resetImpl(long handle);
/**
* Sets the preset dictionary to be used for inflation to {@code buf}.
* {@code needsDictionary()} can be called to determine whether the current
* input was deflated using a preset dictionary.
*
* @param buf
* The buffer containing the dictionary bytes.
* @see #needsDictionary
*/
public synchronized void setDictionary(byte[] buf) {
setDictionary(buf, 0, buf.length);
}
/**
* Like {@code setDictionary(byte[])}, allowing to define a specific region
* inside {@code buf} to be used as a dictionary.
* <p>
* The dictionary should be set if the {@link #inflate(byte[])} returned
* zero bytes inflated and {@link #needsDictionary()} returns
* <code>true</code>.
*
* @param buf
* the buffer containing the dictionary data bytes.
* @param off
* the offset of the data.
* @param nbytes
* the length of the data.
* @see #needsDictionary
*/
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 native synchronized void setDictionaryImpl(byte[] buf, int off,
int nbytes, long handle);
/**
* Sets the current input to to be decrompressed. This method should only be
* called if {@code needsInput()} returns {@code true}.
*
* @param buf
* the input buffer.
* @see #needsInput
*/
public synchronized void setInput(byte[] buf) {
setInput(buf, 0, buf.length);
}
/**
* Sets the current input to the region of the input buffer starting at
* {@code off} and ending at {@code nbytes - 1} where data is written after
* decompression. This method should only be called if {@code needsInput()}
* returns {@code true}.
*
* @param buf
* the input buffer.
* @param off
* the offset to read from the input buffer.
* @param nbytes
* the number of bytes to read.
* @see #needsInput
*/
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) {
inRead = 0;
inLength = nbytes;
setInputImpl(buf, off, nbytes, streamHandle);
} else {
throw new ArrayIndexOutOfBoundsException();
}
}
// BEGIN android-only
/**
* Sets the current input to the region within a file starting at {@code
* off} and ending at {@code nbytes - 1}. This method should only be called
* if {@code needsInput()} returns {@code true}.
*
* @param fd
* the input file.
* @param off
* the offset to read from in buffer.
* @param nbytes
* the number of bytes to read.
* @see #needsInput
*/
synchronized int setFileInput(FileDescriptor fd, long off, int nbytes) {
if (streamHandle == -1) {
throw new IllegalStateException();
}
inRead = 0;
inLength = setFileInputImpl(fd, off, nbytes, streamHandle);
return inLength;
}
// END android-only
private native synchronized void setInputImpl(byte[] buf, int off,
int nbytes, long handle);
// BEGIN android-only
private native synchronized int setFileInputImpl(FileDescriptor fd, long off,
int nbytes, long handle);
// END android-only
}