aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jimfs/src/main/java/com/google/jimfs/internal/BlockList.java110
-rw-r--r--jimfs/src/main/java/com/google/jimfs/internal/ByteStore.java431
-rw-r--r--jimfs/src/main/java/com/google/jimfs/internal/DirectMemoryDisk.java143
-rw-r--r--jimfs/src/main/java/com/google/jimfs/internal/FileContent.java3
-rw-r--r--jimfs/src/main/java/com/google/jimfs/internal/FileFactory.java7
-rw-r--r--jimfs/src/main/java/com/google/jimfs/internal/HeapDisk.java154
-rw-r--r--jimfs/src/main/java/com/google/jimfs/internal/HeapMemoryDisk.java110
-rw-r--r--jimfs/src/main/java/com/google/jimfs/internal/JimfsFileStore.java6
-rw-r--r--jimfs/src/main/java/com/google/jimfs/internal/JimfsFileSystems.java4
-rw-r--r--jimfs/src/main/java/com/google/jimfs/internal/MemoryDisk.java178
-rw-r--r--jimfs/src/main/java/com/google/jimfs/internal/MemoryDiskByteStore.java396
-rw-r--r--jimfs/src/main/java/com/google/jimfs/internal/Util.java38
-rw-r--r--jimfs/src/main/java/com/google/jimfs/internal/package-info.java10
-rw-r--r--jimfs/src/test/java/com/google/jimfs/JimfsUnixLikeFileSystemTest.java8
-rw-r--r--jimfs/src/test/java/com/google/jimfs/internal/AbstractByteStoreTest.java122
-rw-r--r--jimfs/src/test/java/com/google/jimfs/internal/DirectoryTableTest.java9
-rw-r--r--jimfs/src/test/java/com/google/jimfs/internal/FileFactoryTest.java52
-rw-r--r--jimfs/src/test/java/com/google/jimfs/internal/FileTest.java5
-rw-r--r--jimfs/src/test/java/com/google/jimfs/internal/FileTreeTest.java3
-rw-r--r--jimfs/src/test/java/com/google/jimfs/internal/HeapDiskReuseTest.java16
-rw-r--r--jimfs/src/test/java/com/google/jimfs/internal/HeapDiskReuseWithDeleteTest.java4
-rw-r--r--jimfs/src/test/java/com/google/jimfs/internal/HeapDiskTest.java6
-rw-r--r--jimfs/src/test/java/com/google/jimfs/internal/InternalTestUtils.java (renamed from jimfs/src/test/java/com/google/jimfs/internal/DirectDiskTest.java)20
-rw-r--r--jimfs/src/test/java/com/google/jimfs/internal/JimfsAsynchronousFileChannelTest.java56
-rw-r--r--jimfs/src/test/java/com/google/jimfs/internal/JimfsFileChannelTest.java55
-rw-r--r--jimfs/src/test/java/com/google/jimfs/internal/JimfsInputStreamTest.java7
-rw-r--r--jimfs/src/test/java/com/google/jimfs/internal/JimfsOutputStreamTest.java5
-rw-r--r--jimfs/src/test/java/com/google/jimfs/internal/StubByteStore.java162
28 files changed, 853 insertions, 1267 deletions
diff --git a/jimfs/src/main/java/com/google/jimfs/internal/BlockList.java b/jimfs/src/main/java/com/google/jimfs/internal/BlockList.java
new file mode 100644
index 0000000..338d0ea
--- /dev/null
+++ b/jimfs/src/main/java/com/google/jimfs/internal/BlockList.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.jimfs.internal;
+
+import static com.google.jimfs.internal.Util.clear;
+import static com.google.jimfs.internal.Util.nextPowerOf2;
+
+import java.util.Arrays;
+
+/**
+ * Simple list of byte array blocks. Blocks can only be added and removed at the end of the list.
+ *
+ * @author Colin Decker
+ */
+final class BlockList {
+
+ private byte[][] blocks;
+ private int head;
+
+ /**
+ * Creates a new list with a default initial capacity.
+ */
+ public BlockList() {
+ this(32);
+ }
+
+ /**
+ * Creates a new list with the given initial capacity.
+ */
+ public BlockList(int initialCapacity) {
+ this.blocks = new byte[initialCapacity][];
+ }
+
+ private void expandIfNecessary(int minSize) {
+ if (minSize > blocks.length) {
+ this.blocks = Arrays.copyOf(blocks, nextPowerOf2(minSize));
+ }
+ }
+
+ /**
+ * Returns true if size is 0.
+ */
+ public boolean isEmpty() {
+ return head == 0;
+ }
+
+ /**
+ * Returns the number of blocks this list contains.
+ */
+ public int size() {
+ return head;
+ }
+
+ /**
+ * Copies the last {@code count} blocks from this list to the end of the given target list.
+ */
+ public void copyTo(BlockList target, int count) {
+ int start = head - count;
+ int targetEnd = target.head + count;
+ target.expandIfNecessary(targetEnd);
+
+ System.arraycopy(this.blocks, start, target.blocks, target.head, count);
+ target.head = targetEnd;
+ }
+
+ /**
+ * Transfers the last {@code count} blocks from this list to the end of the given target list.
+ */
+ public void transferTo(BlockList target, int count) {
+ copyTo(target, count);
+ truncate(head - count);
+ }
+
+ /**
+ * Truncates this list to the given size.
+ */
+ public void truncate(int size) {
+ clear(blocks, size, head - size);
+ head = size;
+ }
+
+ /**
+ * Adds the given block to the end of this list.
+ */
+ public void add(byte[] block) {
+ expandIfNecessary(head + 1);
+ blocks[head++] = block;
+ }
+
+ /**
+ * Gets the block at the given index in this list.
+ */
+ public byte[] get(int index) {
+ return blocks[index];
+ }
+}
diff --git a/jimfs/src/main/java/com/google/jimfs/internal/ByteStore.java b/jimfs/src/main/java/com/google/jimfs/internal/ByteStore.java
index 07dcb20..ab114ba 100644
--- a/jimfs/src/main/java/com/google/jimfs/internal/ByteStore.java
+++ b/jimfs/src/main/java/com/google/jimfs/internal/ByteStore.java
@@ -16,8 +16,11 @@
package com.google.jimfs.internal;
+import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
+import com.google.common.primitives.UnsignedBytes;
+
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
@@ -32,24 +35,40 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
*
* @author Colin Decker
*/
-abstract class ByteStore implements FileContent {
+final class ByteStore implements FileContent {
private final ReadWriteLock lock = new ReentrantReadWriteLock();
+ private final HeapDisk disk;
+ private final BlockList blocks;
+ private long size;
+
+ public ByteStore(HeapDisk disk) {
+ this(disk, new BlockList(32), 0);
+ }
+
+ private ByteStore(HeapDisk disk, BlockList blocks, long size) {
+ this.disk = checkNotNull(disk);
+ this.blocks = checkNotNull(blocks);
+
+ checkArgument(size >= 0);
+ this.size = size;
+ }
+
private int openCount = 0;
private boolean deleted = false;
/**
* Returns the read lock for this store.
*/
- protected final Lock readLock() {
+ public final Lock readLock() {
return lock.readLock();
}
/**
* Returns the write lock for this store.
*/
- protected final Lock writeLock() {
+ public final Lock writeLock() {
return lock.writeLock();
}
@@ -57,27 +76,41 @@ abstract class ByteStore implements FileContent {
* Gets the current size of this store in bytes. Does not do locking, so should only be called
* when holding a lock.
*/
- public abstract long currentSize();
+ public long currentSize() {
+ return size;
+ }
/**
* Creates a copy of this byte store.
+ *
+ * @throws IOException if the disk cannot allocate enough new blocks to create a copy
*/
- protected abstract ByteStore createCopy();
+ protected ByteStore createCopy() throws IOException {
+ BlockList copyBlocks = new BlockList(Math.max(blocks.size() * 2, 32));
+ disk.allocate(copyBlocks, blocks.size());
+
+ for (int i = 0; i < blocks.size(); i++) {
+ byte[] block = blocks.get(i);
+ byte[] copy = copyBlocks.get(i);
+ System.arraycopy(block, 0, copy, 0, block.length);
+ }
+ return new ByteStore(disk, copyBlocks, size);
+ }
// need to lock in these methods since they're defined by an interface
@Override
- public final long size() {
+ public long size() {
readLock().lock();
try {
- return currentSize();
+ return size;
} finally {
readLock().unlock();
}
}
@Override
- public final ByteStore copy() {
+ public ByteStore copy() throws IOException {
readLock().lock();
try {
return createCopy();
@@ -91,7 +124,7 @@ abstract class ByteStore implements FileContent {
/**
* Called when a stream or channel to this store is opened.
*/
- public final synchronized void opened() {
+ public synchronized void opened() {
openCount++;
}
@@ -99,7 +132,7 @@ abstract class ByteStore implements FileContent {
* Called when a stream or channel to this store is closed. If there are no more streams or
* channels open to the store and it has been deleted, its contents may be deleted.
*/
- public final synchronized void closed() {
+ public synchronized void closed() {
if (--openCount == 0 && deleted) {
deleteContents();
}
@@ -119,7 +152,7 @@ abstract class ByteStore implements FileContent {
* contents are deleted if necessary.
*/
@Override
- public final synchronized void deleted() {
+ public synchronized void deleted() {
deleted = true;
if (openCount == 0) {
deleteContents();
@@ -130,7 +163,10 @@ abstract class ByteStore implements FileContent {
* Deletes the contents of this store. Called when the file that contains this store has been
* deleted and all open streams and channels to the file have been closed.
*/
- protected abstract void deleteContents();
+ protected void deleteContents() {
+ disk.free(blocks);
+ size = 0;
+ }
/**
* Truncates this store to the given {@code size}. If the given size is less than the current size
@@ -139,38 +175,167 @@ abstract class ByteStore implements FileContent {
* does nothing. Returns {@code true} if this store was modified by the call (its size changed)
* and {@code false} otherwise.
*/
- public abstract boolean truncate(long size);
+ public boolean truncate(long size) {
+ if (size >= this.size) {
+ return false;
+ }
+
+ long lastPosition = size - 1;
+ this.size = size;
+
+ int newBlockCount = blockIndex(lastPosition) + 1;
+ int blocksToRemove = blocks.size() - newBlockCount;
+ if (blocksToRemove > 0) {
+ disk.free(blocks, blocksToRemove);
+ }
+
+ return true;
+ }
+
+ /**
+ * Prepares for a write of len bytes starting at position pos.
+ */
+ private void prepareForWrite(long pos, long len) throws IOException {
+ long end = pos + len;
+
+ // allocate any additional blocks needed
+ int lastBlockIndex = blocks.size() - 1;
+ int endBlockIndex = blockIndex(end - 1);
+
+ if (endBlockIndex > lastBlockIndex) {
+ int additionalBlocksNeeded = endBlockIndex - lastBlockIndex;
+ disk.allocate(blocks, additionalBlocksNeeded);
+ }
+
+ // zero bytes between current size and pos
+ if (pos > size) {
+ long remaining = pos - size;
+
+ int blockIndex = blockIndex(size);
+ byte[] block = blocks.get(blockIndex);
+ int off = offsetInBlock(size);
+
+ remaining -= zero(block, off, length(off, remaining));
+
+ while (remaining > 0) {
+ block = blocks.get(++blockIndex);
+
+ remaining -= zero(block, 0, length(remaining));
+ }
+
+ size = pos;
+ }
+ }
/**
* Writes the given byte to this store at position {@code pos}. {@code pos} may be greater than
* the current size of this store, in which case this store is resized and all bytes between the
* current size and {@code pos} are set to 0. Returns the number of bytes written.
+ *
+ * @throws IOException if the store needs more blocks but the disk is full
*/
- public abstract int write(long pos, byte b);
+ public int write(long pos, byte b) throws IOException {
+ prepareForWrite(pos, 1);
+
+ byte[] block = blocks.get(blockIndex(pos));
+ int off = offsetInBlock(pos);
+ block[off] = b;
+
+ if (pos >= size) {
+ size = pos + 1;
+ }
+
+ return 1;
+ }
/**
* Writes {@code len} bytes starting at offset {@code off} in the given byte array to this store
* starting at position {@code pos}. {@code pos} may be greater than the current size of this
* store, in which case this store is resized and all bytes between the current size and {@code
* pos} are set to 0. Returns the number of bytes written.
+ *
+ * @throws IOException if the store needs more blocks but the disk is full
*/
- public abstract int write(long pos, byte[] b, int off, int len);
+ public int write(long pos, byte[] b, int off, int len) throws IOException {
+ prepareForWrite(pos, len);
+
+ if (len == 0) {
+ return 0;
+ }
+
+ int remaining = len;
+
+ int blockIndex = blockIndex(pos);
+ byte[] block = blockForWrite(blockIndex);
+ int offInBlock = offsetInBlock(pos);
+
+ int written = put(block, offInBlock, b, off, length(offInBlock, remaining));
+ remaining -= written;
+ off += written;
+
+ while (remaining > 0) {
+ block = blocks.get(++blockIndex);
+
+ written = put(block, 0, b, off, length(remaining));
+ remaining -= written;
+ off += written;
+ }
+
+ long newPos = pos + len;
+ if (newPos > size) {
+ size = newPos;
+ }
+
+ return len;
+ }
/**
* Writes all available bytes from buffer {@code buf} to this store starting at position {@code
* pos}. {@code pos} may be greater than the current size of this store, in which case this store
* is resized and all bytes between the current size and {@code pos} are set to 0. Returns the
* number of bytes written.
+ *
+ * @throws IOException if the store needs more blocks but the disk is full
*/
- public abstract int write(long pos, ByteBuffer buf);
+ public int write(long pos, ByteBuffer buf) throws IOException {
+ int len = buf.remaining();
+
+ prepareForWrite(pos, len);
+
+ if (len == 0) {
+ return 0;
+ }
+
+ int bytesToWrite = buf.remaining();
+
+ int blockIndex = blockIndex(pos);
+ byte[] block = blockForWrite(blockIndex);
+ int off = offsetInBlock(pos);
+
+ put(block, off, buf);
+
+ while (buf.hasRemaining()) {
+ block = blocks.get(++blockIndex);
+
+ put(block, 0, buf);
+ }
+
+ if (pos + bytesToWrite > size) {
+ size = pos + bytesToWrite;
+ }
+
+ return bytesToWrite;
+ }
/**
* Writes all available bytes from each buffer in {@code bufs}, in order, to this store starting
* at position {@code pos}. {@code pos} may be greater than the current size of this store, in
* which case this store is resized and all bytes between the current size and {@code pos} are set
* to 0. Returns the number of bytes written.
+ *
+ * @throws IOException if the store needs more blocks but the disk is full
*/
- public long write(long pos, Iterable<ByteBuffer> bufs) {
+ public long write(long pos, Iterable<ByteBuffer> bufs) throws IOException {
long start = pos;
for (ByteBuffer buf : bufs) {
pos += write(pos, buf);
@@ -182,29 +347,135 @@ abstract class ByteStore implements FileContent {
* Transfers up to {@code count} bytes from the given channel to this store starting at position
* {@code pos}. Returns the number of bytes transferred. If {@code pos} is greater than the
* current size of this store, the store is truncated up to size {@code pos} before writing.
+ *
+ * @throws IOException if the store needs more blocks but the disk is full or if reading from src
+ * throws an exception
*/
- public abstract long transferFrom(
- ReadableByteChannel src, long pos, long count) throws IOException;
+ public long transferFrom(
+ ReadableByteChannel src, long pos, long count) throws IOException {
+ prepareForWrite(pos, 0);
+
+ if (count == 0) {
+ return 0;
+ }
+
+ long remaining = count;
+
+ int blockIndex = blockIndex(pos);
+ byte[] block = blockForWrite(blockIndex);
+ int off = offsetInBlock(pos);
+
+ ByteBuffer buf = ByteBuffer.wrap(block, off, length(off, remaining));
+
+ int read = 0;
+ while (buf.hasRemaining()) {
+ read = src.read(buf);
+ if (read == -1) {
+ break;
+ }
+
+ remaining -= read;
+ }
+
+ if (read != -1) {
+ outer: while (remaining > 0) {
+ block = blockForWrite(++blockIndex);
+
+ buf = ByteBuffer.wrap(block, 0, length(remaining));
+ while (buf.hasRemaining()) {
+ read = src.read(buf);
+ if (read == -1) {
+ break outer;
+ }
+
+ remaining -= read;
+ }
+ }
+ }
+
+ long written = count - remaining;
+ long newPos = pos + written;
+ if (newPos > size) {
+ size = newPos;
+ }
+
+ return written;
+ }
/**
* Reads the byte at position {@code pos} in this store as an unsigned integer in the range 0-255.
* If {@code pos} is greater than or equal to the size of this store, returns -1 instead.
*/
- public abstract int read(long pos);
+ public int read(long pos) {
+ if (pos >= size) {
+ return -1;
+ }
+
+ byte[] block = blocks.get(blockIndex(pos));
+ int off = offsetInBlock(pos);
+ return UnsignedBytes.toInt(block[off]);
+ }
/**
* Reads up to {@code len} bytes starting at position {@code pos} in this store to the given byte
* array starting at offset {@code off}. Returns the number of bytes actually read or -1 if {@code
* pos} is greater than or equal to the size of this store.
*/
- public abstract int read(long pos, byte[] b, int off, int len);
+ public int read(long pos, byte[] b, int off, int len) {
+ // since max is len (an int), result is guaranteed to be an int
+ int bytesToRead = (int) bytesToRead(pos, len);
+
+ if (bytesToRead > 0) {
+ int remaining = bytesToRead;
+
+ int blockIndex = blockIndex(pos);
+ byte[] block = blocks.get(blockIndex);
+ int offsetInBlock = offsetInBlock(pos);
+
+ int read = get(block, offsetInBlock, b, off, length(offsetInBlock, remaining));
+ remaining -= read;
+ off += read;
+
+ while (remaining > 0) {
+ int index = ++blockIndex;
+ block = blocks.get(index);
+
+ read = get(block, 0, b, off, length(remaining));
+ remaining -= read;
+ off += read;
+ }
+ }
+
+ return bytesToRead;
+ }
/**
* Reads up to {@code buf.remaining()} bytes starting at position {@code pos} in this store to the
* given buffer. Returns the number of bytes read or -1 if {@code pos} is greater than or equal to
* the size of this store.
*/
- public abstract int read(long pos, ByteBuffer buf);
+ public int read(long pos, ByteBuffer buf) {
+ // since max is buf.remaining() (an int), result is guaranteed to be an int
+ int bytesToRead = (int) bytesToRead(pos, buf.remaining());
+
+ if (bytesToRead > 0) {
+ int remaining = bytesToRead;
+
+ int blockIndex = blockIndex(pos);
+ byte[] block = blocks.get(blockIndex);
+ int off = offsetInBlock(pos);
+
+ remaining -= get(block, off, buf, length(off, remaining));
+
+ while (remaining > 0) {
+ int index = ++blockIndex;
+ block = blocks.get(index);
+ remaining -= get(block, 0, buf, length(remaining));
+ }
+ }
+
+ return bytesToRead;
+ }
/**
* Reads up to the total {@code remaining()} number of bytes in each of {@code bufs} starting at
@@ -236,6 +507,118 @@ abstract class ByteStore implements FileContent {
* equal to the current size. This for consistency with {@link FileChannel#transferTo}, which
* this method is primarily intended as an implementation of.
*/
- public abstract long transferTo(
- long pos, long count, WritableByteChannel dest) throws IOException;
+ public long transferTo(
+ long pos, long count, WritableByteChannel dest) throws IOException {
+ long bytesToRead = bytesToRead(pos, count);
+
+ if (bytesToRead > 0) {
+ long remaining = bytesToRead;
+
+ int blockIndex = blockIndex(pos);
+ byte[] block = blocks.get(blockIndex);
+ int off = offsetInBlock(pos);
+
+ ByteBuffer buf = ByteBuffer.wrap(block, off, length(off, remaining));
+ while (buf.hasRemaining()) {
+ remaining -= dest.write(buf);
+ }
+ buf.clear();
+
+ while (remaining > 0) {
+ int index = ++blockIndex;
+ block = blocks.get(index);
+
+ buf = ByteBuffer.wrap(block, 0, length(remaining));
+ while (buf.hasRemaining()) {
+ remaining -= dest.write(buf);
+ }
+ buf.clear();
+ }
+ }
+
+ return Math.max(bytesToRead, 0); // don't return -1 for this method
+ }
+
+ /**
+ * Gets the block at the given index, expanding to create the block if necessary.
+ */
+ private byte[] blockForWrite(int index) throws IOException {
+ int blockCount = blocks.size();
+ if (index >= blockCount) {
+ int additionalBlocksNeeded = index - blockCount + 1;
+ disk.allocate(blocks, additionalBlocksNeeded);
+ }
+
+ return blocks.get(index);
+ }
+
+ private int blockIndex(long position) {
+ return (int) (position / disk.blockSize());
+ }
+
+ private int offsetInBlock(long position) {
+ return (int) (position % disk.blockSize());
+ }
+
+ private int length(long max) {
+ return (int) Math.min(disk.blockSize(), max);
+ }
+
+ private int length(int off, long max) {
+ return (int) Math.min(disk.blockSize() - off, max);
+ }
+
+ /**
+ * Returns the number of bytes that can be read starting at position {@code pos} (up to a maximum
+ * of {@code max}) or -1 if {@code pos} is greater than or equal to the current size.
+ */
+ private long bytesToRead(long pos, long max) {
+ long available = size - pos;
+ if (available <= 0) {
+ return -1;
+ }
+ return Math.min(available, max);
+ }
+
+ /**
+ * Zeroes len bytes in the given block starting at the given offset. Returns len.
+ */
+ private static int zero(byte[] block, int offset, int len) {
+ Util.zero(block, offset, len);
+ return len;
+ }
+
+ /**
+ * Puts the given slice of the given array at the given offset in the given block.
+ */
+ private static int put(byte[] block, int offset, byte[] b, int off, int len) {
+ System.arraycopy(b, off, block, offset, len);
+ return len;
+ }
+
+ /**
+ * Puts the contents of the given byte buffer at the given offset in the given block.
+ */
+ private static int put(byte[] block, int offset, ByteBuffer buf) {
+ int len = Math.min(block.length - offset, buf.remaining());
+ buf.get(block, offset, len);
+ return len;
+ }
+
+ /**
+ * Reads len bytes starting at the given offset in the given block into the given slice of the
+ * given byte array.
+ */
+ private static int get(byte[] block, int offset, byte[] b, int off, int len) {
+ System.arraycopy(block, offset, b, off, len);
+ return len;
+ }
+
+ /**
+ * Reads len bytes starting at the given offset in the given block into the given byte buffer.
+ */
+ private static int get(byte[] block, int offset, ByteBuffer buf, int len) {
+ buf.put(block, offset, len);
+ return len;
+ }
}
diff --git a/jimfs/src/main/java/com/google/jimfs/internal/DirectMemoryDisk.java b/jimfs/src/main/java/com/google/jimfs/internal/DirectMemoryDisk.java
deleted file mode 100644
index 84c4491..0000000
--- a/jimfs/src/main/java/com/google/jimfs/internal/DirectMemoryDisk.java
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.jimfs.internal;
-
-import static com.google.jimfs.internal.Util.nextPowerOf2;
-
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-
-/**
- * {@link MemoryDisk} using {@linkplain ByteBuffer#allocateDirect(int) direct byte buffers} for
- * blocks.
- *
- * @author Colin Decker
- */
-final class DirectMemoryDisk extends MemoryDisk {
-
- private ByteBuffer[] blocks = new ByteBuffer[4096];
-
- DirectMemoryDisk() {
- this(DEFAULT_BLOCK_SIZE);
- }
-
- /**
- * Creates a disk with the given block size and max cache size.
- */
- public DirectMemoryDisk(int blockSize) {
- super(blockSize);
- }
-
- @Override
- protected int allocateMoreBlocks(int count) {
- int newBlockCount = blockCount() + count;
- if (newBlockCount > blocks.length) {
- // using nextPowerOf2 instead of multiplying blocks.length * 2 until it's >= newBlockCount
- blocks = Arrays.copyOf(blocks, nextPowerOf2(newBlockCount));
- }
-
- for (int i = blockCount(); i < newBlockCount; i++) {
- blocks[i] = ByteBuffer.allocateDirect(blockSize);
- free.add(i);
- }
-
- return count;
- }
-
- @Override
- public int zero(int block, int offset, int len) {
- ByteBuffer buffer = blockForRead(block);
- int limit = offset + len;
- for (int i = offset; i < limit; i++) {
- buffer.put(i, (byte) 0);
- }
- return len;
- }
-
- @Override
- public void copy(int block, int copy) {
- ByteBuffer blockBuf = blockForRead(block);
- ByteBuffer copyBuf = blocks[copy];
- copyBuf.put(blockBuf);
- copyBuf.clear();
- }
-
- @Override
- public void put(int block, int offset, byte b) {
- blocks[block].put(offset, b);
- }
-
- @Override
- public int put(int block, int offset, byte[] b, int off, int len) {
- ByteBuffer blockBuf = blocks[block];
- blockBuf.position(offset);
- blockBuf.put(b, off, len);
- blockBuf.clear();
- return len;
- }
-
- @Override
- public int put(int block, int offset, ByteBuffer buf) {
- ByteBuffer blockBuf = blocks[block];
- blockBuf.position(offset);
- int len = Math.min(blockSize - offset, buf.remaining());
- blockBuf.put(buf);
- blockBuf.clear();
- return len;
- }
-
- @Override
- public byte get(int block, int offset) {
- return blocks[block].get(offset);
- }
-
- @Override
- public int get(int block, int offset, byte[] b, int off, int len) {
- ByteBuffer blockBuf = blockForRead(block);
- blockBuf.position(offset);
- blockBuf.get(b, off, len);
- return len;
- }
-
- @Override
- public int get(int block, int offset, ByteBuffer buf, int len) {
- ByteBuffer blockBuf = blockForRead(block);
- blockBuf.position(offset);
- blockBuf.limit(offset + len);
- buf.put(blockBuf);
- return len;
- }
-
- @Override
- public ByteBuffer asByteBuffer(int block, int offset, int len) {
- ByteBuffer result = blockForRead(block);
- result.position(offset);
- result.limit(offset + len);
- return result;
- }
-
- /**
- * Must duplicate the block because we can't have multiple reading threads manipulating the state
- * of the same buffer at the same time. Why oh why couldn't some kind of public buffer type
- * without positional state have been created? ...and a better buffer API than ByteBuffer created
- * on top of it, while I'm wishing.
- */
- private ByteBuffer blockForRead(int block) {
- return blocks[block].duplicate();
- }
-
-}
diff --git a/jimfs/src/main/java/com/google/jimfs/internal/FileContent.java b/jimfs/src/main/java/com/google/jimfs/internal/FileContent.java
index 70347f5..6fb013e 100644
--- a/jimfs/src/main/java/com/google/jimfs/internal/FileContent.java
+++ b/jimfs/src/main/java/com/google/jimfs/internal/FileContent.java
@@ -16,6 +16,7 @@
package com.google.jimfs.internal;
+import java.io.IOException;
import java.nio.file.CopyOption;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -33,7 +34,7 @@ interface FileContent {
* the original; for example, a copy of a directory is an empty directory regardless of what the
* original directory contains.
*/
- FileContent copy();
+ FileContent copy() throws IOException;
/**
* Returns the size, in bytes, of this content. This may be 0 when there is no logical size we
diff --git a/jimfs/src/main/java/com/google/jimfs/internal/FileFactory.java b/jimfs/src/main/java/com/google/jimfs/internal/FileFactory.java
index 2a41503..a62a14e 100644
--- a/jimfs/src/main/java/com/google/jimfs/internal/FileFactory.java
+++ b/jimfs/src/main/java/com/google/jimfs/internal/FileFactory.java
@@ -21,6 +21,7 @@ import static com.google.common.base.Preconditions.checkNotNull;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Supplier;
+import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
/**
@@ -32,12 +33,12 @@ final class FileFactory {
private final AtomicInteger idGenerator = new AtomicInteger();
- private final MemoryDisk disk;
+ private final HeapDisk disk;
/**
* Creates a new file factory using the given disk for regular files.
*/
- public FileFactory(MemoryDisk disk) {
+ public FileFactory(HeapDisk disk) {
this.disk = checkNotNull(disk);
}
@@ -71,7 +72,7 @@ final class FileFactory {
/**
* Creates and returns a copy of the given file.
*/
- public File copy(File file) {
+ public File copy(File file) throws IOException {
return new File(nextFileId(), file.content().copy());
}
diff --git a/jimfs/src/main/java/com/google/jimfs/internal/HeapDisk.java b/jimfs/src/main/java/com/google/jimfs/internal/HeapDisk.java
new file mode 100644
index 0000000..1a97ed5
--- /dev/null
+++ b/jimfs/src/main/java/com/google/jimfs/internal/HeapDisk.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2013 Google Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.google.jimfs.internal;
+
+import static com.google.common.base.Preconditions.checkArgument;
+
+import com.google.common.annotations.VisibleForTesting;
+
+import java.io.IOException;
+
+/**
+ * A resizable pseudo-disk acting as a shared space for storing file data. A disk allocates fixed
+ * size blocks of bytes to files as needed and may cache blocks that have been freed for reuse. A
+ * memory disk has a fixed maximum number of blocks it will allocate at a time (which sets the
+ * total "size" of the disk) and a maximum number of unused blocks it will cache for reuse at a
+ * time (which sets the minimum amount of space the disk will use once
+ *
+ * @author Colin Decker
+ */
+final class HeapDisk {
+
+ /** 8 KB blocks. */
+ public static final int DEFAULT_BLOCK_SIZE = 8192;
+
+ /** 16 GB of space with 8 KB blocks. */
+ public static final int DEFAULT_MAX_BLOCK_COUNT =
+ (int) ((16L * 1024 * 1024 * 1024) / DEFAULT_BLOCK_SIZE);
+
+ /** Fixed size of each block for this disk. */
+ private final int blockSize;
+
+ /** Maximum total number of blocks that the disk may contain at any time. */
+ private final int maxBlockCount;
+
+ /** Maximum total number of unused blocks that may be cached for reuse at any time. */
+ private final int maxCachedBlockCount;
+
+ /** Cache of free blocks to be allocated to files. */
+ @VisibleForTesting final BlockList blockCache;
+
+ /** The current total number of blocks that are currently allocated to files. */
+ private int allocatedBlockCount;
+
+ /**
+ * Creates a new heap disk with 8 KB blocks that can store up to 16 GB of data and caches all
+ * blocks that are freed.
+ */
+ public HeapDisk() {
+ this(DEFAULT_BLOCK_SIZE, DEFAULT_MAX_BLOCK_COUNT, DEFAULT_MAX_BLOCK_COUNT);
+ }
+
+ /**
+ * Creates a new disk with the given {@code blockSize}, {@code maxBlockCount} and
+ * {@code maxCachedBlockCount}.
+ */
+ public HeapDisk(int blockSize, int maxBlockCount, int maxCachedBlockCount) {
+ checkArgument(blockSize > 0, "blockSize (%s) must be positive", blockSize);
+ checkArgument(blockSize % 2 == 0, "blockSize (%s) must be a multiple of 2", blockSize);
+ checkArgument(maxBlockCount > 0, "maxBlockCount (%s) must be positive", maxBlockCount);
+ checkArgument(maxCachedBlockCount >= 0,
+ "maxCachedBlockCount must be non-negative", maxCachedBlockCount);
+ this.blockSize = blockSize;
+ this.maxBlockCount = maxBlockCount;
+ this.maxCachedBlockCount = maxCachedBlockCount;
+ this.blockCache = new BlockList(Math.min(maxCachedBlockCount, 8192));
+ }
+
+ /**
+ * Creates a new, empty byte store.
+ */
+ public ByteStore createByteStore() {
+ return new ByteStore(this);
+ }
+
+ /**
+ * Returns the size of blocks created by this disk.
+ */
+ public int blockSize() {
+ return blockSize;
+ }
+
+ /**
+ * Returns the total size of this disk. This is the maximum size of the disk and does not reflect
+ * the amount of data currently allocated or cached.
+ */
+ public synchronized long getTotalSpace() {
+ return maxBlockCount * (long) blockSize;
+ }
+
+ /**
+ * Returns the current number of unallocated bytes on this disk. This is the maximum number of
+ * additional bytes that could be allocated and does not reflect the number of bytes currently
+ * actually cached in the disk.
+ */
+ public synchronized long getUnallocatedSpace() {
+ return (maxBlockCount - allocatedBlockCount) * (long) blockSize;
+ }
+
+ /**
+ * Allocates the given number of blocks and adds their identifiers to the given list.
+ */
+ public synchronized void allocate(BlockList blocks, int count) throws IOException {
+ int newAllocatedBlockCount = allocatedBlockCount + count;
+ if (newAllocatedBlockCount > maxBlockCount) {
+ throw new IOException("out of disk space");
+ }
+
+ int newBlocksNeeded = Math.max(count - blockCache.size(), 0);
+
+ for (int i = 0; i < newBlocksNeeded; i++) {
+ blocks.add(new byte[blockSize]);
+ }
+
+ if (newBlocksNeeded != count) {
+ blockCache.transferTo(blocks, count - newBlocksNeeded);
+ }
+
+ allocatedBlockCount = newAllocatedBlockCount;
+ }
+
+ /**
+ * Frees all blocks in the given list.
+ */
+ public void free(BlockList blocks) {
+ free(blocks, blocks.size());
+ }
+
+ /**
+ * Frees the last count blocks from the given list.
+ */
+ public synchronized void free(BlockList blocks, int count) {
+ int remainingCacheSpace = maxCachedBlockCount - blocks.size();
+ if (remainingCacheSpace != 0) {
+ blocks.copyTo(blockCache, Math.min(count, remainingCacheSpace));
+ }
+ blocks.truncate(blocks.size() - count);
+
+ allocatedBlockCount -= count;
+ }
+}
diff --git a/jimfs/src/main/java/com/google/jimfs/internal/HeapMemoryDisk.java b/jimfs/src/main/java/com/google/jimfs/internal/HeapMemoryDisk.java
deleted file mode 100644
index 802a4ea..0000000
--- a/jimfs/src/main/java/com/google/jimfs/internal/HeapMemoryDisk.java
+++ /dev/null
@@ -1,110 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.jimfs.internal;
-
-import static com.google.jimfs.internal.Util.nextPowerOf2;
-
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-
-/**
- * {@link MemoryDisk} using byte arrays for blocks.
- *
- * @author Colin Decker
- */
-final class HeapMemoryDisk extends MemoryDisk {
-
- private byte[][] blocks = new byte[4096][];
-
- HeapMemoryDisk() {
- this(DEFAULT_BLOCK_SIZE);
- }
-
- /**
- * Creates a disk with the given block size.
- */
- public HeapMemoryDisk(int blockSize) {
- super(blockSize);
- }
-
- @Override
- protected int allocateMoreBlocks(int count) {
- int newBlockCount = blockCount() + count;
- if (newBlockCount > blocks.length) {
- // using nextPowerOf2 instead of multiplying blocks.length * 2 until it's >= newBlockCount
- blocks = Arrays.copyOf(blocks, nextPowerOf2(newBlockCount));
- }
-
- for (int i = blockCount(); i < newBlockCount; i++) {
- blocks[i] = new byte[blockSize];
- free.add(i);
- }
-
- return count;
- }
-
- @Override
- public int zero(int block, int offset, int len) {
- Util.zero(blocks[block], offset, len);
- return len;
- }
-
- @Override
- public void copy(int block, int copy) {
- System.arraycopy(blocks[block], 0, blocks[copy], 0, blockSize);
- }
-
- @Override
- public void put(int block, int offset, byte b) {
- blocks[block][offset] = b;
- }
-
- @Override
- public int put(int block, int offset, byte[] b, int off, int len) {
- System.arraycopy(b, off, blocks[block], offset, len);
- return len;
- }
-
- @Override
- public int put(int block, int offset, ByteBuffer buf) {
- int len = Math.min(blockSize - offset, buf.remaining());
- buf.get(blocks[block], offset, len);
- return len;
- }
-
- @Override
- public byte get(int block, int offset) {
- return blocks[block][offset];
- }
-
- @Override
- public int get(int block, int offset, byte[] b, int off, int len) {
- System.arraycopy(blocks[block], offset, b, off, len);
- return len;
- }
-
- @Override
- public int get(int block, int offset, ByteBuffer buf, int len) {
- buf.put(blocks[block], offset, len);
- return len;
- }
-
- @Override
- public ByteBuffer asByteBuffer(int block, int offset, int len) {
- return ByteBuffer.wrap(blocks[block], offset, len);
- }
-}
diff --git a/jimfs/src/main/java/com/google/jimfs/internal/JimfsFileStore.java b/jimfs/src/main/java/com/google/jimfs/internal/JimfsFileStore.java
index e36c67f..8ba8800 100644
--- a/jimfs/src/main/java/com/google/jimfs/internal/JimfsFileStore.java
+++ b/jimfs/src/main/java/com/google/jimfs/internal/JimfsFileStore.java
@@ -53,14 +53,14 @@ import javax.annotation.Nullable;
final class JimfsFileStore extends FileStore {
private final FileTree tree;
- private final MemoryDisk disk;
+ private final HeapDisk disk;
private final AttributeService attributes;
private final FileFactory factory;
private final Lock readLock;
private final Lock writeLock;
- public JimfsFileStore(FileTree tree, FileFactory factory, MemoryDisk disk,
+ public JimfsFileStore(FileTree tree, FileFactory factory, HeapDisk disk,
AttributeService attributes) {
this.tree = checkNotNull(tree);
this.factory = checkNotNull(factory);
@@ -143,7 +143,7 @@ final class JimfsFileStore extends FileStore {
* Creates a copy of the given file, copying its attributes as well if copy attributes is true.
* Returns the copy.
*/
- File copy(File file, boolean copyAttributes) {
+ File copy(File file, boolean copyAttributes) throws IOException {
File copy = factory.copy(file);
setInitialAttributes(copy);
if (copyAttributes) {
diff --git a/jimfs/src/main/java/com/google/jimfs/internal/JimfsFileSystems.java b/jimfs/src/main/java/com/google/jimfs/internal/JimfsFileSystems.java
index ba887f4..21e751f 100644
--- a/jimfs/src/main/java/com/google/jimfs/internal/JimfsFileSystems.java
+++ b/jimfs/src/main/java/com/google/jimfs/internal/JimfsFileSystems.java
@@ -56,7 +56,9 @@ final class JimfsFileSystems {
private static JimfsFileStore createFileStore(
Configuration config, PathService pathService) {
AttributeService attributeService = new AttributeService(config);
- MemoryDisk disk = new HeapMemoryDisk();
+
+ // TODO(cgdecker): Make disk values configurable
+ HeapDisk disk = new HeapDisk();
FileFactory fileFactory = new FileFactory(disk);
Map<Name, File> roots = new HashMap<>();
diff --git a/jimfs/src/main/java/com/google/jimfs/internal/MemoryDisk.java b/jimfs/src/main/java/com/google/jimfs/internal/MemoryDisk.java
deleted file mode 100644
index a882e83..0000000
--- a/jimfs/src/main/java/com/google/jimfs/internal/MemoryDisk.java
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.jimfs.internal;
-
-import static com.google.common.base.Preconditions.checkArgument;
-
-import java.nio.ByteBuffer;
-
-/**
- * A resizable pseudo-disk acting as a shared space for storing file data. A disk allocates fixed
- * size blocks of bytes to files as needed and caches blocks that have been freed for reuse. Each
- * block is represented by an integer which is used to locate the block for read and write
- * operations implemented by the disk.
- *
- * <p>Currently, once a memory disk has allocated a block it will cache that block indefinitely
- * once freed. This means that the total size of the disk is always the maximum number of bytes
- * that have been allocated to files at one time so far.
- *
- * @author Colin Decker
- */
-abstract class MemoryDisk {
-
- /**
- * 8K blocks.
- */
- protected static final int DEFAULT_BLOCK_SIZE = 8192;
-
- /**
- * Fixed size of each block for this disk.
- */
- protected final int blockSize;
-
- /**
- * The current total number of blocks this disk contains, including both free blocks and blocks
- * that are allocated to files.
- */
- private int blockCount;
-
- /**
- * Queue of free blocks to be allocated to files.
- */
- protected final IntList free = new IntList(1024);
-
- protected MemoryDisk(int blockSize) {
- checkArgument(blockSize > 0, "blockSize (%s) must be positive", blockSize);
- checkArgument(blockSize % 2 == 0, "blockSize (%s) must be a multiple of 2", blockSize);
- this.blockSize = blockSize;
- }
-
- /**
- * Creates a new, empty byte store.
- */
- public final ByteStore createByteStore() {
- return new MemoryDiskByteStore(this);
- }
-
- /**
- * Allocates at least {@code minBlocks} more blocks if possible. The {@code free} list
- * should have blocks in it when this returns if an exception is not thrown. Returns the number
- * of new blocks that were allocated.
- */
- protected abstract int allocateMoreBlocks(int count);
-
- /**
- * Returns the size of blocks created by this disk.
- */
- public final int blockSize() {
- return blockSize;
- }
-
- /**
- * Returns the current total number of blocks this disk contains.
- */
- protected final int blockCount() {
- return blockCount;
- }
-
- /**
- * Returns the current total size of this disk.
- */
- public final synchronized long getTotalSpace() {
- return blockCount * blockSize;
- }
-
- /**
- * Returns the current number of unallocated bytes on this disk.
- */
- public final synchronized long getUnallocatedSpace() {
- return free.size() * blockSize;
- }
-
- /**
- * Allocates the given number of blocks and adds their identifiers to the given list.
- */
- public final synchronized void allocate(IntList blocks, int count) {
- int additionalBlocksNeeded = count - free.size();
- if (additionalBlocksNeeded > 0) {
- blockCount += allocateMoreBlocks(additionalBlocksNeeded);
- }
-
- free.transferTo(blocks, count);
- }
-
- /**
- * Frees all blocks in the given list.
- */
- public final void free(IntList blocks) {
- free(blocks, blocks.size());
- }
-
- /**
- * Frees the last count blocks from the given list.
- */
- public final synchronized void free(IntList blocks, int count) {
- blocks.transferTo(free, count);
- }
-
- /**
- * Zeroes len bytes in the given block starting at the given offset. Returns len.
- */
- public abstract int zero(int block, int offset, int len);
-
- /**
- * Copies the given block and returns the copy.
- */
- public abstract void copy(int block, int copy);
-
- /**
- * Puts the given byte at the given offset in the given block.
- */
- public abstract void put(int block, int offset, byte b);
-
- /**
- * Puts the given slice of the given array at the given offset in the given block.
- */
- public abstract int put(int block, int offset, byte[] b, int off, int len);
-
- /**
- * Puts the contents of the given byte buffer at the given offset in the given block.
- */
- public abstract int put(int block, int offset, ByteBuffer buf);
-
- /**
- * Returns the byte at the given offset in the given block.
- */
- public abstract byte get(int block, int offset);
-
- /**
- * Reads len bytes starting at the given offset in the given block into the given slice of the
- * given byte array.
- */
- public abstract int get(int block, int offset, byte[] b, int off, int len);
-
- /**
- * Reads len bytes starting at the given offset in the given block into the given byte buffer.
- */
- public abstract int get(int block, int offset, ByteBuffer buf, int len);
-
- /**
- * Returns a ByteBuffer view of the slice of the given block starting at the given offset and
- * having the given length.
- */
- public abstract ByteBuffer asByteBuffer(int block, int offset, int len);
-}
diff --git a/jimfs/src/main/java/com/google/jimfs/internal/MemoryDiskByteStore.java b/jimfs/src/main/java/com/google/jimfs/internal/MemoryDiskByteStore.java
deleted file mode 100644
index f79fb10..0000000
--- a/jimfs/src/main/java/com/google/jimfs/internal/MemoryDiskByteStore.java
+++ /dev/null
@@ -1,396 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.jimfs.internal;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-
-import com.google.common.primitives.UnsignedBytes;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.WritableByteChannel;
-
-/**
- * Byte store backed by a {@link MemoryDisk}.
- *
- * @author Colin Decker
- */
-final class MemoryDiskByteStore extends ByteStore {
-
- private final MemoryDisk disk;
- private final IntList blocks;
- private long size;
-
- public MemoryDiskByteStore(MemoryDisk disk) {
- this(disk, new IntList(32), 0);
- }
-
- private MemoryDiskByteStore(MemoryDisk disk, IntList blocks, long size) {
- this.disk = checkNotNull(disk);
- this.blocks = checkNotNull(blocks);
-
- checkArgument(size >= 0);
- this.size = size;
- }
-
- @Override
- public long currentSize() {
- return size;
- }
-
- @Override
- protected ByteStore createCopy() {
- IntList copyBlocks = new IntList(Math.max(blocks.size() * 2, 32));
- disk.allocate(copyBlocks, blocks.size());
-
- for (int i = 0; i < blocks.size(); i++) {
- int block = blocks.get(i);
- int copy = copyBlocks.get(i);
- disk.copy(block, copy);
- }
- return new MemoryDiskByteStore(disk, copyBlocks, size);
- }
-
- @Override
- protected final void deleteContents() {
- disk.free(blocks);
- size = 0;
- }
-
- @Override
- public boolean truncate(long size) {
- if (size >= this.size) {
- return false;
- }
-
- long lastPosition = size - 1;
- this.size = size;
-
- int newBlockCount = blockIndex(lastPosition) + 1;
- int blocksToRemove = blocks.size() - newBlockCount;
- if (blocksToRemove > 0) {
- disk.free(blocks, blocksToRemove);
- }
-
- return true;
- }
-
- /**
- * Prepares for a write of len bytes starting at position pos.
- */
- private void prepareForWrite(long pos, long len) {
- long end = pos + len;
-
- // allocate any additional blocks needed
- int lastBlockIndex = blocks.size() - 1;
- int endBlockIndex = blockIndex(end - 1);
-
- if (endBlockIndex > lastBlockIndex) {
- int additionalBlocksNeeded = endBlockIndex - lastBlockIndex;
- disk.allocate(blocks, additionalBlocksNeeded);
- }
-
- // zero bytes between current size and pos
- if (pos > size) {
- long remaining = pos - size;
-
- int blockIndex = blockIndex(size);
- int block = blocks.get(blockIndex);
- int off = offsetInBlock(size);
-
- remaining -= disk.zero(block, off, length(off, remaining));
-
- while (remaining > 0) {
- block = blocks.get(++blockIndex);
-
- remaining -= disk.zero(block, 0, length(remaining));
- }
-
- size = pos;
- }
- }
-
- @Override
- public int write(long pos, byte b) {
- prepareForWrite(pos, 1);
-
- int block = blocks.get(blockIndex(pos));
- int off = offsetInBlock(pos);
- disk.put(block, off, b);
-
- if (pos >= size) {
- size = pos + 1;
- }
-
- return 1;
- }
-
- @Override
- public int write(long pos, byte[] b, int off, int len) {
- prepareForWrite(pos, len);
-
- if (len == 0) {
- return 0;
- }
-
- int remaining = len;
-
- int blockIndex = blockIndex(pos);
- int block = blockForWrite(blockIndex);
- int offInBlock = offsetInBlock(pos);
-
- int written = disk.put(block, offInBlock, b, off, length(offInBlock, remaining));
- remaining -= written;
- off += written;
-
- while (remaining > 0) {
- block = blocks.get(++blockIndex);
-
- written = disk.put(block, 0, b, off, length(remaining));
- remaining -= written;
- off += written;
- }
-
- long newPos = pos + len;
- if (newPos > size) {
- size = newPos;
- }
-
- return len;
- }
-
- @Override
- public int write(long pos, ByteBuffer buf) {
- int len = buf.remaining();
-
- prepareForWrite(pos, len);
-
- if (len == 0) {
- return 0;
- }
-
- int bytesToWrite = buf.remaining();
-
- int blockIndex = blockIndex(pos);
- int block = blockForWrite(blockIndex);
- int off = offsetInBlock(pos);
-
- disk.put(block, off, buf);
-
- while (buf.hasRemaining()) {
- block = blocks.get(++blockIndex);
-
- disk.put(block, 0, buf);
- }
-
- if (pos + bytesToWrite > size) {
- size = pos + bytesToWrite;
- }
-
- return bytesToWrite;
- }
-
- @Override
- public long transferFrom(ReadableByteChannel src, long pos, long count) throws IOException {
- prepareForWrite(pos, 0);
-
- if (count == 0) {
- return 0;
- }
-
- long remaining = count;
-
- int blockIndex = blockIndex(pos);
- int block = blockForWrite(blockIndex);
- int off = offsetInBlock(pos);
-
- ByteBuffer buf = disk.asByteBuffer(block, off, length(off, remaining));
-
- int read = 0;
- while (buf.hasRemaining()) {
- read = src.read(buf);
- if (read == -1) {
- break;
- }
-
- remaining -= read;
- }
-
- if (read != -1) {
- outer: while (remaining > 0) {
- block = blockForWrite(++blockIndex);
-
- buf = disk.asByteBuffer(block, 0, length(remaining));
- while (buf.hasRemaining()) {
- read = src.read(buf);
- if (read == -1) {
- break outer;
- }
-
- remaining -= read;
- }
- }
- }
-
- long written = count - remaining;
- long newPos = pos + written;
- if (newPos > size) {
- size = newPos;
- }
-
- return written;
- }
-
- @Override
- public int read(long pos) {
- if (pos >= size) {
- return -1;
- }
-
- int block = blocks.get(blockIndex(pos));
- int off = offsetInBlock(pos);
- return UnsignedBytes.toInt(disk.get(block, off));
- }
-
- @Override
- public int read(long pos, byte[] b, int off, int len) {
- // since max is len (an int), result is guaranteed to be an int
- int bytesToRead = (int) bytesToRead(pos, len);
-
- if (bytesToRead > 0) {
- int remaining = bytesToRead;
-
- int blockIndex = blockIndex(pos);
- int block = blocks.get(blockIndex);
- int offsetInBlock = offsetInBlock(pos);
-
- int read = disk.get(block, offsetInBlock, b, off, length(offsetInBlock, remaining));
- remaining -= read;
- off += read;
-
- while (remaining > 0) {
- int index = ++blockIndex;
- block = blocks.get(index);
-
- read = disk.get(block, 0, b, off, length(remaining));
- remaining -= read;
- off += read;
- }
- }
-
- return bytesToRead;
- }
-
- @Override
- public int read(long pos, ByteBuffer buf) {
- // since max is buf.remaining() (an int), result is guaranteed to be an int
- int bytesToRead = (int) bytesToRead(pos, buf.remaining());
-
- if (bytesToRead > 0) {
- int remaining = bytesToRead;
-
- int blockIndex = blockIndex(pos);
- int block = blocks.get(blockIndex);
- int off = offsetInBlock(pos);
-
- remaining -= disk.get(block, off, buf, length(off, remaining));
-
- while (remaining > 0) {
- int index = ++blockIndex;
- block = blocks.get(index);
- remaining -= disk.get(block, 0, buf, length(remaining));
- }
- }
-
- return bytesToRead;
- }
-
- @Override
- public long transferTo(long pos, long count, WritableByteChannel dest) throws IOException {
- long bytesToRead = bytesToRead(pos, count);
-
- if (bytesToRead > 0) {
- long remaining = bytesToRead;
-
- int blockIndex = blockIndex(pos);
- int block = blocks.get(blockIndex);
- int off = offsetInBlock(pos);
-
- ByteBuffer buf = disk.asByteBuffer(block, off, length(off, remaining));
- while (buf.hasRemaining()) {
- remaining -= dest.write(buf);
- }
- buf.clear();
-
- while (remaining > 0) {
- int index = ++blockIndex;
- block = blocks.get(index);
-
- buf = disk.asByteBuffer(block, 0, length(remaining));
- while (buf.hasRemaining()) {
- remaining -= dest.write(buf);
- }
- buf.clear();
- }
- }
-
- return Math.max(bytesToRead, 0); // don't return -1 for this method
- }
-
- /**
- * Gets the block at the given index, expanding to create the block if necessary.
- */
- private int blockForWrite(int index) {
- int blockCount = blocks.size();
- if (index >= blockCount) {
- int additionalBlocksNeeded = index - blockCount + 1;
- disk.allocate(blocks, additionalBlocksNeeded);
- }
-
- return blocks.get(index);
- }
-
- private int blockIndex(long position) {
- return (int) (position / disk.blockSize());
- }
-
- private int offsetInBlock(long position) {
- return (int) (position % disk.blockSize());
- }
-
- private int length(long max) {
- return (int) Math.min(disk.blockSize(), max);
- }
-
- private int length(int off, long max) {
- return (int) Math.min(disk.blockSize() - off, max);
- }
-
- /**
- * Returns the number of bytes that can be read starting at position {@code pos} (up to a maximum
- * of {@code max}) or -1 if {@code pos} is greater than or equal to the current size.
- */
- private long bytesToRead(long pos, long max) {
- long available = size - pos;
- if (available <= 0) {
- return -1;
- }
- return Math.min(available, max);
- }
-}
diff --git a/jimfs/src/main/java/com/google/jimfs/internal/Util.java b/jimfs/src/main/java/com/google/jimfs/internal/Util.java
index d73dacd..7b5943e 100644
--- a/jimfs/src/main/java/com/google/jimfs/internal/Util.java
+++ b/jimfs/src/main/java/com/google/jimfs/internal/Util.java
@@ -76,30 +76,42 @@ final class Util {
return C2 * Integer.rotateLeft(hashCode * C1, 15);
}
- private static final int ZERO_ARRAY_LEN = 8192;
- private static final byte[] ZERO_ARRAY = new byte[ZERO_ARRAY_LEN];
+ private static final int ARRAY_LEN = 8192;
+ private static final byte[] ZERO_ARRAY = new byte[ARRAY_LEN];
+ private static final byte[][] NULL_ARRAY = new byte[ARRAY_LEN][];
/**
- * Zeroes all bytes of the given array.
+ * Zeroes all bytes between off (inclusive) and off + len (exclusive) in the given array.
*/
- static void zero(byte[] bytes) {
- zero(bytes, 0, bytes.length);
+ static void zero(byte[] bytes, int off, int len) {
+ // this significantly faster than looping or Arrays.fill (which loops), particularly when the
+ // length of the slice to be zeroed is <= to ARRAY_LEN (in that case, it's faster by a
+ // factor of 2)
+ int remaining = len;
+ while (remaining >= ARRAY_LEN) {
+ System.arraycopy(ZERO_ARRAY, 0, bytes, off, ARRAY_LEN);
+ off += ARRAY_LEN;
+ remaining -= ARRAY_LEN;
+ }
+
+ System.arraycopy(ZERO_ARRAY, 0, bytes, off, remaining);
}
/**
- * Zeroes all bytes between off (inclusive) and off + len (exclusive) in the given array.
+ * Clears (sets to null) all blocks between off (inclusive) and off + len (exclusive) in the
+ * given array.
*/
- static void zero(byte[] bytes, int off, int len) {
+ static void clear(byte[][] blocks, int off, int len) {
// this significantly faster than looping or Arrays.fill (which loops), particularly when the
- // length of the slice to be zeroed is <= to ZERO_ARRAY_LEN (in that case, it's faster by a
+ // length of the slice to be cleared is <= to ARRAY_LEN (in that case, it's faster by a
// factor of 2)
int remaining = len;
- while (remaining >= ZERO_ARRAY_LEN) {
- System.arraycopy(ZERO_ARRAY, 0, bytes, off, ZERO_ARRAY_LEN);
- off += ZERO_ARRAY_LEN;
- remaining -= ZERO_ARRAY_LEN;
+ while (remaining >= ARRAY_LEN) {
+ System.arraycopy(NULL_ARRAY, 0, blocks, off, ARRAY_LEN);
+ off += ARRAY_LEN;
+ remaining -= ARRAY_LEN;
}
- System.arraycopy(ZERO_ARRAY, 0, bytes, off, remaining);
+ System.arraycopy(NULL_ARRAY, 0, blocks, off, remaining);
}
}
diff --git a/jimfs/src/main/java/com/google/jimfs/internal/package-info.java b/jimfs/src/main/java/com/google/jimfs/internal/package-info.java
index 10a8174..7cef362 100644
--- a/jimfs/src/main/java/com/google/jimfs/internal/package-info.java
+++ b/jimfs/src/main/java/com/google/jimfs/internal/package-info.java
@@ -48,7 +48,7 @@
* <ul>
* <li>{@link com.google.jimfs.internal.FileFactory FileFactory} handles creation of new file
* objects.</li>
- * <li>{@link com.google.jimfs.internal.MemoryDisk MemoryDisk} handles creation and storage of
+ * <li>{@link com.google.jimfs.internal.HeapDisk HeapDisk} handles creation and storage of
* {@link com.google.jimfs.internal.ByteStore ByteStore} instances, which act as the content of
* regular files.</li>
* <li>{@link com.google.jimfs.internal.FileTree FileTree} stores the root of the file hierarchy
@@ -121,12 +121,10 @@
* <h3>Regular files</h3>
*
* Currently, the only implementation for regular file content is
- * {@link com.google.jimfs.internal.MemoryDiskByteStore MemoryDiskByteStore}, which makes use of a
- * singleton {@link com.google.jimfs.internal.MemoryDisk MemoryDisk}. A disk (which may either use
- * {@linkplain com.google.jimfs.internal.HeapMemoryDisk heap} or
- * {@linkplain com.google.jimfs.internal.DirectMemoryDisk direct} memory) is a resizable cache for
+ * {@link com.google.jimfs.internal.ByteStore ByteStore}, which makes use of a singleton
+ * {@link com.google.jimfs.internal.HeapDisk HeapDisk}. A disk is a resizable factory and cache for
* fixed size blocks of memory. These blocks are allocated to files as needed and returned to the
- * disk when a file is deleted or truncated. When existing free blocks are available, those blocks
+ * disk when a file is deleted or truncated. When cached free blocks are available, those blocks
* are allocated to files first. If more blocks are needed, they are created.
*
* <h3>Linking</h3>
diff --git a/jimfs/src/test/java/com/google/jimfs/JimfsUnixLikeFileSystemTest.java b/jimfs/src/test/java/com/google/jimfs/JimfsUnixLikeFileSystemTest.java
index 545ad88..eab9af2 100644
--- a/jimfs/src/test/java/com/google/jimfs/JimfsUnixLikeFileSystemTest.java
+++ b/jimfs/src/test/java/com/google/jimfs/JimfsUnixLikeFileSystemTest.java
@@ -135,10 +135,10 @@ public class JimfsUnixLikeFileSystemTest extends AbstractJimfsIntegrationTest {
ASSERT.that(fileStore.type()).is("jimfs");
ASSERT.that(fileStore.isReadOnly()).isFalse();
- // no regular files have been created and written to, so no blocks have been allocated yet
- ASSERT.that(fileStore.getTotalSpace()).is(0);
- ASSERT.that(fileStore.getUnallocatedSpace()).is(0);
- ASSERT.that(fileStore.getUsableSpace()).is(0);
+ long expectedSize = 16L * 1024 * 1024 * 1024; // 16 GB
+ ASSERT.that(fileStore.getTotalSpace()).is(expectedSize);
+ ASSERT.that(fileStore.getUnallocatedSpace()).is(expectedSize);
+ ASSERT.that(fileStore.getUsableSpace()).is(expectedSize);
}
@Test
diff --git a/jimfs/src/test/java/com/google/jimfs/internal/AbstractByteStoreTest.java b/jimfs/src/test/java/com/google/jimfs/internal/AbstractByteStoreTest.java
index c4195ba..64efc4f 100644
--- a/jimfs/src/test/java/com/google/jimfs/internal/AbstractByteStoreTest.java
+++ b/jimfs/src/test/java/com/google/jimfs/internal/AbstractByteStoreTest.java
@@ -48,7 +48,7 @@ public abstract class AbstractByteStoreTest {
store = createByteStore();
}
- private void fillContent(String fill) {
+ private void fillContent(String fill) throws IOException {
store.write(0, buffer(fill));
}
@@ -90,65 +90,65 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testEmpty_write_singleByte_atStart() {
+ public void testEmpty_write_singleByte_atStart() throws IOException {
store.write(0, (byte) 1);
assertContentEquals("1", store);
}
@Test
- public void testEmpty_write_byteArray_atStart() {
+ public void testEmpty_write_byteArray_atStart() throws IOException {
byte[] bytes = bytes("111111");
store.write(0, bytes, 0, bytes.length);
assertContentEquals(bytes, store);
}
@Test
- public void testEmpty_write_partialByteArray_atStart() {
+ public void testEmpty_write_partialByteArray_atStart() throws IOException {
byte[] bytes = bytes("2211111122");
store.write(0, bytes, 2, 6);
assertContentEquals("111111", store);
}
@Test
- public void testEmpty_write_singleBuffer_atStart() {
+ public void testEmpty_write_singleBuffer_atStart() throws IOException {
store.write(0, buffer("111111"));
assertContentEquals("111111", store);
}
@Test
- public void testEmpty_write_multipleBuffers_atStart() {
+ public void testEmpty_write_multipleBuffers_atStart() throws IOException {
store.write(0, buffers("111", "111"));
assertContentEquals("111111", store);
}
@Test
- public void testEmpty_write_singleByte_atNonZeroPosition() {
+ public void testEmpty_write_singleByte_atNonZeroPosition() throws IOException {
store.write(5, (byte) 1);
assertContentEquals("000001", store);
}
@Test
- public void testEmpty_write_byteArray_atNonZeroPosition() {
+ public void testEmpty_write_byteArray_atNonZeroPosition() throws IOException {
byte[] bytes = bytes("111111");
store.write(5, bytes, 0, bytes.length);
assertContentEquals("00000111111", store);
}
@Test
- public void testEmpty_write_partialByteArray_atNonZeroPosition() {
+ public void testEmpty_write_partialByteArray_atNonZeroPosition() throws IOException {
byte[] bytes = bytes("2211111122");
store.write(5, bytes, 2, 6);
assertContentEquals("00000111111", store);
}
@Test
- public void testEmpty_write_singleBuffer_atNonZeroPosition() {
+ public void testEmpty_write_singleBuffer_atNonZeroPosition() throws IOException {
store.write(5, buffer("111"));
assertContentEquals("00000111", store);
}
@Test
- public void testEmpty_write_multipleBuffers_atNonZeroPosition() {
+ public void testEmpty_write_multipleBuffers_atNonZeroPosition() throws IOException {
store.write(5, buffers("111", "222"));
assertContentEquals("00000111222", store);
}
@@ -202,31 +202,31 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testEmpty_copy() {
+ public void testEmpty_copy() throws IOException {
ByteStore copy = store.copy();
assertContentEquals("", copy);
}
@Test
- public void testEmpty_truncate_toZero() {
+ public void testEmpty_truncate_toZero() throws IOException {
store.truncate(0);
assertContentEquals("", store);
}
@Test
- public void testEmpty_truncate_sizeUp() {
+ public void testEmpty_truncate_sizeUp() throws IOException {
store.truncate(10);
assertContentEquals("", store);
}
@Test
- public void testNonEmpty() {
+ public void testNonEmpty() throws IOException {
fillContent("222222");
assertContentEquals("222222", store);
}
@Test
- public void testNonEmpty_read_singleByte() {
+ public void testNonEmpty_read_singleByte() throws IOException {
fillContent("123456");
assertEquals(1, store.read(0));
assertEquals(2, store.read(1));
@@ -236,7 +236,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_all_byteArray() {
+ public void testNonEmpty_read_all_byteArray() throws IOException {
fillContent("222222");
byte[] array = new byte[6];
assertEquals(6, store.read(0, array, 0, array.length));
@@ -244,7 +244,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_all_singleBuffer() {
+ public void testNonEmpty_read_all_singleBuffer() throws IOException {
fillContent("222222");
ByteBuffer buffer = ByteBuffer.allocate(6);
assertEquals(6, store.read(0, buffer));
@@ -252,7 +252,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_all_multipleBuffers() {
+ public void testNonEmpty_read_all_multipleBuffers() throws IOException {
fillContent("223334");
ByteBuffer buf1 = ByteBuffer.allocate(3);
ByteBuffer buf2 = ByteBuffer.allocate(3);
@@ -262,7 +262,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_all_byteArray_largerThanContent() {
+ public void testNonEmpty_read_all_byteArray_largerThanContent() throws IOException {
fillContent("222222");
byte[] array = new byte[10];
assertEquals(6, store.read(0, array, 0, array.length));
@@ -273,7 +273,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_all_singleBuffer_largerThanContent() {
+ public void testNonEmpty_read_all_singleBuffer_largerThanContent() throws IOException {
fillContent("222222");
ByteBuffer buffer = ByteBuffer.allocate(16);
assertBufferEquals("0000000000000000", 16, buffer);
@@ -282,7 +282,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_all_multipleBuffers_largerThanContent() {
+ public void testNonEmpty_read_all_multipleBuffers_largerThanContent() throws IOException {
fillContent("222222");
ByteBuffer buf1 = ByteBuffer.allocate(4);
ByteBuffer buf2 = ByteBuffer.allocate(8);
@@ -292,7 +292,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_all_multipleBuffers_extraBuffers() {
+ public void testNonEmpty_read_all_multipleBuffers_extraBuffers() throws IOException {
fillContent("222222");
ByteBuffer buf1 = ByteBuffer.allocate(4);
ByteBuffer buf2 = ByteBuffer.allocate(8);
@@ -304,7 +304,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_partial_fromStart_byteArray() {
+ public void testNonEmpty_read_partial_fromStart_byteArray() throws IOException {
fillContent("222222");
byte[] array = new byte[3];
assertEquals(3, store.read(0, array, 0, array.length));
@@ -315,7 +315,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_partial_fromMiddle_byteArray() {
+ public void testNonEmpty_read_partial_fromMiddle_byteArray() throws IOException {
fillContent("22223333");
byte[] array = new byte[3];
assertEquals(3, store.read(3, array, 0, array.length));
@@ -326,7 +326,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_partial_fromEnd_byteArray() {
+ public void testNonEmpty_read_partial_fromEnd_byteArray() throws IOException {
fillContent("2222222222");
byte[] array = new byte[3];
assertEquals(2, store.read(8, array, 0, array.length));
@@ -337,7 +337,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_partial_fromStart_singleBuffer() {
+ public void testNonEmpty_read_partial_fromStart_singleBuffer() throws IOException {
fillContent("222222");
ByteBuffer buffer = ByteBuffer.allocate(3);
assertEquals(3, store.read(0, buffer));
@@ -345,7 +345,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_partial_fromMiddle_singleBuffer() {
+ public void testNonEmpty_read_partial_fromMiddle_singleBuffer() throws IOException {
fillContent("22223333");
ByteBuffer buffer = ByteBuffer.allocate(3);
assertEquals(3, store.read(3, buffer));
@@ -353,7 +353,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_partial_fromEnd_singleBuffer() {
+ public void testNonEmpty_read_partial_fromEnd_singleBuffer() throws IOException {
fillContent("2222222222");
ByteBuffer buffer = ByteBuffer.allocate(3);
assertEquals(2, store.read(8, buffer));
@@ -361,7 +361,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_partial_fromStart_multipleBuffers() {
+ public void testNonEmpty_read_partial_fromStart_multipleBuffers() throws IOException {
fillContent("12345678");
ByteBuffer buf1 = ByteBuffer.allocate(2);
ByteBuffer buf2 = ByteBuffer.allocate(2);
@@ -371,7 +371,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_partial_fromMiddle_multipleBuffers() {
+ public void testNonEmpty_read_partial_fromMiddle_multipleBuffers() throws IOException {
fillContent("12345678");
ByteBuffer buf1 = ByteBuffer.allocate(2);
ByteBuffer buf2 = ByteBuffer.allocate(2);
@@ -381,7 +381,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_partial_fromEnd_multipleBuffers() {
+ public void testNonEmpty_read_partial_fromEnd_multipleBuffers() throws IOException {
fillContent("123456789");
ByteBuffer buf1 = ByteBuffer.allocate(2);
ByteBuffer buf2 = ByteBuffer.allocate(2);
@@ -391,7 +391,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_fromPastEnd_byteArray() {
+ public void testNonEmpty_read_fromPastEnd_byteArray() throws IOException {
fillContent("123");
byte[] array = new byte[3];
assertEquals(-1, store.read(3, array, 0, array.length));
@@ -401,7 +401,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_fromPastEnd_singleBuffer() {
+ public void testNonEmpty_read_fromPastEnd_singleBuffer() throws IOException {
fillContent("123");
ByteBuffer buffer = ByteBuffer.allocate(3);
store.read(3, buffer);
@@ -409,7 +409,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_read_fromPastEnd_multipleBuffers() {
+ public void testNonEmpty_read_fromPastEnd_multipleBuffers() throws IOException {
fillContent("123");
ByteBuffer buf1 = ByteBuffer.allocate(2);
ByteBuffer buf2 = ByteBuffer.allocate(2);
@@ -419,28 +419,28 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_write_partial_fromStart_singleByte() {
+ public void testNonEmpty_write_partial_fromStart_singleByte() throws IOException {
fillContent("222222");
assertEquals(1, store.write(0, (byte) 1));
assertContentEquals("122222", store);
}
@Test
- public void testNonEmpty_write_partial_fromMiddle_singleByte() {
+ public void testNonEmpty_write_partial_fromMiddle_singleByte() throws IOException {
fillContent("222222");
assertEquals(1, store.write(3, (byte) 1));
assertContentEquals("222122", store);
}
@Test
- public void testNonEmpty_write_partial_fromEnd_singleByte() {
+ public void testNonEmpty_write_partial_fromEnd_singleByte() throws IOException {
fillContent("222222");
assertEquals(1, store.write(6, (byte) 1));
assertContentEquals("2222221", store);
}
@Test
- public void testNonEmpty_write_partial_fromStart_byteArray() {
+ public void testNonEmpty_write_partial_fromStart_byteArray() throws IOException {
fillContent("222222");
assertEquals(3, store.write(0, bytes("111"), 0, 3));
assertContentEquals("111222", store);
@@ -449,7 +449,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_write_partial_fromMiddle_byteArray() {
+ public void testNonEmpty_write_partial_fromMiddle_byteArray() throws IOException {
fillContent("22222222");
assertEquals(3, store.write(3, buffer("111")));
assertContentEquals("22211122", store);
@@ -458,7 +458,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_write_partial_fromBeforeEnd_byteArray() {
+ public void testNonEmpty_write_partial_fromBeforeEnd_byteArray() throws IOException {
fillContent("22222222");
assertEquals(3, store.write(6, bytes("111"), 0, 3));
assertContentEquals("222222111", store);
@@ -467,7 +467,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_write_partial_fromEnd_byteArray() {
+ public void testNonEmpty_write_partial_fromEnd_byteArray() throws IOException {
fillContent("222222");
assertEquals(3, store.write(6, bytes("111"), 0, 3));
assertContentEquals("222222111", store);
@@ -476,7 +476,7 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_write_partial_fromPastEnd_byteArray() {
+ public void testNonEmpty_write_partial_fromPastEnd_byteArray() throws IOException {
fillContent("222222");
assertEquals(3, store.write(8, bytes("111"), 0, 3));
assertContentEquals("22222200111", store);
@@ -485,84 +485,84 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_write_partial_fromStart_singleBuffer() {
+ public void testNonEmpty_write_partial_fromStart_singleBuffer() throws IOException {
fillContent("222222");
assertEquals(3, store.write(0, buffer("111")));
assertContentEquals("111222", store);
}
@Test
- public void testNonEmpty_write_partial_fromMiddle_singleBuffer() {
+ public void testNonEmpty_write_partial_fromMiddle_singleBuffer() throws IOException {
fillContent("22222222");
assertEquals(3, store.write(3, buffer("111")));
assertContentEquals("22211122", store);
}
@Test
- public void testNonEmpty_write_partial_fromBeforeEnd_singleBuffer() {
+ public void testNonEmpty_write_partial_fromBeforeEnd_singleBuffer() throws IOException {
fillContent("22222222");
assertEquals(3, store.write(6, buffer("111")));
assertContentEquals("222222111", store);
}
@Test
- public void testNonEmpty_write_partial_fromEnd_singleBuffer() {
+ public void testNonEmpty_write_partial_fromEnd_singleBuffer() throws IOException {
fillContent("222222");
assertEquals(3, store.write(6, buffer("111")));
assertContentEquals("222222111", store);
}
@Test
- public void testNonEmpty_write_partial_fromPastEnd_singleBuffer() {
+ public void testNonEmpty_write_partial_fromPastEnd_singleBuffer() throws IOException {
fillContent("222222");
assertEquals(3, store.write(8, buffer("111")));
assertContentEquals("22222200111", store);
}
@Test
- public void testNonEmpty_write_partial_fromStart_multipleBuffers() {
+ public void testNonEmpty_write_partial_fromStart_multipleBuffers() throws IOException {
fillContent("222222");
assertEquals(4, store.write(0, buffers("11", "33")));
assertContentEquals("113322", store);
}
@Test
- public void testNonEmpty_write_partial_fromMiddle_multipleBuffers() {
+ public void testNonEmpty_write_partial_fromMiddle_multipleBuffers() throws IOException {
fillContent("22222222");
assertEquals(4, store.write(2, buffers("11", "33")));
assertContentEquals("22113322", store);
}
@Test
- public void testNonEmpty_write_partial_fromBeforeEnd_multipleBuffers() {
+ public void testNonEmpty_write_partial_fromBeforeEnd_multipleBuffers() throws IOException {
fillContent("22222222");
assertEquals(6, store.write(6, buffers("111", "333")));
assertContentEquals("222222111333", store);
}
@Test
- public void testNonEmpty_write_partial_fromEnd_multipleBuffers() {
+ public void testNonEmpty_write_partial_fromEnd_multipleBuffers() throws IOException {
fillContent("222222");
assertEquals(6, store.write(6, buffers("111", "333")));
assertContentEquals("222222111333", store);
}
@Test
- public void testNonEmpty_write_partial_fromPastEnd_multipleBuffers() {
+ public void testNonEmpty_write_partial_fromPastEnd_multipleBuffers() throws IOException {
fillContent("222222");
assertEquals(4, store.write(10, buffers("11", "33")));
assertContentEquals("22222200001133", store);
}
@Test
- public void testNonEmpty_write_overwrite_sameLength() {
+ public void testNonEmpty_write_overwrite_sameLength() throws IOException {
fillContent("2222");
assertEquals(4, store.write(0, buffer("1234")));
assertContentEquals("1234", store);
}
@Test
- public void testNonEmpty_write_overwrite_greaterLength() {
+ public void testNonEmpty_write_overwrite_greaterLength() throws IOException {
fillContent("2222");
assertEquals(8, store.write(0, buffer("12345678")));
assertContentEquals("12345678", store);
@@ -682,14 +682,14 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_copy() {
+ public void testNonEmpty_copy() throws IOException {
fillContent("123456");
ByteStore copy = store.copy();
assertContentEquals("123456", copy);
}
@Test
- public void testNonEmpty_copy_multipleTimes() {
+ public void testNonEmpty_copy_multipleTimes() throws IOException {
/*
This test exposes a bug where the position of the new buffer in ByteBufferByteStore wasn't
being reset to 0 after the old buffer's content was copied into it. If the buffer was then
@@ -703,28 +703,28 @@ public abstract class AbstractByteStoreTest {
}
@Test
- public void testNonEmpty_truncate_toZero() {
+ public void testNonEmpty_truncate_toZero() throws IOException {
fillContent("123456");
store.truncate(0);
assertContentEquals("", store);
}
@Test
- public void testNonEmpty_truncate_partial() {
+ public void testNonEmpty_truncate_partial() throws IOException {
fillContent("12345678");
store.truncate(5);
assertContentEquals("12345", store);
}
@Test
- public void testNonEmpty_truncate_sizeUp() {
+ public void testNonEmpty_truncate_sizeUp() throws IOException {
fillContent("123456");
store.truncate(12);
assertContentEquals("123456", store);
}
@Test
- public void testDeletedStoreRemainsUsableWhileOpen() {
+ public void testDeletedStoreRemainsUsableWhileOpen() throws IOException {
byte[] bytes = bytes("1234567890");
store.write(0, bytes, 0, bytes.length);
diff --git a/jimfs/src/test/java/com/google/jimfs/internal/DirectoryTableTest.java b/jimfs/src/test/java/com/google/jimfs/internal/DirectoryTableTest.java
index 4e70a2c..544a0c3 100644
--- a/jimfs/src/test/java/com/google/jimfs/internal/DirectoryTableTest.java
+++ b/jimfs/src/test/java/com/google/jimfs/internal/DirectoryTableTest.java
@@ -16,6 +16,7 @@
package com.google.jimfs.internal;
+import static com.google.jimfs.internal.InternalTestUtils.byteStore;
import static com.google.jimfs.internal.Name.PARENT;
import static com.google.jimfs.internal.Name.SELF;
import static org.junit.Assert.fail;
@@ -216,8 +217,8 @@ public class DirectoryTableTest {
@Test
public void testSnapshot() {
- root.link(Name.simple("bar"), new File(2, new StubByteStore(10)));
- root.link(Name.simple("abc"), new File(3, new StubByteStore(10)));
+ root.link(Name.simple("bar"), new File(2, byteStore(10)));
+ root.link(Name.simple("abc"), new File(3, byteStore(10)));
// does not include . or .. and is sorted by the name
ASSERT.that(root.snapshot())
@@ -226,8 +227,8 @@ public class DirectoryTableTest {
@Test
public void testSnapshot_sortsUsingStringAndNotCanonicalValueOfNames() {
- table.link(caseInsensitive("FOO"), new File(2, new StubByteStore(10)));
- table.link(caseInsensitive("bar"), new File(3, new StubByteStore(10)));
+ table.link(caseInsensitive("FOO"), new File(2, byteStore(10)));
+ table.link(caseInsensitive("bar"), new File(3, byteStore(10)));
ImmutableSortedSet<Name> snapshot = table.snapshot();
Iterable<String> strings = Iterables.transform(snapshot, Functions.toStringFunction());
diff --git a/jimfs/src/test/java/com/google/jimfs/internal/FileFactoryTest.java b/jimfs/src/test/java/com/google/jimfs/internal/FileFactoryTest.java
index 3f90dc5..bc6a116 100644
--- a/jimfs/src/test/java/com/google/jimfs/internal/FileFactoryTest.java
+++ b/jimfs/src/test/java/com/google/jimfs/internal/FileFactoryTest.java
@@ -24,8 +24,6 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
-import java.nio.ByteBuffer;
-
/**
* Tests for {@link FileFactory}.
*
@@ -38,55 +36,7 @@ public class FileFactoryTest {
@Before
public void setUp() {
- factory = new FileFactory(new MemoryDisk(2) {
- @Override
- protected int allocateMoreBlocks(int count) {
- return 0;
- }
-
- @Override
- public int zero(int block, int offset, int len) {
- return 0;
- }
-
- @Override
- public void copy(int block, int copy) {
- }
-
- @Override
- public void put(int block, int offset, byte b) {
- }
-
- @Override
- public int put(int block, int offset, byte[] b, int off, int len) {
- return 0;
- }
-
- @Override
- public int put(int block, int offset, ByteBuffer buf) {
- return 0;
- }
-
- @Override
- public byte get(int block, int offset) {
- return 0;
- }
-
- @Override
- public int get(int block, int offset, byte[] b, int off, int len) {
- return 0;
- }
-
- @Override
- public int get(int block, int offset, ByteBuffer buf, int len) {
- return 0;
- }
-
- @Override
- public ByteBuffer asByteBuffer(int block, int offset, int len) {
- return null;
- }
- });
+ factory = new FileFactory(new HeapDisk(2, 2, 0));
}
@Test
diff --git a/jimfs/src/test/java/com/google/jimfs/internal/FileTest.java b/jimfs/src/test/java/com/google/jimfs/internal/FileTest.java
index 3090d3e..80d966d 100644
--- a/jimfs/src/test/java/com/google/jimfs/internal/FileTest.java
+++ b/jimfs/src/test/java/com/google/jimfs/internal/FileTest.java
@@ -17,6 +17,7 @@
package com.google.jimfs.internal;
import static com.google.jimfs.internal.FileFactoryTest.fakePath;
+import static com.google.jimfs.internal.InternalTestUtils.byteStore;
import static org.truth0.Truth.ASSERT;
import org.junit.Test;
@@ -52,11 +53,11 @@ public class FileTest {
@Test
public void testRegularFile() {
- File file = new File(0, new StubByteStore(10));
+ File file = new File(0, byteStore(10));
ASSERT.that(file.isDirectory()).isFalse();
ASSERT.that(file.isRegularFile()).isTrue();
ASSERT.that(file.isSymbolicLink()).isFalse();
- ASSERT.that(file.content()).isA(StubByteStore.class);
+ ASSERT.that(file.content()).isA(ByteStore.class);
}
@Test
diff --git a/jimfs/src/test/java/com/google/jimfs/internal/FileTreeTest.java b/jimfs/src/test/java/com/google/jimfs/internal/FileTreeTest.java
index 8368d76..c12b061 100644
--- a/jimfs/src/test/java/com/google/jimfs/internal/FileTreeTest.java
+++ b/jimfs/src/test/java/com/google/jimfs/internal/FileTreeTest.java
@@ -17,6 +17,7 @@
package com.google.jimfs.internal;
import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.jimfs.internal.InternalTestUtils.byteStore;
import static com.google.jimfs.internal.PathServiceTest.fakePathService;
import static java.nio.file.LinkOption.NOFOLLOW_LINKS;
import static org.junit.Assert.fail;
@@ -459,7 +460,7 @@ public class FileTreeTest {
private File createFile(String parent, String name) {
File dir = files.get(parent);
- File newFile = new File(new Random().nextInt(), new StubByteStore(0));
+ File newFile = new File(new Random().nextInt(), byteStore(0));
dir.asDirectory().link(Name.simple(name), newFile);
diff --git a/jimfs/src/test/java/com/google/jimfs/internal/HeapDiskReuseTest.java b/jimfs/src/test/java/com/google/jimfs/internal/HeapDiskReuseTest.java
index 25d0c31..b3c2879 100644
--- a/jimfs/src/test/java/com/google/jimfs/internal/HeapDiskReuseTest.java
+++ b/jimfs/src/test/java/com/google/jimfs/internal/HeapDiskReuseTest.java
@@ -23,8 +23,10 @@ import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
+import java.io.IOException;
+
/**
- * Tests for {@link HeapMemoryDisk} that reuse a disk for each store created. Stores are not deleted
+ * Tests for {@link HeapDisk} that reuse a disk for each store created. Stores are not deleted
* after most tests, meaning new blocks must be allocated for each store created.
*
* @author Colin Decker
@@ -32,7 +34,7 @@ import org.junit.runners.JUnit4;
@RunWith(JUnit4.class)
public class HeapDiskReuseTest extends HeapDiskTest {
- private final MemoryDisk disk = new HeapMemoryDisk(8);
+ private final HeapDisk disk = new HeapDisk(8, 1000, 1000);
@Override
protected ByteStore createByteStore() {
@@ -40,25 +42,25 @@ public class HeapDiskReuseTest extends HeapDiskTest {
}
@Test
- public void testDeletedStore_contentsReturnedToDisk() {
+ public void testDeletedStore_contentsReturnedToDisk() throws IOException {
byte[] bytes = new byte[1000];
store.opened();
store.write(0, bytes, 0, bytes.length);
- int freeBlocksAfterWrite = disk.free.size();
+ int cachedBlocksAfterWrite = disk.blockCache.size();
assertContentEquals(bytes, store);
store.deleted();
- assertEquals(freeBlocksAfterWrite, disk.free.size());
+ assertEquals(cachedBlocksAfterWrite, disk.blockCache.size());
assertContentEquals(bytes, store);
store.closed();
assertContentEquals(new byte[0], store);
- int freeBlocksAfterDelete = disk.free.size();
- assertTrue(freeBlocksAfterDelete > freeBlocksAfterWrite);
+ int cachedBlocksAfterDelete = disk.blockCache.size();
+ assertTrue(cachedBlocksAfterDelete > cachedBlocksAfterWrite);
}
}
diff --git a/jimfs/src/test/java/com/google/jimfs/internal/HeapDiskReuseWithDeleteTest.java b/jimfs/src/test/java/com/google/jimfs/internal/HeapDiskReuseWithDeleteTest.java
index edb24fd..03a2318 100644
--- a/jimfs/src/test/java/com/google/jimfs/internal/HeapDiskReuseWithDeleteTest.java
+++ b/jimfs/src/test/java/com/google/jimfs/internal/HeapDiskReuseWithDeleteTest.java
@@ -21,8 +21,8 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
- * Tests for {@link HeapMemoryDisk} that reuse a disk for each store created. Stores are deleted
- * after each test, meaning blocks will be reused.
+ * Tests for {@link HeapDisk} that reuse a disk for each store created. Stores are deleted after
+ * each test, meaning blocks will be reused.
*
* @author Colin Decker
*/
diff --git a/jimfs/src/test/java/com/google/jimfs/internal/HeapDiskTest.java b/jimfs/src/test/java/com/google/jimfs/internal/HeapDiskTest.java
index c9fed1f..2dbf11e 100644
--- a/jimfs/src/test/java/com/google/jimfs/internal/HeapDiskTest.java
+++ b/jimfs/src/test/java/com/google/jimfs/internal/HeapDiskTest.java
@@ -20,8 +20,8 @@ import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
- * Tests for {@link HeapMemoryDisk} that create a new disk for each byte store created (testing
- * fresh state).
+ * Tests for {@link HeapDisk} that create a new disk for each byte store created (testing fresh
+ * state).
*
* @author Colin Decker
*/
@@ -30,6 +30,6 @@ public class HeapDiskTest extends AbstractByteStoreTest {
@Override
protected ByteStore createByteStore() {
- return new HeapMemoryDisk(4).createByteStore();
+ return new HeapDisk(4, 100, 10).createByteStore();
}
}
diff --git a/jimfs/src/test/java/com/google/jimfs/internal/DirectDiskTest.java b/jimfs/src/test/java/com/google/jimfs/internal/InternalTestUtils.java
index 3a307a3..71d99d4 100644
--- a/jimfs/src/test/java/com/google/jimfs/internal/DirectDiskTest.java
+++ b/jimfs/src/test/java/com/google/jimfs/internal/InternalTestUtils.java
@@ -16,19 +16,23 @@
package com.google.jimfs.internal;
-import org.junit.runner.RunWith;
-import org.junit.runners.JUnit4;
+import java.io.IOException;
/**
* @author Colin Decker
*/
-@RunWith(JUnit4.class)
-public class DirectDiskTest extends AbstractByteStoreTest {
+class InternalTestUtils {
- private final MemoryDisk disk = new DirectMemoryDisk();
+ private InternalTestUtils() {}
- @Override
- protected ByteStore createByteStore() {
- return disk.createByteStore();
+
+ static ByteStore byteStore(int size) {
+ ByteStore store = new ByteStore(new HeapDisk());
+ try {
+ store.write(0, new byte[size], 0, size);
+ return store;
+ } catch (IOException e) {
+ throw new AssertionError(e);
+ }
}
}
diff --git a/jimfs/src/test/java/com/google/jimfs/internal/JimfsAsynchronousFileChannelTest.java b/jimfs/src/test/java/com/google/jimfs/internal/JimfsAsynchronousFileChannelTest.java
index 0e9c841..53f6d73 100644
--- a/jimfs/src/test/java/com/google/jimfs/internal/JimfsAsynchronousFileChannelTest.java
+++ b/jimfs/src/test/java/com/google/jimfs/internal/JimfsAsynchronousFileChannelTest.java
@@ -16,6 +16,7 @@
package com.google.jimfs.internal;
+import static com.google.jimfs.internal.InternalTestUtils.byteStore;
import static com.google.jimfs.testing.TestUtils.buffer;
import static java.nio.file.StandardOpenOption.READ;
import static java.nio.file.StandardOpenOption.WRITE;
@@ -66,17 +67,13 @@ public class JimfsAsynchronousFileChannelTest {
Options.getOptionsForChannel(ImmutableSet.copyOf(options))), executor);
}
- private static StubByteStore store(int size) throws IOException {
- return new StubByteStore(size);
- }
-
/**
* Just tests the main read/write methods... the methods all delegate to the non-async channel
* anyway.
*/
@Test
public void testAsyncChannel() throws Throwable {
- StubByteStore store = store(15);
+ ByteStore store = byteStore(15);
ExecutorService executor = Executors.newSingleThreadExecutor();
JimfsAsynchronousFileChannel channel = channel(store, executor, READ, WRITE);
@@ -86,9 +83,8 @@ public class JimfsAsynchronousFileChannelTest {
assertSame(channel, channel.truncate(5));
assertEquals(5, channel.size());
- store.setSize(10);
+ store.write(5, new byte[5], 0, 5);
checkAsyncRead(channel);
- checkAsyncReadFailure(executor);
checkAsyncWrite(channel);
checkAsyncLock(channel);
@@ -101,7 +97,7 @@ public class JimfsAsynchronousFileChannelTest {
@Test
public void testClosedChannel() throws IOException, InterruptedException {
- StubByteStore store = store(15);
+ ByteStore store = byteStore(15);
ExecutorService executor = Executors.newSingleThreadExecutor();
try {
@@ -119,7 +115,7 @@ public class JimfsAsynchronousFileChannelTest {
@Test
public void testAsyncClose_write() throws IOException, InterruptedException {
- StubByteStore store = store(15);
+ ByteStore store = byteStore(15);
ExecutorService executor = Executors.newFixedThreadPool(4);
try {
@@ -166,7 +162,7 @@ public class JimfsAsynchronousFileChannelTest {
@Test
public void testAsyncClose_read() throws IOException, InterruptedException {
- StubByteStore store = store(15);
+ ByteStore store = byteStore(15);
ExecutorService executor = Executors.newFixedThreadPool(2);
try {
@@ -242,46 +238,6 @@ public class JimfsAsynchronousFileChannelTest {
}
}
- private static void checkAsyncReadFailure(ExecutorService executor) throws Throwable {
- StubByteStore store = store(10);
- store.setThrowException(true);
- AsynchronousFileChannel channel = channel(store, executor, READ);
-
- ByteBuffer buf = buffer("1234567890");
- try {
- channel.read(buf, 0).get();
- fail();
- } catch (ExecutionException expected) {
- assertTrue(expected.getCause() instanceof RuntimeException);
- assertEquals("error", expected.getCause().getMessage());
- }
-
- buf.flip();
- final AtomicReference<Throwable> exceptionHolder = new AtomicReference<>();
- final CountDownLatch completionLatch = new CountDownLatch(1);
- channel.read(buf, 0, null, new CompletionHandler<Integer, Object>() {
- @Override
- public void completed(Integer result, Object attachment) {
- completionLatch.countDown();
- }
-
- @Override
- public void failed(Throwable exc, Object attachment) {
- exceptionHolder.set(exc);
- completionLatch.countDown();
- }
- });
-
- completionLatch.await();
- Throwable exception = exceptionHolder.get();
- if (exception == null) {
- fail();
- } else {
- assertTrue(exception instanceof RuntimeException);
- assertEquals("error", exception.getMessage());
- }
- }
-
private static void checkAsyncWrite(AsynchronousFileChannel asyncChannel) throws Throwable {
ByteBuffer buf = buffer("1234567890");
assertEquals(10, (int) asyncChannel.write(buf, 0).get());
diff --git a/jimfs/src/test/java/com/google/jimfs/internal/JimfsFileChannelTest.java b/jimfs/src/test/java/com/google/jimfs/internal/JimfsFileChannelTest.java
index 0529ba8..3cbfee4 100644
--- a/jimfs/src/test/java/com/google/jimfs/internal/JimfsFileChannelTest.java
+++ b/jimfs/src/test/java/com/google/jimfs/internal/JimfsFileChannelTest.java
@@ -16,6 +16,7 @@
package com.google.jimfs.internal;
+import static com.google.jimfs.internal.InternalTestUtils.byteStore;
import static com.google.jimfs.testing.TestUtils.assertNotEquals;
import static com.google.jimfs.testing.TestUtils.buffer;
import static com.google.jimfs.testing.TestUtils.bytes;
@@ -76,13 +77,9 @@ public class JimfsFileChannelTest {
Options.getOptionsForChannel(ImmutableSet.copyOf(options)));
}
- private static StubByteStore store(int size) throws IOException {
- return new StubByteStore(size);
- }
-
@Test
public void testPosition() throws IOException {
- FileChannel channel = channel(store(10), READ);
+ FileChannel channel = channel(byteStore(10), READ);
assertEquals(0, channel.position());
assertSame(channel, channel.position(100));
assertEquals(100, channel.position());
@@ -90,18 +87,18 @@ public class JimfsFileChannelTest {
@Test
public void testSize() throws IOException {
- StubByteStore store = store(10);
+ ByteStore store = byteStore(10);
FileChannel channel = channel(store, READ);
assertEquals(10, channel.size());
- store.setSize(100);
+ store.write(10, new byte[90], 0, 90);
assertEquals(100, channel.size());
}
@Test
public void testRead() throws IOException {
- StubByteStore store = store(20);
+ ByteStore store = byteStore(20);
FileChannel channel = channel(store, READ);
assertEquals(0, channel.position());
@@ -116,7 +113,7 @@ public class JimfsFileChannelTest {
buf.flip();
buf2.flip();
- store.setSize(30);
+ store.write(20, new byte[10], 0, 10);
assertEquals(10, channel.read(new ByteBuffer[]{buf, buf2}, 0, 2));
assertEquals(30, channel.position());
@@ -131,7 +128,7 @@ public class JimfsFileChannelTest {
@Test
public void testWrite() throws IOException {
- ByteStore store = store(0);
+ ByteStore store = byteStore(0);
FileChannel channel = channel(store, WRITE);
assertEquals(0, channel.position());
@@ -156,7 +153,7 @@ public class JimfsFileChannelTest {
@Test
public void testAppend() throws IOException {
- ByteStore store = store(0);
+ ByteStore store = byteStore(0);
FileChannel channel = channel(store, WRITE, APPEND);
assertEquals(0, channel.position());
@@ -190,7 +187,7 @@ public class JimfsFileChannelTest {
@Test
public void testTransferTo() throws IOException {
- ByteStore store = store(10);
+ ByteStore store = byteStore(10);
FileChannel channel = channel(store, READ);
ByteBufferChannel writeChannel = new ByteBufferChannel(buffer("1234567890"));
@@ -200,7 +197,7 @@ public class JimfsFileChannelTest {
@Test
public void testTransferFrom() throws IOException {
- ByteStore store = store(0);
+ ByteStore store = byteStore(0);
FileChannel channel = channel(store, WRITE);
ByteBufferChannel readChannel = new ByteBufferChannel(buffer("1234567890"));
@@ -210,7 +207,7 @@ public class JimfsFileChannelTest {
@Test
public void testTruncate() throws IOException {
- ByteStore store = store(10);
+ ByteStore store = byteStore(10);
FileChannel channel = channel(store, WRITE);
channel.truncate(10); // no resize, >= size
@@ -229,7 +226,7 @@ public class JimfsFileChannelTest {
@Test
public void testFileTimeUpdates() throws IOException {
- File file = new File(-1, new StubByteStore(10));
+ File file = new File(-1, byteStore(10));
FileChannel channel = new JimfsFileChannel(file, ImmutableSet.<OpenOption>of(READ, WRITE));
// accessed
@@ -303,7 +300,7 @@ public class JimfsFileChannelTest {
@Test
public void testClose() throws IOException {
- FileChannel channel = channel(store(0), READ, WRITE);
+ FileChannel channel = channel(byteStore(0), READ, WRITE);
ExecutorService executor = Executors.newSingleThreadExecutor();
assertTrue(channel.isOpen());
channel.close();
@@ -422,7 +419,7 @@ public class JimfsFileChannelTest {
@Test
public void testWritesInReadOnlyMode() throws IOException {
- FileChannel channel = channel(store(0), READ);
+ FileChannel channel = channel(byteStore(0), READ);
try {
channel.write(buffer("111"));
@@ -468,7 +465,7 @@ public class JimfsFileChannelTest {
@Test
public void testReadsInWriteOnlyMode() throws IOException {
- FileChannel channel = channel(store(0), WRITE);
+ FileChannel channel = channel(byteStore(0), WRITE);
try {
channel.read(buffer("111"));
@@ -508,7 +505,7 @@ public class JimfsFileChannelTest {
@Test
public void testPositionNegative() throws IOException {
- FileChannel channel = channel(store(0), READ, WRITE);
+ FileChannel channel = channel(byteStore(0), READ, WRITE);
try {
channel.position(-1);
@@ -519,7 +516,7 @@ public class JimfsFileChannelTest {
@Test
public void testTruncateNegative() throws IOException {
- FileChannel channel = channel(store(0), READ, WRITE);
+ FileChannel channel = channel(byteStore(0), READ, WRITE);
try {
channel.truncate(-1);
@@ -530,7 +527,7 @@ public class JimfsFileChannelTest {
@Test
public void testWriteNegative() throws IOException {
- FileChannel channel = channel(store(0), READ, WRITE);
+ FileChannel channel = channel(byteStore(0), READ, WRITE);
try {
channel.write(buffer("111"), -1);
@@ -554,7 +551,7 @@ public class JimfsFileChannelTest {
@Test
public void testReadNegative() throws IOException {
- FileChannel channel = channel(store(0), READ, WRITE);
+ FileChannel channel = channel(byteStore(0), READ, WRITE);
try {
channel.read(buffer("111"), -1);
@@ -578,7 +575,7 @@ public class JimfsFileChannelTest {
@Test
public void testTransferToNegative() throws IOException {
- FileChannel channel = channel(store(0), READ, WRITE);
+ FileChannel channel = channel(byteStore(0), READ, WRITE);
try {
channel.transferTo(-1, 0, new ByteBufferChannel(10));
@@ -595,7 +592,7 @@ public class JimfsFileChannelTest {
@Test
public void testTransferFromNegative() throws IOException {
- FileChannel channel = channel(store(0), READ, WRITE);
+ FileChannel channel = channel(byteStore(0), READ, WRITE);
try {
channel.transferFrom(new ByteBufferChannel(10), -1, 0);
@@ -612,7 +609,7 @@ public class JimfsFileChannelTest {
@Test
public void testLockNegative() throws IOException {
- FileChannel channel = channel(store(0), READ, WRITE);
+ FileChannel channel = channel(byteStore(0), READ, WRITE);
try {
channel.lock(-1, 10, true);
@@ -641,7 +638,7 @@ public class JimfsFileChannelTest {
@Test
public void testNullPointerExceptions() throws IOException {
- FileChannel channel = channel(store(100), READ, WRITE);
+ FileChannel channel = channel(byteStore(100), READ, WRITE);
NullPointerTester tester = new NullPointerTester();
tester.testAllPublicInstanceMethods(channel);
@@ -649,7 +646,7 @@ public class JimfsFileChannelTest {
@Test
public void testLock() throws IOException {
- FileChannel channel = channel(store(10), READ, WRITE);
+ FileChannel channel = channel(byteStore(10), READ, WRITE);
assertNotNull(channel.lock());
assertNotNull(channel.lock(0, 10, false));
@@ -667,7 +664,7 @@ public class JimfsFileChannelTest {
@Test
public void testAsynchronousClose() throws IOException, InterruptedException {
- ByteStore store = store(10);
+ ByteStore store = byteStore(10);
final FileChannel channel = channel(store, READ, WRITE);
store.writeLock().lock(); // ensure all operations on the channel will block
@@ -693,7 +690,7 @@ public class JimfsFileChannelTest {
@Test
public void testCloseByInterrupt() throws IOException, InterruptedException {
- ByteStore store = store(10);
+ ByteStore store = byteStore(10);
final FileChannel channel = channel(store, READ, WRITE);
store.writeLock().lock(); // ensure all operations on the channel will block
diff --git a/jimfs/src/test/java/com/google/jimfs/internal/JimfsInputStreamTest.java b/jimfs/src/test/java/com/google/jimfs/internal/JimfsInputStreamTest.java
index 97ac6cf..dec458b 100644
--- a/jimfs/src/test/java/com/google/jimfs/internal/JimfsInputStreamTest.java
+++ b/jimfs/src/test/java/com/google/jimfs/internal/JimfsInputStreamTest.java
@@ -16,6 +16,7 @@
package com.google.jimfs.internal;
+import static com.google.jimfs.internal.InternalTestUtils.byteStore;
import static com.google.jimfs.testing.TestUtils.bytes;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.fail;
@@ -165,7 +166,7 @@ public class JimfsInputStreamTest {
}
@Test
- public void testMark_unsupported() {
+ public void testMark_unsupported() throws IOException {
JimfsInputStream in = newInputStream(1, 2, 3);
ASSERT.that(in.markSupported()).isFalse();
@@ -218,13 +219,13 @@ public class JimfsInputStreamTest {
in.close(); // does nothing
}
- private static JimfsInputStream newInputStream(int... bytes) {
+ private static JimfsInputStream newInputStream(int... bytes) throws IOException {
byte[] b = new byte[bytes.length];
for (int i = 0; i < bytes.length; i++) {
b[i] = (byte) bytes[i];
}
- ByteStore store = new StubByteStore(0);
+ ByteStore store = byteStore(0);
store.write(0, b, 0, b.length);
return new JimfsInputStream(new File(1, store));
}
diff --git a/jimfs/src/test/java/com/google/jimfs/internal/JimfsOutputStreamTest.java b/jimfs/src/test/java/com/google/jimfs/internal/JimfsOutputStreamTest.java
index 3d495c3..9d013ed 100644
--- a/jimfs/src/test/java/com/google/jimfs/internal/JimfsOutputStreamTest.java
+++ b/jimfs/src/test/java/com/google/jimfs/internal/JimfsOutputStreamTest.java
@@ -16,6 +16,7 @@
package com.google.jimfs.internal;
+import static com.google.jimfs.internal.InternalTestUtils.byteStore;
import static com.google.jimfs.testing.TestUtils.bytes;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.fail;
@@ -165,11 +166,11 @@ public class JimfsOutputStreamTest {
}
private static JimfsOutputStream newOutputStream(boolean append) {
- File file = new File(1, new StubByteStore(0));
+ File file = new File(1, byteStore(0));
return new JimfsOutputStream(file, append);
}
- private static void addBytesToStore(JimfsOutputStream out, int... bytes) {
+ private static void addBytesToStore(JimfsOutputStream out, int... bytes) throws IOException {
ByteStore store = out.file.asBytes();
long pos = store.currentSize();
for (int b : bytes) {
diff --git a/jimfs/src/test/java/com/google/jimfs/internal/StubByteStore.java b/jimfs/src/test/java/com/google/jimfs/internal/StubByteStore.java
deleted file mode 100644
index e7f7613..0000000
--- a/jimfs/src/test/java/com/google/jimfs/internal/StubByteStore.java
+++ /dev/null
@@ -1,162 +0,0 @@
-/*
- * Copyright 2013 Google Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.google.jimfs.internal;
-
-import com.google.common.primitives.UnsignedBytes;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.WritableByteChannel;
-
-/**
- * Fake byte store implementation.
- *
- * @author Colin Decker
- */
-public class StubByteStore extends ByteStore {
-
- private final Bytes bytes = new Bytes();
- private long size;
- private boolean throwException;
-
- StubByteStore(long initialSize) {
- setSize(initialSize);
- }
-
- @Override
- public long currentSize() {
- return size;
- }
-
- @Override
- public ByteStore createCopy() {
- return new StubByteStore(size);
- }
-
- @Override
- public void deleteContents() {
- }
-
- public void setSize(long size) {
- this.size = size;
- }
-
- public void setThrowException(boolean throwException) {
- this.throwException = throwException;
- }
-
- @Override
- public boolean truncate(long size) {
- checkThrowException();
- if (size < this.size) {
- setSize(size);
- return true;
- }
- return false;
- }
-
- @Override
- public int write(long pos, byte b) {
- return write(pos, new byte[] {b}, 0, 1);
- }
-
- @Override
- public int write(long pos, byte[] b, int off, int len) {
- return write(pos, ByteBuffer.wrap(b, off, len));
- }
-
- @Override
- public int write(long pos, ByteBuffer buf) {
- checkThrowException();
- int written = buf.remaining();
- bytes.position((int) pos);
- bytes.write(buf.array(), buf.position(), buf.remaining());
- setSize(Math.max(size, pos + written));
- buf.position(buf.position() + written);
- return written;
- }
-
- @Override
- public long transferFrom(ReadableByteChannel src, long position, long count)
- throws IOException {
- checkThrowException();
- ByteBuffer buffer = ByteBuffer.allocate((int) count);
- src.read(buffer);
- buffer.flip();
- return write(position, buffer);
- }
-
- @Override
- public int read(long pos) {
- byte[] b = new byte[1];
- if (read(pos, b, 0, 1) != -1) {
- return UnsignedBytes.toInt(b[0]);
- }
- return -1;
- }
-
- @Override
- public int read(long pos, byte[] b, int off, int len) {
- return read(pos, ByteBuffer.wrap(b, off, len));
- }
-
- @Override
- public int read(long pos, ByteBuffer buf) {
- checkThrowException();
- int len = (int) Math.min(buf.remaining(), size - pos);
- if (len <= 0) {
- return -1;
- }
- byte[] b = bytes.getBytes();
- buf.put(b, (int) pos, len);
- return len;
- }
-
- @Override
- public long transferTo(long position, long count, WritableByteChannel target)
- throws IOException {
- int len = (int) Math.min(count, size - position);
- if (len > 0) {
- ByteBuffer buf = ByteBuffer.allocate(len);
- read(position, buf);
- buf.flip();
- while (buf.hasRemaining()) {
- target.write(buf);
- }
- }
- return Math.max(len, 0);
- }
-
- private void checkThrowException() {
- if (throwException) {
- throw new RuntimeException("error");
- }
- }
-
- private static class Bytes extends ByteArrayOutputStream {
-
- void position(int pos) {
- count = pos;
- }
-
- byte[] getBytes() {
- return buf;
- }
- }
-} \ No newline at end of file