replace bs_t with rx::EnumBitSet

This commit is contained in:
DH 2025-10-04 22:19:57 +03:00
parent 3f14b99f24
commit 2965aaf3e3
46 changed files with 600 additions and 809 deletions

View file

@ -124,7 +124,7 @@ enum class lv2_mp_flag {
strict_get_block_size,
cache,
__bitset_enum_max
bitset_last = cache,
};
enum class lv2_file_type {
@ -140,7 +140,7 @@ struct lv2_fs_mount_point {
const u32 sector_size = 512;
const u64 sector_count = 256;
const u32 block_size = 4096;
const bs_t<lv2_mp_flag> flags{};
const rx::EnumBitSet<lv2_mp_flag> flags{};
lv2_fs_mount_point *const next = nullptr;
mutable shared_mutex mutex;

View file

@ -1,16 +1,8 @@
#pragma once
#include "util/bit_set.h"
#include "util/mutex.h"
#include "Emu/Cell/ErrorCodes.h"
#include "Emu/Memory/vm_ptr.h"
#include <functional>
#include <queue>
#include <utility>
#include <vector>
// Error codes
enum sys_net_error : s32 {
SYS_NET_ENOENT = 2,
@ -54,7 +46,7 @@ enum sys_net_error : s32 {
};
static constexpr sys_net_error operator-(sys_net_error v) {
return sys_net_error{-+v};
return sys_net_error{-static_cast<s32>(v)};
}
// Socket types (prefixed with SYS_NET_)

View file

@ -6,6 +6,8 @@
#include "Emu/IdManager.h"
#include "Emu/NP/ip_address.h"
#include "cellos/sys_net.h"
#include "rx/EnumBitSet.hpp"
#include "util/atomic_bit_set.h"
#include "util/mutex.h"
#ifdef _WIN32
@ -32,7 +34,7 @@ public:
write,
error,
__bitset_enum_max
bitset_last
};
union sockopt_data {
@ -62,10 +64,10 @@ public:
std::unique_lock<shared_mutex> lock();
void set_lv2_id(u32 id);
bs_t<poll_t> get_events() const;
void set_poll_event(bs_t<poll_t> event);
void poll_queue(shared_ptr<ppu_thread> ppu, bs_t<poll_t> event,
std::function<bool(bs_t<poll_t>)> poll_cb);
rx::EnumBitSet<poll_t> get_events() const;
void set_poll_event(rx::EnumBitSet<poll_t> event);
void poll_queue(shared_ptr<ppu_thread> ppu, rx::EnumBitSet<poll_t> event,
std::function<bool(rx::EnumBitSet<poll_t>)> poll_cb);
u32 clear_queue(ppu_thread *);
void handle_events(const pollfd &native_fd, bool unset_connecting = false);
void queue_wake(ppu_thread *ppu);
@ -110,7 +112,7 @@ public:
virtual s32 shutdown(s32 how) = 0;
virtual s32 poll(sys_net_pollfd &sn_pfd, pollfd &native_pfd) = 0;
virtual std::tuple<bool, bool, bool> select(bs_t<poll_t> selected,
virtual std::tuple<bool, bool, bool> select(rx::EnumBitSet<poll_t> selected,
pollfd &native_pfd) = 0;
error_code abort_socket(s32 flags);
@ -137,8 +139,8 @@ protected:
atomic_bs_t<poll_t> events{};
// Event processing workload (pair of thread id and the processing function)
std::vector<
std::pair<shared_ptr<ppu_thread>, std::function<bool(bs_t<poll_t>)>>>
std::vector<std::pair<shared_ptr<ppu_thread>,
std::function<bool(rx::EnumBitSet<poll_t>)>>>
queue;
// Socket options value keepers

View file

@ -58,7 +58,7 @@ public:
bool is_lock = true) override;
s32 poll(sys_net_pollfd &sn_pfd, pollfd &native_pfd) override;
std::tuple<bool, bool, bool> select(bs_t<poll_t> selected,
std::tuple<bool, bool, bool> select(rx::EnumBitSet<poll_t> selected,
pollfd &native_pfd) override;
bool is_socket_connected();

View file

@ -1,6 +1,7 @@
#pragma once
#include "lv2_socket.h"
#include <queue>
class lv2_socket_p2p : public lv2_socket {
public:
@ -38,7 +39,7 @@ public:
s32 shutdown(s32 how) override;
s32 poll(sys_net_pollfd &sn_pfd, pollfd &native_pfd) override;
std::tuple<bool, bool, bool> select(bs_t<poll_t> selected,
std::tuple<bool, bool, bool> select(rx::EnumBitSet<poll_t> selected,
pollfd &native_pfd) override;
void handle_new_data(sys_net_sockaddr_in_p2p p2p_addr,

View file

@ -1,5 +1,6 @@
#pragma once
#include <deque>
#ifdef _WIN32
#include <WS2tcpip.h>
#include <winsock2.h>
@ -101,7 +102,7 @@ public:
s32 shutdown(s32 how) override;
s32 poll(sys_net_pollfd &sn_pfd, pollfd &native_pfd) override;
std::tuple<bool, bool, bool> select(bs_t<poll_t> selected,
std::tuple<bool, bool, bool> select(rx::EnumBitSet<poll_t> selected,
pollfd &native_pfd) override;
private:

View file

@ -40,6 +40,6 @@ public:
s32 shutdown(s32 how) override;
s32 poll(sys_net_pollfd &sn_pfd, pollfd &native_pfd) override;
std::tuple<bool, bool, bool> select(bs_t<poll_t> selected,
std::tuple<bool, bool, bool> select(rx::EnumBitSet<poll_t> selected,
pollfd &native_pfd) override;
};

View file

@ -53,6 +53,7 @@
#include "sys_usbd.h"
#include "sys_vm.h"
#include "util/atomic_bit_set.h"
#include "util/init_mutex.hpp"
#include "util/sysinfo.hpp"
#include "util/tsc.hpp"
@ -1762,17 +1763,18 @@ bool lv2_obj::sleep_unlocked(cpu_thread &thread, u64 timeout,
return_val = false;
}
const auto [_, ok] = ppu->state.fetch_op([&](bs_t<cpu_flag> &val) {
if (!(val & cpu_flag::signal)) {
val += cpu_flag::suspend;
const auto [_, ok] =
ppu->state.fetch_op([&](rx::EnumBitSet<cpu_flag> &val) {
if (!(val & cpu_flag::signal)) {
val += cpu_flag::suspend;
// Flag used for forced timeout notification
ensure(!timeout || !(val & cpu_flag::notify));
return true;
}
// Flag used for forced timeout notification
ensure(!timeout || !(val & cpu_flag::notify));
return true;
}
return false;
});
return false;
});
if (!ok) {
ppu_log.fatal("sleep() failed (signaled) (%s)", ppu->current_function);
@ -2075,7 +2077,7 @@ void lv2_obj::schedule_all(u64 current_time) {
ppu_log.trace("schedule(): %s", target->id);
// Remove yield if it was sleeping until now
const bs_t<cpu_flag> remove_yield =
const rx::EnumBitSet<cpu_flag> remove_yield =
target->start_time == 0 ? +cpu_flag::suspend
: (cpu_flag::yield + cpu_flag::preempt);

View file

@ -740,7 +740,7 @@ lv2_file::open_raw_result_t lv2_file::open_raw(const std::string &local_path,
return {CELL_EISDIR};
}
bs_t<fs::open_mode> open_mode{};
rx::EnumBitSet<fs::open_mode> open_mode{};
switch (flags & CELL_FS_O_ACCMODE) {
case CELL_FS_O_RDONLY:

View file

@ -403,7 +403,7 @@ error_code sys_net_bnet_accept(ppu_thread &ppu, s32 s,
sock.poll_queue(idm::get_unlocked<named_thread<ppu_thread>>(ppu.id),
lv2_socket::poll_t::read,
[&](bs_t<lv2_socket::poll_t> events) -> bool {
[&](rx::EnumBitSet<lv2_socket::poll_t> events) -> bool {
if (events & lv2_socket::poll_t::read) {
auto [success, res, res_socket, res_addr] =
sock.accept(false);
@ -554,7 +554,7 @@ error_code sys_net_bnet_connect(ppu_thread &ppu, s32 s,
sock.poll_queue(idm::get_unlocked<named_thread<ppu_thread>>(ppu.id),
lv2_socket::poll_t::write,
[&](bs_t<lv2_socket::poll_t> events) -> bool {
[&](rx::EnumBitSet<lv2_socket::poll_t> events) -> bool {
if (events & lv2_socket::poll_t::write) {
result = sock.connect_followup();
@ -819,7 +819,7 @@ error_code sys_net_bnet_recvfrom(ppu_thread &ppu, s32 s, vm::ptr<void> buf,
sock.poll_queue(
idm::get_unlocked<named_thread<ppu_thread>>(ppu.id),
lv2_socket::poll_t::read,
[&](bs_t<lv2_socket::poll_t> events) -> bool {
[&](rx::EnumBitSet<lv2_socket::poll_t> events) -> bool {
if (events & lv2_socket::poll_t::read) {
const auto success = sock.recvfrom(flags, len, false);
@ -929,7 +929,7 @@ error_code sys_net_bnet_sendmsg(ppu_thread &ppu, s32 s,
sock.poll_queue(idm::get_unlocked<named_thread<ppu_thread>>(ppu.id),
lv2_socket::poll_t::write,
[&](bs_t<lv2_socket::poll_t> events) -> bool {
[&](rx::EnumBitSet<lv2_socket::poll_t> events) -> bool {
if (events & lv2_socket::poll_t::write) {
const auto success =
sock.sendmsg(flags, *netmsg, false);
@ -1023,7 +1023,7 @@ error_code sys_net_bnet_sendto(ppu_thread &ppu, s32 s, vm::cptr<void> buf,
// Enable write event
sock.poll_queue(idm::get_unlocked<named_thread<ppu_thread>>(ppu.id),
lv2_socket::poll_t::write,
[&](bs_t<lv2_socket::poll_t> events) -> bool {
[&](rx::EnumBitSet<lv2_socket::poll_t> events) -> bool {
if (events & lv2_socket::poll_t::write) {
auto success =
sock.sendto(flags, buf_copy, sn_addr, false);
@ -1336,7 +1336,7 @@ error_code sys_net_bnet_poll(ppu_thread &ppu, vm::ptr<sys_net_pollfd> fds,
sock->set_connecting(connecting[i]);
#endif
bs_t<lv2_socket::poll_t> selected = +lv2_socket::poll_t::error;
rx::EnumBitSet<lv2_socket::poll_t> selected = +lv2_socket::poll_t::error;
if (fds_buf[i].events & SYS_NET_POLLIN)
selected += lv2_socket::poll_t::read;
@ -1348,7 +1348,7 @@ error_code sys_net_bnet_poll(ppu_thread &ppu, vm::ptr<sys_net_pollfd> fds,
sock->poll_queue(idm::get_unlocked<named_thread<ppu_thread>>(ppu.id),
selected,
[sock, selected, &fds_buf, i, &signaled,
&ppu](bs_t<lv2_socket::poll_t> events) {
&ppu](rx::EnumBitSet<lv2_socket::poll_t> events) {
if (events & selected) {
if (events & selected & lv2_socket::poll_t::read)
fds_buf[i].revents |= SYS_NET_POLLIN;
@ -1457,7 +1457,7 @@ error_code sys_net_bnet_select(ppu_thread &ppu, s32 nfds,
for (s32 i = 0; i < nfds; i++) {
_fds[i].fd = -1;
bs_t<lv2_socket::poll_t> selected{};
rx::EnumBitSet<lv2_socket::poll_t> selected{};
if (readfds && _readfds.bit(i))
selected += lv2_socket::poll_t::read;
@ -1529,7 +1529,7 @@ error_code sys_net_bnet_select(ppu_thread &ppu, s32 nfds,
}
for (s32 i = 0; i < nfds; i++) {
bs_t<lv2_socket::poll_t> selected{};
rx::EnumBitSet<lv2_socket::poll_t> selected{};
if (readfds && _readfds.bit(i))
selected += lv2_socket::poll_t::read;
@ -1554,7 +1554,7 @@ error_code sys_net_bnet_select(ppu_thread &ppu, s32 nfds,
sock->poll_queue(
idm::get_unlocked<named_thread<ppu_thread>>(ppu.id), selected,
[sock, selected, i, &rread, &rwrite, &rexcept, &signaled,
&ppu](bs_t<lv2_socket::poll_t> events) {
&ppu](rx::EnumBitSet<lv2_socket::poll_t> events) {
if (events & selected) {
if (selected & lv2_socket::poll_t::read &&
events &

View file

@ -32,17 +32,17 @@ void lv2_socket::set_connecting(bool connecting) {
void lv2_socket::set_lv2_id(u32 id) { lv2_id = id; }
bs_t<lv2_socket::poll_t> lv2_socket::get_events() const {
rx::EnumBitSet<lv2_socket::poll_t> lv2_socket::get_events() const {
return events.load();
}
void lv2_socket::set_poll_event(bs_t<lv2_socket::poll_t> event) {
void lv2_socket::set_poll_event(rx::EnumBitSet<lv2_socket::poll_t> event) {
events += event;
}
void lv2_socket::poll_queue(
shared_ptr<ppu_thread> ppu, bs_t<lv2_socket::poll_t> event,
std::function<bool(bs_t<lv2_socket::poll_t>)> poll_cb) {
shared_ptr<ppu_thread> ppu, rx::EnumBitSet<lv2_socket::poll_t> event,
std::function<bool(rx::EnumBitSet<lv2_socket::poll_t>)> poll_cb) {
set_poll_event(event);
queue.emplace_back(std::move(ppu), poll_cb);
@ -88,7 +88,7 @@ u32 lv2_socket::clear_queue(ppu_thread *ppu) {
void lv2_socket::handle_events(const pollfd &native_pfd,
[[maybe_unused]] bool unset_connecting) {
bs_t<lv2_socket::poll_t> events_happening{};
rx::EnumBitSet<lv2_socket::poll_t> events_happening{};
if (native_pfd.revents & (POLLIN | POLLHUP) &&
events.test_and_reset(lv2_socket::poll_t::read))

View file

@ -260,7 +260,7 @@ std::optional<s32> lv2_socket_native::connect(const sys_net_sockaddr &addr) {
#endif
this->poll_queue(
null_ptr, lv2_socket::poll_t::write,
[this](bs_t<lv2_socket::poll_t> events) -> bool {
[this](rx::EnumBitSet<lv2_socket::poll_t> events) -> bool {
if (events & lv2_socket::poll_t::write) {
int native_error;
::socklen_t size = sizeof(native_error);
@ -1101,7 +1101,7 @@ s32 lv2_socket_native::poll(sys_net_pollfd &sn_pfd, pollfd &native_pfd) {
}
std::tuple<bool, bool, bool>
lv2_socket_native::select(bs_t<lv2_socket::poll_t> selected,
lv2_socket_native::select(rx::EnumBitSet<lv2_socket::poll_t> selected,
pollfd &native_pfd) {
native_pfd.fd = native_socket;
if (selected & lv2_socket::poll_t::read) {

View file

@ -4,6 +4,7 @@
#include "sys_net/lv2_socket_p2p.h"
#include "sys_net/network_context.h"
#include "sys_net/sys_net_helpers.h"
#include <deque>
LOG_CHANNEL(sys_net);
@ -54,7 +55,7 @@ void lv2_socket_p2p::handle_new_data(sys_net_sockaddr_in_p2p p2p_addr,
// Check if poll is happening
if (events.test_and_reset(lv2_socket::poll_t::read)) {
bs_t<lv2_socket::poll_t> read_event = lv2_socket::poll_t::read;
rx::EnumBitSet<lv2_socket::poll_t> read_event = lv2_socket::poll_t::read;
for (auto it = queue.begin(); it != queue.end();) {
if (it->second(read_event)) {
it = queue.erase(it);
@ -378,7 +379,7 @@ s32 lv2_socket_p2p::poll(sys_net_pollfd &sn_pfd,
}
std::tuple<bool, bool, bool>
lv2_socket_p2p::select(bs_t<lv2_socket::poll_t> selected,
lv2_socket_p2p::select(rx::EnumBitSet<lv2_socket::poll_t> selected,
[[maybe_unused]] pollfd &native_pfd) {
std::lock_guard lock(mutex);

View file

@ -332,7 +332,7 @@ bool lv2_socket_p2ps::handle_connected(p2ps_encapsulated_tcp *tcp_header,
// check if polling is happening
if (data_available && events.test_and_reset(lv2_socket::poll_t::read)) {
bs_t<lv2_socket::poll_t> read_event = lv2_socket::poll_t::read;
rx::EnumBitSet<lv2_socket::poll_t> read_event = lv2_socket::poll_t::read;
for (auto it = queue.begin(); it != queue.end();) {
if (it->second(read_event)) {
it = queue.erase(it);
@ -479,7 +479,7 @@ bool lv2_socket_p2ps::handle_listening(p2ps_encapsulated_tcp *tcp_header,
backlog.push_back(new_sock_id);
if (events.test_and_reset(lv2_socket::poll_t::read)) {
bs_t<lv2_socket::poll_t> read_event = lv2_socket::poll_t::read;
rx::EnumBitSet<lv2_socket::poll_t> read_event = lv2_socket::poll_t::read;
for (auto it = queue.begin(); it != queue.end();) {
if (it->second(read_event)) {
it = queue.erase(it);
@ -983,7 +983,7 @@ s32 lv2_socket_p2ps::poll(sys_net_pollfd &sn_pfd,
}
std::tuple<bool, bool, bool>
lv2_socket_p2ps::select(bs_t<lv2_socket::poll_t> selected,
lv2_socket_p2ps::select(rx::EnumBitSet<lv2_socket::poll_t> selected,
[[maybe_unused]] pollfd &native_pfd) {
std::lock_guard lock(mutex);

View file

@ -130,7 +130,7 @@ s32 lv2_socket_raw::poll([[maybe_unused]] sys_net_pollfd &sn_pfd,
}
std::tuple<bool, bool, bool>
lv2_socket_raw::select([[maybe_unused]] bs_t<lv2_socket::poll_t> selected,
lv2_socket_raw::select([[maybe_unused]] rx::EnumBitSet<lv2_socket::poll_t> selected,
[[maybe_unused]] pollfd &native_pfd) {
LOG_ONCE(raw_select, "lv2_socket_raw::select");
return {};

View file

@ -1295,7 +1295,7 @@ error_code sys_spu_thread_group_terminate(ppu_thread &ppu, u32 id, s32 value) {
for (auto &thread : group->threads) {
if (thread) {
thread->state.fetch_op([](bs_t<cpu_flag> &flags) {
thread->state.fetch_op([](rx::EnumBitSet<cpu_flag> &flags) {
if (flags & cpu_flag::stop) {
// In case the thread raised the ret flag itself at some point do not
// raise it again

View file

@ -1612,7 +1612,7 @@ enum class strkey_flag : u32
// set_other, // writing is allowed for other types of PARAM.SFO (not
// possible)
__bitset_enum_max
bitset_last
};
struct string_key_info
@ -1620,7 +1620,7 @@ struct string_key_info
public:
string_key_info() = default;
string_key_info(std::string_view _name, u32 _max_size,
bs_t<strkey_flag> _flags)
rx::EnumBitSet<strkey_flag> _flags)
: name(_name), max_size(_max_size), flags(_flags) {}
std::string_view name;
@ -1653,7 +1653,7 @@ public:
}
private:
bs_t<strkey_flag> flags{}; // allowed operations
rx::EnumBitSet<strkey_flag> flags{}; // allowed operations
};
static string_key_info get_param_string_key(s32 id)

View file

@ -8,8 +8,9 @@
#include "sha256.h"
#include "key_vault.h"
#include <cstring>
#include <stdio.h>
#include <time.h>
#include <cstdio>
#include <ctime>
#include "util/StrFmt.h"
#include "util/StrUtil.h"
#include "util/File.h"

View file

@ -64,7 +64,7 @@ void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
case cpu_flag::dbg_global_pause: return "G-PAUSE";
case cpu_flag::dbg_pause: return "PAUSE";
case cpu_flag::dbg_step: return "STEP";
case cpu_flag::__bitset_enum_max: break;
case cpu_flag::bitset_last: break;
}
return unknown;
@ -72,7 +72,7 @@ void fmt_class_string<cpu_flag>::format(std::string& out, u64 arg)
}
template <>
void fmt_class_string<bs_t<cpu_flag>>::format(std::string& out, u64 arg)
void fmt_class_string<rx::EnumBitSet<cpu_flag>>::format(std::string& out, u64 arg)
{
format_bitset(out, arg, "[", "|", "]", &fmt_class_string<cpu_flag>::format);
}
@ -799,7 +799,7 @@ cpu_thread::cpu_thread(u32 id)
}
}
void cpu_thread::cpu_wait(bs_t<cpu_flag> old)
void cpu_thread::cpu_wait(rx::EnumBitSet<cpu_flag> old)
{
state.wait(old);
}
@ -816,8 +816,8 @@ bool cpu_thread::check_state() noexcept
while (true)
{
// Process all flags in a single atomic op
bs_t<cpu_flag> state1;
auto state0 = state.fetch_op([&](bs_t<cpu_flag>& flags)
rx::EnumBitSet<cpu_flag> state1;
auto state0 = state.fetch_op([&](rx::EnumBitSet<cpu_flag>& flags)
{
bool store = false;
@ -1168,9 +1168,9 @@ cpu_thread& cpu_thread::operator=(thread_state)
return *this;
}
void cpu_thread::add_remove_flags(bs_t<cpu_flag> to_add, bs_t<cpu_flag> to_remove)
void cpu_thread::add_remove_flags(rx::EnumBitSet<cpu_flag> to_add, rx::EnumBitSet<cpu_flag> to_remove)
{
bs_t<cpu_flag> result{};
rx::EnumBitSet<cpu_flag> result{};
if (!to_remove)
{
@ -1183,7 +1183,7 @@ void cpu_thread::add_remove_flags(bs_t<cpu_flag> to_add, bs_t<cpu_flag> to_remov
}
else
{
result = state.atomic_op([&](bs_t<cpu_flag>& v)
result = state.atomic_op([&](rx::EnumBitSet<cpu_flag>& v)
{
v += to_add;
v -= to_remove;

View file

@ -1,7 +1,8 @@
#pragma once
#include "util/Thread.h"
#include "util/bit_set.h"
#include "rx/EnumBitSet.hpp"
#include "util/atomic_bit_set.h"
#include <vector>
#include <any>
@ -33,17 +34,17 @@ enum class cpu_flag : u32
dbg_pause, // Thread paused
dbg_step, // Thread forced to pause after one step (one instruction, etc)
__bitset_enum_max
bitset_last
};
// Test stopped state
constexpr bool is_stopped(bs_t<cpu_flag> state)
constexpr bool is_stopped(rx::EnumBitSet<cpu_flag> state)
{
return !!(state & (cpu_flag::stop + cpu_flag::exit + cpu_flag::again));
}
// Test paused state
constexpr bool is_paused(bs_t<cpu_flag> state)
constexpr bool is_paused(rx::EnumBitSet<cpu_flag> state)
{
return !!(state & (cpu_flag::suspend + cpu_flag::dbg_global_pause + cpu_flag::dbg_pause)) && !is_stopped(state);
}
@ -87,12 +88,12 @@ public:
}
// Wrappers
static constexpr bool is_stopped(bs_t<cpu_flag> s)
static constexpr bool is_stopped(rx::EnumBitSet<cpu_flag> s)
{
return ::is_stopped(s);
}
static constexpr bool is_paused(bs_t<cpu_flag> s)
static constexpr bool is_paused(rx::EnumBitSet<cpu_flag> s)
{
return ::is_paused(s);
}
@ -155,7 +156,7 @@ public:
cpu_thread& operator=(thread_state);
// Add/remove CPU state flags in an atomic operations, notifying if required
void add_remove_flags(bs_t<cpu_flag> to_add, bs_t<cpu_flag> to_remove);
void add_remove_flags(rx::EnumBitSet<cpu_flag> to_add, rx::EnumBitSet<cpu_flag> to_remove);
// Thread stats for external observation
static atomic_t<u64> g_threads_created, g_threads_deleted, g_suspend_counter;
@ -194,7 +195,7 @@ public:
virtual void cpu_return() {}
// Callback for thread_ctrl::wait or RSX wait
virtual void cpu_wait(bs_t<cpu_flag> old);
virtual void cpu_wait(rx::EnumBitSet<cpu_flag> old);
// Callback for function abortion stats on Emu.Kill()
virtual void cpu_on_stop() {}

View file

@ -25,7 +25,7 @@ void fmt_class_string<ppu_attr>::format(std::string& out, u64 arg)
case ppu_attr::no_return: return "no_return";
case ppu_attr::no_size: return "no_size";
case ppu_attr::has_mfvscr: return "has_mfvscr";
case ppu_attr::__bitset_enum_max: break;
case ppu_attr::bitset_last: break;
}
return unknown;
@ -33,7 +33,7 @@ void fmt_class_string<ppu_attr>::format(std::string& out, u64 arg)
}
template <>
void fmt_class_string<bs_t<ppu_attr>>::format(std::string& out, u64 arg)
void fmt_class_string<rx::EnumBitSet<ppu_attr>>::format(std::string& out, u64 arg)
{
format_bitset(out, arg, "[", ",", "]", &fmt_class_string<ppu_attr>::format);
}
@ -857,7 +857,7 @@ bool ppu_module<lv2_obj>::analyse(u32 lib_toc, u32 entry, const u32 sec_end, con
// u32 stack_frame = 0;
u32 single_target = 0;
u32 trampoline = 0;
bs_t<ppu_attr> attr{};
rx::EnumBitSet<ppu_attr> attr{};
std::set<u32> callers{};
};

View file

@ -8,7 +8,7 @@
#include "util/asm.hpp"
#include "util/to_endian.hpp"
#include "util/bit_set.h"
#include "rx/EnumBitSet.hpp"
#include "PPUOpcodes.h"
// PPU Function Attributes
@ -19,7 +19,7 @@ enum class ppu_attr : u8
no_size,
has_mfvscr,
__bitset_enum_max
bitset_last
};
// PPU Function Information
@ -131,7 +131,7 @@ struct ppu_module : public Type
std::string name{}; // Filename
std::string path{}; // Filepath
s64 offset = 0; // Offset of file
mutable bs_t<ppu_attr> attr{}; // Shared module attributes
mutable rx::EnumBitSet<ppu_attr> attr{}; // Shared module attributes
std::string cache{}; // Cache file path
std::vector<ppu_reloc> relocs{}; // Relocations
std::vector<ppu_segment> segs{}; // Segments

View file

@ -70,7 +70,7 @@ enum class ppu_exec_bit : u64
set_call_history,
use_feed_data,
__bitset_enum_max
bitset_last
};
using enum ppu_exec_bit;
@ -80,7 +80,7 @@ template <ppu_exec_bit... Flags0>
struct ppu_exec_select
{
template <ppu_exec_bit Flag, ppu_exec_bit... Flags, typename F>
static ppu_intrp_func_t select(bs_t<ppu_exec_bit> selected, F func)
static ppu_intrp_func_t select(rx::EnumBitSet<ppu_exec_bit> selected, F func)
{
// Make sure there is no flag duplication, otherwise skip flag
if constexpr (((Flags0 != Flag) && ...))
@ -97,7 +97,7 @@ struct ppu_exec_select
}
template <typename F>
static ppu_intrp_func_t select(bs_t<ppu_exec_bit>, F func)
static ppu_intrp_func_t select(rx::EnumBitSet<ppu_exec_bit>, F func)
{
// Instantiate interpreter function with required set of flags
return func.template operator()<Flags0...>();
@ -107,7 +107,7 @@ struct ppu_exec_select
static auto select()
{
#ifndef __INTELLISENSE__
return [](bs_t<ppu_exec_bit> selected, auto func)
return [](rx::EnumBitSet<ppu_exec_bit> selected, auto func)
{
return ppu_exec_select::select<Flags1...>(selected, func);
};
@ -2350,7 +2350,7 @@ template <u32 Count>
struct VSLDOI
{
template <ppu_exec_bit... Flags>
static auto select(bs_t<ppu_exec_bit> selected, auto func)
static auto select(rx::EnumBitSet<ppu_exec_bit> selected, auto func)
{
return ppu_exec_select<>::select<Flags...>(selected, func);
}
@ -3891,7 +3891,7 @@ template <u32 N>
struct MFOCRF
{
template <ppu_exec_bit... Flags>
static auto select(bs_t<ppu_exec_bit> selected, auto func)
static auto select(rx::EnumBitSet<ppu_exec_bit> selected, auto func)
{
return ppu_exec_select<>::select<Flags...>(selected, func);
}
@ -7547,7 +7547,7 @@ struct ppu_interpreter_t
ppu_interpreter_rt_base::ppu_interpreter_rt_base() noexcept
{
// Obtain required set of flags from settings
bs_t<ppu_exec_bit> selected{};
rx::EnumBitSet<ppu_exec_bit> selected{};
if (g_cfg.core.ppu_set_sat_bit)
selected += set_sat;
if (g_cfg.core.ppu_use_nj_bit)

View file

@ -3254,7 +3254,7 @@ bool ppu_load_rel_exec(const ppu_rel_object& elf)
std::stable_sort(shdrs.begin(), shdrs.end(), [](auto& a, auto& b) -> bool
{
const bs_t<sh_flag> flags_a_has = a->sh_flags() - b->sh_flags();
const rx::EnumBitSet<sh_flag> flags_a_has = a->sh_flags() - b->sh_flags();
return flags_a_has.all_of(sh_flag::shf_execinstr);
});

View file

@ -1261,7 +1261,7 @@ static void ppu_break(ppu_thread& ppu, ppu_opcode_t, be_t<u32>* this_op, ppu_int
ppu.cia = old_cia;
// Pause
ppu.state.atomic_op([&](bs_t<cpu_flag>& state)
ppu.state.atomic_op([&](rx::EnumBitSet<cpu_flag>& state)
{
if (pause_all)
state += cpu_flag::dbg_global_pause;
@ -2423,7 +2423,7 @@ void ppu_thread::cpu_on_stop()
}
}
void ppu_thread::cpu_wait(bs_t<cpu_flag> old)
void ppu_thread::cpu_wait(rx::EnumBitSet<cpu_flag> old)
{
// Meanwhile while waiting, notify SPU waiters
if (u32 addr = res_notify)
@ -2781,7 +2781,7 @@ ppu_thread::ppu_thread(utils::serial& ar)
void ppu_thread::save(utils::serial& ar)
{
// For debugging purposes, load this as soon as this function enters
const bs_t<cpu_flag> state_flags = state;
const rx::EnumBitSet<cpu_flag> state_flags = state;
USING_SERIALIZATION_VERSION(ppu);
@ -5421,10 +5421,10 @@ bool ppu_initialize(const ppu_module<lv2_obj>& info, bool check_only, u64 file_s
accurate_nj_mode,
contains_symbol_resolver,
__bitset_enum_max
bitset_last
};
be_t<bs_t<ppu_settings>> settings{};
be_t<rx::EnumBitSet<ppu_settings>> settings{};
#if !defined(_WIN32) && !defined(__APPLE__)
settings += ppu_settings::platform_bit;

View file

@ -151,7 +151,7 @@ public:
virtual void cpu_task() override final;
virtual void cpu_sleep() override;
virtual void cpu_on_stop() override;
virtual void cpu_wait(bs_t<cpu_flag> old) override;
virtual void cpu_wait(rx::EnumBitSet<cpu_flag> old) override;
virtual ~ppu_thread() override;
SAVESTATE_INIT_POS(3);

View file

@ -34,7 +34,7 @@ class PPUTranslator final : public cpu_translator
u64 m_addr = 0;
// Function attributes
bs_t<ppu_attr> m_attr{};
rx::EnumBitSet<ppu_attr> m_attr{};
// Relocation info
const ppu_segment* m_reloc = nullptr;

View file

@ -2920,7 +2920,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
}
// Weak constant propagation context (for guessing branch targets)
std::array<bs_t<vf>, 128> vflags{};
std::array<rx::EnumBitSet<vf>, 128> vflags{};
// Associated constant values for 32-bit preferred slot
std::array<u32, 128> values;
@ -5596,7 +5596,7 @@ spu_program spu_recompiler_base::analyse(const be_t<u32>* ls, u32 entry_point, s
pos = ra.origin;
}
const bs_t<vf> flag = (ra.flag & rb.flag) - vf::is_null;
const rx::EnumBitSet<vf> flag = (ra.flag & rb.flag) - vf::is_null;
vregs[reg] = reg_state_t{flag, value, flag & vf::is_const ? u32{umax} : reg_state_t::alloc_tag(), 0, 0, pos};
};

View file

@ -34,7 +34,7 @@ enum class spu_exec_bit : u64
{
use_dfma,
__bitset_enum_max
bitset_last
};
using enum spu_exec_bit;
@ -43,7 +43,7 @@ template <spu_exec_bit... Flags0>
struct spu_exec_select
{
template <spu_exec_bit Flag, spu_exec_bit... Flags, typename F>
static spu_intrp_func_t select(bs_t<spu_exec_bit> selected, F func)
static spu_intrp_func_t select(rx::EnumBitSet<spu_exec_bit> selected, F func)
{
// Make sure there is no flag duplication, otherwise skip flag
if constexpr (((Flags0 != Flag) && ...))
@ -60,7 +60,7 @@ struct spu_exec_select
}
template <typename F>
static spu_intrp_func_t select(bs_t<spu_exec_bit>, F func)
static spu_intrp_func_t select(rx::EnumBitSet<spu_exec_bit>, F func)
{
// Instantiate interpreter function with required set of flags
return func.template operator()<Flags0...>();
@ -3131,7 +3131,7 @@ struct spu_interpreter_t
spu_interpreter_rt_base::spu_interpreter_rt_base() noexcept
{
// Obtain required set of flags from settings
bs_t<spu_exec_bit> selected{};
rx::EnumBitSet<spu_exec_bit> selected{};
if (g_cfg.core.use_accurate_dfma)
selected += use_dfma;

View file

@ -198,12 +198,12 @@ public:
is_rel,
is_null,
__bitset_enum_max
bitset_last
};
struct reg_state_t
{
bs_t<vf> flag{+vf::is_null};
rx::EnumBitSet<vf> flag{+vf::is_null};
u32 value{};
u32 tag = umax;
u32 known_ones{};

View file

@ -2027,7 +2027,7 @@ void spu_thread::cpu_work()
if (!work_left)
{
// No more pending work
state.atomic_op([](bs_t<cpu_flag>& flags)
state.atomic_op([](rx::EnumBitSet<cpu_flag>& flags)
{
if (flags & cpu_flag::pending_recheck)
{
@ -7010,7 +7010,7 @@ bool spu_thread::stop_and_signal(u32 code)
{
if (thread)
{
thread->state.fetch_op([](bs_t<cpu_flag>& flags)
thread->state.fetch_op([](rx::EnumBitSet<cpu_flag>& flags)
{
if (flags & cpu_flag::stop)
{

View file

@ -1,7 +1,7 @@
#include "stdafx.h"
#include "GDB.h"
#include "util/bit_set.h"
#include "rx/EnumBitSet.hpp"
#include "util/logs.hpp"
#include "util/StrUtil.h"
#include "Emu/Memory/vm.h"
@ -823,7 +823,7 @@ bool gdb_thread::cmd_vcont(gdb_cmd& cmd)
if (ppu)
{
bs_t<cpu_flag> add_flags{};
rx::EnumBitSet<cpu_flag> add_flags{};
if (cmd.data[1] == 's')
{

View file

@ -435,7 +435,7 @@ namespace vm
bool temporary_unlock(cpu_thread& cpu) noexcept
{
bs_t<cpu_flag> add_state = cpu_flag::wait;
rx::EnumBitSet<cpu_flag> add_state = cpu_flag::wait;
if (g_tls_locked && g_tls_locked->compare_and_swap_test(&cpu, nullptr))
{

View file

@ -850,7 +850,7 @@ namespace rsx
on_exit();
}
void thread::cpu_wait(bs_t<cpu_flag> old)
void thread::cpu_wait(rx::EnumBitSet<cpu_flag> old)
{
if (external_interrupt_lock)
{

View file

@ -188,7 +188,7 @@ namespace rsx
u32 get_fifo_cmd() const;
void dump_regs(std::string&, std::any& custom_data) const override;
void cpu_wait(bs_t<cpu_flag> old) override;
void cpu_wait(rx::EnumBitSet<cpu_flag> old) override;
static constexpr u32 id_base = 0x5555'5555; // See get_current_cpu_thread()

View file

@ -2654,7 +2654,7 @@ void Emulator::FinalizeRunRequest()
{
const bool autostart = !m_ar || !!g_cfg.misc.autostart;
bs_t<cpu_flag> add_flags = cpu_flag::dbg_global_pause;
rx::EnumBitSet<cpu_flag> add_flags = cpu_flag::dbg_global_pause;
if (autostart)
{
@ -2663,7 +2663,7 @@ void Emulator::FinalizeRunRequest()
auto spu_select = [&](u32, spu_thread& spu)
{
bs_t<cpu_flag> sub_flags = cpu_flag::stop;
rx::EnumBitSet<cpu_flag> sub_flags = cpu_flag::stop;
if (spu.group && spu.index == spu.group->waiter_spu_index)
{
@ -3605,7 +3605,7 @@ void Emulator::Kill(bool allow_autoexit, bool savestate, savestate_stage* save_s
set_progress_message("Finalizing File");
bs_t<SaveStateExtentionFlags1> extension_flags{SaveStateExtentionFlags1::SupportsMenuOpenResume};
rx::EnumBitSet<SaveStateExtentionFlags1> extension_flags{SaveStateExtentionFlags1::SupportsMenuOpenResume};
if (g_fxo->get<SysutilMenuOpenStatus>().active)
{

View file

@ -4,7 +4,7 @@
#include "util/types.hpp"
#include "util/atomic.hpp"
#include "util/shared_ptr.hpp"
#include "util/bit_set.h"
#include "rx/EnumBitSet.hpp"
#include "config_mode.h"
#include "games_config.h"
#include <functional>
@ -188,10 +188,10 @@ class Emulator final
SupportsMenuOpenResume,
ShouldCloseMenu,
__bitset_enum_max,
bitset_last,
};
bs_t<SaveStateExtentionFlags1> m_savestate_extension_flags1{};
rx::EnumBitSet<SaveStateExtentionFlags1> m_savestate_extension_flags1{};
public:
static constexpr std::string_view game_id_boot_prefix = "%RPCS3_GAMEID%:";

View file

@ -2,7 +2,7 @@
#include "util/types.hpp"
#include "util/File.h"
#include "util/bit_set.h"
#include "rx/EnumBitSet.hpp"
#include "util/endian.hpp"
#include <span>
@ -56,10 +56,10 @@ enum class sh_flag : u32
shf_alloc,
shf_execinstr,
__bitset_enum_max
bitset_last
};
constexpr bool is_memorizable_section(sec_type type, bs_t<sh_flag> flags)
constexpr bool is_memorizable_section(sec_type type, rx::EnumBitSet<sh_flag> flags)
{
switch (type)
{
@ -184,9 +184,9 @@ struct elf_shdr
en_t<sz_t> sh_addralign;
en_t<sz_t> sh_entsize;
bs_t<sh_flag> sh_flags() const
rx::EnumBitSet<sh_flag> sh_flags() const
{
return std::bit_cast<bs_t<sh_flag>>(static_cast<u32>(+_sh_flags));
return std::bit_cast<rx::EnumBitSet<sh_flag>>(static_cast<u32>(+_sh_flags));
}
};
@ -218,7 +218,7 @@ enum class elf_opt : u32
no_sections, // Don't load shdrs
no_data, // Load phdrs without data
__bitset_enum_max
bitset_last
};
// ELF loading error
@ -266,12 +266,12 @@ public:
public:
elf_object() = default;
elf_object(const fs::file& stream, u64 offset = 0, bs_t<elf_opt> opts = {})
elf_object(const fs::file& stream, u64 offset = 0, rx::EnumBitSet<elf_opt> opts = {})
{
open(stream, offset, opts);
}
elf_error open(const fs::file& stream, u64 offset = 0, bs_t<elf_opt> opts = {})
elf_error open(const fs::file& stream, u64 offset = 0, rx::EnumBitSet<elf_opt> opts = {})
{
highest_offset = 0;

View file

@ -146,7 +146,7 @@ bool iso_dev::statfs(const std::string& path, fs::device_stat& info)
return true;
}
std::unique_ptr<fs::file_base> iso_dev::open(const std::string& path, bs_t<fs::open_mode> mode)
std::unique_ptr<fs::file_base> iso_dev::open(const std::string& path, rx::EnumBitSet<fs::open_mode> mode)
{
if (mode & fs::write)
{

View file

@ -230,7 +230,7 @@ public:
bool stat(const std::string& path, fs::stat_t& info) override;
bool statfs(const std::string& path, fs::device_stat& info) override;
std::unique_ptr<fs::file_base> open(const std::string& path, bs_t<fs::open_mode> mode) override;
std::unique_ptr<fs::file_base> open(const std::string& path, rx::EnumBitSet<fs::open_mode> mode) override;
std::unique_ptr<fs::dir_base> open_dir(const std::string& path) override;
private:

View file

@ -1635,7 +1635,7 @@ void fs::sync()
fmt::throw_exception("Stream overflow.");
}
fs::file::file(const std::string& path, bs_t<open_mode> mode)
fs::file::file(const std::string& path, rx::EnumBitSet<open_mode> mode)
{
if (path.empty())
{

View file

@ -3,7 +3,7 @@
#include "util/serialization.hpp"
#include "util/types.hpp"
#include "util/shared_ptr.hpp"
#include "bit_set.h"
#include "rx/EnumBitSet.hpp"
#include <memory>
#include <string>
@ -35,7 +35,7 @@ namespace fs
unread,
isfile,
__bitset_enum_max
bitset_last
};
constexpr auto read = +open_mode::read; // Enable reading
@ -174,7 +174,7 @@ namespace fs
virtual bool trunc(const std::string& path, u64 length);
virtual bool utime(const std::string& path, s64 atime, s64 mtime);
virtual std::unique_ptr<file_base> open(const std::string& path, bs_t<open_mode> mode) = 0;
virtual std::unique_ptr<file_base> open(const std::string& path, rx::EnumBitSet<open_mode> mode) = 0;
virtual std::unique_ptr<dir_base> open_dir(const std::string& path) = 0;
};
@ -258,7 +258,7 @@ namespace fs
file() = default;
// Open file with specified mode
explicit file(const std::string& path, bs_t<open_mode> mode = ::fs::read);
explicit file(const std::string& path, rx::EnumBitSet<open_mode> mode = ::fs::read);
static file from_native_handle(native_handle handle);
@ -903,7 +903,7 @@ namespace fs
}
template <bool Flush = false, typename... Args>
bool write_file(const std::string& path, bs_t<fs::open_mode> mode, const Args&... args)
bool write_file(const std::string& path, rx::EnumBitSet<fs::open_mode> mode, const Args&... args)
{
// Always use write flag, remove read flag
if (fs::file f{path, mode + fs::write - fs::read})

175
rpcs3/util/atomic_bit_set.h Normal file
View file

@ -0,0 +1,175 @@
#pragma once
/*
This header implements bs_t<> class for scoped enum types (enum class).
To enable bs_t<>, enum scope must contain `bitset_last` entry.
enum class flagzz : u32
{
flag1, // Bit indices start from zero
flag2,
bitset_last // It must be the last value
};
This also enables helper operators for this enum type.
Examples:
`+flagzz::flag1` - unary `+` operator convert flagzz value to bs_t<flagzz>
`flagzz::flag1 + flagzz::flag2` - bitset union
`flagzz::flag1 - flagzz::flag2` - bitset difference
Intersection (&) and symmetric difference (^) is also available.
*/
#include "rx/EnumBitSet.hpp"
#include "util/atomic.hpp"
#include "util/StrFmt.h"
template <typename T>
concept BitSetEnum = std::is_enum_v<T> && requires(T x) {
T::bitset_last;
};
// Atomic bitset specialization with optimized operations
template <BitSetEnum T>
class atomic_bs_t : public atomic_t<rx::EnumBitSet<T>>
{
// Corresponding bitset type
using bs_t = rx::EnumBitSet<T>;
// Base class
using base = atomic_t<rx::EnumBitSet<T>>;
// Use underlying m_data
using base::m_data;
public:
// Underlying type
using underlying_type = typename bs_t::underlying_type;
atomic_bs_t() = default;
atomic_bs_t(const atomic_bs_t&) = delete;
atomic_bs_t& operator=(const atomic_bs_t&) = delete;
explicit constexpr atomic_bs_t(bs_t value)
: base(value)
{
}
explicit constexpr atomic_bs_t(T bit)
: base(bit)
{
}
using base::operator bs_t;
explicit operator bool() const
{
return static_cast<bool>(base::load());
}
explicit operator underlying_type() const
{
return static_cast<underlying_type>(base::load());
}
bs_t operator+() const
{
return base::load();
}
bs_t fetch_add(const bs_t& rhs)
{
return bs_t::fromUnderlying(atomic_storage<underlying_type>::fetch_or(m_data.raw(), rhs.toUnderlying()));
}
bs_t add_fetch(const bs_t& rhs)
{
return bs_t::fromUnderlying(atomic_storage<underlying_type>::or_fetch(m_data.raw(), rhs.toUnderlying()));
}
bs_t operator+=(const bs_t& rhs)
{
return add_fetch(rhs);
}
bs_t fetch_sub(const bs_t& rhs)
{
return bs_t::fromUnderlying(atomic_storage<underlying_type>::fetch_and(m_data.raw(), ~rhs.toUnderlying()));
}
bs_t sub_fetch(const bs_t& rhs)
{
return bs_t::fromUnderlying(atomic_storage<underlying_type>::and_fetch(m_data.raw(), ~rhs.toUnderlying()));
}
bs_t operator-=(const bs_t& rhs)
{
return sub_fetch(rhs);
}
bs_t fetch_and(const bs_t& rhs)
{
return bs_t::fromUnderlying(atomic_storage<underlying_type>::fetch_and(m_data.raw(), rhs.toUnderlying()));
}
bs_t and_fetch(const bs_t& rhs)
{
return bs_t::fromUnderlying(atomic_storage<underlying_type>::and_fetch(m_data.raw(), rhs.toUnderlying()));
}
bs_t operator&=(const bs_t& rhs)
{
return and_fetch(rhs);
}
bs_t fetch_xor(const bs_t& rhs)
{
return bs_t::fromUnderlying(atomic_storage<underlying_type>::fetch_xor(m_data.raw(), rhs.toUnderlying()));
}
bs_t xor_fetch(const bs_t& rhs)
{
return bs_t::fromUnderlying(atomic_storage<underlying_type>::xor_fetch(m_data.raw(), rhs.toUnderlying()));
}
bs_t operator^=(const bs_t& rhs)
{
return xor_fetch(rhs);
}
auto fetch_or(const bs_t&) = delete;
auto or_fetch(const bs_t&) = delete;
auto operator|=(const bs_t&) = delete;
bool test_and_set(T rhs)
{
return atomic_storage<underlying_type>::bts(m_data.raw(), static_cast<uint>(static_cast<underlying_type>(rhs)));
}
bool test_and_reset(T rhs)
{
return atomic_storage<underlying_type>::btr(m_data.raw(), static_cast<uint>(static_cast<underlying_type>(rhs)));
}
bool test_and_invert(T rhs)
{
return atomic_storage<underlying_type>::btc(m_data.raw(), static_cast<uint>(static_cast<underlying_type>(rhs)));
}
bool bit_test_set(uint bit) = delete;
bool bit_test_reset(uint bit) = delete;
bool bit_test_invert(uint bit) = delete;
bool all_of(bs_t arg) const
{
return base::load().all_of(arg);
}
bool none_of(bs_t arg) const
{
return base::load().none_of(arg);
}
};

View file

@ -1,404 +0,0 @@
#pragma once
/*
This header implements bs_t<> class for scoped enum types (enum class).
To enable bs_t<>, enum scope must contain `__bitset_enum_max` entry.
enum class flagzz : u32
{
flag1, // Bit indices start from zero
flag2,
__bitset_enum_max // It must be the last value
};
This also enables helper operators for this enum type.
Examples:
`+flagzz::flag1` - unary `+` operator convert flagzz value to bs_t<flagzz>
`flagzz::flag1 + flagzz::flag2` - bitset union
`flagzz::flag1 - flagzz::flag2` - bitset difference
Intersection (&) and symmetric difference (^) is also available.
*/
#include "util/types.hpp"
#include "util/atomic.hpp"
#include "util/StrFmt.h"
template <typename T>
concept BitSetEnum = std::is_enum_v<T> && requires(T x) {
T::__bitset_enum_max;
};
template <BitSetEnum T>
class atomic_bs_t;
// Bitset type for enum class with available bits [0, T::__bitset_enum_max)
template <BitSetEnum T>
class bs_t final
{
public:
// Underlying type
using under = std::underlying_type_t<T>;
ENABLE_BITWISE_SERIALIZATION;
private:
// Underlying value
under m_data;
friend class atomic_bs_t<T>;
// Value constructor
constexpr explicit bs_t(int, under data) noexcept
: m_data(data)
{
}
public:
static constexpr usz bitmax = sizeof(T) * 8;
static constexpr usz bitsize = static_cast<under>(T::__bitset_enum_max);
static_assert(std::is_enum_v<T>, "bs_t<> error: invalid type (must be enum)");
static_assert(bitsize <= bitmax, "bs_t<> error: invalid __bitset_enum_max");
static_assert(bitsize != bitmax || std::is_unsigned_v<under>, "bs_t<> error: invalid __bitset_enum_max (sign bit)");
// Helper function
static constexpr under shift(T value)
{
return static_cast<under>(1) << static_cast<under>(value);
}
bs_t() = default;
// Construct from a single bit
constexpr bs_t(T bit) noexcept
: m_data(shift(bit))
{
}
// Test for empty bitset
constexpr explicit operator bool() const noexcept
{
return m_data != 0;
}
// Extract underlying data
constexpr explicit operator under() const noexcept
{
return m_data;
}
// Copy
constexpr bs_t operator+() const
{
return *this;
}
constexpr bs_t& operator+=(bs_t rhs)
{
m_data |= static_cast<under>(rhs);
return *this;
}
constexpr bs_t& operator-=(bs_t rhs)
{
m_data &= ~static_cast<under>(rhs);
return *this;
}
constexpr bs_t& operator&=(bs_t rhs)
{
m_data &= static_cast<under>(rhs);
return *this;
}
constexpr bs_t& operator^=(bs_t rhs)
{
m_data ^= static_cast<under>(rhs);
return *this;
}
friend constexpr bs_t operator+(bs_t lhs, bs_t rhs)
{
return bs_t(0, lhs.m_data | rhs.m_data);
}
friend constexpr bs_t operator-(bs_t lhs, bs_t rhs)
{
return bs_t(0, lhs.m_data & ~rhs.m_data);
}
friend constexpr bs_t operator&(bs_t lhs, bs_t rhs)
{
return bs_t(0, lhs.m_data & rhs.m_data);
}
friend constexpr bs_t operator^(bs_t lhs, bs_t rhs)
{
return bs_t(0, lhs.m_data ^ rhs.m_data);
}
constexpr bool operator==(bs_t 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 all_of(bs_t arg) const
{
return (m_data & arg.m_data) == arg.m_data;
}
constexpr bool none_of(bs_t arg) const
{
return (m_data & arg.m_data) == 0;
}
};
// Unary '+' operator: promote plain enum value to bitset value
template <BitSetEnum T>
constexpr bs_t<T> operator+(T bit)
{
return bs_t<T>(bit);
}
// Binary '+' operator: bitset union
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<bs_t<T>, U>)
constexpr bs_t<T> operator+(T lhs, const U& rhs)
{
return bs_t<T>(lhs) + bs_t<T>(rhs);
}
// Binary '+' operator: bitset union
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<bs_t<T>, U> && !std::is_enum_v<U>)
constexpr bs_t<T> operator+(const U& lhs, T rhs)
{
return bs_t<T>(lhs) + bs_t<T>(rhs);
}
// Binary '-' operator: bitset difference
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<bs_t<T>, U>)
constexpr bs_t<T> operator-(T lhs, const U& rhs)
{
return bs_t<T>(lhs) - bs_t<T>(rhs);
}
// Binary '-' operator: bitset difference
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<bs_t<T>, U> && !std::is_enum_v<U>)
constexpr bs_t<T> operator-(const U& lhs, T rhs)
{
return bs_t<T>(lhs) - bs_t<T>(rhs);
}
// Binary '&' operator: bitset intersection
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<bs_t<T>, U>)
constexpr bs_t<T> operator&(T lhs, const U& rhs)
{
return bs_t<T>(lhs) & bs_t<T>(rhs);
}
// Binary '&' operator: bitset intersection
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<bs_t<T>, U> && !std::is_enum_v<U>)
constexpr bs_t<T> operator&(const U& lhs, T rhs)
{
return bs_t<T>(lhs) & bs_t<T>(rhs);
}
// Binary '^' operator: bitset symmetric difference
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<bs_t<T>, U>)
constexpr bs_t<T> operator^(T lhs, const U& rhs)
{
return bs_t<T>(lhs) ^ bs_t<T>(rhs);
}
// Binary '^' operator: bitset symmetric difference
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<bs_t<T>, U> && !std::is_enum_v<U>)
constexpr bs_t<T> operator^(const U& lhs, T rhs)
{
return bs_t<T>(lhs) ^ bs_t<T>(rhs);
}
// Atomic bitset specialization with optimized operations
template <BitSetEnum T>
class atomic_bs_t : public atomic_t<::bs_t<T>>
{
// Corresponding bitset type
using bs_t = ::bs_t<T>;
// Base class
using base = atomic_t<::bs_t<T>>;
// Use underlying m_data
using base::m_data;
public:
// Underlying type
using under = typename bs_t::under;
atomic_bs_t() = default;
atomic_bs_t(const atomic_bs_t&) = delete;
atomic_bs_t& operator=(const atomic_bs_t&) = delete;
explicit constexpr atomic_bs_t(bs_t value)
: base(value)
{
}
explicit constexpr atomic_bs_t(T bit)
: base(bit)
{
}
using base::operator bs_t;
explicit operator bool() const
{
return static_cast<bool>(base::load());
}
explicit operator under() const
{
return static_cast<under>(base::load());
}
bs_t operator+() const
{
return base::load();
}
bs_t fetch_add(const bs_t& rhs)
{
return bs_t(0, atomic_storage<under>::fetch_or(m_data.m_data, rhs.m_data));
}
bs_t add_fetch(const bs_t& rhs)
{
return bs_t(0, atomic_storage<under>::or_fetch(m_data.m_data, rhs.m_data));
}
bs_t operator+=(const bs_t& rhs)
{
return add_fetch(rhs);
}
bs_t fetch_sub(const bs_t& rhs)
{
return bs_t(0, atomic_storage<under>::fetch_and(m_data.m_data, ~rhs.m_data));
}
bs_t sub_fetch(const bs_t& rhs)
{
return bs_t(0, atomic_storage<under>::and_fetch(m_data.m_data, ~rhs.m_data));
}
bs_t operator-=(const bs_t& rhs)
{
return sub_fetch(rhs);
}
bs_t fetch_and(const bs_t& rhs)
{
return bs_t(0, atomic_storage<under>::fetch_and(m_data.m_data, rhs.m_data));
}
bs_t and_fetch(const bs_t& rhs)
{
return bs_t(0, atomic_storage<under>::and_fetch(m_data.m_data, rhs.m_data));
}
bs_t operator&=(const bs_t& rhs)
{
return and_fetch(rhs);
}
bs_t fetch_xor(const bs_t& rhs)
{
return bs_t(0, atomic_storage<under>::fetch_xor(m_data.m_data, rhs.m_data));
}
bs_t xor_fetch(const bs_t& rhs)
{
return bs_t(0, atomic_storage<under>::xor_fetch(m_data.m_data, rhs.m_data));
}
bs_t operator^=(const bs_t& rhs)
{
return xor_fetch(rhs);
}
auto fetch_or(const bs_t&) = delete;
auto or_fetch(const bs_t&) = delete;
auto operator|=(const bs_t&) = delete;
bool test_and_set(T rhs)
{
return atomic_storage<under>::bts(m_data.m_data, static_cast<uint>(static_cast<under>(rhs)));
}
bool test_and_reset(T rhs)
{
return atomic_storage<under>::btr(m_data.m_data, static_cast<uint>(static_cast<under>(rhs)));
}
bool test_and_invert(T rhs)
{
return atomic_storage<under>::btc(m_data.m_data, static_cast<uint>(static_cast<under>(rhs)));
}
bool bit_test_set(uint bit) = delete;
bool bit_test_reset(uint bit) = delete;
bool bit_test_invert(uint bit) = delete;
bool all_of(bs_t arg) const
{
return base::load().all_of(arg);
}
bool none_of(bs_t arg) const
{
return base::load().none_of(arg);
}
};
template <typename T>
struct fmt_unveil<bs_t<T>>
{
// Format as is
using type = bs_t<T>;
static inline u64 get(const bs_t<T>& bitset)
{
return static_cast<std::underlying_type_t<T>>(bitset);
}
};

View file

@ -1,268 +0,0 @@
#pragma once
/*
This header implements bs_t<> class for scoped enum types (enum class).
To enable bs_t<>, enum scope must contain `__bitset_enum_max` entry.
enum class flagzz : u32
{
flag1, // Bit indices start from zero
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 "refl.hpp"
#include "types.hpp"
namespace rx {
template <typename T>
concept BitSetEnum =
std::is_enum_v<T> && requires(T x) { rx::fieldCount<T> > 0; };
template <BitSetEnum T> class BitSet;
namespace detail {
template <BitSetEnum T> class InvertedBitSet final {
using underlying_type = std::underlying_type_t<T>;
underlying_type m_data;
constexpr InvertedBitSet(underlying_type data) : m_data(data) {}
friend BitSet<T>;
};
} // namespace detail
// Bitset type for enum class with available bits [0, fieldCount)
template <BitSetEnum T> class BitSet final {
public:
// Underlying type
using underlying_type = std::underlying_type_t<T>;
private:
// Underlying value
underlying_type m_data;
// Value constructor
constexpr explicit BitSet(int, underlying_type data) noexcept
: m_data(data) {}
public:
static constexpr usz bitmax = sizeof(T) * 8;
static constexpr usz bitsize =
static_cast<underlying_type>(rx::fieldCount<T>);
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);
}
BitSet() = default;
// Construct from a single bit
constexpr BitSet(T bit) noexcept : m_data(shift(bit)) {}
// 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::InvertedBitSet<T> operator~() const { return {m_data}; }
constexpr BitSet &operator+=(BitSet rhs) {
m_data |= static_cast<underlying_type>(rhs);
return *this;
}
constexpr BitSet &operator-=(BitSet rhs) {
m_data &= ~static_cast<underlying_type>(rhs);
return *this;
}
constexpr BitSet without(BitSet rhs) const {
BitSet result = *this;
result.m_data &= ~static_cast<underlying_type>(rhs);
return result;
}
constexpr BitSet with(BitSet rhs) const {
BitSet result = *this;
result.m_data |= static_cast<underlying_type>(rhs);
return result;
}
constexpr BitSet &operator&=(BitSet rhs) {
m_data &= static_cast<underlying_type>(rhs);
return *this;
}
constexpr BitSet &operator^=(BitSet rhs) {
m_data ^= static_cast<underlying_type>(rhs);
return *this;
}
[[deprecated("Use operator|")]] friend constexpr BitSet
operator+(BitSet lhs, BitSet rhs) {
return BitSet(0, lhs.m_data | rhs.m_data);
}
friend constexpr BitSet operator-(BitSet lhs, BitSet rhs) {
return BitSet(0, lhs.m_data & ~rhs.m_data);
}
friend constexpr BitSet operator|(BitSet lhs, BitSet rhs) {
return BitSet(0, lhs.m_data | rhs.m_data);
}
friend constexpr BitSet operator&(BitSet lhs, BitSet rhs) {
return BitSet(0, lhs.m_data & rhs.m_data);
}
friend constexpr BitSet operator&(BitSet lhs, detail::InvertedBitSet<T> rhs) {
return BitSet(0, lhs.m_data & rhs.m_data);
}
friend constexpr BitSet operator^(BitSet lhs, BitSet rhs) {
return BitSet(0, lhs.m_data ^ rhs.m_data);
}
constexpr bool operator==(BitSet 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(BitSet arg) const { return (m_data & arg.m_data) != 0; }
constexpr bool all_of(BitSet arg) const {
return (m_data & arg.m_data) == arg.m_data;
}
constexpr bool none_of(BitSet arg) const {
return (m_data & arg.m_data) == 0;
}
};
namespace bitset {
// Unary '+' operator: promote plain enum value to bitset value
template <BitSetEnum T>
[[deprecated("Use toBitSet(bit)")]] constexpr BitSet<T> operator+(T bit) {
return BitSet<T>(bit);
}
template <BitSetEnum T> constexpr BitSet<T> toBitSet(T bit) {
return BitSet<T>(bit);
}
// Binary '+' operator: bitset union
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<BitSet<T>, U>)
[[deprecated("Use operator|")]] constexpr BitSet<T> operator+(T lhs,
const U &rhs) {
return BitSet<T>(lhs) | BitSet<T>(rhs);
}
// Binary '+' operator: bitset union
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<BitSet<T>, U> && !std::is_enum_v<U>)
[[deprecated("Use operator|")]] constexpr BitSet<T> operator+(const U &lhs,
T rhs) {
return BitSet<T>(lhs) | BitSet<T>(rhs);
}
// Binary '|' operator: bitset union
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<BitSet<T>, U>)
constexpr BitSet<T> operator|(T lhs, const U &rhs) {
return BitSet<T>(lhs) | BitSet<T>(rhs);
}
// Binary '|' operator: bitset union
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<BitSet<T>, U> && !std::is_enum_v<U>)
constexpr BitSet<T> operator|(const U &lhs, T rhs) {
return BitSet<T>(lhs) | BitSet<T>(rhs);
}
// Binary '-' operator: bitset difference
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<BitSet<T>, U>)
constexpr BitSet<T> operator-(T lhs, const U &rhs) {
return BitSet<T>(lhs) - BitSet<T>(rhs);
}
// Binary '-' operator: bitset difference
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<BitSet<T>, U> && !std::is_enum_v<U>)
constexpr BitSet<T> operator-(const U &lhs, T rhs) {
return BitSet<T>(lhs) - BitSet<T>(rhs);
}
// Binary '&' operator: bitset intersection
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<BitSet<T>, U>)
constexpr BitSet<T> operator&(T lhs, const U &rhs) {
return BitSet<T>(lhs) & BitSet<T>(rhs);
}
// Binary '&' operator: bitset intersection
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<BitSet<T>, U> && !std::is_enum_v<U>)
constexpr BitSet<T> operator&(const U &lhs, T rhs) {
return BitSet<T>(lhs) & BitSet<T>(rhs);
}
// Binary '&' operator: bitset intersection
template <BitSetEnum T, typename U>
constexpr BitSet<T> operator&(T lhs, detail::InvertedBitSet<T> rhs) {
return BitSet<T>(lhs) & rhs;
}
// Binary '^' operator: bitset symmetric difference
template <BitSetEnum T, typename U>
requires(std::is_constructible_v<BitSet<T>, U>)
constexpr BitSet<T> operator^(T lhs, const U &rhs) {
return BitSet<T>(lhs) ^ BitSet<T>(rhs);
}
// Binary '^' operator: bitset symmetric difference
template <typename U, BitSetEnum T>
requires(std::is_constructible_v<BitSet<T>, U> && !std::is_enum_v<U>)
constexpr BitSet<T> operator^(const U &lhs, T rhs) {
return BitSet<T>(lhs) ^ BitSet<T>(rhs);
}
} // namespace bitset
} // namespace rx
using namespace rx::bitset;

View file

@ -0,0 +1,287 @@
#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 "types.hpp"
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 usz bitmax = sizeof(T) * 8;
static constexpr usz 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);
}
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 {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^=(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;
}
underlying_type &raw() { return m_data; }
};
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);
}
// 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
using namespace rx::bitset;