pylucene 3.5.0-3
[pylucene.git] / lucene-java-3.5.0 / lucene / src / java / org / apache / lucene / store / BufferedIndexInput.java
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   @Override
37   public byte readByte() throws IOException {
38     if (bufferPosition >= bufferLength)
39       refill();
40     return buffer[bufferPosition++];
41   }
42
43   /** @deprecated please pass resourceDesc */
44   @Deprecated
45   public BufferedIndexInput() {
46     this("anonymous BuffereIndexInput");
47   }
48
49   public BufferedIndexInput(String resourceDesc) {
50     this(resourceDesc, BUFFER_SIZE);
51   }
52
53   /** Inits BufferedIndexInput with a specific bufferSize
54    *  @deprecated please pass resourceDesc */
55   @Deprecated
56   public BufferedIndexInput(int bufferSize) {
57     this("anonymous BuffereIndexInput", bufferSize);
58   }
59
60   /** Inits BufferedIndexInput with a specific bufferSize */
61   public BufferedIndexInput(String resourceDesc, int bufferSize) {
62     super(resourceDesc);
63     checkBufferSize(bufferSize);
64     this.bufferSize = bufferSize;
65   }
66
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);
72       bufferSize = newSize;
73       if (buffer != null) {
74         // Resize the existing buffer and carefully save as
75         // many bytes as possible starting from the current
76         // bufferPosition
77         byte[] newBuffer = new byte[newSize];
78         final int leftInBuffer = bufferLength-bufferPosition;
79         final int numToCopy;
80         if (leftInBuffer > newSize)
81           numToCopy = newSize;
82         else
83           numToCopy = leftInBuffer;
84         System.arraycopy(buffer, bufferPosition, newBuffer, 0, numToCopy);
85         bufferStart += bufferPosition;
86         bufferPosition = 0;
87         bufferLength = numToCopy;
88         newBuffer(newBuffer);
89       }
90     }
91   }
92
93   protected void newBuffer(byte[] newBuffer) {
94     // Subclasses can do something here
95     buffer = newBuffer;
96   }
97
98   /** Returns buffer size.  @see #setBufferSize */
99   public int getBufferSize() {
100     return bufferSize;
101   }
102
103   private void checkBufferSize(int bufferSize) {
104     if (bufferSize <= 0)
105       throw new IllegalArgumentException("bufferSize must be greater than 0 (got " + bufferSize + ")");
106   }
107
108   @Override
109   public void readBytes(byte[] b, int offset, int len) throws IOException {
110     readBytes(b, offset, len, true);
111   }
112
113   @Override
114   public void readBytes(byte[] b, int offset, int len, boolean useBuffer) throws IOException {
115
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);
120       bufferPosition+=len;
121     } else {
122       // the buffer does not have enough data. First serve all we've got.
123       int available = bufferLength - bufferPosition;
124       if(available > 0){
125         System.arraycopy(buffer, bufferPosition, b, offset, available);
126         offset += available;
127         len -= available;
128         bufferPosition += available;
129       }
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:
135         refill();
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");
140         } else {
141           System.arraycopy(buffer, 0, b, offset, len);
142           bufferPosition=len;
143         }
144       } else {
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;
153         if(after > length())
154           throw new IOException("read past EOF");
155         readInternal(b, offset, len);
156         bufferStart = after;
157         bufferPosition = 0;
158         bufferLength = 0;                    // trigger refill() on read
159       }
160     }
161   }
162   
163   @Override
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);
168     } else {
169       return super.readInt();
170     }
171   }
172   
173   @Override
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);
181     } else {
182       return super.readLong();
183     }
184   }
185
186   @Override
187   public int readVInt() throws IOException {
188     if (5 <= (bufferLength-bufferPosition)) {
189       byte b = buffer[bufferPosition++];
190       int i = b & 0x7F;
191       for (int shift = 7; (b & 0x80) != 0; shift += 7) {
192         b = buffer[bufferPosition++];
193         i |= (b & 0x7F) << shift;
194       }
195       return i;
196     } else {
197       return super.readVInt();
198     }
199   }
200   
201   @Override
202   public long readVLong() throws IOException {
203     if (9 <= bufferLength-bufferPosition) {
204       byte b = buffer[bufferPosition++];
205       long i = b & 0x7F;
206       for (int shift = 7; (b & 0x80) != 0; shift += 7) {
207         b = buffer[bufferPosition++];
208         i |= (b & 0x7FL) << shift;
209       }
210       return i;
211     } else {
212       return super.readVLong();
213     }
214   }
215   
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
220       end = length();
221     int newLength = (int)(end - start);
222     if (newLength <= 0)
223       throw new IOException("read past EOF");
224
225     if (buffer == null) {
226       newBuffer(new byte[bufferSize]);  // allocate buffer lazily
227       seekInternal(bufferStart);
228     }
229     readInternal(buffer, 0, newLength);
230     bufferLength = newLength;
231     bufferStart = start;
232     bufferPosition = 0;
233   }
234
235   /** Expert: implements buffer refill.  Reads bytes from the current position
236    * in the input.
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
240    */
241   protected abstract void readInternal(byte[] b, int offset, int length)
242           throws IOException;
243
244   @Override
245   public long getFilePointer() { return bufferStart + bufferPosition; }
246
247   @Override
248   public void seek(long pos) throws IOException {
249     if (pos >= bufferStart && pos < (bufferStart + bufferLength))
250       bufferPosition = (int)(pos - bufferStart);  // seek within buffer
251     else {
252       bufferStart = pos;
253       bufferPosition = 0;
254       bufferLength = 0;                           // trigger refill() on read()
255       seekInternal(pos);
256     }
257   }
258
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)
262    */
263   protected abstract void seekInternal(long pos) throws IOException;
264
265   @Override
266   public Object clone() {
267     BufferedIndexInput clone = (BufferedIndexInput)super.clone();
268
269     clone.buffer = null;
270     clone.bufferLength = 0;
271     clone.bufferPosition = 0;
272     clone.bufferStart = getFilePointer();
273
274     return clone;
275   }
276
277   /**
278    * Flushes the in-memory bufer to the given output, copying at most
279    * <code>numBytes</code>.
280    * <p>
281    * <b>NOTE:</b> this method does not refill the buffer, however it does
282    * advance the buffer position.
283    * 
284    * @return the number of bytes actually flushed from the in-memory buffer.
285    */
286   protected int flushBuffer(IndexOutput out, long numBytes) throws IOException {
287     int toCopy = bufferLength - bufferPosition;
288     if (toCopy > numBytes) {
289       toCopy = (int) numBytes;
290     }
291     if (toCopy > 0) {
292       out.writeBytes(buffer, bufferPosition, toCopy);
293       bufferPosition += toCopy;
294     }
295     return toCopy;
296   }
297   
298   @Override
299   public void copyBytes(IndexOutput out, long numBytes) throws IOException {
300     assert numBytes >= 0: "numBytes=" + numBytes;
301
302     while (numBytes > 0) {
303       if (bufferLength == bufferPosition) {
304         refill();
305       }
306       numBytes -= flushBuffer(out, numBytes);
307     }
308   }
309   
310 }