net.sf.ehcache.store.disk.ods.FileAllocationTree Maven / Gradle / Ivy
/**
* Copyright Terracotta, 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 net.sf.ehcache.store.disk.ods;
import java.io.IOException;
import java.io.RandomAccessFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* File allocation tree allows C-like alloc/free operations on a random access file.
*
* @author Chris Dennis
*/
public final class FileAllocationTree extends RegionSet {
private static final Logger LOGGER = LoggerFactory.getLogger(FileAllocationTree.class);
private long fileSize;
private final RandomAccessFile data;
/**
* Create a file allocation tree for the given file, capping it's size at maxSize.
*/
public FileAllocationTree(long maxSize, RandomAccessFile file) {
super(maxSize);
this.data = file;
}
/**
* Allocate a new region of the given size.
*/
public synchronized Region alloc(long size) {
Region r = find(size);
mark(r);
return r;
}
/**
* Mark this region as used
*/
public synchronized void mark(Region r) {
Region current = removeAndReturn(Long.valueOf(r.start()));
if (current == null) {
throw new IllegalArgumentException();
}
Region newRange = current.remove(r);
if (newRange != null) {
add(current);
add(newRange);
} else if (!current.isNull()) {
add(current);
}
checkGrow(r);
}
/**
* Mark this region as free.
*/
public synchronized void free(Region r) {
// Step 1 : Check if the previous number is present, if so add to the same Range.
Region prev = removeAndReturn(Long.valueOf(r.start() - 1));
if (prev != null) {
prev.merge(r);
Region next = removeAndReturn(Long.valueOf(r.end() + 1));
if (next != null) {
prev.merge(next);
}
add(prev);
checkShrink(prev);
return;
}
// Step 2 : Check if the next number is present, if so add to the same Range.
Region next = removeAndReturn(Long.valueOf(r.end() + 1));
if (next != null) {
next.merge(r);
add(next);
checkShrink(next);
return;
}
// Step 3: Add a new range for just this number.
add(r);
checkShrink(r);
}
/**
* Mark this whole file as free
*/
@Override
public synchronized void clear() {
super.clear();
}
private void checkGrow(Region alloc) {
if (alloc.end() >= fileSize) {
fileSize = alloc.end() + 1;
grow(fileSize);
}
}
private void checkShrink(Region free) {
if (free.end() >= fileSize - 1) {
fileSize = free.start();
shrink(fileSize);
}
}
private void grow(long size) {
//no-op
}
private void shrink(long size) {
if (data == null) {
return;
} else {
synchronized (data) {
try {
data.setLength(size);
} catch (IOException e) {
LOGGER.info("Exception while trying to shrink file", e);
}
}
}
}
/**
* Return the current occupied size of this file.
*/
public synchronized long getFileSize() {
return fileSize;
}
}