diff options
author | David Gibson <david@gibson.dropbear.id.au> | 2014-06-02 22:37:28 +1000 |
---|---|---|
committer | Rusty Russell <rusty@rustcorp.com.au> | 2014-06-04 11:00:05 +0930 |
commit | e8fe775b03a9abde11f7ce7a9ebf8e002111e923 (patch) | |
tree | bdbd0ee95e39ea15f13de0faf7fef079b243bff8 | |
parent | 930753e727859171d37f2e51d129f2e42ecf1dfa (diff) |
minmax: New module, safe min and max macros
Add a 'minmax' module with typesafe macros to compute minimum, maximum and
clamping. Inspired by the versions used in the Linux kernel, but using
a different implementation based on __builtin_types_compatible_p() and the
build_assert module.
Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
Signed-off-by: Rusty Russell <rusty@rustcorp.com.au>
-rw-r--r-- | Makefile-ccan | 1 | ||||
l--------- | ccan/minmax/LICENSE | 1 | ||||
-rw-r--r-- | ccan/minmax/_info | 45 | ||||
-rw-r--r-- | ccan/minmax/minmax.h | 65 | ||||
-rw-r--r-- | ccan/minmax/test/compile_fail-wrongsign.c | 19 | ||||
-rw-r--r-- | ccan/minmax/test/compile_fail-wrongsize.c | 19 | ||||
-rw-r--r-- | ccan/minmax/test/run.c | 46 |
7 files changed, 196 insertions, 0 deletions
diff --git a/Makefile-ccan b/Makefile-ccan index cbf1c2fe..1d17e26f 100644 --- a/Makefile-ccan +++ b/Makefile-ccan @@ -20,6 +20,7 @@ MODS_NO_SRC := alignof \ container_of \ darray \ endian \ + minmax \ objset \ short_types \ tcon \ diff --git a/ccan/minmax/LICENSE b/ccan/minmax/LICENSE new file mode 120000 index 00000000..b7951dab --- /dev/null +++ b/ccan/minmax/LICENSE @@ -0,0 +1 @@ +../../licenses/CC0
\ No newline at end of file diff --git a/ccan/minmax/_info b/ccan/minmax/_info new file mode 100644 index 00000000..1490ba43 --- /dev/null +++ b/ccan/minmax/_info @@ -0,0 +1,45 @@ +#include <string.h> +#include "config.h" + +/** + * minmax - typesafe minimum and maximum functions + * + * The classic implementation of minimum / maximum macros in C can be + * very dangerous. If the two arguments have different sizes, or + * different signedness, type promotion rules can lead to very + * surprising results. + * + * This module implements typesafe versions, which will generate a + * compile time error, if the arguments have different types. + * + * Example: + * #include <ccan/minmax/minmax.h> + * #include <stdio.h> + * + * int main(int argc, char *argv[]) + * { + * printf("Signed max: %d\n", max(1, -1)); + * printf("Unsigned max: %u\n", max(1U, -1U)); + * return 0; + * } + * + * Author: David Gibson <david@gibson.dropbear.id.au> + * License: CC0 (Public domain) + * + * Ccanlint: + * // We need several gcc extensions + * tests_compile_without_features FAIL + */ +int main(int argc, char *argv[]) +{ + /* Expect exactly one argument */ + if (argc != 2) + return 1; + + if (strcmp(argv[1], "depends") == 0) { + printf("ccan/build_assert\n"); + return 0; + } + + return 1; +} diff --git a/ccan/minmax/minmax.h b/ccan/minmax/minmax.h new file mode 100644 index 00000000..d111d1bc --- /dev/null +++ b/ccan/minmax/minmax.h @@ -0,0 +1,65 @@ +/* CC0 (Public domain) - see LICENSE file for details */ +#ifndef CCAN_MINMAX_H +#define CCAN_MINMAX_H + +#include "config.h" + +#include <ccan/build_assert/build_assert.h> + +#if !HAVE_STATEMENT_EXPR || !HAVE_TYPEOF +/* + * Without these, there's no way to avoid unsafe double evaluation of + * the arguments + */ +#error Sorry, minmax module requires statement expressions and typeof +#endif + +#if HAVE_BUILTIN_TYPES_COMPATIBLE_P +#define MINMAX_ASSERT_COMPATIBLE(a, b) \ + BUILD_ASSERT(__builtin_types_compatible_p(a, b)) +#else +#define MINMAX_ASSERT_COMPATIBLE(a, b) \ + do { } while (0) +#endif + +#define min(a, b) \ + ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + MINMAX_ASSERT_COMPATIBLE(typeof(_a), typeof(_b)); \ + _a < _b ? _a : _b; \ + }) + +#define max(a, b) \ + ({ \ + typeof(a) _a = (a); \ + typeof(b) _b = (b); \ + MINMAX_ASSERT_COMPATIBLE(typeof(_a), typeof(_b)); \ + _a > _b ? _a : _b; \ + }) + +#define clamp(v, f, c) (max(min((v), (c)), (f))) + + +#define min_t(t, a, b) \ + ({ \ + t _ta = (a); \ + t _tb = (b); \ + min(_ta, _tb); \ + }) +#define max_t(t, a, b) \ + ({ \ + t _ta = (a); \ + t _tb = (b); \ + max(_ta, _tb); \ + }) + +#define clamp_t(t, v, f, c) \ + ({ \ + t _tv = (v); \ + t _tf = (f); \ + t _tc = (c); \ + clamp(_tv, _tf, _tc); \ + }) + +#endif /* CCAN_MINMAX_H */ diff --git a/ccan/minmax/test/compile_fail-wrongsign.c b/ccan/minmax/test/compile_fail-wrongsign.c new file mode 100644 index 00000000..6aeaf1c7 --- /dev/null +++ b/ccan/minmax/test/compile_fail-wrongsign.c @@ -0,0 +1,19 @@ +#include <ccan/minmax/minmax.h> + +static int function(void) +{ +#ifdef FAIL + return min(1, 1U); +#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P +#error "Unfortunately we don't fail if the typechecks are noops." +#endif +#else + return 0; +#endif +} + +int main(int argc, char *argv[]) +{ + function(); + return 0; +} diff --git a/ccan/minmax/test/compile_fail-wrongsize.c b/ccan/minmax/test/compile_fail-wrongsize.c new file mode 100644 index 00000000..ef57784f --- /dev/null +++ b/ccan/minmax/test/compile_fail-wrongsize.c @@ -0,0 +1,19 @@ +#include <ccan/minmax/minmax.h> + +static int function(void) +{ +#ifdef FAIL + return min(1, 1L); +#if !HAVE_TYPEOF||!HAVE_BUILTIN_CHOOSE_EXPR||!HAVE_BUILTIN_TYPES_COMPATIBLE_P +#error "Unfortunately we don't fail if the typechecks are noops." +#endif +#else + return 0; +#endif +} + +int main(int argc, char *argv[]) +{ + function(); + return 0; +} diff --git a/ccan/minmax/test/run.c b/ccan/minmax/test/run.c new file mode 100644 index 00000000..e954efdd --- /dev/null +++ b/ccan/minmax/test/run.c @@ -0,0 +1,46 @@ +#include <ccan/minmax/minmax.h> +#include <ccan/tap/tap.h> + +int main(void) +{ + int a, b; + + /* This is how many tests you plan to run */ + plan_tests(23); + + ok1(min(1, 2) == 1); + ok1(max(1, 2) == 2); + ok1(min(-1, 1) == -1); + ok1(max(-1, 1) == 1); + + ok1(min(-1U, 1U) == 1U); + ok1(max(-1U, 1U) == -1U); + + ok1(max_t(signed int, -1, 1U) == 1); + ok1(max_t(unsigned int, -1, 1) == -1U); + + ok1(min_t(signed int, -1, 1U) == -1); + ok1(min_t(unsigned int, -1, 1) == 1U); + + ok1(clamp(1, 2, 5) == 2); + ok1(clamp(2, 2, 5) == 2); + ok1(clamp(3, 2, 5) == 3); + ok1(clamp(5, 2, 5) == 5); + ok1(clamp(6, 2, 5) == 5); + + ok1(clamp(-1, 2, 5) == 2); + ok1(clamp(-1U, 2U, 5U) == 5U); + + ok1(clamp_t(signed int, -1, 2, 5) == 2); + ok1(clamp_t(unsigned int, -1, 2, 5) == 5); + + /* test for double evaluation */ + a = b = 0; + ok1(min(a++, b++) == 0); + ok1((a == 1) && (b == 1)); + ok1(max(++a, ++b) == 2); + ok1((a == 2) && (b == 2)); + + /* This exits depending on whether all tests passed */ + return exit_status(); +} |