org.h2.mvstore.FreeSpaceBitSet Maven / Gradle / Ivy
/*
* Copyright 2004-2014 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (http://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.mvstore;
import java.util.BitSet;
import org.h2.util.MathUtils;
/**
* A free space bit set.
*/
public class FreeSpaceBitSet {
private static final boolean DETAILED_INFO = false;
/**
* The first usable block.
*/
private final int firstFreeBlock;
/**
* The block size in bytes.
*/
private final int blockSize;
/**
* The bit set.
*/
private final BitSet set = new BitSet();
/**
* Create a new free space map.
*
* @param firstFreeBlock the first free block
* @param blockSize the block size
*/
public FreeSpaceBitSet(int firstFreeBlock, int blockSize) {
this.firstFreeBlock = firstFreeBlock;
this.blockSize = blockSize;
clear();
}
/**
* Reset the list.
*/
public void clear() {
set.clear();
set.set(0, firstFreeBlock);
}
/**
* Check whether one of the blocks is in use.
*
* @param pos the position in bytes
* @param length the number of bytes
* @return true if a block is in use
*/
public boolean isUsed(long pos, int length) {
int start = getBlock(pos);
int blocks = getBlockCount(length);
for (int i = start; i < start + blocks; i++) {
if (!set.get(i)) {
return false;
}
}
return true;
}
/**
* Check whether one of the blocks is free.
*
* @param pos the position in bytes
* @param length the number of bytes
* @return true if a block is free
*/
public boolean isFree(long pos, int length) {
int start = getBlock(pos);
int blocks = getBlockCount(length);
for (int i = start; i < start + blocks; i++) {
if (set.get(i)) {
return false;
}
}
return true;
}
/**
* Allocate a number of blocks and mark them as used.
*
* @param length the number of bytes to allocate
* @return the start position in bytes
*/
public long allocate(int length) {
int blocks = getBlockCount(length);
for (int i = 0;;) {
int start = set.nextClearBit(i);
int end = set.nextSetBit(start + 1);
if (end < 0 || end - start >= blocks) {
set.set(start, start + blocks);
return getPos(start);
}
i = end;
}
}
/**
* Mark the space as in use.
*
* @param pos the position in bytes
* @param length the number of bytes
*/
public void markUsed(long pos, int length) {
int start = getBlock(pos);
int blocks = getBlockCount(length);
set.set(start, start + blocks);
}
/**
* Mark the space as free.
*
* @param pos the position in bytes
* @param length the number of bytes
*/
public void free(long pos, int length) {
int start = getBlock(pos);
int blocks = getBlockCount(length);
set.clear(start, start + blocks);
}
private long getPos(int block) {
return (long) block * (long) blockSize;
}
private int getBlock(long pos) {
return (int) (pos / blockSize);
}
private int getBlockCount(int length) {
return MathUtils.roundUpInt(length, blockSize) / blockSize;
}
/**
* Get the fill rate of the space in percent. The value 0 means the space is
* completely free, and 100 means it is completely full.
*
* @return the fill rate (0 - 100)
*/
public int getFillRate() {
int total = set.length(), count = 0;
for (int i = 0; i < total; i++) {
if (set.get(i)) {
count++;
}
}
if (count == 0) {
return 0;
}
return Math.max(1, (int) (100L * count / total));
}
/**
* Get the position of the first free space.
*
* @return the position.
*/
public long getFirstFree() {
return getPos(set.nextClearBit(0));
}
@Override
public String toString() {
StringBuilder buff = new StringBuilder();
if (DETAILED_INFO) {
int onCount = 0, offCount = 0;
int on = 0;
for (int i = 0; i < set.length(); i++) {
if (set.get(i)) {
onCount++;
on++;
} else {
offCount++;
}
if ((i & 1023) == 1023) {
buff.append(String.format("%3x", on)).append(' ');
on = 0;
}
}
buff.append("\n");
buff.append(" on " + onCount + " off " + offCount);
buff.append(" " + 100 * onCount / (onCount+offCount) + "% used ");
}
buff.append('[');
for (int i = 0;;) {
if (i > 0) {
buff.append(", ");
}
int start = set.nextClearBit(i);
buff.append(Integer.toHexString(start)).append('-');
int end = set.nextSetBit(start + 1);
if (end < 0) {
break;
}
buff.append(Integer.toHexString(end - 1));
i = end + 1;
}
buff.append(']');
return buff.toString();
}
}