From 8ad056634c2ca7f060b83ca317fe9c546d44ec08 Mon Sep 17 00:00:00 2001 From: Abtin Keshavarzian Date: Mon, 6 Jul 2026 10:10:35 -0700 Subject: [PATCH] [bit-utils] add `MaskForBitSize()` helper function (#13295) This commit adds `MaskForBitSize(uint8_t aBitSize)` as a `constexpr inline` template helper function in `bit_utils.hpp`. It generates an unsigned integer bit-mask with the specified number of lowest bits set to 1. Key changes: - Adds `MaskForBitSize(aBitSize)` in `bit_utils.hpp`. - Adds runtime (`VerifyOrQuit`) and compile-time (`static_assert`) unit tests in `test_bit_utils.cpp`. --- src/core/common/bit_utils.hpp | 17 ++++++++++ tests/unit/test_bit_utils.cpp | 64 +++++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/src/core/common/bit_utils.hpp b/src/core/common/bit_utils.hpp index 6afd17813..c7323affc 100644 --- a/src/core/common/bit_utils.hpp +++ b/src/core/common/bit_utils.hpp @@ -98,6 +98,23 @@ uint16_t CountMatchingBits(const uint8_t *aFirst, const uint8_t *aSecond, uint16 */ uint8_t DetermineMinBitSizeFor(uint32_t aValue); +/** + * Generates an unsigned integer bit-mask with a specified number of lowest bits set to 1. + * + * @tparam UintType The value type (MUST be `uint8_t`, `uint16_t`, `uint32_t`, or `uint64_t`). + * + * @param[in] aBitSize The number of lowest bits to set to 1. + * + * @returns The generated bit-mask. + */ +template constexpr inline UintType MaskForBitSize(uint8_t aBitSize) +{ + static_assert(TypeTraits::IsUint::kValue, "UintType must be an unsigned int (8, 16, 32, or 64 bit len)"); + + return (aBitSize >= BitSizeOf(UintType)) ? NumericLimits::kMax + : static_cast((static_cast(1) << aBitSize) - 1); +} + /** * Sets the specified bit in a given integer to 1. * diff --git a/tests/unit/test_bit_utils.cpp b/tests/unit/test_bit_utils.cpp index 21c5c23cc..dd10cc92d 100644 --- a/tests/unit/test_bit_utils.cpp +++ b/tests/unit/test_bit_utils.cpp @@ -176,6 +176,69 @@ void TestDetermineMinBitSize(void) printf("TestDetermineMinBitSize() passed\n"); } +void TestMaskForBitSize(void) +{ + VerifyOrQuit(MaskForBitSize(0) == 0); + VerifyOrQuit(MaskForBitSize(1) == 0x01); + VerifyOrQuit(MaskForBitSize(2) == 0x03); + VerifyOrQuit(MaskForBitSize(3) == 0x07); + VerifyOrQuit(MaskForBitSize(4) == 0x0f); + VerifyOrQuit(MaskForBitSize(7) == 0x7f); + VerifyOrQuit(MaskForBitSize(8) == 0xff); + VerifyOrQuit(MaskForBitSize(9) == 0xff); + + VerifyOrQuit(MaskForBitSize(0) == 0); + VerifyOrQuit(MaskForBitSize(7) == 0x007f); + VerifyOrQuit(MaskForBitSize(8) == 0x00ff); + VerifyOrQuit(MaskForBitSize(11) == 0x07ff); + VerifyOrQuit(MaskForBitSize(16) == 0xffff); + VerifyOrQuit(MaskForBitSize(20) == 0xffff); + + VerifyOrQuit(MaskForBitSize(0) == 0); + VerifyOrQuit(MaskForBitSize(16) == 0x0000ffff); + VerifyOrQuit(MaskForBitSize(31) == 0x7fffffff); + VerifyOrQuit(MaskForBitSize(32) == 0xffffffff); + VerifyOrQuit(MaskForBitSize(33) == 0xffffffff); + + VerifyOrQuit(MaskForBitSize(0) == 0); + VerifyOrQuit(MaskForBitSize(32) == 0xffffffffULL); + VerifyOrQuit(MaskForBitSize(63) == 0x7fffffffffffffffULL); + VerifyOrQuit(MaskForBitSize(64) == 0xffffffffffffffffULL); + VerifyOrQuit(MaskForBitSize(65) == 0xffffffffffffffffULL); + + // Compile-time checks + + static_assert(MaskForBitSize(0) == 0, "MaskForBitSize(0) failed"); + static_assert(MaskForBitSize(1) == 0x01, "MaskForBitSize(1) failed"); + static_assert(MaskForBitSize(2) == 0x03, "MaskForBitSize(2) failed"); + static_assert(MaskForBitSize(3) == 0x07, "MaskForBitSize(3) failed"); + static_assert(MaskForBitSize(4) == 0x0f, "MaskForBitSize(4) failed"); + static_assert(MaskForBitSize(7) == 0x7f, "MaskForBitSize(7) failed"); + static_assert(MaskForBitSize(8) == 0xff, "MaskForBitSize(8) failed"); + static_assert(MaskForBitSize(9) == 0xff, "MaskForBitSize(9) failed"); + + static_assert(MaskForBitSize(0) == 0, "MaskForBitSize(0) failed"); + static_assert(MaskForBitSize(7) == 0x007f, "MaskForBitSize(7) failed"); + static_assert(MaskForBitSize(8) == 0x00ff, "MaskForBitSize(8) failed"); + static_assert(MaskForBitSize(11) == 0x07ff, "MaskForBitSize(11) failed"); + static_assert(MaskForBitSize(16) == 0xffff, "MaskForBitSize(16) failed"); + static_assert(MaskForBitSize(20) == 0xffff, "MaskForBitSize(20) failed"); + + static_assert(MaskForBitSize(0) == 0, "MaskForBitSize(0) failed"); + static_assert(MaskForBitSize(16) == 0x0000ffff, "MaskForBitSize(16) failed"); + static_assert(MaskForBitSize(31) == 0x7fffffff, "MaskForBitSize(31) failed"); + static_assert(MaskForBitSize(32) == 0xffffffff, "MaskForBitSize(32) failed"); + static_assert(MaskForBitSize(33) == 0xffffffff, "MaskForBitSize(33) failed"); + + static_assert(MaskForBitSize(0) == 0, "MaskForBitSize(0) failed"); + static_assert(MaskForBitSize(32) == 0xffffffffULL, "MaskForBitSize(32) failed"); + static_assert(MaskForBitSize(63) == 0x7fffffffffffffffULL, "MaskForBitSize(63) failed"); + static_assert(MaskForBitSize(64) == 0xffffffffffffffffULL, "MaskForBitSize(64) failed"); + static_assert(MaskForBitSize(65) == 0xffffffffffffffffULL, "MaskForBitSize(65) failed"); + + printf("TestMaskForBitSize() passed\n"); +} + } // namespace ot int main(void) @@ -184,6 +247,7 @@ int main(void) ot::TestCountMatchingBitsAllCombinations(); ot::TestCountMatchingBitsExamples(); ot::TestDetermineMinBitSize(); + ot::TestMaskForBitSize(); printf("All tests passed\n"); return 0;