Skip to content

Instantly share code, notes, and snippets.

@tomilov
Last active February 3, 2023 09:47
Show Gist options
  • Save tomilov/a2a44aa273bcb89ea881e52424e9fe93 to your computer and use it in GitHub Desktop.
Save tomilov/a2a44aa273bcb89ea881e52424e9fe93 to your computer and use it in GitHub Desktop.
#include <unordered_set>
#include <list>
#include <optional>
#include <functional>
#include <utility>
#include <tuple>
#include <memory>
#include <type_traits>
#include <cstdlib>
template<typename T>
constexpr bool alwaysFalse = false;
template<typename L, typename R>
constexpr void replace(L & lhs, R && rhs) noexcept(std::is_nothrow_assignable_v<L &, R &&> || (!std::is_assignable_v<L &, R &&> && std::is_nothrow_constructible_v<L, R &&>))
{
if constexpr (std::is_assignable_v<L &, R &&>) {
lhs = std::forward<R>(rhs);
} else {
std::destroy_at(&lhs);
std::construct_at(&lhs, std::forward<R>(rhs));
}
}
template<typename Key, typename Value>
class LRUCache
{
public:
LRUCache(size_t capacity)
: capacity{capacity}
{}
template<typename K>
Value * get(const K & key) {
auto it = cache.find(key);
if (it == std::end(cache)) {
return nullptr;
}
lru.splice(*it, lru, std::end(lru));
return &std::get<1>(**it);
}
template<typename K, typename V>
void put(K && key, V && value)
{
auto it = cache.find(key);
if (it != std::end(cache)) {
std::get<1>(**it) = std::forward<V>(value);
lru.splice(*it, lru, std::end(lru));
return;
}
if (lru.size() == capacity) {
cache.erase(std::begin(lru));
replace(std::get<0>(lru.front()), std::forward<K>(key));
replace(std::get<1>(lru.front()), std::forward<V>(value));
cache.insert(std::begin(lru));
lru.splice(std::begin(lru), lru, std::end(lru));
} else {
lru.emplace_back(std::forward<K>(key), std::forward<V>(value));
cache.insert(std::prev(std::end(lru)));
}
}
private:
template<typename K>
static const Key & unref(const K & key) noexcept
{
if constexpr (std::is_same_v<K, std::remove_const_t<Key>>) {
return key;
} else if constexpr (std::is_same_v<K, std::tuple<Key, Value>>) {
return std::get<0>(key);
} else if constexpr (std::is_same_v<K, typename LRU::iterator>) {
return unref(*key);
} else {
static_assert(alwaysFalse<K>);
}
}
struct Hash : std::hash<std::remove_const_t<Key>>
{
using is_transparent = void;
using std::hash<std::remove_const_t<Key>>::operator ();
template<typename K>
size_t operator () (const K & key) const noexcept
{
return operator () (unref(key));
}
};
struct EqualTo : std::equal_to<Key>
{
using is_transparent = void;
using std::equal_to<Key>::operator ();
template<typename L, typename R>
bool operator () (const L & lhs, const R & rhs) const noexcept
{
return operator () (unref(lhs), unref(rhs));
}
};
using LRU = std::list<std::tuple<Key, Value>>;
using Cache = std::unordered_set<typename LRU::iterator, Hash, EqualTo>;
const size_t capacity;
LRU lru;
Cache cache;
};
#include <cassert>
int main()
{
LRUCache<const int, int> cache{2};
cache.put(1, 10);
assert(cache.get(1));
assert(*cache.get(1) == 10);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment