| /* 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; |
| } |
| |