mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2024-11-14 13:27:45 -07:00
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.
This commit is contained in:
parent
30e57cecf7
commit
0a6f0dfb74
102
Source/Core/Common/BitUtils.h
Normal file
102
Source/Core/Common/BitUtils.h
Normal file
@ -0,0 +1,102 @@
|
||||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <climits>
|
||||
#include <cstddef>
|
||||
#include <type_traits>
|
||||
|
||||
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 <typename T>
|
||||
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 <typename T>
|
||||
constexpr T ExtractBit(const T src, const size_t bit) noexcept
|
||||
{
|
||||
return (src >> bit) & static_cast<T>(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 <size_t bit, typename T>
|
||||
constexpr T ExtractBit(const T src) noexcept
|
||||
{
|
||||
static_assert(bit < BitSize<T>(), "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 <typename T, typename Result = std::make_unsigned_t<T>>
|
||||
constexpr Result ExtractBits(const T src, const size_t begin, const size_t end) noexcept
|
||||
{
|
||||
return static_cast<Result>(((static_cast<Result>(src) << ((BitSize<T>() - 1) - end)) >>
|
||||
(BitSize<T>() - 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 <size_t begin, size_t end, typename T, typename Result = std::make_unsigned_t<T>>
|
||||
constexpr Result ExtractBits(const T src) noexcept
|
||||
{
|
||||
static_assert(begin < end, "Beginning bit must be less than the ending bit.");
|
||||
static_assert(begin < BitSize<T>(), "Beginning bit is larger than T's bit width.");
|
||||
static_assert(end < BitSize<T>(), "Ending bit is larger than T's bit width.");
|
||||
|
||||
return ExtractBits<T, Result>(src, begin, end);
|
||||
}
|
||||
} // namespace Common
|
@ -43,6 +43,7 @@
|
||||
<ClInclude Include="Atomic_Win32.h" />
|
||||
<ClInclude Include="BitField.h" />
|
||||
<ClInclude Include="BitSet.h" />
|
||||
<ClInclude Include="BitUtils.h" />
|
||||
<ClInclude Include="BlockingLoop.h" />
|
||||
<ClInclude Include="CDUtils.h" />
|
||||
<ClInclude Include="ChunkFile.h" />
|
||||
|
@ -24,6 +24,7 @@
|
||||
<ClInclude Include="Atomic_Win32.h" />
|
||||
<ClInclude Include="BitField.h" />
|
||||
<ClInclude Include="BitSet.h" />
|
||||
<ClInclude Include="BitUtils.h" />
|
||||
<ClInclude Include="BlockingLoop.h" />
|
||||
<ClInclude Include="CDUtils.h" />
|
||||
<ClInclude Include="ChunkFile.h" />
|
||||
|
59
Source/UnitTests/Common/BitUtilsTest.cpp
Normal file
59
Source/UnitTests/Common/BitUtilsTest.cpp
Normal file
@ -0,0 +1,59 @@
|
||||
// Copyright 2017 Dolphin Emulator Project
|
||||
// Licensed under GPLv2+
|
||||
// Refer to the license.txt file included.
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "Common/BitUtils.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
|
||||
TEST(BitUtils, BitSize)
|
||||
{
|
||||
EXPECT_EQ(Common::BitSize<s8>(), 8);
|
||||
EXPECT_EQ(Common::BitSize<s16>(), 16);
|
||||
EXPECT_EQ(Common::BitSize<s32>(), 32);
|
||||
EXPECT_EQ(Common::BitSize<s64>(), 64);
|
||||
|
||||
EXPECT_EQ(Common::BitSize<u8>(), 8);
|
||||
EXPECT_EQ(Common::BitSize<u16>(), 16);
|
||||
EXPECT_EQ(Common::BitSize<u32>(), 32);
|
||||
EXPECT_EQ(Common::BitSize<u64>(), 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);
|
||||
}
|
@ -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)
|
||||
|
Loading…
Reference in New Issue
Block a user