profuse/Bitmap.cpp

197 lines
3.9 KiB
C++

#include "Bitmap.h"
#include <cstring>
#include "auto.h"
// returns # of 1-bits set (0-8)
inline static unsigned popCount(uint8_t x)
{
#ifdef __GNUC__
return __builtin_popcount(x);
#endif
// Brian Kernighan / Peter Wegner in CACM 3 (1960), 322.
unsigned count;
for (count = 0; x; ++count)
{
x &= x - 1;
}
return count;
}
Bitmap::Bitmap(unsigned blocks)
{
_blocks = _freeBlocks = blocks;
_bitmapBlocks = (blocks + 4095) / 4096;
_freeIndex = 0;
unsigned bitmapSize = _bitmapBlocks * 512;
unsigned blockSize = blocks / 8;
auto_array bitmap(new uint8_t[bitmapSize]);
// mark overflow in use, everything else free.
std::memset(bitmap, 0xff, blocks / 8);
std::memset(bitmap + blockSize, 0x00, bitmapSize - blockSize);
// edge case
unsigned tmp = blocks & 0x07;
bitmap[blocks / 8] = ~(0xff >> tmp);
_bitmap = bitmap.release();
}
Bitmap::Bitmap(BlockDevice *device, unsigned keyPointer, unsigned blocks)
{
_blocks = blocks;
_freeBlocks = 0;
_freeIndex = 0;
_bitmapBlocks = (blocks + 4095) / 4096;
unsigned bitmapSize = _bitmapBlocks * 512;
unsigned blockSize = blocks / 8;
auto_array bitmap(new uint8_t[bitmapSize]);
for (unsigned i = 0; i < blockSize; ++i)
{
device->readBlock(keyPointer + i, bitmap + 512 * i);
}
// make sure all trailing bits are marked in use.
// edge case
unsigned tmp = blocks & 0x07;
bitmap[blocks / 8] &= ~(0xff >> tmp);
std::memset(bitmap + blockSize, 0x00, bitmapSize - blockSize);
// set _freeBlocks and _freeIndex;
for (unsigned i = 0; i < (blocks + 7) / 8; ++i)
{
_freeBlocks += popCount(bitmap[i]);
}
if (_freeblocks)
{
for (unsigned i = 0; i < (blocks + 7) / 8; ++i)
{
if (tmp[i])
{
_freeIndex = i;
break;
}
}
}
_bitmap = bitmap.release();
}
Bitmap::~Bitmap()
{
if (_bitmap) delete []_bitmap;
}
void Bitmap::freeBlock(unsigned block)
{
if (block >= _blocks) return;
unsigned index = block / 8;
unsigned offset = block & 0x07;
unsigned mask = 0x80 >> offset;
uint8_t tmp = _bitmap[index];
if ((tmp & mask) == 0)
{
++_freeBlocks;
_bitmap[index] = tmp | mask;
}
}
int Bitmap::allocBlock(unsigned block)
{
if (block >= _blocks) return -1;
unsigned index = block / 8;
unsigned offset = block & 0x07;
unsigned mask = 0x80 >> offset;
uint8_t tmp = _bitmap[index];
if ((tmp & mask))
{
--_freeBlocks;
_bitmap[index] = tmp & ~mask;
return block;
}
return -1;
}
int Bitmap::allocBlock()
{
if (!_freeBlocks) return -1;
unsigned firstIndex = _firstIndex;
unsigned maxIndex = (_blocks + 7) / 8;
for (unsigned index = _firstIndex; index < maxIndex; ++index)
{
uint8_t tmp = _bitmap[index];
if (!tmp) continue;
unsigned mask = 0x80;
for (unsigned offset = 0; offset < 8; ++offset)
{
if (tmp & mask)
{
_firstIndex = index;
_bitmap[index] = tmp & ~mask;
--_freeBlocks;
return index * 8 + offset;
}
mask = mask >> 1;
}
}
for (unsigned index = 0; index < _firstIndex; ++index)
{
uint8_t tmp = _bitmap[index];
if (!tmp) continue;
unsigned mask = 0x80;
for (unsigned offset = 0; offset < 8; ++offset)
{
if (tmp & mask)
{
_firstIndex = index;
_bitmap[index] = tmp & ~mask;
--_freeBlocks;
return index * 8 + offset;
}
mask = mask >> 1;
}
}
// should never happen...
return -1;
}