libvirt/src/util/virbitmap.c
Laine Stump f2090f362d util: use g_auto() for all virBuffers
Signed-off-by: Laine Stump <laine@redhat.com>
Reviewed-by: Ján Tomko <jtomko@redhat.com>
2020-07-08 16:34:26 -04:00

1330 lines
28 KiB
C

/*
* virbitmap.c: Simple bitmap operations
*
* Copyright (C) 2010-2013 Red Hat, Inc.
* Copyright (C) 2010 Novell, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <sys/types.h>
#include "virbitmap.h"
#include "viralloc.h"
#include "virbuffer.h"
#include "virstring.h"
#include "virerror.h"
#define VIR_FROM_THIS VIR_FROM_NONE
struct _virBitmap {
size_t nbits;
size_t map_len;
size_t map_alloc;
/* Note that code below depends on the fact that unused bits of the bitmap
* are not set. Any function decreasing the size of the map needs clear
* bits which don't belong to the bitmap any more. */
unsigned long *map;
};
#define VIR_BITMAP_BITS_PER_UNIT ((int) sizeof(unsigned long) * CHAR_BIT)
#define VIR_BITMAP_UNIT_OFFSET(b) ((b) / VIR_BITMAP_BITS_PER_UNIT)
#define VIR_BITMAP_BIT_OFFSET(b) ((b) % VIR_BITMAP_BITS_PER_UNIT)
#define VIR_BITMAP_BIT(b) (1UL << VIR_BITMAP_BIT_OFFSET(b))
/**
* virBitmapNewQuiet:
* @size: number of bits
*
* Allocate a bitmap capable of containing @size bits.
*
* Returns a pointer to the allocated bitmap or NULL if either memory cannot be
* allocated or size is 0. Does not report libvirt errors.
*/
virBitmapPtr
virBitmapNewQuiet(size_t size)
{
virBitmapPtr bitmap;
size_t sz;
if (SIZE_MAX - VIR_BITMAP_BITS_PER_UNIT < size || size == 0)
return NULL;
sz = VIR_DIV_UP(size, VIR_BITMAP_BITS_PER_UNIT);
if (VIR_ALLOC_QUIET(bitmap) < 0)
return NULL;
if (VIR_ALLOC_N_QUIET(bitmap->map, sz) < 0) {
VIR_FREE(bitmap);
return NULL;
}
bitmap->nbits = size;
bitmap->map_len = sz;
bitmap->map_alloc = sz;
return bitmap;
}
/**
* virBitmapNew:
* @size: number of bits
*
* Allocate a bitmap capable of containing @size bits.
*
* Returns a pointer to the allocated bitmap or NULL if either memory cannot be
* allocated or size is 0. Reports libvirt errors.
*/
virBitmapPtr
virBitmapNew(size_t size)
{
virBitmapPtr ret;
if (!(ret = virBitmapNewQuiet(size)))
virReportOOMError();
return ret;
}
/**
* virBitmapNewEmpty:
*
* Allocate an empty bitmap. It can be used with self-expanding APIs.
*
* Returns a pointer to the allocated bitmap.
*/
virBitmapPtr
virBitmapNewEmpty(void)
{
return g_new0(virBitmap, 1);
}
/**
* virBitmapFree:
* @bitmap: previously allocated bitmap
*
* Free @bitmap previously allocated by virBitmapNew.
*/
void
virBitmapFree(virBitmapPtr bitmap)
{
if (bitmap) {
VIR_FREE(bitmap->map);
VIR_FREE(bitmap);
}
}
/**
* virBitmapCopy:
* @dst: destination bitmap
* @src: source bitmap
*
* Copies contents of @src to @dst. @dst must have the same size as @src.
* Returns -1 if the size is not the same or 0 on success.
*/
int
virBitmapCopy(virBitmapPtr dst,
virBitmapPtr src)
{
if (dst->nbits != src->nbits) {
errno = EINVAL;
return -1;
}
memcpy(dst->map, src->map, src->map_len * sizeof(src->map[0]));
return 0;
}
/**
* virBitmapSetBit:
* @bitmap: Pointer to bitmap
* @b: bit position to set
*
* Set bit position @b in @bitmap
*
* Returns 0 on if bit is successfully set, -1 on error.
*/
int
virBitmapSetBit(virBitmapPtr bitmap,
size_t b)
{
if (bitmap->nbits <= b)
return -1;
bitmap->map[VIR_BITMAP_UNIT_OFFSET(b)] |= VIR_BITMAP_BIT(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 + 1, 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->nbits = 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->nbits <= 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
* @b: bit position to clear
*
* Clear bit position @b in @bitmap
*
* Returns 0 on if bit is successfully clear, -1 on error.
*/
int
virBitmapClearBit(virBitmapPtr bitmap,
size_t b)
{
if (bitmap->nbits <= b)
return -1;
bitmap->map[VIR_BITMAP_UNIT_OFFSET(b)] &= ~VIR_BITMAP_BIT(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->nbits <= 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->nbits */
static bool
virBitmapIsSet(virBitmapPtr bitmap, size_t b)
{
return !!(bitmap->map[VIR_BITMAP_UNIT_OFFSET(b)] & VIR_BITMAP_BIT(b));
}
/**
* virBitmapIsBitSet:
* @bitmap: Pointer to bitmap
* @b: bit position to get
*
* Get setting of bit position @b in @bitmap.
*
* If @b is in the range of @bitmap, returns the value of the bit.
* Otherwise false is returned.
*/
bool
virBitmapIsBitSet(virBitmapPtr bitmap,
size_t b)
{
if (bitmap->nbits <= b)
return false;
return virBitmapIsSet(bitmap, b);
}
/**
* virBitmapGetBit:
* @bitmap: Pointer to bitmap
* @b: bit position to get
* @result: bool pointer to receive bit setting
*
* Get setting of bit position @b in @bitmap and store in @result
*
* On success, @result will contain the setting of @b and 0 is
* returned. On failure, -1 is returned and @result is unchanged.
*/
int
virBitmapGetBit(virBitmapPtr bitmap,
size_t b,
bool *result)
{
if (bitmap->nbits <= b)
return -1;
*result = virBitmapIsSet(bitmap, b);
return 0;
}
/**
* virBitmapToString:
* @bitmap: Pointer to bitmap
* @prefix: Whether to prepend "0x"
* @trim: Whether to output only the minimum required characters
*
* Convert @bitmap to printable string.
*
* Returns pointer to the string or NULL on error.
*/
char *
virBitmapToString(virBitmapPtr bitmap,
bool prefix,
bool trim)
{
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
size_t sz;
size_t len;
size_t diff;
char *ret = NULL;
if (prefix)
virBufferAddLit(&buf, "0x");
sz = bitmap->map_len;
while (sz--) {
virBufferAsprintf(&buf, "%0*lx",
VIR_BITMAP_BITS_PER_UNIT / 4,
bitmap->map[sz]);
}
ret = virBufferContentAndReset(&buf);
if (!ret)
return NULL;
if (!trim)
return ret;
if (bitmap->nbits != bitmap->map_len * VIR_BITMAP_BITS_PER_UNIT) {
char *tmp = ret;
if (prefix)
tmp += 2;
len = strlen(tmp);
sz = VIR_DIV_UP(bitmap->nbits, 4);
diff = len - sz;
if (diff)
memmove(tmp, tmp + diff, sz + 1);
}
return ret;
}
/**
* virBitmapFormat:
* @bitmap: the bitmap
*
* This function is the counterpart of virBitmapParse. This function creates
* a human-readable string representing the bits in bitmap.
*
* See virBitmapParse for the format of @str.
*
* If bitmap is NULL or it has no bits set, an empty string is returned.
*
* Returns the string on success or NULL otherwise. Caller should call
* VIR_FREE to free the string.
*/
char *
virBitmapFormat(virBitmapPtr bitmap)
{
g_auto(virBuffer) buf = VIR_BUFFER_INITIALIZER;
bool first = true;
int start, cur, prev;
if (!bitmap || (cur = virBitmapNextSetBit(bitmap, -1)) < 0) {
char *ret;
ret = g_strdup("");
return ret;
}
start = prev = cur;
while (prev >= 0) {
cur = virBitmapNextSetBit(bitmap, prev);
if (cur == prev + 1) {
prev = cur;
continue;
}
/* cur < 0 or cur > prev + 1 */
if (!first)
virBufferAddLit(&buf, ",");
else
first = false;
if (prev == start)
virBufferAsprintf(&buf, "%d", start);
else
virBufferAsprintf(&buf, "%d-%d", start, prev);
start = prev = cur;
}
return virBufferContentAndReset(&buf);
}
/**
* virBitmapParseSeparator:
* @str: points to a string representing a human-readable bitmap
* @terminator: character separating the bitmap to parse
* @bitmap: a bitmap created from @str
* @bitmapSize: the upper limit of num of bits in created bitmap
*
* This function is the counterpart of virBitmapFormat. This function creates
* a bitmap, in which bits are set according to the content of @str.
*
* @str is a comma separated string of fields N, which means a number of bit
* to set, and ^N, which means to unset the bit, and N-M for ranges of bits
* to set.
*
* To allow parsing of bitmaps within larger strings it is possible to set
* a termination character in the argument @terminator. When the character
* in @terminator is encountered in @str, the parsing of the bitmap stops.
* Pass 0 as @terminator if it is not needed. Whitespace characters may not
* be used as terminators.
*
* Returns 0 on success, or -1 in case of error.
*/
int
virBitmapParseSeparator(const char *str,
char terminator,
virBitmapPtr *bitmap,
size_t bitmapSize)
{
bool neg = false;
const char *cur = str;
char *tmp;
size_t i;
int start, last;
if (!(*bitmap = virBitmapNew(bitmapSize)))
return -1;
if (!str)
goto error;
virSkipSpaces(&cur);
if (*cur == '\0')
goto error;
while (*cur != 0 && *cur != terminator) {
/*
* 3 constructs are allowed:
* - N : a single CPU number
* - N-M : a range of CPU numbers with N < M
* - ^N : remove a single CPU number from the current set
*/
if (*cur == '^') {
cur++;
neg = true;
}
if (!g_ascii_isdigit(*cur))
goto error;
if (virStrToLong_i(cur, &tmp, 10, &start) < 0)
goto error;
if (start < 0)
goto error;
cur = tmp;
virSkipSpaces(&cur);
if (*cur == ',' || *cur == 0 || *cur == terminator) {
if (neg) {
if (virBitmapClearBit(*bitmap, start) < 0)
goto error;
} else {
if (virBitmapSetBit(*bitmap, start) < 0)
goto error;
}
} else if (*cur == '-') {
if (neg)
goto error;
cur++;
virSkipSpaces(&cur);
if (virStrToLong_i(cur, &tmp, 10, &last) < 0)
goto error;
if (last < start)
goto error;
cur = tmp;
for (i = start; i <= last; i++) {
if (virBitmapSetBit(*bitmap, i) < 0)
goto error;
}
virSkipSpaces(&cur);
}
if (*cur == ',') {
cur++;
virSkipSpaces(&cur);
neg = false;
} else if (*cur == 0 || *cur == terminator) {
break;
} else {
goto error;
}
}
return 0;
error:
virReportError(VIR_ERR_INVALID_ARG,
_("Failed to parse bitmap '%s'"), str);
virBitmapFree(*bitmap);
*bitmap = NULL;
return -1;
}
/**
* virBitmapParse:
* @str: points to a string representing a human-readable bitmap
* @bitmap: a bitmap created from @str
* @bitmapSize: the upper limit of num of bits in created bitmap
*
* This function is the counterpart of virBitmapFormat. This function creates
* a bitmap, in which bits are set according to the content of @str.
*
* @str is a comma separated string of fields N, which means a number of bit
* to set, and ^N, which means to unset the bit, and N-M for ranges of bits
* to set.
*
* Returns 0 on success, or -1 in case of error.
*/
int
virBitmapParse(const char *str,
virBitmapPtr *bitmap,
size_t bitmapSize)
{
return virBitmapParseSeparator(str, '\0', bitmap, bitmapSize);
}
/**
* virBitmapParseUnlimited:
* @str: points to a string representing a human-readable bitmap
*
* This function is the counterpart of virBitmapFormat. This function creates
* a bitmap, in which bits are set according to the content of @str.
*
* The bitmap is expanded to accommodate all the bits.
*
* @str is a comma separated string of fields N, which means a number of bit
* to set, and ^N, which means to unset the bit, and N-M for ranges of bits
* to set.
*
* Returns @bitmap on success, or NULL in case of error
*/
virBitmapPtr
virBitmapParseUnlimited(const char *str)
{
virBitmapPtr bitmap = virBitmapNewEmpty();
bool neg = false;
const char *cur = str;
char *tmp;
size_t i;
int start, last;
if (!str)
goto error;
virSkipSpaces(&cur);
if (*cur == '\0')
goto error;
while (*cur != 0) {
/*
* 3 constructs are allowed:
* - N : a single CPU number
* - N-M : a range of CPU numbers with N < M
* - ^N : remove a single CPU number from the current set
*/
if (*cur == '^') {
cur++;
neg = true;
}
if (!g_ascii_isdigit(*cur))
goto error;
if (virStrToLong_i(cur, &tmp, 10, &start) < 0)
goto error;
if (start < 0)
goto error;
cur = tmp;
virSkipSpaces(&cur);
if (*cur == ',' || *cur == 0) {
if (neg) {
if (virBitmapClearBitExpand(bitmap, start) < 0)
goto error;
} else {
if (virBitmapSetBitExpand(bitmap, start) < 0)
goto error;
}
} else if (*cur == '-') {
if (neg)
goto error;
cur++;
virSkipSpaces(&cur);
if (virStrToLong_i(cur, &tmp, 10, &last) < 0)
goto error;
if (last < start)
goto error;
cur = tmp;
for (i = start; i <= last; i++) {
if (virBitmapSetBitExpand(bitmap, i) < 0)
goto error;
}
virSkipSpaces(&cur);
}
if (*cur == ',') {
cur++;
virSkipSpaces(&cur);
neg = false;
} else if (*cur == 0) {
break;
} else {
goto error;
}
}
return bitmap;
error:
virReportError(VIR_ERR_INVALID_ARG,
_("Failed to parse bitmap '%s'"), NULLSTR(str));
virBitmapFree(bitmap);
return NULL;
}
/**
* virBitmapNewCopy:
* @src: the source bitmap.
*
* Makes a copy of bitmap @src.
*
* returns the copied bitmap on success, or NULL otherwise. Caller
* should call virBitmapFree to free the returned bitmap.
*/
virBitmapPtr
virBitmapNewCopy(virBitmapPtr src)
{
virBitmapPtr dst;
if ((dst = virBitmapNew(src->nbits)) == NULL)
return NULL;
if (virBitmapCopy(dst, src) != 0) {
virBitmapFree(dst);
return NULL;
}
return dst;
}
/**
* virBitmapNewData:
* @data: the data
* @len: length of @data in bytes
*
* Allocate a bitmap from a chunk of data containing bits
* information
*
* Returns a pointer to the allocated bitmap or NULL if
* memory cannot be allocated.
*/
virBitmapPtr
virBitmapNewData(const void *data,
int len)
{
virBitmapPtr bitmap;
size_t i, j;
unsigned long *p;
const unsigned char *bytes = data;
bitmap = virBitmapNew(len * CHAR_BIT);
if (!bitmap)
return NULL;
/* le64toh is not available, so we do the conversion by hand */
p = bitmap->map;
for (i = j = 0; i < len; i++, j++) {
if (j == sizeof(*p)) {
j = 0;
p++;
}
*p |= (unsigned long) bytes[i] << (j * CHAR_BIT);
}
return bitmap;
}
/**
* virBitmapToData:
* @data: the data
* @len: len of @data in byte
*
* Convert a bitmap to a chunk of data containing bits information.
* Data consists of sequential bytes, with lower bytes containing
* lower bits. This function allocates @data.
*
* Returns 0 on success, -1 otherwise.
*/
int
virBitmapToData(virBitmapPtr bitmap,
unsigned char **data,
int *dataLen)
{
ssize_t len;
if ((len = virBitmapLastSetBit(bitmap)) < 0)
len = 1;
else
len = (len + CHAR_BIT) / CHAR_BIT;
if (VIR_ALLOC_N(*data, len) < 0)
return -1;
*dataLen = len;
virBitmapToDataBuf(bitmap, *data, *dataLen);
return 0;
}
/**
* virBitmapToDataBuf:
* @bytes: pointer to memory to fill
* @len: len of @bytes in byte
*
* Convert a bitmap to a chunk of data containing bits information.
* Data consists of sequential bytes, with lower bytes containing
* lower bits.
*/
void
virBitmapToDataBuf(virBitmapPtr bitmap,
unsigned char *bytes,
size_t len)
{
size_t nbytes = bitmap->map_len * (VIR_BITMAP_BITS_PER_UNIT / CHAR_BIT);
unsigned long *l;
size_t i, j;
memset(bytes, 0, len);
/* If bitmap and buffer differ in size, only fill to the smaller length */
len = MIN(len, nbytes);
/* htole64 is not available, so we do the conversion by hand */
l = bitmap->map;
for (i = j = 0; i < len; i++, j++) {
if (j == sizeof(*l)) {
j = 0;
l++;
}
bytes[i] = *l >> (j * CHAR_BIT);
}
}
/**
* virBitmapEqual:
* @b1: bitmap 1
* @b2: bitmap 2
*
* Compares two bitmaps, whose lengths can be different from each other.
*
* Returns true if two bitmaps have exactly the same set of bits set,
* otherwise false.
*/
bool
virBitmapEqual(virBitmapPtr b1,
virBitmapPtr b2)
{
virBitmapPtr tmp;
size_t i;
if (!b1 && !b2)
return true;
if (!b1 || !b2)
return false;
if (b1->nbits > b2->nbits) {
tmp = b1;
b1 = b2;
b2 = tmp;
}
/* Now b1 is the smaller one, if not equal */
for (i = 0; i < b1->map_len; i++) {
if (b1->map[i] != b2->map[i])
return false;
}
for (; i < b2->map_len; i++) {
if (b2->map[i])
return false;
}
return true;
}
/**
* virBitmapSize:
* @bitmap: virBitmap to inspect
*
* Returns number of bits @bitmap can store.
*/
size_t
virBitmapSize(virBitmapPtr bitmap)
{
return bitmap->nbits;
}
/**
* virBitmapSetAll:
* @bitmap: the bitmap
*
* set all bits in @bitmap.
*/
void virBitmapSetAll(virBitmapPtr bitmap)
{
int tail = bitmap->nbits % VIR_BITMAP_BITS_PER_UNIT;
memset(bitmap->map, 0xff,
bitmap->map_len * (VIR_BITMAP_BITS_PER_UNIT / CHAR_BIT));
/* Ensure tail bits are clear. */
if (tail)
bitmap->map[bitmap->map_len - 1] &=
-1UL >> (VIR_BITMAP_BITS_PER_UNIT - tail);
}
/**
* virBitmapClearAll:
* @bitmap: the bitmap
*
* clear all bits in @bitmap.
*/
void
virBitmapClearAll(virBitmapPtr bitmap)
{
memset(bitmap->map, 0,
bitmap->map_len * (VIR_BITMAP_BITS_PER_UNIT / CHAR_BIT));
}
/**
* virBitmapIsAllSet:
* @bitmap: the bitmap to check
*
* check if all bits in @bitmap are set.
*/
bool
virBitmapIsAllSet(virBitmapPtr bitmap)
{
size_t i;
int unusedBits;
size_t sz;
unusedBits = bitmap->map_len * VIR_BITMAP_BITS_PER_UNIT - bitmap->nbits;
sz = bitmap->map_len;
if (unusedBits > 0)
sz--;
for (i = 0; i < sz; i++)
if (bitmap->map[i] != -1)
return false;
if (unusedBits > 0) {
if ((bitmap->map[sz] & ((1UL << (VIR_BITMAP_BITS_PER_UNIT - unusedBits)) - 1))
!= ((1UL << (VIR_BITMAP_BITS_PER_UNIT - unusedBits)) - 1))
return false;
}
return true;
}
/**
* virBitmapIsAllClear:
* @bitmap: the bitmap to check
*
* check if all bits in @bitmap are clear
*/
bool
virBitmapIsAllClear(virBitmapPtr bitmap)
{
size_t i;
for (i = 0; i < bitmap->map_len; i++)
if (bitmap->map[i] != 0)
return false;
return true;
}
/**
* virBitmapNextSetBit:
* @bitmap: the bitmap
* @pos: the position after which to search for a set bit
*
* Search for the first set bit after position @pos in bitmap @bitmap.
* @pos can be -1 to search for the first set bit. Position starts
* at 0.
*
* Returns the position of the found bit, or -1 if no bit found.
*/
ssize_t
virBitmapNextSetBit(virBitmapPtr bitmap,
ssize_t pos)
{
size_t nl;
size_t nb;
unsigned long bits;
if (pos < 0)
pos = -1;
pos++;
if (pos >= bitmap->nbits)
return -1;
nl = pos / VIR_BITMAP_BITS_PER_UNIT;
nb = pos % VIR_BITMAP_BITS_PER_UNIT;
bits = bitmap->map[nl] & ~((1UL << nb) - 1);
while (bits == 0 && ++nl < bitmap->map_len)
bits = bitmap->map[nl];
if (bits == 0)
return -1;
return __builtin_ffsl(bits) - 1 + nl * VIR_BITMAP_BITS_PER_UNIT;
}
/**
* virBitmapLastSetBit:
* @bitmap: the bitmap
*
* Search for the last set bit in bitmap @bitmap.
*
* Returns the position of the found bit, or -1 if no bit is set.
*/
ssize_t
virBitmapLastSetBit(virBitmapPtr bitmap)
{
ssize_t i;
int unusedBits;
ssize_t sz;
unsigned long bits;
/* If bitmap is empty then there is no set bit */
if (bitmap->map_len == 0)
return -1;
unusedBits = bitmap->map_len * VIR_BITMAP_BITS_PER_UNIT - bitmap->nbits;
sz = bitmap->map_len - 1;
if (unusedBits > 0) {
bits = bitmap->map[sz] & (VIR_BITMAP_BIT(VIR_BITMAP_BITS_PER_UNIT - unusedBits) - 1);
if (bits != 0)
goto found;
sz--;
}
for (; sz >= 0; sz--) {
bits = bitmap->map[sz];
if (bits != 0)
goto found;
}
/* Only reached if no set bit was found */
return -1;
found:
for (i = VIR_BITMAP_BITS_PER_UNIT - 1; i >= 0; i--) {
if (bits & 1UL << i)
return i + sz * VIR_BITMAP_BITS_PER_UNIT;
}
return -1;
}
/**
* virBitmapNextClearBit:
* @bitmap: the bitmap
* @pos: the position after which to search for a clear bit
*
* Search for the first clear bit after position @pos in bitmap @bitmap.
* @pos can be -1 to search for the first set bit. Position starts
* at 0.
*
* Returns the position of the found bit, or -1 if no bit found.
*/
ssize_t
virBitmapNextClearBit(virBitmapPtr bitmap,
ssize_t pos)
{
size_t nl;
size_t nb;
unsigned long bits;
if (pos < 0)
pos = -1;
pos++;
if (pos >= bitmap->nbits)
return -1;
nl = pos / VIR_BITMAP_BITS_PER_UNIT;
nb = pos % VIR_BITMAP_BITS_PER_UNIT;
bits = ~bitmap->map[nl] & ~((1UL << nb) - 1);
while (bits == 0 && ++nl < bitmap->map_len)
bits = ~bitmap->map[nl];
if (nl == bitmap->map_len - 1) {
/* Ensure tail bits are ignored. */
int tail = bitmap->nbits % VIR_BITMAP_BITS_PER_UNIT;
if (tail)
bits &= -1UL >> (VIR_BITMAP_BITS_PER_UNIT - tail);
}
if (bits == 0)
return -1;
return __builtin_ffsl(bits) - 1 + nl * VIR_BITMAP_BITS_PER_UNIT;
}
/**
* virBitmapCountBits:
* @bitmap: bitmap to inspect
*
* Return the number of bits currently set in @bitmap.
*/
size_t
virBitmapCountBits(virBitmapPtr bitmap)
{
size_t i;
size_t ret = 0;
for (i = 0; i < bitmap->map_len; i++)
ret += __builtin_popcountl(bitmap->map[i]);
return ret;
}
/**
* virBitmapNewString:
* @string: the string to be converted to a bitmap
*
* Allocate a bitmap from a string of hexadecimal data.
*
* Returns a pointer to the allocated bitmap or NULL if
* memory cannot be allocated.
*/
virBitmapPtr
virBitmapNewString(const char *string)
{
virBitmapPtr bitmap;
size_t i = 0;
size_t len = strlen(string);
if (strspn(string, "0123456789abcdefABCDEF") != len) {
virReportError(VIR_ERR_INVALID_ARG,
_("Invalid hexadecimal string '%s'"), string);
return NULL;
}
bitmap = virBitmapNew(len * 4);
if (!bitmap)
return NULL;
for (i = 0; i < len; i++) {
unsigned long nibble = g_ascii_xdigit_value(string[len - i - 1]);
nibble <<= VIR_BITMAP_BIT_OFFSET(i * 4);
bitmap->map[VIR_BITMAP_UNIT_OFFSET(i * 4)] |= nibble;
}
return bitmap;
}
/**
* virBitmapDataFormat:
* @data: the data
* @len: length of @data in bytes
*
* Convert a chunk of data containing bits information to a human
* readable string, e.g.: 0-1,4
*
* Returns: a string representation of the data, or NULL on error
*/
char *
virBitmapDataFormat(const void *data,
int len)
{
g_autoptr(virBitmap) map = NULL;
if (!(map = virBitmapNewData(data, len)))
return NULL;
return virBitmapFormat(map);
}
/**
* virBitmapOverlaps:
* @b1: virBitmap to inspect
* @b2: virBitmap to inspect
*
* Returns true if at least one bit with the same index is set both in @b1 and
* @b2.
*/
bool
virBitmapOverlaps(virBitmapPtr b1,
virBitmapPtr b2)
{
size_t i;
if (b1->nbits > b2->nbits) {
virBitmapPtr tmp = b1;
b1 = b2;
b2 = tmp;
}
for (i = 0; i < b1->map_len; i++) {
if (b1->map[i] & b2->map[i])
return true;
}
return false;
}
/**
* virBitmapIntersect:
* @a: bitmap, modified to contain result
* @b: bitmap
*
* Performs intersection of two bitmaps: a = intersect(a, b)
*/
void
virBitmapIntersect(virBitmapPtr a,
virBitmapPtr b)
{
size_t i;
size_t max = a->map_len;
if (max > b->map_len)
max = b->map_len;
for (i = 0; i < max; i++)
a->map[i] &= b->map[i];
}
/**
* virBitmapUnion:
* @a: bitmap, modified to contain result
* @b: other bitmap
*
* Performs union of two bitmaps: a = union(a, b)
*
* Returns 0 on success, <0 on failure.
*/
int
virBitmapUnion(virBitmapPtr a,
const virBitmap *b)
{
size_t i;
if (a->nbits < b->nbits &&
virBitmapExpand(a, b->nbits - 1) < 0) {
return -1;
}
for (i = 0; i < b->map_len; i++)
a->map[i] |= b->map[i];
return 0;
}
/**
* virBitmapSubtract:
* @a: minuend/result
* @b: subtrahend
*
* Performs subtraction of two bitmaps: a = a - b
*/
void
virBitmapSubtract(virBitmapPtr a,
virBitmapPtr b)
{
size_t i;
size_t max = a->map_len;
if (max > b->map_len)
max = b->map_len;
for (i = 0; i < max; i++)
a->map[i] &= ~b->map[i];
}
/**
* virBitmapShrink:
* @map: Pointer to bitmap
* @b: Size to reduce the bitmap to
*
* Reduces the bitmap to size @b. Nothing will change if the size is already
* smaller than or equal to @b.
*/
void
virBitmapShrink(virBitmapPtr map,
size_t b)
{
size_t toremove;
size_t nl = 0;
size_t nb = 0;
if (!map)
return;
if (map->nbits >= b)
map->nbits = b;
nl = map->nbits / VIR_BITMAP_BITS_PER_UNIT;
nb = map->nbits % VIR_BITMAP_BITS_PER_UNIT;
map->map[nl] &= ((1UL << nb) - 1);
toremove = map->map_alloc - (nl + 1);
if (toremove == 0)
return;
VIR_SHRINK_N(map->map, map->map_alloc, toremove);
/* length needs to be fixed as well */
map->map_len = map->map_alloc;
}