LCOV - code coverage report
Current view: top level - port - cpl_mem_cache.h (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 109 116 94.0 %
Date: 2025-07-02 23:05:47 Functions: 220 247 89.1 %

          Line data    Source code
       1             : /*
       2             :  * LRUCache11 - a templated C++11 based LRU cache class that allows
       3             :  * specification of
       4             :  * key, value and optionally the map container type (defaults to
       5             :  * std::unordered_map)
       6             :  * By using the std::map and a linked list of keys it allows O(1) insert, delete
       7             :  * and
       8             :  * refresh operations.
       9             :  *
      10             :  * This is a header-only library and all you need is the LRUCache11.hpp file
      11             :  *
      12             :  * Github: https://github.com/mohaps/lrucache11
      13             :  *
      14             :  * This is a follow-up to the LRUCache project -
      15             :  * https://github.com/mohaps/lrucache
      16             :  *
      17             :  * Copyright (c) 2012-22 SAURAV MOHAPATRA <mohaps@gmail.com>
      18             :  *
      19             :  * Permission to use, copy, modify, and distribute this software for any
      20             :  * purpose with or without fee is hereby granted, provided that the above
      21             :  * copyright notice and this permission notice appear in all copies.
      22             :  *
      23             :  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
      24             :  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
      25             :  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
      26             :  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
      27             :  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
      28             :  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
      29             :  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
      30             :  */
      31             : 
      32             : /*! @cond Doxygen_Suppress */
      33             : 
      34             : #pragma once
      35             : #include <algorithm>
      36             : #include <cstdint>
      37             : #include <list>
      38             : #include <mutex>
      39             : #include <stdexcept>
      40             : #include <thread>
      41             : #include <unordered_map>
      42             : 
      43             : namespace lru11
      44             : {
      45             : /*
      46             :  * a noop lockable concept that can be used in place of std::mutex
      47             :  */
      48             : class NullLock
      49             : {
      50             :   public:
      51     5913366 :     void lock()
      52             :     {
      53     5913366 :     }
      54             : 
      55     5914836 :     void unlock()
      56             :     {
      57     5914836 :     }
      58             : 
      59             :     bool try_lock()
      60             :     {
      61             :         return true;
      62             :     }
      63             : };
      64             : 
      65             : #if defined(__clang__)
      66             : #pragma clang diagnostic push
      67             : #pragma clang diagnostic ignored "-Wweak-vtables"
      68             : #endif
      69             : 
      70             : /**
      71             :  * error raised when a key not in cache is passed to get()
      72             :  */
      73             : class KeyNotFound : public std::invalid_argument
      74             : {
      75             :   public:
      76           1 :     KeyNotFound() : std::invalid_argument("key_not_found")
      77             :     {
      78           1 :     }
      79             : };
      80             : 
      81             : #if defined(__clang__)
      82             : #pragma clang diagnostic pop
      83             : #endif
      84             : 
      85             : template <typename K, typename V> struct KeyValuePair
      86             : {
      87             :   public:
      88             :     K key;
      89             :     V value;
      90             : 
      91        5016 :     KeyValuePair(const K &k, const V &v) : key(k), value(v)
      92             :     {
      93        4986 :     }
      94             : 
      95       16801 :     KeyValuePair(const K &k, V &&v) : key(k), value(std::move(v))
      96             :     {
      97       16801 :     }
      98             : 
      99             :   private:
     100             :     KeyValuePair(const KeyValuePair &) = delete;
     101             :     KeyValuePair &operator=(const KeyValuePair &) = delete;
     102             : };
     103             : 
     104             : /**
     105             :  *  The LRU Cache class templated by
     106             :  *    Key - key type
     107             :  *    Value - value type
     108             :  *    MapType - an associative container like std::unordered_map
     109             :  *    LockType - a lock type derived from the Lock class (default:
     110             :  *NullLock = no synchronization)
     111             :  *
     112             :  *  The default NullLock based template is not thread-safe, however passing
     113             :  *Lock=std::mutex will make it
     114             :  *  thread-safe
     115             :  */
     116             : template <class Key, class Value, class Lock = NullLock,
     117             :           class Map = std::unordered_map<
     118             :               Key, typename std::list<KeyValuePair<Key, Value>>::iterator>>
     119             : class Cache
     120             : {
     121             :   public:
     122             :     typedef KeyValuePair<Key, Value> node_type;
     123             :     typedef std::list<KeyValuePair<Key, Value>> list_type;
     124             :     typedef Map map_type;
     125             :     typedef Lock lock_type;
     126             :     using Guard = std::lock_guard<lock_type>;
     127             : 
     128             :     /**
     129             :      * the max size is the hard limit of keys and (maxSize + elasticity) is the
     130             :      * soft limit
     131             :      * the cache is allowed to grow till maxSize + elasticity and is pruned back
     132             :      * to maxSize keys
     133             :      * set maxSize = 0 for an unbounded cache (but in that case, you're better
     134             :      * off using a std::unordered_map directly anyway! :)
     135             :      */
     136       91087 :     explicit Cache(size_t maxSize = 64, size_t elasticity = 10)
     137       91087 :         : maxSize_(maxSize), elasticity_(elasticity)
     138             :     {
     139       90917 :     }
     140             : 
     141       86990 :     virtual ~Cache() = default;
     142             : 
     143         820 :     size_t size() const
     144             :     {
     145        1640 :         Guard g(lock_);
     146         820 :         return cache_.size();
     147             :     }
     148             : 
     149        4253 :     bool empty() const
     150             :     {
     151        8506 :         Guard g(lock_);
     152        4253 :         return cache_.empty();
     153             :     }
     154             : 
     155       63009 :     void clear()
     156             :     {
     157      126018 :         Guard g(lock_);
     158       63009 :         cache_.clear();
     159       63009 :         keys_.clear();
     160       63009 :     }
     161             : 
     162        6841 :     void insert(const Key &k, const Value &v)
     163             :     {
     164        6841 :         Guard g(lock_);
     165        6705 :         const auto iter = cache_.find(k);
     166        6777 :         if (iter != cache_.end())
     167             :         {
     168        1764 :             iter->second->value = v;
     169        1769 :             keys_.splice(keys_.begin(), keys_, iter->second);
     170        1771 :             return;
     171             :         }
     172             : 
     173        5011 :         keys_.emplace_front(k, v);
     174        5020 :         cache_[k] = keys_.begin();
     175        4950 :         prune();
     176             :     }
     177             : 
     178       17484 :     Value &insert(const Key &k, Value &&v)
     179             :     {
     180       34968 :         Guard g(lock_);
     181       17484 :         const auto iter = cache_.find(k);
     182       17485 :         if (iter != cache_.end())
     183             :         {
     184         683 :             iter->second->value = std::move(v);
     185         683 :             keys_.splice(keys_.begin(), keys_, iter->second);
     186         683 :             return keys_.front().value;
     187             :         }
     188             : 
     189       16802 :         keys_.emplace_front(k, std::move(v));
     190       16799 :         cache_[k] = keys_.begin();
     191       16801 :         prune();
     192       16801 :         return keys_.front().value;
     193             :     }
     194             : 
     195     5585207 :     bool tryGet(const Key &kIn, Value &vOut)
     196             :     {
     197    11169912 :         Guard g(lock_);
     198     5581850 :         const auto iter = cache_.find(kIn);
     199     5584883 :         if (iter == cache_.end())
     200             :         {
     201     4430734 :             return false;
     202             :         }
     203     1151310 :         keys_.splice(keys_.begin(), keys_, iter->second);
     204     1152643 :         vOut = iter->second->value;
     205     1153964 :         return true;
     206             :     }
     207             : 
     208             :     /**
     209             :      *  The const reference returned here is only
     210             :      *    guaranteed to be valid till the next insert/delete
     211             :      */
     212           4 :     const Value &get(const Key &k)
     213             :     {
     214           5 :         Guard g(lock_);
     215           4 :         const auto iter = cache_.find(k);
     216           4 :         if (iter == cache_.end())
     217             :         {
     218           1 :             throw KeyNotFound();
     219             :         }
     220           3 :         keys_.splice(keys_.begin(), keys_, iter->second);
     221           6 :         return iter->second->value;
     222             :     }
     223             : 
     224      194168 :     Value *getPtr(const Key &k)
     225             :     {
     226      388337 :         Guard g(lock_);
     227      194168 :         const auto iter = cache_.find(k);
     228      194169 :         if (iter == cache_.end())
     229             :         {
     230       14321 :             return nullptr;
     231             :         }
     232      179848 :         keys_.splice(keys_.begin(), keys_, iter->second);
     233      179848 :         return &(iter->second->value);
     234             :     }
     235             : 
     236             :     /**
     237             :      * returns a copy of the stored object (if found)
     238             :      */
     239           1 :     Value getCopy(const Key &k)
     240             :     {
     241           1 :         return get(k);
     242             :     }
     243             : 
     244        3015 :     bool remove(const Key &k)
     245             :     {
     246        6030 :         Guard g(lock_);
     247        3015 :         auto iter = cache_.find(k);
     248        3015 :         if (iter == cache_.end())
     249             :         {
     250         105 :             return false;
     251             :         }
     252        2910 :         keys_.erase(iter->second);
     253        2910 :         cache_.erase(iter);
     254        2910 :         return true;
     255             :     }
     256             : 
     257       16451 :     bool contains(const Key &k)
     258             :     {
     259       16451 :         Guard g(lock_);
     260       32902 :         return cache_.find(k) != cache_.end();
     261             :     }
     262             : 
     263           0 :     bool getOldestEntry(Key &kOut, Value &vOut)
     264             :     {
     265           0 :         Guard g(lock_);
     266           0 :         if (keys_.empty())
     267             :         {
     268           0 :             return false;
     269             :         }
     270           0 :         kOut = keys_.back().key;
     271           0 :         vOut = keys_.back().value;
     272           0 :         return true;
     273             :     }
     274             : 
     275         137 :     bool removeAndRecycleOldestEntry(Value &vOut)
     276             :     {
     277         274 :         Guard g(lock_);
     278         137 :         if (keys_.empty())
     279             :         {
     280           1 :             return false;
     281             :         }
     282         136 :         vOut = std::move(keys_.back().value);
     283         136 :         cache_.erase(keys_.back().key);
     284         136 :         keys_.pop_back();
     285         136 :         return true;
     286             :     }
     287             : 
     288         653 :     size_t getMaxSize() const
     289             :     {
     290         653 :         return maxSize_;
     291             :     }
     292             : 
     293           1 :     size_t getElasticity() const
     294             :     {
     295           1 :         return elasticity_;
     296             :     }
     297             : 
     298         163 :     size_t getMaxAllowedSize() const
     299             :     {
     300         163 :         return maxSize_ + elasticity_;
     301             :     }
     302             : 
     303       25123 :     template <typename F> void cwalk(F &f) const
     304             :     {
     305       49980 :         Guard g(lock_);
     306       25123 :         std::for_each(keys_.begin(), keys_.end(), f);
     307       25123 :     }
     308             : 
     309             :     Cache(Cache &&other)
     310             :         : cache_(std::move(other.cache_)), keys_(std::move(other.keys_)),
     311             :           maxSize_(other.maxSize_), elasticity_(other.elasticity_)
     312             :     {
     313             :     }
     314             : 
     315             :   protected:
     316       21840 :     size_t prune()
     317             :     {
     318       21840 :         size_t maxAllowed = maxSize_ + elasticity_;
     319       21840 :         if (maxSize_ == 0 || cache_.size() <= maxAllowed)
     320             :         { /* ERO: changed < to <= */
     321       20470 :             return 0;
     322             :         }
     323        1367 :         size_t count = 0;
     324       10584 :         while (cache_.size() > maxSize_)
     325             :         {
     326        9211 :             cache_.erase(keys_.back().key);
     327        9217 :             keys_.pop_back();
     328        9217 :             ++count;
     329             :         }
     330        1278 :         return count;
     331             :     }
     332             : 
     333             :   private:
     334             :     // Disallow copying.
     335             :     Cache(const Cache &) = delete;
     336             :     Cache &operator=(const Cache &) = delete;
     337             : 
     338             :     mutable Lock lock_{};
     339             :     Map cache_{};
     340             :     list_type keys_{};
     341             :     size_t maxSize_;
     342             :     size_t elasticity_;
     343             : };
     344             : 
     345             : }  // namespace lru11
     346             : 
     347             : /*! @endcond */

Generated by: LCOV version 1.14