/* * Copyright (c) 2018-2020 Atmosphère-NX * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU General Public License, * version 2, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for * more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #pragma once #include #include #include #include namespace ams::util { namespace impl { template constexpr void NegateImpl(Storage arr[]) { for (size_t i = 0; i < Count; i++) { arr[i] = ~arr[i]; } } template constexpr void AndImpl(Storage dst[], const Storage src[]) { for (size_t i = 0; i < Count; i++) { dst[i] &= src[i]; } } template constexpr void OrImpl(Storage dst[], const Storage src[]) { for (size_t i = 0; i < Count; i++) { dst[i] |= src[i]; } } template constexpr void XorImpl(Storage dst[], const Storage src[]) { for (size_t i = 0; i < Count; i++) { dst[i] ^= src[i]; } } template constexpr bool IsEqual(const Storage lhs[], const Storage rhs[]) { for (size_t i = 0; i < Count; i++) { if (lhs[i] != rhs[i]) { return false; } } return true; } template constexpr bool IsAnySet(const Storage arr[]) { for (size_t i = 0; i < Count; i++) { if (arr[i]) { return true; } } return false; } template constexpr int PopCount(const Storage arr[]) { int count = 0; for (size_t i = 0; i < Count; i++) { count += PopCount(arr[i]); } return count; } } template struct BitFlagSet { static_assert(N > 0); using Storage = typename std::conditional::type; static constexpr size_t StorageBitCount = BITSIZEOF(Storage); static constexpr size_t StorageCount = util::AlignUp(N, StorageBitCount) / StorageBitCount; Storage _storage[StorageCount]; private: constexpr BitFlagSet &SetImpl(s32 idx, Storage mask, bool en) { if (en) { this->_storage[idx] |= mask; } else { this->_storage[idx] &= ~mask; } return *this; } constexpr bool TestImpl(s32 idx, Storage mask) const { return (this->_storage[idx] & mask) != 0; } constexpr void Truncate() { TruncateIf(std::integral_constant{}); } constexpr void TruncateIf(std::true_type) { this->_storage[StorageCount - 1] &= MakeStorageMask(N) - 1; } constexpr void TruncateIf(std::false_type) { /* ... */ } static constexpr s32 GetStorageIndex(s32 idx) { return idx / StorageBitCount; } static constexpr Storage MakeStorageMask(s32 idx) { return static_cast(1) << (idx % StorageBitCount); } public: class Reference { friend struct BitFlagSet; private: BitFlagSet *set; s32 idx; private: constexpr Reference() : set(nullptr), idx(0) { /* ... */ } constexpr Reference(BitFlagSet &s, s32 i) : set(std::addressof(s)), idx(i) { /* ... */ } public: constexpr 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 Reference &Negate() { this->set->Negate(this->idx); return *this; } constexpr operator bool() const { return this->set->Test(this->idx); } constexpr bool operator~() const { return !this->set->Test(this->idx); } }; template struct Flag { static_assert(_Index < static_cast(N)); friend struct BitFlagSet; static constexpr s32 Index = _Index; static const BitFlagSet Mask; private: static constexpr s32 StorageIndex = Index / StorageBitCount; static constexpr Storage StorageMask = static_cast(1) << (Index % StorageBitCount); template struct SingleStorageTrait { static_assert(StorageCount == 1); using Type = Storage; }; }; template constexpr bool Test() const { return this->TestImpl(FlagType::StorageIndex, FlagType::StorageMask); } constexpr bool Test(s32 idx) const { return this->TestImpl(GetStorageIndex(idx), MakeStorageMask(idx)); } template constexpr BitFlagSet &Set(bool en = true) { return this->SetImpl(FlagType::StorageIndex, FlagType::StorageMask, en); } constexpr BitFlagSet &Set(s32 idx, bool en = true) { return this->SetImpl(GetStorageIndex(idx), MakeStorageMask(idx), en); } constexpr BitFlagSet &Set() { std::memset(this->_storage, ~0, sizeof(this->_storage)); this->Truncate(); return *this; } template constexpr BitFlagSet &Reset() { return this->Set(false); } constexpr BitFlagSet &Reset(s32 idx) { return this->Set(idx, false); } constexpr BitFlagSet &Reset() { std::memset(this->_storage, 0, sizeof(this->_storage)); this->Truncate(); return *this; } template constexpr BitFlagSet &Negate() { return this->Set(!this->Test()); } constexpr BitFlagSet &Negate(s32 idx) { return this->Set(idx, !this->Test(idx)); } constexpr BitFlagSet &Negate() { ams::util::impl::NegateImpl(this->_storage); this->Truncate(); return *this; } constexpr int GetCount() const { return static_cast(N); } constexpr bool IsAnySet() const { return ams::util::impl::IsAnySet(this->_storage); } constexpr int PopCount() const { return ams::util::impl::PopCount(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 Reference operator[](s32 idx) { return Reference(*this, idx); } constexpr bool operator==(const BitFlagSet &rhs) const { return ams::util::impl::IsEqual(this->_storage, rhs._storage); } constexpr bool operator!=(const BitFlagSet &rhs) const { return !(*this == rhs); } constexpr BitFlagSet operator~() const { BitFlagSet tmp = *this; return tmp.Negate(); } constexpr BitFlagSet operator&(const BitFlagSet &rhs) const { BitFlagSet v = *this; v &= rhs; return v; } constexpr BitFlagSet operator^(const BitFlagSet &rhs) const { BitFlagSet v = *this; v ^= rhs; return v; } constexpr BitFlagSet operator|(const BitFlagSet &rhs) const { BitFlagSet v = *this; v |= rhs; return v; } constexpr BitFlagSet &operator&=(const BitFlagSet &rhs) { ams::util::impl::AndImpl(this->_storage, rhs._storage); return *this; } constexpr BitFlagSet &operator^=(const BitFlagSet &rhs) { ams::util::impl::XorImpl(this->_storage, rhs._storage); return *this; } constexpr BitFlagSet &operator|=(const BitFlagSet &rhs) { ams::util::impl::OrImpl(this->_storage, rhs._storage); return *this; } }; template template constexpr inline const BitFlagSet BitFlagSet::Flag::Mask = { { static_cast::StorageCount>::Type>(1) << Index } }; template constexpr BitFlagSet MakeBitFlagSet() { return BitFlagSet{}; } template constexpr BitFlagSet MakeBitFlagSet() { return MakeBitFlagSet(); } }