1 package org.apache.lucene.store;
4 * Licensed to the Apache Software Foundation (ASF) under one or more
5 * contributor license agreements. See the NOTICE file distributed with
6 * this work for additional information regarding copyright ownership.
7 * The ASF licenses this file to You under the Apache License, Version 2.0
8 * (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
11 * http://www.apache.org/licenses/LICENSE-2.0
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
20 import java.io.IOException;
22 /** Base implementation class for buffered {@link IndexInput}. */
23 public abstract class BufferedIndexInput extends IndexInput {
25 /** Default buffer size */
26 public static final int BUFFER_SIZE = 1024;
28 private int bufferSize = BUFFER_SIZE;
30 protected byte[] buffer;
32 private long bufferStart = 0; // position in file of buffer
33 private int bufferLength = 0; // end of valid bytes
34 private int bufferPosition = 0; // next byte to read
37 public byte readByte() throws IOException {
38 if (bufferPosition >= bufferLength)
40 return buffer[bufferPosition++];
43 public BufferedIndexInput() {}
45 /** Inits BufferedIndexInput with a specific bufferSize */
46 public BufferedIndexInput(int bufferSize) {
47 checkBufferSize(bufferSize);
48 this.bufferSize = bufferSize;
51 /** Change the buffer size used by this IndexInput */
52 public void setBufferSize(int newSize) {
53 assert buffer == null || bufferSize == buffer.length: "buffer=" + buffer + " bufferSize=" + bufferSize + " buffer.length=" + (buffer != null ? buffer.length : 0);
54 if (newSize != bufferSize) {
55 checkBufferSize(newSize);
58 // Resize the existing buffer and carefully save as
59 // many bytes as possible starting from the current
61 byte[] newBuffer = new byte[newSize];
62 final int leftInBuffer = bufferLength-bufferPosition;
64 if (leftInBuffer > newSize)
67 numToCopy = leftInBuffer;
68 System.arraycopy(buffer, bufferPosition, newBuffer, 0, numToCopy);
69 bufferStart += bufferPosition;
71 bufferLength = numToCopy;
77 protected void newBuffer(byte[] newBuffer) {
78 // Subclasses can do something here
82 /** Returns buffer size. @see #setBufferSize */
83 public int getBufferSize() {
87 private void checkBufferSize(int bufferSize) {
89 throw new IllegalArgumentException("bufferSize must be greater than 0 (got " + bufferSize + ")");
93 public void readBytes(byte[] b, int offset, int len) throws IOException {
94 readBytes(b, offset, len, true);
98 public void readBytes(byte[] b, int offset, int len, boolean useBuffer) throws IOException {
100 if(len <= (bufferLength-bufferPosition)){
101 // the buffer contains enough data to satisfy this request
102 if(len>0) // to allow b to be null if len is 0...
103 System.arraycopy(buffer, bufferPosition, b, offset, len);
106 // the buffer does not have enough data. First serve all we've got.
107 int available = bufferLength - bufferPosition;
109 System.arraycopy(buffer, bufferPosition, b, offset, available);
112 bufferPosition += available;
114 // and now, read the remaining 'len' bytes:
115 if (useBuffer && len<bufferSize){
116 // If the amount left to read is small enough, and
117 // we are allowed to use our buffer, do it in the usual
118 // buffered way: fill the buffer and copy from it:
120 if(bufferLength<len){
121 // Throw an exception when refill() could not read len bytes:
122 System.arraycopy(buffer, 0, b, offset, bufferLength);
123 throw new IOException("read past EOF");
125 System.arraycopy(buffer, 0, b, offset, len);
129 // The amount left to read is larger than the buffer
130 // or we've been asked to not use our buffer -
131 // there's no performance reason not to read it all
132 // at once. Note that unlike the previous code of
133 // this function, there is no need to do a seek
134 // here, because there's no need to reread what we
135 // had in the buffer.
136 long after = bufferStart+bufferPosition+len;
138 throw new IOException("read past EOF");
139 readInternal(b, offset, len);
142 bufferLength = 0; // trigger refill() on read
148 public int readInt() throws IOException {
149 if (4 <= (bufferLength-bufferPosition)) {
150 return ((buffer[bufferPosition++] & 0xFF) << 24) | ((buffer[bufferPosition++] & 0xFF) << 16)
151 | ((buffer[bufferPosition++] & 0xFF) << 8) | (buffer[bufferPosition++] & 0xFF);
153 return super.readInt();
158 public long readLong() throws IOException {
159 if (8 <= (bufferLength-bufferPosition)) {
160 final int i1 = ((buffer[bufferPosition++] & 0xff) << 24) | ((buffer[bufferPosition++] & 0xff) << 16) |
161 ((buffer[bufferPosition++] & 0xff) << 8) | (buffer[bufferPosition++] & 0xff);
162 final int i2 = ((buffer[bufferPosition++] & 0xff) << 24) | ((buffer[bufferPosition++] & 0xff) << 16) |
163 ((buffer[bufferPosition++] & 0xff) << 8) | (buffer[bufferPosition++] & 0xff);
164 return (((long)i1) << 32) | (i2 & 0xFFFFFFFFL);
166 return super.readLong();
171 public int readVInt() throws IOException {
172 if (5 <= (bufferLength-bufferPosition)) {
173 byte b = buffer[bufferPosition++];
175 for (int shift = 7; (b & 0x80) != 0; shift += 7) {
176 b = buffer[bufferPosition++];
177 i |= (b & 0x7F) << shift;
181 return super.readVInt();
186 public long readVLong() throws IOException {
187 if (9 <= bufferLength-bufferPosition) {
188 byte b = buffer[bufferPosition++];
190 for (int shift = 7; (b & 0x80) != 0; shift += 7) {
191 b = buffer[bufferPosition++];
192 i |= (b & 0x7FL) << shift;
196 return super.readVLong();
200 private void refill() throws IOException {
201 long start = bufferStart + bufferPosition;
202 long end = start + bufferSize;
203 if (end > length()) // don't read past EOF
205 int newLength = (int)(end - start);
207 throw new IOException("read past EOF");
209 if (buffer == null) {
210 newBuffer(new byte[bufferSize]); // allocate buffer lazily
211 seekInternal(bufferStart);
213 readInternal(buffer, 0, newLength);
214 bufferLength = newLength;
219 /** Expert: implements buffer refill. Reads bytes from the current position
221 * @param b the array to read bytes into
222 * @param offset the offset in the array to start storing bytes
223 * @param length the number of bytes to read
225 protected abstract void readInternal(byte[] b, int offset, int length)
229 public long getFilePointer() { return bufferStart + bufferPosition; }
232 public void seek(long pos) throws IOException {
233 if (pos >= bufferStart && pos < (bufferStart + bufferLength))
234 bufferPosition = (int)(pos - bufferStart); // seek within buffer
238 bufferLength = 0; // trigger refill() on read()
243 /** Expert: implements seek. Sets current position in this file, where the
244 * next {@link #readInternal(byte[],int,int)} will occur.
245 * @see #readInternal(byte[],int,int)
247 protected abstract void seekInternal(long pos) throws IOException;
250 public Object clone() {
251 BufferedIndexInput clone = (BufferedIndexInput)super.clone();
254 clone.bufferLength = 0;
255 clone.bufferPosition = 0;
256 clone.bufferStart = getFilePointer();
262 * Flushes the in-memory bufer to the given output, copying at most
263 * <code>numBytes</code>.
265 * <b>NOTE:</b> this method does not refill the buffer, however it does
266 * advance the buffer position.
268 * @return the number of bytes actually flushed from the in-memory buffer.
270 protected int flushBuffer(IndexOutput out, long numBytes) throws IOException {
271 int toCopy = bufferLength - bufferPosition;
272 if (toCopy > numBytes) {
273 toCopy = (int) numBytes;
276 out.writeBytes(buffer, bufferPosition, toCopy);
277 bufferPosition += toCopy;
283 public void copyBytes(IndexOutput out, long numBytes) throws IOException {
284 assert numBytes >= 0: "numBytes=" + numBytes;
286 while (numBytes > 0) {
287 if (bufferLength == bufferPosition) {
290 numBytes -= flushBuffer(out, numBytes);