mirror of
https://github.com/dolphin-emu/dolphin
synced 2024-11-04 20:43:44 -05:00
6ffd71ffae
`std::bit_cast` participates in overload resolution only if `sizeof(To) == sizeof(From)` and both `To` and `From` are *TriviallyCopyable* types, so the static assertions here can be removed. `[[nodiscard]]` was added as well.
244 lines
6.8 KiB
C++
244 lines
6.8 KiB
C++
// Copyright 2017 Dolphin Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#pragma once
|
|
|
|
#include <array>
|
|
#include <bit>
|
|
#include <climits>
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <cstring>
|
|
#include <initializer_list>
|
|
#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);
|
|
}
|
|
|
|
///
|
|
/// 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;
|
|
}
|
|
|
|
template <typename T, typename PtrType>
|
|
class BitCastPtrType
|
|
{
|
|
public:
|
|
static_assert(std::is_trivially_copyable<PtrType>(),
|
|
"BitCastPtr source type must be trivially copyable.");
|
|
static_assert(std::is_trivially_copyable<T>(),
|
|
"BitCastPtr destination type must be trivially copyable.");
|
|
|
|
explicit BitCastPtrType(PtrType* ptr) : m_ptr(ptr) {}
|
|
|
|
// Enable operator= only for pointers to non-const data
|
|
template <typename S>
|
|
inline typename std::enable_if<std::is_same<S, T>() && !std::is_const<PtrType>()>::type
|
|
operator=(const S& source)
|
|
{
|
|
std::memcpy(m_ptr, &source, sizeof(source));
|
|
}
|
|
|
|
inline operator T() const
|
|
{
|
|
T result;
|
|
std::memcpy(&result, m_ptr, sizeof(result));
|
|
return result;
|
|
}
|
|
|
|
private:
|
|
PtrType* m_ptr;
|
|
};
|
|
|
|
// Provides an aliasing-safe alternative to reinterpret_cast'ing pointers to structs
|
|
// Conversion constructor and operator= provided for a convenient syntax.
|
|
// Usage: MyStruct s = BitCastPtr<MyStruct>(some_ptr);
|
|
// BitCastPtr<MyStruct>(some_ptr) = s;
|
|
template <typename T, typename PtrType>
|
|
inline auto BitCastPtr(PtrType* ptr) noexcept -> BitCastPtrType<T, PtrType>
|
|
{
|
|
return BitCastPtrType<T, PtrType>{ptr};
|
|
}
|
|
|
|
// Similar to BitCastPtr, but specifically for aliasing structs to arrays.
|
|
template <typename ValueType, typename From>
|
|
[[nodiscard]] constexpr auto BitCastToArray(const From& obj) noexcept
|
|
{
|
|
return std::bit_cast<std::array<ValueType, sizeof(From) / sizeof(ValueType)>>(obj);
|
|
}
|
|
|
|
template <typename T>
|
|
void SetBit(T& value, size_t bit_number, bool bit_value)
|
|
{
|
|
static_assert(std::is_unsigned<T>(), "SetBit is only sane on unsigned types.");
|
|
|
|
if (bit_value)
|
|
value |= (T{1} << bit_number);
|
|
else
|
|
value &= ~(T{1} << bit_number);
|
|
}
|
|
|
|
template <size_t bit_number, typename T>
|
|
void SetBit(T& value, bool bit_value)
|
|
{
|
|
SetBit(value, bit_number, bit_value);
|
|
}
|
|
|
|
template <typename T>
|
|
class FlagBit
|
|
{
|
|
public:
|
|
FlagBit(std::underlying_type_t<T>& bits, T bit) : m_bits(bits), m_bit(bit) {}
|
|
explicit operator bool() const
|
|
{
|
|
return (m_bits & static_cast<std::underlying_type_t<T>>(m_bit)) != 0;
|
|
}
|
|
FlagBit& operator=(const bool rhs)
|
|
{
|
|
if (rhs)
|
|
m_bits |= static_cast<std::underlying_type_t<T>>(m_bit);
|
|
else
|
|
m_bits &= ~static_cast<std::underlying_type_t<T>>(m_bit);
|
|
return *this;
|
|
}
|
|
|
|
private:
|
|
std::underlying_type_t<T>& m_bits;
|
|
T m_bit;
|
|
};
|
|
|
|
template <typename T>
|
|
class Flags
|
|
{
|
|
public:
|
|
constexpr Flags() = default;
|
|
constexpr Flags(std::initializer_list<T> bits)
|
|
{
|
|
for (auto bit : bits)
|
|
{
|
|
m_hex |= static_cast<std::underlying_type_t<T>>(bit);
|
|
}
|
|
}
|
|
FlagBit<T> operator[](T bit) { return FlagBit(m_hex, bit); }
|
|
|
|
std::underlying_type_t<T> m_hex = 0;
|
|
};
|
|
|
|
// Left-shift a value and set new LSBs to that of the supplied LSB.
|
|
// Converts a value from a N-bit range to an (N+X)-bit range. e.g. 0x101 -> 0x10111
|
|
template <typename T>
|
|
T ExpandValue(T value, size_t left_shift_amount)
|
|
{
|
|
static_assert(std::is_unsigned<T>(), "ExpandValue is only sane on unsigned types.");
|
|
|
|
return (value << left_shift_amount) |
|
|
(T(-ExtractBit<0>(value)) >> (BitSize<T>() - left_shift_amount));
|
|
}
|
|
} // namespace Common
|