package it.cavallium.strangedb.database.references; import it.cavallium.strangedb.database.IReferencesMetadata; 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.Future; import static it.cavallium.strangedb.database.IBlocksMetadata.EMPTY_BLOCK_ID; import static it.cavallium.strangedb.database.IBlocksMetadata.ERROR_BLOCK_ID; public class DatabaseReferencesMetadata implements IReferencesMetadata { private final AsynchronousFileChannel metaFileChannel; private final int REF_META_BYTES_COUNT = Long.BYTES; private final DatabaseReferencesMetadataCache cache; private long firstFreeReference; public DatabaseReferencesMetadata(Path refMetaFile) throws IOException { metaFileChannel = AsynchronousFileChannel.open(refMetaFile, StandardOpenOption.READ, StandardOpenOption.WRITE); firstFreeReference = metaFileChannel.size() / REF_META_BYTES_COUNT; this.cache = new DatabaseReferencesMetadataCache(this::writeReferenceToDisk); } @Override public long getReference(long reference) throws IOException { if (reference >= firstFreeReference) { return EMPTY_BLOCK_ID; } long block; if ((block = cache.get(reference)) != ERROR_BLOCK_ID) { return block; } ByteBuffer buffer = ByteBuffer.allocate(REF_META_BYTES_COUNT); try { metaFileChannel.read(buffer, reference * REF_META_BYTES_COUNT).get(); } catch (InterruptedException e) { throw new IOException(e); } catch (ExecutionException e) { throw new IOException(e.getCause()); } buffer.flip(); block = buffer.getLong(); if (buffer.limit() != 0 && block != 0xFFFFFFFFFFFFFFFFL) { cache.put(reference, block); return block; } else { cache.put(reference, EMPTY_BLOCK_ID); return EMPTY_BLOCK_ID; } } @Override public long newReference(long blockId) throws IOException { long newReference = firstFreeReference++; cache.put(newReference, blockId); return newReference; } @Override public void editReference(long reference, long blockId) throws IOException { cache.put(reference, blockId); } @Override public void close() throws IOException { cache.close(); metaFileChannel.close(); } @Override public long getFirstFreeReference() { return firstFreeReference; } private Future writeReferenceToDisk(long reference, long blockId) { ByteBuffer data = ByteBuffer.allocate(REF_META_BYTES_COUNT); data.putLong(blockId); data.flip(); return metaFileChannel.write(data, reference * REF_META_BYTES_COUNT); } }