1 package org.apache.lucene.store;
2
3 /**
4 * Copyright 2004 The Apache Software Foundation
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 import java.io.IOException;
20
21 /** Abstract base class for input from a file in a {@link Directory}. A
22 * random-access input stream. Used for all Lucene index input operations.
23 * @see Directory
24 * @see OutputStream
25 */
26 public abstract class InputStream implements Cloneable {
27 static final int BUFFER_SIZE = OutputStream.BUFFER_SIZE;
28
29 private byte[] buffer;
30 private char[] chars;
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 protected long length; // set by subclasses
37
38 /** Reads and returns a single byte.
39 * @see OutputStream#writeByte(byte)
40 */
41 public final byte readByte() throws IOException {
42 if (bufferPosition >= bufferLength)
43 refill();
44 return buffer[bufferPosition++];
45 }
46
47 /** Reads a specified number of bytes into an array at the specified offset.
48 * @param b the array to read bytes into
49 * @param offset the offset in the array to start storing bytes
50 * @param len the number of bytes to read
51 * @see OutputStream#writeBytes(byte[],int)
52 */
53 public final void readBytes(byte[] b, int offset, int len)
54 throws IOException {
55 if (len < BUFFER_SIZE) {
56 for (int i = 0; i < len; i++) // read byte-by-byte
57 b[i + offset] = (byte)readByte();
58 } else { // read all-at-once
59 long start = getFilePointer();
60 seekInternal(start);
61 readInternal(b, offset, len);
62
63 bufferStart = start + len; // adjust stream variables
64 bufferPosition = 0;
65 bufferLength = 0; // trigger refill() on read
66 }
67 }
68
69 /** Reads four bytes and returns an int.
70 * @see OutputStream#writeInt(int)
71 */
72 public final int readInt() throws IOException {
73 return ((readByte() & 0xFF) << 24) | ((readByte() & 0xFF) << 16)
74 | ((readByte() & 0xFF) << 8) | (readByte() & 0xFF);
75 }
76
77 /** Reads an int stored in variable-length format. Reads between one and
78 * five bytes. Smaller values take fewer bytes. Negative numbers are not
79 * supported.
80 * @see OutputStream#writeVInt(int)
81 */
82 public final int readVInt() throws IOException {
83 byte b = readByte();
84 int i = b & 0x7F;
85 for (int shift = 7; (b & 0x80) != 0; shift += 7) {
86 b = readByte();
87 i |= (b & 0x7F) << shift;
88 }
89 return i;
90 }
91
92 /** Reads eight bytes and returns a long.
93 * @see OutputStream#writeLong(long)
94 */
95 public final long readLong() throws IOException {
96 return (((long)readInt()) << 32) | (readInt() & 0xFFFFFFFFL);
97 }
98
99 /** Reads a long stored in variable-length format. Reads between one and
100 * nine bytes. Smaller values take fewer bytes. Negative numbers are not
101 * supported. */
102 public final long readVLong() throws IOException {
103 byte b = readByte();
104 long i = b & 0x7F;
105 for (int shift = 7; (b & 0x80) != 0; shift += 7) {
106 b = readByte();
107 i |= (b & 0x7FL) << shift;
108 }
109 return i;
110 }
111
112 /** Reads a string.
113 * @see OutputStream#writeString(String)
114 */
115 public final String readString() throws IOException {
116 int length = readVInt();
117 if (chars == null || length > chars.length)
118 chars = new char[length];
119 readChars(chars, 0, length);
120 return new String(chars, 0, length);
121 }
122
123 /** Reads UTF-8 encoded characters into an array.
124 * @param buffer the array to read characters into
125 * @param start the offset in the array to start storing characters
126 * @param length the number of characters to read
127 * @see OutputStream#writeChars(String,int,int)
128 */
129 public final void readChars(char[] buffer, int start, int length)
130 throws IOException {
131 final int end = start + length;
132 for (int i = start; i < end; i++) {
133 byte b = readByte();
134 if ((b & 0x80) == 0)
135 buffer[i] = (char)(b & 0x7F);
136 else if ((b & 0xE0) != 0xE0) {
137 buffer[i] = (char)(((b & 0x1F) << 6)
138 | (readByte() & 0x3F));
139 } else
140 buffer[i] = (char)(((b & 0x0F) << 12)
141 | ((readByte() & 0x3F) << 6)
142 | (readByte() & 0x3F));
143 }
144 }
145
146
147 private void refill() throws IOException {
148 long start = bufferStart + bufferPosition;
149 long end = start + BUFFER_SIZE;
150 if (end > length) // don't read past EOF
151 end = length;
152 bufferLength = (int)(end - start);
153 if (bufferLength == 0)
154 throw new IOException("read past EOF");
155
156 if (buffer == null)
157 buffer = new byte[BUFFER_SIZE]; // allocate buffer lazily
158 readInternal(buffer, 0, bufferLength);
159
160 bufferStart = start;
161 bufferPosition = 0;
162 }
163
164 /** Expert: implements buffer refill. Reads bytes from the current position
165 * in the input.
166 * @param b the array to read bytes into
167 * @param offset the offset in the array to start storing bytes
168 * @param length the number of bytes to read
169 */
170 protected abstract void readInternal(byte[] b, int offset, int length)
171 throws IOException;
172
173 /** Closes the stream to futher operations. */
174 public abstract void close() throws IOException;
175
176 /** Returns the current position in this file, where the next read will
177 * occur.
178 * @see #seek(long)
179 */
180 public final long getFilePointer() {
181 return bufferStart + bufferPosition;
182 }
183
184 /** Sets current position in this file, where the next read will occur.
185 * @see #getFilePointer()
186 */
187 public final void seek(long pos) throws IOException {
188 if (pos >= bufferStart && pos < (bufferStart + bufferLength))
189 bufferPosition = (int)(pos - bufferStart); // seek within buffer
190 else {
191 bufferStart = pos;
192 bufferPosition = 0;
193 bufferLength = 0; // trigger refill() on read()
194 seekInternal(pos);
195 }
196 }
197
198 /** Expert: implements seek. Sets current position in this file, where the
199 * next {@link #readInternal(byte[],int,int)} will occur.
200 * @see #readInternal(byte[],int,int)
201 */
202 protected abstract void seekInternal(long pos) throws IOException;
203
204 /** The number of bytes in the file. */
205 public final long length() {
206 return length;
207 }
208
209 /** Returns a clone of this stream.
210 *
211 * <p>Clones of a stream access the same data, and are positioned at the same
212 * point as the stream they were cloned from.
213 *
214 * <p>Expert: Subclasses must ensure that clones may be positioned at
215 * different points in the input from each other and from the stream they
216 * were cloned from.
217 */
218 public Object clone() {
219 InputStream clone = null;
220 try {
221 clone = (InputStream)super.clone();
222 } catch (CloneNotSupportedException e) {}
223
224 if (buffer != null) {
225 clone.buffer = new byte[BUFFER_SIZE];
226 System.arraycopy(buffer, 0, clone.buffer, 0, bufferLength);
227 }
228
229 clone.chars = null;
230
231 return clone;
232 }
233
234 }