View Javadoc

1   // BSD License (http://www.galagosearch.org/license)
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          // is any of this data cached?
78          if (offset - bufferStart < cacheBuffer.length) {
79              // this cast is safe because we know it's smaller than cacheBuffer.length
80              bufferPosition = (int) (offset - bufferStart);
81          } else {
82              // this sets the stream position to the appropriate point,
83              // and effectively invalidates the current cache contents.
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         // shift a to high word
181         a <<= 32;
182         // mask b
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     // inlining here for performance
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         // quick check to see if it's already buffered
211         if (cacheBuffer.length - bufferPosition >= length) {
212             return;        // if it's not buffered, is there enough room left in the
213                            // file to cache this much data?
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 }