2016-02-24 13:48:16 +00:00
|
|
|
#pragma once
|
2023-04-01 03:18:41 +00:00
|
|
|
///@file
|
2016-02-24 13:48:16 +00:00
|
|
|
|
2020-06-03 10:15:22 +00:00
|
|
|
#include <cassert>
|
2016-02-24 13:48:16 +00:00
|
|
|
#include <map>
|
|
|
|
#include <list>
|
2019-02-12 12:43:32 +00:00
|
|
|
#include <optional>
|
2016-02-24 13:48:16 +00:00
|
|
|
|
|
|
|
namespace nix {
|
|
|
|
|
2023-03-27 01:12:25 +00:00
|
|
|
/**
|
|
|
|
* A simple least-recently used cache. Not thread-safe.
|
|
|
|
*/
|
2016-02-24 13:48:16 +00:00
|
|
|
template<typename Key, typename Value>
|
|
|
|
class LRUCache
|
|
|
|
{
|
|
|
|
private:
|
|
|
|
|
2017-04-06 12:30:31 +00:00
|
|
|
size_t capacity;
|
2016-02-24 13:48:16 +00:00
|
|
|
|
|
|
|
// Stupid wrapper to get around circular dependency between Data
|
|
|
|
// and LRU.
|
|
|
|
struct LRUIterator;
|
|
|
|
|
|
|
|
using Data = std::map<Key, std::pair<LRUIterator, Value>>;
|
|
|
|
using LRU = std::list<typename Data::iterator>;
|
|
|
|
|
|
|
|
struct LRUIterator { typename LRU::iterator it; };
|
|
|
|
|
|
|
|
Data data;
|
|
|
|
LRU lru;
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
2017-04-06 12:30:31 +00:00
|
|
|
LRUCache(size_t capacity) : capacity(capacity) { }
|
2016-02-24 13:48:16 +00:00
|
|
|
|
2023-03-27 01:12:25 +00:00
|
|
|
/**
|
|
|
|
* Insert or upsert an item in the cache.
|
|
|
|
*/
|
2016-02-24 13:48:16 +00:00
|
|
|
void upsert(const Key & key, const Value & value)
|
|
|
|
{
|
2017-04-06 12:30:31 +00:00
|
|
|
if (capacity == 0) return;
|
|
|
|
|
2016-02-24 13:48:16 +00:00
|
|
|
erase(key);
|
|
|
|
|
2017-04-06 12:30:31 +00:00
|
|
|
if (data.size() >= capacity) {
|
2023-03-27 01:12:25 +00:00
|
|
|
/**
|
|
|
|
* Retire the oldest item.
|
|
|
|
*/
|
2016-02-24 13:48:16 +00:00
|
|
|
auto oldest = lru.begin();
|
|
|
|
data.erase(*oldest);
|
|
|
|
lru.erase(oldest);
|
|
|
|
}
|
|
|
|
|
|
|
|
auto res = data.emplace(key, std::make_pair(LRUIterator(), value));
|
|
|
|
assert(res.second);
|
|
|
|
auto & i(res.first);
|
|
|
|
|
|
|
|
auto j = lru.insert(lru.end(), i);
|
|
|
|
|
|
|
|
i->second.first.it = j;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool erase(const Key & key)
|
|
|
|
{
|
|
|
|
auto i = data.find(key);
|
|
|
|
if (i == data.end()) return false;
|
|
|
|
lru.erase(i->second.first.it);
|
|
|
|
data.erase(i);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2023-03-27 01:12:25 +00:00
|
|
|
/**
|
|
|
|
* Look up an item in the cache. If it exists, it becomes the most
|
|
|
|
* recently used item.
|
|
|
|
* */
|
2019-02-12 12:43:32 +00:00
|
|
|
std::optional<Value> get(const Key & key)
|
2016-02-24 13:48:16 +00:00
|
|
|
{
|
|
|
|
auto i = data.find(key);
|
2018-03-09 13:19:51 +00:00
|
|
|
if (i == data.end()) return {};
|
2016-02-24 13:48:16 +00:00
|
|
|
|
2023-03-27 01:12:25 +00:00
|
|
|
/**
|
|
|
|
* Move this item to the back of the LRU list.
|
|
|
|
*/
|
2016-02-24 13:48:16 +00:00
|
|
|
lru.erase(i->second.first.it);
|
|
|
|
auto j = lru.insert(lru.end(), i);
|
|
|
|
i->second.first.it = j;
|
|
|
|
|
2018-03-09 13:19:51 +00:00
|
|
|
return i->second.second;
|
2016-02-24 13:48:16 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
size_t size()
|
|
|
|
{
|
|
|
|
return data.size();
|
|
|
|
}
|
2016-04-19 16:50:15 +00:00
|
|
|
|
|
|
|
void clear()
|
|
|
|
{
|
|
|
|
data.clear();
|
|
|
|
lru.clear();
|
|
|
|
}
|
2016-02-24 13:48:16 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
}
|