Skip to content

Commit 3689539

Browse files
peffgitster
authored andcommitted
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 <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent e7792a7 commit 3689539

File tree

1 file changed

+62
-0
lines changed

1 file changed

+62
-0
lines changed

git-compat-util.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -782,6 +782,68 @@ extern FILE *fopen_for_writing(const char *path);
782782
#define ALLOC_ARRAY(x, alloc) (x) = xmalloc(st_mult(sizeof(*(x)), (alloc)))
783783
#define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc)))
784784

785+
/*
786+
* These functions help you allocate structs with flex arrays, and copy
787+
* the data directly into the array. For example, if you had:
788+
*
789+
* struct foo {
790+
* int bar;
791+
* char name[FLEX_ARRAY];
792+
* };
793+
*
794+
* you can do:
795+
*
796+
* struct foo *f;
797+
* FLEX_ALLOC_MEM(f, name, src, len);
798+
*
799+
* to allocate a "foo" with the contents of "src" in the "name" field.
800+
* The resulting struct is automatically zero'd, and the flex-array field
801+
* is NUL-terminated (whether the incoming src buffer was or not).
802+
*
803+
* The FLEXPTR_* variants operate on structs that don't use flex-arrays,
804+
* but do want to store a pointer to some extra data in the same allocated
805+
* block. For example, if you have:
806+
*
807+
* struct foo {
808+
* char *name;
809+
* int bar;
810+
* };
811+
*
812+
* you can do:
813+
*
814+
* struct foo *f;
815+
* FLEX_ALLOC_STR(f, name, src);
816+
*
817+
* and "name" will point to a block of memory after the struct, which will be
818+
* freed along with the struct (but the pointer can be repointed anywhere).
819+
*
820+
* The *_STR variants accept a string parameter rather than a ptr/len
821+
* combination.
822+
*
823+
* Note that these macros will evaluate the first parameter multiple
824+
* times, and it must be assignable as an lvalue.
825+
*/
826+
#define FLEX_ALLOC_MEM(x, flexname, buf, len) do { \
827+
(x) = NULL; /* silence -Wuninitialized for offset calculation */ \
828+
(x) = xalloc_flex(sizeof(*(x)), (char *)(&((x)->flexname)) - (char *)(x), (buf), (len)); \
829+
} while (0)
830+
#define FLEXPTR_ALLOC_MEM(x, ptrname, buf, len) do { \
831+
(x) = xalloc_flex(sizeof(*(x)), sizeof(*(x)), (buf), (len)); \
832+
(x)->ptrname = (void *)((x)+1); \
833+
} while(0)
834+
#define FLEX_ALLOC_STR(x, flexname, str) \
835+
FLEX_ALLOC_MEM((x), flexname, (str), strlen(str))
836+
#define FLEXPTR_ALLOC_STR(x, ptrname, str) \
837+
FLEXPTR_ALLOC_MEM((x), ptrname, (str), strlen(str))
838+
839+
static inline void *xalloc_flex(size_t base_len, size_t offset,
840+
const void *src, size_t src_len)
841+
{
842+
unsigned char *ret = xcalloc(1, st_add3(base_len, src_len, 1));
843+
memcpy(ret + offset, src, src_len);
844+
return ret;
845+
}
846+
785847
static inline char *xstrdup_or_null(const char *str)
786848
{
787849
return str ? xstrdup(str) : NULL;

0 commit comments

Comments
 (0)