From 0a6f0dfb74d2a5ebacd20c13452f3eb3af994417 Mon Sep 17 00:00:00 2001 From: Lioncash Date: Fri, 13 Jan 2017 17:18:10 -0500 Subject: [PATCH] Common: Add bit utility header This attempts to make some bit arithmetic more self-documenting and also make it easier during review to identify potential off-by-one errors by making it possible to just specify which bits are being extracted. Functions both support the case where bits being extracted can vary and fixed bit extraction. In the case the bits are fixed, compile-time asserts are present to prevent accidental API usage at compile-time. e.g. Instead of shifting and masking to get bits 10 to 15, Common::ExtractBits<10, 15>(value) can just be done instead. --- Source/Core/Common/BitUtils.h | 102 ++++++++++++++++++++++ Source/Core/Common/Common.vcxproj | 1 + Source/Core/Common/Common.vcxproj.filters | 1 + Source/UnitTests/Common/BitUtilsTest.cpp | 59 +++++++++++++ Source/UnitTests/Common/CMakeLists.txt | 1 + 5 files changed, 164 insertions(+) create mode 100644 Source/Core/Common/BitUtils.h create mode 100644 Source/UnitTests/Common/BitUtilsTest.cpp diff --git a/Source/Core/Common/BitUtils.h b/Source/Core/Common/BitUtils.h new file mode 100644 index 0000000000..eacf6bf099 --- /dev/null +++ b/Source/Core/Common/BitUtils.h @@ -0,0 +1,102 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#pragma once + +#include +#include +#include + +namespace Common +{ +/// +/// Retrieves the size of a type in bits. +/// +/// @tparam T Type to get the size of. +/// +/// @return the size of the type in bits. +/// +template +constexpr size_t BitSize() noexcept +{ + return sizeof(T) * CHAR_BIT; +} + +/// +/// Extracts a bit from a value. +/// +/// @param src The value to extract a bit from. +/// @param bit The bit to extract. +/// +/// @tparam T The type of the value. +/// +/// @return The extracted bit. +/// +template +constexpr T ExtractBit(const T src, const size_t bit) noexcept +{ + return (src >> bit) & static_cast(1); +} + +/// +/// Extracts a bit from a value. +/// +/// @param src The value to extract a bit from. +/// +/// @tparam bit The bit to extract. +/// @tparam T The type of the value. +/// +/// @return The extracted bit. +/// +template +constexpr T ExtractBit(const T src) noexcept +{ + static_assert(bit < BitSize(), "Specified bit must be within T's bit width."); + + return ExtractBit(src, bit); +} + +/// +/// Extracts a range of bits from a value. +/// +/// @param src The value to extract the bits from. +/// @param begin The beginning of the bit range. This is inclusive. +/// @param end The ending of the bit range. This is inclusive. +/// +/// @tparam T The type of the value. +/// @tparam Result The returned result type. This is the unsigned analog +/// of a signed type if a signed type is passed as T. +/// +/// @return The extracted bits. +/// +template > +constexpr Result ExtractBits(const T src, const size_t begin, const size_t end) noexcept +{ + return static_cast(((static_cast(src) << ((BitSize() - 1) - end)) >> + (BitSize() - end + begin - 1))); +} + +/// +/// Extracts a range of bits from a value. +/// +/// @param src The value to extract the bits from. +/// +/// @tparam begin The beginning of the bit range. This is inclusive. +/// @tparam end The ending of the bit range. This is inclusive. +/// @tparam T The type of the value. +/// @tparam Result The returned result type. This is the unsigned analog +/// of a signed type if a signed type is passed as T. +/// +/// @return The extracted bits. +/// +template > +constexpr Result ExtractBits(const T src) noexcept +{ + static_assert(begin < end, "Beginning bit must be less than the ending bit."); + static_assert(begin < BitSize(), "Beginning bit is larger than T's bit width."); + static_assert(end < BitSize(), "Ending bit is larger than T's bit width."); + + return ExtractBits(src, begin, end); +} +} // namespace Common diff --git a/Source/Core/Common/Common.vcxproj b/Source/Core/Common/Common.vcxproj index 1dfcf692cb..52f408e776 100644 --- a/Source/Core/Common/Common.vcxproj +++ b/Source/Core/Common/Common.vcxproj @@ -43,6 +43,7 @@ + diff --git a/Source/Core/Common/Common.vcxproj.filters b/Source/Core/Common/Common.vcxproj.filters index 25a8bf3fd4..7731482020 100644 --- a/Source/Core/Common/Common.vcxproj.filters +++ b/Source/Core/Common/Common.vcxproj.filters @@ -24,6 +24,7 @@ + diff --git a/Source/UnitTests/Common/BitUtilsTest.cpp b/Source/UnitTests/Common/BitUtilsTest.cpp new file mode 100644 index 0000000000..f607f0da2f --- /dev/null +++ b/Source/UnitTests/Common/BitUtilsTest.cpp @@ -0,0 +1,59 @@ +// Copyright 2017 Dolphin Emulator Project +// Licensed under GPLv2+ +// Refer to the license.txt file included. + +#include + +#include "Common/BitUtils.h" +#include "Common/CommonTypes.h" + +TEST(BitUtils, BitSize) +{ + EXPECT_EQ(Common::BitSize(), 8); + EXPECT_EQ(Common::BitSize(), 16); + EXPECT_EQ(Common::BitSize(), 32); + EXPECT_EQ(Common::BitSize(), 64); + + EXPECT_EQ(Common::BitSize(), 8); + EXPECT_EQ(Common::BitSize(), 16); + EXPECT_EQ(Common::BitSize(), 32); + EXPECT_EQ(Common::BitSize(), 64); +} + +TEST(BitUtils, ExtractBit) +{ + constexpr s32 zero = 0; + EXPECT_EQ(Common::ExtractBit<0>(zero), 0); + + constexpr s32 one = 1; + EXPECT_EQ(Common::ExtractBit<0>(one), 1); + + constexpr s32 negative_one = -1; + EXPECT_EQ(Common::ExtractBit<31>(negative_one), 1); + + constexpr s32 one_hundred_twenty_eight = 0b10000000; + EXPECT_EQ(Common::ExtractBit<7>(one_hundred_twenty_eight), 1); +} + +TEST(BitUtils, ExtractBits) +{ + // Note: Parenthesizing is necessary to prevent the macros from + // mangling the template function usages. + + constexpr s32 two_hundred_four_signed = 0b0011001100; + EXPECT_EQ((Common::ExtractBits<2, 3>(two_hundred_four_signed)), 3); + EXPECT_EQ((Common::ExtractBits<2, 7>(two_hundred_four_signed)), 51); + EXPECT_EQ((Common::ExtractBits<3, 6>(two_hundred_four_signed)), 9); + + constexpr u32 two_hundred_four_unsigned = 0b0011001100; + EXPECT_EQ((Common::ExtractBits<2, 3>(two_hundred_four_unsigned)), 3); + EXPECT_EQ((Common::ExtractBits<2, 7>(two_hundred_four_unsigned)), 51); + EXPECT_EQ((Common::ExtractBits<3, 6>(two_hundred_four_unsigned)), 9); + + // Ensure bit extraction remains sign-independent even when signed types are used. + constexpr s32 negative_one = -1; + EXPECT_EQ((Common::ExtractBits<0, 31>(negative_one)), 0xFFFFFFFFU); + + // Ensure bit extraction with type overriding works as expected + EXPECT_EQ((Common::ExtractBits<0, 31, s32, s32>(negative_one)), -1); +} diff --git a/Source/UnitTests/Common/CMakeLists.txt b/Source/UnitTests/Common/CMakeLists.txt index 28ca1237b3..8a88efe60c 100644 --- a/Source/UnitTests/Common/CMakeLists.txt +++ b/Source/UnitTests/Common/CMakeLists.txt @@ -1,5 +1,6 @@ add_dolphin_test(BitFieldTest BitFieldTest.cpp) add_dolphin_test(BitSetTest BitSetTest.cpp) +add_dolphin_test(BitUtilsTest BitUtilsTest.cpp) add_dolphin_test(BlockingLoopTest BlockingLoopTest.cpp) add_dolphin_test(BusyLoopTest BusyLoopTest.cpp) add_dolphin_test(CommonFuncsTest CommonFuncsTest.cpp)