strangedb-core/src/main/java/it/cavallium/strangedb/database/DatabaseFileIO.java

95 lines
2.7 KiB
Java

package it.cavallium.strangedb.database;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.AsynchronousFileChannel;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class DatabaseFileIO implements IFileIO {
private final AsynchronousFileChannel dataFileChannel;
private final AtomicLong firstFreeIndex;
private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(false);
private volatile boolean closed = false;
public DatabaseFileIO(Path dataFile) throws IOException {
dataFileChannel = AsynchronousFileChannel.open(dataFile, StandardOpenOption.READ, StandardOpenOption.WRITE);
firstFreeIndex = new AtomicLong(dataFileChannel.size());
}
@Override
public ByteBuffer readAt(long index, int length) throws IOException {
lock.readLock().lock();
try {
if (closed) throw new IOException("Database closed!");
ByteBuffer dataBuffer = ByteBuffer.allocate(length);
try {
dataFileChannel.read(dataBuffer, index).get();
} catch (InterruptedException e) {
throw new IOException(e);
} catch (ExecutionException e) {
throw new IOException(e.getCause());
}
dataBuffer.flip();
return dataBuffer;
} finally {
lock.readLock().unlock();
}
}
@Override
public int writeAt(long index, int length, ByteBuffer data) throws IOException {
lock.writeLock().lock();
try {
return writeAt_(index, length, data);
} finally {
lock.writeLock().unlock();
}
}
private int writeAt_(long index, int length, ByteBuffer data) throws IOException {
if (closed) throw new IOException("Database closed!");
if (data.position() != 0) {
throw new IOException("You didn't flip the ByteBuffer!");
}
firstFreeIndex.updateAndGet((firstFreeIndex) -> firstFreeIndex < index + length ? index + length : firstFreeIndex);
try {
return dataFileChannel.write(data, index).get();
} catch (InterruptedException e) {
throw new IOException(e);
} catch (ExecutionException e) {
throw new IOException(e.getCause());
}
}
@Override
public long writeAtEnd(int length, ByteBuffer data) throws IOException {
lock.writeLock().lock();
try {
if (closed) throw new IOException("Database closed!");
long index = firstFreeIndex.getAndAdd(length);
writeAt_(index, length, data);
return index;
} finally {
lock.writeLock().unlock();
}
}
@Override
public void close() throws IOException {
lock.writeLock().lock();
try {
if (closed) throw new IOException("Database already closed!");
closed = true;
dataFileChannel.close();
} finally {
lock.writeLock().unlock();
}
}
}