mirror of
https://github.com/libvirt/libvirt.git
synced 2025-02-25 18:55:26 -06:00
memory: make it easier to avoid quadratic scaling of arrays
* src/util/memory.h (VIR_RESIZE_N): New macro. * src/util/memory.c (virResizeN): New function. * src/libvirt_private.syms: Export new helper. * docs/hacking.html.in: Document it. * HACKING: Regenerate.
This commit is contained in:
parent
5a0beacc12
commit
269d3b72f6
45
HACKING
45
HACKING
@ -308,7 +308,7 @@ routines, use the macros from memory.h.
|
|||||||
- To allocate an array of object pointers:
|
- To allocate an array of object pointers:
|
||||||
|
|
||||||
virDomainPtr *domains;
|
virDomainPtr *domains;
|
||||||
int ndomains = 10;
|
size_t ndomains = 10;
|
||||||
|
|
||||||
if (VIR_ALLOC_N(domains, ndomains) < 0) {
|
if (VIR_ALLOC_N(domains, ndomains) < 0) {
|
||||||
virReportOOMError();
|
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):
|
||||||
|
|
||||||
if (VIR_EXPAND_N(domains, ndomains, 10) < 0) {
|
virDomainPtr domains;
|
||||||
|
size_t ndomains = 0;
|
||||||
|
|
||||||
|
if (VIR_EXPAND_N(domains, ndomains, 1) < 0) {
|
||||||
virReportOOMError();
|
virReportOOMError();
|
||||||
return NULL;
|
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:
|
- 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;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -385,7 +385,7 @@
|
|||||||
<li><p>To allocate an array of object pointers:</p>
|
<li><p>To allocate an array of object pointers:</p>
|
||||||
<pre>
|
<pre>
|
||||||
virDomainPtr *domains;
|
virDomainPtr *domains;
|
||||||
int ndomains = 10;
|
size_t ndomains = 10;
|
||||||
|
|
||||||
if (VIR_ALLOC_N(domains, ndomains) < 0) {
|
if (VIR_ALLOC_N(domains, ndomains) < 0) {
|
||||||
virReportOOMError();
|
virReportOOMError();
|
||||||
@ -394,26 +394,61 @@
|
|||||||
</pre>
|
</pre>
|
||||||
</li>
|
</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>
|
<pre>
|
||||||
if (VIR_EXPAND_N(domains, ndomains, 10) < 0) {
|
virDomainPtr domains;
|
||||||
|
size_t ndomains = 0;
|
||||||
|
|
||||||
|
if (VIR_EXPAND_N(domains, ndomains, 1) < 0) {
|
||||||
virReportOOMError();
|
virReportOOMError();
|
||||||
return NULL;
|
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>
|
</pre>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
<li><p>To trim an array of domains to have one less element:</p>
|
<li><p>To trim an array of domains to have one less element:</p>
|
||||||
|
|
||||||
<pre>
|
<pre>
|
||||||
VIR_SHRINK_N(domains, ndomains, 1);
|
virDomainPtr domains;
|
||||||
|
size_t ndomains = x;
|
||||||
|
size_t ndomains_max = y;
|
||||||
|
|
||||||
|
VIR_SHRINK_N(domains, ndomains_max, 1);
|
||||||
</pre></li>
|
</pre></li>
|
||||||
|
|
||||||
<li><p>To free the domain:</p>
|
<li><p>To free an array of domains:</p>
|
||||||
<pre>
|
<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>
|
</pre>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2><a name="file_handling">File handling</a></h2>
|
<h2><a name="file_handling">File handling</a></h2>
|
||||||
|
@ -506,6 +506,7 @@ virAllocN;
|
|||||||
virExpandN;
|
virExpandN;
|
||||||
virFree;
|
virFree;
|
||||||
virReallocN;
|
virReallocN;
|
||||||
|
virResizeN;
|
||||||
virShrinkN;
|
virShrinkN;
|
||||||
|
|
||||||
|
|
||||||
|
@ -197,6 +197,41 @@ int virExpandN(void *ptrptr, size_t size, size_t *countptr, size_t add)
|
|||||||
return ret;
|
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:
|
* virShrinkN:
|
||||||
* @ptrptr: pointer to pointer for address of allocated memory
|
* @ptrptr: pointer to pointer for address of allocated memory
|
||||||
|
@ -54,6 +54,9 @@ int virReallocN(void *ptrptr, size_t size, size_t count) ATTRIBUTE_RETURN_CHECK
|
|||||||
ATTRIBUTE_NONNULL(1);
|
ATTRIBUTE_NONNULL(1);
|
||||||
int virExpandN(void *ptrptr, size_t size, size_t *count, size_t add)
|
int virExpandN(void *ptrptr, size_t size, size_t *count, size_t add)
|
||||||
ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
|
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)
|
void virShrinkN(void *ptrptr, size_t size, size_t *count, size_t remove)
|
||||||
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
|
ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3);
|
||||||
int virAllocVar(void *ptrptr,
|
int virAllocVar(void *ptrptr,
|
||||||
@ -116,6 +119,29 @@ void virFree(void *ptrptr) ATTRIBUTE_NONNULL(1);
|
|||||||
# define VIR_EXPAND_N(ptr, count, add) \
|
# define VIR_EXPAND_N(ptr, count, add) \
|
||||||
virExpandN(&(ptr), sizeof(*(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:
|
* VIR_SHRINK_N:
|
||||||
* @ptr: pointer to hold address of allocated memory
|
* @ptr: pointer to hold address of allocated memory
|
||||||
|
Loading…
Reference in New Issue
Block a user