util: bitmap: Introduce self-expanding bitmap APIs

In some cases it's impractical to use the regular APIs as the bitmap
size needs to be pre-declared. These new APIs allow to use bitmaps that
self expand.

The new code adds a property to the bitmap to track the allocation of
memory so that VIR_RESIZE_N can be used.
This commit is contained in:
Peter Krempa 2016-03-18 15:41:59 +01:00
parent 4ed5937d71
commit 917426c8d7
4 changed files with 158 additions and 0 deletions

View File

@ -1134,6 +1134,7 @@ virAuthConfigNewData;
# util/virbitmap.h
virBitmapClearAll;
virBitmapClearBit;
virBitmapClearBitExpand;
virBitmapCopy;
virBitmapCountBits;
virBitmapDataToString;
@ -1148,6 +1149,7 @@ virBitmapLastSetBit;
virBitmapNew;
virBitmapNewCopy;
virBitmapNewData;
virBitmapNewEmpty;
virBitmapNewQuiet;
virBitmapNextClearBit;
virBitmapNextSetBit;
@ -1155,6 +1157,7 @@ virBitmapOverlaps;
virBitmapParse;
virBitmapSetAll;
virBitmapSetBit;
virBitmapSetBitExpand;
virBitmapSize;
virBitmapString;
virBitmapSubtract;

View File

