| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| #ifndef LRU_TIMED_CACHE_HPP |
| #define LRU_TIMED_CACHE_HPP |
|
|
| #include <algorithm> |
| #include <cassert> |
| #include <chrono> |
| #include <cstddef> |
| #include <functional> |
| #include <iterator> |
| #include <list> |
| #include <stdexcept> |
| #include <unordered_map> |
| #include <utility> |
|
|
| #include <lru/error.hpp> |
| #include <lru/internal/base-cache.hpp> |
| #include <lru/internal/last-accessed.hpp> |
| #include <lru/internal/timed-information.hpp> |
|
|
| namespace LRU { |
| namespace Internal { |
| template <typename Key, |
| typename Value, |
| typename HashFunction, |
| typename KeyEqual> |
| using TimedCacheBase = BaseCache<Key, |
| Value, |
| Internal::TimedInformation, |
| HashFunction, |
| KeyEqual, |
| Tag::TimedCache>; |
| } |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| template <typename Key, |
| typename Value, |
| typename Duration = std::chrono::duration<double, std::milli>, |
| typename HashFunction = std::hash<Key>, |
| typename KeyEqual = std::equal_to<Key>> |
| class TimedCache |
| : public Internal::TimedCacheBase<Key, Value, HashFunction, KeyEqual> { |
| private: |
| using super = Internal::TimedCacheBase<Key, Value, HashFunction, KeyEqual>; |
| using PRIVATE_BASE_CACHE_MEMBERS; |
|
|
| public: |
| using Tag = LRU::Tag::TimedCache; |
| using PUBLIC_BASE_CACHE_MEMBERS; |
| using super::ordered_end; |
| using super::unordered_end; |
| using typename super::size_t; |
|
|
| |
| |
| template <typename AnyDurationType = Duration> |
| explicit TimedCache(const AnyDurationType& time_to_live, |
| size_t capacity = Internal::DEFAULT_CAPACITY, |
| const HashFunction& hash = HashFunction(), |
| const KeyEqual& equal = KeyEqual()) |
| : super(capacity, hash, equal) |
| , _time_to_live(std::chrono::duration_cast<Duration>(time_to_live)) { |
| } |
|
|
| |
| |
| |
| |
| template <typename Iterator, typename AnyDurationType = Duration> |
| TimedCache(const AnyDurationType& time_to_live, |
| size_t capacity, |
| Iterator begin, |
| Iterator end, |
| const HashFunction& hash = HashFunction(), |
| const KeyEqual& equal = KeyEqual()) |
| : super(capacity, begin, end, hash, equal) |
| , _time_to_live(std::chrono::duration_cast<Duration>(time_to_live)) { |
| } |
|
|
| |
| |
| |
| template <typename Iterator, typename AnyDurationType = Duration> |
| TimedCache(const AnyDurationType& time_to_live, |
| Iterator begin, |
| Iterator end, |
| const HashFunction& hash = HashFunction(), |
| const KeyEqual& equal = KeyEqual()) |
| : super(begin, end, hash, equal) |
| , _time_to_live(std::chrono::duration_cast<Duration>(time_to_live)) { |
| } |
|
|
| |
| |
| |
| template <typename Range, |
| typename AnyDurationType = Duration, |
| typename = Internal::enable_if_range<Range>> |
| TimedCache(const AnyDurationType& time_to_live, |
| size_t capacity, |
| Range&& range, |
| const HashFunction& hash = HashFunction(), |
| const KeyEqual& equal = KeyEqual()) |
| : super(capacity, std::forward<Range>(range), hash, equal) |
| , _time_to_live(std::chrono::duration_cast<Duration>(time_to_live)) { |
| } |
|
|
| |
| |
| |
| template <typename Range, |
| typename AnyDurationType = Duration, |
| typename = Internal::enable_if_range<Range>> |
| explicit TimedCache(const AnyDurationType& time_to_live, |
| Range&& range, |
| const HashFunction& hash = HashFunction(), |
| const KeyEqual& equal = KeyEqual()) |
| : super(std::forward<Range>(range), hash, equal) |
| , _time_to_live(std::chrono::duration_cast<Duration>(time_to_live)) { |
| } |
|
|
| |
| |
| |
| template <typename AnyDurationType = Duration> |
| TimedCache(const AnyDurationType& time_to_live, |
| InitializerList list, |
| const HashFunction& hash = HashFunction(), |
| const KeyEqual& equal = KeyEqual()) |
| : super(list, hash, equal), |
| _time_to_live(std::chrono::duration_cast<Duration>(time_to_live)) { |
| } |
|
|
| |
| |
| |
| |
| template <typename AnyDurationType = Duration> |
| TimedCache(const AnyDurationType& time_to_live, |
| size_t capacity, |
| InitializerList list, |
| const HashFunction& hash = HashFunction(), |
| const KeyEqual& equal = KeyEqual()) |
| : super(capacity, list, hash, equal), |
| _time_to_live(std::chrono::duration_cast<Duration>(time_to_live)) { |
| } |
|
|
| |
| void swap(TimedCache& other) noexcept { |
| using std::swap; |
|
|
| super::swap(other); |
| swap(_time_to_live, other._time_to_live); |
| } |
|
|
| |
| |
| |
| |
| friend void swap(TimedCache& first, TimedCache& second) noexcept { |
| first.swap(second); |
| } |
|
|
| |
| UnorderedIterator find(const Key& key) override { |
| auto iterator = _map.find(key); |
| if (iterator != _map.end()) { |
| if (!_has_expired(iterator->second)) { |
| _register_hit(key, iterator->second.value); |
| _move_to_front(iterator->second.order); |
| _last_accessed = iterator; |
| return {*this, iterator}; |
| } |
| } |
|
|
| _register_miss(key); |
|
|
| return end(); |
| } |
|
|
| |
| UnorderedConstIterator find(const Key& key) const override { |
| auto iterator = _map.find(key); |
| if (iterator != _map.end()) { |
| if (!_has_expired(iterator->second)) { |
| _register_hit(key, iterator->second.value); |
| _move_to_front(iterator->second.order); |
| _last_accessed = iterator; |
| return {*this, iterator}; |
| } |
| } |
|
|
| _register_miss(key); |
|
|
| return cend(); |
| } |
|
|
| |
| |
|
|
| |
| bool all_expired() const { |
| |
| if (is_empty()) return true; |
|
|
| |
| auto latest = _map.find(_order.back()); |
| return _has_expired(latest->second); |
| } |
|
|
| |
| |
| |
| |
| size_t clear_expired() { |
| |
| |
| |
| |
|
|
| if (is_empty()) return 0; |
|
|
| auto iterator = _order.begin(); |
| size_t number_of_erasures = 0; |
|
|
| while (iterator != _order.end()) { |
| auto map_iterator = _map.find(*iterator); |
|
|
| |
| |
| if (!_has_expired(map_iterator->second)) break; |
|
|
| _erase(map_iterator); |
|
|
| iterator = _order.begin(); |
| number_of_erasures += 1; |
| } |
|
|
| return number_of_erasures; |
| } |
|
|
| |
| |
| bool has_expired(const Key& key) const noexcept { |
| auto iterator = _map.find(key); |
| return iterator != _map.end() && _has_expired(iterator->second); |
| } |
|
|
| |
| |
| |
| bool has_expired(OrderedConstIterator ordered_iterator) const noexcept { |
| if (ordered_iterator == ordered_end()) return false; |
| auto iterator = _map.find(ordered_iterator->key()); |
| assert(iterator != _map.end()); |
|
|
| return _has_expired(iterator->second); |
| } |
|
|
| |
| |
| |
| bool has_expired(UnorderedConstIterator unordered_iterator) const noexcept { |
| if (unordered_iterator == unordered_end()) return false; |
| assert(unordered_iterator._iterator != _map.end()); |
|
|
| return _has_expired(unordered_iterator._iterator->second); |
| } |
|
|
| |
| bool is_valid(UnorderedConstIterator unordered_iterator) const |
| noexcept override { |
| if (!super::is_valid(unordered_iterator)) return false; |
| if (has_expired(unordered_iterator)) return false; |
| return true; |
| } |
|
|
| |
| bool is_valid(OrderedConstIterator ordered_iterator) const noexcept override { |
| if (!super::is_valid(ordered_iterator)) return false; |
| if (has_expired(ordered_iterator)) return false; |
| return true; |
| } |
|
|
| |
| |
| |
| void |
| throw_if_invalid(UnorderedConstIterator unordered_iterator) const override { |
| super::throw_if_invalid(unordered_iterator); |
| if (has_expired(unordered_iterator)) { |
| throw LRU::Error::KeyExpired(); |
| } |
| } |
|
|
| |
| |
| |
| void throw_if_invalid(OrderedConstIterator ordered_iterator) const override { |
| super::throw_if_invalid(ordered_iterator); |
| if (has_expired(ordered_iterator)) { |
| throw LRU::Error::KeyExpired(); |
| } |
| } |
|
|
| private: |
| using Clock = Internal::Clock; |
|
|
| |
| |
| |
| bool _last_accessed_is_ok(const Key& key) const noexcept override { |
| if (!super::_last_accessed_is_ok(key)) return false; |
| return !_has_expired(_last_accessed.information()); |
| } |
|
|
| |
| Value& _value_for_last_accessed() override { |
| auto& information = _last_accessed.information(); |
| if (_has_expired(information)) { |
| throw LRU::Error::KeyExpired(); |
| } else { |
| return information.value; |
| } |
| } |
|
|
| |
| |
| |
| const Value& _value_for_last_accessed() const override { |
| const auto& information = _last_accessed.information(); |
| if (_has_expired(information)) { |
| throw LRU::Error::KeyExpired(); |
| } else { |
| return information.value; |
| } |
| } |
|
|
| |
| |
| |
| |
| bool _has_expired(const Information& information) const noexcept { |
| auto elapsed = Clock::now() - information.insertion_time; |
| return std::chrono::duration_cast<Duration>(elapsed) > _time_to_live; |
| } |
|
|
| |
| Duration _time_to_live; |
| }; |
|
|
| namespace Lowercase { |
| template <typename... Ts> |
| using timed_cache = TimedCache<Ts...>; |
| } |
|
|
| } |
|
|
| #endif |
|
|