/*
* Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package java.util.zip;
import java.lang.ref.Cleaner.Cleanable;
import java.lang.ref.Reference;
import java.nio.ByteBuffer;
import java.nio.ReadOnlyBufferException;
import java.util.Objects;
import jdk.internal.ref.CleanerFactory;
import sun.nio.ch.DirectBuffer;
This class provides support for general purpose decompression using the
popular ZLIB compression library. The ZLIB compression library was
initially developed as part of the PNG graphics standard and is not
protected by patents. It is fully described in the specifications at
the java.util.zip
package description.
This class inflates sequences of ZLIB compressed bytes. The input byte sequence is provided in either byte array or byte buffer, via one of the setInput()
methods. The output byte sequence is written to the output byte array or byte buffer passed to the deflate()
methods.
The following code fragment demonstrates a trivial compression and decompression of a string using Deflater
and Inflater
.
try {
// Encode a String into bytes
String inputString = "blahblahblah\u20AC\u20AC";
byte[] input = inputString.getBytes("UTF-8");
// Compress the bytes
byte[] output = new byte[100];
Deflater compresser = new Deflater();
compresser.setInput(input);
compresser.finish();
int compressedDataLength = compresser.deflate(output);
// Decompress the bytes
Inflater decompresser = new Inflater();
decompresser.setInput(output, 0, compressedDataLength);
byte[] result = new byte[100];
int resultLength = decompresser.inflate(result);
decompresser.end();
// Decode the bytes into a String
String outputString = new String(result, 0, resultLength, "UTF-8");
} catch (java.io.UnsupportedEncodingException ex) {
// handle
} catch (java.util.zip.DataFormatException ex) {
// handle
}
Author: David Connelly See Also: API Note: To release resources used by this Inflater
, the end()
method should be called explicitly. Subclasses are responsible for the cleanup of resources acquired by the subclass. Subclasses that override Object.finalize()
in order to perform cleanup should be modified to use alternative cleanup mechanisms such as Cleaner
and remove the overriding finalize
method. Since: 1.1
/**
* This class provides support for general purpose decompression using the
* popular ZLIB compression library. The ZLIB compression library was
* initially developed as part of the PNG graphics standard and is not
* protected by patents. It is fully described in the specifications at
* the <a href="package-summary.html#package.description">java.util.zip
* package description</a>.
* <p>
* This class inflates sequences of ZLIB compressed bytes. The input byte
* sequence is provided in either byte array or byte buffer, via one of the
* {@code setInput()} methods. The output byte sequence is written to the
* output byte array or byte buffer passed to the {@code deflate()} methods.
* <p>
* The following code fragment demonstrates a trivial compression
* and decompression of a string using {@code Deflater} and
* {@code Inflater}.
*
* <blockquote><pre>
* try {
* // Encode a String into bytes
* String inputString = "blahblahblah\u20AC\u20AC";
* byte[] input = inputString.getBytes("UTF-8");
*
* // Compress the bytes
* byte[] output = new byte[100];
* Deflater compresser = new Deflater();
* compresser.setInput(input);
* compresser.finish();
* int compressedDataLength = compresser.deflate(output);
*
* // Decompress the bytes
* Inflater decompresser = new Inflater();
* decompresser.setInput(output, 0, compressedDataLength);
* byte[] result = new byte[100];
* int resultLength = decompresser.inflate(result);
* decompresser.end();
*
* // Decode the bytes into a String
* String outputString = new String(result, 0, resultLength, "UTF-8");
* } catch (java.io.UnsupportedEncodingException ex) {
* // handle
* } catch (java.util.zip.DataFormatException ex) {
* // handle
* }
* </pre></blockquote>
*
* @apiNote
* To release resources used by this {@code Inflater}, the {@link #end()} method
* should be called explicitly. Subclasses are responsible for the cleanup of resources
* acquired by the subclass. Subclasses that override {@link #finalize()} in order
* to perform cleanup should be modified to use alternative cleanup mechanisms such
* as {@link java.lang.ref.Cleaner} and remove the overriding {@code finalize} method.
*
* @see Deflater
* @author David Connelly
* @since 1.1
*
*/
public class Inflater {
private final InflaterZStreamRef zsRef;
private ByteBuffer input = ZipUtils.defaultBuf;
private byte[] inputArray;
private int inputPos, inputLim;
private boolean finished;
private boolean needDict;
private long bytesRead;
private long bytesWritten;
/*
* These fields are used as an "out" parameter from JNI when a
* DataFormatException is thrown during the inflate operation.
*/
private int inputConsumed;
private int outputConsumed;
static {
ZipUtils.loadLibrary();
initIDs();
}
Creates a new decompressor. If the parameter 'nowrap' is true then
the ZLIB header and checksum fields will not be used. This provides
compatibility with the compression format used by both GZIP and PKZIP.
Note: When using the 'nowrap' option it is also necessary to provide
an extra "dummy" byte as input. This is required by the ZLIB native
library in order to support certain optimizations.
Params: - nowrap – if true then support GZIP compatible compression
/**
* Creates a new decompressor. If the parameter 'nowrap' is true then
* the ZLIB header and checksum fields will not be used. This provides
* compatibility with the compression format used by both GZIP and PKZIP.
* <p>
* Note: When using the 'nowrap' option it is also necessary to provide
* an extra "dummy" byte as input. This is required by the ZLIB native
* library in order to support certain optimizations.
*
* @param nowrap if true then support GZIP compatible compression
*/
public Inflater(boolean nowrap) {
this.zsRef = new InflaterZStreamRef(this, init(nowrap));
}
Creates a new decompressor.
/**
* Creates a new decompressor.
*/
public Inflater() {
this(false);
}
Sets input data for decompression.
One of the setInput()
methods should be called whenever needsInput()
returns true indicating that more input data is required.
Params: - input – the input data bytes
- off – the start offset of the input data
- len – the length of the input data
See Also:
/**
* Sets input data for decompression.
* <p>
* One of the {@code setInput()} methods should be called whenever
* {@code needsInput()} returns true indicating that more input data
* is required.
*
* @param input the input data bytes
* @param off the start offset of the input data
* @param len the length of the input data
* @see Inflater#needsInput
*/
public void setInput(byte[] input, int off, int len) {
if (off < 0 || len < 0 || off > input.length - len) {
throw new ArrayIndexOutOfBoundsException();
}
synchronized (zsRef) {
this.input = null;
this.inputArray = input;
this.inputPos = off;
this.inputLim = off + len;
}
}
Sets input data for decompression.
One of the setInput()
methods should be called whenever needsInput()
returns true indicating that more input data is required.
Params: - input – the input data bytes
See Also:
/**
* Sets input data for decompression.
* <p>
* One of the {@code setInput()} methods should be called whenever
* {@code needsInput()} returns true indicating that more input data
* is required.
*
* @param input the input data bytes
* @see Inflater#needsInput
*/
public void setInput(byte[] input) {
setInput(input, 0, input.length);
}
Sets input data for decompression.
One of the setInput()
methods should be called whenever needsInput()
returns true indicating that more input data is required.
The given buffer's position will be advanced as inflate
operations are performed, up to the buffer's limit.
The input buffer may be modified (refilled) between inflate
operations; doing so is equivalent to creating a new buffer
and setting it with this method.
Modifying the input buffer's contents, position, or limit
concurrently with an inflate operation will result in
undefined behavior, which may include incorrect operation
results or operation failure.
Params: - input – the input data bytes
See Also: Since: 11
/**
* Sets input data for decompression.
* <p>
* One of the {@code setInput()} methods should be called whenever
* {@code needsInput()} returns true indicating that more input data
* is required.
* <p>
* The given buffer's position will be advanced as inflate
* operations are performed, up to the buffer's limit.
* The input buffer may be modified (refilled) between inflate
* operations; doing so is equivalent to creating a new buffer
* and setting it with this method.
* <p>
* Modifying the input buffer's contents, position, or limit
* concurrently with an inflate operation will result in
* undefined behavior, which may include incorrect operation
* results or operation failure.
*
* @param input the input data bytes
* @see Inflater#needsInput
* @since 11
*/
public void setInput(ByteBuffer input) {
Objects.requireNonNull(input);
synchronized (zsRef) {
this.input = input;
this.inputArray = null;
}
}
Sets the preset dictionary to the given array of bytes. Should be
called when inflate() returns 0 and needsDictionary() returns true
indicating that a preset dictionary is required. The method getAdler()
can be used to get the Adler-32 value of the dictionary needed.
Params: - dictionary – the dictionary data bytes
- off – the start offset of the data
- len – the length of the data
See Also:
/**
* Sets the preset dictionary to the given array of bytes. Should be
* called when inflate() returns 0 and needsDictionary() returns true
* indicating that a preset dictionary is required. The method getAdler()
* can be used to get the Adler-32 value of the dictionary needed.
* @param dictionary the dictionary data bytes
* @param off the start offset of the data
* @param len the length of the data
* @see Inflater#needsDictionary
* @see Inflater#getAdler
*/
public void setDictionary(byte[] dictionary, int off, int len) {
if (off < 0 || len < 0 || off > dictionary.length - len) {
throw new ArrayIndexOutOfBoundsException();
}
synchronized (zsRef) {
ensureOpen();
setDictionary(zsRef.address(), dictionary, off, len);
needDict = false;
}
}
Sets the preset dictionary to the given array of bytes. Should be
called when inflate() returns 0 and needsDictionary() returns true
indicating that a preset dictionary is required. The method getAdler()
can be used to get the Adler-32 value of the dictionary needed.
Params: - dictionary – the dictionary data bytes
See Also:
/**
* Sets the preset dictionary to the given array of bytes. Should be
* called when inflate() returns 0 and needsDictionary() returns true
* indicating that a preset dictionary is required. The method getAdler()
* can be used to get the Adler-32 value of the dictionary needed.
* @param dictionary the dictionary data bytes
* @see Inflater#needsDictionary
* @see Inflater#getAdler
*/
public void setDictionary(byte[] dictionary) {
setDictionary(dictionary, 0, dictionary.length);
}
Sets the preset dictionary to the bytes in the given buffer. Should be
called when inflate() returns 0 and needsDictionary() returns true
indicating that a preset dictionary is required. The method getAdler()
can be used to get the Adler-32 value of the dictionary needed.
The bytes in given byte buffer will be fully consumed by this method. On
return, its position will equal its limit.
Params: - dictionary – the dictionary data bytes
See Also: Since: 11
/**
* Sets the preset dictionary to the bytes in the given buffer. Should be
* called when inflate() returns 0 and needsDictionary() returns true
* indicating that a preset dictionary is required. The method getAdler()
* can be used to get the Adler-32 value of the dictionary needed.
* <p>
* The bytes in given byte buffer will be fully consumed by this method. On
* return, its position will equal its limit.
*
* @param dictionary the dictionary data bytes
* @see Inflater#needsDictionary
* @see Inflater#getAdler
* @since 11
*/
public void setDictionary(ByteBuffer dictionary) {
synchronized (zsRef) {
int position = dictionary.position();
int remaining = Math.max(dictionary.limit() - position, 0);
ensureOpen();
if (dictionary.isDirect()) {
long address = ((DirectBuffer) dictionary).address();
try {
setDictionaryBuffer(zsRef.address(), address + position, remaining);
} finally {
Reference.reachabilityFence(dictionary);
}
} else {
byte[] array = ZipUtils.getBufferArray(dictionary);
int offset = ZipUtils.getBufferOffset(dictionary);
setDictionary(zsRef.address(), array, offset + position, remaining);
}
dictionary.position(position + remaining);
needDict = false;
}
}
Returns the total number of bytes remaining in the input buffer.
This can be used to find out what bytes still remain in the input
buffer after decompression has finished.
Returns: the total number of bytes remaining in the input buffer
/**
* Returns the total number of bytes remaining in the input buffer.
* This can be used to find out what bytes still remain in the input
* buffer after decompression has finished.
* @return the total number of bytes remaining in the input buffer
*/
public int getRemaining() {
synchronized (zsRef) {
ByteBuffer input = this.input;
return input == null ? inputLim - inputPos : input.remaining();
}
}
Returns true if no data remains in the input buffer. This can be used to determine if one of the setInput()
methods should be called in order to provide more input. Returns: true if no data remains in the input buffer
/**
* Returns true if no data remains in the input buffer. This can
* be used to determine if one of the {@code setInput()} methods should be
* called in order to provide more input.
*
* @return true if no data remains in the input buffer
*/
public boolean needsInput() {
synchronized (zsRef) {
ByteBuffer input = this.input;
return input == null ? inputLim == inputPos : ! input.hasRemaining();
}
}
Returns true if a preset dictionary is needed for decompression.
See Also: Returns: true if a preset dictionary is needed for decompression
/**
* Returns true if a preset dictionary is needed for decompression.
* @return true if a preset dictionary is needed for decompression
* @see Inflater#setDictionary
*/
public boolean needsDictionary() {
synchronized (zsRef) {
return needDict;
}
}
Returns true if the end of the compressed data stream has been
reached.
Returns: true if the end of the compressed data stream has been
reached
/**
* Returns true if the end of the compressed data stream has been
* reached.
* @return true if the end of the compressed data stream has been
* reached
*/
public boolean finished() {
synchronized (zsRef) {
return finished;
}
}
Uncompresses bytes into specified buffer. Returns actual number
of bytes uncompressed. A return value of 0 indicates that
needsInput() or needsDictionary() should be called in order to
determine if more input data or a preset dictionary is required.
In the latter case, getAdler() can be used to get the Adler-32
value of the dictionary required.
If the setInput(ByteBuffer)
method was called to provide a buffer for input, the input buffer's position will be advanced by the number of bytes consumed by this operation, even in the event that a DataFormatException
is thrown.
The remaining byte count will be reduced by the number of consumed input bytes. If the setInput(ByteBuffer)
method was called to provide a buffer for input, the input buffer's position will be advanced the number of consumed bytes.
These byte totals, as well as the total bytes read and the total bytes written values, will be updated even in the event that a DataFormatException
is thrown to reflect the amount of data consumed and produced before the exception occurred.
Params: - output – the buffer for the uncompressed data
- off – the start offset of the data
- len – the maximum number of uncompressed bytes
Throws: - DataFormatException – if the compressed data format is invalid
See Also: Returns: the actual number of uncompressed bytes
/**
* Uncompresses bytes into specified buffer. Returns actual number
* of bytes uncompressed. A return value of 0 indicates that
* needsInput() or needsDictionary() should be called in order to
* determine if more input data or a preset dictionary is required.
* In the latter case, getAdler() can be used to get the Adler-32
* value of the dictionary required.
* <p>
* If the {@link #setInput(ByteBuffer)} method was called to provide a buffer
* for input, the input buffer's position will be advanced by the number of bytes
* consumed by this operation, even in the event that a {@link DataFormatException}
* is thrown.
* <p>
* The {@linkplain #getRemaining() remaining byte count} will be reduced by
* the number of consumed input bytes. If the {@link #setInput(ByteBuffer)}
* method was called to provide a buffer for input, the input buffer's position
* will be advanced the number of consumed bytes.
* <p>
* These byte totals, as well as
* the {@linkplain #getBytesRead() total bytes read}
* and the {@linkplain #getBytesWritten() total bytes written}
* values, will be updated even in the event that a {@link DataFormatException}
* is thrown to reflect the amount of data consumed and produced before the
* exception occurred.
*
* @param output the buffer for the uncompressed data
* @param off the start offset of the data
* @param len the maximum number of uncompressed bytes
* @return the actual number of uncompressed bytes
* @throws DataFormatException if the compressed data format is invalid
* @see Inflater#needsInput
* @see Inflater#needsDictionary
*/
public int inflate(byte[] output, int off, int len)
throws DataFormatException
{
if (off < 0 || len < 0 || off > output.length - len) {
throw new ArrayIndexOutOfBoundsException();
}
synchronized (zsRef) {
ensureOpen();
ByteBuffer input = this.input;
long result;
int inputPos;
try {
if (input == null) {
inputPos = this.inputPos;
try {
result = inflateBytesBytes(zsRef.address(),
inputArray, inputPos, inputLim - inputPos,
output, off, len);
} catch (DataFormatException e) {
this.inputPos = inputPos + inputConsumed;
throw e;
}
} else {
inputPos = input.position();
try {
int inputRem = Math.max(input.limit() - inputPos, 0);
if (input.isDirect()) {
try {
long inputAddress = ((DirectBuffer) input).address();
result = inflateBufferBytes(zsRef.address(),
inputAddress + inputPos, inputRem,
output, off, len);
} finally {
Reference.reachabilityFence(input);
}
} else {
byte[] inputArray = ZipUtils.getBufferArray(input);
int inputOffset = ZipUtils.getBufferOffset(input);
result = inflateBytesBytes(zsRef.address(),
inputArray, inputOffset + inputPos, inputRem,
output, off, len);
}
} catch (DataFormatException e) {
input.position(inputPos + inputConsumed);
throw e;
}
}
} catch (DataFormatException e) {
bytesRead += inputConsumed;
inputConsumed = 0;
int written = outputConsumed;
bytesWritten += written;
outputConsumed = 0;
throw e;
}
int read = (int) (result & 0x7fff_ffffL);
int written = (int) (result >>> 31 & 0x7fff_ffffL);
if ((result >>> 62 & 1) != 0) {
finished = true;
}
if ((result >>> 63 & 1) != 0) {
needDict = true;
}
if (input != null) {
input.position(inputPos + read);
} else {
this.inputPos = inputPos + read;
}
bytesWritten += written;
bytesRead += read;
return written;
}
}
Uncompresses bytes into specified buffer. Returns actual number
of bytes uncompressed. A return value of 0 indicates that
needsInput() or needsDictionary() should be called in order to
determine if more input data or a preset dictionary is required.
In the latter case, getAdler() can be used to get the Adler-32
value of the dictionary required.
The remaining byte count will be reduced by the number of consumed input bytes. If the setInput(ByteBuffer)
method was called to provide a buffer for input, the input buffer's position will be advanced the number of consumed bytes.
These byte totals, as well as the total bytes read and the total bytes written values, will be updated even in the event that a DataFormatException
is thrown to reflect the amount of data consumed and produced before the exception occurred.
Params: - output – the buffer for the uncompressed data
Throws: - DataFormatException – if the compressed data format is invalid
See Also: Returns: the actual number of uncompressed bytes
/**
* Uncompresses bytes into specified buffer. Returns actual number
* of bytes uncompressed. A return value of 0 indicates that
* needsInput() or needsDictionary() should be called in order to
* determine if more input data or a preset dictionary is required.
* In the latter case, getAdler() can be used to get the Adler-32
* value of the dictionary required.
* <p>
* The {@linkplain #getRemaining() remaining byte count} will be reduced by
* the number of consumed input bytes. If the {@link #setInput(ByteBuffer)}
* method was called to provide a buffer for input, the input buffer's position
* will be advanced the number of consumed bytes.
* <p>
* These byte totals, as well as
* the {@linkplain #getBytesRead() total bytes read}
* and the {@linkplain #getBytesWritten() total bytes written}
* values, will be updated even in the event that a {@link DataFormatException}
* is thrown to reflect the amount of data consumed and produced before the
* exception occurred.
*
* @param output the buffer for the uncompressed data
* @return the actual number of uncompressed bytes
* @throws DataFormatException if the compressed data format is invalid
* @see Inflater#needsInput
* @see Inflater#needsDictionary
*/
public int inflate(byte[] output) throws DataFormatException {
return inflate(output, 0, output.length);
}
Uncompresses bytes into specified buffer. Returns actual number
of bytes uncompressed. A return value of 0 indicates that
needsInput() or needsDictionary() should be called in order to
determine if more input data or a preset dictionary is required.
In the latter case, getAdler() can be used to get the Adler-32
value of the dictionary required.
On success, the position of the given output
byte buffer will be advanced by as many bytes as were produced by the operation, which is equal to the number returned by this method. Note that the position of the output
buffer will be advanced even in the event that a DataFormatException
is thrown.
The remaining byte count will be reduced by the number of consumed input bytes. If the setInput(ByteBuffer)
method was called to provide a buffer for input, the input buffer's position will be advanced the number of consumed bytes.
These byte totals, as well as the total bytes read and the total bytes written values, will be updated even in the event that a DataFormatException
is thrown to reflect the amount of data consumed and produced before the exception occurred.
Params: - output – the buffer for the uncompressed data
Throws: - DataFormatException – if the compressed data format is invalid
- ReadOnlyBufferException – if the given output buffer is read-only
See Also: Returns: the actual number of uncompressed bytes Since: 11
/**
* Uncompresses bytes into specified buffer. Returns actual number
* of bytes uncompressed. A return value of 0 indicates that
* needsInput() or needsDictionary() should be called in order to
* determine if more input data or a preset dictionary is required.
* In the latter case, getAdler() can be used to get the Adler-32
* value of the dictionary required.
* <p>
* On success, the position of the given {@code output} byte buffer will be
* advanced by as many bytes as were produced by the operation, which is equal
* to the number returned by this method. Note that the position of the
* {@code output} buffer will be advanced even in the event that a
* {@link DataFormatException} is thrown.
* <p>
* The {@linkplain #getRemaining() remaining byte count} will be reduced by
* the number of consumed input bytes. If the {@link #setInput(ByteBuffer)}
* method was called to provide a buffer for input, the input buffer's position
* will be advanced the number of consumed bytes.
* <p>
* These byte totals, as well as
* the {@linkplain #getBytesRead() total bytes read}
* and the {@linkplain #getBytesWritten() total bytes written}
* values, will be updated even in the event that a {@link DataFormatException}
* is thrown to reflect the amount of data consumed and produced before the
* exception occurred.
*
* @param output the buffer for the uncompressed data
* @return the actual number of uncompressed bytes
* @throws DataFormatException if the compressed data format is invalid
* @throws ReadOnlyBufferException if the given output buffer is read-only
* @see Inflater#needsInput
* @see Inflater#needsDictionary
* @since 11
*/
public int inflate(ByteBuffer output) throws DataFormatException {
if (output.isReadOnly()) {
throw new ReadOnlyBufferException();
}
synchronized (zsRef) {
ensureOpen();
ByteBuffer input = this.input;
long result;
int inputPos;
int outputPos = output.position();
int outputRem = Math.max(output.limit() - outputPos, 0);
try {
if (input == null) {
inputPos = this.inputPos;
try {
if (output.isDirect()) {
long outputAddress = ((DirectBuffer) output).address();
try {
result = inflateBytesBuffer(zsRef.address(),
inputArray, inputPos, inputLim - inputPos,
outputAddress + outputPos, outputRem);
} finally {
Reference.reachabilityFence(output);
}
} else {
byte[] outputArray = ZipUtils.getBufferArray(output);
int outputOffset = ZipUtils.getBufferOffset(output);
result = inflateBytesBytes(zsRef.address(),
inputArray, inputPos, inputLim - inputPos,
outputArray, outputOffset + outputPos, outputRem);
}
} catch (DataFormatException e) {
this.inputPos = inputPos + inputConsumed;
throw e;
}
} else {
inputPos = input.position();
int inputRem = Math.max(input.limit() - inputPos, 0);
try {
if (input.isDirect()) {
long inputAddress = ((DirectBuffer) input).address();
try {
if (output.isDirect()) {
long outputAddress = ((DirectBuffer) output).address();
try {
result = inflateBufferBuffer(zsRef.address(),
inputAddress + inputPos, inputRem,
outputAddress + outputPos, outputRem);
} finally {
Reference.reachabilityFence(output);
}
} else {
byte[] outputArray = ZipUtils.getBufferArray(output);
int outputOffset = ZipUtils.getBufferOffset(output);
result = inflateBufferBytes(zsRef.address(),
inputAddress + inputPos, inputRem,
outputArray, outputOffset + outputPos, outputRem);
}
} finally {
Reference.reachabilityFence(input);
}
} else {
byte[] inputArray = ZipUtils.getBufferArray(input);
int inputOffset = ZipUtils.getBufferOffset(input);
if (output.isDirect()) {
long outputAddress = ((DirectBuffer) output).address();
try {
result = inflateBytesBuffer(zsRef.address(),
inputArray, inputOffset + inputPos, inputRem,
outputAddress + outputPos, outputRem);
} finally {
Reference.reachabilityFence(output);
}
} else {
byte[] outputArray = ZipUtils.getBufferArray(output);
int outputOffset = ZipUtils.getBufferOffset(output);
result = inflateBytesBytes(zsRef.address(),
inputArray, inputOffset + inputPos, inputRem,
outputArray, outputOffset + outputPos, outputRem);
}
}
} catch (DataFormatException e) {
input.position(inputPos + inputConsumed);
throw e;
}
}
} catch (DataFormatException e) {
bytesRead += inputConsumed;
inputConsumed = 0;
int written = outputConsumed;
output.position(outputPos + written);
bytesWritten += written;
outputConsumed = 0;
throw e;
}
int read = (int) (result & 0x7fff_ffffL);
int written = (int) (result >>> 31 & 0x7fff_ffffL);
if ((result >>> 62 & 1) != 0) {
finished = true;
}
if ((result >>> 63 & 1) != 0) {
needDict = true;
}
if (input != null) {
input.position(inputPos + read);
} else {
this.inputPos = inputPos + read;
}
// Note: this method call also serves to keep the byteBuffer ref alive
output.position(outputPos + written);
bytesWritten += written;
bytesRead += read;
return written;
}
}
Returns the ADLER-32 value of the uncompressed data.
Returns: the ADLER-32 value of the uncompressed data
/**
* Returns the ADLER-32 value of the uncompressed data.
* @return the ADLER-32 value of the uncompressed data
*/
public int getAdler() {
synchronized (zsRef) {
ensureOpen();
return getAdler(zsRef.address());
}
}
Returns the total number of compressed bytes input so far.
Since the number of bytes may be greater than Integer.MAX_VALUE, the getBytesRead()
method is now the preferred means of obtaining this information.
Returns: the total number of compressed bytes input so far
/**
* Returns the total number of compressed bytes input so far.
*
* <p>Since the number of bytes may be greater than
* Integer.MAX_VALUE, the {@link #getBytesRead()} method is now
* the preferred means of obtaining this information.</p>
*
* @return the total number of compressed bytes input so far
*/
public int getTotalIn() {
return (int) getBytesRead();
}
Returns the total number of compressed bytes input so far.
Returns: the total (non-negative) number of compressed bytes input so far Since: 1.5
/**
* Returns the total number of compressed bytes input so far.
*
* @return the total (non-negative) number of compressed bytes input so far
* @since 1.5
*/
public long getBytesRead() {
synchronized (zsRef) {
ensureOpen();
return bytesRead;
}
}
Returns the total number of uncompressed bytes output so far.
Since the number of bytes may be greater than Integer.MAX_VALUE, the getBytesWritten()
method is now the preferred means of obtaining this information.
Returns: the total number of uncompressed bytes output so far
/**
* Returns the total number of uncompressed bytes output so far.
*
* <p>Since the number of bytes may be greater than
* Integer.MAX_VALUE, the {@link #getBytesWritten()} method is now
* the preferred means of obtaining this information.</p>
*
* @return the total number of uncompressed bytes output so far
*/
public int getTotalOut() {
return (int) getBytesWritten();
}
Returns the total number of uncompressed bytes output so far.
Returns: the total (non-negative) number of uncompressed bytes output so far Since: 1.5
/**
* Returns the total number of uncompressed bytes output so far.
*
* @return the total (non-negative) number of uncompressed bytes output so far
* @since 1.5
*/
public long getBytesWritten() {
synchronized (zsRef) {
ensureOpen();
return bytesWritten;
}
}
Resets inflater so that a new set of input data can be processed.
/**
* Resets inflater so that a new set of input data can be processed.
*/
public void reset() {
synchronized (zsRef) {
ensureOpen();
reset(zsRef.address());
input = ZipUtils.defaultBuf;
inputArray = null;
finished = false;
needDict = false;
bytesRead = bytesWritten = 0;
}
}
Closes the decompressor and discards any unprocessed input.
This method should be called when the decompressor is no longer
being used. Once this method is called, the behavior of the
Inflater object is undefined.
/**
* Closes the decompressor and discards any unprocessed input.
*
* This method should be called when the decompressor is no longer
* being used. Once this method is called, the behavior of the
* Inflater object is undefined.
*/
public void end() {
synchronized (zsRef) {
zsRef.clean();
input = ZipUtils.defaultBuf;
inputArray = null;
}
}
private void ensureOpen () {
assert Thread.holdsLock(zsRef);
if (zsRef.address() == 0)
throw new NullPointerException("Inflater has been closed");
}
private static native void initIDs();
private static native long init(boolean nowrap);
private static native void setDictionary(long addr, byte[] b, int off,
int len);
private static native void setDictionaryBuffer(long addr, long bufAddress, int len);
private native long inflateBytesBytes(long addr,
byte[] inputArray, int inputOff, int inputLen,
byte[] outputArray, int outputOff, int outputLen) throws DataFormatException;
private native long inflateBytesBuffer(long addr,
byte[] inputArray, int inputOff, int inputLen,
long outputAddress, int outputLen) throws DataFormatException;
private native long inflateBufferBytes(long addr,
long inputAddress, int inputLen,
byte[] outputArray, int outputOff, int outputLen) throws DataFormatException;
private native long inflateBufferBuffer(long addr,
long inputAddress, int inputLen,
long outputAddress, int outputLen) throws DataFormatException;
private static native int getAdler(long addr);
private static native void reset(long addr);
private static native void end(long addr);
A reference to the native zlib's z_stream structure. It also
serves as the "cleaner" to clean up the native resource when
the Inflater is ended, closed or cleaned.
/**
* A reference to the native zlib's z_stream structure. It also
* serves as the "cleaner" to clean up the native resource when
* the Inflater is ended, closed or cleaned.
*/
static class InflaterZStreamRef implements Runnable {
private long address;
private final Cleanable cleanable;
private InflaterZStreamRef(Inflater owner, long addr) {
this.cleanable = (owner != null) ? CleanerFactory.cleaner().register(owner, this) : null;
this.address = addr;
}
long address() {
return address;
}
void clean() {
cleanable.clean();
}
public synchronized void run() {
long addr = address;
address = 0;
if (addr != 0) {
end(addr);
}
}
}
}