updated InMemoryFile to support distinct read and write access without mixing up positions and stuff

This commit is contained in:
Sebastian Stenzel
2015-12-29 21:58:47 +01:00
parent 6acbba476b
commit c86068d7bb
4 changed files with 173 additions and 80 deletions

View File

@@ -31,7 +31,7 @@ public final class ByteBuffers {
final ByteBuffer tmp = source.asReadOnlyBuffer();
tmp.limit(tmp.position() + numBytes);
destination.put(tmp);
source.position(tmp.position());
source.position(tmp.position()); // until now only tmp pos has been incremented, so we need to adjust the position
return numBytes;
}

View File

@@ -13,16 +13,17 @@ import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import org.cryptomator.filesystem.File;
import org.cryptomator.filesystem.ReadableFile;
import org.cryptomator.filesystem.WritableFile;
import org.cryptomator.io.ByteBuffers;
class InMemoryFile extends InMemoryNode implements File, ReadableFile, WritableFile {
class InMemoryFile extends InMemoryNode implements File {
private final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
private ByteBuffer content = ByteBuffer.wrap(new byte[0]);
private ByteBuffer content = ByteBuffer.allocate(0);
public InMemoryFile(InMemoryFolder parent, String name, Instant lastModified) {
super(parent, name, lastModified);
@@ -33,14 +34,15 @@ class InMemoryFile extends InMemoryNode implements File, ReadableFile, WritableF
if (!exists()) {
throw new UncheckedIOException(new FileNotFoundException(this.name() + " does not exist"));
}
lock.readLock().lock();
content.rewind();
return this;
final ReadLock readLock = lock.readLock();
readLock.lock();
return new InMemoryReadableFile(this::getContent, readLock);
}
@Override
public WritableFile openWritable() {
lock.writeLock().lock();
final WriteLock writeLock = lock.writeLock();
writeLock.lock();
final InMemoryFolder parent = parent().get();
parent.children.compute(this.name(), (k, v) -> {
if (v != null && v != this) {
@@ -48,94 +50,30 @@ class InMemoryFile extends InMemoryNode implements File, ReadableFile, WritableF
}
return this;
});
return this;
return new InMemoryWritableFile(this::setLastModified, this::getContent, this::setContent, this::delete, writeLock);
}
@Override
public void position(long position) throws UncheckedIOException {
content.position((int) position);
private void setLastModified(Instant lastModified) {
this.lastModified = lastModified;
}
@Override
public int read(ByteBuffer target) {
if (content.hasRemaining()) {
return ByteBuffers.copy(content, target);
} else {
return -1;
}
private ByteBuffer getContent() {
return content;
}
@Override
public int write(ByteBuffer source) {
assert content != null;
final int initialContentPosition = content.position();
expandContentCapacityIfRequired(initialContentPosition + source.remaining());
content.position(initialContentPosition);
assert content.remaining() >= source.remaining();
content.put(source);
return content.position() - initialContentPosition;
private void setContent(ByteBuffer content) {
this.content = content;
}
private void expandContentCapacityIfRequired(int requiredCapacity) {
if (requiredCapacity > content.capacity()) {
final int currentPos = content.position();
final ByteBuffer tmp = ByteBuffer.allocate(requiredCapacity);
content.rewind();
ByteBuffers.copy(content, tmp);
content = tmp;
content.position(currentPos);
}
}
@Override
public void setLastModified(Instant instant) {
this.lastModified = instant;
}
@Override
public void truncate() {
content = ByteBuffer.wrap(new byte[0]);
}
@Override
public void copyTo(WritableFile other) {
content.rewind();
other.truncate();
other.write(content);
}
@Override
public void moveTo(WritableFile other) {
this.copyTo(other);
this.delete();
}
@Override
public void delete() {
private void delete(Void param) {
final InMemoryFolder parent = parent().get();
parent.children.computeIfPresent(this.name(), (k, v) -> {
truncate();
// returning null removes the entry.
return null;
});
assert!this.exists();
}
@Override
public boolean isOpen() {
return lock.isWriteLockedByCurrentThread() || lock.getReadHoldCount() > 0;
}
@Override
public void close() {
if (lock.isWriteLockedByCurrentThread()) {
this.setLastModified(Instant.now());
lock.writeLock().unlock();
} else if (lock.getReadHoldCount() > 0) {
lock.readLock().unlock();
}
}
@Override
public String toString() {
return parent.toString() + name;

View File

@@ -0,0 +1,62 @@
package org.cryptomator.filesystem.inmem;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.function.Supplier;
import org.cryptomator.filesystem.ReadableFile;
import org.cryptomator.filesystem.WritableFile;
import org.cryptomator.io.ByteBuffers;
class InMemoryReadableFile implements ReadableFile {
private final Supplier<ByteBuffer> contentGetter;
private final ReadLock readLock;
private boolean open = true;
private int position = 0;
public InMemoryReadableFile(Supplier<ByteBuffer> contentGetter, ReadLock readLock) {
this.contentGetter = contentGetter;
this.readLock = readLock;
}
@Override
public boolean isOpen() {
return open;
}
@Override
public void copyTo(WritableFile other) throws UncheckedIOException {
ByteBuffer source = contentGetter.get().asReadOnlyBuffer();
source.position(position);
this.position += other.write(source);
}
@Override
public int read(ByteBuffer destination) throws UncheckedIOException {
ByteBuffer source = contentGetter.get().asReadOnlyBuffer();
if (position >= source.limit()) {
return -1;
} else {
source.position(position);
assert source.hasRemaining();
int numRead = ByteBuffers.copy(source, destination);
this.position += numRead;
return numRead;
}
}
@Override
public void position(long position) throws UncheckedIOException {
assert position < Integer.MAX_VALUE : "Can not use that big in-memory files.";
this.position = (int) position;
}
@Override
public void close() throws UncheckedIOException {
open = false;
readLock.unlock();
}
}

View File

@@ -0,0 +1,93 @@
package org.cryptomator.filesystem.inmem;
import java.io.UncheckedIOException;
import java.nio.ByteBuffer;
import java.time.Instant;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.function.Consumer;
import java.util.function.Supplier;
import org.cryptomator.filesystem.WritableFile;
import org.cryptomator.io.ByteBuffers;
public class InMemoryWritableFile implements WritableFile {
private final Consumer<Instant> lastModifiedSetter;
private final Supplier<ByteBuffer> contentGetter;
private final Consumer<ByteBuffer> contentSetter;
private final Consumer<Void> deleter;
private final WriteLock writeLock;
private boolean open;
private int position = 0;
public InMemoryWritableFile(Consumer<Instant> lastModifiedSetter, Supplier<ByteBuffer> contentGetter, Consumer<ByteBuffer> contentSetter, Consumer<Void> deleter, WriteLock writeLock) {
this.lastModifiedSetter = lastModifiedSetter;
this.contentGetter = contentGetter;
this.contentSetter = contentSetter;
this.deleter = deleter;
this.writeLock = writeLock;
}
@Override
public boolean isOpen() {
return open;
}
@Override
public void moveTo(WritableFile other) throws UncheckedIOException {
if (other instanceof InMemoryWritableFile) {
InMemoryWritableFile destination = (InMemoryWritableFile) other;
destination.contentSetter.accept(this.contentGetter.get());
destination.contentGetter.get().rewind();
}
deleter.accept(null);
}
@Override
public void setLastModified(Instant instant) throws UncheckedIOException {
lastModifiedSetter.accept(instant);
}
@Override
public void delete() throws UncheckedIOException {
deleter.accept(null);
open = false;
}
@Override
public void truncate() throws UncheckedIOException {
contentSetter.accept(ByteBuffer.allocate(0));
}
@Override
public int write(ByteBuffer source) throws UncheckedIOException {
ByteBuffer destination = contentGetter.get();
int requiredSize = position + source.remaining();
if (destination.capacity() < requiredSize) {
ByteBuffer old = destination;
old.rewind();
destination = ByteBuffer.allocate(requiredSize);
ByteBuffers.copy(old, destination);
contentSetter.accept(destination);
}
destination.position(position);
int numWritten = ByteBuffers.copy(source, destination);
this.position += numWritten;
return numWritten;
}
@Override
public void position(long position) throws UncheckedIOException {
assert position < Integer.MAX_VALUE : "Can not use that big in-memory files.";
this.position = (int) position;
}
@Override
public void close() throws UncheckedIOException {
open = false;
writeLock.unlock();
lastModifiedSetter.accept(Instant.now());
}
}