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)