diff options
Diffstat (limited to 'jimfs/src/main/java/com/google/common/jimfs/HeapDisk.java')
-rw-r--r-- | jimfs/src/main/java/com/google/common/jimfs/HeapDisk.java | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/jimfs/src/main/java/com/google/common/jimfs/HeapDisk.java b/jimfs/src/main/java/com/google/common/jimfs/HeapDisk.java new file mode 100644 index 0000000..ab06933 --- /dev/null +++ b/jimfs/src/main/java/com/google/common/jimfs/HeapDisk.java @@ -0,0 +1,145 @@ +/* + * 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.common.jimfs; + +import static com.google.common.base.Preconditions.checkArgument; + +import com.google.common.annotations.VisibleForTesting; +import com.google.common.math.LongMath; +import java.io.IOException; +import java.math.RoundingMode; + +/** + * 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 { + + /** 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. While this is stored as a file, it isn't used + * like a normal file: only the methods for accessing its blocks are used. + */ + @VisibleForTesting final RegularFile blockCache; + + /** The current total number of blocks that are currently allocated to files. */ + private int allocatedBlockCount; + + /** Creates a new disk using settings from the given configuration. */ + public HeapDisk(Configuration config) { + this.blockSize = config.blockSize; + this.maxBlockCount = toBlockCount(config.maxSize, blockSize); + this.maxCachedBlockCount = + config.maxCacheSize == -1 ? maxBlockCount : toBlockCount(config.maxCacheSize, blockSize); + this.blockCache = createBlockCache(maxCachedBlockCount); + } + + /** + * 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(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 = createBlockCache(maxCachedBlockCount); + } + + /** Returns the nearest multiple of {@code blockSize} that is <= {@code size}. */ + private static int toBlockCount(long size, int blockSize) { + return (int) LongMath.divide(size, blockSize, RoundingMode.FLOOR); + } + + private RegularFile createBlockCache(int maxCachedBlockCount) { + return new RegularFile(-1, this, new byte[Math.min(maxCachedBlockCount, 8192)][], 0, 0); + } + + /** 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 them to the given file. */ + public synchronized void allocate(RegularFile file, int count) throws IOException { + int newAllocatedBlockCount = allocatedBlockCount + count; + if (newAllocatedBlockCount > maxBlockCount) { + throw new IOException("out of disk space"); + } + + int newBlocksNeeded = Math.max(count - blockCache.blockCount(), 0); + + for (int i = 0; i < newBlocksNeeded; i++) { + file.addBlock(new byte[blockSize]); + } + + if (newBlocksNeeded != count) { + blockCache.transferBlocksTo(file, count - newBlocksNeeded); + } + + allocatedBlockCount = newAllocatedBlockCount; + } + + /** Frees all blocks in the given file. */ + public void free(RegularFile file) { + free(file, file.blockCount()); + } + + /** Frees the last {@code count} blocks from the given file. */ + public synchronized void free(RegularFile file, int count) { + int remainingCacheSpace = maxCachedBlockCount - blockCache.blockCount(); + if (remainingCacheSpace > 0) { + file.copyBlocksTo(blockCache, Math.min(count, remainingCacheSpace)); + } + file.truncateBlocks(file.blockCount() - count); + + allocatedBlockCount -= count; + } +} |