@ -43,6 +43,7 @@
struct _virBitmap {
size_t max_bit;
size_t map_len;
size_t map_alloc;
unsigned long *map;
};
@ -83,6 +84,7 @@ virBitmapNewQuiet(size_t size)
bitmap->max_bit = size;
bitmap->map_len = sz;
bitmap->map_alloc = sz;
return bitmap;
}
@ -108,6 +110,25 @@ virBitmapNew(size_t size)
}
/**
* virBitmapNewEmpty:
*
* Allocate an empty bitmap. It can be used with self-expanding APIs.
*
* Returns a pointer to the allocated bitmap or NULL if memory cannot be
* allocated. Reports libvirt errors.
*/
virBitmapPtr
virBitmapNewEmpty(void)
{
virBitmapPtr ret;
ignore_value(VIR_ALLOC(ret));
return ret;
}
/**
* virBitmapFree:
* @bitmap: previously allocated bitmap
@ -154,6 +175,54 @@ int virBitmapSetBit(virBitmapPtr bitmap, size_t b)
return 0;
}
/**
* virBitmapExpand:
* @map: Pointer to bitmap
* @b: bit position to include in bitmap
*
* Resizes the bitmap so that bit @b will fit into it. This shall be called only
* if @b would not fit into the map.
*
* Returns 0 on success, -1 on error.
*/
static int virBitmapExpand(virBitmapPtr map, size_t b)
{
size_t new_len = VIR_DIV_UP(b, VIR_BITMAP_BITS_PER_UNIT);
/* resize the memory if necessary */
if (map->map_len < new_len) {
if (VIR_RESIZE_N(map->map, map->map_alloc, map->map_len,
new_len - map->map_len) < 0)
return -1;
}
map->max_bit = b + 1;
map->map_len = new_len;
return 0;
}
/**
* virBitmapSetBitExpand:
* @bitmap: Pointer to bitmap
* @b: bit position to set
*
* Set bit position @b in @bitmap. Expands the bitmap as necessary so that @b is
* included in the map.
*
* Returns 0 on if bit is successfully set, -1 on error.
*/
int virBitmapSetBitExpand(virBitmapPtr bitmap, size_t b)
{
if (bitmap->max_bit <= b && virBitmapExpand(bitmap, b) < 0)
return -1;
bitmap->map[VIR_BITMAP_UNIT_OFFSET(b)] |= VIR_BITMAP_BIT(b);
return 0;
}
/**
* virBitmapClearBit:
* @bitmap: Pointer to bitmap
@ -172,6 +241,30 @@ int virBitmapClearBit(virBitmapPtr bitmap, size_t b)
return 0;
}
/**
* virBitmapClearBitExpand:
* @bitmap: Pointer to bitmap
* @b: bit position to set
*
* Clear bit position @b in @bitmap. Expands the bitmap as necessary so that
* @b is included in the map.
*
* Returns 0 on if bit is successfully cleared, -1 on error.
*/
int virBitmapClearBitExpand(virBitmapPtr bitmap, size_t b)
{
if (bitmap->max_bit <= b) {
if (virBitmapExpand(bitmap, b) < 0)
return -1;
} else {
bitmap->map[VIR_BITMAP_UNIT_OFFSET(b)] &= ~VIR_BITMAP_BIT(b);
}
return 0;
}
/* Helper function. caller must ensure b < bitmap->max_bit */
static bool virBitmapIsSet(virBitmapPtr bitmap, size_t b)
{

View File

@ -37,6 +37,7 @@ typedef virBitmap *virBitmapPtr;
*/
virBitmapPtr virBitmapNewQuiet(size_t size) ATTRIBUTE_RETURN_CHECK;
virBitmapPtr virBitmapNew(size_t size) ATTRIBUTE_RETURN_CHECK;
virBitmapPtr virBitmapNewEmpty(void) ATTRIBUTE_RETURN_CHECK;
/*
* Free previously allocated bitmap
@ -55,12 +56,19 @@ int virBitmapCopy(virBitmapPtr dst, virBitmapPtr src);
int virBitmapSetBit(virBitmapPtr bitmap, size_t b)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
int virBitmapSetBitExpand(virBitmapPtr bitmap, size_t b)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
/*
* Clear bit position @b in @bitmap
*/
int virBitmapClearBit(virBitmapPtr bitmap, size_t b)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
int virBitmapClearBitExpand(virBitmapPtr bitmap, size_t b)
ATTRIBUTE_NONNULL(1) ATTRIBUTE_RETURN_CHECK;
/*
* Get bit @b in @bitmap. Returns false if b is out of range.
*/

View File

@ -590,6 +590,57 @@ test11(const void *opaque)
return ret;
}
#define TEST_MAP(sz, expect) \
do { \
char *actual; \
if (virBitmapSize(map) != sz) { \
fprintf(stderr, "\n expected bitmap size: '%d' actual size: " \
"'%zu'\n", sz, virBitmapSize(map)); \
goto cleanup; \
} \
\
actual = virBitmapFormat(map); \
\
if (STRNEQ_NULLABLE(expect, actual)) { \
fprintf(stderr, "\n expected bitmap contents '%s' actual contents "\
"'%s'\n", NULLSTR(expect), NULLSTR(actual)); \
VIR_FREE(actual); \
goto cleanup; \
} \
VIR_FREE(actual); \
} while (0)
/* test self-expanding bitmap APIs */
static int
test12(const void *opaque ATTRIBUTE_UNUSED)
{
virBitmapPtr map = NULL;
int ret = -1;
if (!(map = virBitmapNewEmpty()))
return -1;
TEST_MAP(0, "");
if (virBitmapSetBitExpand(map, 100) < 0)
goto cleanup;
TEST_MAP(101, "100");
if (virBitmapClearBitExpand(map, 150) < 0)
goto cleanup;
TEST_MAP(151, "100");
ret = 0;
cleanup:
virBitmapFree(map);
return ret;
}
#undef TEST_MAP
#define TESTBINARYOP(A, B, RES, FUNC) \
testBinaryOpData.a = A; \
testBinaryOpData.b = B; \
@ -633,6 +684,9 @@ mymain(void)
TESTBINARYOP("0-3", "0,^0", "0-3", test11);
TESTBINARYOP("0,2", "1,3", "0,2", test11);
if (virtTestRun("test12", test12, NULL) < 0)
ret = -1;
return ret;
}