Преглед на файлове

Increased performance of non-random-access read/write by switching to block-aligned mode only when necessary.

Sebastian Stenzel преди 9 години
родител
ревизия
a8f53b7084

+ 23 - 1
main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedReadableFile.java

@@ -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);

+ 22 - 0
main/filesystem-crypto/src/main/java/org/cryptomator/filesystem/blockaligned/BlockAlignedWritableFile.java

@@ -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);

+ 21 - 0
main/filesystem-crypto/src/test/java/org/cryptomator/filesystem/blockaligned/BlockAlignedReadableFileTest.java

@@ -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();

+ 25 - 0
main/filesystem-crypto/src/test/java/org/cryptomator/filesystem/blockaligned/BlockAlignedWritableFileTest.java

@@ -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();