package yuku.alkitab.io;
import java.io.EOFException;
import java.io.IOException;
import java.io.InputStream;
import java.util.zip.GZIPInputStream;
/**
* Works like GzipInputStream, but if the source is not
* detected as gzip (header 1F 8B) then it is streamed through as is.
*/
public class OptionalGzipInputStream extends InputStream {
InputStream wrapper;
static class PeekInputStream extends InputStream {
private final InputStream source;
int pos = 0; // current read position. 2 means 2 or more
int len = -1; // -1 unknown, 0 zero bytes, 1 one byte
int firstByte;
int secondByte;
PeekInputStream(InputStream source) throws IOException {
this.source = source;
// immediately read 2 bytes
firstByte = source.read();
secondByte = source.read();
if (firstByte == -1) len = 0;
else if (secondByte == -1) len = 1;
}
public int getFirstByte() {
return firstByte;
}
public int getSecondByte() {
return secondByte;
}
@Override
public int read() throws IOException {
if (len == 0) {
return -1;
} else if (len == 1) {
if (pos == 0) {
pos++;
return firstByte;
} else {
return -1;
}
} else { // unknown length
if (pos == 0) {
pos++;
return firstByte;
} else if (pos == 1) {
pos++;
return secondByte;
} else {
pos++;
return source.read();
}
}
}
@Override
public int read(final byte[] buffer) throws IOException {
return read(buffer, 0, buffer.length);
}
@Override
public int read(final byte[] buffer, final int byteOffset, final int byteCount) throws IOException {
if (byteCount == 0) {
return 0;
}
if (byteCount == 1) {
final int b = read();
if (b == -1) {
return -1;
} else {
buffer[byteOffset] = (byte) b;
return 1;
}
}
if (len == 0) {
return -1;
}
if (len == 1 && pos == 0) {
final int b = read();
buffer[byteOffset] = (byte) b;
return 1;
} else if (len == 1) {
return -1;
}
// byteCount is 2 or more, length of source is 2 or more
int remainingToRead;
int newOffset;
if (pos == 0) {
buffer[byteOffset] = (byte) firstByte;
buffer[byteOffset + 1] = (byte) secondByte;
pos = 2;
newOffset = byteOffset + 2;
remainingToRead = byteCount - 2;
} else if (pos == 1) {
buffer[byteOffset] = (byte) secondByte;
pos = 2;
newOffset = byteOffset + 1;
remainingToRead = byteCount - 1;
} else {
newOffset = byteOffset;
remainingToRead = byteCount;
}
final int read = source.read(buffer, newOffset, remainingToRead);
if (read == -1) {
final int res = newOffset - byteOffset;
if (res == 0) {
// about to return 0, but source says we are EOF. Return -1 instead
return -1;
}
return res;
}
return read + newOffset - byteOffset;
}
@Override
public void close() throws IOException {
source.close();
}
}
public OptionalGzipInputStream(InputStream source) throws IOException {
final PeekInputStream peek = new PeekInputStream(source);
final int first = peek.getFirstByte();
final int second = peek.getSecondByte();
if (first == 0x1f && second == 0x8b) {
try {
wrapper = new GZIPInputStream(peek);
} catch (EOFException e) { // error reading gzip header, so this is not gzip
wrapper = peek;
}
} else {
wrapper = peek;
}
}
@Override
public int read() throws IOException {
return wrapper.read();
}
@Override
public int read(final byte[] buffer) throws IOException {
return wrapper.read(buffer);
}
@Override
public int read(final byte[] buffer, final int byteOffset, final int byteCount) throws IOException {
return wrapper.read(buffer, byteOffset, byteCount);
}
@Override
public void close() throws IOException {
wrapper.close();
}
}