olp-cpp-sdk 1.24.0
Loading...
Searching...
No Matches
LruCache.h
1/*
2 * Copyright (C) 2019-2025 HERE Europe B.V.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 *
16 * SPDX-License-Identifier: Apache-2.0
17 * License-Filename: LICENSE
18 */
19
20#pragma once
21
22#include <cassert>
23#include <cstddef>
24#include <functional>
25#include <map>
26#include <utility>
27
28#include <olp/core/porting/try_emplace.h>
29
30namespace olp {
31namespace utils {
40template <typename T>
41struct CacheCost {
43 std::size_t operator()(const T&) const { return 1; }
44};
45
66template <typename Key, typename Value,
67 typename CacheCostFunc = CacheCost<Value>,
68 typename Compare = std::less<Key>,
69 template <typename> class Alloc = std::allocator>
70class LruCache {
71 struct Bucket;
72 using MapType =
73 std::map<Key, Bucket, Compare, Alloc<std::pair<const Key, Bucket> > >;
74
75 public:
77 using EvictionFunction = std::function<void(const Key&, Value&&)>;
78
80 using CompareType = typename MapType::key_compare;
81
83 using AllocType = typename MapType::allocator_type;
84
90 class ValueType {
91 public:
97 inline const Key& key() const;
98
104 inline const Value& value() const;
105
106 protected:
108 typename MapType::const_iterator m_it;
109 };
110
112 class const_iterator : public ValueType {
113 public:
115 typedef std::bidirectional_iterator_tag iterator_category;
117 typedef std::ptrdiff_t difference_type;
121 typedef const value_type& reference;
123 typedef const value_type* pointer;
124
126 const_iterator() = default;
135
144 inline bool operator==(const const_iterator& other) const;
153 inline bool operator!=(const const_iterator& other) const {
154 return !operator==(other);
155 }
156
162 inline const_iterator& operator++();
169 inline const_iterator operator++(int);
170
176 inline const_iterator& operator--();
183 inline const_iterator operator--(int);
184
190 reference operator*() const { return *this; }
191
197 pointer operator->() const { return this; }
198
199 private:
200 template <typename, typename, typename, typename, template <typename> class>
201 friend class LruCache;
202
203 explicit const_iterator(const typename MapType::const_iterator& it) {
204 this->m_it = it;
205 }
206
207 explicit const_iterator(const typename MapType::iterator& it) {
208 this->m_it = it;
209 }
210 };
211
220 explicit LruCache(const AllocType& alloc = AllocType())
221 : map_(alloc),
222 first_(map_.end()),
223 last_(map_.end()),
224 max_size_(0),
225 size_(0) {}
226
236 LruCache(std::size_t maxSize, CacheCostFunc cacheCostFunc = CacheCostFunc(),
237 const CompareType& compare = CompareType(),
238 const AllocType& alloc = AllocType())
239 : cache_cost_func_(std::move(cacheCostFunc)),
240 map_(compare, alloc),
241 first_(map_.end()),
242 last_(map_.end()),
243 max_size_(maxSize),
244 size_(0) {}
245
247 LruCache(const LruCache&) = delete;
248
250 LruCache(LruCache&& other) noexcept
251 : eviction_callback_(std::move(other.eviction_callback_)),
252 cache_cost_func_(std::move(other.cache_cost_func_)),
253 map_(std::move(other.map_)),
254 first_(other.first_ != map_.end() ? map_.find(other.first_->first)
255 : map_.end()),
256 last_(other.last_ != map_.end() ? map_.find(other.last_->first)
257 : map_.end()),
258 max_size_(other.max_size_),
259 size_(other.size_) {}
260
262 LruCache& operator=(const LruCache&) = delete;
263
265 LruCache& operator=(LruCache&& other) noexcept {
266 eviction_callback_ = std::move(other.eviction_callback_);
267 cache_cost_func_ = std::move(other.cache_cost_func_);
268 map_ = std::move(other.map_);
269 first_ = other.first_ != map_.end() ? map_.find(other.first_->first)
270 : map_.end();
271 last_ =
272 other.last_ != map_.end() ? map_.find(other.last_->first) : map_.end();
273 std::swap(max_size_, other.max_size_);
274 std::swap(size_, other.size_);
275
276 return *this;
277 }
278
303 template <typename _Key, typename _Value>
304 std::pair<const_iterator, bool> Insert(_Key&& key, _Value&& value);
305
322 template <typename _Value>
323 std::pair<const_iterator, bool> InsertOrAssign(Key key, _Value&& value);
324
333 bool Erase(const Key& key) {
334 auto it = map_.find(key);
335 if (it == map_.end())
336 return false;
337
338 Erase(it, false);
339 return true;
340 }
341
350 auto prev = it++;
351
352 Erase(prev->key());
353
354 return it;
355 }
356
362 std::size_t Size() const { return size_; }
363
369 std::size_t GetMaxSize() const { return max_size_; }
370
379 void Resize(size_t maxSize) {
380 max_size_ = maxSize;
381 evict();
382 }
383
394 const_iterator Find(const Key& key) {
395 auto it = map_.find(key);
396 if (it != map_.end())
397 Promote(it);
398 return const_iterator{it};
399 }
400
411 const_iterator FindNoPromote(const Key& key) const {
412 auto it = map_.find(key);
413 return const_iterator{it};
414 }
415
426 const Value& Find(const Key& key, const Value& nullValue) {
427 auto it = Find(key);
428 return it == end() ? nullValue : it.value();
429 }
430
432 const_iterator begin() const { return const_iterator{first_}; }
433
435 const_iterator end() const { return const_iterator{map_.end()}; }
436
438 const_iterator rbegin() const { return const_iterator{last_}; }
439
441 const_iterator rend() const { return const_iterator{map_.end()}; }
442
449 void Clear() {
450 map_.clear();
451 first_ = last_ = map_.end();
452 size_ = 0u;
453 }
454
468 eviction_callback_ = std::move(func);
469 }
470
471 private:
472 friend struct CacheLruChecker; // for unit-tests
473
474 EvictionFunction eviction_callback_;
475 CacheCostFunc cache_cost_func_;
476 MapType map_;
477 typename MapType::iterator first_;
478 typename MapType::iterator last_;
479 std::size_t max_size_;
480 std::size_t size_;
481
482 // Checks whether two keys are equal using only the comparison
483 // operator.
484 inline bool IsEqual(const Key& key1, const Key& key2) {
485 auto comp = map_.key_comp();
486 return !comp(key1, key2) && !comp(key2, key1);
487 }
488
489 // The bucket that contains the value and the pointer to the previous
490 // and next items in the LRU chain.
491 struct Bucket {
492 using Iterator = typename MapType::iterator;
493
494 // note - these constructors are only here because MSVC2013 doesn't
495 // auto-generate them as the standard mandates
496#if defined(_MSC_VER) && _MSC_VER < 1900
497 inline Bucket(Iterator next, Iterator previous, Value&& value)
498 : next_(std::move(next)),
499 previous_(std::move(previous)),
500 value_(std::move(value)) {}
501 inline Bucket(Iterator next, Iterator previous, const Value& value)
502 : next_(std::move(next)),
503 previous_(std::move(previous)),
504 value_(value) {}
505 inline Bucket(const Bucket&) = delete;
506 inline Bucket(Bucket&& other)
507 : next_(std::move(other.next_)),
508 previous_(std::move(other.previous_)),
509 value_(std::move(other.value_)) {}
510#endif
511
512 Iterator next_;
513 Iterator previous_;
514 Value value_;
515
516 inline void setNext(Iterator it) { next_ = std::move(it); }
517
518 inline void setPrevious(Iterator it) { previous_ = std::move(it); }
519 };
520
521 void Erase(typename MapType::iterator it, bool);
522 void AddInternal(const typename MapType::iterator& it, std::size_t cost,
523 std::size_t* oldCost = nullptr);
524 void Promote(const typename MapType::iterator& it);
525 void PopLast();
526 void evict() {
527 while (size_ > max_size_)
528 PopLast();
529 }
530};
531
532template <typename Key, typename Value, typename CacheCostFunc,
533 typename Compare, template <typename> class Alloc>
534template <typename _Key, typename _Value>
535
559 _Value&& value)
560 -> std::pair<const_iterator, bool> {
561 Bucket bucket{map_.end(), map_.end(), std::forward<_Value>(value)};
562
563 // If the item is too large, do not insert it.
564 std::size_t valueCost = cache_cost_func_(bucket.value_);
565 if (valueCost > max_size_)
566 return std::make_pair(const_iterator{end()}, false);
567
568 auto it =
569 porting::try_emplace(map_, std::forward<_Key>(key), std::move(bucket));
570
571 if (it.second) {
572 AddInternal(it.first, valueCost);
573 return std::make_pair(const_iterator{it.first}, true);
574 }
575 Promote(it.first);
576 return std::make_pair(const_iterator{it.first}, false);
577}
578
579template <typename Key, typename Value, typename CacheCostFunc,
580 typename Compare, template <typename> class Alloc>
581template <typename _Value>
582
599 Key key, _Value&& value) -> std::pair<const_iterator, bool> {
600 // as we need "key" with the right type a couple of times, create a temp
601 // copy to prevent implicit conversions below
602 auto it = map_.lower_bound(key);
603 if (it != map_.end() && IsEqual(key, it->first)) {
604 // element already exists, update it
605 std::size_t oldCost = cache_cost_func_(it->second.value_);
606 it->second.value_ = std::forward<_Value>(value);
607 std::size_t newCost = cache_cost_func_(it->second.value_);
608 AddInternal(it, newCost, &oldCost);
609 return std::make_pair(const_iterator{it}, false);
610 } else {
611 // element doesn't exist, insert it
612 Bucket bucket{map_.end(), map_.end(), std::forward<_Value>(value)};
613 std::size_t newCost = cache_cost_func_(bucket.value_);
614 if (newCost > max_size_)
615 return std::make_pair(const_iterator{end()}, false);
616
617 auto new_it =
618 map_.insert(it, std::make_pair(std::move(key), std::move(bucket)));
619 AddInternal(new_it, newCost);
620 return std::make_pair(const_iterator{new_it}, true);
621 }
622}
623
624template <typename Key, typename Value, typename CacheCostFunc,
625 typename Compare, template <typename> class Alloc>
627 const typename MapType::iterator& it) {
628 if (it == first_)
629 return; // nothing to do
630
631 // re-link previous and next buckets together
632 it->second.previous_->second.setNext(it->second.next_);
633 if (it->second.next_ != map_.end())
634 it->second.next_->second.setPrevious(it->second.previous_);
635 else
636 last_ = it->second.previous_;
637
638 // re-link our bucket
639 it->second.setPrevious(map_.end());
640 it->second.setNext(first_);
641
642 // update current head to ourselves
643 first_->second.setPrevious(it);
644 first_ = it;
645}
646
647template <typename Key, typename Value, typename CacheCostFunc,
648 typename Compare, template <typename> class Alloc>
649void LruCache<Key, Value, CacheCostFunc, Compare, Alloc>::AddInternal(
650 const typename MapType::iterator& it, std::size_t cost,
651 std::size_t* oldCost) {
652 if (!oldCost) {
653 // new bucket added
654 if (map_.size() == 1) {
655 // we're the first and only one.
656 assert(last_ == map_.end() && first_ == map_.end());
657 last_ = first_ = it;
658 } else {
659 // relink first bucket
660 it->second.setNext(first_);
661 first_->second.setPrevious(it);
662 first_ = it;
663 }
664 size_ += cost;
665 } else {
666 // key already in map - only promote
667 size_ += cost - *oldCost;
668 Promote(it);
669 }
670
671 evict();
672}
673
674template <typename Key, typename Value, typename CacheCostFunc,
675 typename Compare, template <typename> class Alloc>
677 typename MapType::iterator it, bool doEvictionCallback) {
678 std::size_t cost = cache_cost_func_(it->second.value_);
679
680 if (it->second.next_ == map_.end())
681 last_ = it->second.previous_;
682 else
683 it->second.next_->second.setPrevious(it->second.previous_);
684
685 if (it->second.previous_ == map_.end())
686 first_ = it->second.next_;
687 else
688 it->second.previous_->second.setNext(it->second.next_);
689
690 if (doEvictionCallback && eviction_callback_)
691 eviction_callback_(it->first, std::move(it->second.value_));
692
693 map_.erase(it);
694 size_ -= cost;
695}
696
697template <typename Key, typename Value, typename CacheCostFunc,
698 typename Compare, template <typename> class Alloc>
699void LruCache<Key, Value, CacheCostFunc, Compare, Alloc>::PopLast() {
700 // assert if the cache is empty
701 assert(last_ != map_.end());
702 // assert that our item's 'next' is pointing indeed to the end of the cache
703 assert(last_->second.next_ == map_.end());
704
705 Erase(last_, true);
706}
707
708template <typename Key, typename Value, typename CacheCostFunc,
709 typename Compare, template <typename> class Alloc>
710inline const Key&
714
715template <typename Key, typename Value, typename CacheCostFunc,
716 typename Compare, template <typename> class Alloc>
717inline const Value&
721
722template <typename Key, typename Value, typename CacheCostFunc,
723 typename Compare, template <typename> class Alloc>
724inline bool
729
730template <typename Key, typename Value, typename CacheCostFunc,
731 typename Compare, template <typename> class Alloc>
732inline typename LruCache<Key, Value, CacheCostFunc, Compare,
733 Alloc>::const_iterator&
735operator++() {
736 this->m_it = this->m_it->second.next_;
737 return *this;
738}
739
740template <typename Key, typename Value, typename CacheCostFunc,
741 typename Compare, template <typename> class Alloc>
742inline
745 operator++(int) {
746 typename MapType::const_iterator old_value = this->m_it;
747 this->m_it = this->m_it->second.next_;
748 return const_iterator{old_value};
749}
750
751template <typename Key, typename Value, typename CacheCostFunc,
752 typename Compare, template <typename> class Alloc>
753inline typename LruCache<Key, Value, CacheCostFunc, Compare,
754 Alloc>::const_iterator&
756operator--() {
757 this->m_it = this->m_it->second.previous_;
758 return *this;
759}
760
761template <typename Key, typename Value, typename CacheCostFunc,
762 typename Compare, template <typename> class Alloc>
763inline
766 operator--(int) {
767 typename MapType::const_iterator old_value = this->m_it;
768 this->m_it = this->m_it->second.previous_;
769 return const_iterator{old_value};
770}
771
772} // namespace utils
773} // namespace olp
A type of objects to be stored.
Definition LruCache.h:90
const Key & key() const
Gets the key of the ValueType object.
Definition LruCache.h:711
MapType::const_iterator m_it
A typename of the map constant iterator.
Definition LruCache.h:108
const Value & value() const
Gets the value of the ValueType object.
Definition LruCache.h:718
A constant iterator of the LruCache object.
Definition LruCache.h:112
bool operator!=(const const_iterator &other) const
Checks whether the values of the const_iterator parameter are not the same as the values of the other...
Definition LruCache.h:153
std::ptrdiff_t difference_type
A typedef for the difference type.
Definition LruCache.h:117
const value_type & reference
A typedef for the ValueType constant reference.
Definition LruCache.h:121
const_iterator()=default
Creates a constant iterator object.
std::bidirectional_iterator_tag iterator_category
A typedef for the iterator category.
Definition LruCache.h:115
const_iterator(const const_iterator &)=default
Creates a constant iterator object.
pointer operator->() const
Gets a pointer to this object.
Definition LruCache.h:197
reference operator*() const
Gets a reference to this object.
Definition LruCache.h:190
const_iterator & operator=(const const_iterator &)=default
Copies this and the specified iterator to this.
bool operator==(const const_iterator &other) const
Checks whether the values of the const_iterator parameter are the same as the values of the other par...
Definition LruCache.h:725
const_iterator & operator++()
Iterates to the next LruCache object.
Definition LruCache.h:735
ValueType value_type
A typedef for the ValueType type.
Definition LruCache.h:119
const value_type * pointer
A typedef for the ValueType constant pointer.
Definition LruCache.h:123
const_iterator & operator--()
Iterates to the previous LruCache object.
Definition LruCache.h:756
A generic key-value LRU cache.
Definition LruCache.h:70
std::size_t Size() const
Gets the current size of the cache.
Definition LruCache.h:362
const_iterator Erase(const_iterator &it)
Removes a key from the cache.
Definition LruCache.h:349
const_iterator FindNoPromote(const Key &key) const
Finds a value in the cache.
Definition LruCache.h:411
void Resize(size_t maxSize)
Sets the new maximum size of the cache.
Definition LruCache.h:379
typename MapType::key_compare CompareType
An alias for the key comparison function.
Definition LruCache.h:80
LruCache(const AllocType &alloc=AllocType())
Creates an LruCache instance.
Definition LruCache.h:220
void SetEvictionCallback(EvictionFunction func)
Sets a function that is invoked when a value is evicted from the cache.
Definition LruCache.h:467
bool Erase(const Key &key)
Removes a key from the cache.
Definition LruCache.h:333
std::function< void(const Key &, Value &&)> EvictionFunction
An alias for the eviction function.
Definition LruCache.h:77
typename MapType::allocator_type AllocType
An alias for the cache allocator type.
Definition LruCache.h:83
LruCache(std::size_t maxSize, CacheCostFunc cacheCostFunc=CacheCostFunc(), const CompareType &compare=CompareType(), const AllocType &alloc=AllocType())
Creates an LruCache instance.
Definition LruCache.h:236
const_iterator rend() const
Returns a reverse constant iterator to the end.
Definition LruCache.h:441
std::pair< const_iterator, bool > InsertOrAssign(Key key, _Value &&value)
Inserts a key-value pair in the cache or updates an existing key-value pair.
const Value & Find(const Key &key, const Value &nullValue)
Finds a value in the cache.
Definition LruCache.h:426
const_iterator rbegin() const
Returns a reverse constant iterator to the beginning.
Definition LruCache.h:438
const_iterator Find(const Key &key)
Finds a value in the cache.
Definition LruCache.h:394
std::pair< const_iterator, bool > Insert(_Key &&key, _Value &&value)
Inserts a key-value pair in the cache.
void Clear()
Removes all items from the cache.
Definition LruCache.h:449
LruCache(const LruCache &)=delete
The deleted copy constructor.
std::size_t GetMaxSize() const
Gets the maximum size of the cache.
Definition LruCache.h:369
LruCache & operator=(const LruCache &)=delete
The deleted assignment operator.
LruCache(LruCache &&other) noexcept
The default move constructor.
Definition LruCache.h:250
LruCache & operator=(LruCache &&other) noexcept
The default move assignment operator.
Definition LruCache.h:265
const_iterator begin() const
Returns a constant iterator to the beginning.
Definition LruCache.h:432
const_iterator end() const
Returns a constant iterator to the end.
Definition LruCache.h:435
Rules all the other namespaces.
Definition AppleSignInProperties.h:24
The default cost operator for LruCache.
Definition LruCache.h:41
std::size_t operator()(const T &) const
Gets the size of the specified object.
Definition LruCache.h:43