001package org.biojava.utils.io;
002
003import java.io.IOException;
004import java.nio.MappedByteBuffer;
005import java.nio.channels.FileChannel;
006
007import org.biojava.utils.Constants;
008
009/**
010 * Wrapper arround MappedByteBuffers to allow long-indexed access to files
011 * larger than 2 gigs.
012 *
013 * @author Matthews Pocock
014 */
015public class LargeBuffer {
016  /*
017   * We will set up MappedByteBuffers that are responsible for PAGE_SIZE
018   * bytes. Unfortunately, word boundaries are not aligned, so someone could
019   * try to write a double to the last byte in a buffer. So, 
020   */
021
022  private static final long PAGE_SIZE;
023  private static final long PAGE_OVERLAP;
024  
025  static {
026    PAGE_OVERLAP = Constants.BYTES_IN_LONG;
027    PAGE_SIZE = Integer.MAX_VALUE / 8 - PAGE_OVERLAP;
028  }
029  
030  private final long pos;
031  private final long size;
032  private final FileChannel channel;
033  private final FileChannel.MapMode mode;
034
035  private long position = 0;
036  private int lastBufferIndex = -1;
037  private MappedByteBuffer lastBuffer = null;
038  
039  
040  public LargeBuffer(
041    FileChannel channel,
042    FileChannel.MapMode mode,
043    long pos,
044    long size
045  ) throws IOException {
046    this.channel = channel;
047    this.mode = mode;
048    this.pos = pos;
049    this.size = size;
050  }
051  
052  private MappedByteBuffer getBuffer(int index)
053  throws IOException {
054    if(index != lastBufferIndex) {
055      System.out.println("Allocating page: " + index);
056      long offset = PAGE_SIZE * index;
057      System.out.println("From: " + (pos + offset));
058      System.out.println("Size: " + Math.min(size - offset, PAGE_SIZE + PAGE_OVERLAP));
059      lastBuffer = channel.map(
060        mode,
061        pos + offset,
062        Math.min(size - offset, PAGE_SIZE + PAGE_OVERLAP)
063      );
064      lastBufferIndex = index;
065      System.out.println("Done");
066    }
067    
068    return lastBuffer;
069  }
070  
071  public byte get(long pos)
072  throws IndexOutOfBoundsException, IOException {
073    int offset = getOffset(pos);
074    int index = getIndex(pos);
075    
076    MappedByteBuffer buffer = getBuffer(index);
077    return buffer.get(offset);
078  }
079  
080  public byte get()
081  throws IndexOutOfBoundsException, IOException {
082    byte val = get(position);
083    position += Constants.BYTES_IN_BYTE;
084    return val;
085  }
086  
087  public void put(long pos, byte b)
088  throws IndexOutOfBoundsException, IOException {
089    int offset = getOffset(pos);
090    int index = getIndex(pos);
091    
092    MappedByteBuffer buffer = getBuffer(index);
093    buffer.put(offset, b);
094  }
095  
096  public void put(byte val)
097  throws IndexOutOfBoundsException, IOException {
098    put(position, val);
099    position += Constants.BYTES_IN_BYTE;
100  }
101  
102  public char getChar(long pos)
103  throws IndexOutOfBoundsException, IOException {
104    int offset = getOffset(pos);
105    int index = getIndex(pos);
106    
107    MappedByteBuffer buffer = getBuffer(index);
108    return buffer.getChar(offset);
109  }
110  
111  public char getChar()
112  throws IndexOutOfBoundsException, IOException {
113    char val = getChar(position);
114    position += Constants.BYTES_IN_CHAR;
115    return val;
116  }
117  
118  public void putChar(long pos, char c)
119  throws IndexOutOfBoundsException, IOException {
120    int offset = getOffset(pos);
121    int index = getIndex(pos);
122    
123    MappedByteBuffer buffer = getBuffer(index);
124    buffer.putChar(offset, c);
125  }
126  
127  public void putChar(char val)
128  throws IndexOutOfBoundsException, IOException {
129    putChar(position, val);
130    position += Constants.BYTES_IN_CHAR;
131  }
132  
133  public double getDouble(long pos)
134  throws IndexOutOfBoundsException, IOException {
135    int offset = getOffset(pos);
136    int index = getIndex(pos);
137    
138    MappedByteBuffer buffer = getBuffer(index);
139    return buffer.getDouble(offset);
140  }
141  
142  public double getDouble()
143  throws IndexOutOfBoundsException, IOException {
144    double val = getDouble(position);
145    position += Constants.BYTES_IN_DOUBLE;
146    return val;
147  }
148  
149  public void putDouble(long pos, double d)
150  throws IndexOutOfBoundsException, IOException {
151    int offset = getOffset(pos);
152    int index = getIndex(pos);
153    
154    MappedByteBuffer buffer = getBuffer(index);
155    buffer.putDouble(offset, d);
156  }
157  
158  public void putDouble(double val)
159  throws IndexOutOfBoundsException, IOException {
160    putDouble(position, val);
161    position += Constants.BYTES_IN_DOUBLE;
162  }
163  
164  public float getFloat(long pos)
165  throws IndexOutOfBoundsException, IOException {
166    int offset = getOffset(pos);
167    int index = getIndex(pos);
168    
169    MappedByteBuffer buffer = getBuffer(index);
170    return buffer.getFloat(offset);
171  }
172  
173  public float getFloat()
174  throws IndexOutOfBoundsException, IOException {
175    float val = getFloat(position);
176    position += Constants.BYTES_IN_FLOAT;
177    return val;
178  }
179  
180  public void putFloat(long pos, float f)
181  throws IndexOutOfBoundsException, IOException {
182    int offset = getOffset(pos);
183    int index = getIndex(pos);
184    
185    MappedByteBuffer buffer = getBuffer(index);
186    buffer.putFloat(offset, f);
187  }
188  
189  public void putFloat(float val)
190  throws IndexOutOfBoundsException, IOException {
191    putFloat(position, val);
192    position += Constants.BYTES_IN_FLOAT;
193  }
194  
195  public int getInt(long pos)
196  throws IndexOutOfBoundsException, IOException {
197    int offset = getOffset(pos);
198    int index = getIndex(pos);
199    
200    MappedByteBuffer buffer = getBuffer(index);
201    return buffer.getInt(offset);
202  }
203  
204  public int getInt()
205  throws IndexOutOfBoundsException, IOException {
206    int val = getInt(position);
207    position += Constants.BYTES_IN_INT;
208    return val;
209  }
210  
211  public void putInt(long pos, int i)
212  throws IndexOutOfBoundsException, IOException {
213    int offset = getOffset(pos);
214    int index = getIndex(pos);
215    
216    MappedByteBuffer buffer = getBuffer(index);
217    buffer.putInt(offset, i);
218  }
219  
220  public void putInt(int val)
221  throws IndexOutOfBoundsException, IOException {
222    putInt(position, val);
223    position += Constants.BYTES_IN_INT;
224  }
225  
226  public long getLong(long pos)
227  throws IndexOutOfBoundsException, IOException {
228    int offset = getOffset(pos);
229    int index = getIndex(pos);
230    
231    MappedByteBuffer buffer = getBuffer(index);
232    return buffer.getLong(offset);
233  }
234  
235  public long getLong()
236  throws IndexOutOfBoundsException, IOException {
237    long val = getLong(position);
238    position += Constants.BYTES_IN_LONG;
239    return val;
240  }
241  
242  public void putLong(long pos, long l)
243  throws IndexOutOfBoundsException, IOException {
244    int offset = getOffset(pos);
245    int index = getIndex(pos);
246    
247    MappedByteBuffer buffer = getBuffer(index);
248    buffer.putLong(offset, l);
249  }
250  
251  public void putLong(long val)
252  throws IndexOutOfBoundsException, IOException {
253    putLong(position, val);
254    position += Constants.BYTES_IN_LONG;
255  }
256  
257  public short getShort(long pos)
258  throws IndexOutOfBoundsException, IOException {
259    int offset = getOffset(pos);
260    int index = getIndex(pos);
261    
262    MappedByteBuffer buffer = getBuffer(index);
263    return buffer.getShort(offset);
264  }
265  
266  public short getShort()
267  throws IndexOutOfBoundsException, IOException {
268    short val = getShort(position);
269    position += Constants.BYTES_IN_SHORT;
270    return val;
271  }
272  
273  public void putShort(long pos, short s)
274  throws IndexOutOfBoundsException, IOException {
275    int offset = getOffset(pos);
276    int index = getIndex(pos);
277    
278    MappedByteBuffer buffer = getBuffer(index);
279    buffer.putShort(offset, s);
280  }
281  
282  public void putShort(short val)
283  throws IndexOutOfBoundsException, IOException {
284    putShort(position, val);
285    position += Constants.BYTES_IN_SHORT;
286  }
287  
288  public long position() {
289    return position;
290  }
291  
292  public void position(long pos) {
293    this.position = pos;
294  }
295  
296  private int getOffset(long pos)
297  throws IndexOutOfBoundsException {
298    if(pos > size) {
299      throw new IndexOutOfBoundsException();
300    }
301    return (int) (pos % PAGE_SIZE);
302  }
303  
304  private int getIndex(long pos) {
305    return (int) (pos / (long) PAGE_SIZE);
306  }
307  
308  public void force() {
309//    for(Iterator i = buffers.iterator(); i.hasNext(); ) {
310//      MappedByteBuffer buff = (MappedByteBuffer) i.next();
311//      buff.force();
312//    }
313  }
314}