• L
    util: add VIR_(APPEND|INSERT|DELETE)_ELEMENT · 85b22f52
    Laine Stump 提交于
    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 few
    utility functions/macros - see the .h file for full documentation.
    
    The intent is 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_ELEMENT: insert one element at an arbitrary index within an
      array of objects. The size of each object is determined
      automatically by the macro using sizeof(*array). The new element's
      contents are copied into the inserted space, then the original copy
      of contents are 0'ed out (if everything else was
      successful). Compile-time assignment and size compatibility between
      the array and the new element is guaranteed (see explanation below
      [*])
    
    VIR_INSERT_ELEMENT_COPY: identical to VIR_INSERT_ELEMENT, except that
      the original contents of newelem are not cleared to 0 (i.e. a copy
      is made).
    
    VIR_APPEND_ELEMENT: This is just a special case of VIR_INSERT_ELEMENT
      that "inserts" one past the current last element.
    
    VIR_APPEND_ELEMENT_COPY: identical to VIR_APPEND_ELEMENT, except that
      the original contents of newelem are not cleared to 0 (i.e. a copy
      is made).
    
    VIR_DELETE_ELEMENT: delete one element at an arbitrary index within an
      array of objects. It's assumed that the element being deleted is
      already saved elsewhere (or cleared, if that's what is appropriate).
    
    All five of these macros have an _INPLACE variant, which skips the
    memory re-allocation of the array, assuming that the caller has
    already done it (when inserting) or will do it later (when deleting).
    
    Note that VIR_DELETE_ELEMENT* 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 (that's why
    the helper function virDeleteElementsN isn't declared with
    ATTRIBUTE_RETURN_CHECK). A warning is logged if this ever happens,
    since it is surely a coding error.
    
    [*] One initial problem with the INSERT and APPEND macros was that,
    due to both the array pointer and newelem pointer being cast to void*
    when passing to virInsertElementsN(), any chance of type-checking was
    lost. If we were going to move in newelem with a memmove anyway, we
    would be no worse off for this. However, most current open-coded
    insert/append operations use direct struct assignment to move the new
    element into place (or just populate the new element directly) - thus
    use of the new macros would open a possibility for new usage errors
    that didn't exist before (e.g. accidentally sending &newelemptr rather
    than newelemptr - I actually did this quite a lot in my test
    conversions of existing code).
    
    But thanks to Eric Blake's clever thinking, I was able to modify the
    INSERT and APPEND macros so that they *do* check for both assignment
    and size compatibility of *ptr (an element in the array) and newelem
    (the element being copied into the new position of the array). This is
    done via clever use of the C89-guaranteed fact that the sizeof()
    operator must have *no* side effects (so an assignment inside sizeof()
    is checked for validity, but not actually evaluated), and the fact
    that virInsertElementsN has a "# of new elements" argument that we
    want to always be 1.
    85b22f52
cfg.mk 29.7 KB