diff --git a/libraries/libvapours/include/vapours/util.hpp b/libraries/libvapours/include/vapours/util.hpp index 70771d657..c7d5b8c2b 100644 --- a/libraries/libvapours/include/vapours/util.hpp +++ b/libraries/libvapours/include/vapours/util.hpp @@ -46,6 +46,8 @@ #include #include +#include + #ifdef ATMOSPHERE_IS_STRATOSPHERE #include #endif diff --git a/libraries/libvapours/include/vapours/util/impl/util_available_index_finder.hpp b/libraries/libvapours/include/vapours/util/impl/util_available_index_finder.hpp new file mode 100644 index 000000000..8bc7967b2 --- /dev/null +++ b/libraries/libvapours/include/vapours/util/impl/util_available_index_finder.hpp @@ -0,0 +1,220 @@ +/* + * 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 + +namespace ams::util::impl { + + class AvailableIndexFinder { + private: + static constexpr int MaxDepthOfBox = 5; + + struct BitFlags64 { + private: + u64 m_data; + public: + constexpr ALWAYS_INLINE bool GetFlag(int index) const { + AMS_ASSERT(index < 64); + return ((m_data >> index) & UINT64_C(1)) != 0; + } + + constexpr ALWAYS_INLINE void SetFlag(int index) { + AMS_ASSERT(index < 64); + m_data |= (UINT64_C(1) << index); + } + + constexpr ALWAYS_INLINE void ClearFlag(int index) { + AMS_ASSERT(index < 64); + m_data &= ~(UINT64_C(1) << index); + } + + constexpr ALWAYS_INLINE bool IsFull() const { + return m_data == ~(UINT64_C(0)); + } + + constexpr ALWAYS_INLINE int FindIndexOfBitZero() const { + AMS_ASSERT(!this->IsFull()); + return __builtin_ctzll(~m_data); + } + }; + private: + int *m_p_current_index; + int *m_p_map_index; + void *m_buffer; + int m_depth; + BitFlags64 *m_flags; + private: + static constexpr int Pow64(int e) { + switch (e) { + case 0: return 0x1; + case 1: return 0x40; + case 2: return 0x1000; + case 3: return 0x40000; + case 4: return 0x1000000; + case 5: return 0x40000000; + default: return -1; + } + } + + static constexpr u64 Roundup64(u64 value) { + return (value + (64 - 1)) / 64; + } + + static constexpr size_t GetRequiredNumOfBox(int depth, size_t num_elements) { + if (depth == 1) { + return Roundup64(num_elements); + } else if (depth == 2) { + return Roundup64(num_elements) + Pow64(0); + } else if (depth == 3) { + return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2); + } else if (depth == 4) { + return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2) + Pow64(3); + } else if (depth == 5) { + return Roundup64(num_elements) + Pow64(0) + Pow64(1) + Pow64(2) + Pow64(3) + Pow64(4); + } else { + return 0; + } + } + + static constexpr int CalcOffset(int *arr, int depth) { + int offset = 0; + for (auto i = 0; i < depth; ++i) { + offset += Pow64(i); + } + for (auto i = 0; i < depth; ++i) { + offset += Pow64(i) - arr[depth - 1 - i]; + } + return offset; + } + public: + static consteval int GetSignature() { + return static_cast(util::ReverseFourCC<'B', 'I', 'T', 'S'>::Code); + } + + static constexpr int GetNeedDepth(size_t num_elements) { + if (num_elements <= 0x40) { + return 1; + } else if (num_elements <= 0x1000) { + return 2; + } else if (num_elements <= 0x40000) { + return 3; + } else if (num_elements <= 0x1000000) { + return 4; + } else if (num_elements <= 0x40000000) { + return 5; + } else { + return -1; + } + } + + static constexpr size_t GetRequiredMemorySize(size_t num_elements) { + return sizeof(BitFlags64) * GetRequiredNumOfBox(GetNeedDepth(num_elements), num_elements); + } + public: + void Initialize(int *cur, int *map, u8 *buf) { + const size_t num_elements = static_cast(*map); + AMS_ASSERT(num_elements <= static_cast(Pow64(MaxDepthOfBox - 1))); + + /* Set fields. */ + m_p_current_index = cur; + m_p_map_index = map; + m_buffer = buf; + m_depth = GetNeedDepth(num_elements); + + /* Validate fields. */ + AMS_ASSERT(m_depth > 0); + + /* Setup memory. */ + std::memset(m_buffer, 0, GetRequiredMemorySize(num_elements)); + m_flags = reinterpret_cast(m_buffer); + } + + int AcquireIndex() { + /* Validate pre-conditions. */ + AMS_ASSERT(*m_p_current_index < *m_p_map_index); + + /* Build up arrays. */ + int table[MaxDepthOfBox]; + BitFlags64 *pos[MaxDepthOfBox]; + for (auto i = 0; i < m_depth; ++i) { + /* Determine the position. */ + pos[i] = std::addressof(m_flags[CalcOffset(table, i)]); + + /* Set table entry. */ + table[i] = pos[i]->FindIndexOfBitZero(); + AMS_ASSERT(table[i] != BITSIZEOF(u64)); + } + + /* Validate that the index is not acquired. */ + AMS_ASSERT(!pos[m_depth - 1]->GetFlag(table[m_depth - 1])); + + /* Acquire the index. */ + pos[m_depth - 1]->SetFlag(table[m_depth - 1]); + + /* Validate that the index was acquired. */ + AMS_ASSERT(pos[m_depth - 1]->GetFlag(table[m_depth - 1])); + + /* Update tracking flags. */ + for (auto i = m_depth - 1; i > 0; --i) { + if (pos[i]->IsFull()) { + pos[i - 1]->SetFlag(table[i - 1]); + } + } + + /* Calculate the index we acquired. */ + int index = 0, pow = 0; + for (auto i = m_depth; i > 0; --i, ++pow) { + index += Pow64(pow) * table[i - 1]; + } + + /* Increment current index. */ + ++(*m_p_current_index); + + return index; + } + + void ReleaseIndex(int index) { + /* Convert index to table. */ + int table[MaxDepthOfBox]; + for (auto i = 0; i < m_depth; ++i) { + table[m_depth - 1 - i] = index % BITSIZEOF(u64); + index /= BITSIZEOF(u64); + } + + /* Build up arrays. */ + BitFlags64 *pos[MaxDepthOfBox]; + for (auto i = 0; i < m_depth; ++i) { + /* Determine the position. */ + pos[i] = std::addressof(m_flags[CalcOffset(table, i)]); + } + + /* Validate that the flag is set. */ + AMS_ASSERT(pos[m_depth - 1]->GetFlag(table[m_depth - 1])); + + /* Clear the flags. */ + for (auto i = m_depth - 1; i >= 0; --i) { + pos[i]->ClearFlag(table[i]); + } + + /* Decrement current index. */ + --(*m_p_current_index); + } + }; + +}