From 01ba6cf610641f1937092b469843b14ebc2a5962 Mon Sep 17 00:00:00 2001 From: Fernando Sahmkow Date: Sun, 4 Feb 2024 14:44:17 +0100 Subject: [PATCH] Common: Introduce Range Sets --- src/common/CMakeLists.txt | 2 + src/common/range_sets.h | 73 ++++++++++ src/common/range_sets.inc | 279 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 354 insertions(+) create mode 100644 src/common/range_sets.h create mode 100644 src/common/range_sets.inc diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index bf3f3b7814..c19af2ab89 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -107,6 +107,8 @@ add_library(common STATIC quaternion.h range_map.h range_mutex.h + range_sets.h + range_sets.inc reader_writer_queue.h ring_buffer.h ${CMAKE_CURRENT_BINARY_DIR}/scm_rev.cpp diff --git a/src/common/range_sets.h b/src/common/range_sets.h new file mode 100644 index 0000000000..f4ee00fec7 --- /dev/null +++ b/src/common/range_sets.h @@ -0,0 +1,73 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include + +#include "common/common_types.h" + +namespace Common { + +template +class RangeSet { +public: + RangeSet(); + ~RangeSet(); + + RangeSet(RangeSet const&) = delete; + RangeSet& operator=(RangeSet const&) = delete; + + RangeSet(RangeSet&& other); + RangeSet& operator=(RangeSet&& other); + + void Add(AddressType base_address, size_t size); + void Subtract(AddressType base_address, size_t size); + void Clear(); + bool Empty() const; + + template + void ForEach(Func&& func) const; + + template + void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const; + +private: + struct RangeSetImpl; + std::unique_ptr m_impl; +}; + +template +class SplitRangeSet { +public: + SplitRangeSet(); + ~SplitRangeSet(); + + SplitRangeSet(SplitRangeSet const&) = delete; + SplitRangeSet& operator=(SplitRangeSet const&) = delete; + + SplitRangeSet(SplitRangeSet&& other); + SplitRangeSet& operator=(SplitRangeSet&& other); + + void Add(AddressType base_address, size_t size); + void Subtract(AddressType base_address, size_t size); + + template + void Subtract(AddressType base_address, size_t size, Func&& on_delete); + + void DeleteAll(AddressType base_address, size_t size); + void Clear(); + bool Empty() const; + + template + void ForEach(Func&& func) const; + + template + void ForEachInRange(AddressType device_addr, size_t size, Func&& func) const; + +private: + struct SplitRangeSetImpl; + std::unique_ptr m_impl; +}; + +} // namespace Common \ No newline at end of file diff --git a/src/common/range_sets.inc b/src/common/range_sets.inc new file mode 100644 index 0000000000..fa55a68fbb --- /dev/null +++ b/src/common/range_sets.inc @@ -0,0 +1,279 @@ +// SPDX-FileCopyrightText: 2024 yuzu Emulator Project +// SPDX-License-Identifier: GPL-2.0-or-later + +#pragma once + +#include +#include + +#define BOOST_NO_MT +#include +#undef BOOST_NO_MT +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common/range_sets.h" + +namespace boost { +template +class fast_pool_allocator; +} + +namespace Common { + +template +struct RangeSet::RangeSetImpl { + using IntervalSet = boost::icl::interval_set< + AddressType, std::less, ICL_INTERVAL_INSTANCE(ICL_INTERVAL_DEFAULT, AddressType, std::less), + boost::fast_pool_allocator>; + using IntervalType = typename IntervalSet::interval_type; + + RangeSetImpl() = default; + ~RangeSetImpl() = default; + + void Add(AddressType base_address, size_t size) { + AddressType end_address = base_address + static_cast(size); + IntervalType interval{base_address, end_address}; + m_ranges_set.add(interval); + } + + void Subtract(AddressType base_address, size_t size) { + AddressType end_address = base_address + static_cast(size); + IntervalType interval{base_address, end_address}; + m_ranges_set.subtract(interval); + } + + IntervalSet m_ranges_set; +}; + +template +struct SplitRangeSet::SplitRangeSetImpl { + + using IntervalSet = + boost::icl::split_interval_map; + using IntervalType = typename IntervalSet::interval_type; + + SplitRangeSetImpl() = default; + ~SplitRangeSetImpl() = default; + + void Add(AddressType base_address, size_t size) { + AddressType end_address = base_address + static_cast(size); + IntervalType interval{base_address, end_address}; + m_split_ranges_set += std::make_pair(interval, 1); + } + + template + void Subtract(AddressType base_address, size_t size, s32 amount, + [[maybe_unused]] Func&& on_delete) { + AddressType end_address = base_address + static_cast(size); + IntervalType interval{base_address, end_address}; + bool any_removals = false; + m_split_ranges_set += std::make_pair(interval, -amount); + do { + any_removals = false; + auto it = m_split_ranges_set.lower_bound(interval); + if (it == m_split_ranges_set.end()) { + return; + } + auto end_it = m_split_ranges_set.upper_bound(interval); + for (; it != end_it; it++) { + if (it->second <= 0) { + if constexpr (has_on_delete) { + if (it->second == 0) { + on_delete(it->first.lower(), it->first.upper()); + } + } + any_removals = true; + m_split_ranges_set.erase(it); + break; + } + } + } while (any_removals); + } + + IntervalSet m_split_ranges_set; +}; + +template +RangeSet::RangeSet() { + m_impl = std::make_unique::RangeSetImpl>(); +} + +template +RangeSet::~RangeSet() = default; + +template +RangeSet::RangeSet(RangeSet&& other) { + m_impl = std::make_unique::RangeSetImpl>(); + m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set); +} + +template +RangeSet& RangeSet::operator=(RangeSet&& other) { + m_impl->m_ranges_set = std::move(other.m_impl->m_ranges_set); +} + +template +void RangeSet::Add(AddressType base_address, size_t size) { + m_impl->Add(base_address, size); +} + +template +void RangeSet::Subtract(AddressType base_address, size_t size) { + m_impl->Subtract(base_address, size); +} + +template +void RangeSet::Clear() { + m_impl->m_ranges_set.clear(); +} + +template +bool RangeSet::Empty() const { + return m_impl->m_ranges_set.empty(); +} + +template +template +void RangeSet::ForEach(Func&& func) const { + if (m_impl->m_ranges_set.empty()) { + return; + } + auto it = m_impl->m_ranges_set.begin(); + auto end_it = m_impl->m_ranges_set.end(); + for (; it != end_it; it++) { + const AddressType inter_addr_end = it->upper(); + const AddressType inter_addr = it->lower(); + func(inter_addr, inter_addr_end); + } +} + +template +template +void RangeSet::ForEachInRange(AddressType base_addr, size_t size, Func&& func) const { + auto& range_set = m_impl->m_ranges_set; + const AddressType start_address = base_addr; + const AddressType end_address = start_address + size; + const RangeSetImpl::IntervalType search_interval{start_address, end_address}; + auto it = range_set.lower_bound(search_interval); + if (it == range_set.end()) { + return; + } + auto end_it = range_set.upper_bound(search_interval); + for (; it != end_it; it++) { + AddressType inter_addr_end = it->upper(); + AddressType inter_addr = it->lower(); + if (inter_addr_end > end_address) { + inter_addr_end = end_address; + } + if (inter_addr < start_address) { + inter_addr = start_address; + } + func(inter_addr, inter_addr_end); + } +} + +template +SplitRangeSet::SplitRangeSet() { + m_impl = std::make_unique::SplitRangeSetImpl>(); +} + +template +SplitRangeSet::~SplitRangeSet() = default; + +template +SplitRangeSet::SplitRangeSet(SplitRangeSet&& other) { + m_impl = std::make_unique::SplitRangeSetImpl>(); + m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set); +} + +template +SplitRangeSet& SplitRangeSet::operator=(SplitRangeSet&& other) { + m_impl->m_split_ranges_set = std::move(other.m_impl->m_split_ranges_set); +} + +template +void SplitRangeSet::Add(AddressType base_address, size_t size) { + m_impl->Add(base_address, size); +} + +template +void SplitRangeSet::Subtract(AddressType base_address, size_t size) { + m_impl->Subtract(base_address, size, 1, [](AddressType, AddressType) {}); +} + +template +template +void SplitRangeSet::Subtract(AddressType base_address, size_t size, Func&& on_delete) { + m_impl->Subtract(base_address, size, 1, on_delete); +} + +template +void SplitRangeSet::DeleteAll(AddressType base_address, size_t size) { + m_impl->Subtract(base_address, size, std::numeric_limits::max(), + [](AddressType, AddressType) {}); +} + +template +void SplitRangeSet::Clear() { + m_impl->m_split_ranges_set.clear(); +} + +template +bool SplitRangeSet::Empty() const { + return m_impl->m_split_ranges_set.empty(); +} + +template +template +void SplitRangeSet::ForEach(Func&& func) const { + if (m_impl->m_split_ranges_set.empty()) { + return; + } + auto it = m_impl->m_split_ranges_set.begin(); + auto end_it = m_impl->m_split_ranges_set.end(); + for (; it != end_it; it++) { + const AddressType inter_addr_end = it->first.upper(); + const AddressType inter_addr = it->first.lower(); + func(inter_addr, inter_addr_end, it->second); + } +} + +template +template +void SplitRangeSet::ForEachInRange(AddressType base_address, size_t size, + Func&& func) const { + auto& range_set = m_impl->m_split_ranges_set; + const AddressType start_address = base_address; + const AddressType end_address = start_address + size; + const SplitRangeSetImpl::IntervalType search_interval{start_address, end_address}; + auto it = range_set.lower_bound(search_interval); + if (it == range_set.end()) { + return; + } + auto end_it = range_set.upper_bound(search_interval); + for (; it != end_it; it++) { + auto& inter = it->first; + AddressType inter_addr_end = inter.upper(); + AddressType inter_addr = inter.lower(); + if (inter_addr_end > end_address) { + inter_addr_end = end_address; + } + if (inter_addr < start_address) { + inter_addr = start_address; + } + func(inter_addr, inter_addr_end, it->second); + } +} + +} // namespace Common \ No newline at end of file