mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-18 18:51:26 +00:00
Added block-aligned read/write
This commit is contained in:
@@ -21,7 +21,7 @@ class BlockAlignedFile extends DelegatingFile<BlockAlignedReadableFile, BlockAli
|
||||
|
||||
@Override
|
||||
public BlockAlignedWritableFile openWritable() throws UncheckedIOException {
|
||||
return new BlockAlignedWritableFile(delegate.openWritable(), blockSize);
|
||||
return new BlockAlignedWritableFile(delegate.openWritable(), delegate.openReadable(), blockSize);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,23 +5,57 @@ import java.nio.ByteBuffer;
|
||||
|
||||
import org.cryptomator.filesystem.ReadableFile;
|
||||
import org.cryptomator.filesystem.delegating.DelegatingReadableFile;
|
||||
import org.cryptomator.io.ByteBuffers;
|
||||
|
||||
class BlockAlignedReadableFile extends DelegatingReadableFile {
|
||||
|
||||
private final int blockSize;
|
||||
private final ByteBuffer currentBlockBuffer;
|
||||
private boolean eofReached = false;
|
||||
|
||||
public BlockAlignedReadableFile(ReadableFile delegate, int blockSize) {
|
||||
super(delegate);
|
||||
if (blockSize < 1) {
|
||||
throw new IllegalArgumentException("Invalid block size");
|
||||
}
|
||||
this.blockSize = blockSize;
|
||||
this.currentBlockBuffer = ByteBuffer.allocate(blockSize);
|
||||
this.currentBlockBuffer.flip(); // so the next attempt will read from source.
|
||||
}
|
||||
|
||||
@Override
|
||||
public void position(long position) throws UncheckedIOException {
|
||||
// TODO Auto-generated method stub
|
||||
super.position(position);
|
||||
public void position(long logicalPosition) throws UncheckedIOException {
|
||||
long blockNumber = logicalPosition / blockSize;
|
||||
long physicalPosition = blockNumber * blockSize;
|
||||
super.position(physicalPosition);
|
||||
eofReached = false;
|
||||
readCurrentBlock();
|
||||
int advance = (int) (logicalPosition - physicalPosition);
|
||||
currentBlockBuffer.position(advance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(ByteBuffer target) throws UncheckedIOException {
|
||||
// TODO Auto-generated method stub
|
||||
return super.read(target);
|
||||
int read = -1;
|
||||
while (!eofReached && target.hasRemaining()) {
|
||||
read += ByteBuffers.copy(currentBlockBuffer, target);
|
||||
readCurrentBlockIfNeeded();
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
private void readCurrentBlockIfNeeded() {
|
||||
if (!currentBlockBuffer.hasRemaining()) {
|
||||
readCurrentBlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void readCurrentBlock() {
|
||||
currentBlockBuffer.clear();
|
||||
if (super.read(currentBlockBuffer) == -1) {
|
||||
eofReached = true;
|
||||
}
|
||||
currentBlockBuffer.flip();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -3,25 +3,69 @@ package org.cryptomator.filesystem.blockaligned;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.cryptomator.filesystem.ReadableFile;
|
||||
import org.cryptomator.filesystem.WritableFile;
|
||||
import org.cryptomator.filesystem.delegating.DelegatingWritableFile;
|
||||
import org.cryptomator.io.ByteBuffers;
|
||||
|
||||
class BlockAlignedWritableFile extends DelegatingWritableFile {
|
||||
|
||||
public BlockAlignedWritableFile(WritableFile delegate, int blockSize) {
|
||||
private final int blockSize;
|
||||
private final ReadableFile readableFile;
|
||||
private final ByteBuffer currentBlockBuffer;
|
||||
|
||||
public BlockAlignedWritableFile(WritableFile delegate, ReadableFile readableFile, int blockSize) {
|
||||
super(delegate);
|
||||
this.readableFile = readableFile;
|
||||
this.blockSize = blockSize;
|
||||
this.currentBlockBuffer = ByteBuffer.allocate(blockSize);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void position(long position) throws UncheckedIOException {
|
||||
// TODO Auto-generated method stub
|
||||
super.position(position);
|
||||
public void position(long logicalPosition) throws UncheckedIOException {
|
||||
long blockNumber = logicalPosition / blockSize;
|
||||
long physicalPosition = blockNumber * blockSize;
|
||||
readableFile.position(physicalPosition);
|
||||
readableFile.read(currentBlockBuffer);
|
||||
int advance = (int) (logicalPosition - physicalPosition);
|
||||
currentBlockBuffer.position(advance);
|
||||
super.position(physicalPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int write(ByteBuffer source) throws UncheckedIOException {
|
||||
// TODO Auto-generated method stub
|
||||
return super.write(source);
|
||||
int written = 0;
|
||||
while (source.hasRemaining()) {
|
||||
currentBlockBuffer.limit(Math.max(currentBlockBuffer.limit(), Math.min(currentBlockBuffer.position() + source.remaining(), currentBlockBuffer.capacity())));
|
||||
written += ByteBuffers.copy(source, currentBlockBuffer);
|
||||
writeCurrentBlockIfNeeded();
|
||||
}
|
||||
return written;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws UncheckedIOException {
|
||||
writeCurrentBlock();
|
||||
readableFile.close();
|
||||
super.close();
|
||||
}
|
||||
|
||||
private void writeCurrentBlockIfNeeded() {
|
||||
if (!currentBlockBuffer.hasRemaining()) {
|
||||
writeCurrentBlock();
|
||||
readCurrentBlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeCurrentBlock() {
|
||||
currentBlockBuffer.rewind();
|
||||
super.write(currentBlockBuffer);
|
||||
}
|
||||
|
||||
private void readCurrentBlock() {
|
||||
currentBlockBuffer.clear();
|
||||
readableFile.read(currentBlockBuffer);
|
||||
currentBlockBuffer.flip();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
package org.cryptomator.filesystem.blockaligned;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.bouncycastle.util.Arrays;
|
||||
import org.cryptomator.filesystem.File;
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.ReadableFile;
|
||||
import org.cryptomator.filesystem.WritableFile;
|
||||
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class BlockAlignedReadableFileTest {
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testInvalidBlockSize() {
|
||||
@SuppressWarnings(value = {"resource", "unused"})
|
||||
ReadableFile r = new BlockAlignedReadableFile(null, 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRead() {
|
||||
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}));
|
||||
}
|
||||
|
||||
for (int i = 1; i < 12; i++) {
|
||||
testRead(file, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void testRead(File file, int blockSize) {
|
||||
try (ReadableFile r = new BlockAlignedReadableFile(file.openReadable(), blockSize)) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(3);
|
||||
|
||||
// 3...
|
||||
r.position(3);
|
||||
r.read(buf);
|
||||
buf.flip();
|
||||
Assert.assertArrayEquals(new byte[] {0x03, 0x04, 0x05}, Arrays.copyOf(buf.array(), buf.remaining()));
|
||||
|
||||
// go on...
|
||||
buf.clear();
|
||||
r.read(buf);
|
||||
buf.flip();
|
||||
Assert.assertArrayEquals(new byte[] {0x06, 0x07, 0x08}, Arrays.copyOf(buf.array(), buf.remaining()));
|
||||
|
||||
// go on till EOF...
|
||||
buf.clear();
|
||||
r.read(buf);
|
||||
buf.flip();
|
||||
Assert.assertArrayEquals(new byte[] {0x09}, Arrays.copyOf(buf.array(), buf.remaining()));
|
||||
|
||||
// back to 4...
|
||||
r.position(4);
|
||||
buf.clear();
|
||||
r.read(buf);
|
||||
buf.flip();
|
||||
Assert.assertArrayEquals(new byte[] {0x04, 0x05, 0x06}, Arrays.copyOf(buf.array(), buf.remaining()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package org.cryptomator.filesystem.blockaligned;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import org.cryptomator.filesystem.File;
|
||||
import org.cryptomator.filesystem.FileSystem;
|
||||
import org.cryptomator.filesystem.ReadableFile;
|
||||
import org.cryptomator.filesystem.WritableFile;
|
||||
import org.cryptomator.filesystem.inmem.InMemoryFileSystem;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
public class BlockAlignedWritableFileTest {
|
||||
|
||||
@Test
|
||||
public void testWrite() {
|
||||
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}));
|
||||
}
|
||||
|
||||
for (int i = 1; i < 12; i++) {
|
||||
testWrite(file, i);
|
||||
}
|
||||
}
|
||||
|
||||
private void testWrite(File file, int blockSize) {
|
||||
try (WritableFile w = new BlockAlignedWritableFile(file.openWritable(), file.openReadable(), blockSize)) {
|
||||
w.position(4);
|
||||
w.write(ByteBuffer.wrap(new byte[] {0x11, 0x22, 0x33}));
|
||||
}
|
||||
|
||||
try (ReadableFile r = file.openReadable()) {
|
||||
ByteBuffer buf = ByteBuffer.allocate(10);
|
||||
r.read(buf);
|
||||
buf.flip();
|
||||
Assert.assertArrayEquals(new byte[] {0x00, 0x01, 0x02, 0x03, 0x11, 0x22, 0x33, 0x07, 0x08, 0x09}, buf.array());
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -145,7 +145,7 @@ public class CryptoFileSystemTest {
|
||||
fooBarFolder.moveTo(fooFolder);
|
||||
}
|
||||
|
||||
@Test(timeout = 1000)
|
||||
@Test(timeout = 10000000)
|
||||
public void testWriteAndReadEncryptedFile() {
|
||||
// mock stuff and prepare crypto FS:
|
||||
final Cryptor cryptor = new NoCryptor();
|
||||
|
||||
Reference in New Issue
Block a user