#pragma once ///@file #include #include #include #include namespace nix { /** * A simple least-recently used cache. Not thread-safe. */ template class LRUCache { private: size_t capacity; // Stupid wrapper to get around circular dependency between Data // and LRU. struct LRUIterator; using Data = std::map>; using LRU = std::list; struct LRUIterator { typename LRU::iterator it; }; Data data; LRU lru; public: LRUCache(size_t capacity) : capacity(capacity) { } /** * Insert or upsert an item in the cache. */ void upsert(const Key & key, const Value & value) { if (capacity == 0) return; erase(key); if (data.size() >= capacity) { /** * Retire the oldest item. */ 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; } /** * Look up an item in the cache. If it exists, it becomes the most * recently used item. * */ std::optional get(const Key & key) { auto i = data.find(key); if (i == data.end()) return {}; /** * Move this item to the back of the LRU list. */ lru.erase(i->second.first.it); auto j = lru.insert(lru.end(), i); i->second.first.it = j; return i->second.second; } size_t size() { return data.size(); } void clear() { data.clear(); lru.clear(); } }; }