--- /dev/null
+package org.apache.lucene.store;
+
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+
+/** Base implementation class for buffered {@link IndexInput}. */
+public abstract class BufferedIndexInput extends IndexInput {
+
+ /** Default buffer size */
+ public static final int BUFFER_SIZE = 1024;
+
+ private int bufferSize = BUFFER_SIZE;
+
+ protected byte[] buffer;
+
+ private long bufferStart = 0; // position in file of buffer
+ private int bufferLength = 0; // end of valid bytes
+ private int bufferPosition = 0; // next byte to read
+
+ @Override
+ public byte readByte() throws IOException {
+ if (bufferPosition >= bufferLength)
+ refill();
+ return buffer[bufferPosition++];
+ }
+
+ /** @deprecated please pass resourceDesc */
+ @Deprecated
+ public BufferedIndexInput() {
+ this("anonymous BuffereIndexInput");
+ }
+
+ public BufferedIndexInput(String resourceDesc) {
+ this(resourceDesc, BUFFER_SIZE);
+ }
+
+ /** Inits BufferedIndexInput with a specific bufferSize
+ * @deprecated please pass resourceDesc */
+ @Deprecated
+ public BufferedIndexInput(int bufferSize) {
+ this("anonymous BuffereIndexInput", bufferSize);
+ }
+
+ /** Inits BufferedIndexInput with a specific bufferSize */
+ public BufferedIndexInput(String resourceDesc, int bufferSize) {
+ super(resourceDesc);
+ checkBufferSize(bufferSize);
+ this.bufferSize = bufferSize;
+ }
+
+ /** Change the buffer size used by this IndexInput */
+ public void setBufferSize(int newSize) {
+ assert buffer == null || bufferSize == buffer.length: "buffer=" + buffer + " bufferSize=" + bufferSize + " buffer.length=" + (buffer != null ? buffer.length : 0);
+ if (newSize != bufferSize) {
+ checkBufferSize(newSize);
+ bufferSize = newSize;
+ if (buffer != null) {
+ // Resize the existing buffer and carefully save as
+ // many bytes as possible starting from the current
+ // bufferPosition
+ byte[] newBuffer = new byte[newSize];
+ final int leftInBuffer = bufferLength-bufferPosition;
+ final int numToCopy;
+ if (leftInBuffer > newSize)
+ numToCopy = newSize;
+ else
+ numToCopy = leftInBuffer;
+ System.arraycopy(buffer, bufferPosition, newBuffer, 0, numToCopy);
+ bufferStart += bufferPosition;
+ bufferPosition = 0;
+ bufferLength = numToCopy;
+ newBuffer(newBuffer);
+ }
+ }
+ }
+
+ protected void newBuffer(byte[] newBuffer) {
+ // Subclasses can do something here
+ buffer = newBuffer;
+ }
+
+ /** Returns buffer size. @see #setBufferSize */
+ public int getBufferSize() {
+ return bufferSize;
+ }
+
+ private void checkBufferSize(int bufferSize) {
+ if (bufferSize <= 0)
+ throw new IllegalArgumentException("bufferSize must be greater than 0 (got " + bufferSize + ")");
+ }
+
+ @Override
+ public void readBytes(byte[] b, int offset, int len) throws IOException {
+ readBytes(b, offset, len, true);
+ }
+
+ @Override
+ public void readBytes(byte[] b, int offset, int len, boolean useBuffer) throws IOException {
+
+ if(len <= (bufferLength-bufferPosition)){
+ // the buffer contains enough data to satisfy this request
+ if(len>0) // to allow b to be null if len is 0...
+ System.arraycopy(buffer, bufferPosition, b, offset, len);
+ bufferPosition+=len;
+ } else {
+ // the buffer does not have enough data. First serve all we've got.
+ int available = bufferLength - bufferPosition;
+ if(available > 0){
+ System.arraycopy(buffer, bufferPosition, b, offset, available);
+ offset += available;
+ len -= available;
+ bufferPosition += available;
+ }
+ // and now, read the remaining 'len' bytes:
+ if (useBuffer && len<bufferSize){
+ // If the amount left to read is small enough, and
+ // we are allowed to use our buffer, do it in the usual
+ // buffered way: fill the buffer and copy from it:
+ refill();
+ if(bufferLength<len){
+ // Throw an exception when refill() could not read len bytes:
+ System.arraycopy(buffer, 0, b, offset, bufferLength);
+ throw new IOException("read past EOF");
+ } else {
+ System.arraycopy(buffer, 0, b, offset, len);
+ bufferPosition=len;
+ }
+ } else {
+ // The amount left to read is larger than the buffer
+ // or we've been asked to not use our buffer -
+ // there's no performance reason not to read it all
+ // at once. Note that unlike the previous code of
+ // this function, there is no need to do a seek
+ // here, because there's no need to reread what we
+ // had in the buffer.
+ long after = bufferStart+bufferPosition+len;
+ if(after > length())
+ throw new IOException("read past EOF");
+ readInternal(b, offset, len);
+ bufferStart = after;
+ bufferPosition = 0;
+ bufferLength = 0; // trigger refill() on read
+ }
+ }
+ }
+
+ @Override
+ public int readInt() throws IOException {
+ if (4 <= (bufferLength-bufferPosition)) {
+ return ((buffer[bufferPosition++] & 0xFF) << 24) | ((buffer[bufferPosition++] & 0xFF) << 16)
+ | ((buffer[bufferPosition++] & 0xFF) << 8) | (buffer[bufferPosition++] & 0xFF);
+ } else {
+ return super.readInt();
+ }
+ }
+
+ @Override
+ public long readLong() throws IOException {
+ if (8 <= (bufferLength-bufferPosition)) {
+ final int i1 = ((buffer[bufferPosition++] & 0xff) << 24) | ((buffer[bufferPosition++] & 0xff) << 16) |
+ ((buffer[bufferPosition++] & 0xff) << 8) | (buffer[bufferPosition++] & 0xff);
+ final int i2 = ((buffer[bufferPosition++] & 0xff) << 24) | ((buffer[bufferPosition++] & 0xff) << 16) |
+ ((buffer[bufferPosition++] & 0xff) << 8) | (buffer[bufferPosition++] & 0xff);
+ return (((long)i1) << 32) | (i2 & 0xFFFFFFFFL);
+ } else {
+ return super.readLong();
+ }
+ }
+
+ @Override
+ public int readVInt() throws IOException {
+ if (5 <= (bufferLength-bufferPosition)) {
+ byte b = buffer[bufferPosition++];
+ int i = b & 0x7F;
+ for (int shift = 7; (b & 0x80) != 0; shift += 7) {
+ b = buffer[bufferPosition++];
+ i |= (b & 0x7F) << shift;
+ }
+ return i;
+ } else {
+ return super.readVInt();
+ }
+ }
+
+ @Override
+ public long readVLong() throws IOException {
+ if (9 <= bufferLength-bufferPosition) {
+ byte b = buffer[bufferPosition++];
+ long i = b & 0x7F;
+ for (int shift = 7; (b & 0x80) != 0; shift += 7) {
+ b = buffer[bufferPosition++];
+ i |= (b & 0x7FL) << shift;
+ }
+ return i;
+ } else {
+ return super.readVLong();
+ }
+ }
+
+ private void refill() throws IOException {
+ long start = bufferStart + bufferPosition;
+ long end = start + bufferSize;
+ if (end > length()) // don't read past EOF
+ end = length();
+ int newLength = (int)(end - start);
+ if (newLength <= 0)
+ throw new IOException("read past EOF");
+
+ if (buffer == null) {
+ newBuffer(new byte[bufferSize]); // allocate buffer lazily
+ seekInternal(bufferStart);
+ }
+ readInternal(buffer, 0, newLength);
+ bufferLength = newLength;
+ bufferStart = start;
+ bufferPosition = 0;
+ }
+
+ /** Expert: implements buffer refill. Reads bytes from the current position
+ * in the input.
+ * @param b the array to read bytes into
+ * @param offset the offset in the array to start storing bytes
+ * @param length the number of bytes to read
+ */
+ protected abstract void readInternal(byte[] b, int offset, int length)
+ throws IOException;
+
+ @Override
+ public long getFilePointer() { return bufferStart + bufferPosition; }
+
+ @Override
+ public void seek(long pos) throws IOException {
+ if (pos >= bufferStart && pos < (bufferStart + bufferLength))
+ bufferPosition = (int)(pos - bufferStart); // seek within buffer
+ else {
+ bufferStart = pos;
+ bufferPosition = 0;
+ bufferLength = 0; // trigger refill() on read()
+ seekInternal(pos);
+ }
+ }
+
+ /** Expert: implements seek. Sets current position in this file, where the
+ * next {@link #readInternal(byte[],int,int)} will occur.
+ * @see #readInternal(byte[],int,int)
+ */
+ protected abstract void seekInternal(long pos) throws IOException;
+
+ @Override
+ public Object clone() {
+ BufferedIndexInput clone = (BufferedIndexInput)super.clone();
+
+ clone.buffer = null;
+ clone.bufferLength = 0;
+ clone.bufferPosition = 0;
+ clone.bufferStart = getFilePointer();
+
+ return clone;
+ }
+
+ /**
+ * Flushes the in-memory bufer to the given output, copying at most
+ * <code>numBytes</code>.
+ * <p>
+ * <b>NOTE:</b> this method does not refill the buffer, however it does
+ * advance the buffer position.
+ *
+ * @return the number of bytes actually flushed from the in-memory buffer.
+ */
+ protected int flushBuffer(IndexOutput out, long numBytes) throws IOException {
+ int toCopy = bufferLength - bufferPosition;
+ if (toCopy > numBytes) {
+ toCopy = (int) numBytes;
+ }
+ if (toCopy > 0) {
+ out.writeBytes(buffer, bufferPosition, toCopy);
+ bufferPosition += toCopy;
+ }
+ return toCopy;
+ }
+
+ @Override
+ public void copyBytes(IndexOutput out, long numBytes) throws IOException {
+ assert numBytes >= 0: "numBytes=" + numBytes;
+
+ while (numBytes > 0) {
+ if (bufferLength == bufferPosition) {
+ refill();
+ }
+ numBytes -= flushBuffer(out, numBytes);
+ }
+ }
+
+}