core/SimpleLruCache.h
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #ifndef AVS_SIMPLELRUCACHE_H | ||
| 2 | #define AVS_SIMPLELRUCACHE_H | ||
| 3 | |||
| 4 | #include <list> | ||
| 5 | #include <functional> | ||
| 6 | #include <limits> | ||
| 7 | #include <avs/minmax.h> | ||
| 8 | |||
| 9 | template<typename K, typename V> | ||
| 10 | class SimpleLruCache | ||
| 11 | { | ||
| 12 | public: | ||
| 13 | struct Entry | ||
| 14 | { | ||
| 15 | K key; | ||
| 16 | V value; | ||
| 17 | |||
| 18 | 5 | Entry(const K& k) : | |
| 19 | 5 | key(k) | |
| 20 | 5 | {} | |
| 21 | }; | ||
| 22 | |||
| 23 | typedef V value_type; | ||
| 24 | typedef K key_type; | ||
| 25 | typedef typename std::list<Entry>::iterator entry_type; | ||
| 26 | |||
| 27 | typedef std::function<bool(SimpleLruCache*, const Entry&, void*)> EvictEventType; | ||
| 28 | |||
| 29 | private: | ||
| 30 | size_t MinCapacity; | ||
| 31 | size_t MaxCapacity; | ||
| 32 | size_t RequestedCapacity; | ||
| 33 | size_t RealCapacity; | ||
| 34 | std::list<Entry> Cache; | ||
| 35 | std::list<Entry> Pool; | ||
| 36 | |||
| 37 | void* EventUserData; | ||
| 38 | const EvictEventType EvictEvent; | ||
| 39 | |||
| 40 | public: | ||
| 41 | 10 | SimpleLruCache(size_t capacity, const EvictEventType&& evict, void* evData) : | |
| 42 | 10 | MinCapacity(0), | |
| 43 | 10 | MaxCapacity(std::numeric_limits<size_t>::max()), | |
| 44 | 10 | RequestedCapacity(capacity), | |
| 45 | 10 | RealCapacity(capacity), | |
| 46 | 10 | EventUserData(evData), | |
| 47 |
2/4SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruGhostEntry>::SimpleLruCache(unsigned long, std::function<bool (SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruGhostEntry>*, SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruGhostEntry>::Entry const&, void*)> const&&, void*):
✓ Branch 5 → 6 taken 5 times.
✗ Branch 5 → 7 not taken.
SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruEntry*>::SimpleLruCache(unsigned long, std::function<bool (SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruEntry*>*, SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruEntry*>::Entry const&, void*)> const&&, void*):
✓ Branch 5 → 6 taken 5 times.
✗ Branch 5 → 7 not taken.
|
10 | EvictEvent(evict) |
| 48 | { | ||
| 49 | 10 | } | |
| 50 | |||
| 51 | ✗ | size_t size() const | |
| 52 | { | ||
| 53 | ✗ | return Cache.size(); | |
| 54 | } | ||
| 55 | |||
| 56 | 5 | size_t requested_capacity() const | |
| 57 | { | ||
| 58 | 5 | return RequestedCapacity; | |
| 59 | } | ||
| 60 | |||
| 61 | 5 | size_t capacity() const | |
| 62 | { | ||
| 63 | 5 | return RealCapacity; | |
| 64 | } | ||
| 65 | |||
| 66 | ✗ | void limits(size_t* min, size_t* max) const | |
| 67 | { | ||
| 68 | ✗ | *min = MinCapacity; | |
| 69 | ✗ | *max = MaxCapacity; | |
| 70 | ✗ | } | |
| 71 | |||
| 72 | ✗ | void set_limits(size_t min, size_t max) | |
| 73 | { | ||
| 74 | ✗ | MinCapacity = min; | |
| 75 | ✗ | MaxCapacity = max; | |
| 76 | ✗ | resize(RequestedCapacity); | |
| 77 | ✗ | } | |
| 78 | |||
| 79 | 10 | V* lookup(const K& key, bool *found, bool lookuponly = false) | |
| 80 | { | ||
| 81 | // Look for an existing cache entry, | ||
| 82 | // and return it when found | ||
| 83 | 10 | const entry_type it_end = Cache.end(); | |
| 84 | 10 | for ( | |
| 85 | 10 | entry_type it = Cache.begin(); | |
| 86 |
2/4SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruGhostEntry>::lookup(unsigned long const&, bool*, bool):
✗ Branch 19 → 5 not taken.
✓ Branch 19 → 20 taken 5 times.
SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruEntry*>::lookup(unsigned long const&, bool*, bool):
✗ Branch 19 → 5 not taken.
✓ Branch 19 → 20 taken 5 times.
|
10 | it != it_end; |
| 87 | ✗ | ++it | |
| 88 | ) | ||
| 89 | { | ||
| 90 | ✗ | if (it->key == key) | |
| 91 | { | ||
| 92 | // Move found element to the front of the list | ||
| 93 | ✗ | if (it != Cache.begin()) | |
| 94 | ✗ | Cache.splice(Cache.begin(), Cache, it); | |
| 95 | |||
| 96 | ✗ | *found = true; | |
| 97 | ✗ | return &(Cache.front().value); | |
| 98 | } | ||
| 99 | } | ||
| 100 | |||
| 101 | // Nothing found | ||
| 102 | 10 | *found = false; | |
| 103 | |||
| 104 |
2/4SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruGhostEntry>::lookup(unsigned long const&, bool*, bool):
✗ Branch 20 → 22 not taken.
✓ Branch 20 → 23 taken 5 times.
SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruEntry*>::lookup(unsigned long const&, bool*, bool):
✗ Branch 20 → 22 not taken.
✓ Branch 20 → 23 taken 5 times.
|
10 | if (lookuponly) { |
| 105 | ✗ | return NULL; | |
| 106 | } | ||
| 107 | |||
| 108 | // Evict an old element if the cache is full | ||
| 109 |
2/4SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruGhostEntry>::lookup(unsigned long const&, bool*, bool):
✓ Branch 23 → 24 taken 5 times.
✗ Branch 23 → 40 not taken.
SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruEntry*>::lookup(unsigned long const&, bool*, bool):
✓ Branch 23 → 24 taken 5 times.
✗ Branch 23 → 40 not taken.
|
10 | trim(); |
| 110 | |||
| 111 |
2/4SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruGhostEntry>::lookup(unsigned long const&, bool*, bool):
✓ Branch 24 → 25 taken 5 times.
✗ Branch 24 → 37 not taken.
SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruEntry*>::lookup(unsigned long const&, bool*, bool):
✗ Branch 24 → 25 not taken.
✓ Branch 24 → 37 taken 5 times.
|
10 | if (RealCapacity != 0) |
| 112 | { | ||
| 113 | // See if we can take one from our pool | ||
| 114 |
1/4SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruGhostEntry>::lookup(unsigned long const&, bool*, bool):
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 34 taken 5 times.
SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruEntry*>::lookup(unsigned long const&, bool*, bool):
✗ Branch 26 → 27 not taken.
✗ Branch 26 → 34 not taken.
|
5 | if (!Pool.empty()) |
| 115 | { | ||
| 116 | ✗ | Cache.splice(Cache.begin(), Pool, Pool.begin()); | |
| 117 | ✗ | Cache.front().key = key; | |
| 118 | } | ||
| 119 | else | ||
| 120 | { | ||
| 121 |
1/4SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruGhostEntry>::lookup(unsigned long const&, bool*, bool):
✓ Branch 34 → 35 taken 5 times.
✗ Branch 34 → 40 not taken.
SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruEntry*>::lookup(unsigned long const&, bool*, bool):
✗ Branch 34 → 35 not taken.
✗ Branch 34 → 40 not taken.
|
5 | Cache.emplace_front(key); |
| 122 | } | ||
| 123 | |||
| 124 | 5 | return &(Cache.front().value); | |
| 125 | } | ||
| 126 | |||
| 127 | // Return NULL-storage if we cannot store anything | ||
| 128 | 5 | return NULL; | |
| 129 | } | ||
| 130 | |||
| 131 | ✗ | void remove(const K& key) | |
| 132 | { | ||
| 133 | ✗ | const entry_type it_end = Cache.end(); | |
| 134 | ✗ | for ( | |
| 135 | ✗ | entry_type it = Cache.begin(); | |
| 136 | ✗ | it != it_end; | |
| 137 | ✗ | ++it | |
| 138 | ) | ||
| 139 | { | ||
| 140 | ✗ | if (it->key == key) | |
| 141 | { | ||
| 142 | ✗ | Pool.splice(Pool.begin(), Cache, it); | |
| 143 | ✗ | break; | |
| 144 | } | ||
| 145 | } | ||
| 146 | ✗ | } | |
| 147 | |||
| 148 | 10 | void trim() | |
| 149 | { | ||
| 150 |
2/4SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruGhostEntry>::trim():
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 32 taken 5 times.
SimpleLruCache<unsigned long, LruCache<unsigned long, PVideoFrame>::LruEntry*>::trim():
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 32 taken 5 times.
|
10 | if (Cache.size() > RealCapacity) |
| 151 | { | ||
| 152 | ✗ | size_t nItemsToDelete = Cache.size() - RealCapacity; | |
| 153 | ✗ | auto it = --Cache.end(); | |
| 154 | ✗ | for (size_t i = 0; i < nItemsToDelete; ++i) | |
| 155 | { | ||
| 156 | ✗ | auto prev_it = it; | |
| 157 | ✗ | bool end = (it == Cache.begin()); | |
| 158 | ✗ | if (!end) | |
| 159 | { | ||
| 160 | ✗ | prev_it = it; | |
| 161 | ✗ | --prev_it; | |
| 162 | } | ||
| 163 | |||
| 164 | ✗ | if (EvictEvent != NULL) | |
| 165 | { | ||
| 166 | ✗ | if (EvictEvent(this, *it, EventUserData)) | |
| 167 | { | ||
| 168 | ✗ | Pool.splice(Pool.begin(), Cache, it); | |
| 169 | } | ||
| 170 | } | ||
| 171 | else | ||
| 172 | { | ||
| 173 | // TODO: Do we want the consumer to always define EvictItem? | ||
| 174 | ✗ | Pool.splice(Pool.begin(), Cache, it); | |
| 175 | } | ||
| 176 | |||
| 177 | ✗ | if (!end) | |
| 178 | ✗ | it = prev_it; | |
| 179 | } | ||
| 180 | } | ||
| 181 | 10 | } | |
| 182 | |||
| 183 | ✗ | void resize(size_t new_cap) | |
| 184 | { | ||
| 185 | ✗ | RequestedCapacity = new_cap; | |
| 186 | ✗ | RealCapacity = clamp(RequestedCapacity, MinCapacity, MaxCapacity); | |
| 187 | ✗ | trim(); | |
| 188 | ✗ | } | |
| 189 | }; | ||
| 190 | |||
| 191 | #endif // AVS_SIMPLELRUCACHE_H | ||
| 192 |