// Copyright 2017 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #pragma once #include #include #include #include #include #include #include #ifdef _MSC_VER #include #endif 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); } /// /// Rotates a value left (ROL). /// /// @param value The value to rotate. /// @param amount The number of bits to rotate the value. /// @tparam T An unsigned type. /// /// @return The rotated value. /// template constexpr T RotateLeft(const T value, size_t amount) noexcept { static_assert(std::is_unsigned(), "Can only rotate unsigned types left."); amount %= BitSize(); if (amount == 0) return value; return static_cast((value << amount) | (value >> (BitSize() - amount))); } /// /// Rotates a value right (ROR). /// /// @param value The value to rotate. /// @param amount The number of bits to rotate the value. /// @tparam T An unsigned type. /// /// @return The rotated value. /// template constexpr T RotateRight(const T value, size_t amount) noexcept { static_assert(std::is_unsigned(), "Can only rotate unsigned types right."); amount %= BitSize(); if (amount == 0) return value; return static_cast((value >> amount) | (value << (BitSize() - amount))); } /// /// 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 constexpr bool IsValidLowMask(const T mask) noexcept { static_assert(std::is_integral::value, "Mask must be an integral type."); static_assert(std::is_unsigned::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; } /// /// Reinterpret objects of one type as another by bit-casting between object representations. /// /// @remark This is the example implementation of std::bit_cast which is to be included /// in C++2a. See http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0476r2.html /// for more details. The only difference is this variant is not constexpr, /// as the mechanism for bit_cast requires a compiler built-in to have that quality. /// /// @param source The source object to convert to another representation. /// /// @tparam To The type to reinterpret source as. /// @tparam From The initial type representation of source. /// /// @return The representation of type From as type To. /// /// @pre Both To and From types must be the same size /// @pre Both To and From types must satisfy the TriviallyCopyable concept. /// template inline To BitCast(const From& source) noexcept { static_assert(sizeof(From) == sizeof(To), "BitCast source and destination types must be equal in size."); static_assert(std::is_trivially_copyable(), "BitCast source type must be trivially copyable."); static_assert(std::is_trivially_copyable(), "BitCast destination type must be trivially copyable."); alignas(To) std::byte storage[sizeof(To)]; std::memcpy(&storage, &source, sizeof(storage)); return reinterpret_cast(storage); } template class BitCastPtrType { public: static_assert(std::is_trivially_copyable(), "BitCastPtr source type must be trivially copyable."); static_assert(std::is_trivially_copyable(), "BitCastPtr destination type must be trivially copyable."); explicit BitCastPtrType(PtrType* ptr) : m_ptr(ptr) {} // Enable operator= only for pointers to non-const data template inline typename std::enable_if() && !std::is_const()>::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(some_ptr); // BitCastPtr(some_ptr) = s; template inline auto BitCastPtr(PtrType* ptr) noexcept -> BitCastPtrType { return BitCastPtrType{ptr}; } // Similar to BitCastPtr, but specifically for aliasing structs to arrays. template > inline auto BitCastToArray(const T& obj) noexcept -> Container { static_assert(sizeof(T) % sizeof(ArrayType) == 0, "Size of array type must be a factor of size of source type."); static_assert(std::is_trivially_copyable(), "BitCastToArray source type must be trivially copyable."); static_assert(std::is_trivially_copyable(), "BitCastToArray array type must be trivially copyable."); Container result; std::memcpy(result.data(), &obj, sizeof(T)); return result; } template > inline void BitCastFromArray(const Container& array, T& obj) noexcept { static_assert(sizeof(T) % sizeof(ArrayType) == 0, "Size of array type must be a factor of size of destination type."); static_assert(std::is_trivially_copyable(), "BitCastFromArray array type must be trivially copyable."); static_assert(std::is_trivially_copyable(), "BitCastFromArray destination type must be trivially copyable."); std::memcpy(&obj, array.data(), sizeof(T)); } template > inline auto BitCastFromArray(const Container& array) noexcept -> T { static_assert(sizeof(T) % sizeof(ArrayType) == 0, "Size of array type must be a factor of size of destination type."); static_assert(std::is_trivially_copyable(), "BitCastFromArray array type must be trivially copyable."); static_assert(std::is_trivially_copyable(), "BitCastFromArray destination type must be trivially copyable."); T obj; std::memcpy(&obj, array.data(), sizeof(T)); return obj; } template void SetBit(T& value, size_t bit_number, bool bit_value) { static_assert(std::is_unsigned(), "SetBit is only sane on unsigned types."); if (bit_value) value |= (T{1} << bit_number); else value &= ~(T{1} << bit_number); } template void SetBit(T& value, bool bit_value) { SetBit(value, bit_number, bit_value); } template class FlagBit { public: FlagBit(std::underlying_type_t& bits, T bit) : m_bits(bits), m_bit(bit) {} explicit operator bool() const { return (m_bits & static_cast>(m_bit)) != 0; } FlagBit& operator=(const bool rhs) { if (rhs) m_bits |= static_cast>(m_bit); else m_bits &= ~static_cast>(m_bit); return *this; } private: std::underlying_type_t& m_bits; T m_bit; }; template class Flags { public: constexpr Flags() = default; constexpr Flags(std::initializer_list bits) { for (auto bit : bits) { m_hex |= static_cast>(bit); } } FlagBit operator[](T bit) { return FlagBit(m_hex, bit); } std::underlying_type_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 T ExpandValue(T value, size_t left_shift_amount) { static_assert(std::is_unsigned(), "ExpandValue is only sane on unsigned types."); return (value << left_shift_amount) | (T(-ExtractBit<0>(value)) >> (BitSize() - left_shift_amount)); } template constexpr int CountLeadingZerosConst(T value) { int result = sizeof(T) * 8; while (value) { result--; value >>= 1; } return result; } constexpr int CountLeadingZeros(uint64_t value) { #if defined(__GNUC__) return value ? __builtin_clzll(value) : 64; #elif defined(_MSC_VER) if (std::is_constant_evaluated()) { return CountLeadingZerosConst(value); } else { unsigned long index = 0; return _BitScanReverse64(&index, value) ? 63 - index : 64; } #else return CountLeadingZerosConst(value); #endif } constexpr int CountLeadingZeros(uint32_t value) { #if defined(__GNUC__) return value ? __builtin_clz(value) : 32; #elif defined(_MSC_VER) if (std::is_constant_evaluated()) { return CountLeadingZerosConst(value); } else { unsigned long index = 0; return _BitScanReverse(&index, value) ? 31 - index : 32; } #else return CountLeadingZerosConst(value); #endif } template constexpr int CountTrailingZerosConst(T value) { int result = sizeof(T) * 8; while (value) { result--; value <<= 1; } return result; } constexpr int CountTrailingZeros(uint64_t value) { #if defined(__GNUC__) return value ? __builtin_ctzll(value) : 64; #elif defined(_MSC_VER) if (std::is_constant_evaluated()) { return CountTrailingZerosConst(value); } else { unsigned long index = 0; return _BitScanForward64(&index, value) ? index : 64; } #else return CountTrailingZerosConst(value); #endif } constexpr int CountTrailingZeros(uint32_t value) { #if defined(__GNUC__) return value ? __builtin_ctz(value) : 32; #elif defined(_MSC_VER) if (std::is_constant_evaluated()) { return CountTrailingZerosConst(value); } else { unsigned long index = 0; return _BitScanForward(&index, value) ? index : 32; } #else return CountTrailingZerosConst(value); #endif } } // namespace Common