Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
stenzek
GitHub Repository: stenzek/duckstation
Path: blob/master/src/common/lru_cache.h
4211 views
1
// SPDX-FileCopyrightText: 2019-2025 Connor McLaughlin <[email protected]>
2
// SPDX-License-Identifier: CC-BY-NC-ND-4.0
3
4
#pragma once
5
#include "heterogeneous_containers.h"
6
#include <cstdint>
7
#include <map>
8
9
template<class K, class V>
10
class LRUCache
11
{
12
using CounterType = std::uint64_t;
13
14
struct Item
15
{
16
V value;
17
CounterType last_access;
18
};
19
20
using MapType = std::conditional_t<std::is_same_v<K, std::string>, StringMap<Item>, std::map<K, Item>>;
21
22
public:
23
LRUCache(std::size_t max_capacity = 16, bool manual_evict = false)
24
: m_max_capacity(max_capacity), m_manual_evict(manual_evict)
25
{
26
}
27
~LRUCache() = default;
28
29
std::size_t GetSize() const { return m_items.size(); }
30
std::size_t GetMaxCapacity() const { return m_max_capacity; }
31
32
void Clear() { m_items.clear(); }
33
34
void SetMaxCapacity(std::size_t capacity)
35
{
36
m_max_capacity = capacity;
37
if (m_items.size() > m_max_capacity)
38
Evict(m_items.size() - m_max_capacity);
39
}
40
41
template<typename KeyT>
42
V* Lookup(const KeyT& key)
43
{
44
auto iter = m_items.find(key);
45
if (iter == m_items.end())
46
return nullptr;
47
48
iter->second.last_access = ++m_last_counter;
49
return &iter->second.value;
50
}
51
52
V* Insert(K key, V value)
53
{
54
ShrinkForNewItem();
55
56
auto iter = m_items.find(key);
57
if (iter != m_items.end())
58
{
59
iter->second.value = std::move(value);
60
iter->second.last_access = ++m_last_counter;
61
return &iter->second.value;
62
}
63
else
64
{
65
Item it;
66
it.last_access = ++m_last_counter;
67
it.value = std::move(value);
68
auto ip = m_items.emplace(std::move(key), std::move(it));
69
return &ip.first->second.value;
70
}
71
}
72
73
void Evict(std::size_t count = 1)
74
{
75
while (!m_items.empty() && count > 0)
76
{
77
typename MapType::iterator lowest = m_items.end();
78
for (auto iter = m_items.begin(); iter != m_items.end(); ++iter)
79
{
80
if (lowest == m_items.end() || iter->second.last_access < lowest->second.last_access)
81
lowest = iter;
82
}
83
m_items.erase(lowest);
84
count--;
85
}
86
}
87
88
template<typename Pred>
89
std::size_t RemoveMatchingItems(const Pred& pred)
90
{
91
std::size_t removed_count = 0;
92
for (auto iter = m_items.begin(); iter != m_items.end();)
93
{
94
if (pred(iter->first))
95
{
96
iter = m_items.erase(iter);
97
removed_count++;
98
}
99
else
100
{
101
++iter;
102
}
103
}
104
return removed_count;
105
}
106
107
template<typename KeyT>
108
bool Remove(const KeyT& key)
109
{
110
auto iter = m_items.find(key);
111
if (iter == m_items.end())
112
return false;
113
m_items.erase(iter);
114
return true;
115
}
116
void SetManualEvict(bool block)
117
{
118
m_manual_evict = block;
119
if (!m_manual_evict)
120
ManualEvict();
121
}
122
void ManualEvict()
123
{
124
// evict if we went over
125
while (m_items.size() > m_max_capacity)
126
Evict(m_items.size() - m_max_capacity);
127
}
128
129
private:
130
void ShrinkForNewItem()
131
{
132
if (m_items.size() < m_max_capacity)
133
return;
134
135
Evict(m_items.size() - (m_max_capacity - 1));
136
}
137
138
MapType m_items;
139
CounterType m_last_counter = 0;
140
std::size_t m_max_capacity = 0;
141
bool m_manual_evict = false;
142
};
143