diff options
author | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
---|---|---|
committer | Kae <80987908+Novaenia@users.noreply.github.com> | 2023-06-20 14:33:09 +1000 |
commit | 6352e8e3196f78388b6c771073f9e03eaa612673 (patch) | |
tree | e23772f79a7fbc41bc9108951e9e136857484bf4 /source/core/StarTtlCache.hpp | |
parent | 6741a057e5639280d85d0f88ba26f000baa58f61 (diff) |
everything everywhere
all at once
Diffstat (limited to 'source/core/StarTtlCache.hpp')
-rw-r--r-- | source/core/StarTtlCache.hpp | 203 |
1 files changed, 203 insertions, 0 deletions
diff --git a/source/core/StarTtlCache.hpp b/source/core/StarTtlCache.hpp new file mode 100644 index 0000000..c70741a --- /dev/null +++ b/source/core/StarTtlCache.hpp @@ -0,0 +1,203 @@ +#ifndef STAR_TTL_CACHE_HPP +#define STAR_TTL_CACHE_HPP + +#include "StarLruCache.hpp" +#include "StarTime.hpp" +#include "StarRandom.hpp" + +namespace Star { + +template <typename LruCacheType> +class TtlCacheBase { +public: + typedef typename LruCacheType::Key Key; + typedef typename LruCacheType::Value::second_type Value; + + typedef function<Value(Key const&)> ProducerFunction; + + TtlCacheBase(int64_t timeToLive = 10000, int timeSmear = 1000, size_t maxSize = NPos, bool ttlUpdateEnabled = true); + + int64_t timeToLive() const; + void setTimeToLive(int64_t timeToLive); + + int timeSmear() const; + void setTimeSmear(int timeSmear); + + // If a max size is set, this cache also acts as an LRU cache with the given + // maximum size. + size_t maxSize() const; + void setMaxSize(size_t maxSize = NPos); + + size_t currentSize() const; + + List<Key> keys() const; + List<Value> values() const; + + // If ttlUpdateEnabled is false, then the time to live for entries will not + // be updated on access. + bool ttlUpdateEnabled() const; + void setTtlUpdateEnabled(bool enabled); + + // If the value is in the cache, returns it and updates the access time, + // otherwise returns nullptr. + Value* ptr(Key const& key); + + // Put the given value into the cache. + void set(Key const& key, Value value); + // Removes the given value from the cache. If found and removed, returns + // true. + bool remove(Key const& key); + + // Remove all key / value pairs matching a filter. + void removeWhere(function<bool(Key const&, Value&)> filter); + + // If the value for the key is not found in the cache, produce it with the + // given producer. Producer should take the key as an argument and return + // the Value. + template <typename Producer> + Value& get(Key const& key, Producer producer); + + void clear(); + + // Cleanup any cached entries that are older than their time to live, if the + // refreshFilter is given, things that match the refreshFilter instead have + // their ttl refreshed rather than being removed. + void cleanup(function<bool(Key const&, Value const&)> refreshFilter = {}); + +private: + LruCacheType m_cache; + int64_t m_timeToLive; + int m_timeSmear; + bool m_ttlUpdateEnabled; +}; + +template <typename Key, typename Value, typename Compare = std::less<Key>, typename Allocator = BlockAllocator<pair<Key const, pair<int64_t, Value>>, 1024>> +using TtlCache = TtlCacheBase<LruCache<Key, pair<int64_t, Value>, Compare, Allocator>>; + +template <typename Key, typename Value, typename Hash = Star::hash<Key>, typename Equals = std::equal_to<Key>, typename Allocator = BlockAllocator<pair<Key const, pair<int64_t, Value>>, 1024>> +using HashTtlCache = TtlCacheBase<HashLruCache<Key, pair<int64_t, Value>, Hash, Equals, Allocator>>; + +template <typename LruCacheType> +TtlCacheBase<LruCacheType>::TtlCacheBase(int64_t timeToLive, int timeSmear, size_t maxSize, bool ttlUpdateEnabled) { + m_cache.setMaxSize(maxSize); + m_timeToLive = timeToLive; + m_timeSmear = timeSmear; + m_ttlUpdateEnabled = ttlUpdateEnabled; +} + +template <typename LruCacheType> +int64_t TtlCacheBase<LruCacheType>::timeToLive() const { + return m_timeToLive; +} + +template <typename LruCacheType> +void TtlCacheBase<LruCacheType>::setTimeToLive(int64_t timeToLive) { + m_timeToLive = timeToLive; +} + +template <typename LruCacheType> +int TtlCacheBase<LruCacheType>::timeSmear() const { + return m_timeSmear; +} + +template <typename LruCacheType> +void TtlCacheBase<LruCacheType>::setTimeSmear(int timeSmear) { + m_timeSmear = timeSmear; +} + +template <typename LruCacheType> +bool TtlCacheBase<LruCacheType>::ttlUpdateEnabled() const { + return m_ttlUpdateEnabled; +} + +template <typename LruCacheType> +size_t TtlCacheBase<LruCacheType>::maxSize() const { + return m_cache.maxSize(); +} + +template <typename LruCacheType> +void TtlCacheBase<LruCacheType>::setMaxSize(size_t maxSize) { + m_cache.setMaxSize(maxSize); +} + +template <typename LruCacheType> +size_t TtlCacheBase<LruCacheType>::currentSize() const { + return m_cache.currentSize(); +} + +template <typename LruCacheType> +auto TtlCacheBase<LruCacheType>::keys() const -> List<Key> { + return m_cache.keys(); +} + +template <typename LruCacheType> +auto TtlCacheBase<LruCacheType>::values() const -> List<Value> { + List<Value> values; + for (auto& p : m_cache.values()) + values.append(move(p.second)); + return values; +} + +template <typename LruCacheType> +void TtlCacheBase<LruCacheType>::setTtlUpdateEnabled(bool enabled) { + m_ttlUpdateEnabled = enabled; +} + +template <typename LruCacheType> +auto TtlCacheBase<LruCacheType>::ptr(Key const& key) -> Value * { + if (auto p = m_cache.ptr(key)) { + if (m_ttlUpdateEnabled) + p->first = Time::monotonicMilliseconds() + Random::randInt(-m_timeSmear, m_timeSmear); + return &p->second; + } + return nullptr; +} + +template <typename LruCacheType> +void TtlCacheBase<LruCacheType>::set(Key const& key, Value value) { + m_cache.set(key, make_pair(Time::monotonicMilliseconds() + Random::randInt(-m_timeSmear, m_timeSmear), value)); +} + +template <typename LruCacheType> +bool TtlCacheBase<LruCacheType>::remove(Key const& key) { + return m_cache.remove(key); +} + +template <typename LruCacheType> +void TtlCacheBase<LruCacheType>::removeWhere(function<bool(Key const&, Value&)> filter) { + m_cache.removeWhere([&filter](auto const& key, auto& value) { return filter(key, value.second); }); +} + +template <typename LruCacheType> +template <typename Producer> +auto TtlCacheBase<LruCacheType>::get(Key const& key, Producer producer) -> Value & { + auto& value = m_cache.get(key, [producer](Key const& key) { + return pair<int64_t, Value>(0, producer(key)); + }); + if (value.first == 0 || m_ttlUpdateEnabled) + value.first = Time::monotonicMilliseconds() + Random::randInt(-m_timeSmear, m_timeSmear); + return value.second; +} + +template <typename LruCacheType> +void TtlCacheBase<LruCacheType>::clear() { + m_cache.clear(); +} + +template <typename LruCacheType> +void TtlCacheBase<LruCacheType>::cleanup(function<bool(Key const&, Value const&)> refreshFilter) { + int64_t currentTime = Time::monotonicMilliseconds(); + m_cache.removeWhere([&](auto const& key, auto& value) { + if (refreshFilter && refreshFilter(key, value.second)) { + value.first = currentTime; + } else { + if (currentTime - value.first > m_timeToLive) + return true; + } + return false; + }); +} + +} + +#endif |