diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 7c44047281..3e110081bf 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -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; diff --git a/src/util/virbitmap.c b/src/util/virbitmap.c index f116607998..9283aef173 100644 --- a/src/util/virbitmap.c +++ b/src/util/virbitmap.c @@ -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) { diff --git a/src/util/virbitmap.h b/src/util/virbitmap.h index 846aca3682..79f53dd378 100644 --- a/src/util/virbitmap.h +++ b/src/util/virbitmap.h @@ -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. */ diff --git a/tests/virbitmaptest.c b/tests/virbitmaptest.c index 967a5c8291..a030a92e03 100644 --- a/tests/virbitmaptest.c +++ b/tests/virbitmaptest.c @@ -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; }