blob: ebbe25571ed90be132d62c47db59226ac1ad90aa [file] [log] [blame] [edit]
/* GPLv2 or OpenIB.org BSD (MIT) See COPYING file */
#define _GNU_SOURCE
#include "bitmap.h"
#include <string.h>
#include <strings.h>
#include <ccan/minmax.h>
#define BMP_WORD_INDEX(n) ((n) / BITS_PER_LONG)
#define BMP_WORD_OFFSET(n) ((n) % BITS_PER_LONG)
#define BMP_FIRST_WORD_MASK(start) (~0UL << BMP_WORD_OFFSET(start))
#define BMP_LAST_WORD_MASK(end) (BMP_WORD_OFFSET(end) == 0 ? ~0UL : \
~BMP_FIRST_WORD_MASK(end))
/*
* Finds the first set bit in the bitmap starting from
* 'start' bit until ('end'-1) bit.
*
* Returns the set bit index if found, otherwise returns 'end'.
*/
unsigned long bitmap_find_first_bit(const unsigned long *bmp,
unsigned long start, unsigned long end)
{
unsigned long curr_offset = BMP_WORD_OFFSET(start);
unsigned long curr_idx = BMP_WORD_INDEX(start);
assert(start <= end);
for (; start < end; curr_idx++) {
unsigned long bit = ffsl(bmp[curr_idx] >> curr_offset);
if (bit)
return min(end, start + bit - 1);
start += BITS_PER_LONG - curr_offset;
curr_offset = 0;
}
return end;
}
/*
* Zeroes bitmap bits in the following range: [start,end-1]
*/
void bitmap_zero_region(unsigned long *bmp, unsigned long start,
unsigned long end)
{
unsigned long start_mask;
unsigned long last_mask;
unsigned long curr_idx = BMP_WORD_INDEX(start);
unsigned long last_idx = BMP_WORD_INDEX(end - 1);
assert(start <= end);
if (start >= end)
return;
start_mask = BMP_FIRST_WORD_MASK(start);
last_mask = BMP_LAST_WORD_MASK(end);
if (curr_idx == last_idx) {
bmp[curr_idx] &= ~(start_mask & last_mask);
return;
}
bmp[curr_idx] &= ~start_mask;
for (curr_idx++; curr_idx < last_idx; curr_idx++)
bmp[curr_idx] = 0;
bmp[curr_idx] &= ~last_mask;
}
/*
* Sets bitmap bits in the following range: [start,end-1]
*/
void bitmap_fill_region(unsigned long *bmp, unsigned long start,
unsigned long end)
{
unsigned long start_mask;
unsigned long last_mask;
unsigned long curr_idx = BMP_WORD_INDEX(start);
unsigned long last_idx = BMP_WORD_INDEX(end - 1);
assert(start <= end);
if (start >= end)
return;
start_mask = BMP_FIRST_WORD_MASK(start);
last_mask = BMP_LAST_WORD_MASK(end);
if (curr_idx == last_idx) {
bmp[curr_idx] |= (start_mask & last_mask);
return;
}
bmp[curr_idx] |= start_mask;
for (curr_idx++; curr_idx < last_idx; curr_idx++)
bmp[curr_idx] = ULONG_MAX;
bmp[curr_idx] |= last_mask;
}
/*
* Checks whether the contiguous region of region_size bits starting from
* start is free.
*
* Returns true if the said region is free, otherwise returns false.
*/
static bool bitmap_is_free_region(unsigned long *bmp, unsigned long start,
unsigned long region_size)
{
unsigned long curr_idx;
unsigned long last_idx;
unsigned long last_mask;
unsigned long start_mask;
curr_idx = BMP_WORD_INDEX(start);
start_mask = BMP_FIRST_WORD_MASK(start);
last_idx = BMP_WORD_INDEX(start + region_size - 1);
last_mask = BMP_LAST_WORD_MASK(start + region_size);
if (curr_idx == last_idx)
return !(bmp[curr_idx] & start_mask & last_mask);
if (bmp[curr_idx] & start_mask)
return false;
for (curr_idx++; curr_idx < last_idx; curr_idx++) {
if (bmp[curr_idx])
return false;
}
return !(bmp[curr_idx] & last_mask);
}
/*
* Finds a contiguous region with the size of region_size
* in the bitmap that is not set.
*
* Returns first index of such region if found,
* otherwise returns nbits.
*/
unsigned long bitmap_find_free_region(unsigned long *bmp,
unsigned long nbits,
unsigned long region_size)
{
unsigned long start;
if (!region_size)
return 0;
for (start = 0; start + region_size <= nbits; start++) {
if (bitmap_test_bit(bmp, start))
continue;
if (bitmap_is_free_region(bmp, start, region_size))
return start;
}
return nbits;
}