001: package org.apache.lucene.store;
002:
003: /**
004: * Licensed to the Apache Software Foundation (ASF) under one or more
005: * contributor license agreements. See the NOTICE file distributed with
006: * this work for additional information regarding copyright ownership.
007: * The ASF licenses this file to You under the Apache License, Version 2.0
008: * (the "License"); you may not use this file except in compliance with
009: * the License. You may obtain a copy of the License at
010: *
011: * http://www.apache.org/licenses/LICENSE-2.0
012: *
013: * Unless required by applicable law or agreed to in writing, software
014: * distributed under the License is distributed on an "AS IS" BASIS,
015: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
016: * See the License for the specific language governing permissions and
017: * limitations under the License.
018: */
019:
020: import java.io.IOException;
021:
022: /** Base implementation class for buffered {@link IndexInput}. */
023: public abstract class BufferedIndexInput extends IndexInput {
024:
025: /** Default buffer size */
026: public static final int BUFFER_SIZE = 1024;
027:
028: private int bufferSize = BUFFER_SIZE;
029:
030: private byte[] buffer;
031:
032: private long bufferStart = 0; // position in file of buffer
033: private int bufferLength = 0; // end of valid bytes
034: private int bufferPosition = 0; // next byte to read
035:
036: public byte readByte() throws IOException {
037: if (bufferPosition >= bufferLength)
038: refill();
039: return buffer[bufferPosition++];
040: }
041:
042: public BufferedIndexInput() {
043: }
044:
045: /** Inits BufferedIndexInput with a specific bufferSize */
046: public BufferedIndexInput(int bufferSize) {
047: checkBufferSize(bufferSize);
048: this .bufferSize = bufferSize;
049: }
050:
051: /** Change the buffer size used by this IndexInput */
052: public void setBufferSize(int newSize) {
053: assert buffer == null || bufferSize == buffer.length;
054: if (newSize != bufferSize) {
055: checkBufferSize(newSize);
056: bufferSize = newSize;
057: if (buffer != null) {
058: // Resize the existing buffer and carefully save as
059: // many bytes as possible starting from the current
060: // bufferPosition
061: byte[] newBuffer = new byte[newSize];
062: final int leftInBuffer = bufferLength - bufferPosition;
063: final int numToCopy;
064: if (leftInBuffer > newSize)
065: numToCopy = newSize;
066: else
067: numToCopy = leftInBuffer;
068: System.arraycopy(buffer, bufferPosition, newBuffer, 0,
069: numToCopy);
070: bufferStart += bufferPosition;
071: bufferPosition = 0;
072: bufferLength = numToCopy;
073: buffer = newBuffer;
074: }
075: }
076: }
077:
078: /** Returns buffer size. @see #setBufferSize */
079: public int getBufferSize() {
080: return bufferSize;
081: }
082:
083: private void checkBufferSize(int bufferSize) {
084: if (bufferSize <= 0)
085: throw new IllegalArgumentException(
086: "bufferSize must be greater than 0 (got "
087: + bufferSize + ")");
088: }
089:
090: public void readBytes(byte[] b, int offset, int len)
091: throws IOException {
092: readBytes(b, offset, len, true);
093: }
094:
095: public void readBytes(byte[] b, int offset, int len,
096: boolean useBuffer) throws IOException {
097:
098: if (len <= (bufferLength - bufferPosition)) {
099: // the buffer contains enough data to satisfy this request
100: if (len > 0) // to allow b to be null if len is 0...
101: System
102: .arraycopy(buffer, bufferPosition, b, offset,
103: len);
104: bufferPosition += len;
105: } else {
106: // the buffer does not have enough data. First serve all we've got.
107: int available = bufferLength - bufferPosition;
108: if (available > 0) {
109: System.arraycopy(buffer, bufferPosition, b, offset,
110: available);
111: offset += available;
112: len -= available;
113: bufferPosition += available;
114: }
115: // and now, read the remaining 'len' bytes:
116: if (useBuffer && len < bufferSize) {
117: // If the amount left to read is small enough, and
118: // we are allowed to use our buffer, do it in the usual
119: // buffered way: fill the buffer and copy from it:
120: refill();
121: if (bufferLength < len) {
122: // Throw an exception when refill() could not read len bytes:
123: System
124: .arraycopy(buffer, 0, b, offset,
125: bufferLength);
126: throw new IOException("read past EOF");
127: } else {
128: System.arraycopy(buffer, 0, b, offset, len);
129: bufferPosition = len;
130: }
131: } else {
132: // The amount left to read is larger than the buffer
133: // or we've been asked to not use our buffer -
134: // there's no performance reason not to read it all
135: // at once. Note that unlike the previous code of
136: // this function, there is no need to do a seek
137: // here, because there's no need to reread what we
138: // had in the buffer.
139: long after = bufferStart + bufferPosition + len;
140: if (after > length())
141: throw new IOException("read past EOF");
142: readInternal(b, offset, len);
143: bufferStart = after;
144: bufferPosition = 0;
145: bufferLength = 0; // trigger refill() on read
146: }
147: }
148: }
149:
150: private void refill() throws IOException {
151: long start = bufferStart + bufferPosition;
152: long end = start + bufferSize;
153: if (end > length()) // don't read past EOF
154: end = length();
155: bufferLength = (int) (end - start);
156: if (bufferLength <= 0)
157: throw new IOException("read past EOF");
158:
159: if (buffer == null) {
160: buffer = new byte[bufferSize]; // allocate buffer lazily
161: seekInternal(bufferStart);
162: }
163: readInternal(buffer, 0, bufferLength);
164:
165: bufferStart = start;
166: bufferPosition = 0;
167: }
168:
169: /** Expert: implements buffer refill. Reads bytes from the current position
170: * in the input.
171: * @param b the array to read bytes into
172: * @param offset the offset in the array to start storing bytes
173: * @param length the number of bytes to read
174: */
175: protected abstract void readInternal(byte[] b, int offset,
176: int length) throws IOException;
177:
178: public long getFilePointer() {
179: return bufferStart + bufferPosition;
180: }
181:
182: public void seek(long pos) throws IOException {
183: if (pos >= bufferStart && pos < (bufferStart + bufferLength))
184: bufferPosition = (int) (pos - bufferStart); // seek within buffer
185: else {
186: bufferStart = pos;
187: bufferPosition = 0;
188: bufferLength = 0; // trigger refill() on read()
189: seekInternal(pos);
190: }
191: }
192:
193: /** Expert: implements seek. Sets current position in this file, where the
194: * next {@link #readInternal(byte[],int,int)} will occur.
195: * @see #readInternal(byte[],int,int)
196: */
197: protected abstract void seekInternal(long pos) throws IOException;
198:
199: public Object clone() {
200: BufferedIndexInput clone = (BufferedIndexInput) super .clone();
201:
202: clone.buffer = null;
203: clone.bufferLength = 0;
204: clone.bufferPosition = 0;
205: clone.bufferStart = getFilePointer();
206:
207: return clone;
208: }
209:
210: }
|