profuse/Bitmap.cpp

244 lines
5.2 KiB
C++

#include "Bitmap.h"
#include <cstring>
namespace ProDOS {
/*
*
*
*
*/
// 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;
}
inline static unsigned countLeadingZeros(uint8_t x)
{
if (x == 0) return sizeof(uint8_t);
#ifdef __GNUC__
return __builtin_clz(x) + sizeof(uint8_t) - sizeof(unsigned);
#endif
unsigned rv = 0;
if ((x & 0xf0) == 0) { x <<= 4; rv = 4; }
if (x & 0x80) return rv;
if (x & 0x40) return rv + 1;
if (x & 0x20) return rv + 2;
if (x & 0x10) return rv + 3;
// unreachable
return 0;
}
inline static unsigned countLeadingOnes(uint8_t x)
{
return countLeadingZeros(~x);
}
Bitmap::Bitmap(unsigned blocks) :
_blocks(blocks),
_freeBlocks(blocks),
_bitmapSize((blocks + 4096 - 1) >> 12),
_bitmap(NULL)
{
// 1 block = 512 bytes = 4096 bits
_bitmap = new uint8_t[_bitmapSize];
// mark every requested block as free.
unsigned bytes = blocks >> 3;
std::memset(_bitmap, 0xff, bytes);
// all trailing blocks are marked as used.
std::memset(_bitmap + bytes, 0x00, _bitmapSize - bytes);
// and handle the edge case...
/*
* 0 -> 0 0 0 0 0 0 0 0
* 1 -> 1 0 0 0 0 0 0 0
* 2 -> 1 1 0 0 0 0 0 0
* 3 -> 1 1 1 0 0 0 0 0
* ...
* 7 -> 1 1 1 1 1 1 1 0
*/
//unsigned tmp = (1 << (8 - (blocks & 0x07))) - 1;
//_bitmap[bytes] = ~tmp & 0xff;
_bitmap[bytes] = 0 - (1 << (8 - (blocks & 0x07)));
//_bitmap[bytes] = (0xff00 >> (blocks & 0x07)) & 0xff;
}
Bitmap::~Bitmap()
{
delete[] _bitmap;
}
bool Bitmap::markBlock(unsigned block, bool inUse)
{
if (block >= _blocks) return false;
unsigned index = BlockIndex(block);
unsigned mask = BlockMask(block);
uint8_t data = _bitmap[index];
_freeBlocks -= popCount(data);
if (inUse) data &= ~mask;
else data |= mask;
_bitmap[index] = data;
_freeBlocks += popCount(data);
return true;
}
// find the first block starting from (and including) the
// startingBlock
int Bitmap::firstFreeBlock(unsigned startingBlock) const
{
if (startingBlock >= _blocks) return -1;
if (!_freeBlocks) return -1;
unsigned index = BlockIndex(startingBlock);
unsigned bit = startingBlock & 0x0f;
unsigned bytes = (_blocks + 7) >> 3;
// special case for first (partial) bitmap
if (bit != 0)
{
uint8_t data = _bitmap[index];
unsigned mask = BlockMask(startingBlock);
// 0 0 1 0 0 0 0 0 -> 0 0 1 1 1 1 1 1
mask = (mask - 1) | mask;
data &= mask;
if (data) return (index << 3) + countLeadingZeros(data);
++index;
}
for ( ; index < bytes; ++index)
{
uint8_t data = _bitmap[index];
if (!data) continue;
return (index << 3) + countLeadingZeros(data);
}
return -1;
}
// count the number of unused blocks.... (including startingBlock)
int Bitmap::countUnusedBlocks(unsigned startingBlock, unsigned maxSearch) const
{
if (startingBlock >= _blocks) return -1;
if (!_freeBlocks) return -1;
unsigned count = 0;
unsigned index = BlockIndex(startingBlock);
unsigned bit = startingBlock & 0x0f;
unsigned bytes = (_blocks + 7) >> 3;
// special case for the first (partial) byte.
if (bit)
{
uint8_t data = _bitmap[index];
if (data == 0) return 0;
unsigned mask = BlockMask(startingBlock);
// 0 0 1 0 0 0 0 0 -> 1 1 0 0 0 0 0 0
mask = ~ ((mask - 1) | mask);
data = data | mask;
if (data == 0xff)
{
count = 8 - bit;
if (count >= maxSearch) return count;
++index;
}
else
{
return countLeadingOnes(data) - bit;
}
}
for ( ; index < bytes; ++index)
{
uint8_t data = _bitmap[index];
// no free blocks = end search
if (data == 0) break;
// all free = continue (if necessary)
if (data == 0xff)
{
count += 8;
if (count >= maxSearch) break;
continue;
}
// otherwise, add on any leading free and terminate.
count += countLeadingOnes(data);
break;
}
return count;
}
// finds the first free block (with a possible range).
int Bitmap::freeBlock(unsigned count) const
{
if (count == 0 || count > freeBlocks()) return -1;
// we could keep a linked list/etc of
// free ranges
// for now, scan the entire bitmap.
int startBlock = 0;
--count;
for(;;)
{
startBlock = firstFreeBlock(startBlock);
if (startBlock < 0) return -1;
if (count == 0) return startBlock;
int tmp = countUnusedBlocks(startBlock + 1, count);
if (tmp <= 0) break;
if (tmp >= count) return startBlock;
// miss ... jump ahead.
startBlock += tmp + 1;
}
return -1;
}
} // namespace