* src/util/memory.h (VIR_RESIZE_N): New macro. * src/util/memory.c (virResizeN): New function. * docs/hacking.html.in: Document it. * HACKING: Regenerate. * src/libvirt_private.syms: Export new helper. --- HACKING | 43 +++++++++++++++++++++++++++++++++++++---- docs/hacking.html.in | 47 ++++++++++++++++++++++++++++++++++++++++----- src/libvirt_private.syms | 1 + src/util/memory.c | 35 ++++++++++++++++++++++++++++++++++ src/util/memory.h | 26 +++++++++++++++++++++++++ 5 files changed, 141 insertions(+), 11 deletions(-) diff --git a/HACKING b/HACKING index 2b19fe4..e54af05 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: + virDomainPtr domains; + size_t ndomains = x; + size_t ndomains_max = y; + VIR_SHRINK_N(domains, ndomains, 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 ffe6aea..138a1ef 100644 --- a/docs/hacking.html.in +++ b/docs/hacking.html.in @@ -385,7 +385,7 @@ <li><p>To allocate an array of object pointers:</p> <pre> virDomainPtr *domains; - int ndomains = 10; + size_t ndomains = 10; if (VIR_ALLOC_N(domains, ndomains) < 0) { virReportOOMError(); @@ -394,26 +394,61 @@ </pre> </li> - <li><p>To re-allocate the array of domains to be longer:</p> + <li><p>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):</p> <pre> - 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; +</pre></li> + + <li><p>To ensure an array has room to hold at least one more + element (this approach scales better, but requires tracking + allocation separately from usage)</p> + +<pre> + 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; </pre> </li> <li><p>To trim an array of domains to have one less element:</p> <pre> + virDomainPtr domains; + size_t ndomains = x; + size_t ndomains_max = y; + VIR_SHRINK_N(domains, ndomains, 1); </pre></li> - <li><p>To free the domain:</p> + <li><p>To free an array of domains:</p> <pre> - 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; </pre> - </li> + </li> </ul> <h2><a name="file_handling">File handling</a></h2> diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 796559c..8bf1028 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 59685b3..d1237e3 100644 --- a/src/util/memory.c +++ b/src/util/memory.c @@ -198,6 +198,41 @@ int virExpandN(void *ptrptr, size_t size, size_t *countptr, size_t add) } /** + * 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 * @size: number of bytes per element diff --git a/src/util/memory.h b/src/util/memory.h index 98ac2b3..2b39393 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, @@ -117,6 +120,29 @@ void virFree(void *ptrptr) ATTRIBUTE_NONNULL(1); 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 'count', 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 * @count: variable tracking number of elements currently allocated -- 1.7.3.2 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list