package org.apache.lucene.store;
import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public abstract class BufferedIndexInput extends IndexInput implements RandomAccessInput {
private static final ByteBuffer EMPTY_BYTEBUFFER = ByteBuffer.allocate(0);
public static final int BUFFER_SIZE = 1024;
public static final int MIN_BUFFER_SIZE = 8;
public static final int MERGE_BUFFER_SIZE = 4096;
private int bufferSize = BUFFER_SIZE;
private ByteBuffer buffer = EMPTY_BYTEBUFFER;
private long bufferStart = 0;
@Override
public final byte readByte() throws IOException {
if (buffer.hasRemaining() == false) {
refill();
}
return buffer.get();
}
public BufferedIndexInput(String resourceDesc) {
this(resourceDesc, BUFFER_SIZE);
}
public BufferedIndexInput(String resourceDesc, IOContext context) {
this(resourceDesc, bufferSize(context));
}
public BufferedIndexInput(String resourceDesc, int bufferSize) {
super(resourceDesc);
checkBufferSize(bufferSize);
this.bufferSize = bufferSize;
}
public final void setBufferSize(int newSize) {
assert buffer == EMPTY_BYTEBUFFER || bufferSize == buffer.capacity(): "buffer=" + buffer + " bufferSize=" + bufferSize + " buffer.length=" + (buffer != null ? buffer.capacity() : 0);
if (newSize != bufferSize) {
checkBufferSize(newSize);
bufferSize = newSize;
if (buffer != EMPTY_BYTEBUFFER) {
ByteBuffer newBuffer = ByteBuffer.allocate(newSize);
assert newBuffer.order() == ByteOrder.BIG_ENDIAN;
if (buffer.remaining() > newBuffer.capacity()) {
buffer.limit(buffer.position() + newBuffer.capacity());
}
assert buffer.remaining() <= newBuffer.capacity();
newBuffer.put(buffer);
newBuffer.flip();
buffer = newBuffer;
}
}
}
public final int getBufferSize() {
return bufferSize;
}
private void checkBufferSize(int bufferSize) {
if (bufferSize < MIN_BUFFER_SIZE)
throw new IllegalArgumentException("bufferSize must be at least MIN_BUFFER_SIZE (got " + bufferSize + ")");
}
@Override
public final void readBytes(byte[] b, int offset, int len) throws IOException {
readBytes(b, offset, len, true);
}
@Override
public final void readBytes(byte[] b, int offset, int len, boolean useBuffer) throws IOException {
int available = buffer.remaining();
if(len <= available){
if(len>0)
buffer.get(b, offset, len);
} else {
if(available > 0){
buffer.get(b, offset, available);
offset += available;
len -= available;
}
if (useBuffer && len<bufferSize){
refill();
if(buffer.remaining()<len){
buffer.get(b, offset, buffer.remaining());
throw new EOFException("read past EOF: " + this);
} else {
buffer.get(b, offset, len);
}
} else {
long after = bufferStart+buffer.position()+len;
if(after > length())
throw new EOFException("read past EOF: " + this);
readInternal(ByteBuffer.wrap(b, offset, len));
bufferStart = after;
buffer.limit(0);
}
}
}
@Override
public final short readShort() throws IOException {
if (Short.BYTES <= buffer.remaining()) {
return buffer.getShort();
} else {
return super.readShort();
}
}
@Override
public final int readInt() throws IOException {
if (Integer.BYTES <= buffer.remaining()) {
return buffer.getInt();
} else {
return super.readInt();
}
}
@Override
public final long readLong() throws IOException {
if (Long.BYTES <= buffer.remaining()) {
return buffer.getLong();
} else {
return super.readLong();
}
}
@Override
public final int readVInt() throws IOException {
if (5 <= buffer.remaining()) {
byte b = buffer.get();
if (b >= 0) return b;
int i = b & 0x7F;
b = buffer.get();
i |= (b & 0x7F) << 7;
if (b >= 0) return i;
b = buffer.get();
i |= (b & 0x7F) << 14;
if (b >= 0) return i;
b = buffer.get();
i |= (b & 0x7F) << 21;
if (b >= 0) return i;
b = buffer.get();
i |= (b & 0x0F) << 28;
if ((b & 0xF0) == 0) return i;
throw new IOException("Invalid vInt detected (too many bits)");
} else {
return super.readVInt();
}
}
@Override
public final long readVLong() throws IOException {
if (9 <= buffer.remaining()) {
byte b = buffer.get();
if (b >= 0) return b;
long i = b & 0x7FL;
b = buffer.get();
i |= (b & 0x7FL) << 7;
if (b >= 0) return i;
b = buffer.get();
i |= (b & 0x7FL) << 14;
if (b >= 0) return i;
b = buffer.get();
i |= (b & 0x7FL) << 21;
if (b >= 0) return i;
b = buffer.get();
i |= (b & 0x7FL) << 28;
if (b >= 0) return i;
b = buffer.get();
i |= (b & 0x7FL) << 35;
if (b >= 0) return i;
b = buffer.get();
i |= (b & 0x7FL) << 42;
if (b >= 0) return i;
b = buffer.get();
i |= (b & 0x7FL) << 49;
if (b >= 0) return i;
b = buffer.get();
i |= (b & 0x7FL) << 56;
if (b >= 0) return i;
throw new IOException("Invalid vLong detected (negative values disallowed)");
} else {
return super.readVLong();
}
}
@Override
public final byte readByte(long pos) throws IOException {
long index = pos - bufferStart;
if (index < 0 || index >= buffer.limit()) {
bufferStart = pos;
buffer.limit(0);
seekInternal(pos);
refill();
index = 0;
}
return buffer.get((int) index);
}
@Override
public final short readShort(long pos) throws IOException {
long index = pos - bufferStart;
if (index < 0 || index >= buffer.limit()-1) {
bufferStart = pos;
buffer.limit(0);
seekInternal(pos);
refill();
index = 0;
}
return buffer.getShort((int) index);
}
@Override
public final int readInt(long pos) throws IOException {
long index = pos - bufferStart;
if (index < 0 || index >= buffer.limit()-3) {
bufferStart = pos;
buffer.limit(0);
seekInternal(pos);
refill();
index = 0;
}
return buffer.getInt((int) index);
}
@Override
public final long readLong(long pos) throws IOException {
long index = pos - bufferStart;
if (index < 0 || index >= buffer.limit()-7) {
bufferStart = pos;
buffer.limit(0);
seekInternal(pos);
refill();
index = 0;
}
return buffer.getLong((int) index);
}
private void refill() throws IOException {
long start = bufferStart + buffer.position();
long end = start + bufferSize;
if (end > length())
end = length();
int newLength = (int)(end - start);
if (newLength <= 0)
throw new EOFException("read past EOF: " + this);
if (buffer == EMPTY_BYTEBUFFER) {
buffer = ByteBuffer.allocate(bufferSize);
seekInternal(bufferStart);
}
buffer.position(0);
buffer.limit(newLength);
bufferStart = start;
readInternal(buffer);
assert buffer.order() == ByteOrder.BIG_ENDIAN : buffer.order();
assert buffer.remaining() == 0 : "should have thrown EOFException";
assert buffer.position() == newLength;
buffer.flip();
}
protected abstract void readInternal(ByteBuffer b) throws IOException;
@Override
public final long getFilePointer() { return bufferStart + buffer.position(); }
@Override
public final void seek(long pos) throws IOException {
if (pos >= bufferStart && pos < (bufferStart + buffer.limit()))
buffer.position((int)(pos - bufferStart));
else {
bufferStart = pos;
buffer.limit(0);
seekInternal(pos);
}
}
protected abstract void seekInternal(long pos) throws IOException;
@Override
public BufferedIndexInput clone() {
BufferedIndexInput clone = (BufferedIndexInput)super.clone();
clone.buffer = EMPTY_BYTEBUFFER;
clone.bufferStart = getFilePointer();
return clone;
}
@Override
public IndexInput slice(String sliceDescription, long offset, long length) throws IOException {
return wrap(sliceDescription, this, offset, length);
}
public static int bufferSize(IOContext context) {
switch (context.context) {
case MERGE:
return MERGE_BUFFER_SIZE;
default:
return BUFFER_SIZE;
}
}
public static BufferedIndexInput wrap(String sliceDescription, IndexInput other, long offset, long length) {
return new SlicedIndexInput(sliceDescription, other, offset, length);
}
private static final class SlicedIndexInput extends BufferedIndexInput {
IndexInput base;
long fileOffset;
long length;
SlicedIndexInput(String sliceDescription, IndexInput base, long offset, long length) {
super((sliceDescription == null) ? base.toString() : (base.toString() + " [slice=" + sliceDescription + "]"), BufferedIndexInput.BUFFER_SIZE);
if (offset < 0 || length < 0 || offset + length > base.length()) {
throw new IllegalArgumentException("slice() " + sliceDescription + " out of bounds: " + base);
}
this.base = base.clone();
this.fileOffset = offset;
this.length = length;
}
@Override
public SlicedIndexInput clone() {
SlicedIndexInput clone = (SlicedIndexInput)super.clone();
clone.base = base.clone();
clone.fileOffset = fileOffset;
clone.length = length;
return clone;
}
@Override
protected void readInternal(ByteBuffer b) throws IOException {
long start = getFilePointer();
if (start + b.remaining() > length) {
throw new EOFException("read past EOF: " + this);
}
base.seek(fileOffset + start);
base.readBytes(b.array(), b.position(), b.remaining());
b.position(b.position() + b.remaining());
}
@Override
protected void seekInternal(long pos) {}
@Override
public void close() throws IOException {
base.close();
}
@Override
public long length() {
return length;
}
}
}