add --shared
[pylucene.git] / lucene-java-3.4.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   public BufferedIndexInput() {}
44
45   /** Inits BufferedIndexInput with a specific bufferSize */
46   public BufferedIndexInput(int bufferSize) {
47     checkBufferSize(bufferSize);
48     this.bufferSize = bufferSize;
49   }
50
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);
56       bufferSize = newSize;
57       if (buffer != null) {
58         // Resize the existing buffer and carefully save as
59         // many bytes as possible starting from the current
60         // bufferPosition
61         byte[] newBuffer = new byte[newSize];
62         final int leftInBuffer = bufferLength-bufferPosition;
63         final int numToCopy;
64         if (leftInBuffer > newSize)
65           numToCopy = newSize;
66         else
67           numToCopy = leftInBuffer;
68         System.arraycopy(buffer, bufferPosition, newBuffer, 0, numToCopy);
69         bufferStart += bufferPosition;
70         bufferPosition = 0;
71         bufferLength = numToCopy;
72         newBuffer(newBuffer);
73       }
74     }
75   }
76
77   protected void newBuffer(byte[] newBuffer) {
78     // Subclasses can do something here
79     buffer = newBuffer;
80   }
81
82   /** Returns buffer size.  @see #setBufferSize */
83   public int getBufferSize() {
84     return bufferSize;
85   }
86
87   private void checkBufferSize(int bufferSize) {
88     if (bufferSize <= 0)
89       throw new IllegalArgumentException("bufferSize must be greater than 0 (got " + bufferSize + ")");
90   }
91
92   @Override
93   public void readBytes(byte[] b, int offset, int len) throws IOException {
94     readBytes(b, offset, len, true);
95   }
96
97   @Override
98   public void readBytes(byte[] b, int offset, int len, boolean useBuffer) throws IOException {
99
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);
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, available);
110         offset += available;
111         len -= available;
112         bufferPosition += available;
113       }
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:
119         refill();
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");
124         } else {
125           System.arraycopy(buffer, 0, b, offset, len);
126           bufferPosition=len;
127         }
128       } else {
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;
137         if(after > length())
138           throw new IOException("read past EOF");
139         readInternal(b, offset, len);
140         bufferStart = after;
141         bufferPosition = 0;
142         bufferLength = 0;                    // trigger refill() on read
143       }
144     }
145   }
146   
147   @Override
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);
152     } else {
153       return super.readInt();
154     }
155   }
156   
157   @Override
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);
165     } else {
166       return super.readLong();
167     }
168   }
169
170   @Override
171   public int readVInt() throws IOException {
172     if (5 <= (bufferLength-bufferPosition)) {
173       byte b = buffer[bufferPosition++];
174       int i = b & 0x7F;
175       for (int shift = 7; (b & 0x80) != 0; shift += 7) {
176         b = buffer[bufferPosition++];
177         i |= (b & 0x7F) << shift;
178       }
179       return i;
180     } else {
181       return super.readVInt();
182     }
183   }
184   
185   @Override
186   public long readVLong() throws IOException {
187     if (9 <= bufferLength-bufferPosition) {
188       byte b = buffer[bufferPosition++];
189       long i = b & 0x7F;
190       for (int shift = 7; (b & 0x80) != 0; shift += 7) {
191         b = buffer[bufferPosition++];
192         i |= (b & 0x7FL) << shift;
193       }
194       return i;
195     } else {
196       return super.readVLong();
197     }
198   }
199   
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
204       end = length();
205     int newLength = (int)(end - start);
206     if (newLength <= 0)
207       throw new IOException("read past EOF");
208
209     if (buffer == null) {
210       newBuffer(new byte[bufferSize]);  // allocate buffer lazily
211       seekInternal(bufferStart);
212     }
213     readInternal(buffer, 0, newLength);
214     bufferLength = newLength;
215     bufferStart = start;
216     bufferPosition = 0;
217   }
218
219   /** Expert: implements buffer refill.  Reads bytes from the current position
220    * in the input.
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
224    */
225   protected abstract void readInternal(byte[] b, int offset, int length)
226           throws IOException;
227
228   @Override
229   public long getFilePointer() { return bufferStart + bufferPosition; }
230
231   @Override
232   public void seek(long pos) throws IOException {
233     if (pos >= bufferStart && pos < (bufferStart + bufferLength))
234       bufferPosition = (int)(pos - bufferStart);  // seek within buffer
235     else {
236       bufferStart = pos;
237       bufferPosition = 0;
238       bufferLength = 0;                           // trigger refill() on read()
239       seekInternal(pos);
240     }
241   }
242
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)
246    */
247   protected abstract void seekInternal(long pos) throws IOException;
248
249   @Override
250   public Object clone() {
251     BufferedIndexInput clone = (BufferedIndexInput)super.clone();
252
253     clone.buffer = null;
254     clone.bufferLength = 0;
255     clone.bufferPosition = 0;
256     clone.bufferStart = getFilePointer();
257
258     return clone;
259   }
260
261   /**
262    * Flushes the in-memory bufer to the given output, copying at most
263    * <code>numBytes</code>.
264    * <p>
265    * <b>NOTE:</b> this method does not refill the buffer, however it does
266    * advance the buffer position.
267    * 
268    * @return the number of bytes actually flushed from the in-memory buffer.
269    */
270   protected int flushBuffer(IndexOutput out, long numBytes) throws IOException {
271     int toCopy = bufferLength - bufferPosition;
272     if (toCopy > numBytes) {
273       toCopy = (int) numBytes;
274     }
275     if (toCopy > 0) {
276       out.writeBytes(buffer, bufferPosition, toCopy);
277       bufferPosition += toCopy;
278     }
279     return toCopy;
280   }
281   
282   @Override
283   public void copyBytes(IndexOutput out, long numBytes) throws IOException {
284     assert numBytes >= 0: "numBytes=" + numBytes;
285
286     while (numBytes > 0) {
287       if (bufferLength == bufferPosition) {
288         refill();
289       }
290       numBytes -= flushBuffer(out, numBytes);
291     }
292   }
293   
294 }