InflaterInputStream.java
/*
* Copyright (c) 2011 ymnk, JCraft,Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted
* provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of conditions
* and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list of
* conditions and the following disclaimer in the documentation and/or other materials provided with
* the distribution.
*
* 3. The names of the authors may not be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL JCRAFT, INC. OR ANY CONTRIBUTORS TO THIS SOFTWARE BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.jcraft.jsch.jzlib;
import java.io.EOFException;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
final class InflaterInputStream extends FilterInputStream {
protected final Inflater inflater;
protected byte[] buf;
private boolean closed = false;
protected boolean eof = false;
private boolean close_in = true;
protected static final int DEFAULT_BUFSIZE = 512;
InflaterInputStream(InputStream in) throws IOException {
this(in, false);
}
InflaterInputStream(InputStream in, boolean nowrap) throws IOException {
this(in, new Inflater(nowrap));
myinflater = true;
}
InflaterInputStream(InputStream in, Inflater inflater) throws IOException {
this(in, inflater, DEFAULT_BUFSIZE);
}
InflaterInputStream(InputStream in, Inflater inflater, int size) throws IOException {
this(in, inflater, size, true);
}
InflaterInputStream(InputStream in, Inflater inflater, int size, boolean close_in)
throws IOException {
super(in);
if (in == null || inflater == null) {
throw new NullPointerException();
} else if (size <= 0) {
throw new IllegalArgumentException("buffer size must be greater than 0");
}
this.inflater = inflater;
buf = new byte[size];
this.close_in = close_in;
}
protected boolean myinflater = false;
private byte[] byte1 = new byte[1];
@Override
public int read() throws IOException {
if (closed) {
throw new IOException("Stream closed");
}
return read(byte1, 0, 1) == -1 ? -1 : byte1[0] & 0xff;
}
@Override
public int read(byte[] b, int off, int len) throws IOException {
if (closed) {
throw new IOException("Stream closed");
}
if (b == null) {
throw new NullPointerException();
} else if (off < 0 || len < 0 || len > b.length - off) {
throw new IndexOutOfBoundsException();
} else if (len == 0) {
return 0;
} else if (eof) {
return -1;
}
int n = 0;
inflater.setOutput(b, off, len);
while (!eof) {
if (inflater.avail_in == 0)
fill();
int err = inflater.inflate(JZlib.Z_NO_FLUSH);
n += inflater.next_out_index - off;
off = inflater.next_out_index;
switch (err) {
case JZlib.Z_DATA_ERROR:
throw new IOException(inflater.msg);
case JZlib.Z_STREAM_END:
case JZlib.Z_NEED_DICT:
eof = true;
if (err == JZlib.Z_NEED_DICT)
return -1;
break;
default:
}
if (inflater.avail_out == 0)
break;
}
return n;
}
@Override
public int available() throws IOException {
if (closed) {
throw new IOException("Stream closed");
}
if (eof) {
return 0;
} else {
return 1;
}
}
private byte[] b = new byte[512];
@Override
public long skip(long n) throws IOException {
if (n < 0) {
throw new IllegalArgumentException("negative skip length");
}
if (closed) {
throw new IOException("Stream closed");
}
int max = (int) Math.min(n, Integer.MAX_VALUE);
int total = 0;
while (total < max) {
int len = max - total;
if (len > b.length) {
len = b.length;
}
len = read(b, 0, len);
if (len == -1) {
eof = true;
break;
}
total += len;
}
return total;
}
@Override
public void close() throws IOException {
if (!closed) {
if (myinflater)
inflater.end();
if (close_in)
in.close();
closed = true;
}
}
protected void fill() throws IOException {
if (closed) {
throw new IOException("Stream closed");
}
int len = in.read(buf, 0, buf.length);
if (len == -1) {
if (inflater.istate.wrap == 0 && !inflater.finished()) {
buf[0] = 0;
len = 1;
} else if (inflater.istate.was != -1) { // in reading trailer
throw new IOException("footer is not found");
} else {
throw new EOFException("Unexpected end of ZLIB input stream");
}
}
inflater.setInput(buf, 0, len, true);
}
@Override
public boolean markSupported() {
return false;
}
@Override
public synchronized void mark(int readlimit) {}
@Override
public synchronized void reset() throws IOException {
throw new IOException("mark/reset not supported");
}
long getTotalIn() {
return inflater.getTotalIn();
}
long getTotalOut() {
return inflater.getTotalOut();
}
byte[] getAvailIn() {
if (inflater.avail_in <= 0)
return null;
byte[] tmp = new byte[inflater.avail_in];
System.arraycopy(inflater.next_in, inflater.next_in_index, tmp, 0, inflater.avail_in);
return tmp;
}
void readHeader() throws IOException {
byte[] empty = "".getBytes(StandardCharsets.UTF_8);
inflater.setInput(empty, 0, 0, false);
inflater.setOutput(empty, 0, 0);
int err = inflater.inflate(JZlib.Z_NO_FLUSH);
if (!inflater.istate.inParsingHeader()) {
return;
}
byte[] b1 = new byte[1];
do {
int i = in.read(b1);
if (i <= 0)
throw new IOException("no input");
inflater.setInput(b1);
err = inflater.inflate(JZlib.Z_NO_FLUSH);
if (err != 0 /* Z_OK */)
throw new IOException(inflater.msg);
} while (inflater.istate.inParsingHeader());
}
Inflater getInflater() {
return inflater;
}
}