1
2 package org.galagosearch.tupleflow;
3
4 import java.io.EOFException;
5 import java.io.IOException;
6 import java.io.RandomAccessFile;
7
8 /***
9 *
10 * @author trevor
11 */
12 public class BufferedFileDataStream implements DataStream {
13 RandomAccessFile stream;
14 long stopPosition;
15 long startPosition;
16 final static int cacheLength = 32768;
17 long bufferStart;
18 int bufferPosition;
19 byte[] cacheBuffer;
20
21 /*** Creates a new instance of BufferedFileDataStream */
22 public BufferedFileDataStream(RandomAccessFile stream, long stopPosition) throws IOException {
23 this(stream, stream.getFilePointer(), stopPosition);
24 }
25
26 public BufferedFileDataStream(RandomAccessFile stream, long start, long end) {
27 assert start <= end;
28
29 this.stream = stream;
30 this.stopPosition = end;
31 this.cacheBuffer = new byte[0];
32 this.bufferPosition = 0;
33 this.bufferStart = start;
34 this.startPosition = start;
35 }
36
37 public BufferedFileDataStream subStream(long start, long length) {
38 assert start < length();
39 assert start + length <= length();
40 return new BufferedFileDataStream(
41 stream, bufferStart + start,
42 bufferStart + start + length);
43 }
44
45 public boolean isDone() {
46 return stopPosition <= bufferStart + bufferPosition;
47 }
48
49 public long length() {
50 return stopPosition - startPosition;
51 }
52
53 public long getPosition() {
54 return getAbsolutePosition() - startPosition;
55 }
56
57 public long getAbsolutePosition() {
58 return bufferStart + bufferPosition;
59 }
60
61 /***
62 * Seeks forward into the stream to a particular byte offset (reverse
63 * seeks are not allowed). The offset is relative to the start position of
64 * this data stream, not the beginning of the file.
65 */
66 public void seek(long offset) {
67 seekAbsolute(offset + startPosition);
68 }
69
70 /***
71 * Seeks forward into the stream to a particular byte offset (reverse
72 * seeks are not allowed). The offset is relative to the start of the file.
73 */
74 public void seekAbsolute(long offset) {
75 assert bufferStart + bufferPosition <= offset;
76
77
78 if (offset - bufferStart < cacheBuffer.length) {
79
80 bufferPosition = (int) (offset - bufferStart);
81 } else {
82
83
84 bufferStart = offset - cacheBuffer.length;
85 bufferPosition = cacheBuffer.length;
86 }
87 }
88
89 public void readFully(byte[] buffer, int start, int length) throws IOException {
90 cache(length);
91 System.arraycopy(cacheBuffer, bufferPosition, buffer, start, length);
92 update(length);
93 }
94
95 public void readFully(byte[] buffer) throws IOException {
96 cache(buffer.length);
97 System.arraycopy(cacheBuffer, bufferPosition, buffer, 0, buffer.length);
98 update(buffer.length);
99 }
100
101 public int skipBytes(int n) throws IOException {
102 update(n);
103 return n;
104 }
105
106 public int readUnsignedShort() throws IOException {
107 cache(2);
108
109 byte a = cacheByte(0);
110 byte b = cacheByte(1);
111
112 int result = (((a << 8) | (b & 0xff)) & 0xffff);
113
114 update(2);
115 return result;
116 }
117
118 public boolean readBoolean() throws IOException {
119 cache(1);
120 boolean result = (cacheByte(0) != 0) ? true : false;
121 update(1);
122 return result;
123 }
124
125 public byte readByte() throws IOException {
126 cache(1);
127 byte result = cacheByte(0);
128 update(1);
129 return result;
130 }
131
132 public char readChar() throws IOException {
133 return (char) readShort();
134 }
135
136 public short readShort() throws IOException {
137 cache(2);
138 byte a = cacheByte(0);
139 byte b = cacheByte(1);
140 short result = (short) ((a << 8) | (b & 0xff));
141 update(2);
142 return result;
143 }
144
145 public double readDouble() throws IOException {
146 long result = readLong();
147 return Double.longBitsToDouble(result);
148 }
149
150 public float readFloat() throws IOException {
151 int result = readInt();
152 return Float.intBitsToFloat(result);
153 }
154
155 public int readInt() throws IOException {
156 cache(4);
157
158 int a = cacheByte(0);
159 int b = cacheByte(1);
160 int c = cacheByte(2);
161 int d = cacheByte(3);
162
163 int result = ((a & 0xff) << 24) |
164 ((b & 0xff) << 16) |
165 ((c & 0xff) << 8) |
166 (d & 0xff);
167
168 update(4);
169 return result;
170 }
171
172 public String readLine() throws IOException {
173 throw new IOException("readLine is unimplemented and deprecated");
174 }
175
176 public long readLong() throws IOException {
177 long a = readInt();
178 long b = readInt();
179
180
181 a <<= 32;
182
183 b &= (0xFFFFFFFFL);
184
185 return a | b;
186 }
187
188 public String readUTF() throws IOException {
189 throw new IOException("readUTF is unimplemented");
190 }
191
192
193 public int readUnsignedByte() throws IOException {
194 if (cacheBuffer.length - bufferPosition >= 1) {
195 int result = 0xff & (int) cacheBuffer[bufferPosition];
196 bufferPosition += 1;
197 return result;
198 } else {
199 cache(1);
200 int b = cacheByte(0);
201 update(1);
202 return b & 0xff;
203 }
204 }
205
206 private void cache(int length) throws IOException {
207 assert length >= 0 : "Length can't be negative: " + length + " " +
208 bufferStart + bufferPosition + " " + stopPosition;
209
210
211 if (cacheBuffer.length - bufferPosition >= length) {
212 return;
213
214 }
215 if (bufferStart + bufferPosition + length > stopPosition) {
216 throw new EOFException("Tried to read off the end of the file.");
217 }
218 long current = bufferStart + bufferPosition;
219 int readLength = (int) Math.min(stopPosition - current, cacheLength);
220 readLength = Math.max(readLength, length);
221
222 if (readLength != cacheBuffer.length) {
223 cacheBuffer = new byte[readLength];
224 }
225 stream.seek(current);
226 stream.readFully(cacheBuffer);
227 bufferStart = current;
228 bufferPosition = 0;
229 }
230
231 private void update(int length) {
232 bufferPosition += length;
233 }
234
235 private byte cacheByte(int i) {
236 return cacheBuffer[bufferPosition + i];
237 }
238 }