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>

Jeff King committed Feb 22, 2016 at 17:43 UTC 3689539127f786f32ff04da374d37cc1f72fb918
1 file changed +62
git-compat-util.h
+62
@@ -782,6 +782,68 @@ extern FILE *fopen_for_writing(const char *path);
782 #define ALLOC_ARRAY(x, alloc) (x) = xmalloc(st_mult(sizeof(*(x)), (alloc)))
783 #define REALLOC_ARRAY(x, alloc) (x) = xrealloc((x), st_mult(sizeof(*(x)), (alloc)))
784
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 +
847 static inline char *xstrdup_or_null(const char *str)
848 {
849 return str ? xstrdup(str) : NULL;