mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-29 16:10:19 +00:00
Increased performance of non-random-access read/write by switching to block-aligned mode only when necessary.
This commit is contained in:
@@ -20,6 +20,11 @@ class BlockAlignedReadableFile extends DelegatingReadableFile {
|
||||
private final int blockSize;
|
||||
private final ByteBuffer currentBlockBuffer;
|
||||
private boolean eofReached = false;
|
||||
private Mode mode = Mode.PASSTHROUGH;
|
||||
|
||||
private enum Mode {
|
||||
BLOCK_ALIGNED, PASSTHROUGH;
|
||||
}
|
||||
|
||||
public BlockAlignedReadableFile(ReadableFile delegate, int blockSize) {
|
||||
super(delegate);
|
||||
@@ -28,11 +33,12 @@ class BlockAlignedReadableFile extends DelegatingReadableFile {
|
||||
}
|
||||
this.blockSize = blockSize;
|
||||
this.currentBlockBuffer = ByteBuffer.allocate(blockSize);
|
||||
this.currentBlockBuffer.flip(); // so the next attempt will read from source.
|
||||
this.currentBlockBuffer.flip(); // so remaining() is 0 -> next read will read from physical source.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void position(long logicalPosition) throws UncheckedIOException {
|
||||
switchToBlockAlignedMode();
|
||||
long blockNumber = logicalPosition / blockSize;
|
||||
long physicalPosition = blockNumber * blockSize;
|
||||
assert physicalPosition <= logicalPosition;
|
||||
@@ -45,8 +51,24 @@ class BlockAlignedReadableFile extends DelegatingReadableFile {
|
||||
currentBlockBuffer.position(diff);
|
||||
}
|
||||
|
||||
// visible for testing
|
||||
void switchToBlockAlignedMode() {
|
||||
mode = Mode.BLOCK_ALIGNED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuffer target) throws UncheckedIOException {
|
||||
switch (mode) {
|
||||
case PASSTHROUGH:
|
||||
return super.read(target);
|
||||
case BLOCK_ALIGNED:
|
||||
return readBlockAligned(target);
|
||||
default:
|
||||
throw new IllegalStateException("Unsupported mode " + mode);
|
||||
}
|
||||
}
|
||||
|
||||
private int readBlockAligned(ByteBuffer target) {
|
||||
int read = -1;
|
||||
while (!eofReached && target.hasRemaining()) {
|
||||
read += ByteBuffers.copy(currentBlockBuffer, target);
|
||||
|
||||
@@ -21,6 +21,11 @@ class BlockAlignedWritableFile extends DelegatingWritableFile {
|
||||
private final int blockSize;
|
||||
private final ReadableFile readableFile;
|
||||
private final ByteBuffer currentBlockBuffer;
|
||||
private Mode mode = Mode.PASSTHROUGH;
|
||||
|
||||
private enum Mode {
|
||||
BLOCK_ALIGNED, PASSTHROUGH;
|
||||
}
|
||||
|
||||
public BlockAlignedWritableFile(WritableFile delegate, ReadableFile readableFile, int blockSize) {
|
||||
super(delegate);
|
||||
@@ -31,6 +36,7 @@ class BlockAlignedWritableFile extends DelegatingWritableFile {
|
||||
|
||||
@Override
|
||||
public void position(long logicalPosition) throws UncheckedIOException {
|
||||
switchToBlockAlignedMode();
|
||||
long blockNumber = logicalPosition / blockSize;
|
||||
long physicalPosition = blockNumber * blockSize;
|
||||
readableFile.position(physicalPosition);
|
||||
@@ -40,8 +46,24 @@ class BlockAlignedWritableFile extends DelegatingWritableFile {
|
||||
super.position(physicalPosition);
|
||||
}
|
||||
|
||||
// visible for testing
|
||||
void switchToBlockAlignedMode() {
|
||||
mode = Mode.BLOCK_ALIGNED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(ByteBuffer source) throws UncheckedIOException {
|
||||
switch (mode) {
|
||||
case PASSTHROUGH:
|
||||
return super.write(source);
|
||||
case BLOCK_ALIGNED:
|
||||
return writeBlockAligned(source);
|
||||
default:
|
||||
throw new IllegalStateException("Unsupported mode " + mode);
|
||||
}
|
||||
}
|
||||
|
||||
private int writeBlockAligned(ByteBuffer source) {
|
||||
int written = 0;
|
||||
while (source.hasRemaining()) {
|
||||
written += ByteBuffers.copy(source, currentBlockBuffer);
|
||||
|
||||
@@ -18,6 +18,7 @@ import org.cryptomator.filesystem.WritableFile;
|
||||
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
public class BlockAlignedReadableFileTest {
|
||||
|
||||
@@ -27,6 +28,26 @@ public class BlockAlignedReadableFileTest {
|
||||
ReadableFile r = new BlockAlignedReadableFile(null, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSwitchingModes() {
|
||||
FileSystem fs = new InMemoryFileSystem();
|
||||
File file = fs.file("test");
|
||||
try (WritableFile w = file.openWritable()) {
|
||||
w.write(ByteBuffer.wrap(new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}));
|
||||
}
|
||||
|
||||
BlockAlignedReadableFile readable = Mockito.spy(new BlockAlignedReadableFile(file.openReadable(), 2));
|
||||
ByteBuffer firstRead = ByteBuffer.allocate(4);
|
||||
readable.read(firstRead);
|
||||
Mockito.verify(readable, Mockito.never()).switchToBlockAlignedMode();
|
||||
readable.position(0);
|
||||
Mockito.verify(readable).switchToBlockAlignedMode();
|
||||
ByteBuffer secondRead = ByteBuffer.allocate(4);
|
||||
readable.read(secondRead);
|
||||
Assert.assertArrayEquals(firstRead.array(), secondRead.array());
|
||||
readable.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead() {
|
||||
FileSystem fs = new InMemoryFileSystem();
|
||||
|
||||
@@ -17,9 +17,34 @@ import org.cryptomator.filesystem.WritableFile;
|
||||
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mockito;
|
||||
|
||||
public class BlockAlignedWritableFileTest {
|
||||
|
||||
@Test
|
||||
public void testSwitchingModes() {
|
||||
FileSystem fs = new InMemoryFileSystem();
|
||||
File file = fs.file("test");
|
||||
try (WritableFile w = file.openWritable()) {
|
||||
w.write(ByteBuffer.wrap(new byte[] {0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}));
|
||||
}
|
||||
|
||||
BlockAlignedWritableFile writable = Mockito.spy(new BlockAlignedWritableFile(file.openWritable(), file.openReadable(), 2));
|
||||
writable.write(ByteBuffer.wrap(new byte[] {0x11, 0x12, 0x13}));
|
||||
Mockito.verify(writable, Mockito.never()).switchToBlockAlignedMode();
|
||||
writable.position(1);
|
||||
Mockito.verify(writable).switchToBlockAlignedMode();
|
||||
writable.write(ByteBuffer.wrap(new byte[] {0x14, 0x15, 0x16}));
|
||||
writable.close();
|
||||
|
||||
try (ReadableFile r = file.openReadable()) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(10);
|
||||
r.read(buf);
|
||||
buf.flip();
|
||||
Assert.assertArrayEquals(new byte[] {0x11, 0x14, 0x15, 0x16, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09}, buf.array());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrite() {
|
||||
FileSystem fs = new InMemoryFileSystem();
|
||||
|
||||
Reference in New Issue
Block a user