Add some bit math helpers Change-Id: I59ed76d52aa6346f46595faa9c7220b66a6a6964 Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/100447 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: Lily Chen <chlily@google.com>
diff --git a/crypto/crypto_test.cc b/crypto/crypto_test.cc index 8658a3c..bbcabd4 100644 --- a/crypto/crypto_test.cc +++ b/crypto/crypto_test.cc
@@ -56,6 +56,74 @@ CRYPTO_bswap8(UINT64_C(0x0102030405060708))); } +TEST(CryptoTest, BitWidth) { + EXPECT_EQ(CRYPTO_bit_width(0), 0); + EXPECT_EQ(CRYPTO_bit_width(1), 1); + EXPECT_EQ(CRYPTO_bit_width(2), 2); + EXPECT_EQ(CRYPTO_bit_width(3), 2); + EXPECT_EQ(CRYPTO_bit_width(4), 3); + EXPECT_EQ(CRYPTO_bit_width(5), 3); + EXPECT_EQ(CRYPTO_bit_width(6), 3); + EXPECT_EQ(CRYPTO_bit_width(7), 3); + EXPECT_EQ(CRYPTO_bit_width(8), 4); + EXPECT_EQ(CRYPTO_bit_width(~uint64_t{0}), 64); + EXPECT_EQ(CRYPTO_bit_width(~uint64_t{0} - 1), 64); +} + +TEST(CryptoTest, Popcount) { + EXPECT_EQ(CRYPTO_popcount(0), 0); + EXPECT_EQ(CRYPTO_popcount(1), 1); + EXPECT_EQ(CRYPTO_popcount(2), 1); + EXPECT_EQ(CRYPTO_popcount(3), 2); + EXPECT_EQ(CRYPTO_popcount(4), 1); + EXPECT_EQ(CRYPTO_popcount(5), 2); + EXPECT_EQ(CRYPTO_popcount(6), 2); + EXPECT_EQ(CRYPTO_popcount(7), 3); + EXPECT_EQ(CRYPTO_popcount(8), 1); + EXPECT_EQ(CRYPTO_popcount(~uint64_t{0}), 64); + EXPECT_EQ(CRYPTO_popcount(~uint64_t{0} - 1), 63); +} + +TEST(CryptoTest, BitCeil) { + EXPECT_EQ(CRYPTO_bit_ceil(0), 1u); + EXPECT_EQ(CRYPTO_bit_ceil(1), 1u); + EXPECT_EQ(CRYPTO_bit_ceil(2), 2u); + EXPECT_EQ(CRYPTO_bit_ceil(3), 4u); + EXPECT_EQ(CRYPTO_bit_ceil(4), 4u); + EXPECT_EQ(CRYPTO_bit_ceil(5), 8u); + EXPECT_EQ(CRYPTO_bit_ceil(6), 8u); + EXPECT_EQ(CRYPTO_bit_ceil(7), 8u); + EXPECT_EQ(CRYPTO_bit_ceil(8), 8u); + EXPECT_EQ(CRYPTO_bit_ceil(9), 16u); +} + +TEST(CryptoTest, BitFloor) { + EXPECT_EQ(CRYPTO_bit_floor(0), 0u); + EXPECT_EQ(CRYPTO_bit_floor(1), 1u); + EXPECT_EQ(CRYPTO_bit_floor(2), 2u); + EXPECT_EQ(CRYPTO_bit_floor(3), 2u); + EXPECT_EQ(CRYPTO_bit_floor(4), 4u); + EXPECT_EQ(CRYPTO_bit_floor(5), 4u); + EXPECT_EQ(CRYPTO_bit_floor(6), 4u); + EXPECT_EQ(CRYPTO_bit_floor(7), 4u); + EXPECT_EQ(CRYPTO_bit_floor(8), 8u); + EXPECT_EQ(CRYPTO_bit_floor(9), 8u); +} + +TEST(CryptoTest, HasSingleBit) { + EXPECT_FALSE(CRYPTO_has_single_bit(0)); + EXPECT_TRUE(CRYPTO_has_single_bit(1)); + EXPECT_TRUE(CRYPTO_has_single_bit(2)); + EXPECT_FALSE(CRYPTO_has_single_bit(3)); + EXPECT_TRUE(CRYPTO_has_single_bit(4)); + EXPECT_FALSE(CRYPTO_has_single_bit(5)); + EXPECT_FALSE(CRYPTO_has_single_bit(6)); + EXPECT_FALSE(CRYPTO_has_single_bit(7)); + EXPECT_TRUE(CRYPTO_has_single_bit(8)); + EXPECT_FALSE(CRYPTO_has_single_bit(~uint64_t{0})); + EXPECT_TRUE(CRYPTO_has_single_bit(uint64_t{1} << 63)); +} + #if defined(BORINGSSL_FIPS_COUNTERS) using CounterArray = size_t[fips_counter_max + 1];
diff --git a/crypto/internal.h b/crypto/internal.h index dda1933..e318a24 100644 --- a/crypto/internal.h +++ b/crypto/internal.h
@@ -1025,6 +1025,60 @@ } +// Bit math functions. +// +// Polyfills for C++20. + +// CRYPTO_bit_width returns the smallest number of bits needed to represent `n`. +// It returns zero if `n` is zero. +inline int CRYPTO_bit_width(uint64_t n) { +#if OPENSSL_HAS_BUILTIN(__builtin_clzll) + static_assert(sizeof(unsigned long long) >= sizeof(uint64_t)); + return n ? (sizeof(unsigned long long) * 8 - __builtin_clzll(n)) : 0; +#else + int width = 0; + while (n > 0) { + ++width; + n >>= 1; + } + return width; +#endif +} + +// CRYPTO_popcount returns the number of set bits in `n`'s binary +// representation. +inline int CRYPTO_popcount(uint64_t n) { +#if OPENSSL_HAS_BUILTIN(__builtin_popcountll) + static_assert(sizeof(unsigned long long) >= sizeof(uint64_t)); + return __builtin_popcountll(n); +#else + int count = 0; + while (n > 0) { + n &= (n - 1); // Clears the least significant bit that is set. + ++count; + } + return count; +#endif +} + +// CRYPTO_bit_ceil returns the smallest power of 2 that is greater than or equal +// to `n`. +inline uint64_t CRYPTO_bit_ceil(uint64_t n) { + return n ? uint64_t{1} << CRYPTO_bit_width(n - 1) : uint64_t{1}; +} + +// CRYPTO_bit_floor returns the largest power of 2 that is less than or equal to +// `n`. It returns zero if `n` is zero. +inline uint64_t CRYPTO_bit_floor(uint64_t n) { + return n ? (uint64_t{1} << (CRYPTO_bit_width(n) - 1)) : uint64_t{0}; +} + +// CRYPTO_has_single_bit returns whether `n` is an integral power of 2. +inline bool CRYPTO_has_single_bit(uint64_t n) { + return CRYPTO_popcount(n) == 1; +} + + // FIPS functions. #if defined(BORINGSSL_FIPS)