mirror of
https://github.com/ksherlock/profuse.git
synced 2024-12-23 11:31:43 +00:00
dd0703bd09
git-svn-id: https://profuse.googlecode.com/svn/trunk@59 aa027e90-d47c-11dd-86d7-074df07e0730
244 lines
5.2 KiB
C++
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
|
|
|