From b73895df0afd525dbfdc2c8ef27030fb49742a2d Mon Sep 17 00:00:00 2001 From: Michael Scire Date: Wed, 8 Apr 2020 08:39:36 -0700 Subject: [PATCH] util: add bitflagset --- libraries/libvapours/include/vapours/util.hpp | 1 + .../include/vapours/util/util_bitflagset.hpp | 196 ++++++++++++++++++ 2 files changed, 197 insertions(+) create mode 100644 libraries/libvapours/include/vapours/util/util_bitflagset.hpp diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp index f3dbdb62d..f6d8a68c2 100644 --- a/libraries/libvapours/include/vapours/util.hpp +++ b/libraries/libvapours/include/vapours/util.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include diff --git a/libraries/libvapours/include/vapours/util/util_bitflagset.hpp b/libraries/libvapours/include/vapours/util/util_bitflagset.hpp new file mode 100644 index 000000000..2c421afd6 --- /dev/null +++ b/libraries/libvapours/include/vapours/util/util_bitflagset.hpp @@ -0,0 +1,196 @@ +/* + * 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) const { ams::util::impl::AndImpl(this->_storage, rhs._storage); return *this; } + constexpr BitFlagSet &operator^=(const BitFlagSet &rhs) const { ams::util::impl::XorImpl(this->_storage, rhs._storage); return *this; } + constexpr BitFlagSet &operator|=(const BitFlagSet &rhs) const { 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(); } + +}