/* * 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 * . */ #include #include #include "virbitmap.h" #include "viralloc.h" #include "virbuffer.h" #include "virstring.h" #include "virutil.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 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 * * 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) { 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) { 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; bool neg = false; const char *cur = str; char *tmp; size_t i; int start, last; if (!(bitmap = virBitmapNewEmpty())) return NULL; 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 provided by gnulib, 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 provided by gnulib, 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 = virHexToBin(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; }