1 package org.apache.lucene.store;
2
3 /**
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
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
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.
18 */
19
20 import java.io.IOException;
21
22 /** Base implementation class for buffered {@link IndexInput}. */
23 public abstract class BufferedIndexInput extends IndexInput {
24
25 /** Default buffer size */
26 public static final int BUFFER_SIZE = 1024;
27
28 private int bufferSize = BUFFER_SIZE;
29
30 protected byte[] buffer;
31
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
35
36 public byte readByte() throws IOException {
37 if (bufferPosition >= bufferLength)
38 refill();
39 return buffer[bufferPosition++];
40 }
41
42 public BufferedIndexInput() {}
43
44 /** Inits BufferedIndexInput with a specific bufferSize */
45 public BufferedIndexInput(int bufferSize) {
46 checkBufferSize(bufferSize);
47 this.bufferSize = bufferSize;
48 }
49
50 /** Change the buffer size used by this IndexInput */
51 public void setBufferSize(int newSize) {
52 assert buffer == null || bufferSize == buffer.length: "buffer=" + buffer + " bufferSize=" + bufferSize + " buffer.length=" + (buffer != null ? buffer.length : 0);
53 if (newSize != bufferSize) {
54 checkBufferSize(newSize);
55 bufferSize = newSize;
56 if (buffer != null) {
57 // Resize the existing buffer and carefully save as
58 // many bytes as possible starting from the current
59 // bufferPosition
60 byte[] newBuffer = new byte[newSize];
61 final int leftInBuffer = bufferLength-bufferPosition;
62 final int numToCopy;
63 if (leftInBuffer > newSize)
64 numToCopy = newSize;
65 else
66 numToCopy = leftInBuffer;
67 System.arraycopy(buffer, bufferPosition, newBuffer, 0, numToCopy);
68 bufferStart += bufferPosition;
69 bufferPosition = 0;
70 bufferLength = numToCopy;
71 newBuffer(newBuffer);
72 }
73 }
74 }
75
76 protected void newBuffer(byte[] newBuffer) {
77 // Subclasses can do something here
78 buffer = newBuffer;
79 }
80
81 /** Returns buffer size. @see #setBufferSize */
82 public int getBufferSize() {
83 return bufferSize;
84 }
85
86 private void checkBufferSize(int bufferSize) {
87 if (bufferSize <= 0)
88 throw new IllegalArgumentException("bufferSize must be greater than 0 (got " + bufferSize + ")");
89 }
90
91 public void readBytes(byte[] b, int offset, int len) throws IOException {
92 readBytes(b, offset, len, true);
93 }
94
95 public void readBytes(byte[] b, int offset, int len, boolean useBuffer) throws IOException {
96
97 if(len <= (bufferLength-bufferPosition)){
98 // the buffer contains enough data to satisfy this request
99 if(len>0) // to allow b to be null if len is 0...
100 System.arraycopy(buffer, bufferPosition, b, offset, len);
101 bufferPosition+=len;
102 } else {
103 // the buffer does not have enough data. First serve all we've got.
104 int available = bufferLength - bufferPosition;
105 if(available > 0){
106 System.arraycopy(buffer, bufferPosition, b, offset, available);
107 offset += available;
108 len -= available;
109 bufferPosition += available;
110 }
111 // and now, read the remaining 'len' bytes:
112 if (useBuffer && len<bufferSize){
113 // If the amount left to read is small enough, and
114 // we are allowed to use our buffer, do it in the usual
115 // buffered way: fill the buffer and copy from it:
116 refill();
117 if(bufferLength<len){
118 // Throw an exception when refill() could not read len bytes:
119 System.arraycopy(buffer, 0, b, offset, bufferLength);
120 throw new IOException("read past EOF");
121 } else {
122 System.arraycopy(buffer, 0, b, offset, len);
123 bufferPosition=len;
124 }
125 } else {
126 // The amount left to read is larger than the buffer
127 // or we've been asked to not use our buffer -
128 // there's no performance reason not to read it all
129 // at once. Note that unlike the previous code of
130 // this function, there is no need to do a seek
131 // here, because there's no need to reread what we
132 // had in the buffer.
133 long after = bufferStart+bufferPosition+len;
134 if(after > length())
135 throw new IOException("read past EOF");
136 readInternal(b, offset, len);
137 bufferStart = after;
138 bufferPosition = 0;
139 bufferLength = 0; // trigger refill() on read
140 }
141 }
142 }
143
144 private void refill() throws IOException {
145 long start = bufferStart + bufferPosition;
146 long end = start + bufferSize;
147 if (end > length()) // don't read past EOF
148 end = length();
149 int newLength = (int)(end - start);
150 if (newLength <= 0)
151 throw new IOException("read past EOF");
152
153 if (buffer == null) {
154 newBuffer(new byte[bufferSize]); // allocate buffer lazily
155 seekInternal(bufferStart);
156 }
157 readInternal(buffer, 0, newLength);
158 bufferLength = newLength;
159 bufferStart = start;
160 bufferPosition = 0;
161 }
162
163 /** Expert: implements buffer refill. Reads bytes from the current position
164 * in the input.
165 * @param b the array to read bytes into
166 * @param offset the offset in the array to start storing bytes
167 * @param length the number of bytes to read
168 */
169 protected abstract void readInternal(byte[] b, int offset, int length)
170 throws IOException;
171
172 public long getFilePointer() { return bufferStart + bufferPosition; }
173
174 public void seek(long pos) throws IOException {
175 if (pos >= bufferStart && pos < (bufferStart + bufferLength))
176 bufferPosition = (int)(pos - bufferStart); // seek within buffer
177 else {
178 bufferStart = pos;
179 bufferPosition = 0;
180 bufferLength = 0; // trigger refill() on read()
181 seekInternal(pos);
182 }
183 }
184
185 /** Expert: implements seek. Sets current position in this file, where the
186 * next {@link #readInternal(byte[],int,int)} will occur.
187 * @see #readInternal(byte[],int,int)
188 */
189 protected abstract void seekInternal(long pos) throws IOException;
190
191 public Object clone() {
192 BufferedIndexInput clone = (BufferedIndexInput)super.clone();
193
194 clone.buffer = null;
195 clone.bufferLength = 0;
196 clone.bufferPosition = 0;
197 clone.bufferStart = getFilePointer();
198
199 return clone;
200 }
201
202 }