package org.mapdb.volume;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.mapdb.CC;
import org.mapdb.DBException;
import org.mapdb.DataIO;
import java.io.File;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.MappedByteBuffer;
import java.util.Arrays;
/**
* Created by jan on 3/13/16.
*/
public final class ByteBufferMemoryVol extends ByteBufferVol {
/**
* factory for DirectByteBuffer storage
*/
public static final VolumeFactory FACTORY = new VolumeFactory() {
@Override
public Volume makeVolume(String file, boolean readOnly, long fileLockWait, int sliceShift, long initSize, boolean fixedSize) {
//TODO optimize for fixedSize smaller than 2GB
return new ByteBufferMemoryVol(true, sliceShift, false, initSize);
}
@NotNull
@Override
public boolean exists(@Nullable String file) {
return false;
}
@Override
public boolean handlesReadonly() {
return false;
}
};
/**
* factory for DirectByteBuffer storage
*/
public static final VolumeFactory FACTORY_WITH_CLEANER_HACK = new VolumeFactory() {
@Override
public Volume makeVolume(String file, boolean readOnly, long fileLockWait, int sliceShift, long initSize, boolean fixedSize) {//TODO prealocate initSize
//TODO optimize for fixedSize smaller than 2GB
return new ByteBufferMemoryVol(true, sliceShift, true, initSize);
}
@NotNull
@Override
public boolean exists(@Nullable String file) {
return false;
}
@Override
public boolean handlesReadonly() {
return false;
}
};
protected final boolean useDirectBuffer;
@Override
public String toString() {
return super.toString() + ",direct=" + useDirectBuffer;
}
public ByteBufferMemoryVol(final boolean useDirectBuffer, final int sliceShift, boolean cleanerHackEnabled, long initSize) {
super(false, sliceShift, cleanerHackEnabled);
this.useDirectBuffer = useDirectBuffer;
if (initSize != 0)
ensureAvailable(initSize);
}
@Override
public final void ensureAvailable(long offset) {
offset = DataIO.roundUp(offset, 1L << sliceShift);
int slicePos = (int) (offset >>> sliceShift);
//check for most common case, this is already mapped
if (slicePos < slices.length) {
return;
}
growLock.lock();
try {
//check second time
if (slicePos <= slices.length)
return;
int oldSize = slices.length;
ByteBuffer[] slices2 = slices;
slices2 = Arrays.copyOf(slices2, slicePos);
for (int pos = oldSize; pos < slices2.length; pos++) {
ByteBuffer b = useDirectBuffer ?
ByteBuffer.allocateDirect(sliceSize) :
ByteBuffer.allocate(sliceSize);
if (CC.ASSERT && b.order() != ByteOrder.BIG_ENDIAN)
throw new AssertionError("little-endian");
slices2[pos] = b;
}
slices = slices2;
} catch (OutOfMemoryError e) {
throw new DBException.OutOfMemory(e);
} finally {
growLock.unlock();
}
}
@Override
public void truncate(long size) {
final int maxSize = 1 + (int) (size >>> sliceShift);
if (maxSize == slices.length)
return;
if (maxSize > slices.length) {
ensureAvailable(size);
return;
}
growLock.lock();
try {
if (maxSize >= slices.length)
return;
ByteBuffer[] old = slices;
slices = Arrays.copyOf(slices, maxSize);
//unmap remaining buffers
for (int i = maxSize; i < old.length; i++) {
if (cleanerHackEnabled && old[i] instanceof MappedByteBuffer)
unmap((MappedByteBuffer) old[i]);
old[i] = null;
}
} finally {
growLock.unlock();
}
}
@Override
public void close() {
if (!closed.compareAndSet(false,true))
return;
growLock.lock();
try {
if (cleanerHackEnabled) {
for (ByteBuffer b : slices) {
if (b != null && (b instanceof MappedByteBuffer)) {
unmap((MappedByteBuffer) b);
}
}
}
Arrays.fill(slices, null);
slices = null;
} finally {
growLock.unlock();
}
}
@Override
public void sync() {
}
@Override
public long length() {
return ((long) slices.length) * sliceSize;
}
@Override
public boolean isReadOnly() {
return readOnly;
}
@Override
public File getFile() {
return null;
}
@Override
public boolean getFileLocked() {
return false;
}
}