kern: use util::BitFlagSet for capability flags

This commit is contained in:
Michael Scire 2021-09-27 11:37:21 -07:00
parent 273206f314
commit d00cec38b0
4 changed files with 94 additions and 118 deletions

View file

@ -23,8 +23,10 @@ namespace ams::kern {
class KCapabilities { class KCapabilities {
private: private:
static constexpr size_t SvcFlagCount = svc::NumSupervisorCalls / BITSIZEOF(u8); static constexpr size_t InterruptIdCount = 0x400;
static constexpr size_t IrqFlagCount = /* TODO */0x80;
struct InterruptFlagSetTag{};
using InterruptFlagSet = util::BitFlagSet<InterruptIdCount, InterruptFlagSetTag>;
enum class CapabilityType : u32 { enum class CapabilityType : u32 {
CorePriority = (1u << 3) - 1, CorePriority = (1u << 3) - 1,
@ -154,6 +156,7 @@ namespace ams::kern {
}; };
static const u32 PaddingInterruptId = 0x3FF; static const u32 PaddingInterruptId = 0x3FF;
static_assert(PaddingInterruptId < InterruptIdCount);
struct InterruptPair { struct InterruptPair {
using IdBits = Field<0, CapabilityId<CapabilityType::InterruptPair> + 1>; using IdBits = Field<0, CapabilityId<CapabilityType::InterruptPair> + 1>;
@ -200,8 +203,8 @@ namespace ams::kern {
CapabilityFlag<CapabilityType::HandleTable> | CapabilityFlag<CapabilityType::HandleTable> |
CapabilityFlag<CapabilityType::DebugFlags>; CapabilityFlag<CapabilityType::DebugFlags>;
private: private:
u8 m_svc_access_flags[SvcFlagCount]{}; svc::SvcAccessFlagSet m_svc_access_flags{};
u8 m_irq_access_flags[IrqFlagCount]{}; InterruptFlagSet m_irq_access_flags{};
u64 m_core_mask{}; u64 m_core_mask{};
u64 m_priority_mask{}; u64 m_priority_mask{};
util::BitPack32 m_debug_capabilities{0}; util::BitPack32 m_debug_capabilities{0};
@ -209,37 +212,19 @@ namespace ams::kern {
util::BitPack32 m_intended_kernel_version{0}; util::BitPack32 m_intended_kernel_version{0};
u32 m_program_type{}; u32 m_program_type{};
private: private:
static constexpr ALWAYS_INLINE void SetSvcAllowedImpl(u8 *data, u32 id) { constexpr bool SetSvcAllowed(u32 id) {
constexpr size_t BitsPerWord = BITSIZEOF(*data); if (AMS_LIKELY(id < m_svc_access_flags.GetCount())) {
MESOSPHERE_ASSERT(id < svc::SvcId_Count); m_svc_access_flags[id] = true;
data[id / BitsPerWord] |= (1ul << (id % BitsPerWord));
}
static constexpr ALWAYS_INLINE void ClearSvcAllowedImpl(u8 *data, u32 id) {
constexpr size_t BitsPerWord = BITSIZEOF(*data);
MESOSPHERE_ASSERT(id < svc::SvcId_Count);
data[id / BitsPerWord] &= ~(1ul << (id % BitsPerWord));
}
static constexpr ALWAYS_INLINE bool GetSvcAllowedImpl(const u8 *data, u32 id) {
constexpr size_t BitsPerWord = BITSIZEOF(*data);
MESOSPHERE_ASSERT(id < svc::SvcId_Count);
return (data[id / BitsPerWord] & (1ul << (id % BitsPerWord))) != 0;
}
bool SetSvcAllowed(u32 id) {
if (id < BITSIZEOF(m_svc_access_flags)) {
SetSvcAllowedImpl(m_svc_access_flags, id);
return true; return true;
} else { } else {
return false; return false;
} }
} }
bool SetInterruptPermitted(u32 id) { constexpr bool SetInterruptPermitted(u32 id) {
constexpr size_t BitsPerWord = BITSIZEOF(m_irq_access_flags[0]); constexpr size_t BitsPerWord = BITSIZEOF(m_irq_access_flags[0]);
if (id < BITSIZEOF(m_irq_access_flags)) { if (AMS_LIKELY(id < m_irq_access_flags.GetCount())) {
m_irq_access_flags[id / BitsPerWord] |= (1ul << (id % BitsPerWord)); m_irq_access_flags[id] = true;
return true; return true;
} else { } else {
return false; return false;
@ -271,94 +256,80 @@ namespace ams::kern {
constexpr s32 GetHandleTableSize() const { return m_handle_table_size; } constexpr s32 GetHandleTableSize() const { return m_handle_table_size; }
ALWAYS_INLINE void CopySvcPermissionsTo(KThread::StackParameters &sp) const { ALWAYS_INLINE void CopySvcPermissionsTo(KThread::StackParameters &sp) const {
static_assert(sizeof(m_svc_access_flags) == sizeof(sp.svc_permission));
/* Copy permissions. */ /* Copy permissions. */
std::memcpy(sp.svc_permission, m_svc_access_flags, sizeof(m_svc_access_flags)); sp.svc_access_flags = m_svc_access_flags;
/* Clear specific SVCs based on our state. */ /* Clear specific SVCs based on our state. */
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); sp.svc_access_flags[svc::SvcId_ReturnFromException] = false;
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_SynchronizePreemptionState); sp.svc_access_flags[svc::SvcId_SynchronizePreemptionState] = false;
if (sp.is_pinned) { if (sp.is_pinned) {
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo); sp.svc_access_flags[svc::SvcId_GetInfo] = false;
} }
} }
ALWAYS_INLINE void CopyPinnedSvcPermissionsTo(KThread::StackParameters &sp) const { ALWAYS_INLINE void CopyPinnedSvcPermissionsTo(KThread::StackParameters &sp) const {
static_assert(sizeof(m_svc_access_flags) == sizeof(sp.svc_permission));
/* Get whether we have access to return from exception. */ /* Get whether we have access to return from exception. */
const bool return_from_exception = GetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); const bool return_from_exception = sp.svc_access_flags[svc::SvcId_ReturnFromException];
/* Clear all permissions. */ /* Clear all permissions. */
std::memset(sp.svc_permission, 0, sizeof(m_svc_access_flags)); sp.svc_access_flags.Reset();
/* Set SynchronizePreemptionState if allowed. */ /* Set SynchronizePreemptionState if allowed. */
if (GetSvcAllowedImpl(m_svc_access_flags, svc::SvcId_SynchronizePreemptionState)) { if (m_svc_access_flags[svc::SvcId_SynchronizePreemptionState]) {
SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_SynchronizePreemptionState); sp.svc_access_flags[svc::SvcId_SynchronizePreemptionState] = true;
} }
/* If we previously had ReturnFromException, potentially grant it and GetInfo. */ /* If we previously had ReturnFromException, potentially grant it and GetInfo. */
if (return_from_exception) { if (return_from_exception) {
/* Set ReturnFromException if allowed. */ /* Set ReturnFromException (guaranteed allowed, if we're here). */
if (GetSvcAllowedImpl(m_svc_access_flags, svc::SvcId_ReturnFromException)) { sp.svc_access_flags[svc::SvcId_ReturnFromException] = true;
SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException);
}
/* Set GetInfo if allowed. */ /* Set GetInfo if allowed. */
if (GetSvcAllowedImpl(m_svc_access_flags, svc::SvcId_GetInfo)) { if (m_svc_access_flags[svc::SvcId_GetInfo]) {
SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo); sp.svc_access_flags[svc::SvcId_GetInfo] = true;
} }
} }
} }
ALWAYS_INLINE void CopyUnpinnedSvcPermissionsTo(KThread::StackParameters &sp) const { ALWAYS_INLINE void CopyUnpinnedSvcPermissionsTo(KThread::StackParameters &sp) const {
static_assert(sizeof(m_svc_access_flags) == sizeof(sp.svc_permission));
/* Get whether we have access to return from exception. */ /* Get whether we have access to return from exception. */
const bool return_from_exception = GetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); const bool return_from_exception = sp.svc_access_flags[svc::SvcId_ReturnFromException];
/* Copy permissions. */ /* Copy permissions. */
std::memcpy(sp.svc_permission, m_svc_access_flags, sizeof(m_svc_access_flags)); sp.svc_access_flags = m_svc_access_flags;
/* Clear/Set specific SVCs based on our state. */ /* Clear specific SVCs based on our state. */
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); sp.svc_access_flags[svc::SvcId_SynchronizePreemptionState] = false;
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_SynchronizePreemptionState);
if (return_from_exception) { if (!return_from_exception) {
SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); sp.svc_access_flags[svc::SvcId_ReturnFromException] = false;
} }
} }
ALWAYS_INLINE void CopyEnterExceptionSvcPermissionsTo(KThread::StackParameters &sp) const { ALWAYS_INLINE void CopyEnterExceptionSvcPermissionsTo(KThread::StackParameters &sp) const {
static_assert(sizeof(m_svc_access_flags) == sizeof(sp.svc_permission));
/* Set ReturnFromException if allowed. */ /* Set ReturnFromException if allowed. */
if (GetSvcAllowedImpl(m_svc_access_flags, svc::SvcId_ReturnFromException)) { if (m_svc_access_flags[svc::SvcId_ReturnFromException]) {
SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); sp.svc_access_flags[svc::SvcId_ReturnFromException] = true;
} }
/* Set GetInfo if allowed. */ /* Set GetInfo if allowed. */
if (GetSvcAllowedImpl(m_svc_access_flags, svc::SvcId_GetInfo)) { if (m_svc_access_flags[svc::SvcId_GetInfo]) {
SetSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo); sp.svc_access_flags[svc::SvcId_GetInfo] = true;
} }
} }
ALWAYS_INLINE void CopyLeaveExceptionSvcPermissionsTo(KThread::StackParameters &sp) const { ALWAYS_INLINE void CopyLeaveExceptionSvcPermissionsTo(KThread::StackParameters &sp) const {
static_assert(sizeof(m_svc_access_flags) == sizeof(sp.svc_permission));
/* Clear ReturnFromException. */ /* Clear ReturnFromException. */
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_ReturnFromException); sp.svc_access_flags[svc::SvcId_ReturnFromException] = false;
/* If pinned, clear GetInfo. */ /* If pinned, clear GetInfo. */
if (sp.is_pinned) { if (sp.is_pinned) {
ClearSvcAllowedImpl(sp.svc_permission, svc::SvcId_GetInfo); sp.svc_access_flags[svc::SvcId_GetInfo] = false;
} }
} }
constexpr bool IsPermittedInterrupt(u32 id) const { constexpr bool IsPermittedInterrupt(u32 id) const {
constexpr size_t BitsPerWord = BITSIZEOF(m_irq_access_flags[0]); return (id < m_irq_access_flags.GetCount()) && m_irq_access_flags[id];
if (id < BITSIZEOF(m_irq_access_flags)) {
return (m_irq_access_flags[id / BitsPerWord] & (1ul << (id % BitsPerWord))) != 0;
} else {
return false;
}
} }
constexpr bool IsPermittedDebug() const { constexpr bool IsPermittedDebug() const {

View file

@ -14,6 +14,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
#include <mesosphere/kern_svc.hpp>
#include <mesosphere/kern_slab_helpers.hpp> #include <mesosphere/kern_slab_helpers.hpp>
#include <mesosphere/kern_k_synchronization_object.hpp> #include <mesosphere/kern_k_synchronization_object.hpp>
#include <mesosphere/kern_k_affinity_mask.hpp> #include <mesosphere/kern_k_affinity_mask.hpp>
@ -84,7 +85,7 @@ namespace ams::kern {
}; };
struct StackParameters { struct StackParameters {
alignas(0x10) u8 svc_permission[0x18]; alignas(0x10) svc::SvcAccessFlagSet svc_access_flags;
KThreadContext *context; KThreadContext *context;
KThread *cur_thread; KThread *cur_thread;
s16 disable_count; s16 disable_count;
@ -100,7 +101,7 @@ namespace ams::kern {
static_assert(alignof(StackParameters) == 0x10); static_assert(alignof(StackParameters) == 0x10);
static_assert(sizeof(StackParameters) == THREAD_STACK_PARAMETERS_SIZE); static_assert(sizeof(StackParameters) == THREAD_STACK_PARAMETERS_SIZE);
static_assert(__builtin_offsetof(StackParameters, svc_permission) == THREAD_STACK_PARAMETERS_SVC_PERMISSION); static_assert(__builtin_offsetof(StackParameters, svc_access_flags) == THREAD_STACK_PARAMETERS_SVC_PERMISSION);
static_assert(__builtin_offsetof(StackParameters, context) == THREAD_STACK_PARAMETERS_CONTEXT); static_assert(__builtin_offsetof(StackParameters, context) == THREAD_STACK_PARAMETERS_CONTEXT);
static_assert(__builtin_offsetof(StackParameters, cur_thread) == THREAD_STACK_PARAMETERS_CUR_THREAD); static_assert(__builtin_offsetof(StackParameters, cur_thread) == THREAD_STACK_PARAMETERS_CUR_THREAD);
static_assert(__builtin_offsetof(StackParameters, disable_count) == THREAD_STACK_PARAMETERS_DISABLE_COUNT); static_assert(__builtin_offsetof(StackParameters, disable_count) == THREAD_STACK_PARAMETERS_DISABLE_COUNT);

View file

@ -45,5 +45,8 @@ namespace ams::kern::svc {
#undef AMS_KERN_SVC_DECLARE_PROTOTYPE_64 #undef AMS_KERN_SVC_DECLARE_PROTOTYPE_64
#undef AMS_KERN_SVC_DECLARE_PROTOTYPE_64_FROM_32 #undef AMS_KERN_SVC_DECLARE_PROTOTYPE_64_FROM_32
struct SvcAccessFlagSetTag{};
using SvcAccessFlagSet = util::BitFlagSet<NumSupervisorCalls, SvcAccessFlagSetTag>;
} }

View file

@ -25,35 +25,35 @@ namespace ams::util {
namespace impl { namespace impl {
template<size_t Count, typename Storage> template<size_t Count, typename Storage>
constexpr void NegateImpl(Storage arr[]) { constexpr ALWAYS_INLINE void NegateImpl(Storage arr[]) {
for (size_t i = 0; i < Count; i++) { for (size_t i = 0; i < Count; i++) {
arr[i] = ~arr[i]; arr[i] = ~arr[i];
} }
} }
template<size_t Count, typename Storage> template<size_t Count, typename Storage>
constexpr void AndImpl(Storage dst[], const Storage src[]) { constexpr ALWAYS_INLINE void AndImpl(Storage dst[], const Storage src[]) {
for (size_t i = 0; i < Count; i++) { for (size_t i = 0; i < Count; i++) {
dst[i] &= src[i]; dst[i] &= src[i];
} }
} }
template<size_t Count, typename Storage> template<size_t Count, typename Storage>
constexpr void OrImpl(Storage dst[], const Storage src[]) { constexpr ALWAYS_INLINE void OrImpl(Storage dst[], const Storage src[]) {
for (size_t i = 0; i < Count; i++) { for (size_t i = 0; i < Count; i++) {
dst[i] |= src[i]; dst[i] |= src[i];
} }
} }
template<size_t Count, typename Storage> template<size_t Count, typename Storage>
constexpr void XorImpl(Storage dst[], const Storage src[]) { constexpr ALWAYS_INLINE void XorImpl(Storage dst[], const Storage src[]) {
for (size_t i = 0; i < Count; i++) { for (size_t i = 0; i < Count; i++) {
dst[i] ^= src[i]; dst[i] ^= src[i];
} }
} }
template<size_t Count, typename Storage> template<size_t Count, typename Storage>
constexpr bool IsEqual(const Storage lhs[], const Storage rhs[]) { constexpr ALWAYS_INLINE bool IsEqual(const Storage lhs[], const Storage rhs[]) {
for (size_t i = 0; i < Count; i++) { for (size_t i = 0; i < Count; i++) {
if (lhs[i] != rhs[i]) { if (lhs[i] != rhs[i]) {
return false; return false;
@ -63,7 +63,7 @@ namespace ams::util {
} }
template<size_t Count, typename Storage> template<size_t Count, typename Storage>
constexpr bool IsAnySet(const Storage arr[]) { constexpr ALWAYS_INLINE bool IsAnySet(const Storage arr[]) {
for (size_t i = 0; i < Count; i++) { for (size_t i = 0; i < Count; i++) {
if (arr[i]) { if (arr[i]) {
return true; return true;
@ -73,7 +73,7 @@ namespace ams::util {
} }
template<size_t Count, typename Storage> template<size_t Count, typename Storage>
constexpr int PopCount(const Storage arr[]) { constexpr ALWAYS_INLINE int PopCount(const Storage arr[]) {
int count = 0; int count = 0;
for (size_t i = 0; i < Count; i++) { for (size_t i = 0; i < Count; i++) {
count += ::ams::util::PopCount(arr[i]); count += ::ams::util::PopCount(arr[i]);
@ -91,7 +91,7 @@ namespace ams::util {
static constexpr size_t StorageCount = util::AlignUp(N, StorageBitCount) / StorageBitCount; static constexpr size_t StorageCount = util::AlignUp(N, StorageBitCount) / StorageBitCount;
Storage _storage[StorageCount]; Storage _storage[StorageCount];
private: private:
constexpr BitFlagSet<N, T> &SetImpl(s32 idx, Storage mask, bool en) { constexpr ALWAYS_INLINE BitFlagSet<N, T> &SetImpl(s32 idx, Storage mask, bool en) {
if (en) { if (en) {
this->_storage[idx] |= mask; this->_storage[idx] |= mask;
} else { } else {
@ -100,13 +100,13 @@ namespace ams::util {
return *this; return *this;
} }
constexpr bool TestImpl(s32 idx, Storage mask) const { return (this->_storage[idx] & mask) != 0; } constexpr ALWAYS_INLINE bool TestImpl(s32 idx, Storage mask) const { return (this->_storage[idx] & mask) != 0; }
constexpr void Truncate() { TruncateIf(std::integral_constant<bool, N % StorageBitCount != 0>{}); } constexpr ALWAYS_INLINE void Truncate() { TruncateIf(std::integral_constant<bool, N % StorageBitCount != 0>{}); }
constexpr void TruncateIf(std::true_type) { this->_storage[StorageCount - 1] &= MakeStorageMask(N) - 1; } constexpr ALWAYS_INLINE void TruncateIf(std::true_type) { this->_storage[StorageCount - 1] &= MakeStorageMask(N) - 1; }
constexpr void TruncateIf(std::false_type) { /* ... */ } constexpr ALWAYS_INLINE void TruncateIf(std::false_type) { /* ... */ }
static constexpr s32 GetStorageIndex(s32 idx) { return idx / StorageBitCount; } static constexpr ALWAYS_INLINE s32 GetStorageIndex(s32 idx) { return idx / StorageBitCount; }
static constexpr Storage MakeStorageMask(s32 idx) { return static_cast<Storage>(1) << (idx % StorageBitCount); } static constexpr ALWAYS_INLINE Storage MakeStorageMask(s32 idx) { return static_cast<Storage>(1) << (idx % StorageBitCount); }
public: public:
class Reference { class Reference {
friend struct BitFlagSet<N, T>; friend struct BitFlagSet<N, T>;
@ -114,14 +114,14 @@ namespace ams::util {
BitFlagSet<N, T> *set; BitFlagSet<N, T> *set;
s32 idx; s32 idx;
private: private:
constexpr Reference() : set(nullptr), idx(0) { /* ... */ } constexpr ALWAYS_INLINE Reference() : set(nullptr), idx(0) { /* ... */ }
constexpr Reference(BitFlagSet<N, T> &s, s32 i) : set(std::addressof(s)), idx(i) { /* ... */ } constexpr ALWAYS_INLINE Reference(BitFlagSet<N, T> &s, s32 i) : set(std::addressof(s)), idx(i) { /* ... */ }
public: public:
constexpr Reference &operator=(bool en) { this->set->Set(this->idx, en); return *this; } constexpr ALWAYS_INLINE Reference &operator=(bool en) { this->set->Set(this->idx, en); return *this; }
constexpr Reference &operator=(const Reference &r) { this->set->Set(this->idx, r); return *this; } constexpr ALWAYS_INLINE Reference &operator=(const Reference &r) { this->set->Set(this->idx, r); return *this; }
constexpr Reference &Negate() { this->set->Negate(this->idx); return *this; } constexpr ALWAYS_INLINE Reference &Negate() { this->set->Negate(this->idx); return *this; }
constexpr operator bool() const { return this->set->Test(this->idx); } constexpr ALWAYS_INLINE operator bool() const { return this->set->Test(this->idx); }
constexpr bool operator~() const { return !this->set->Test(this->idx); } constexpr ALWAYS_INLINE bool operator~() const { return !this->set->Test(this->idx); }
}; };
template<s32 _Index> template<s32 _Index>
@ -142,45 +142,46 @@ namespace ams::util {
}; };
template<typename FlagType> template<typename FlagType>
constexpr bool Test() const { return this->TestImpl(FlagType::StorageIndex, FlagType::StorageMask); } constexpr ALWAYS_INLINE bool Test() const { return this->TestImpl(FlagType::StorageIndex, FlagType::StorageMask); }
constexpr bool Test(s32 idx) const { return this->TestImpl(GetStorageIndex(idx), MakeStorageMask(idx)); } constexpr ALWAYS_INLINE bool Test(s32 idx) const { return this->TestImpl(GetStorageIndex(idx), MakeStorageMask(idx)); }
template<typename FlagType> template<typename FlagType>
constexpr BitFlagSet<N, T> &Set(bool en = true) { return this->SetImpl(FlagType::StorageIndex, FlagType::StorageMask, en); } constexpr ALWAYS_INLINE BitFlagSet<N, T> &Set(bool en = true) { return this->SetImpl(FlagType::StorageIndex, FlagType::StorageMask, en); }
constexpr BitFlagSet<N, T> &Set(s32 idx, bool en = true) { return this->SetImpl(GetStorageIndex(idx), MakeStorageMask(idx), en); } constexpr ALWAYS_INLINE BitFlagSet<N, T> &Set(s32 idx, bool en = true) { return this->SetImpl(GetStorageIndex(idx), MakeStorageMask(idx), en); }
constexpr BitFlagSet<N, T> &Set() { std::memset(this->_storage, ~0, sizeof(this->_storage)); this->Truncate(); return *this; } constexpr ALWAYS_INLINE BitFlagSet<N, T> &Set() { std::memset(this->_storage, ~0, sizeof(this->_storage)); this->Truncate(); return *this; }
template<typename FlagType> template<typename FlagType>
constexpr BitFlagSet<N, T> &Reset() { return this->Set<FlagType>(false); } constexpr ALWAYS_INLINE BitFlagSet<N, T> &Reset() { return this->Set<FlagType>(false); }
constexpr BitFlagSet<N, T> &Reset(s32 idx) { return this->Set(idx, false); } constexpr ALWAYS_INLINE BitFlagSet<N, T> &Reset(s32 idx) { return this->Set(idx, false); }
constexpr BitFlagSet<N, T> &Reset() { std::memset(this->_storage, 0, sizeof(this->_storage)); this->Truncate(); return *this; } constexpr ALWAYS_INLINE BitFlagSet<N, T> &Reset() { std::memset(this->_storage, 0, sizeof(this->_storage)); this->Truncate(); return *this; }
template<typename FlagType> template<typename FlagType>
constexpr BitFlagSet<N, T> &Negate() { return this->Set<FlagType>(!this->Test<FlagType>()); } constexpr ALWAYS_INLINE BitFlagSet<N, T> &Negate() { return this->Set<FlagType>(!this->Test<FlagType>()); }
constexpr BitFlagSet<N, T> &Negate(s32 idx) { return this->Set(idx, !this->Test(idx)); } constexpr ALWAYS_INLINE BitFlagSet<N, T> &Negate(s32 idx) { return this->Set(idx, !this->Test(idx)); }
constexpr BitFlagSet<N, T> &Negate() { ams::util::impl::NegateImpl<StorageCount>(this->_storage); this->Truncate(); return *this; } constexpr ALWAYS_INLINE BitFlagSet<N, T> &Negate() { ams::util::impl::NegateImpl<StorageCount>(this->_storage); this->Truncate(); return *this; }
constexpr int GetCount() const { return static_cast<int>(N); } consteval static int GetCount() { return static_cast<int>(N); }
constexpr bool IsAnySet() const { return ams::util::impl::IsAnySet<StorageCount>(this->_storage); }
constexpr int PopCount() const { return ams::util::impl::PopCount<StorageCount>(this->_storage); }
constexpr bool IsAllSet() const { return this->PopCount() == this->GetCount(); }
constexpr bool IsAllOff() const { return !this->IsAnySet(); }
constexpr bool operator[](s32 idx) const { return this->Test(idx); } constexpr ALWAYS_INLINE bool IsAnySet() const { return ams::util::impl::IsAnySet<StorageCount>(this->_storage); }
constexpr Reference operator[](s32 idx) { return Reference(*this, idx); } constexpr ALWAYS_INLINE int PopCount() const { return ams::util::impl::PopCount<StorageCount>(this->_storage); }
constexpr ALWAYS_INLINE bool IsAllSet() const { return this->PopCount() == this->GetCount(); }
constexpr ALWAYS_INLINE bool IsAllOff() const { return !this->IsAnySet(); }
constexpr bool operator==(const BitFlagSet<N, T> &rhs) const { return ams::util::impl::IsEqual<StorageCount>(this->_storage, rhs._storage); } constexpr ALWAYS_INLINE bool operator[](s32 idx) const { return this->Test(idx); }
constexpr bool operator!=(const BitFlagSet<N, T> &rhs) const { return !(*this == rhs); } constexpr ALWAYS_INLINE Reference operator[](s32 idx) { return Reference(*this, idx); }
constexpr BitFlagSet<N, T> operator~() const { BitFlagSet<N, T> tmp = *this; return tmp.Negate(); } constexpr ALWAYS_INLINE bool operator==(const BitFlagSet<N, T> &rhs) const { return ams::util::impl::IsEqual<StorageCount>(this->_storage, rhs._storage); }
constexpr ALWAYS_INLINE bool operator!=(const BitFlagSet<N, T> &rhs) const { return !(*this == rhs); }
constexpr BitFlagSet<N, T> operator&(const BitFlagSet<N, T> &rhs) const { BitFlagSet<N, T> v = *this; v &= rhs; return v; } constexpr ALWAYS_INLINE BitFlagSet<N, T> operator~() const { BitFlagSet<N, T> tmp = *this; return tmp.Negate(); }
constexpr BitFlagSet<N, T> operator^(const BitFlagSet<N, T> &rhs) const { BitFlagSet<N, T> v = *this; v ^= rhs; return v; }
constexpr BitFlagSet<N, T> operator|(const BitFlagSet<N, T> &rhs) const { BitFlagSet<N, T> v = *this; v |= rhs; return v; }
constexpr BitFlagSet<N, T> &operator&=(const BitFlagSet<N, T> &rhs) { ams::util::impl::AndImpl<StorageCount>(this->_storage, rhs._storage); return *this; } constexpr ALWAYS_INLINE BitFlagSet<N, T> operator&(const BitFlagSet<N, T> &rhs) const { BitFlagSet<N, T> v = *this; v &= rhs; return v; }
constexpr BitFlagSet<N, T> &operator^=(const BitFlagSet<N, T> &rhs) { ams::util::impl::XorImpl<StorageCount>(this->_storage, rhs._storage); return *this; } constexpr ALWAYS_INLINE BitFlagSet<N, T> operator^(const BitFlagSet<N, T> &rhs) const { BitFlagSet<N, T> v = *this; v ^= rhs; return v; }
constexpr BitFlagSet<N, T> &operator|=(const BitFlagSet<N, T> &rhs) { ams::util::impl::OrImpl<StorageCount>(this->_storage, rhs._storage); return *this; } constexpr ALWAYS_INLINE BitFlagSet<N, T> operator|(const BitFlagSet<N, T> &rhs) const { BitFlagSet<N, T> v = *this; v |= rhs; return v; }
constexpr ALWAYS_INLINE BitFlagSet<N, T> &operator&=(const BitFlagSet<N, T> &rhs) { ams::util::impl::AndImpl<StorageCount>(this->_storage, rhs._storage); return *this; }
constexpr ALWAYS_INLINE BitFlagSet<N, T> &operator^=(const BitFlagSet<N, T> &rhs) { ams::util::impl::XorImpl<StorageCount>(this->_storage, rhs._storage); return *this; }
constexpr ALWAYS_INLINE BitFlagSet<N, T> &operator|=(const BitFlagSet<N, T> &rhs) { ams::util::impl::OrImpl<StorageCount>(this->_storage, rhs._storage); return *this; }
}; };
template<size_t N, typename T> template<size_t N, typename T>