diff --git a/include/sys/util.h b/include/sys/util.h index 85d74359e2e..a1e78a6cdd0 100644 --- a/include/sys/util.h +++ b/include/sys/util.h @@ -178,6 +178,16 @@ extern "C" { #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #endif +/** + * @def CLAMP + * @brief Clamp a value to a given range. + * @note Arguments are evaluated multiple times. + */ +#ifndef CLAMP +/* Use Z_CLAMP for a GCC-only, single evaluation version */ +#define CLAMP(val, low, high) (((val) <= (low)) ? (low) : MIN(val, high)) +#endif + /** * @brief Is @p x a power of two? * @param x value to check diff --git a/include/toolchain/gcc.h b/include/toolchain/gcc.h index b1213dce8d6..c74f5eff45a 100644 --- a/include/toolchain/gcc.h +++ b/include/toolchain/gcc.h @@ -474,6 +474,21 @@ do { \ _value_a_ < _value_b_ ? _value_a_ : _value_b_; \ }) +/** @brief Return a value clamped to a given range. + * + * Macro ensures that expressions are evaluated only once. See @ref Z_MAX for + * macro limitations. + */ +#define Z_CLAMP(val, low, high) ({ \ + /* random suffix to avoid naming conflict */ \ + __typeof__(val) _value_val_ = (val); \ + __typeof__(low) _value_low_ = (low); \ + __typeof__(high) _value_high_ = (high); \ + (_value_val_ < _value_low_) ? _value_low_ : \ + (_value_val_ > _value_high_) ? _value_high_ : \ + _value_val_; \ + }) + /** * @brief Calculate power of two ceiling for some nonzero value * diff --git a/tests/unit/util/test.inc b/tests/unit/util/test.inc index cafd5f6f5c1..a318d252e17 100644 --- a/tests/unit/util/test.inc +++ b/tests/unit/util/test.inc @@ -203,10 +203,10 @@ static int inc_func(void) return a++; } -/* Test checks if @ref Z_MAX and @ref Z_MIN return correct result and perform - * single evaluation of input arguments. +/* Test checks if @ref Z_MAX, @ref Z_MIN and @ref Z_CLAMP return correct result + * and perform single evaluation of input arguments. */ -static void test_z_max_z_min(void) +static void test_z_max_z_min_z_clamp(void) { zassert_equal(Z_MAX(inc_func(), 0), 1, "Unexpected macro result"); /* Z_MAX should have call inc_func only once */ @@ -215,6 +215,31 @@ static void test_z_max_z_min(void) zassert_equal(Z_MIN(inc_func(), 2), 2, "Unexpected macro result"); /* Z_MIN should have call inc_func only once */ zassert_equal(inc_func(), 4, "Unexpected return value"); + + zassert_equal(Z_CLAMP(inc_func(), 1, 3), 3, "Unexpected macro result"); + /* Z_CLAMP should have call inc_func only once */ + zassert_equal(inc_func(), 6, "Unexpected return value"); + + zassert_equal(Z_CLAMP(inc_func(), 10, 15), 10, + "Unexpected macro result"); + /* Z_CLAMP should have call inc_func only once */ + zassert_equal(inc_func(), 8, "Unexpected return value"); +} + +static void test_CLAMP(void) +{ + zassert_equal(CLAMP(5, 3, 7), 5, "Unexpected clamp result"); + zassert_equal(CLAMP(3, 3, 7), 3, "Unexpected clamp result"); + zassert_equal(CLAMP(7, 3, 7), 7, "Unexpected clamp result"); + zassert_equal(CLAMP(1, 3, 7), 3, "Unexpected clamp result"); + zassert_equal(CLAMP(8, 3, 7), 7, "Unexpected clamp result"); + + zassert_equal(CLAMP(-5, -7, -3), -5, "Unexpected clamp result"); + zassert_equal(CLAMP(-9, -7, -3), -7, "Unexpected clamp result"); + zassert_equal(CLAMP(1, -7, -3), -3, "Unexpected clamp result"); + + zassert_equal(CLAMP(0xffffffffaULL, 0xffffffff0ULL, 0xfffffffffULL), + 0xffffffffaULL, "Unexpected clamp result"); } static void test_FOR_EACH(void) @@ -433,7 +458,8 @@ void test_cc(void) ztest_unit_test(test_IF_ENABLED), ztest_unit_test(test_UTIL_LISTIFY), ztest_unit_test(test_MACRO_MAP_CAT), - ztest_unit_test(test_z_max_z_min), + ztest_unit_test(test_z_max_z_min_z_clamp), + ztest_unit_test(test_CLAMP), ztest_unit_test(test_FOR_EACH), ztest_unit_test(test_FOR_EACH_NONEMPTY_TERM), ztest_unit_test(test_FOR_EACH_FIXED_ARG),