/*
* Geotoolkit - An Open Source Java GIS Toolkit
* http://www.geotoolkit.org
*
* (C) 2004-2008, Open Source Geospatial Foundation (OSGeo)
* (C) 2009-2010, Geomatys
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*/
package org.geotoolkit.index.quadtree.fs;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer;
import java.nio.channels.FileChannel;
/**
* A utility class to access file contents by using a single scrolling
* buffer reading file contents with a minimum of 8kb per access
*
* @author Johann Sorel (Geomatys)
* @module
*/
public class ScrollingBuffer {
private final FileChannel channel;
private final ByteOrder order;
ByteBuffer original;
/** the initial position of the buffer in the channel */
private long bufferStart;
public ScrollingBuffer(final FileChannel channel, final ByteOrder order)
throws IOException {
this.channel = channel;
this.order = order;
this.bufferStart = channel.position();
// start with an 8kb buffer
this.original = ByteBuffer.allocateDirect(8 * 1024);
this.original.order(order);
channel.read(original);
original.flip();
}
public int getInt() throws IOException {
if (original.remaining() < 4) {
refillBuffer(4);
}
return original.getInt();
}
public double getDouble() throws IOException {
if (original.remaining() < 8) {
refillBuffer(8);
}
return original.getDouble();
}
public void getIntArray(final int[] array) throws IOException {
final int size = array.length * 4;
if (original.remaining() < size) {
refillBuffer(size);
}
// read the array using a view
final IntBuffer intView = original.asIntBuffer();
intView.limit(array.length);
intView.get(array);
// don't forget to update the original buffer position, since the
// view is independent
original.position(original.position() + size);
}
/**
*
* @param requiredSize
* @throws IOException
*/
void refillBuffer(final int requiredSize) throws IOException {
// compute the actual position up to we have read something
final long currentPosition = bufferStart + original.position();
// if the buffer is not big enough enlarge it
if (original.capacity() < requiredSize) {
int size = original.capacity();
while (size < requiredSize) {
size *= 2;
}
original = ByteBuffer.allocateDirect(size);
original.order(order);
}
readBuffer(currentPosition);
}
private void readBuffer(final long currentPosition) throws IOException {
channel.position(currentPosition);
original.clear();
channel.read(original);
original.flip();
bufferStart = currentPosition;
}
/**
* Jumps the buffer to the specified position in the file
*
* @param newPosition
* @throws IOException
*/
public void goTo(final long newPosition) throws IOException {
// if the new position is already in the buffer, just move the
// buffer position
// otherwise we have to reload it
if (newPosition >= bufferStart && newPosition <= bufferStart + original.limit()) {
original.position((int) (newPosition - bufferStart));
} else {
readBuffer(newPosition);
}
}
/**
* Returns the absolute position of the next byte that will be read
*
* @return
*/
public long getPosition() {
return bufferStart + original.position();
}
}