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