4#include "Stroika/Foundation/Containers/Common.h"
6#include "Stroika/Foundation/Cryptography/Digest/Hash.h"
16 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
17 struct LRUCache<KEY, VALUE, TRAITS>::CacheIterator_ {
18 explicit CacheIterator_ (CacheElement_** start, CacheElement_** end)
21 , fCur{start == end ? nullptr : *fCurV}
24 CacheIterator_& operator++ ()
27 Require (fCurV != fEndV);
29 if (fCur ==
nullptr) {
37 optional<KeyValuePair_>& operator* ()
40 return fCur->fElement;
42 optional<KeyValuePair_>* operator->()
45 return &fCur->fElement;
47 bool operator== (
const CacheIterator_& rhs)
const
49 return fCur == rhs.fCur;
53 CacheElement_** fCurV;
54 CacheElement_** fEndV;
63 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
64 struct LRUCache<KEY, VALUE, TRAITS>::CacheElement_ {
65 CacheElement_* fNext{
nullptr};
66 CacheElement_* fPrev{
nullptr};
67 optional<KeyValuePair_> fElement{};
75 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
77 requires (same_as<typename TRAITS::KeyHashFunctionType, nullptr_t> and same_as<typename TRAITS::KeyEqualsCompareFunctionType, equal_to<KEY>>)
81 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
82 inline LRUCache<KEY, VALUE, TRAITS>::LRUCache (
size_t maxCacheSize,
const typename TRAITS::KeyEqualsCompareFunctionType& keyEqualsComparer)
83 requires (same_as<typename TRAITS::KeyHashFunctionType, nullptr_t>)
85 , fKeyEqualsComparer_{keyEqualsComparer}
86 , fHashFunction_{nullptr}
88 , fCachedElts_First_{Memory::eUninitialized, 1}
89 , fCachedElts_Last_{Memory::eUninitialized, 1}
91 Require (maxCacheSize >= 1);
92 SetMaxCacheSize (maxCacheSize);
94 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
95 inline LRUCache<KEY, VALUE, TRAITS>::LRUCache (
size_t maxCacheSize,
const typename TRAITS::KeyEqualsCompareFunctionType& keyEqualsComparer,
96 size_t hashTableSize,
const typename TRAITS::KeyHashFunctionType& hashFunction)
97 requires (not same_as<typename TRAITS::KeyHashFunctionType, nullptr_t>)
98 : fHashtableSize_{hashTableSize}
99 , fKeyEqualsComparer_{keyEqualsComparer}
100 , fHashFunction_{hashFunction}
101 , fCachedElts_BUF_{hashTableSize}
102 , fCachedElts_First_{Memory::eUninitialized, hashTableSize}
103 , fCachedElts_Last_{Memory::eUninitialized, hashTableSize}
105 Require (hashTableSize >= 1);
107 SetMaxCacheSize (maxCacheSize);
109 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
110 inline LRUCache<KEY, VALUE, TRAITS>::LRUCache (
size_t maxCacheSize,
size_t hashTableSize,
const typename TRAITS::KeyHashFunctionType& hashFunction)
111 requires (not same_as<typename TRAITS::KeyHashFunctionType, nullptr_t>)
112 : LRUCache{maxCacheSize, typename TRAITS::KeyEqualsCompareFunctionType{}, hashTableSize, hashFunction}
115 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
116 inline LRUCache<KEY, VALUE, TRAITS>::LRUCache (LRUCache&& from)
123 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
125 requires (same_as<typename TRAITS::KeyHashFunctionType, nullptr_t>)
126 : LRUCache{from.GetMaxCacheSize (), from.GetKeyEqualsCompareFunction ()}
128 shared_lock lockSrc{from.fMaybeMutex_};
129 for (CacheIterator_ i = from.begin_ (); i != from.end_ (); ++i) {
131 Add_ ((*i)->fKey, (*i)->fValue);
135 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
137 requires (not same_as<typename TRAITS::KeyHashFunctionType, nullptr_t>)
138 : LRUCache{from.GetMaxCacheSize (), from.GetKeyEqualsCompareFunction (), from.GetHashTableSize (), from.GetKeyHashFunction ()}
140 shared_lock lockSrc{from.fMaybeMutex_};
141 for (CacheIterator_ i = from.begin_ (); i != from.end_ (); ++i) {
143 Add_ ((*i)->fKey, (*i)->fValue);
147 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
148 inline auto LRUCache<KEY, VALUE, TRAITS>::operator= (LRUCache&& rhs)
noexcept -> LRUCache&
150 IgnoreExceptionsForCall (
return operator= (rhs));
152 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
153 auto LRUCache<KEY, VALUE, TRAITS>::operator= (
const LRUCache& rhs) -> LRUCache&
157 SetMaxCacheSize (rhs.GetMaxCacheSize ());
158 scoped_lock critSec{fMaybeMutex_};
160 for (
const auto& i : rhs.Elements ()) {
162 Add_ (*i.fKey, *i.fValue);
168 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
169 inline size_t LRUCache<KEY, VALUE, TRAITS>::GetMaxCacheSize ()
const
171 shared_lock critSec{fMaybeMutex_};
172 return fHashtableSize_ * fCachedElts_BUF_[0].size ();
174 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
177 Require (maxCacheSize >= 1);
178 scoped_lock critSec{fMaybeMutex_};
179 maxCacheSize = ((maxCacheSize + fHashtableSize_ - 1) / fHashtableSize_);
180 maxCacheSize = max (maxCacheSize,
static_cast<size_t> (1));
181 for (
size_t hi = 0; hi < fHashtableSize_; ++hi) {
182 if (maxCacheSize != fCachedElts_BUF_[hi].size ()) {
183 fCachedElts_BUF_[hi].resize (maxCacheSize);
186 fCachedElts_Last_[hi] = fCachedElts_First_[hi] + maxCacheSize - 1;
187 fCachedElts_BUF_[hi][0].fPrev =
nullptr;
188 for (
size_t i = 0; i < maxCacheSize - 1; ++i) {
189 fCachedElts_BUF_[hi][i].fNext = fCachedElts_First_[hi] + (i + 1);
190 fCachedElts_BUF_[hi][i + 1].fPrev = fCachedElts_First_[hi] + (i);
192 fCachedElts_BUF_[hi][maxCacheSize - 1].fNext =
nullptr;
196 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
199 shared_lock critSec{fMaybeMutex_};
200 return fKeyEqualsComparer_;
202 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
203 inline auto LRUCache<KEY, VALUE, TRAITS>::GetStats () const -> StatsType
205 shared_lock critSec{fMaybeMutex_};
208 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
209 inline auto LRUCache<KEY, VALUE, TRAITS>::GetHashTableSize () const ->
size_t
211 shared_lock critSec{fMaybeMutex_};
212 if constexpr (same_as<typename TRAITS::KeyHashFunctionType, nullptr_t>) {
216 return fHashtableSize_;
219 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
220 inline auto LRUCache<KEY, VALUE, TRAITS>::GetKeyHashFunction () const -> typename TRAITS::KeyHashFunctionType
222 shared_lock critSec{fMaybeMutex_};
223 return fHashFunction_;
225 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
228 scoped_lock critSec{fMaybeMutex_};
231 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
234 scoped_lock critSec{fMaybeMutex_};
235 optional<KeyValuePair_>* v = LookupElement_ (key);
239 Ensure (not Lookup (key));
241 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
244 scoped_lock critSec{fMaybeMutex_};
245 for (
auto i = begin_ (); i != end_ (); ++i) {
246 if (i->has_value () and clearPredicate ((*i)->fKey)) {
251 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
252 template <qCompilerAndStdLib_Constra
intDiffersInTemplateRedeclaration_BWA (predicate<KEY>) PREDICATE>
255 scoped_lock critSec{fMaybeMutex_};
256 for (
auto i = begin_ (); i != end_ (); ++i) {
257 if (i->has_value () and forward<PREDICATE> (removeIfReturnsTrue) ((*i)->fKey)) {
262 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
263 template <
typename V>
264 requires (not IValuelessCache<V>)
267 scoped_lock critSec{fMaybeMutex_};
268 optional<KeyValuePair_>* v = LookupElement_ (key);
270 return optional<VALUE>{};
272 Ensure (fKeyEqualsComparer_ (key, (*v)->fKey));
275 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
276 template <
typename V>
277 requires (IValuelessCache<V>)
280 scoped_lock critSec{fMaybeMutex_};
281 optional<KeyValuePair_>* v = LookupElement_ (key);
283 return optional<KEY>{};
285 Ensure (fKeyEqualsComparer_ (key, (*v)->fKey));
288 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
289 template <
typename V>
290 requires (not IValuelessCache<V>)
293 scoped_lock critSec{fMaybeMutex_};
296 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
297 template <
typename V>
298 requires (IValuelessCache<V>)
301 scoped_lock critSec{fMaybeMutex_};
302 optional<KeyValuePair_>* v = AddNewButDontFillIn_ (key);
303 v->emplace (KeyValuePair_{.fKey = key});
305 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
306 void LRUCache<KEY, VALUE, TRAITS>::Add_ (
typename Common::ArgByValueType<KEY> key,
typename Common::ArgByValueType<VALUE> value)
308 optional<KeyValuePair_>* v = AddNewButDontFillIn_ (key);
309 v->emplace (KeyValuePair_{.fKey = key, .fValue = value});
311 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
314 Assert (fHashtableSize_ >= 1);
315 if constexpr (same_as<typename TRAITS::KeyHashFunctionType, nullptr_t>) {
319 return fHashFunction_ (k) % fHashtableSize_;
322 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
326 shared_lock critSec{fMaybeMutex_};
327 for (CacheIterator_ i = begin_ (); i != end_ (); ++i) {
329 result.
Add ((*i)->fKey, (*i)->fValue);
334 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
338 auto v = Lookup (key);
339 if (v.has_value ()) {
343 VALUE newV = valueFetcher (key);
348 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
349 inline auto LRUCache<KEY, VALUE, TRAITS>::begin_ () const -> CacheIterator_
351 LRUCache* ncThis =
const_cast<LRUCache*
> (
this);
352 return CacheIterator_{std::begin (ncThis->fCachedElts_First_), std::end (ncThis->fCachedElts_First_)};
354 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
355 inline typename LRUCache<KEY, VALUE, TRAITS>::CacheIterator_ LRUCache<KEY, VALUE, TRAITS>::end_ ()
const
357 return CacheIterator_{
nullptr,
nullptr};
359 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
360 inline void LRUCache<KEY, VALUE, TRAITS>::ShuffleToHead_ (
size_t chainIdx, CacheElement_* b)
362 Require (chainIdx < fHashtableSize_);
364 if (b == fCachedElts_First_[chainIdx]) {
365 Assert (b->fPrev ==
nullptr);
368 CacheElement_* prev = b->fPrev;
371 prev->fNext = b->fNext;
372 if (b->fNext ==
nullptr) {
373 Assert (b == fCachedElts_Last_[chainIdx]);
374 fCachedElts_Last_[chainIdx] = b->fPrev;
377 b->fNext->fPrev = prev;
381 CacheElement_* oldFirst = fCachedElts_First_[chainIdx];
386 fCachedElts_First_[chainIdx] = b;
388 Ensure (fCachedElts_Last_[chainIdx] !=
nullptr and fCachedElts_Last_[chainIdx]->fNext ==
nullptr);
389 Ensure (fCachedElts_First_[chainIdx] !=
nullptr and fCachedElts_First_[chainIdx] == b and
390 fCachedElts_First_[chainIdx]->fPrev ==
nullptr and fCachedElts_First_[chainIdx]->fNext !=
nullptr);
392 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
393 inline void LRUCache<KEY, VALUE, TRAITS>::ClearCache_ ()
395 for (
size_t hi = 0; hi < fHashtableSize_; ++hi) {
396 for (CacheElement_* cur = fCachedElts_First_[hi]; cur !=
nullptr; cur = cur->fNext) {
397 cur->fElement = nullopt;
401 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
402 inline auto LRUCache<KEY, VALUE, TRAITS>::LookupElement_ (
typename Common::ArgByValueType<KeyType> item) -> optional<KeyValuePair_>*
404 size_t chainIdx = H_ (item);
405 Assert (0 <= chainIdx and chainIdx < fHashtableSize_);
406 for (CacheElement_* cur = fCachedElts_First_[chainIdx]; cur !=
nullptr; cur = cur->fNext) {
407 if (cur->fElement and fKeyEqualsComparer_ (cur->fElement->fKey, item)) {
408 ShuffleToHead_ (chainIdx, cur);
409 fStats_.IncrementHits ();
410 return &fCachedElts_First_[chainIdx]->fElement;
413 fStats_.IncrementMisses ();
416 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS>
419 size_t chainIdx = H_ (item);
420 Assert (0 <= chainIdx and chainIdx < fHashtableSize_);
421 ShuffleToHead_ (chainIdx, fCachedElts_Last_[chainIdx]);
422 return &fCachedElts_First_[chainIdx]->fElement;
430 namespace Factory::LRUCache {
431 template <
typename KEY,
typename VALUE, InternallySynchronized
internallySynchronized,
typename STATS_TYPE>
432 template <Common::IEqualsComparer<KEY> KEY_EQUALS_COMPARER>
433#if __cplusplus >= kStrokia_Foundation_Common_cplusplus_23 || _HAS_CXX23
439 using namespace LRUCacheSupport;
440 using TRAITS_ = WithKeyComparerTraits<DefaultTraits<KEY, VALUE>, KEY_EQUALS_COMPARER>;
441 if constexpr (internallySynchronized == Execution::InternallySynchronized::eInternallySynchronized) {
448 template <
typename KEY,
typename VALUE, InternallySynchronized
internallySynchronized,
typename STATS_TYPE>
449 template <
typename KEY_HASH_FUNCTION>
450#if __cplusplus >= kStrokia_Foundation_Common_cplusplus_23 || _HAS_CXX23
452 KEY_HASH_FUNCTION&& hashFunction)
455 KEY_HASH_FUNCTION&& hashFunction)
const
458 Require (maxCacheSize >= hashTableSize);
459 using namespace LRUCacheSupport;
460 using TRAITS_ = WithKeyHashTraits<DefaultTraits<KEY, VALUE>, KEY_HASH_FUNCTION>;
461 if constexpr (internallySynchronized == InternallySynchronized::eInternallySynchronized) {
468 template <
typename KEY,
typename VALUE, InternallySynchronized
internallySynchronized,
typename STATS_TYPE>
469 template <
typename KEY_EQUALS_COMPARER,
typename KEY_HASH_FUNCTION>
470#if __cplusplus >= kStrokia_Foundation_Common_cplusplus_23 || _HAS_CXX23
472 size_t hashTableSize, KEY_HASH_FUNCTION&& hashFunction)
475 size_t hashTableSize, KEY_HASH_FUNCTION&& hashFunction)
const
478 Require (maxCacheSize >= hashTableSize);
479 using namespace LRUCacheSupport;
480 using TRAITS_ = WithKeyHashTraits<WithKeyComparerTraits<DefaultTraits<KEY, VALUE>, KEY_EQUALS_COMPARER>, KEY_HASH_FUNCTION>;
481 if constexpr (internallySynchronized == InternallySynchronized::eInternallySynchronized) {
493 template <
typename KEY,
typename VALUE, LRUCacheSupport::ITraits<KEY, VALUE> TRAITS = LRUCacheSupport::DefaultTraits<KEY, VALUE>>
495 [[deprecated (
"Since Stroika v3.0d23 - use LRUCacheSupport::InternallySynchronizedTraits or Factory::LRUCache::Maker<string, "
496 "string,InternallySynchronized::eInternallySynchronized>{}(3)")]] =
500 template <
typename KEY,
typename VALUE,
typename STATS_TYPE = Statistics::StatsType_DEFAULT>
501 struct [[deprecated (
"Since Stroika v3.0d23 use Factory::LRUCache::Maker<KEY,VALUE>")]] LRUCache_NoHash {
502 template <Common::IEqualsComparer<KEY> KEY_EQUALS_COMPARER = equal_to<KEY>>
503 auto operator() (
size_t maxCacheSize = 1,
const KEY_EQUALS_COMPARER& keyComparer = {})
const
506 maxCacheSize, keyComparer);
509 template <
typename KEY,
typename VALUE,
typename STATS_TYPE = Statistics::StatsType_DEFAULT,
typename DEFAULT_KEY_EQUALS_COMPARER = equal_to<KEY>>
510 struct [[deprecated (
"Since Stroika v3.0d23 use Factory::LRUCache::Maker<KEY,VALUE>")]] LRUCache_WithHash {
511 template <
typename KEY_HASH_FUNCTION = hash<KEY>>
512 auto operator() (
size_t maxCacheSize,
size_t hashTableSize,
const KEY_HASH_FUNCTION& hashFunction = {})
const
515 maxCacheSize, DEFAULT_KEY_EQUALS_COMPARER{}, hashTableSize, hashFunction);
517 template <
typename KEY_EQUALS_COMPARER,
typename KEY_HASH_FUNCTION = hash<KEY>>
518 auto operator() (
size_t maxCacheSize,
const KEY_EQUALS_COMPARER& keyComparer,
size_t hashTableSize,
519 const KEY_HASH_FUNCTION& hashFunction = {})
const
522 maxCacheSize, keyComparer, hashTableSize, hashFunction);
525 template <
typename KEY,
typename VALUE,
typename STATS_TYPE = Statistics::StatsType_DEFAULT>
526 struct [[deprecated (
"Since Stroika v3.0d23 use Factory::LRUCache::Maker<KEY, "
527 "VALUE,InternallySynchronized::eInternallySynchronized>")]] SynchronizedLRUCache_NoHash {
528 template <Common::IEqualsComparer<KEY> KEY_EQUALS_COMPARER = equal_to<KEY>>
529 auto operator() (
size_t maxCacheSize = 1,
const KEY_EQUALS_COMPARER& keyComparer = {})
const
534 template <
typename KEY,
typename VALUE,
typename STATS_TYPE = Statistics::StatsType_DEFAULT,
typename DEFAULT_KEY_EQUALS_COMPARER = equal_to<KEY>>
535 struct [[deprecated (
"Since Stroika v3.0d23 use Factory::LRUCache::Maker<KEY, "
536 "VALUE,Execution::InternallySynchronized::eInternallySynchronized>")]] SynchronizedLRUCache_WithHash {
537 template <
typename KEY_HASH_FUNCTION = hash<KEY>>
538 auto operator() (
size_t maxCacheSize,
size_t hashTableSize,
const KEY_HASH_FUNCTION& hashFunction = {})
const
541 maxCacheSize, hashTableSize, hashFunction);
543 template <
typename KEY_EQUALS_COMPARER,
typename KEY_HASH_FUNCTION = hash<KEY>>
544 auto operator() (
size_t maxCacheSize,
const KEY_EQUALS_COMPARER& keyComparer,
size_t hashTableSize,
545 const KEY_HASH_FUNCTION& hashFunction = {})
const
548 maxCacheSize, keyComparer, hashTableSize, hashFunction);
#define RequireNotNull(p)
#define WeakAssert(c)
A WeakAssert() is for things that aren't guaranteed to be true, but are overwhelmingly likely to be t...
LRUCache implements a simple least-recently-used caching strategy, with optional hashing (of keys) to...
nonvirtual void Add(typename Common::ArgByValueType< KEY > key, typename Common::ArgByValueType< V > value)
nonvirtual optional< V > Lookup(typename Common::ArgByValueType< KEY > key)
nonvirtual VALUE LookupValue(typename Common::ArgByValueType< KEY > key, const function< VALUE(typename Common::ArgByValueType< KEY >)> &valueFetcher)
nonvirtual void RemoveAll(PREDICATE &&removeIfReturnsTrue)
nonvirtual Containers::Mapping< KEY, VALUE > Elements() const
nonvirtual void SetMaxCacheSize(size_t maxCacheSize)
nonvirtual bool Add(ArgByValueType< key_type > key, ArgByValueType< mapped_type > newElt, AddReplaceMode addReplaceMode=AddReplaceMode::eAddReplaces)
conditional_t<(sizeof(CHECK_T)<=2 *sizeof(void *)) and is_trivially_copyable_v< CHECK_T >, CHECK_T, const CHECK_T & > ArgByValueType
This is an alias for 'T' - but how we want to pass it on stack as formal parameter.
CONTAINER::value_type * Start(CONTAINER &c)
For a contiguous container (such as a vector or basic_string) - find the pointer to the start of the ...
Utility to make it easier to construct a LRUCache constexpr/type name from a few parameters and types...
static auto operator()(size_t maxCacheSize=1, KEY_EQUALS_COMPARER &&keyComparer={})
NOHASH versions.