Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
add helpers for allocating flex-array structs
Allocating a struct with a flex array is pretty simple in
practice: you over-allocate the struct, then copy some data
into the over-allocation. But it can be a slight pain to
make sure you're allocating and copying the right amounts.

This patch adds a few helpers to turn simple cases of
flex-array struct allocation into a one-liner that properly
checks for overflow. See the embedded documentation for
details.

Ideally we could provide a more flexible version that could
handle multiple strings, like:

  FLEX_ALLOC_FMT(ref, name, "%s%s", prefix, name);

But we have to implement this as a macro (because of the
offset calculation of the flex member), which means we would
need all compilers to support variadic macros.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
  • Loading branch information
Jeff King authored and Junio C Hamano committed Feb 22, 2016
1 parent e7792a7 commit 3689539
Showing 1 changed file with 62 additions and 0 deletions.
62 changes: 62 additions & 0 deletions git-compat-util.h
Expand Up @@ -782,6 +782,68 @@ extern FILE *fopen_for_writing(const char *path);
#define ALLOC_ARRAY(x, alloc) (x) = xmalloc(st_mult(sizeof(*(x)), (alloc)))
#define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc)))

/*
* These functions help you allocate structs with flex arrays, and copy
* the data directly into the array. For example, if you had:
*
* struct foo {
* int bar;
* char name[FLEX_ARRAY];
* };
*
* you can do:
*
* struct foo *f;
* FLEX_ALLOC_MEM(f, name, src, len);
*
* to allocate a "foo" with the contents of "src" in the "name" field.
* The resulting struct is automatically zero'd, and the flex-array field
* is NUL-terminated (whether the incoming src buffer was or not).
*
* The FLEXPTR_* variants operate on structs that don't use flex-arrays,
* but do want to store a pointer to some extra data in the same allocated
* block. For example, if you have:
*
* struct foo {
* char *name;
* int bar;
* };
*
* you can do:
*
* struct foo *f;
* FLEX_ALLOC_STR(f, name, src);
*
* and "name" will point to a block of memory after the struct, which will be
* freed along with the struct (but the pointer can be repointed anywhere).
*
* The *_STR variants accept a string parameter rather than a ptr/len
* combination.
*
* Note that these macros will evaluate the first parameter multiple
* times, and it must be assignable as an lvalue.
*/
#define FLEX_ALLOC_MEM(x, flexname, buf, len) do { \
(x) = NULL; /* silence -Wuninitialized for offset calculation */ \
(x) = xalloc_flex(sizeof(*(x)), (char *)(&((x)->flexname)) - (char *)(x), (buf), (len)); \
} while (0)
#define FLEXPTR_ALLOC_MEM(x, ptrname, buf, len) do { \
(x) = xalloc_flex(sizeof(*(x)), sizeof(*(x)), (buf), (len)); \
(x)->ptrname = (void *)((x)+1); \
} while(0)
#define FLEX_ALLOC_STR(x, flexname, str) \
FLEX_ALLOC_MEM((x), flexname, (str), strlen(str))
#define FLEXPTR_ALLOC_STR(x, ptrname, str) \
FLEXPTR_ALLOC_MEM((x), ptrname, (str), strlen(str))

static inline void *xalloc_flex(size_t base_len, size_t offset,
const void *src, size_t src_len)
{
unsigned char *ret = xcalloc(1, st_add3(base_len, src_len, 1));
memcpy(ret + offset, src, src_len);
return ret;
}

static inline char *xstrdup_or_null(const char *str)
{
return str ? xstrdup(str) : NULL;
Expand Down

0 comments on commit 3689539

Please sign in to comment.