Commit e8fe775b authored by David Gibson's avatar David Gibson Committed by Rusty Russell

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: default avatarDavid Gibson <david@gibson.dropbear.id.au>
Signed-off-by: default avatarRusty Russell <rusty@rustcorp.com.au>
parent 930753e7
......@@ -20,6 +20,7 @@ MODS_NO_SRC := alignof \
container_of \
darray \
endian \
minmax \
objset \
short_types \
tcon \
......
../../licenses/CC0
\ No newline at end of file
#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;
}
/* 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 */
#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;
}
#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;
}
#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();
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment