nbdkit/0011-common-include-Add-ARR...

303 lines
11 KiB
Diff

From 06d47e2efe1387a3873529c52a4c7c680ddeee22 Mon Sep 17 00:00:00 2001
From: "Richard W.M. Jones" <rjones@redhat.com>
Date: Thu, 7 Jul 2022 12:37:10 +0100
Subject: [PATCH] common/include: Add ARRAY_SIZE macro
This macro returns the length (in elements) at compile time of arrays
declared as:
int foo[] = { 1, 2, 3 };
ARRAY_SIZE(foo)
===> 3
It also attempts to detect some errors, such as using the macro with a
pointer instead of an array, with some ideas borrowed (not copied)
from Linux, QEMU and Stack Overflow questions.
Link: https://stackoverflow.com/questions/19452971/array-size-macro-that-rejects-pointers
Link: https://listman.redhat.com/archives/libguestfs/2022-July/029412.html
Thanks: Laszlo Ersek
(cherry picked from commit 0fa23df5cd5dc97a55857416ea81d5de6d867c18)
---
.gitignore | 1 +
common/include/Makefile.am | 7 +++
common/include/array-size.h | 41 +++++++++++++
common/include/compiler-macros.h | 60 +++++++++++++++++++
common/include/test-array-size.c | 100 +++++++++++++++++++++++++++++++
5 files changed, 209 insertions(+)
create mode 100644 common/include/array-size.h
create mode 100644 common/include/compiler-macros.h
create mode 100644 common/include/test-array-size.c
diff --git a/.gitignore b/.gitignore
index 10e1f99d..658fd4e6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -35,6 +35,7 @@ plugins/*/*.3
/aclocal.m4
/autom4te.cache
/common/bitmap/test-bitmap
+/common/include/test-array-size
/common/include/test-ascii-ctype
/common/include/test-ascii-string
/common/include/test-byte-swapping
diff --git a/common/include/Makefile.am b/common/include/Makefile.am
index b73dd471..d3cf9408 100644
--- a/common/include/Makefile.am
+++ b/common/include/Makefile.am
@@ -34,10 +34,12 @@ include $(top_srcdir)/common-rules.mk
# These headers contain only common code shared by the core server,
# plugins and/or filters. They are not installed.
EXTRA_DIST = \
+ array-size.h \
ascii-ctype.h \
ascii-string.h \
byte-swapping.h \
checked-overflow.h \
+ compiler-macros.h \
exit-with-parent.h \
hexdigit.h \
isaligned.h \
@@ -55,6 +57,7 @@ EXTRA_DIST = \
# Unit tests.
TESTS = \
+ test-array-size \
test-ascii-ctype \
test-ascii-string \
test-byte-swapping \
@@ -69,6 +72,10 @@ TESTS = \
$(NULL)
check_PROGRAMS = $(TESTS)
+test_array_size_SOURCES = test-array-size.c array-size.h
+test_array_size_CPPFLAGS = -I$(srcdir)
+test_array_size_CFLAGS = $(WARNINGS_CFLAGS)
+
test_ascii_ctype_SOURCES = test-ascii-ctype.c ascii-ctype.h
test_ascii_ctype_CPPFLAGS = -I$(srcdir)
test_ascii_ctype_CFLAGS = $(WARNINGS_CFLAGS)
diff --git a/common/include/array-size.h b/common/include/array-size.h
new file mode 100644
index 00000000..b6d33dde
--- /dev/null
+++ b/common/include/array-size.h
@@ -0,0 +1,41 @@
+/* nbdkit
+ * Copyright (C) 2013-2022 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef NBDKIT_ARRAY_SIZE_H
+#define NBDKIT_ARRAY_SIZE_H
+
+#include "compiler-macros.h"
+
+#define ARRAY_SIZE(a) \
+ ((sizeof (a) / sizeof ((a)[0])) + BUILD_BUG_ON_ZERO (!TYPE_IS_ARRAY(a)))
+
+#endif /* NBDKIT_ARRAY_SIZE_H */
diff --git a/common/include/compiler-macros.h b/common/include/compiler-macros.h
new file mode 100644
index 00000000..504e0085
--- /dev/null
+++ b/common/include/compiler-macros.h
@@ -0,0 +1,60 @@
+/* nbdkit
+ * Copyright (C) 2013-2022 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef NBDKIT_COMPILER_MACROS_H
+#define NBDKIT_COMPILER_MACROS_H
+
+#ifndef __cplusplus
+
+/* This expression fails at compile time if 'expr' is true. It does
+ * this by constructing a struct which has an impossible
+ * (negative-sized) array.
+ *
+ * If 'expr' is false then we subtract the sizes of the two identical
+ * structures, returning zero.
+ */
+#define BUILD_BUG_ON_ZERO_SIZEOF(expr) \
+ (sizeof (struct { int _array_size_failed[(expr) ? -1 : 1]; }))
+#define BUILD_BUG_ON_ZERO(expr) \
+ (BUILD_BUG_ON_ZERO_SIZEOF(expr) - BUILD_BUG_ON_ZERO_SIZEOF(expr))
+
+#define TYPE_IS_ARRAY(a) \
+ (!__builtin_types_compatible_p (typeof (a), typeof (&(a)[0])))
+
+#else /* __cplusplus */
+
+#define BUILD_BUG_ON_ZERO(expr) 0
+#define TYPE_IS_ARRAY(a) 1
+
+#endif /* __cplusplus */
+
+#endif /* NBDKIT_COMPILER_MACROS_H */
diff --git a/common/include/test-array-size.c b/common/include/test-array-size.c
new file mode 100644
index 00000000..d77ba3c9
--- /dev/null
+++ b/common/include/test-array-size.c
@@ -0,0 +1,100 @@
+/* nbdkit
+ * Copyright (C) 2020-2022 Red Hat Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * * Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * * Neither the name of Red Hat nor the names of its contributors may be
+ * used to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY RED HAT AND CONTRIBUTORS ''AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+ * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL RED HAT OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#undef NDEBUG /* Keep test strong even for nbdkit built without assertions */
+#include <assert.h>
+
+#include "array-size.h"
+
+struct st { const char *s; int i; };
+
+static const char *s0[] = { };
+static const char *s1[] = { "a" };
+static const char *s3[] = { "a", "b", "c" };
+static const char *s4[4] = { "a", "b", "c", "d" };
+static int i0[] = { };
+static int i1[] = { 1 };
+static int i3[] = { 1, 2, 3 };
+static int i4[4] = { 1, 2, 3, 4 };
+static struct st st0[] = { };
+static struct st st1[] = { { "a", 1 } };
+static struct st st3[] = { { "a", 1 }, { "b", 2 }, { "c", 3 } };
+static struct st st4[4] = { { "a", 1 }, { "b", 2 }, { "c", 3 }, { "d", 4 } };
+static struct st st4_0[4];
+
+int
+main (void)
+{
+ assert (ARRAY_SIZE (s0) == 0);
+ assert (ARRAY_SIZE (s1) == 1);
+ assert (ARRAY_SIZE (s3) == 3);
+ assert (ARRAY_SIZE (s4) == 4);
+ assert (ARRAY_SIZE (i0) == 0);
+ assert (ARRAY_SIZE (i1) == 1);
+ assert (ARRAY_SIZE (i3) == 3);
+ assert (ARRAY_SIZE (i4) == 4);
+ assert (ARRAY_SIZE (st0) == 0);
+ assert (ARRAY_SIZE (st1) == 1);
+ assert (ARRAY_SIZE (st3) == 3);
+ assert (ARRAY_SIZE (st4) == 4);
+ assert (ARRAY_SIZE (st4_0) == 4);
+
+#ifdef static_assert
+ static_assert (ARRAY_SIZE (s0) == 0, "ARRAY_SIZE macro does not work");
+ static_assert (ARRAY_SIZE (s1) == 1, "ARRAY_SIZE macro does not work");
+ static_assert (ARRAY_SIZE (s3) == 3, "ARRAY_SIZE macro does not work");
+ static_assert (ARRAY_SIZE (s4) == 4, "ARRAY_SIZE macro does not work");
+ static_assert (ARRAY_SIZE (i0) == 0, "ARRAY_SIZE macro does not work");
+ static_assert (ARRAY_SIZE (i1) == 1, "ARRAY_SIZE macro does not work");
+ static_assert (ARRAY_SIZE (i3) == 3, "ARRAY_SIZE macro does not work");
+ static_assert (ARRAY_SIZE (i4) == 4, "ARRAY_SIZE macro does not work");
+ static_assert (ARRAY_SIZE (st0) == 0, "ARRAY_SIZE macro does not work");
+ static_assert (ARRAY_SIZE (st1) == 1, "ARRAY_SIZE macro does not work");
+ static_assert (ARRAY_SIZE (st3) == 3, "ARRAY_SIZE macro does not work");
+ static_assert (ARRAY_SIZE (st4) == 4, "ARRAY_SIZE macro does not work");
+ static_assert (ARRAY_SIZE (st4_0) == 4, "ARRAY_SIZE macro does not work");
+#endif
+
+ /* You can uncomment this to test the negative case. Unfortunately
+ * it's difficult to automate this test.
+ */
+#if 0
+ int *p = i4;
+ assert (ARRAY_SIZE (p) == 4);
+#endif
+
+ exit (EXIT_SUCCESS);
+}
--
2.31.1