rpcsx/rx/include/rx/EnumBitSet.hpp
DH d7ad77b406 orbis: implement physical memory emulation level & utils improvement
fix blockpool & dmem implementation
modernize blockpool & dmem io devices
use budgets per allocation
add serialization support for MemoryTableWithPayload and AddressRange utils
add format support for EnumBitSet util
implemented trace formatter per syscall
increased allowed reference count for Ref
2025-11-30 15:46:37 +03:00

349 lines
9.9 KiB
C++

#pragma once
/*
This header implements EnumBitSet<> class for scoped enum types (enum class).
To enable EnumBitSet<>, enum scope must contain `bitset_last` entry.
enum class flagzz : u32
{
flag1, // Bit indices start from zero
flag2,
bitset_last = flag2
};
This also enables helper operators for this enum type.
Examples:
`flagzz::flag1 | flagzz::flag2` - bitset union
`flagzz::flag1 & ~flagzz::flag2` - bitset difference
Intersection (&) and symmetric difference (^) is also available.
*/
#include "format-base.hpp"
#include "rx/refl.hpp"
#include <type_traits>
namespace rx {
template <typename T>
concept BitSetEnum = std::is_enum_v<T> && requires { T::bitset_last; };
template <BitSetEnum T> class EnumBitSet;
namespace detail {
template <BitSetEnum T> class InvertedEnumBitSet final {
using underlying_type = std::underlying_type_t<T>;
underlying_type m_data;
constexpr InvertedEnumBitSet(underlying_type data) : m_data(data) {}
friend EnumBitSet<T>;
};
} // namespace detail
// Bitset type for enum class with available bits [0, fieldCount)
template <BitSetEnum T> class EnumBitSet final {
public:
// Underlying type
using underlying_type = std::underlying_type_t<T>;
private:
// Underlying value
underlying_type m_data;
// Value constructor
constexpr explicit EnumBitSet(int, underlying_type data) noexcept
: m_data(data) {}
public:
static constexpr std::size_t bitmax = sizeof(T) * 8;
static constexpr std::size_t bitsize =
static_cast<underlying_type>(T::bitset_last) + 1;
static_assert(std::is_enum_v<T>,
"BitSet<> error: invalid type (must be enum)");
static_assert(bitsize <= bitmax,
"BitSet<> error: failed to determine enum field count");
static_assert(bitsize != bitmax || std::is_unsigned_v<underlying_type>,
"BitSet<> error: invalid field count (sign bit)");
// Helper function
static constexpr underlying_type shift(T value) {
return static_cast<underlying_type>(1)
<< static_cast<underlying_type>(value);
}
constexpr EnumBitSet() = default;
// Construct from a single bit
constexpr EnumBitSet(T bit) noexcept : m_data(shift(bit)) {}
[[nodiscard]] constexpr underlying_type toUnderlying() const {
return m_data;
}
[[nodiscard]] static constexpr EnumBitSet
fromUnderlying(underlying_type raw) {
return EnumBitSet(0, raw);
}
// Test for empty bitset
constexpr explicit operator bool() const noexcept { return m_data != 0; }
// Extract underlying data
constexpr explicit operator underlying_type() const noexcept {
return m_data;
}
constexpr detail::InvertedEnumBitSet<T> operator~() const {
return static_cast<underlying_type>(~m_data);
}
[[deprecated("Use operator|=")]] constexpr EnumBitSet &
operator+=(EnumBitSet rhs) {
m_data |= static_cast<underlying_type>(rhs);
return *this;
}
constexpr EnumBitSet &operator|=(EnumBitSet rhs) {
m_data |= static_cast<underlying_type>(rhs);
return *this;
}
constexpr EnumBitSet &operator-=(EnumBitSet rhs) {
m_data &= ~static_cast<underlying_type>(rhs);
return *this;
}
constexpr EnumBitSet without(EnumBitSet rhs) const {
EnumBitSet result = *this;
result.m_data &= ~static_cast<underlying_type>(rhs);
return result;
}
constexpr EnumBitSet with(EnumBitSet rhs) const {
EnumBitSet result = *this;
result.m_data |= static_cast<underlying_type>(rhs);
return result;
}
constexpr EnumBitSet &operator&=(EnumBitSet rhs) {
m_data &= static_cast<underlying_type>(rhs);
return *this;
}
constexpr EnumBitSet &operator&=(detail::InvertedEnumBitSet<T> rhs) {
m_data &= rhs.m_data;
return *this;
}
constexpr EnumBitSet &operator^=(EnumBitSet rhs) {
m_data ^= static_cast<underlying_type>(rhs);
return *this;
}
[[deprecated("Use operator|")]] friend constexpr EnumBitSet
operator+(EnumBitSet lhs, EnumBitSet rhs) {
return EnumBitSet(0, lhs.m_data | rhs.m_data);
}
friend constexpr EnumBitSet operator-(EnumBitSet lhs, EnumBitSet rhs) {
return EnumBitSet(0, lhs.m_data & ~rhs.m_data);
}
friend constexpr EnumBitSet operator|(EnumBitSet lhs, EnumBitSet rhs) {
return EnumBitSet(0, lhs.m_data | rhs.m_data);
}
friend constexpr EnumBitSet operator&(EnumBitSet lhs, EnumBitSet rhs) {
return EnumBitSet(0, lhs.m_data & rhs.m_data);
}
friend constexpr EnumBitSet operator&(EnumBitSet lhs,
detail::InvertedEnumBitSet<T> rhs) {
return EnumBitSet(0, lhs.m_data & rhs.m_data);
}
friend constexpr EnumBitSet operator^(EnumBitSet lhs, EnumBitSet rhs) {
return EnumBitSet(0, lhs.m_data ^ rhs.m_data);
}
constexpr bool operator==(EnumBitSet rhs) const noexcept {
return m_data == rhs.m_data;
}
constexpr bool test_and_set(T bit) {
bool r = (m_data & shift(bit)) != 0;
m_data |= shift(bit);
return r;
}
constexpr bool test_and_reset(T bit) {
bool r = (m_data & shift(bit)) != 0;
m_data &= ~shift(bit);
return r;
}
constexpr bool test_and_complement(T bit) {
bool r = (m_data & shift(bit)) != 0;
m_data ^= shift(bit);
return r;
}
constexpr bool any_of(EnumBitSet arg) const {
return (m_data & arg.m_data) != 0;
}
constexpr bool all_of(EnumBitSet arg) const {
return (m_data & arg.m_data) == arg.m_data;
}
constexpr bool none_of(EnumBitSet arg) const {
return (m_data & arg.m_data) == 0;
}
constexpr underlying_type &raw() { return m_data; }
constexpr auto operator<=>(const EnumBitSet &) const = default;
};
template <BitSetEnum T> constexpr EnumBitSet<T> toBitSet(T bit) {
return EnumBitSet<T>(bit);
}
namespace bitset {
// Unary '+' operator: promote plain enum value to bitset value
template <BitSetEnum T>
[[deprecated("Use toBitSet(bit)")]] constexpr EnumBitSet<T> operator+(T bit) {
return EnumBitSet<T>(bit);
}
template <BitSetEnum T>
constexpr detail::InvertedEnumBitSet<T> operator~(T bit) {
return ~toBitSet(bit);
}
// Binary '+' operator: bitset union
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<EnumBitSet<T>, U>)
[[deprecated("Use operator|")]] constexpr EnumBitSet<T>
operator+(T lhs, const U &rhs) {
return EnumBitSet<T>(lhs) | EnumBitSet<T>(rhs);
}
// Binary '+' operator: bitset union
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<EnumBitSet<T>, U> && !std::is_enum_v<U>)
[[deprecated("Use operator|")]] constexpr EnumBitSet<T> operator+(const U &lhs,
T rhs) {
return EnumBitSet<T>(lhs) | EnumBitSet<T>(rhs);
}
// Binary '|' operator: bitset union
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<EnumBitSet<T>, U>)
constexpr EnumBitSet<T> operator|(T lhs, const U &rhs) {
return EnumBitSet<T>(lhs) | EnumBitSet<T>(rhs);
}
// Binary '|' operator: bitset union
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<EnumBitSet<T>, U> && !std::is_enum_v<U>)
constexpr EnumBitSet<T> operator|(const U &lhs, T rhs) {
return EnumBitSet<T>(lhs) | EnumBitSet<T>(rhs);
}
// Binary '-' operator: bitset difference
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<EnumBitSet<T>, U>)
constexpr EnumBitSet<T> operator-(T lhs, const U &rhs) {
return EnumBitSet<T>(lhs) - EnumBitSet<T>(rhs);
}
// Binary '-' operator: bitset difference
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<EnumBitSet<T>, U> && !std::is_enum_v<U>)
constexpr EnumBitSet<T> operator-(const U &lhs, T rhs) {
return EnumBitSet<T>(lhs) - EnumBitSet<T>(rhs);
}
// Binary '&' operator: bitset intersection
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<EnumBitSet<T>, U>)
constexpr EnumBitSet<T> operator&(T lhs, const U &rhs) {
return EnumBitSet<T>(lhs) & EnumBitSet<T>(rhs);
}
// Binary '&' operator: bitset intersection
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<EnumBitSet<T>, U> && !std::is_enum_v<U>)
constexpr EnumBitSet<T> operator&(const U &lhs, T rhs) {
return EnumBitSet<T>(lhs) & EnumBitSet<T>(rhs);
}
// Binary '&' operator: bitset intersection
template <BitSetEnum T, typename U>
constexpr EnumBitSet<T> operator&(T lhs, detail::InvertedEnumBitSet<T> rhs) {
return EnumBitSet<T>(lhs) & rhs;
}
// Binary '^' operator: bitset symmetric difference
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<EnumBitSet<T>, U>)
constexpr EnumBitSet<T> operator^(T lhs, const U &rhs) {
return EnumBitSet<T>(lhs) ^ EnumBitSet<T>(rhs);
}
// Binary '^' operator: bitset symmetric difference
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<EnumBitSet<T>, U> && !std::is_enum_v<U>)
constexpr EnumBitSet<T> operator^(const U &lhs, T rhs) {
return EnumBitSet<T>(lhs) ^ EnumBitSet<T>(rhs);
}
} // namespace bitset
} // namespace rx
template <typename T>
requires requires(rx::format_parse_context &ctx) {
rx::formatter<T>().parse(ctx);
}
struct rx::formatter<rx::EnumBitSet<T>> {
constexpr rx::format_parse_context::iterator
parse(rx::format_parse_context &ctx) {
return ctx.begin();
}
rx::format_context::iterator format(rx::EnumBitSet<T> bitSet,
rx::format_context &ctx) const {
auto raw = bitSet.toUnderlying();
if (raw != 0) {
bool first = true;
for (std::size_t i = 0; i <= static_cast<std::size_t>(T::bitset_last);
++i) {
auto mask = 1ull << i;
if (!(raw & mask)) {
continue;
}
if (first) {
first = false;
} else {
rx::format_to(ctx.out(), " | ");
}
rx::format_to(ctx.out(), "{}", T(i));
raw &= ~mask;
}
if (raw) {
if (!first) {
rx::format_to(ctx.out(), " | ");
}
rx::format_to(ctx.out(), "{:#x}", raw);
}
} else {
rx::format_to(ctx.out(), "{}::None", rx::getNameOf<T>());
}
return ctx.out();
}
};
using namespace rx::bitset;