summaryrefslogtreecommitdiff
path: root/src/common/lru_cache.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/common/lru_cache.h')
-rw-r--r--src/common/lru_cache.h141
1 files changed, 141 insertions, 0 deletions
diff --git a/src/common/lru_cache.h b/src/common/lru_cache.h
new file mode 100644
index 000000000..048e9c3da
--- /dev/null
+++ b/src/common/lru_cache.h
@@ -0,0 +1,141 @@
1// Copyright 2021 yuzu Emulator Project
2// Licensed under GPLv2+ or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include <deque>
8#include <memory>
9#include <type_traits>
10
11#include "common/common_types.h"
12
13namespace Common {
14
15template <class Traits>
16class LeastRecentlyUsedCache {
17 using ObjectType = typename Traits::ObjectType;
18 using TickType = typename Traits::TickType;
19
20 struct Item {
21 ObjectType obj;
22 TickType tick;
23 Item* next{};
24 Item* prev{};
25 };
26
27public:
28 LeastRecentlyUsedCache() : first_item{}, last_item{} {}
29 ~LeastRecentlyUsedCache() = default;
30
31 size_t Insert(ObjectType obj, TickType tick) {
32 const auto new_id = build();
33 auto& item = item_pool[new_id];
34 item.obj = obj;
35 item.tick = tick;
36 attach(item);
37 return new_id;
38 }
39
40 void Touch(size_t id, TickType tick) {
41 auto& item = item_pool[id];
42 if (item.tick >= tick) {
43 return;
44 }
45 item.tick = tick;
46 if (&item == last_item) {
47 return;
48 }
49 detach(item);
50 attach(item);
51 }
52
53 void Free(size_t id) {
54 auto& item = item_pool[id];
55 detach(item);
56 item.prev = nullptr;
57 item.next = nullptr;
58 free_items.push_back(id);
59 }
60
61 template <typename Func>
62 void ForEachItemBelow(TickType tick, Func&& func) {
63 static constexpr bool RETURNS_BOOL =
64 std::is_same_v<std::invoke_result<Func, ObjectType>, bool>;
65 Item* iterator = first_item;
66 while (iterator) {
67 if (static_cast<s64>(tick) - static_cast<s64>(iterator->tick) < 0) {
68 return;
69 }
70 Item* next = iterator->next;
71 if constexpr (RETURNS_BOOL) {
72 if (func(iterator->obj)) {
73 return;
74 }
75 } else {
76 func(iterator->obj);
77 }
78 iterator = next;
79 }
80 }
81
82private:
83 size_t build() {
84 if (free_items.empty()) {
85 const size_t item_id = item_pool.size();
86 item_pool.emplace_back();
87 auto& item = item_pool[item_id];
88 item.next = nullptr;
89 item.prev = nullptr;
90 return item_id;
91 }
92 const size_t item_id = free_items.front();
93 free_items.pop_front();
94 auto& item = item_pool[item_id];
95 item.next = nullptr;
96 item.prev = nullptr;
97 return item_id;
98 }
99
100 void attach(Item& item) {
101 if (!first_item) {
102 first_item = &item;
103 }
104 if (!last_item) {
105 last_item = &item;
106 } else {
107 item.prev = last_item;
108 last_item->next = &item;
109 item.next = nullptr;
110 last_item = &item;
111 }
112 }
113
114 void detach(Item& item) {
115 if (item.prev) {
116 item.prev->next = item.next;
117 }
118 if (item.next) {
119 item.next->prev = item.prev;
120 }
121 if (&item == first_item) {
122 first_item = item.next;
123 if (first_item) {
124 first_item->prev = nullptr;
125 }
126 }
127 if (&item == last_item) {
128 last_item = item.prev;
129 if (last_item) {
130 last_item->next = nullptr;
131 }
132 }
133 }
134
135 std::deque<Item> item_pool;
136 std::deque<size_t> free_items;
137 Item* first_item;
138 Item* last_item;
139};
140
141} // namespace Common