diff --git a/HACKING b/HACKING index 4a9516745a1ad5ef01027d64b79f9c185f42487f..35ceb29822f0c4381f9ea4d850b27239dbaef0d5 100644 --- a/HACKING +++ b/HACKING @@ -308,7 +308,7 @@ routines, use the macros from memory.h. - To allocate an array of object pointers: virDomainPtr *domains; - int ndomains = 10; + size_t ndomains = 10; if (VIR_ALLOC_N(domains, ndomains) < 0) { virReportOOMError(); @@ -317,24 +317,57 @@ routines, use the macros from memory.h. -- To re-allocate the array of domains to be longer: +- To re-allocate the array of domains to be 1 element longer (however, note that +repeatedly expanding an array by 1 scales quadratically, so this is +recommended only for smaller arrays): + + virDomainPtr domains; + size_t ndomains = 0; - if (VIR_EXPAND_N(domains, ndomains, 10) < 0) { + if (VIR_EXPAND_N(domains, ndomains, 1) < 0) { virReportOOMError(); return NULL; } + domains[ndomains - 1] = domain; + + + +- To ensure an array has room to hold at least one more element (this approach +scales better, but requires tracking allocation separately from usage) + + virDomainPtr domains; + size_t ndomains = 0; + size_t ndomains_max = 0; + + if (VIR_RESIZE_N(domains, ndomains_max, ndomains, 1) < 0) { + virReportOOMError(); + return NULL; + } + domains[ndomains++] = domain; - To trim an array of domains to have one less element: - VIR_SHRINK_N(domains, ndomains, 1); + virDomainPtr domains; + size_t ndomains = x; + size_t ndomains_max = y; + + VIR_SHRINK_N(domains, ndomains_max, 1); -- To free the domain: +- To free an array of domains: - VIR_FREE(domain); + virDomainPtr domains; + size_t ndomains = x; + size_t ndomains_max = y; + size_t i; + + for (i = 0; i < ndomains; i++) + VIR_FREE(domains[i]); + VIR_FREE(domains); + ndomains_max = ndomains = 0; diff --git a/docs/hacking.html.in b/docs/hacking.html.in index 964904e3db3b4ba8585bbbd9d1dd11ca96bcecaa..890692f05803c5e37778face4f03e82fd5e47768 100644 --- a/docs/hacking.html.in +++ b/docs/hacking.html.in @@ -385,7 +385,7 @@
  • To allocate an array of object pointers:

       virDomainPtr *domains;
    -  int ndomains = 10;
    +  size_t ndomains = 10;
     
       if (VIR_ALLOC_N(domains, ndomains) < 0) {
           virReportOOMError();
    @@ -394,26 +394,61 @@
     
  • -
  • To re-allocate the array of domains to be longer:

    +
  • To re-allocate the array of domains to be 1 element + longer (however, note that repeatedly expanding an array by 1 + scales quadratically, so this is recommended only for smaller + arrays):

    -  if (VIR_EXPAND_N(domains, ndomains, 10) < 0) {
    +  virDomainPtr domains;
    +  size_t ndomains = 0;
    +
    +  if (VIR_EXPAND_N(domains, ndomains, 1) < 0) {
           virReportOOMError();
           return NULL;
       }
    +  domains[ndomains - 1] = domain;
    +
  • + +
  • To ensure an array has room to hold at least one more + element (this approach scales better, but requires tracking + allocation separately from usage)

    + +
    +  virDomainPtr domains;
    +  size_t ndomains = 0;
    +  size_t ndomains_max = 0;
    +
    +  if (VIR_RESIZE_N(domains, ndomains_max, ndomains, 1) < 0) {
    +      virReportOOMError();
    +      return NULL;
    +  }
    +  domains[ndomains++] = domain;
     
  • To trim an array of domains to have one less element:

    -  VIR_SHRINK_N(domains, ndomains, 1);
    +  virDomainPtr domains;
    +  size_t ndomains = x;
    +  size_t ndomains_max = y;
    +
    +  VIR_SHRINK_N(domains, ndomains_max, 1);
     
  • -
  • To free the domain:

    +
  • To free an array of domains:

    -  VIR_FREE(domain);
    +  virDomainPtr domains;
    +  size_t ndomains = x;
    +  size_t ndomains_max = y;
    +  size_t i;
    +
    +  for (i = 0; i < ndomains; i++)
    +      VIR_FREE(domains[i]);
    +  VIR_FREE(domains);
    +  ndomains_max = ndomains = 0;
     
    -
  • +

    File handling

    diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 796559c597829a3945557ab47a02587f0a9bb988..8bf102839d75407fb9140f07cbb1746b81e9561c 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -506,6 +506,7 @@ virAllocN; virExpandN; virFree; virReallocN; +virResizeN; virShrinkN; diff --git a/src/util/memory.c b/src/util/memory.c index 59685b30ef02e5f277a2aeeb47390468d5f46e71..96222db3d3f8a440b155dc298bb9983af8b107a9 100644 --- a/src/util/memory.c +++ b/src/util/memory.c @@ -197,6 +197,41 @@ int virExpandN(void *ptrptr, size_t size, size_t *countptr, size_t add) return ret; } +/** + * virResizeN: + * @ptrptr: pointer to pointer for address of allocated memory + * @size: number of bytes per element + * @allocptr: pointer to number of elements allocated in array + * @count: number of elements currently used in array + * @add: minimum number of additional elements to support in array + * + * If 'count' + 'add' is larger than '*allocptr', then resize the + * block of memory in 'ptrptr' to be an array of at least 'count' + + * 'add' elements, each 'size' bytes in length. Update 'ptrptr' and + * 'allocptr' with the details of the newly allocated memory. On + * failure, 'ptrptr' and 'allocptr' are not changed. Any newly + * allocated memory in 'ptrptr' is zero-filled. + * + * Returns -1 on failure to allocate, zero on success + */ +int virResizeN(void *ptrptr, size_t size, size_t *allocptr, size_t count, + size_t add) +{ + size_t delta; + + if (count + add < count) { + errno = ENOMEM; + return -1; + } + if (count + add <= *allocptr) + return 0; + + delta = count + add - *allocptr; + if (delta < *allocptr / 2) + delta = *allocptr / 2; + return virExpandN(ptrptr, size, allocptr, delta); +} + /** * virShrinkN: * @ptrptr: pointer to pointer for address of allocated memory diff --git a/src/util/memory.h b/src/util/memory.h index 98ac2b358209d6ff12b36ce1a7c8d1e9f641f79f..750a6b01817da5d130f3e2c11b19a93e16bc4327 100644 --- a/src/util/memory.h +++ b/src/util/memory.h @@ -54,6 +54,9 @@ int virReallocN(void *ptrptr, size_t size, size_t count) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1); int virExpandN(void *ptrptr, size_t size, size_t *count, size_t add) ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); +int virResizeN(void *ptrptr, size_t size, size_t *alloc, size_t count, + size_t desired) + ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); void virShrinkN(void *ptrptr, size_t size, size_t *count, size_t remove) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); int virAllocVar(void *ptrptr, @@ -116,6 +119,29 @@ void virFree(void *ptrptr) ATTRIBUTE_NONNULL(1); # define VIR_EXPAND_N(ptr, count, add) \ virExpandN(&(ptr), sizeof(*(ptr)), &(count), add) +/** + * VIR_RESIZE_N: + * @ptr: pointer to hold address of allocated memory + * @alloc: variable tracking number of elements currently allocated + * @count: number of elements currently in use + * @add: minimum number of elements to additionally support + * + * Blindly using VIR_EXPAND_N(array, alloc, 1) in a loop scales + * quadratically, because every iteration must copy contents from + * all prior iterations. But amortized linear scaling can be achieved + * by tracking allocation size separately from the number of used + * elements, and growing geometrically only as needed. + * + * If 'count' + 'add' is larger than 'alloc', then geometrically reallocate + * the array of 'alloc' elements, each sizeof(*ptr) bytes long, and store + * the address of allocated memory in 'ptr' and the new size in 'alloc'. + * The new elements are filled with zero. + * + * Returns -1 on failure, 0 on success + */ +# define VIR_RESIZE_N(ptr, alloc, count, add) \ + virResizeN(&(ptr), sizeof(*(ptr)), &(alloc), count, add) + /** * VIR_SHRINK_N: * @ptr: pointer to hold address of allocated memory