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 org.mapdb.DataInput2; import java.io.File; import java.nio.ByteBuffer; import java.util.Arrays; import java.util.concurrent.locks.ReentrantLock; /** * Created by jan on 2/29/16. */ public final class ByteArrayVol extends Volume { 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 if bellow 2GB return new org.mapdb.volume.ByteArrayVol(sliceShift, initSize); } @NotNull @Override public boolean exists(@Nullable String file) { return false; } @Override public boolean handlesReadonly() { return false; } }; protected final ReentrantLock growLock = new ReentrantLock(); protected final int sliceShift; protected final int sliceSizeModMask; protected final int sliceSize; protected volatile byte[][] slices = new byte[0][]; public ByteArrayVol() { this(CC.PAGE_SHIFT, 0L); } public ByteArrayVol(int sliceShift, long initSize) { this.sliceShift = sliceShift; this.sliceSize = 1 << sliceShift; this.sliceSizeModMask = sliceSize - 1; if (initSize != 0) { ensureAvailable(initSize); } } protected final byte[] getSlice(long offset) { byte[][] slices = this.slices; int pos = ((int) (offset >>> sliceShift)); if (pos >= slices.length) throw new DBException.VolumeEOF("offset points beyond slices"); return slices[pos]; } @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; byte[][] slices2 = slices; slices2 = Arrays.copyOf(slices2, slicePos); for (int pos = oldSize; pos < slices2.length; pos++) { slices2[pos] = new byte[sliceSize]; } 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; slices = Arrays.copyOf(slices, maxSize); } finally { growLock.unlock(); } } @Override public void putLong(long offset, long v) { int pos = (int) (offset & sliceSizeModMask); byte[] buf = getSlice(offset); DataIO.putLong(buf, pos, v); } @Override public void putInt(long offset, int value) { int pos = (int) (offset & sliceSizeModMask); byte[] buf = getSlice(offset); buf[pos++] = (byte) (0xff & (value >> 24)); buf[pos++] = (byte) (0xff & (value >> 16)); buf[pos++] = (byte) (0xff & (value >> 8)); buf[pos++] = (byte) (0xff & (value)); } @Override public void putByte(long offset, byte value) { final byte[] b = getSlice(offset); b[((int) (offset & sliceSizeModMask))] = value; } @Override public void putData(long offset, byte[] src, int srcPos, int srcSize) { int pos = (int) (offset & sliceSizeModMask); byte[] buf = getSlice(offset); System.arraycopy(src, srcPos, buf, pos, srcSize); } @Override public void putData(long offset, ByteBuffer buf) { int pos = (int) (offset & sliceSizeModMask); byte[] dst = getSlice(offset); buf.get(dst, pos, buf.remaining()); } @Override public void copyTo(long inputOffset, Volume target, long targetOffset, long size) { int pos = (int) (inputOffset & sliceSizeModMask); byte[] buf = getSlice(inputOffset); //TODO size>Integer.MAX_VALUE target.putData(targetOffset, buf, pos, (int) size); } @Override public void putDataOverlap(long offset, byte[] data, int pos, int len) { boolean overlap = (offset >>> sliceShift != (offset + len) >>> sliceShift); if (overlap) { while (len > 0) { byte[] b = getSlice(offset); int pos2 = (int) (offset & sliceSizeModMask); int toPut = Math.min(len, sliceSize - pos2); System.arraycopy(data, pos, b, pos2, toPut); pos += toPut; len -= toPut; offset += toPut; } } else { putData(offset, data, pos, len); } } @Override public DataInput2 getDataInputOverlap(long offset, int size) { boolean overlap = (offset >>> sliceShift != (offset + size) >>> sliceShift); if (overlap) { byte[] bb = new byte[size]; final int origLen = size; while (size > 0) { byte[] b = getSlice(offset); int pos = (int) (offset & sliceSizeModMask); int toPut = Math.min(size, sliceSize - pos); System.arraycopy(b, pos, bb, origLen - size, toPut); size -= toPut; offset += toPut; } return new DataInput2.ByteArray(bb); } else { //return mapped buffer return getDataInput(offset, size); } } @Override public void clear(long startOffset, long endOffset) { if (CC.ASSERT && (startOffset >>> sliceShift) != ((endOffset - 1) >>> sliceShift)) throw new AssertionError(); byte[] buf = getSlice(startOffset); int start = (int) (startOffset & sliceSizeModMask); int end = (int) (start + (endOffset - startOffset)); int pos = start; while (pos < end) { System.arraycopy(CLEAR, 0, buf, pos, Math.min(CLEAR.length, end - pos)); pos += CLEAR.length; } } @Override public long getLong(long offset) { int pos = (int) (offset & sliceSizeModMask); byte[] buf = getSlice(offset); return DataIO.getLong(buf, pos); } @Override public int getInt(long offset) { int pos = (int) (offset & sliceSizeModMask); byte[] buf = getSlice(offset); //TODO verify loop final int end = pos + 4; int ret = 0; for (; pos < end; pos++) { ret = (ret << 8) | (buf[pos] & 0xFF); } return ret; } @Override public byte getByte(long offset) { final byte[] b = getSlice(offset); return b[((int) (offset & sliceSizeModMask))]; } @Override public DataInput2 getDataInput(long offset, int size) { int pos = (int) (offset & sliceSizeModMask); byte[] buf = getSlice(offset); return new DataInput2.ByteArray(buf, pos); } @Override public void getData(long offset, byte[] bytes, int bytesPos, int length) { int pos = (int) (offset & sliceSizeModMask); byte[] buf = getSlice(offset); System.arraycopy(buf, pos, bytes, bytesPos, length); } @Override public void close() { closed.set(true); slices = null; } @Override public void sync() { } @Override public int sliceSize() { return sliceSize; } @Override public boolean isSliced() { return true; } @Override public long length() { return ((long) slices.length) * sliceSize; } @Override public boolean isReadOnly() { return false; } @Override public File getFile() { return null; } @Override public boolean getFileLocked() { return false; } }