{"thread":{"id":"37841","subject":"[PATCH] git-compat-util.h: implement a different ARRAY_SIZE macro for for safely deriving the size of array","startedAt":"2014-10-30T10:56:12Z","lastAt":"2014-10-30T10:56:12Z","messageCount":1,"participants":["Elia Pinto"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"251213","messageId":"1414666572-4812-1-git-send-email-gitter.spiros@gmail.com","threadId":"37841","inReplyTo":null,"subject":"[PATCH] git-compat-util.h: implement a different ARRAY_SIZE macro for for safely deriving the size of array","fromName":"Elia Pinto","fromEmail":"gitter.spiros@gmail.com","sentAt":"2014-10-30T10:56:12Z","receivedAt":"2014-10-30T10:56:12Z","isPatch":true,"sender":{"key":"gitter.spiros@gmail.com","avatar":"https://avatars.githubusercontent.com/u/158490?v=4"},"body":"To get number of elements in an array git use the ARRAY_SIZE macro defined as:\n\n#define ARRAY_SIZE(x) (sizeof(x)/sizeof((x)[0]))\n\nThe problem with it is a possibility of mistakenly passing to it a\npointer instead an array.\n\nUse instead a different but compatible ARRAY_SIZE() macro,\nwhich (given a good compiler) will also break compile if you try to\nuse it on a pointer.\n\nThis can ensure your code is robust to changes, without\nneeding a gratuitous macro or constant. A similar\nARRAY_SIZE implementation also exists in the linux kernel.\n\nCredits to Rusty Russell and his ccan library.\n\nSigned-off-by: Elia Pinto <gitter.spiros@gmail.com>\n---\n Makefile          |    7 +++++++\n configure.ac      |   24 ++++++++++++++++++++++++\n git-compat-util.h |   36 +++++++++++++++++++++++++++++++++++-\n 3 files changed, 66 insertions(+), 1 deletion(-)\n\ndiff --git a/Makefile b/Makefile\nindex 827006b..37eb1e1 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -339,6 +339,9 @@ all::\n # return NULL when it receives a bogus time_t.\n #\n # Define HAVE_CLOCK_GETTIME if your platform has clock_gettime in librt.\n+#\n+# Define SUPPORT__BUILTIN_TYPES_COMPATIBLE_P if your compiler support \n+# the builtin function __builtin_types_compatible_\n \n GIT-VERSION-FILE: FORCE\n \t@$(SHELL_PATH) ./GIT-VERSION-GEN\n@@ -1382,6 +1385,10 @@ ifdef HAVE_CLOCK_GETTIME\n \tEXTLIBS += -lrt\n endif\n \n+ifdef SUPPORT__BUILTIN_TYPES_COMPATIBLE_P\n+\tBASIC_CFLAGS += -DSUPPORT__BUILTIN_TYPES_COMPATIBLE_P\n+endif\n+\n ifeq ($(TCLTK_PATH),)\n NO_TCLTK = NoThanks\n endif\ndiff --git a/configure.ac b/configure.ac\nindex 6af9647..9d489c1 100644\n--- a/configure.ac\n+++ b/configure.ac\n@@ -139,6 +139,28 @@ if test -n \"$1\"; then\n fi\n ])\n \n+AC_DEFUN([GIT_FUNC_TYPES_COMPATIBLE_P], [\n+\tAC_CACHE_CHECK([if compiler has __builtin_types_compatible_p function],\n+\t\t[cc_cv_func_types_compatible_p],\n+\t\t[ac_save_CFLAGS=\"$CFLAGS\"\n+\t\tCFLAGS=\"$CFLAGS -Werror\"\n+\t\tAC_COMPILE_IFELSE([AC_LANG_SOURCE(\n+\t\t\t[int some_function();\n+\t\t\tint some_function() {\n+\t\t\treturn __builtin_types_compatible_p(char *, int) ? 1 : 0;\n+\t\t\t}])],\n+\t\t\t[cc_cv_func_types_compatible_p=yes],\n+\t\t\t[cc_cv_func_types_compatible_p=no])\n+\t\tCFLAGS=\"$ac_save_CFLAGS\"\n+\t])\n+\n+\tAS_IF([test \"x$cc_cv_func_types_compatible_p\" = \"xyes\"],\n+\t\t[GIT_CONF_SUBST([SUPPORT__BUILTIN_TYPES_COMPATIBLE_P], [YesPlease])],\n+\t\t[$2])\n+])\n+\n+\n+\n ## Configure body starts here.\n \n AC_PREREQ(2.59)\n@@ -874,6 +896,8 @@ else\n fi\n GIT_CONF_SUBST([SNPRINTF_RETURNS_BOGUS])\n \n+GIT_FUNC_TYPES_COMPATIBLE_P\n+\n \n ## Checks for library functions.\n ## (in default C library and libraries checked by AC_CHECK_LIB)\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 400e921..cb45886 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -25,7 +25,41 @@\n #endif\n #endif\n \n-#define ARRAY_SIZE(x) (sizeof(x)/sizeof(x[0]))\n+\n+/*\n+ * BUILD_ASSERT_OR_ZERO - assert a build-time dependency, as an expression.\n+ * @cond: the compile-time condition which must be true.\n+ *\n+ * Your compile will fail if the condition isn't true, or can't be evaluated\n+ * by the compiler.  This can be used in an expression: its value is \"0\".\n+ *\n+ * Example:\n+ *\t#define foo_to_char(foo)\t\t\t\t\t\\\n+ *\t\t ((char *)(foo)\t\t\t\t\t\t\\\n+ *\t\t  + BUILD_ASSERT_OR_ZERO(offsetof(struct foo, string) == 0))\n+ */\n+#define BUILD_ASSERT_OR_ZERO(cond) \\\n+\t(sizeof(char [1 - 2*!(cond)]) - 1)\n+\n+\n+#if SUPPORT__BUILTIN_TYPES_COMPATIBLE_P\n+/* &arr[0] degrades to a pointer: a different type from an array */\n+#define _array_size_chk(arr)\t\t\t\t\t\t\\\n+\tBUILD_ASSERT_OR_ZERO(!__builtin_types_compatible_p(typeof(arr), \\\n+\t\t\t\t\t\t\ttypeof(&(arr)[0])))\n+#else\n+#define _array_size_chk(arr) 0\n+#endif\n+\n+/*\n+ * ARRAY_SIZE - get the number of elements in a visible array\n+ *  <at> x: the array whose size you want.\n+ *\n+ * This does not work on pointers, or arrays declared as [], or\n+ * function parameters.  With correct compiler support, such usage\n+ * will cause a build error (see build_assert).\n+ */\n+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]) + _array_size_chk(x))\n #define bitsizeof(x)  (CHAR_BIT * sizeof(x))\n \n #define maximum_signed_value_of_type(a) \\\n-- \n1.7.10.4\n"}]}