summaryrefslogblamecommitdiffstats
path: root/bitmap.c
blob: cdc1b46c0d11ae6b52ce0384ff15143ba6fcbcc5 (plain) (tree)





































































































































































































































                                                                                  
#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);
    }
}