summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNiels Boehm <blubberdiblub@gmail.com>2017-06-22 12:36:54 +0200
committerNiels Boehm <blubberdiblub@gmail.com>2017-06-22 20:22:53 +0200
commitdf82adca43db7f5820a27624ae3e2b60c9085efd (patch)
tree342c33b23eb2ec4f3acb4d0bb0f3f679a542277e
parente30484e8a7895a2a26f7e4e609c44c5b0d1c6331 (diff)
Add function testing whether a bitmask is valid.
This one verifies bitmasks where low bits are set to 1 (hence the name). Any stray 0 among the lower ones or any stray 1 among the higher zeros renders the mask invalid. The edge cases of all zeros and all ones are considered valid masks. It uses an efficient implementation. It's the counterpart of https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
-rw-r--r--Source/Core/Common/BitUtils.h22
-rw-r--r--Source/UnitTests/Common/BitUtilsTest.cpp32
2 files changed, 54 insertions, 0 deletions
diff --git a/Source/Core/Common/BitUtils.h b/Source/Core/Common/BitUtils.h
index eacf6bf099..8d5398fb39 100644
--- a/Source/Core/Common/BitUtils.h
+++ b/Source/Core/Common/BitUtils.h
@@ -99,4 +99,26 @@ constexpr Result ExtractBits(const T src) noexcept
return ExtractBits<T, Result>(src, begin, end);
}
+
+///
+/// Verifies whether the supplied value is a valid bit mask of the form 0b00...0011...11.
+/// Both edge cases of all zeros and all ones are considered valid masks, too.
+///
+/// @param mask The mask value to test for validity.
+///
+/// @tparam T The type of the value.
+///
+/// @return A bool indicating whether the mask is valid.
+///
+template <typename T>
+constexpr bool IsValidLowMask(const T mask) noexcept
+{
+ static_assert(std::is_integral<T>::value, "Mask must be an integral type.");
+ static_assert(std::is_unsigned<T>::value, "Signed masks can introduce hard to find bugs.");
+
+ // Can be efficiently determined without looping or bit counting. It's the counterpart
+ // to https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2
+ // and doesn't require special casing either edge case.
+ return (mask & (mask + 1)) == 0;
+}
} // namespace Common
diff --git a/Source/UnitTests/Common/BitUtilsTest.cpp b/Source/UnitTests/Common/BitUtilsTest.cpp
index 987868e6e4..75f7adf50e 100644
--- a/Source/UnitTests/Common/BitUtilsTest.cpp
+++ b/Source/UnitTests/Common/BitUtilsTest.cpp
@@ -57,3 +57,35 @@ TEST(BitUtils, ExtractBits)
// Ensure bit extraction with type overriding works as expected
EXPECT_EQ((Common::ExtractBits<0, 31, s32, s32>(negative_one)), -1);
}
+
+TEST(BitUtils, IsValidLowMask)
+{
+ EXPECT_TRUE(Common::IsValidLowMask(0b0u));
+ EXPECT_TRUE(Common::IsValidLowMask(0b1u));
+ EXPECT_FALSE(Common::IsValidLowMask(0b10u));
+ EXPECT_TRUE(Common::IsValidLowMask(0b11u));
+ EXPECT_FALSE(Common::IsValidLowMask(0b1110u));
+ EXPECT_TRUE(Common::IsValidLowMask(0b1111u));
+ EXPECT_FALSE(Common::IsValidLowMask(0b10000u));
+ EXPECT_FALSE(Common::IsValidLowMask(0b101111u));
+
+ EXPECT_TRUE(Common::IsValidLowMask((u8)~0b0));
+ EXPECT_FALSE(Common::IsValidLowMask((u8)(~0b0 - 1)));
+ EXPECT_FALSE(Common::IsValidLowMask((u8)~(0b10000)));
+ EXPECT_FALSE(Common::IsValidLowMask((u8)(~((u8)(~0b0) >> 1) | 0b1111)));
+
+ EXPECT_TRUE(Common::IsValidLowMask((u16)~0b0));
+ EXPECT_FALSE(Common::IsValidLowMask((u16)(~0b0 - 1)));
+ EXPECT_FALSE(Common::IsValidLowMask((u16)~(0b10000)));
+ EXPECT_FALSE(Common::IsValidLowMask((u16)(~((u16)(~0b0) >> 1) | 0b1111)));
+
+ EXPECT_TRUE(Common::IsValidLowMask((u32)~0b0));
+ EXPECT_FALSE(Common::IsValidLowMask((u32)(~0b0 - 1)));
+ EXPECT_FALSE(Common::IsValidLowMask((u32)~(0b10000)));
+ EXPECT_FALSE(Common::IsValidLowMask((u32)(~((u32)(~0b0) >> 1) | 0b1111)));
+
+ EXPECT_TRUE(Common::IsValidLowMask((u64)~0b0));
+ EXPECT_FALSE(Common::IsValidLowMask((u64)(~0b0 - 1)));
+ EXPECT_FALSE(Common::IsValidLowMask((u64)~(0b10000)));
+ EXPECT_FALSE(Common::IsValidLowMask((u64)(~((u64)(~0b0) >> 1) | 0b1111)));
+}