// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- // vim: ts=8 sw=2 smarttab #pragma once #include #include #include #include #include template class SimpleLRU { static_assert(std::is_default_constructible_v); using list_type = std::list; template using map_t = std::conditional_t, std::unordered_map>; using map_type = map_t>; list_type lru; map_type cache; const size_t max_size; public: SimpleLRU(size_t size = 20) : cache(size), max_size(size) {} size_t size() const { return cache.size(); } size_t capacity() const { return max_size; } using insert_return_type = std::pair; insert_return_type insert(const Key& key, Value value); std::optional find(const Key& key); std::optional> lower_bound(const Key& key); void erase(const Key& key); void clear(); private: // bump the item to the front of the lru list Value _lru_add(typename map_type::iterator found); // evict the last element of most recently used list void _evict(); }; template typename SimpleLRU::insert_return_type SimpleLRU::insert(const Key& key, Value value) { if constexpr(Ordered) { auto found = cache.lower_bound(key); if (found != cache.end() && found->first == key) { // already exists return {found->second.first, true}; } else { if (size() >= capacity()) { _evict(); } lru.push_front(key); // use lower_bound as hint to save the lookup cache.emplace_hint(found, key, std::make_pair(value, lru.begin())); return {std::move(value), false}; } } else { // cache is not ordered auto found = cache.find(key); if (found != cache.end()) { // already exists return {found->second.first, true}; } else { if (size() >= capacity()) { _evict(); } lru.push_front(key); cache.emplace(key, std::make_pair(value, lru.begin())); return {std::move(value), false}; } } } template std::optional SimpleLRU::find(const Key& key) { if (auto found = cache.find(key); found != cache.end()){ return _lru_add(found); } else { return {}; } } template std::optional> SimpleLRU::lower_bound(const Key& key) { if (auto found = cache.lower_bound(key); found != cache.end()) { return _lru_add(found); } else { return {}; } } template void SimpleLRU::clear() { lru.clear(); cache.clear(); } template void SimpleLRU::erase(const Key& key) { if (auto found = cache.find(key); found != cache.end()) { lru.erase(found->second.second); cache.erase(found); } } template Value SimpleLRU::_lru_add( typename SimpleLRU::map_type::iterator found) { auto& [value, in_lru] = found->second; if (in_lru != lru.begin()){ // move item to the front lru.splice(lru.begin(), lru, in_lru); } // the item is already at the front return value; } template void SimpleLRU::_evict() { // evict the last element of most recently used list auto last = --lru.end(); cache.erase(*last); lru.erase(last); }