95 lines
2.7 KiB
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();
|
|
}
|
|
}
|
|
}
|