I noticed when writing the backend functions for virNetworkUpdate that I was repeating the same sequence of memmove, VIR_REALLOC, nXXX-- (and messed up the args to memmove at least once), and had seen the same sequence in a lot of other places, so I decided to write a couple utility functions/macros - see the .h file for full documentation. The intent is both to reduce the number of lines of code, but more importantly to eliminate the need to check the element size and element count arithmetic every time we need to do this (I *always* make at least one mistake.) VIR_INSERT_ELEMENTS_N: insert one or more elements at an arbitrary index within an array of objects. The size of each object is determined automatically by the macro using sizeof(*array). If a pointer to new elements is provided, they are copied in, otherwise the new space is set to all 0. VIR_APPEND_ELEMENTS_N: This is just a special case of VIR_INSERT_ELEMENTS_N that "inserts" one past the current last element. VIR_DELETE_ELEMENTS_N: delete one or more elements at an arbitrary index within an array of objects. It's assumed that the elements being deleted are already saved elsewhere (or cleared, if that's what is appropriate). Note that VIR_DELETE_ELEMENTS can return a failure, but only if an invalid index is given (index + amount to delete is > current array size), so in most cases you can safely ignore the return. --- src/libvirt_private.syms | 2 ++ src/util/memory.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++- src/util/memory.h | 71 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 148 insertions(+), 2 deletions(-) diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 60f9c7f..bead98c 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -821,8 +821,10 @@ virLogUnlock; virAlloc; virAllocN; virAllocVar; +virDeleteElementsN; virExpandN; virFree; +virInsertElementsN; virReallocN; virResizeN; virShrinkN; diff --git a/src/util/memory.c b/src/util/memory.c index 0f7aca1..46b6fdd 100644 --- a/src/util/memory.c +++ b/src/util/memory.c @@ -1,7 +1,7 @@ /* * memory.c: safer memory allocation * - * Copyright (C) 2010-2011 Red Hat, Inc. + * Copyright (C) 2010-2012 Red Hat, Inc. * Copyright (C) 2008 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -253,6 +253,81 @@ void virShrinkN(void *ptrptr, size_t size, size_t *countptr, size_t toremove) } } +/** + * virInsertElementsN: + * @ptrptr: pointer to hold address of allocated memory + * @countptr: variable tracking number of elements currently allocated + * @add: number of elements to add + * @at: index within array where new elements should be added + * @newelem: pointer to array of one or more new elements to move into place + * (the originals will be zeroed out if successful) + * + * Re-allocate an array of 'count' elements, each sizeof(*ptr) bytes + * long, to be 'count' + 'add' elements long, then appropriately move + * the elements starting at ptr[at] up by 'count' elements, copy the + * items from 'newelem' into ptr[at], then store the address of + * allocated memory in 'ptr' and the new size in 'count'. If + * 'newelem' is NULL, the new elements at ptr[at] are instead filled + * with zero. + * + * Returns -1 on failure, 0 on success + */ +int +virInsertElementsN(void *ptrptr, size_t size, size_t *countptr, + size_t add, size_t at, void *newelem) +{ + if ((at > *countptr) || (virExpandN(ptrptr, size, countptr, add) < 0)) + return -1; + + /* memory was successfully re-allocated. Move up all elements from + * ptrptr[at] to the end, memcpy in the new elements, and clear + * the elements from their original location. Remember that + * *countptr has already been updated with new element count! + */ + memmove(*(char**)ptrptr + (size * (at + add)), + *(char**)ptrptr + (size * at), + size * (*countptr - add - at)); + + if (newelem) { + memmove(*(char**)ptrptr + (size * at), newelem, size * add); + memset((char*)newelem, 0, size * add); + } else { + memset(*(char**)ptrptr + (size * at), 0, size * add); + } + return 0; +} + +/** + * virDeleteElementsN: + * @ptr: pointer to hold address of allocated memory + * @count: variable tracking number of elements currently allocated + * @remove: number of elements to remove + * @at: index within array where new elements should be deleted + * + * Re-allocate an array of 'count' elements, each sizeof(*ptr) + * bytes long, to be 'count' - 'remove' elements long, then store the + * address of allocated memory in 'ptr' and the new size in 'count'. + * If 'count' <= 'remove', the entire array is freed. + * + * Returns -1 on failure, 0 on success + */ +int +virDeleteElementsN(void *ptrptr, size_t size, size_t *countptr, + size_t remove, size_t at) +{ + if (at + remove > *countptr) + return -1; + + /* First move down the elements at the end that won't be deleted, + * then realloc. We assume that the items being deleted have + * already been cleared. + */ + memmove(*(char**)ptrptr + (size * at), + *(char**)ptrptr + (size * (at + remove)), + size * (*countptr - remove - at)); + virShrinkN(ptrptr, size, countptr, remove); + return 0; +} /** * Vir_Alloc_Var: diff --git a/src/util/memory.h b/src/util/memory.h index ad8ee64..872fec2 100644 --- a/src/util/memory.h +++ b/src/util/memory.h @@ -1,7 +1,7 @@ /* * memory.c: safer memory allocation * - * Copyright (C) 2010-2011 Red Hat, Inc. + * Copyright (C) 2010-2012 Red Hat, Inc. * Copyright (C) 2008 Daniel P. Berrange * * This library is free software; you can redistribute it and/or @@ -59,6 +59,12 @@ int virResizeN(void *ptrptr, size_t size, size_t *alloc, size_t count, ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); void virShrinkN(void *ptrptr, size_t size, size_t *count, size_t toremove) ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); +int virInsertElementsN(void *ptrptr, size_t size, size_t *countptr, + size_t add, size_t at, void *newelem) + ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); +int virDeleteElementsN(void *ptrptr, size_t size, size_t *countptr, + size_t remove, size_t at) + ATTRIBUTE_RETURN_CHECK ATTRIBUTE_NONNULL(1) ATTRIBUTE_NONNULL(3); int virAllocVar(void *ptrptr, size_t struct_size, size_t element_size, @@ -158,6 +164,69 @@ void virFree(void *ptrptr) ATTRIBUTE_NONNULL(1); # define VIR_SHRINK_N(ptr, count, remove) \ virShrinkN(&(ptr), sizeof(*(ptr)), &(count), remove) +/** + * VIR_INSERT_ELEMENTS_N: + * @ptr: pointer to hold address of allocated memory + * @at: index within array where new elements should be added + * @count: variable tracking number of elements currently allocated + * @add: number of elements to add + * @newelem: pointer to array of one or more new elements to move into place + * (the originals will be zeroed out if successful) + * + * Re-allocate an array of 'count' elements, each sizeof(*ptr) bytes + * long, to be 'count' + 'add' elements long, then appropriately move + * the elements starting at ptr[at] up by 'count' elements, copy the + * items from 'newelem' into ptr[at], then store the address of + * allocated memory in 'ptr' and the new size in 'count'. If + * 'newelem' is NULL, the new elements at ptr[at] are instead filled + * with zero. + * + * Returns -1 on failure, 0 on success + * + * + */ +# define VIR_INSERT_ELEMENTS_N(ptr, at, count, add, newelem) \ + virInsertElementsN(&(ptr), sizeof(*(ptr)), &(count), add, at, newelem) + +/** + * VIR_APPEND_ELEMENTS_N: + * @ptr: pointer to hold address of allocated memory + * @count: variable tracking number of elements currently allocated + * @add: number of elements to add + * @newelem: pointer to array of one or more new elements to move into place + * (the originals will be zeroed out if successful) + * + * Re-allocate an array of 'count' elements, each sizeof(*ptr) bytes + * long, to be 'count' + 'add' elements long, then copy the items from + * 'newelem' into ptr[count+1], then store the address of allocated + * memory in 'ptr' and the new size in 'count'. If 'newelem' is NULL, + * the new elements at ptr[at] are instead filled with zero. + * + * Returns -1 on failure, 0 on success + * + * + */ +# define VIR_APPEND_ELEMENTS_N(ptr, count, add, newelem) \ + virInsertElementsN(&(ptr), sizeof(*(ptr)), &(count), \ + add, (count) + 1, newelem) + +/** + * VIR_DELETE_ELEMENTS_N: + * @ptr: pointer to hold address of allocated memory + * @at: index within array where new elements should be deleted + * @count: variable tracking number of elements currently allocated + * @remove: number of elements to remove + * + * Re-allocate an array of 'count' elements, each sizeof(*ptr) + * bytes long, to be 'count' - 'remove' elements long, then store the + * address of allocated memory in 'ptr' and the new size in 'count'. + * If 'count' <= 'remove', the entire array is freed. + * + * No return value. + */ +# define VIR_DELETE_ELEMENTS_N(ptr, at, count, remove) \ + virDeleteElementsN(&(ptr), sizeof(*(ptr)), &(count), remove, at) + /* * VIR_ALLOC_VAR_OVERSIZED: * @M: size of base structure -- 1.7.11.7 -- libvir-list mailing list libvir-list@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/libvir-list