提交 269d3b72 编写于 作者: E Eric Blake

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.
上级 5a0beacc
...@@ -308,7 +308,7 @@ routines, use the macros from memory.h. ...@@ -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. ...@@ -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(); 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 @@ ...@@ -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) &lt; 0) { if (VIR_ALLOC_N(domains, ndomains) &lt; 0) {
virReportOOMError(); virReportOOMError();
...@@ -394,26 +394,61 @@ ...@@ -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) &lt; 0) { virDomainPtr domains;
size_t ndomains = 0;
if (VIR_EXPAND_N(domains, ndomains, 1) &lt; 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) &lt; 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 &lt; 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; ...@@ -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) ...@@ -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 ...@@ -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); ...@@ -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
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册