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 /** @deprecated please pass resourceDesc */
45 public BufferedIndexInput() {
46 this("anonymous BuffereIndexInput");
49 public BufferedIndexInput(String resourceDesc) {
50 this(resourceDesc, BUFFER_SIZE);
53 /** Inits BufferedIndexInput with a specific bufferSize
54 * @deprecated please pass resourceDesc */
56 public BufferedIndexInput(int bufferSize) {
57 this("anonymous BuffereIndexInput", bufferSize);
60 /** Inits BufferedIndexInput with a specific bufferSize */
61 public BufferedIndexInput(String resourceDesc, int bufferSize) {
63 checkBufferSize(bufferSize);
64 this.bufferSize = bufferSize;
67 /** Change the buffer size used by this IndexInput */
68 public void setBufferSize(int newSize) {
69 assert buffer == null || bufferSize == buffer.length: "buffer=" + buffer + " bufferSize=" + bufferSize + " buffer.length=" + (buffer != null ? buffer.length : 0);
70 if (newSize != bufferSize) {
71 checkBufferSize(newSize);
74 // Resize the existing buffer and carefully save as
75 // many bytes as possible starting from the current
77 byte[] newBuffer = new byte[newSize];
78 final int leftInBuffer = bufferLength-bufferPosition;
80 if (leftInBuffer > newSize)
83 numToCopy = leftInBuffer;
84 System.arraycopy(buffer, bufferPosition, newBuffer, 0, numToCopy);
85 bufferStart += bufferPosition;
87 bufferLength = numToCopy;
93 protected void newBuffer(byte[] newBuffer) {
94 // Subclasses can do something here
98 /** Returns buffer size. @see #setBufferSize */
99 public int getBufferSize() {
103 private void checkBufferSize(int bufferSize) {
105 throw new IllegalArgumentException("bufferSize must be greater than 0 (got " + bufferSize + ")");
109 public void readBytes(byte[] b, int offset, int len) throws IOException {
110 readBytes(b, offset, len, true);
114 public void readBytes(byte[] b, int offset, int len, boolean useBuffer) throws IOException {
116 if(len <= (bufferLength-bufferPosition)){
117 // the buffer contains enough data to satisfy this request
118 if(len>0) // to allow b to be null if len is 0...
119 System.arraycopy(buffer, bufferPosition, b, offset, len);
122 // the buffer does not have enough data. First serve all we've got.
123 int available = bufferLength - bufferPosition;
125 System.arraycopy(buffer, bufferPosition, b, offset, available);
128 bufferPosition += available;
130 // and now, read the remaining 'len' bytes:
131 if (useBuffer && len<bufferSize){
132 // If the amount left to read is small enough, and
133 // we are allowed to use our buffer, do it in the usual
134 // buffered way: fill the buffer and copy from it:
136 if(bufferLength<len){
137 // Throw an exception when refill() could not read len bytes:
138 System.arraycopy(buffer, 0, b, offset, bufferLength);
139 throw new IOException("read past EOF");
141 System.arraycopy(buffer, 0, b, offset, len);
145 // The amount left to read is larger than the buffer
146 // or we've been asked to not use our buffer -
147 // there's no performance reason not to read it all
148 // at once. Note that unlike the previous code of
149 // this function, there is no need to do a seek
150 // here, because there's no need to reread what we
151 // had in the buffer.
152 long after = bufferStart+bufferPosition+len;
154 throw new IOException("read past EOF");
155 readInternal(b, offset, len);
158 bufferLength = 0; // trigger refill() on read
164 public int readInt() throws IOException {
165 if (4 <= (bufferLength-bufferPosition)) {
166 return ((buffer[bufferPosition++] & 0xFF) << 24) | ((buffer[bufferPosition++] & 0xFF) << 16)
167 | ((buffer[bufferPosition++] & 0xFF) << 8) | (buffer[bufferPosition++] & 0xFF);
169 return super.readInt();
174 public long readLong() throws IOException {
175 if (8 <= (bufferLength-bufferPosition)) {
176 final int i1 = ((buffer[bufferPosition++] & 0xff) << 24) | ((buffer[bufferPosition++] & 0xff) << 16) |
177 ((buffer[bufferPosition++] & 0xff) << 8) | (buffer[bufferPosition++] & 0xff);
178 final int i2 = ((buffer[bufferPosition++] & 0xff) << 24) | ((buffer[bufferPosition++] & 0xff) << 16) |
179 ((buffer[bufferPosition++] & 0xff) << 8) | (buffer[bufferPosition++] & 0xff);
180 return (((long)i1) << 32) | (i2 & 0xFFFFFFFFL);
182 return super.readLong();
187 public int readVInt() throws IOException {
188 if (5 <= (bufferLength-bufferPosition)) {
189 byte b = buffer[bufferPosition++];
191 for (int shift = 7; (b & 0x80) != 0; shift += 7) {
192 b = buffer[bufferPosition++];
193 i |= (b & 0x7F) << shift;
197 return super.readVInt();
202 public long readVLong() throws IOException {
203 if (9 <= bufferLength-bufferPosition) {
204 byte b = buffer[bufferPosition++];
206 for (int shift = 7; (b & 0x80) != 0; shift += 7) {
207 b = buffer[bufferPosition++];
208 i |= (b & 0x7FL) << shift;
212 return super.readVLong();
216 private void refill() throws IOException {
217 long start = bufferStart + bufferPosition;
218 long end = start + bufferSize;
219 if (end > length()) // don't read past EOF
221 int newLength = (int)(end - start);
223 throw new IOException("read past EOF");
225 if (buffer == null) {
226 newBuffer(new byte[bufferSize]); // allocate buffer lazily
227 seekInternal(bufferStart);
229 readInternal(buffer, 0, newLength);
230 bufferLength = newLength;
235 /** Expert: implements buffer refill. Reads bytes from the current position
237 * @param b the array to read bytes into
238 * @param offset the offset in the array to start storing bytes
239 * @param length the number of bytes to read
241 protected abstract void readInternal(byte[] b, int offset, int length)
245 public long getFilePointer() { return bufferStart + bufferPosition; }
248 public void seek(long pos) throws IOException {
249 if (pos >= bufferStart && pos < (bufferStart + bufferLength))
250 bufferPosition = (int)(pos - bufferStart); // seek within buffer
254 bufferLength = 0; // trigger refill() on read()
259 /** Expert: implements seek. Sets current position in this file, where the
260 * next {@link #readInternal(byte[],int,int)} will occur.
261 * @see #readInternal(byte[],int,int)
263 protected abstract void seekInternal(long pos) throws IOException;
266 public Object clone() {
267 BufferedIndexInput clone = (BufferedIndexInput)super.clone();
270 clone.bufferLength = 0;
271 clone.bufferPosition = 0;
272 clone.bufferStart = getFilePointer();
278 * Flushes the in-memory bufer to the given output, copying at most
279 * <code>numBytes</code>.
281 * <b>NOTE:</b> this method does not refill the buffer, however it does
282 * advance the buffer position.
284 * @return the number of bytes actually flushed from the in-memory buffer.
286 protected int flushBuffer(IndexOutput out, long numBytes) throws IOException {
287 int toCopy = bufferLength - bufferPosition;
288 if (toCopy > numBytes) {
289 toCopy = (int) numBytes;
292 out.writeBytes(buffer, bufferPosition, toCopy);
293 bufferPosition += toCopy;
299 public void copyBytes(IndexOutput out, long numBytes) throws IOException {
300 assert numBytes >= 0: "numBytes=" + numBytes;
302 while (numBytes > 0) {
303 if (bufferLength == bufferPosition) {
306 numBytes -= flushBuffer(out, numBytes);