1*6777b538SAndroid Build Coastguard Worker // Copyright 2012 The Chromium Authors 2*6777b538SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be 3*6777b538SAndroid Build Coastguard Worker // found in the LICENSE file. 4*6777b538SAndroid Build Coastguard Worker 5*6777b538SAndroid Build Coastguard Worker #ifndef NET_BASE_EXPIRING_CACHE_H_ 6*6777b538SAndroid Build Coastguard Worker #define NET_BASE_EXPIRING_CACHE_H_ 7*6777b538SAndroid Build Coastguard Worker 8*6777b538SAndroid Build Coastguard Worker #include <stddef.h> 9*6777b538SAndroid Build Coastguard Worker 10*6777b538SAndroid Build Coastguard Worker #include <map> 11*6777b538SAndroid Build Coastguard Worker #include <utility> 12*6777b538SAndroid Build Coastguard Worker 13*6777b538SAndroid Build Coastguard Worker #include "base/gtest_prod_util.h" 14*6777b538SAndroid Build Coastguard Worker #include "base/memory/raw_ref.h" 15*6777b538SAndroid Build Coastguard Worker #include "base/time/time.h" 16*6777b538SAndroid Build Coastguard Worker 17*6777b538SAndroid Build Coastguard Worker namespace net { 18*6777b538SAndroid Build Coastguard Worker 19*6777b538SAndroid Build Coastguard Worker template <typename KeyType, 20*6777b538SAndroid Build Coastguard Worker typename ValueType, 21*6777b538SAndroid Build Coastguard Worker typename ExpirationType> 22*6777b538SAndroid Build Coastguard Worker class NoopEvictionHandler { 23*6777b538SAndroid Build Coastguard Worker public: Handle(const KeyType & key,const ValueType & value,const ExpirationType & expiration,const ExpirationType & now,bool onGet)24*6777b538SAndroid Build Coastguard Worker void Handle(const KeyType& key, 25*6777b538SAndroid Build Coastguard Worker const ValueType& value, 26*6777b538SAndroid Build Coastguard Worker const ExpirationType& expiration, 27*6777b538SAndroid Build Coastguard Worker const ExpirationType& now, 28*6777b538SAndroid Build Coastguard Worker bool onGet) const { 29*6777b538SAndroid Build Coastguard Worker } 30*6777b538SAndroid Build Coastguard Worker }; 31*6777b538SAndroid Build Coastguard Worker 32*6777b538SAndroid Build Coastguard Worker // Cache implementation where all entries have an explicit expiration policy. As 33*6777b538SAndroid Build Coastguard Worker // new items are added, expired items will be removed first. 34*6777b538SAndroid Build Coastguard Worker // The template types have the following requirements: 35*6777b538SAndroid Build Coastguard Worker // KeyType must be LessThanComparable, Assignable, and CopyConstructible. 36*6777b538SAndroid Build Coastguard Worker // ValueType must be CopyConstructible and Assignable. 37*6777b538SAndroid Build Coastguard Worker // ExpirationType must be CopyConstructible and Assignable. 38*6777b538SAndroid Build Coastguard Worker // ExpirationCompare is a function class that takes two arguments of the 39*6777b538SAndroid Build Coastguard Worker // type ExpirationType and returns a bool. If |comp| is an instance of 40*6777b538SAndroid Build Coastguard Worker // ExpirationCompare, then the expression |comp(current, expiration)| shall 41*6777b538SAndroid Build Coastguard Worker // return true iff |current| is still valid within |expiration|. 42*6777b538SAndroid Build Coastguard Worker // 43*6777b538SAndroid Build Coastguard Worker // A simple use of this class may use base::TimeTicks, which provides a 44*6777b538SAndroid Build Coastguard Worker // monotonically increasing clock, for the expiration type. Because it's always 45*6777b538SAndroid Build Coastguard Worker // increasing, std::less<> can be used, which will simply ensure that |now| is 46*6777b538SAndroid Build Coastguard Worker // sorted before |expiration|: 47*6777b538SAndroid Build Coastguard Worker // 48*6777b538SAndroid Build Coastguard Worker // ExpiringCache<std::string, std::string, base::TimeTicks, 49*6777b538SAndroid Build Coastguard Worker // std::less<base::TimeTicks> > cache(0); 50*6777b538SAndroid Build Coastguard Worker // // Add a value that expires in 5 minutes 51*6777b538SAndroid Build Coastguard Worker // cache.Put("key1", "value1", base::TimeTicks::Now(), 52*6777b538SAndroid Build Coastguard Worker // base::TimeTicks::Now() + base::Minutes(5)); 53*6777b538SAndroid Build Coastguard Worker // // Add another value that expires in 10 minutes. 54*6777b538SAndroid Build Coastguard Worker // cache.Put("key2", "value2", base::TimeTicks::Now(), 55*6777b538SAndroid Build Coastguard Worker // base::TimeTicks::Now() + base::Minutes(10)); 56*6777b538SAndroid Build Coastguard Worker // 57*6777b538SAndroid Build Coastguard Worker // Alternatively, there may be some more complex expiration criteria, at which 58*6777b538SAndroid Build Coastguard Worker // point a custom functor may be used: 59*6777b538SAndroid Build Coastguard Worker // 60*6777b538SAndroid Build Coastguard Worker // struct ComplexExpirationFunctor { 61*6777b538SAndroid Build Coastguard Worker // bool operator()(const ComplexExpiration& now, 62*6777b538SAndroid Build Coastguard Worker // const ComplexExpiration& expiration) const; 63*6777b538SAndroid Build Coastguard Worker // }; 64*6777b538SAndroid Build Coastguard Worker // ExpiringCache<std::string, std::string, ComplexExpiration, 65*6777b538SAndroid Build Coastguard Worker // ComplexExpirationFunctor> cache(15); 66*6777b538SAndroid Build Coastguard Worker // // Add a value that expires once the 'sprocket' has 'cog'-ified. 67*6777b538SAndroid Build Coastguard Worker // cache.Put("key1", "value1", ComplexExpiration("sprocket"), 68*6777b538SAndroid Build Coastguard Worker // ComplexExpiration("cog")); 69*6777b538SAndroid Build Coastguard Worker template <typename KeyType, 70*6777b538SAndroid Build Coastguard Worker typename ValueType, 71*6777b538SAndroid Build Coastguard Worker typename ExpirationType, 72*6777b538SAndroid Build Coastguard Worker typename ExpirationCompare, 73*6777b538SAndroid Build Coastguard Worker typename EvictionHandler = NoopEvictionHandler<KeyType, 74*6777b538SAndroid Build Coastguard Worker ValueType, 75*6777b538SAndroid Build Coastguard Worker ExpirationType> > 76*6777b538SAndroid Build Coastguard Worker class ExpiringCache { 77*6777b538SAndroid Build Coastguard Worker public: 78*6777b538SAndroid Build Coastguard Worker ExpiringCache(const ExpiringCache&) = delete; 79*6777b538SAndroid Build Coastguard Worker ExpiringCache& operator=(const ExpiringCache&) = delete; 80*6777b538SAndroid Build Coastguard Worker 81*6777b538SAndroid Build Coastguard Worker private: 82*6777b538SAndroid Build Coastguard Worker // Intentionally violate the C++ Style Guide so that EntryMap is known to be 83*6777b538SAndroid Build Coastguard Worker // a dependent type. Without this, Clang's two-phase lookup complains when 84*6777b538SAndroid Build Coastguard Worker // using EntryMap::const_iterator, while GCC and MSVC happily resolve the 85*6777b538SAndroid Build Coastguard Worker // typename. 86*6777b538SAndroid Build Coastguard Worker 87*6777b538SAndroid Build Coastguard Worker // Tuple to represent the value and when it expires. 88*6777b538SAndroid Build Coastguard Worker typedef std::pair<ValueType, ExpirationType> Entry; 89*6777b538SAndroid Build Coastguard Worker typedef std::map<KeyType, Entry> EntryMap; 90*6777b538SAndroid Build Coastguard Worker 91*6777b538SAndroid Build Coastguard Worker public: 92*6777b538SAndroid Build Coastguard Worker typedef KeyType key_type; 93*6777b538SAndroid Build Coastguard Worker typedef ValueType value_type; 94*6777b538SAndroid Build Coastguard Worker typedef ExpirationType expiration_type; 95*6777b538SAndroid Build Coastguard Worker 96*6777b538SAndroid Build Coastguard Worker // This class provides a read-only iterator over items in the ExpiringCache 97*6777b538SAndroid Build Coastguard Worker class Iterator { 98*6777b538SAndroid Build Coastguard Worker public: Iterator(const ExpiringCache & cache)99*6777b538SAndroid Build Coastguard Worker explicit Iterator(const ExpiringCache& cache) 100*6777b538SAndroid Build Coastguard Worker : cache_(cache), it_(cache_->entries_.begin()) {} 101*6777b538SAndroid Build Coastguard Worker ~Iterator() = default; 102*6777b538SAndroid Build Coastguard Worker HasNext()103*6777b538SAndroid Build Coastguard Worker bool HasNext() const { return it_ != cache_->entries_.end(); } Advance()104*6777b538SAndroid Build Coastguard Worker void Advance() { ++it_; } 105*6777b538SAndroid Build Coastguard Worker key()106*6777b538SAndroid Build Coastguard Worker const KeyType& key() const { return it_->first; } value()107*6777b538SAndroid Build Coastguard Worker const ValueType& value() const { return it_->second.first; } expiration()108*6777b538SAndroid Build Coastguard Worker const ExpirationType& expiration() const { return it_->second.second; } 109*6777b538SAndroid Build Coastguard Worker 110*6777b538SAndroid Build Coastguard Worker private: 111*6777b538SAndroid Build Coastguard Worker const raw_ref<const ExpiringCache> cache_; 112*6777b538SAndroid Build Coastguard Worker 113*6777b538SAndroid Build Coastguard Worker // Use a second layer of type indirection, as both EntryMap and 114*6777b538SAndroid Build Coastguard Worker // EntryMap::const_iterator are dependent types. 115*6777b538SAndroid Build Coastguard Worker typedef typename ExpiringCache::EntryMap EntryMap; 116*6777b538SAndroid Build Coastguard Worker typename EntryMap::const_iterator it_; 117*6777b538SAndroid Build Coastguard Worker }; 118*6777b538SAndroid Build Coastguard Worker 119*6777b538SAndroid Build Coastguard Worker 120*6777b538SAndroid Build Coastguard Worker // Constructs an ExpiringCache that stores up to |max_entries|. ExpiringCache(size_t max_entries)121*6777b538SAndroid Build Coastguard Worker explicit ExpiringCache(size_t max_entries) : max_entries_(max_entries) {} 122*6777b538SAndroid Build Coastguard Worker ~ExpiringCache() = default; 123*6777b538SAndroid Build Coastguard Worker 124*6777b538SAndroid Build Coastguard Worker // Returns the value matching |key|, which must be valid at the time |now|. 125*6777b538SAndroid Build Coastguard Worker // Returns NULL if the item is not found or has expired. If the item has 126*6777b538SAndroid Build Coastguard Worker // expired, it is immediately removed from the cache. 127*6777b538SAndroid Build Coastguard Worker // Note: The returned pointer remains owned by the ExpiringCache and is 128*6777b538SAndroid Build Coastguard Worker // invalidated by a call to a non-const method. Get(const KeyType & key,const ExpirationType & now)129*6777b538SAndroid Build Coastguard Worker const ValueType* Get(const KeyType& key, const ExpirationType& now) { 130*6777b538SAndroid Build Coastguard Worker typename EntryMap::iterator it = entries_.find(key); 131*6777b538SAndroid Build Coastguard Worker if (it == entries_.end()) 132*6777b538SAndroid Build Coastguard Worker return nullptr; 133*6777b538SAndroid Build Coastguard Worker 134*6777b538SAndroid Build Coastguard Worker // Immediately remove expired entries. 135*6777b538SAndroid Build Coastguard Worker if (!expiration_comp_(now, it->second.second)) { 136*6777b538SAndroid Build Coastguard Worker Evict(it, now, true); 137*6777b538SAndroid Build Coastguard Worker return nullptr; 138*6777b538SAndroid Build Coastguard Worker } 139*6777b538SAndroid Build Coastguard Worker 140*6777b538SAndroid Build Coastguard Worker return &it->second.first; 141*6777b538SAndroid Build Coastguard Worker } 142*6777b538SAndroid Build Coastguard Worker 143*6777b538SAndroid Build Coastguard Worker // Updates or replaces the value associated with |key|. Put(const KeyType & key,const ValueType & value,const ExpirationType & now,const ExpirationType & expiration)144*6777b538SAndroid Build Coastguard Worker void Put(const KeyType& key, 145*6777b538SAndroid Build Coastguard Worker const ValueType& value, 146*6777b538SAndroid Build Coastguard Worker const ExpirationType& now, 147*6777b538SAndroid Build Coastguard Worker const ExpirationType& expiration) { 148*6777b538SAndroid Build Coastguard Worker typename EntryMap::iterator it = entries_.find(key); 149*6777b538SAndroid Build Coastguard Worker if (it == entries_.end()) { 150*6777b538SAndroid Build Coastguard Worker // Compact the cache if it grew beyond the limit. 151*6777b538SAndroid Build Coastguard Worker if (entries_.size() == max_entries_ ) 152*6777b538SAndroid Build Coastguard Worker Compact(now); 153*6777b538SAndroid Build Coastguard Worker 154*6777b538SAndroid Build Coastguard Worker // No existing entry. Creating a new one. 155*6777b538SAndroid Build Coastguard Worker entries_.insert(std::pair(key, Entry(value, expiration))); 156*6777b538SAndroid Build Coastguard Worker } else { 157*6777b538SAndroid Build Coastguard Worker // Update an existing cache entry. 158*6777b538SAndroid Build Coastguard Worker it->second.first = value; 159*6777b538SAndroid Build Coastguard Worker it->second.second = expiration; 160*6777b538SAndroid Build Coastguard Worker } 161*6777b538SAndroid Build Coastguard Worker } 162*6777b538SAndroid Build Coastguard Worker 163*6777b538SAndroid Build Coastguard Worker // Empties the cache. Clear()164*6777b538SAndroid Build Coastguard Worker void Clear() { 165*6777b538SAndroid Build Coastguard Worker entries_.clear(); 166*6777b538SAndroid Build Coastguard Worker } 167*6777b538SAndroid Build Coastguard Worker 168*6777b538SAndroid Build Coastguard Worker // Returns the number of entries in the cache. size()169*6777b538SAndroid Build Coastguard Worker size_t size() const { return entries_.size(); } 170*6777b538SAndroid Build Coastguard Worker 171*6777b538SAndroid Build Coastguard Worker // Returns the maximum number of entries in the cache. max_entries()172*6777b538SAndroid Build Coastguard Worker size_t max_entries() const { return max_entries_; } 173*6777b538SAndroid Build Coastguard Worker empty()174*6777b538SAndroid Build Coastguard Worker bool empty() const { return entries_.empty(); } 175*6777b538SAndroid Build Coastguard Worker 176*6777b538SAndroid Build Coastguard Worker private: 177*6777b538SAndroid Build Coastguard Worker FRIEND_TEST_ALL_PREFIXES(ExpiringCacheTest, Compact); 178*6777b538SAndroid Build Coastguard Worker FRIEND_TEST_ALL_PREFIXES(ExpiringCacheTest, CustomFunctor); 179*6777b538SAndroid Build Coastguard Worker 180*6777b538SAndroid Build Coastguard Worker // Prunes entries from the cache to bring it below |max_entries()|. Compact(const ExpirationType & now)181*6777b538SAndroid Build Coastguard Worker void Compact(const ExpirationType& now) { 182*6777b538SAndroid Build Coastguard Worker // Clear out expired entries. 183*6777b538SAndroid Build Coastguard Worker typename EntryMap::iterator it; 184*6777b538SAndroid Build Coastguard Worker for (it = entries_.begin(); it != entries_.end(); ) { 185*6777b538SAndroid Build Coastguard Worker if (!expiration_comp_(now, it->second.second)) { 186*6777b538SAndroid Build Coastguard Worker Evict(it++, now, false); 187*6777b538SAndroid Build Coastguard Worker } else { 188*6777b538SAndroid Build Coastguard Worker ++it; 189*6777b538SAndroid Build Coastguard Worker } 190*6777b538SAndroid Build Coastguard Worker } 191*6777b538SAndroid Build Coastguard Worker 192*6777b538SAndroid Build Coastguard Worker if (entries_.size() < max_entries_) 193*6777b538SAndroid Build Coastguard Worker return; 194*6777b538SAndroid Build Coastguard Worker 195*6777b538SAndroid Build Coastguard Worker // If the cache is still too full, start deleting items 'randomly'. 196*6777b538SAndroid Build Coastguard Worker for (it = entries_.begin(); 197*6777b538SAndroid Build Coastguard Worker it != entries_.end() && entries_.size() >= max_entries_;) { 198*6777b538SAndroid Build Coastguard Worker Evict(it++, now, false); 199*6777b538SAndroid Build Coastguard Worker } 200*6777b538SAndroid Build Coastguard Worker } 201*6777b538SAndroid Build Coastguard Worker Evict(typename EntryMap::iterator it,const ExpirationType & now,bool on_get)202*6777b538SAndroid Build Coastguard Worker void Evict(typename EntryMap::iterator it, 203*6777b538SAndroid Build Coastguard Worker const ExpirationType& now, 204*6777b538SAndroid Build Coastguard Worker bool on_get) { 205*6777b538SAndroid Build Coastguard Worker eviction_handler_.Handle(it->first, it->second.first, it->second.second, 206*6777b538SAndroid Build Coastguard Worker now, on_get); 207*6777b538SAndroid Build Coastguard Worker entries_.erase(it); 208*6777b538SAndroid Build Coastguard Worker } 209*6777b538SAndroid Build Coastguard Worker 210*6777b538SAndroid Build Coastguard Worker // Bound on total size of the cache. 211*6777b538SAndroid Build Coastguard Worker size_t max_entries_; 212*6777b538SAndroid Build Coastguard Worker 213*6777b538SAndroid Build Coastguard Worker EntryMap entries_; 214*6777b538SAndroid Build Coastguard Worker ExpirationCompare expiration_comp_; 215*6777b538SAndroid Build Coastguard Worker EvictionHandler eviction_handler_; 216*6777b538SAndroid Build Coastguard Worker }; 217*6777b538SAndroid Build Coastguard Worker 218*6777b538SAndroid Build Coastguard Worker } // namespace net 219*6777b538SAndroid Build Coastguard Worker 220*6777b538SAndroid Build Coastguard Worker #endif // NET_BASE_EXPIRING_CACHE_H_ 221