#include "bitmap.h"
#define BITMAP_MASK_FULL (~((bmcell_t)0))
#define BITMAP_CELL_SIZE (sizeof(bmcell_t) * 8)
#define BITMAP_CELL_SHIFT 6
#define BITMAP_SUBCELL_SHIFT 3
#define BITMAP_TEST(map, i) (map[i >> BITMAP_CELL_SHIFT] & (((bmcell_t)1) << i))
#define BITMAP_SET(map, i) (map[i >> BITMAP_CELL_SHIFT] |= (((bmcell_t)1) << i))
#define BITMAP_USET(map, i) (map[i >> BITMAP_CELL_SHIFT] &= ~(((bmcell_t)1) << i))
void
bmvl_setup(struct bitmap_vl *bm, size_t bits)
{
bm->head.length = bm->head.bits_free = bits;
bm->head.last_free = 0;
for(size_t i = 0; i < (bits / BITMAP_BITS_PER_CELL); i++) {
bm->cells[i] = 0;
}
}
bool
__bm_test(bmcell_t *cells, size_t i)
{
return BITMAP_TEST(cells, i) != 0;
}
bm_signed_t
__bm_take(struct bitmap_head *head, bmcell_t *cells)
{
/*Definitely OOM*/
if(head->bits_free == 0) return -1;
/*If last_free is already free, we can just return that*/
if(BITMAP_TEST(cells, head->last_free) == 0) {
BITMAP_SET(cells, head->last_free);
return head->last_free++;
}
/*Search all cells after last_free if last_free was taken.
* It could mean we're OOM, or that we've missed a gap somewhere*/
for(size_t cell = (head->last_free >> BITMAP_CELL_SHIFT);
cell < (head->length >> BITMAP_CELL_SHIFT);
cell++)
{
/*A full bitmask means that every bit in the cell is taken*/
if(cells[cell] == BITMAP_MASK_FULL) continue;
/*We sub-divide the search into bytes, so that we can take advantage
* of mask searches*/
uint8_t *subcells = (uint8_t*)(&cells[cell]);
for(uint8_t subcell = 0; subcell < 8; subcell++) {
/*If the byte is full, we search the next byte*/
if(subcells[subcell] == 0xFF) continue;
/*A per-bit search is necessary to find the exact free bit*/
for(uint8_t bit = 0; bit < 8; bit++) {
if(subcells[subcell] & (1 << bit)) continue;
/*Mark the bit as taken*/
subcells[subcell] |= ((bmcell_t)1 << bit);
/*Because we're operating on pow2, we can use bitshifts to
* simplify 2^x.
* Since subcell and bit can only be 1-7, we can OR it into
* the shifted cell value*/
head->last_free =
(cell << BITMAP_CELL_SHIFT) |
(subcell << BITMAP_SUBCELL_SHIFT) |
bit;
/*This returns the last_free, then increments so that the next
* call can (possibly) get a free value*/
head->bits_free--;
return head->last_free++;
}
}
}
/*Getting here means the bitmap couldn't find a free bit and we are
* presumably OOM*/
return -1;
}
bm_signed_t
__bm_take_contig(struct bitmap_head *head, bmcell_t *cells, size_t l)
{
/*If the bitmap doesn't have enough free bits for the span, it will never
* have enough contiguous bits*/
if(head->bits_free < l) return -1;
/*Simul completion flag and utility value for calculating offsets for return
* value*/
size_t left = l;
for(size_t cell = (head->last_free >> BITMAP_CELL_SHIFT);
cell < (head->length >> BITMAP_CELL_SHIFT);
cell++)
{
/*Return case for breakout when we find the contig span*/
if(left == 0) break;
/*If the bitmask is full, there won't be room to fit the requested span*/
if(cells[cell] == BITMAP_MASK_FULL) {
left = l;
continue;
}
/*If the bitmask is 0, there are atleast BITMAP_CELL_SIZE contig bits
* in the bitmap starting here*/
if(cells[cell] == 0) {
/*Only continue the loop if there aren't enough bits in this current
* cell.*/
if(left > BITMAP_CELL_SIZE) {
left -= BITMAP_CELL_SIZE;
continue;
}
/*Because there was room for the rest of the span, we can return
* this address without worrying about fetching other bits.
* The cell index is subtracted by the inverse of left*/
head->last_free =
(cell << BITMAP_CELL_SHIFT) - (l - left);
/*Mark left so that we break out of this loop and call the proper
* return function*/
left = 0;
break;
}
/*Divide search into bytes for faster search of bmcell_t structure*/
uint8_t *subcells = (uint8_t*)(&cells[cell]);
for(uint8_t subcell = 0; subcell < 8; subcell++) {
if(left == 0) break;
/*Subcell is full, we refill left and stop searching this subcell*/
if(subcells[subcell] == 0xFF) {
left = l;
continue;
}
/*Since the subcell is empty, there are atleast 8 contig bits*/
if(subcells[subcell] == 0) {
/*If we need more than 8 bits, we subtract 8 from left and
* continue*/
if(left > 8) {
left -= 8;
continue;
}
/*Otherwise, we return the index of this particular subcell
* minus the inverse of left*/
head->last_free =
((cell << BITMAP_CELL_SHIFT) |
(subcell << BITMAP_SUBCELL_SHIFT)) -
(l - left);
/*Mark left for proper exit and return*/
left = 0;
break;
}
/*Search the entire subcell for free bits.*/
for(uint8_t bit = 0; bit < 8; bit++) {
/*When we find a taken bit, and there are still bits to fetch,
* then the block will never be contiguous and we need to
* start again*/
if(subcells[subcell] & (1 << bit)) {
left = l;
continue;
}
left--;
if(left == 0) {
/*When we have found all the bits we need, we return the
* index to the start of the contig span*/
head->last_free =
((cell << BITMAP_CELL_SHIFT) |
(subcell << BITMAP_SUBCELL_SHIFT) |
bit) -
(l - left);
break;
}
}
}
}
/*We didn't find a contiguous span. Either the bitmap is fragmented or
* there's not enough free space to fit the span*/
if(left != 0) return -1;
/*Mark all of the bits in the span as taken*/
for(size_t i = 0; i < l; i++) {
BITMAP_SET(cells, (head->last_free + i));
}
/*Setup to return last_free, while incrementing last_free by l*/
head->last_free += l;
head->bits_free -= l;
return head->last_free - l;
}
void
__bm_reserve(struct bitmap_head *head, bmcell_t *cells, size_t i)
{
/*Take the bit at i and update the bits_free counter*/
BITMAP_SET(cells, i);
head->bits_free--;
}
void
__bm_reserve_contig(
struct bitmap_head *head,
bmcell_t *cells,
size_t i,
size_t l)
{
/*For every bit between i and i+l, mark as reserved*/
head->bits_free -= l;
for(; l != 0; l--, i++) {
BITMAP_SET(cells, i);
}
}
void
__bm_release(struct bitmap_head *head, bmcell_t *cells, size_t i)
{
/*Release the bit at i, update last_free, and update the bits_free counter*/
BITMAP_USET(cells, i);
if(head->last_free > i) head->last_free = i;
head->bits_free++;
}
void
__bm_release_contig(
struct bitmap_head *head,
bmcell_t *cells,
size_t i,
size_t l)
{
/*Update the last_free and bits_free counters*/
head->bits_free += i;
if(head->last_free > i) head->last_free = i;
/*For every bit between i and i+l, mark as free*/
for(; l != 0; l--, i++) {
BITMAP_USET(cells, i);
}
}