git-compat-util.h: implement a different ARRAY_SIZE macro for for safely deriving the size of array

To get number of elements in an array git use the ARRAY_SIZE macro defined as: #define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0])) The problem with it is a possibility of mistakenly passing to it a pointer instead an array. The ARRAY_SIZE macro as conventionally defined does not provide good type-safety and the open-coded approach is more fragile, more verbose and provides no improvement in type-safety. Use instead a different but compatible ARRAY_SIZE() macro, which will also break compile if you try to use it on a pointer. This implemention revert to the original code if the compiler doesn't know the typeof and __builtin_types_compatible_p GCC extensions. This can ensure our code is robust to changes, without needing a gratuitous macro or constant. A similar ARRAY_SIZE implementation also exists in the linux kernel. Credits to Rusty Russell and his ccan library. Signed-off-by: Elia Pinto <gitter.spiros@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Elia Pinto committed Apr 30, 2015 at 14:44 UTC 89c855ed3cbe64bea93ea081d0e96077e9ee8517
1 file changed +53 -1
git-compat-util.h
+53 -1
@@ -3,6 +3,23 @@
3
4 #define _FILE_OFFSET_BITS 64
5
6 +
7 +/* Derived from Linux "Features Test Macro" header
8 + * Convenience macros to test the versions of gcc (or
9 + * a compatible compiler).
10 + * Use them like this:
11 + * #if GIT_GNUC_PREREQ (2,8)
12 + * ... code requiring gcc 2.8 or later ...
13 + * #endif
14 +*/
15 +#if defined(__GNUC__) && defined(__GNUC_MINOR__)
16 +# define GIT_GNUC_PREREQ(maj, min) \
17 + ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
18 +#else
19 + #define GIT_GNUC_PREREQ(maj, min) 0
20 +#endif
21 +
22 +
23 #ifndef FLEX_ARRAY
24 /*
25 * See if our compiler is known to support flexible array members.
@@ -25,7 +42,42 @@
42 #endif
43 #endif
44
28 -#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))
45 +
46 +/*
47 + * BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression.
48 + * @cond: the compile-time condition which must be true.
49 + *
50 + * Your compile will fail if the condition isn't true, or can't be evaluated
51 + * by the compiler. This can be used in an expression: its value is "0".
52 + *
53 + * Example:
54 + * #define foo_to_char(foo) \
55 + * ((char *)(foo) \
56 + * + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0))
57 + */
58 +#define BUILD_ASSERT_OR_ZERO(cond) \
59 + (sizeof(char [1 - 2*!(cond)]) - 1)
60 +
61 +#if defined(__GNUC__) && (__GNUC__ >= 3)
62 +# if GIT_GNUC_PREREQ(3, 1)
63 + /* &arr[0] degrades to a pointer: a different type from an array */
64 +# define BARF_UNLESS_AN_ARRAY(arr) \
65 + BUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(__typeof__(arr), \
66 + __typeof__(&(arr)[0])))
67 +# else
68 +# define BARF_UNLESS_AN_ARRAY(arr) 0
69 +# endif
70 +#endif
71 +/*
72 + * ARRAY_SIZE - get the number of elements in a visible array
73 + * <at> x: the array whose size you want.
74 + *
75 + * This does not work on pointers, or arrays declared as [], or
76 + * function parameters. With correct compiler support, such usage
77 + * will cause a build error (see the build_assert_or_zero macro).
78 + */
79 +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]) + BARF_UNLESS_AN_ARRAY(x))
80 +
81 #define bitsizeof(x) (CHAR_BIT * sizeof(x))
82
83 #define maximum_signed_value_of_type(a) \