308 lines
9.8 KiB
Java
308 lines
9.8 KiB
Java
/*
|
|
* Copyright (C) 2014 The Android Open Source Project
|
|
* Copyright (c) 2006, 2021, 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.io.FilterInputStream;
|
|
import java.io.InputStream;
|
|
import java.io.IOException;
|
|
|
|
/**
|
|
* Implements an input stream filter for compressing data in the "deflate"
|
|
* compression format.
|
|
*
|
|
* @since 1.6
|
|
* @author David R Tribble (david@tribble.com)
|
|
*
|
|
* @see DeflaterOutputStream
|
|
* @see InflaterOutputStream
|
|
* @see InflaterInputStream
|
|
*/
|
|
|
|
public class DeflaterInputStream extends FilterInputStream {
|
|
/** Compressor for this stream. */
|
|
protected final Deflater def;
|
|
|
|
/** Input buffer for reading compressed data. */
|
|
protected final byte[] buf;
|
|
|
|
/** Temporary read buffer. */
|
|
private byte[] rbuf = new byte[1];
|
|
|
|
/** Default compressor is used. */
|
|
private boolean usesDefaultDeflater = false;
|
|
|
|
/** End of the underlying input stream has been reached. */
|
|
private boolean reachEOF = false;
|
|
|
|
/**
|
|
* Check to make sure that this stream has not been closed.
|
|
*/
|
|
private void ensureOpen() throws IOException {
|
|
if (in == null) {
|
|
throw new IOException("Stream closed");
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Creates a new input stream with a default compressor and buffer
|
|
* size.
|
|
*
|
|
* @param in input stream to read the uncompressed data to
|
|
* @throws NullPointerException if {@code in} is null
|
|
*/
|
|
public DeflaterInputStream(InputStream in) {
|
|
this(in, in != null ? new Deflater() : null);
|
|
usesDefaultDeflater = true;
|
|
}
|
|
|
|
/**
|
|
* Creates a new input stream with the specified compressor and a
|
|
* default buffer size.
|
|
*
|
|
* @param in input stream to read the uncompressed data to
|
|
* @param defl compressor ("deflater") for this stream
|
|
* @throws NullPointerException if {@code in} or {@code defl} is null
|
|
*/
|
|
public DeflaterInputStream(InputStream in, Deflater defl) {
|
|
this(in, defl, 512);
|
|
}
|
|
|
|
/**
|
|
* Creates a new input stream with the specified compressor and buffer
|
|
* size.
|
|
*
|
|
* @param in input stream to read the uncompressed data to
|
|
* @param defl compressor ("deflater") for this stream
|
|
* @param bufLen compression buffer size
|
|
* @throws IllegalArgumentException if {@code bufLen <= 0}
|
|
* @throws NullPointerException if {@code in} or {@code defl} is null
|
|
*/
|
|
public DeflaterInputStream(InputStream in, Deflater defl, int bufLen) {
|
|
super(in);
|
|
|
|
// Sanity checks
|
|
if (in == null)
|
|
throw new NullPointerException("Null input");
|
|
if (defl == null)
|
|
throw new NullPointerException("Null deflater");
|
|
if (bufLen < 1)
|
|
throw new IllegalArgumentException("Buffer size < 1");
|
|
|
|
// Initialize
|
|
def = defl;
|
|
buf = new byte[bufLen];
|
|
}
|
|
|
|
/**
|
|
* Closes this input stream and its underlying input stream, discarding
|
|
* any pending uncompressed data.
|
|
*
|
|
* @throws IOException if an I/O error occurs
|
|
*/
|
|
public void close() throws IOException {
|
|
if (in != null) {
|
|
try {
|
|
// Clean up
|
|
if (usesDefaultDeflater) {
|
|
def.end();
|
|
}
|
|
|
|
in.close();
|
|
} finally {
|
|
in = null;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reads a single byte of compressed data from the input stream.
|
|
* This method will block until some input can be read and compressed.
|
|
*
|
|
* @return a single byte of compressed data, or -1 if the end of the
|
|
* uncompressed input stream is reached
|
|
* @throws IOException if an I/O error occurs or if this stream is
|
|
* already closed
|
|
*/
|
|
public int read() throws IOException {
|
|
// Read a single byte of compressed data
|
|
int len = read(rbuf, 0, 1);
|
|
if (len <= 0)
|
|
return -1;
|
|
return (rbuf[0] & 0xFF);
|
|
}
|
|
|
|
/**
|
|
* Reads compressed data into a byte array.
|
|
* This method will block until some input can be read and compressed.
|
|
*
|
|
* @param b buffer into which the data is read
|
|
* @param off starting offset of the data within {@code b}
|
|
* @param len maximum number of compressed bytes to read into {@code b}
|
|
* @return the actual number of bytes read, or -1 if the end of the
|
|
* uncompressed input stream is reached
|
|
* @throws IndexOutOfBoundsException if {@code len > b.length - off}
|
|
* @throws IOException if an I/O error occurs or if this input stream is
|
|
* already closed
|
|
*/
|
|
public int read(byte[] b, int off, int len) throws IOException {
|
|
// Sanity checks
|
|
ensureOpen();
|
|
if (b == null) {
|
|
throw new NullPointerException("Null buffer for read");
|
|
} else if (off < 0 || len < 0 || len > b.length - off) {
|
|
throw new IndexOutOfBoundsException();
|
|
} else if (len == 0) {
|
|
return 0;
|
|
}
|
|
|
|
// Read and compress (deflate) input data bytes
|
|
int cnt = 0;
|
|
while (len > 0 && !def.finished()) {
|
|
int n;
|
|
|
|
// Read data from the input stream
|
|
if (def.needsInput()) {
|
|
n = in.read(buf, 0, buf.length);
|
|
if (n < 0) {
|
|
// End of the input stream reached
|
|
def.finish();
|
|
} else if (n > 0) {
|
|
def.setInput(buf, 0, n);
|
|
}
|
|
}
|
|
|
|
// Compress the input data, filling the read buffer
|
|
n = def.deflate(b, off, len);
|
|
cnt += n;
|
|
off += n;
|
|
len -= n;
|
|
}
|
|
// BEGIN Android-changed: Return more accurate value from available().
|
|
// Set reachEOF eagerly when the Deflater has finished, and not just when the number of
|
|
// bytes is zero so that available is more accurate.
|
|
// See http://b/111589691
|
|
/*
|
|
if (cnt == 0 && def.finished()) {
|
|
reachEOF = true;
|
|
cnt = -1;
|
|
}
|
|
*/
|
|
if (def.finished()) {
|
|
reachEOF = true;
|
|
if (cnt == 0) {
|
|
cnt = -1;
|
|
}
|
|
}
|
|
// END Android-changed: Return more accurate value from available().
|
|
|
|
return cnt;
|
|
}
|
|
|
|
/**
|
|
* Skips over and discards data from the input stream.
|
|
* This method may block until the specified number of bytes are read and
|
|
* skipped. <em>Note:</em> While {@code n} is given as a {@code long},
|
|
* the maximum number of bytes which can be skipped is
|
|
* {@code Integer.MAX_VALUE}.
|
|
*
|
|
* @param n number of bytes to be skipped
|
|
* @return the actual number of bytes skipped
|
|
* @throws IOException if an I/O error occurs or if this stream is
|
|
* already closed
|
|
*/
|
|
public long skip(long n) throws IOException {
|
|
if (n < 0) {
|
|
throw new IllegalArgumentException("negative skip length");
|
|
}
|
|
ensureOpen();
|
|
|
|
// Skip bytes by repeatedly decompressing small blocks
|
|
if (rbuf.length < 512)
|
|
rbuf = new byte[512];
|
|
|
|
int total = (int)Math.min(n, Integer.MAX_VALUE);
|
|
long cnt = 0;
|
|
while (total > 0) {
|
|
// Read a small block of uncompressed bytes
|
|
int len = read(rbuf, 0, (total <= rbuf.length ? total : rbuf.length));
|
|
|
|
if (len < 0) {
|
|
break;
|
|
}
|
|
cnt += len;
|
|
total -= len;
|
|
}
|
|
return cnt;
|
|
}
|
|
|
|
/**
|
|
* Returns 0 after EOF has been reached, otherwise always return 1.
|
|
* <p>
|
|
* Programs should not count on this method to return the actual number
|
|
* of bytes that could be read without blocking
|
|
* @return zero after the end of the underlying input stream has been
|
|
* reached, otherwise always returns 1
|
|
* @throws IOException if an I/O error occurs or if this stream is
|
|
* already closed
|
|
*/
|
|
public int available() throws IOException {
|
|
ensureOpen();
|
|
if (reachEOF) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/**
|
|
* Always returns {@code false} because this input stream does not support
|
|
* the {@link #mark mark()} and {@link #reset reset()} methods.
|
|
*
|
|
* @return false, always
|
|
*/
|
|
public boolean markSupported() {
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* <i>This operation is not supported</i>.
|
|
*
|
|
* @param limit maximum bytes that can be read before invalidating the position marker
|
|
*/
|
|
public void mark(int limit) {
|
|
// Operation not supported
|
|
}
|
|
|
|
/**
|
|
* <i>This operation is not supported</i>.
|
|
*
|
|
* @throws IOException always thrown
|
|
*/
|
|
public void reset() throws IOException {
|
|
throw new IOException("mark/reset not supported");
|
|
}
|
|
}
|