From f4ada1b42ffd14a12bbad48b40d65332673bc869 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Zbigniew=20J=C4=99drzejewski-Szmek?= Date: Fri, 26 Nov 2021 11:46:54 +0100 Subject: [PATCH] basic/util: add macro variants of log2 functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The macro variants can be used in static initializers. The same guard against calling __builtin_clz(0) is added as for __builtin_clzll(0), since that's undefined behaviour too. Our code wouldn't call it, but this avoids a potential pitfall with the macro. All variants map 0→0. Otherwise we'd often have to handle 0 specially in callers. __builtin_clz takes unsigned as the argument, so there's no LOG2I macro. --- src/basic/util.h | 36 ++++++++++++++--------- src/test/test-util.c | 69 +++++++++++++++++++++++++++++++++++++------- 2 files changed, 81 insertions(+), 24 deletions(-) diff --git a/src/basic/util.h b/src/basic/util.h index 20bda9bbf3e..f5434c96418 100644 --- a/src/basic/util.h +++ b/src/basic/util.h @@ -22,11 +22,20 @@ void in_initrd_force(bool value); int on_ac_power(void); -static inline unsigned log2u64(uint64_t n) { +/* Note: log2(0) == log2(1) == 0 here and below. */ + +#define CONST_LOG2ULL(x) ((x) > 1 ? (unsigned) __builtin_clzll(x) ^ 63U : 0) +#define NONCONST_LOG2ULL(x) ({ \ + unsigned long long _x = (x); \ + _x > 1 ? (unsigned) __builtin_clzll(_x) ^ 63U : 0; \ + }) +#define LOG2ULL(x) __builtin_choose_expr(__builtin_constant_p(x), CONST_LOG2ULL(x), NONCONST_LOG2ULL(x)) + +static inline unsigned log2u64(uint64_t x) { #if __SIZEOF_LONG_LONG__ == 8 - return (n > 1) ? (unsigned) __builtin_clzll(n) ^ 63U : 0; + return LOG2ULL(x); #else -#error "Wut?" +# error "Wut?" #endif } @@ -34,26 +43,27 @@ static inline unsigned u32ctz(uint32_t n) { #if __SIZEOF_INT__ == 4 return n != 0 ? __builtin_ctz(n) : 32; #else -#error "Wut?" +# error "Wut?" #endif } -static inline unsigned log2i(int x) { - assert(x > 0); +#define CONST_LOG2U(x) ((x) > 1 ? __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1 : 0) +#define NONCONST_LOG2U(x) ({ \ + unsigned _x = (x); \ + _x > 1 ? __SIZEOF_INT__ * 8 - __builtin_clz(_x) - 1 : 0; \ + }) +#define LOG2U(x) __builtin_choose_expr(__builtin_constant_p(x), CONST_LOG2U(x), NONCONST_LOG2U(x)) - return __SIZEOF_INT__ * 8 - __builtin_clz(x) - 1; +static inline unsigned log2i(int x) { + return LOG2U(x); } static inline unsigned log2u(unsigned x) { - assert(x > 0); - - return sizeof(unsigned) * 8 - __builtin_clz(x) - 1; + return LOG2U(x); } static inline unsigned log2u_round_up(unsigned x) { - assert(x > 0); - - if (x == 1) + if (x <= 1) return 0; return log2u(x - 1) + 1; diff --git a/src/test/test-util.c b/src/test/test-util.c index 8e8ad05205f..21ab016c228 100644 --- a/src/test/test-util.c +++ b/src/test/test-util.c @@ -17,8 +17,42 @@ #include "tests.h" #include "util.h" +TEST(LOG2ULL) { + assert_se(LOG2ULL(0) == 0); + assert_se(LOG2ULL(1) == 0); + assert_se(LOG2ULL(8) == 3); + assert_se(LOG2ULL(9) == 3); + assert_se(LOG2ULL(15) == 3); + assert_se(LOG2ULL(16) == 4); + assert_se(LOG2ULL(1024*1024) == 20); + assert_se(LOG2ULL(1024*1024+5) == 20); +} + +TEST(CONST_LOG2ULL) { + assert_se(CONST_LOG2ULL(0) == 0); + assert_se(CONST_LOG2ULL(1) == 0); + assert_se(CONST_LOG2ULL(8) == 3); + assert_se(CONST_LOG2ULL(9) == 3); + assert_se(CONST_LOG2ULL(15) == 3); + assert_se(CONST_LOG2ULL(16) == 4); + assert_se(CONST_LOG2ULL(1024*1024) == 20); + assert_se(CONST_LOG2ULL(1024*1024+5) == 20); +} + +TEST(NONCONST_LOG2ULL) { + assert_se(NONCONST_LOG2ULL(0) == 0); + assert_se(NONCONST_LOG2ULL(1) == 0); + assert_se(NONCONST_LOG2ULL(8) == 3); + assert_se(NONCONST_LOG2ULL(9) == 3); + assert_se(NONCONST_LOG2ULL(15) == 3); + assert_se(NONCONST_LOG2ULL(16) == 4); + assert_se(NONCONST_LOG2ULL(1024*1024) == 20); + assert_se(NONCONST_LOG2ULL(1024*1024+5) == 20); +} + TEST(log2u64) { assert_se(log2u64(0) == 0); + assert_se(log2u64(1) == 0); assert_se(log2u64(8) == 3); assert_se(log2u64(9) == 3); assert_se(log2u64(15) == 3); @@ -27,6 +61,30 @@ TEST(log2u64) { assert_se(log2u64(1024*1024+5) == 20); } +TEST(log2u) { + assert_se(log2u(0) == 0); + assert_se(log2u(1) == 0); + assert_se(log2u(2) == 1); + assert_se(log2u(3) == 1); + assert_se(log2u(4) == 2); + assert_se(log2u(32) == 5); + assert_se(log2u(33) == 5); + assert_se(log2u(63) == 5); + assert_se(log2u(INT_MAX) == sizeof(int)*8-2); +} + +TEST(log2i) { + assert_se(log2i(0) == 0); + assert_se(log2i(1) == 0); + assert_se(log2i(2) == 1); + assert_se(log2i(3) == 1); + assert_se(log2i(4) == 2); + assert_se(log2i(32) == 5); + assert_se(log2i(33) == 5); + assert_se(log2i(63) == 5); + assert_se(log2i(INT_MAX) == sizeof(int)*8-2); +} + TEST(protect_errno) { errno = 12; { @@ -58,17 +116,6 @@ TEST(unprotect_errno) { assert_se(errno == 4711); } -TEST(log2i) { - assert_se(log2i(1) == 0); - assert_se(log2i(2) == 1); - assert_se(log2i(3) == 1); - assert_se(log2i(4) == 2); - assert_se(log2i(32) == 5); - assert_se(log2i(33) == 5); - assert_se(log2i(63) == 5); - assert_se(log2i(INT_MAX) == sizeof(int)*8-2); -} - TEST(eqzero) { const uint32_t zeros[] = {0, 0, 0}; const uint32_t ones[] = {1, 1};