diff options
| author | lizzie <lizzie@eden-emu.dev> | 2026-07-09 03:47:04 +0200 |
|---|---|---|
| committer | crueter <crueter@eden-emu.dev> | 2026-07-09 03:47:04 +0200 |
| commit | 41762940d6bd6350901f12662248b95b91b51e6c (patch) | |
| tree | 9aa1c2290aaae63076d7662caba7af725c6062a4 /src/common | |
| parent | c2e74a93804f0ba4e7e87a95e8881bbe817aa0a4 (diff) | |
[common] rework BitField and BitUtil, use concepts whenever possible (#4076)
test no regressions especially on Liiinux
Signed-off-by: lizzie <lizzie@eden-emu.dev>
Reviewed-on: https://git.eden-emu.dev/eden-emu/eden/pulls/4076
Reviewed-by: CamilleLaVey <camillelavey99@gmail.com>
Reviewed-by: MaranBr <maranbr@eden-emu.dev>
Diffstat (limited to 'src/common')
| -rw-r--r-- | src/common/bit_field.h | 58 | ||||
| -rw-r--r-- | src/common/bit_util.h | 47 | ||||
| -rw-r--r-- | src/common/common_funcs.h | 72 | ||||
| -rw-r--r-- | src/common/fs/file.h | 7 | ||||
| -rw-r--r-- | src/common/ring_buffer.h | 6 | ||||
| -rw-r--r-- | src/common/typed_address.h | 13 | ||||
| -rw-r--r-- | src/common/x64/xbyak.h | 2 |
7 files changed, 86 insertions, 119 deletions
diff --git a/src/common/bit_field.h b/src/common/bit_field.h index 0168ff9cbf..fbc71590d5 100644 --- a/src/common/bit_field.h +++ b/src/common/bit_field.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: 2014 Tony Wasserka // SPDX-FileCopyrightText: 2014 Dolphin Emulator Project // SPDX-License-Identifier: BSD-3-Clause AND GPL-2.0-or-later @@ -7,6 +10,7 @@ #include <cstddef> #include <limits> #include <type_traits> +#include <climits> #include "common/swap.h" /* @@ -83,25 +87,22 @@ */ #pragma pack(1) template <std::size_t Position, std::size_t Bits, typename T, typename EndianTag = LETag> + requires std::is_trivially_copyable_v<T> struct BitField { private: // UnderlyingType is T for non-enum types and the underlying type of T if // T is an enumeration. Note that T is wrapped within an enable_if in the // former case to workaround compile errors which arise when using // std::underlying_type<T>::type directly. - using UnderlyingType = typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>, - std::enable_if<true, T>>::type; - + using UnderlyingType = typename std::conditional_t<std::is_enum_v<T>, std::underlying_type<T>, std::enable_if<true, T>>::type; // We store the value as the unsigned type to avoid undefined behaviour on value shifting using StorageType = std::make_unsigned_t<UnderlyingType>; - using StorageTypeWithEndian = typename AddEndian<StorageType, EndianTag>::type; - public: /// Constants to allow limited introspection of fields if needed static constexpr std::size_t position = Position; static constexpr std::size_t bits = Bits; - static constexpr StorageType mask = (((StorageType)~0) >> (8 * sizeof(T) - bits)) << position; + static constexpr StorageType mask = (StorageType(~0) >> (CHAR_BIT * sizeof(T) - bits)) << position; /** * Formats a value by masking and shifting it according to the field parameters. A value @@ -109,21 +110,18 @@ public: * the results together. */ [[nodiscard]] static constexpr StorageType FormatValue(const T& value) { - return (static_cast<StorageType>(value) << position) & mask; + return (StorageType(value) << position) & mask; } - /** - * Extracts a value from the passed storage. In most situations prefer use the member functions - * (such as Value() or operator T), but this can be used to extract a value from a bitfield - * union in a constexpr context. - */ + /// @brief Extracts a value from the passed storage. In most situations prefer use the member functions + /// (such as Value() or operator T), but this can be used to extract a value from a bitfield + /// union in a constexpr context. [[nodiscard]] static constexpr T ExtractValue(const StorageType& storage) { if constexpr (std::numeric_limits<UnderlyingType>::is_signed) { - std::size_t shift = 8 * sizeof(T) - bits; - return static_cast<T>(static_cast<UnderlyingType>(storage << (shift - position)) >> - shift); + std::size_t shift = CHAR_BIT * sizeof(T) - bits; + return T(UnderlyingType(storage << (shift - position)) >> shift); } else { - return static_cast<T>((storage & mask) >> position); + return T((storage & mask) >> position); } } @@ -141,30 +139,14 @@ public: constexpr BitField(BitField&&) noexcept = default; constexpr BitField& operator=(BitField&&) noexcept = default; - constexpr void Assign(const T& value) { -#ifdef _MSC_VER - storage = static_cast<StorageType>((storage & ~mask) | FormatValue(value)); -#else - // Explicitly reload with memcpy to avoid compiler aliasing quirks - // regarding optimization: GCC/Clang clobber chained stores to - // different bitfields in the same struct with the last value. - StorageTypeWithEndian storage_; - std::memcpy(&storage_, &storage, sizeof(storage_)); - storage = static_cast<StorageType>((storage_ & ~mask) | FormatValue(value)); -#endif + constexpr void Assign(const T value) { + storage = StorageType((storage & ~mask) | FormatValue(value)); } [[nodiscard]] constexpr T Value() const { return ExtractValue(storage); } - template <typename ConvertedToType> - [[nodiscard]] constexpr ConvertedToType As() const { - static_assert(!std::is_same_v<T, ConvertedToType>, - "Unnecessary cast. Use Value() instead."); - return static_cast<ConvertedToType>(Value()); - } - [[nodiscard]] constexpr operator T() const { return Value(); } @@ -176,13 +158,11 @@ public: private: StorageTypeWithEndian storage; - static_assert(bits + position <= 8 * sizeof(T), "Bitfield out of range"); - + static_assert(bits + position <= CHAR_BIT * sizeof(T), "Bitfield out of range"); // And, you know, just in case people specify something stupid like bits=position=0x80000000 - static_assert(position < 8 * sizeof(T), "Invalid position"); - static_assert(bits <= 8 * sizeof(T), "Invalid number of bits"); + static_assert(position < CHAR_BIT * sizeof(T), "Invalid position"); + static_assert(bits <= CHAR_BIT * sizeof(T), "Invalid number of bits"); static_assert(bits > 0, "Invalid number of bits"); - static_assert(std::is_trivially_copyable_v<T>, "T must be trivially copyable in a BitField"); }; #pragma pack() diff --git a/src/common/bit_util.h b/src/common/bit_util.h index 13368b4392..ce916e6162 100644 --- a/src/common/bit_util.h +++ b/src/common/bit_util.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -14,46 +17,34 @@ namespace Common { /// Gets the size of a specified type T in bits. template <typename T> + requires std::is_integral_v<T> [[nodiscard]] constexpr std::size_t BitSize() { - return sizeof(T) * CHAR_BIT; -} - -[[nodiscard]] constexpr u32 MostSignificantBit32(const u32 value) { - return 31U - static_cast<u32>(std::countl_zero(value)); -} - -[[nodiscard]] constexpr u32 MostSignificantBit64(const u64 value) { - return 63U - static_cast<u32>(std::countl_zero(value)); -} - -[[nodiscard]] constexpr u32 Log2Floor32(const u32 value) { - return MostSignificantBit32(value); -} - -[[nodiscard]] constexpr u32 Log2Floor64(const u64 value) { - return MostSignificantBit64(value); + return std::size_t(sizeof(T) * CHAR_BIT); } -[[nodiscard]] constexpr u32 Log2Ceil32(const u32 value) { - const u32 log2_f = Log2Floor32(value); - return log2_f + static_cast<u32>((value ^ (1U << log2_f)) != 0U); +template<typename T> + requires std::is_integral_v<T> +[[nodiscard]] constexpr u32 MostSignificantBit(const T value) { + return u32(sizeof(T) * CHAR_BIT - 1 - std::countl_zero(value)); } -[[nodiscard]] constexpr u32 Log2Ceil64(const u64 value) { - const u64 log2_f = Log2Floor64(value); - return static_cast<u32>(log2_f + static_cast<u64>((value ^ (1ULL << log2_f)) != 0ULL)); +template<typename T> + requires std::is_integral_v<T> +[[nodiscard]] constexpr T Log2Floor(const T value) { + return T(MostSignificantBit<T>(value)); } -template <typename T> - requires std::is_unsigned_v<T> -[[nodiscard]] constexpr bool IsPow2(T value) { - return std::has_single_bit(value); +template<typename T> + requires std::is_integral_v<T> +[[nodiscard]] constexpr T Log2Ceil(const T value) { + const T log2_f = Log2Floor<T>(value); + return T(log2_f + T((value ^ (T(1ULL) << log2_f)) != T(0ULL))); } template <typename T> requires std::is_integral_v<T> [[nodiscard]] T NextPow2(T value) { - return static_cast<T>(1ULL << ((8U * sizeof(T)) - std::countl_zero(value - 1U))); + return T(1ULL << (sizeof(T) * CHAR_BIT - std::countl_zero(value - 1U))); } template <size_t bit_index, typename T> diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h index 639c2d0e0d..18e83d3381 100644 --- a/src/common/common_funcs.h +++ b/src/common/common_funcs.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2019 yuzu Emulator Project @@ -8,12 +8,21 @@ #include <array> #include <iterator> - -#if !defined(ARCHITECTURE_x86_64) +#include <utility> #include <cstdlib> // for exit -#endif #include "common/common_types.h" +#ifndef __cpp_lib_to_underlying +namespace std { +// https://en.cppreference.com/cpp/utility/to_underlying +template<class T> + requires std::is_enum_v<T> +constexpr std::underlying_type_t<T> to_underlying(T e) noexcept { + return std::underlying_type_t<T>(e); +} +} +#endif + /// Textually concatenates two tokens. The double-expansion is required by the C preprocessor. #define CONCAT2(x, y) DO_CONCAT2(x, y) #define DO_CONCAT2(x, y) x##y @@ -38,56 +47,43 @@ #define DECLARE_ENUM_FLAG_OPERATORS(type) \ [[nodiscard]] constexpr type operator|(type a, type b) noexcept { \ - using T = std::underlying_type_t<type>; \ - return static_cast<type>(static_cast<T>(a) | static_cast<T>(b)); \ + return type(std::to_underlying(a) | std::to_underlying(b)); \ } \ [[nodiscard]] constexpr type operator&(type a, type b) noexcept { \ - using T = std::underlying_type_t<type>; \ - return static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \ + return type(std::to_underlying(a) & std::to_underlying(b)); \ } \ [[nodiscard]] constexpr type operator^(type a, type b) noexcept { \ - using T = std::underlying_type_t<type>; \ - return static_cast<type>(static_cast<T>(a) ^ static_cast<T>(b)); \ + return type(std::to_underlying(a) ^ std::to_underlying(b)); \ } \ [[nodiscard]] constexpr type operator<<(type a, type b) noexcept { \ - using T = std::underlying_type_t<type>; \ - return static_cast<type>(static_cast<T>(a) << static_cast<T>(b)); \ + return type(std::to_underlying(a) << std::to_underlying(b)); \ } \ [[nodiscard]] constexpr type operator>>(type a, type b) noexcept { \ - using T = std::underlying_type_t<type>; \ - return static_cast<type>(static_cast<T>(a) >> static_cast<T>(b)); \ + return type(std::to_underlying(a) >> std::to_underlying(b)); \ } \ - constexpr type& operator|=(type& a, type b) noexcept { \ - a = a | b; \ - return a; \ + constexpr type operator|=(type& a, type b) noexcept { \ + return a = a | b; \ } \ - constexpr type& operator&=(type& a, type b) noexcept { \ - a = a & b; \ - return a; \ + constexpr type operator&=(type& a, type b) noexcept { \ + return a = a & b; \ } \ - constexpr type& operator^=(type& a, type b) noexcept { \ - a = a ^ b; \ - return a; \ + constexpr type operator^=(type& a, type b) noexcept { \ + return a = a ^ b; \ } \ - constexpr type& operator<<=(type& a, type b) noexcept { \ - a = a << b; \ - return a; \ + constexpr type operator<<=(type& a, type b) noexcept { \ + return a = a << b; \ } \ - constexpr type& operator>>=(type& a, type b) noexcept { \ - a = a >> b; \ - return a; \ + constexpr type operator>>=(type& a, type b) noexcept { \ + return a = a >> b; \ } \ [[nodiscard]] constexpr type operator~(type key) noexcept { \ - using T = std::underlying_type_t<type>; \ - return static_cast<type>(~static_cast<T>(key)); \ + return type(~std::to_underlying(key)); \ } \ [[nodiscard]] constexpr bool True(type key) noexcept { \ - using T = std::underlying_type_t<type>; \ - return static_cast<T>(key) != 0; \ + return std::to_underlying(key) != 0; \ } \ [[nodiscard]] constexpr bool False(type key) noexcept { \ - using T = std::underlying_type_t<type>; \ - return static_cast<T>(key) == 0; \ + return std::to_underlying(key) == 0; \ } #define YUZU_NON_COPYABLE(cls) \ @@ -104,10 +100,8 @@ namespace Common { return u32(a) | u32(b) << 8 | u32(c) << 16 | u32(d) << 24; } -[[nodiscard]] constexpr u64 MakeMagic(char a, char b, char c, char d, char e, char f, char g, - char h) { - return u64(a) << 0 | u64(b) << 8 | u64(c) << 16 | u64(d) << 24 | u64(e) << 32 | u64(f) << 40 | - u64(g) << 48 | u64(h) << 56; +[[nodiscard]] constexpr u64 MakeMagic(char a, char b, char c, char d, char e, char f, char g, char h) { + return u64(a) << 0 | u64(b) << 8 | u64(c) << 16 | u64(d) << 24 | u64(e) << 32 | u64(f) << 40 | u64(g) << 48 | u64(h) << 56; } } // namespace Common diff --git a/src/common/fs/file.h b/src/common/fs/file.h index 246c3db5d6..b1ccfbd18a 100644 --- a/src/common/fs/file.h +++ b/src/common/fs/file.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2021 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -277,13 +280,11 @@ public: * @returns Count of T data successfully read. */ template <typename T> + requires std::is_trivially_copyable_v<T> [[nodiscard]] size_t ReadSpan(std::span<T> data) const { - static_assert(std::is_trivially_copyable_v<T>, "Data type must be trivially copyable."); - if (!IsOpen()) { return 0; } - return std::fread(data.data(), sizeof(T), data.size(), file); } diff --git a/src/common/ring_buffer.h b/src/common/ring_buffer.h index e97854f514..10e53d3018 100644 --- a/src/common/ring_buffer.h +++ b/src/common/ring_buffer.h @@ -1,4 +1,4 @@ -// SPDX-FileCopyrightText: Copyright 2025 Eden Emulator Project +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project // SPDX-License-Identifier: GPL-3.0-or-later // SPDX-FileCopyrightText: Copyright 2018 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -23,17 +23,15 @@ namespace Common { /// @tparam T Element type /// @tparam capacity Number of slots in ring buffer template <typename T, std::size_t capacity> + requires std::is_trivial_v<T> class RingBuffer { /// A "slot" is made of a single `T`. static constexpr std::size_t slot_size = sizeof(T); - // T must be safely memcpy-able and have a trivial default constructor. - static_assert(std::is_trivial_v<T>); // Ensure capacity is sensible. static_assert(capacity < (std::numeric_limits<std::size_t>::max)() / 2); static_assert((capacity & (capacity - 1)) == 0, "capacity must be a power of two"); // Ensure lock-free. static_assert(std::atomic_size_t::is_always_lock_free); - public: /// Pushes slots into the ring buffer /// @param new_slots Pointer to the slots to push diff --git a/src/common/typed_address.h b/src/common/typed_address.h index 4df48a99fb..f716accd03 100644 --- a/src/common/typed_address.h +++ b/src/common/typed_address.h @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2026 Eden Emulator Project +// SPDX-License-Identifier: GPL-3.0-or-later + // SPDX-FileCopyrightText: Copyright 2023 yuzu Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later @@ -30,8 +33,8 @@ public: // Arithmetic operators. template <typename I> + requires std::is_integral_v<I> constexpr inline TypedAddress operator+(I rhs) const { - static_assert(std::is_integral_v<I>); return m_address + rhs; } @@ -48,8 +51,8 @@ public: } template <typename I> + requires std::is_integral_v<I> constexpr inline TypedAddress operator-(I rhs) const { - static_assert(std::is_integral_v<I>); return m_address - rhs; } @@ -66,15 +69,15 @@ public: } template <typename I> + requires std::is_integral_v<I> constexpr inline TypedAddress operator+=(I rhs) { - static_assert(std::is_integral_v<I>); m_address += rhs; return *this; } template <typename I> + requires std::is_integral_v<I> constexpr inline TypedAddress operator-=(I rhs) { - static_assert(std::is_integral_v<I>); m_address -= rhs; return *this; } @@ -89,8 +92,8 @@ public: } template <typename I> + requires std::is_integral_v<I> constexpr inline TypedAddress operator|=(I rhs) { - static_assert(std::is_integral_v<I>); m_address |= rhs; return *this; } diff --git a/src/common/x64/xbyak.h b/src/common/x64/xbyak.h index e541215994..5288c5184f 100644 --- a/src/common/x64/xbyak.h +++ b/src/common/x64/xbyak.h @@ -268,8 +268,8 @@ constexpr bool IsWithin2G(uintptr_t ref, uintptr_t target) noexcept { } template <typename T> + requires std::is_pointer_v<T> inline void CallFarFunction(Xbyak::CodeGenerator& code, const T f) { - static_assert(std::is_pointer_v<T>, "Argument must be a (function) pointer."); uintptr_t addr = uintptr_t(f); if (IsWithin2G(uintptr_t(code.getCurr()), addr)) { code.call(f); |
