diff options
Diffstat (limited to 'src/common/lru_cache.h')
-rw-r--r-- | src/common/lru_cache.h | 140 |
1 files changed, 140 insertions, 0 deletions
diff --git a/src/common/lru_cache.h b/src/common/lru_cache.h new file mode 100644 index 000000000..365488ba5 --- /dev/null +++ b/src/common/lru_cache.h @@ -0,0 +1,140 @@ +// Copyright 2021 yuzu Emulator Project +// Licensed under GPLv2+ or any later version +// Refer to the license.txt file included. + +#pragma once + +#include <deque> +#include <memory> +#include <type_traits> + +#include "common/common_types.h" + +namespace Common { + +template <class Traits> +class LeastRecentlyUsedCache { + using ObjectType = typename Traits::ObjectType; + using TickType = typename Traits::TickType; + + struct Item { + ObjectType obj; + TickType tick; + Item* next{}; + Item* prev{}; + }; + +public: + LeastRecentlyUsedCache() : first_item{}, last_item{} {} + ~LeastRecentlyUsedCache() = default; + + size_t Insert(ObjectType obj, TickType tick) { + const auto new_id = Build(); + auto& item = item_pool[new_id]; + item.obj = obj; + item.tick = tick; + Attach(item); + return new_id; + } + + void Touch(size_t id, TickType tick) { + auto& item = item_pool[id]; + if (item.tick >= tick) { + return; + } + item.tick = tick; + if (&item == last_item) { + return; + } + Detach(item); + Attach(item); + } + + void Free(size_t id) { + auto& item = item_pool[id]; + Detach(item); + item.prev = nullptr; + item.next = nullptr; + free_items.push_back(id); + } + + template <typename Func> + void ForEachItemBelow(TickType tick, Func&& func) { + static constexpr bool RETURNS_BOOL = + std::is_same_v<std::invoke_result<Func, ObjectType>, bool>; + Item* iterator = first_item; + while (iterator) { + if (static_cast<s64>(tick) - static_cast<s64>(iterator->tick) < 0) { + return; + } + Item* next = iterator->next; + if constexpr (RETURNS_BOOL) { + if (func(iterator->obj)) { + return; + } + } else { + func(iterator->obj); + } + iterator = next; + } + } + +private: + size_t Build() { + if (free_items.empty()) { + const size_t item_id = item_pool.size(); + auto& item = item_pool.emplace_back(); + item.next = nullptr; + item.prev = nullptr; + return item_id; + } + const size_t item_id = free_items.front(); + free_items.pop_front(); + auto& item = item_pool[item_id]; + item.next = nullptr; + item.prev = nullptr; + return item_id; + } + + void Attach(Item& item) { + if (!first_item) { + first_item = &item; + } + if (!last_item) { + last_item = &item; + } else { + item.prev = last_item; + last_item->next = &item; + item.next = nullptr; + last_item = &item; + } + } + + void Detach(Item& item) { + if (item.prev) { + item.prev->next = item.next; + } + if (item.next) { + item.next->prev = item.prev; + } + if (&item == first_item) { + first_item = item.next; + if (first_item) { + first_item->prev = nullptr; + } + } + if (&item == last_item) { + last_item = item.prev; + if (last_item) { + last_item->next = nullptr; + } + } + } + + std::deque<Item> item_pool; + std::deque<size_t> free_items; + Item* first_item{}; + Item* last_item{}; +}; + +} // namespace Common |