From f0705bb6286d8a24e08ddd99641264ba947ebd03 Mon Sep 17 00:00:00 2001 From: David Rowley Date: Wed, 8 Apr 2020 16:22:52 +1200 Subject: [PATCH] Add functions to calculate the next power of 2 There are many areas in the code where we need to determine the next highest power of 2 of a given number. We tend to always do that in an ad-hoc way each time, generally with some tight for loop which performs a bitshift left once per loop and goes until it finds a number above the given number. Here we add two generic functions which make use of the existing pg_leftmost_one_pos* functions which, when available, will allow us to calculate the next power of 2 without any looping. Here we don't add any code which uses these new functions. That will be done in follow-up commits. Author: David Fetter, with some minor adjustments by me Reviewed-by: John Naylor, Jesse Zhang Discussion: https://postgr.es/m/20200114173553.GE32763%40fetter.org --- src/include/port/pg_bitutils.h | 72 ++++++++++++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/src/include/port/pg_bitutils.h b/src/include/port/pg_bitutils.h index 498e5323081..4ca92f076dc 100644 --- a/src/include/port/pg_bitutils.h +++ b/src/include/port/pg_bitutils.h @@ -129,6 +129,78 @@ pg_rightmost_one_pos64(uint64 word) #endif /* HAVE__BUILTIN_CTZ */ } +/* + * pg_nextpower2_32 + * Returns the next highest power of 2 of 'num', or 'num', if it's + * already a power of 2. + * + * 'num' mustn't be 0 or be above PG_UINT32_MAX / 2 + 1. + */ +static inline uint32 +pg_nextpower2_32(uint32 num) +{ + Assert(num > 0 && num <= PG_UINT32_MAX / 2 + 1); + + /* + * A power 2 number has only 1 bit set. Subtracting 1 from such a number + * will turn on all previous bits resulting in no common bits being set + * between num and num-1. + */ + if ((num & (num - 1)) == 0) + return num; /* already power 2 */ + + return ((uint32) 1) << (pg_leftmost_one_pos32(num) + 1); +} + +/* + * pg_nextpower2_64 + * Returns the next highest power of 2 of 'num', or 'num', if it's + * already a power of 2. + * + * 'num' mustn't be 0 or be above PG_UINT64_MAX / 2 + 1. + */ +static inline uint64 +pg_nextpower2_64(uint64 num) +{ + Assert(num > 0 && num <= PG_UINT64_MAX / 2 + 1); + + /* + * A power 2 number has only 1 bit set. Subtracting 1 from such a number + * will turn on all previous bits resulting in no common bits being set + * between num and num-1. + */ + if ((num & (num - 1)) == 0) + return num; /* already power 2 */ + + return ((uint64) 1) << (pg_leftmost_one_pos64(num) + 1); +} + +/* + * pg_ceil_log2_32 + * Returns equivalent of ceil(log2(num)) + */ +static inline uint32 +pg_ceil_log2_32(uint32 num) +{ + if (num < 2) + return 0; + else + return pg_leftmost_one_pos32(num - 1) + 1; +} + +/* + * pg_ceil_log2_64 + * Returns equivalent of ceil(log2(num)) + */ +static inline uint64 +pg_ceil_log2_64(uint64 num) +{ + if (num < 2) + return 0; + else + return pg_leftmost_one_pos64(num - 1) + 1; +} + /* Count the number of one-bits in a uint32 or uint64 */ extern int (*pg_popcount32) (uint32 word); extern int (*pg_popcount64) (uint64 word);