package FlexibleEncoding.ORC;
/**
adapted from ORC
@author wangmeng
*/
import java.io.IOException;
import java.io.InputStream;
import java.nio.ByteBuffer;
public abstract class InStream extends InputStream {
private static class UncompressedStream extends InStream {
private final String name;
private byte[] array;
private int offset;
private final int base;
private final int limit;
public UncompressedStream(String name, ByteBuffer input) {
this.name = name;
this.array = input.array();
base = input.arrayOffset() + input.position();
offset = base;
limit = input.arrayOffset() + input.limit();
}
@Override
public int read() {
if (offset == limit) {
return -1;
}
return 0xff & array[offset++];
}
@Override
public int read(byte[] data, int offset, int length) {
if (this.offset == limit) {
return -1;
}
int actualLength = Math.min(length, limit - this.offset);
System.arraycopy(array, this.offset, data, offset, actualLength);
this.offset += actualLength;
return actualLength;
}
@Override
public int available() {
return limit - offset;
}
@Override
public void close() {
array = null;
offset = 0;
}
@Override
public void seek(PositionProvider index) throws IOException {
offset = base + (int) index.getNext();
}
@Override
public String toString() {
return "uncompressed stream " + name + " base: " + base +
" offset: " + offset + " limit: " + limit;
}
}
private static class CompressedStream extends InStream {
private final String name;
private byte[] array;
private final int bufferSize;
private ByteBuffer uncompressed = null;
private final CompressionCodec codec;
private int offset;
private final int base;
private final int limit;
private boolean isUncompressedOriginal;
public CompressedStream(String name, ByteBuffer input,
CompressionCodec codec, int bufferSize
) {
this.array = input.array();
this.name = name;
this.codec = codec;
this.bufferSize = bufferSize;
base = input.arrayOffset() + input.position();
offset = base;
limit = input.arrayOffset() + input.limit();
}
private void readHeader() throws IOException {
if (limit - offset > OutStream.HEADER_SIZE) {
int chunkLength = ((0xff & array[offset + 2]) << 15) |
((0xff & array[offset + 1]) << 7) | ((0xff & array[offset]) >> 1);
if (chunkLength > bufferSize) {
throw new IllegalArgumentException("Buffer size too small. size = " +
bufferSize + " needed = " + chunkLength);
}
boolean isOriginal = (array[offset] & 0x01) == 1;
offset += OutStream.HEADER_SIZE;
if (isOriginal) {
isUncompressedOriginal = true;
uncompressed = ByteBuffer.wrap(array, offset, chunkLength);
} else {
if (isUncompressedOriginal) {
uncompressed = ByteBuffer.allocate(bufferSize);
isUncompressedOriginal = false;
} else if (uncompressed == null) {
uncompressed = ByteBuffer.allocate(bufferSize);
} else {
uncompressed.clear();
}
codec.decompress(ByteBuffer.wrap(array, offset, chunkLength),
uncompressed);
}
offset += chunkLength;
} else {
throw new IllegalStateException("Can't read header");
}
}
@Override
public int read() throws IOException {
if (uncompressed == null || uncompressed.remaining() == 0) {
if (offset == limit) {
return -1;
}
readHeader();
}
return 0xff & uncompressed.get();
}
@Override
public int read(byte[] data, int offset, int length) throws IOException {
if (uncompressed == null || uncompressed.remaining() == 0) {
if (this.offset == this.limit) {
return -1;
}
readHeader();
}
int actualLength = Math.min(length, uncompressed.remaining());
System.arraycopy(uncompressed.array(),
uncompressed.arrayOffset() + uncompressed.position(), data,
offset, actualLength);
uncompressed.position(uncompressed.position() + actualLength);
return actualLength;
}
@Override
public int available() throws IOException {
if (uncompressed == null || uncompressed.remaining() == 0) {
if (offset == limit) {
return 0;
}
readHeader();
}
return uncompressed.remaining();
}
@Override
public void close() {
array = null;
uncompressed = null;
offset = 0;
}
@Override
public void seek(PositionProvider index) throws IOException {
offset = base + (int) index.getNext();
int uncompBytes = (int) index.getNext();
if (uncompBytes != 0) {
readHeader();
uncompressed.position(uncompressed.position() + uncompBytes);
} else if (uncompressed != null) {
uncompressed.position(uncompressed.limit());
}
}
@Override
public String toString() {
return "compressed stream " + name + " base: " + base +
" offset: " + offset + " limit: " + limit +
(uncompressed == null ? "" :
" uncompressed: " + uncompressed.position() + " to " +
uncompressed.limit());
}
}
public abstract void seek(PositionProvider index) throws IOException;
public static InStream create(String name,
ByteBuffer input,
CompressionCodec codec,
int bufferSize) throws IOException {
if (codec == null) {
return new UncompressedStream(name, input);
} else {
return new CompressedStream(name, input, codec, bufferSize);
}
}
}