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 | 7 |
8 #include <map> | 8 #include <map> |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
11 #include "base/basictypes.h" | 11 #include "base/basictypes.h" |
12 #include "base/gtest_prod_util.h" | 12 #include "base/gtest_prod_util.h" |
13 #include "base/time.h" | 13 #include "base/time.h" |
14 | 14 |
15 namespace net { | 15 namespace net { |
16 | 16 |
17 template <typename KeyType, | |
18 typename ValueType, | |
19 typename ExpirationType> | |
20 class NoopEvictionHandler { | |
21 public: | |
22 void handle(const KeyType& key, | |
cbentzel
2012/11/07 10:40:22
Nit: Should be Handle rather than handle.
| |
23 const ValueType& value, | |
24 const ExpirationType& expiration, | |
25 const ExpirationType& now, | |
26 bool onGet) const { | |
cbentzel
2012/11/07 10:40:22
Nit: on_get instead of onGet.
| |
27 } | |
28 }; | |
29 | |
17 // Cache implementation where all entries have an explicit expiration policy. As | 30 // Cache implementation where all entries have an explicit expiration policy. As |
18 // new items are added, expired items will be removed first. | 31 // new items are added, expired items will be removed first. |
19 // The template types have the following requirements: | 32 // The template types have the following requirements: |
20 // KeyType must be LessThanComparable, Assignable, and CopyConstructible. | 33 // KeyType must be LessThanComparable, Assignable, and CopyConstructible. |
21 // ValueType must be CopyConstructible and Assignable. | 34 // ValueType must be CopyConstructible and Assignable. |
22 // ExpirationType must be CopyConstructible and Assignable. | 35 // ExpirationType must be CopyConstructible and Assignable. |
23 // ExpirationCompare is a function class that takes two arguments of the | 36 // ExpirationCompare is a function class that takes two arguments of the |
24 // type ExpirationType and returns a bool. If |comp| is an instance of | 37 // type ExpirationType and returns a bool. If |comp| is an instance of |
25 // ExpirationCompare, then the expression |comp(current, expiration)| shall | 38 // ExpirationCompare, then the expression |comp(current, expiration)| shall |
26 // return true iff |current| is still valid within |expiration|. | 39 // return true iff |current| is still valid within |expiration|. |
27 // | 40 // |
28 // A simple use of this class may use base::TimeTicks, which provides a | 41 // A simple use of this class may use base::TimeTicks, which provides a |
29 // monotonically increasing clock, for the expiration type. Because it's always | 42 // monotonically increasing clock, for the expiration type. Because it's always |
30 // increasing, std::less<> can be used, which will simply ensure that |now| is | 43 // increasing, std::less<> can be used, which will simply ensure that |now| is |
31 // sorted before |expiration|: | 44 // sorted before |expiration|: |
32 // | 45 // |
33 // ExpiringCache<std::string, std::string, base::TimeTicks, | 46 // ExpiringCache<std::string, std::string, base::TimeTicks, |
34 // std::less<base::TimeTicks> > cache(0); | 47 // std::less<base::TimeTicks> > cache(0); |
jar (doing other things)
2012/11/05 17:44:56
This looks like it tries to create a cache with ze
| |
35 // // Add a value that expires in 5 minutes | 48 // // Add a value that expires in 5 minutes |
36 // cache.Put("key1", "value1", base::TimeTicks::Now(), | 49 // cache.Put("key1", "value1", base::TimeTicks::Now(), |
37 // base::TimeTicks::Now() + base::TimeDelta::FromMinutes(5)); | 50 // base::TimeTicks::Now() + base::TimeDelta::FromMinutes(5)); |
38 // // Add another value that expires in 10 minutes. | 51 // // Add another value that expires in 10 minutes. |
jar (doing other things)
2012/11/05 17:44:56
Given the cache size of zero, this appear to repla
| |
39 // cache.Put("key2", "value2", base::TimeTicks::Now(), | 52 // cache.Put("key2", "value2", base::TimeTicks::Now(), |
40 // base::TimeTicks::Now() + base::TimeDelta::FromMinutes(10)); | 53 // base::TimeTicks::Now() + base::TimeDelta::FromMinutes(10)); |
41 // | 54 // |
42 // Alternatively, there may be some more complex expiration criteria, at which | 55 // Alternatively, there may be some more complex expiration criteria, at which |
43 // point a custom functor may be used: | 56 // point a custom functor may be used: |
44 // | 57 // |
45 // struct ComplexExpirationFunctor { | 58 // struct ComplexExpirationFunctor { |
46 // bool operator()(const ComplexExpiration& now, | 59 // bool operator()(const ComplexExpiration& now, |
47 // const ComplexExpiration& expiration) const; | 60 // const ComplexExpiration& expiration) const; |
48 // }; | 61 // }; |
49 // ExpiringCache<std::string, std::string, ComplexExpiration, | 62 // ExpiringCache<std::string, std::string, ComplexExpiration, |
50 // ComplexExpirationFunctor> cache(15); | 63 // ComplexExpirationFunctor> cache(15); |
51 // // Add a value that expires once the 'sprocket' has 'cog'-ified. | 64 // // Add a value that expires once the 'sprocket' has 'cog'-ified. |
52 // cache.Put("key1", "value1", ComplexExpiration("sprocket"), | 65 // cache.Put("key1", "value1", ComplexExpiration("sprocket"), |
53 // ComplexExpiration("cog")); | 66 // ComplexExpiration("cog")); |
54 template <typename KeyType, | 67 template <typename KeyType, |
55 typename ValueType, | 68 typename ValueType, |
56 typename ExpirationType, | 69 typename ExpirationType, |
57 typename ExpirationCompare> | 70 typename ExpirationCompare, |
71 typename EvictionHandler = NoopEvictionHandler<KeyType, | |
72 ValueType, | |
73 ExpirationType> > | |
58 class ExpiringCache { | 74 class ExpiringCache { |
59 private: | 75 private: |
60 // Intentionally violate the C++ Style Guide so that EntryMap is known to be | 76 // Intentionally violate the C++ Style Guide so that EntryMap is known to be |
61 // a dependent type. Without this, Clang's two-phase lookup complains when | 77 // a dependent type. Without this, Clang's two-phase lookup complains when |
62 // using EntryMap::const_iterator, while GCC and MSVC happily resolve the | 78 // using EntryMap::const_iterator, while GCC and MSVC happily resolve the |
63 // typename. | 79 // typename. |
64 | 80 |
65 // Tuple to represent the value and when it expires. | 81 // Tuple to represent the value and when it expires. |
66 typedef std::pair<ValueType, ExpirationType> Entry; | 82 typedef std::pair<ValueType, ExpirationType> Entry; |
67 typedef std::map<KeyType, Entry> EntryMap; | 83 typedef std::map<KeyType, Entry> EntryMap; |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
106 // expired, it is immediately removed from the cache. | 122 // expired, it is immediately removed from the cache. |
107 // Note: The returned pointer remains owned by the ExpiringCache and is | 123 // Note: The returned pointer remains owned by the ExpiringCache and is |
108 // invalidated by a call to a non-const method. | 124 // invalidated by a call to a non-const method. |
109 const ValueType* Get(const KeyType& key, const ExpirationType& now) { | 125 const ValueType* Get(const KeyType& key, const ExpirationType& now) { |
110 typename EntryMap::iterator it = entries_.find(key); | 126 typename EntryMap::iterator it = entries_.find(key); |
111 if (it == entries_.end()) | 127 if (it == entries_.end()) |
112 return NULL; | 128 return NULL; |
113 | 129 |
114 // Immediately remove expired entries. | 130 // Immediately remove expired entries. |
115 if (!expiration_comp_(now, it->second.second)) { | 131 if (!expiration_comp_(now, it->second.second)) { |
116 entries_.erase(it); | 132 Evict(it, now, true); |
117 return NULL; | 133 return NULL; |
118 } | 134 } |
119 | 135 |
120 return &it->second.first; | 136 return &it->second.first; |
121 } | 137 } |
122 | 138 |
123 // Updates or replaces the value associated with |key|. | 139 // Updates or replaces the value associated with |key|. |
124 void Put(const KeyType& key, | 140 void Put(const KeyType& key, |
125 const ValueType& value, | 141 const ValueType& value, |
126 const ExpirationType& now, | 142 const ExpirationType& now, |
(...skipping 29 matching lines...) Expand all Loading... | |
156 private: | 172 private: |
157 FRIEND_TEST_ALL_PREFIXES(ExpiringCacheTest, Compact); | 173 FRIEND_TEST_ALL_PREFIXES(ExpiringCacheTest, Compact); |
158 FRIEND_TEST_ALL_PREFIXES(ExpiringCacheTest, CustomFunctor); | 174 FRIEND_TEST_ALL_PREFIXES(ExpiringCacheTest, CustomFunctor); |
159 | 175 |
160 // Prunes entries from the cache to bring it below |max_entries()|. | 176 // Prunes entries from the cache to bring it below |max_entries()|. |
161 void Compact(const ExpirationType& now) { | 177 void Compact(const ExpirationType& now) { |
162 // Clear out expired entries. | 178 // Clear out expired entries. |
163 typename EntryMap::iterator it; | 179 typename EntryMap::iterator it; |
164 for (it = entries_.begin(); it != entries_.end(); ) { | 180 for (it = entries_.begin(); it != entries_.end(); ) { |
165 if (!expiration_comp_(now, it->second.second)) { | 181 if (!expiration_comp_(now, it->second.second)) { |
166 entries_.erase(it++); | 182 Evict(it++, now, false); |
167 } else { | 183 } else { |
168 ++it; | 184 ++it; |
169 } | 185 } |
170 } | 186 } |
171 | 187 |
172 if (entries_.size() < max_entries_) | 188 if (entries_.size() < max_entries_) |
173 return; | 189 return; |
174 | 190 |
175 // If the cache is still too full, start deleting items 'randomly'. | 191 // If the cache is still too full, start deleting items 'randomly'. |
176 for (it = entries_.begin(); | 192 for (it = entries_.begin(); |
177 it != entries_.end() && entries_.size() >= max_entries_;) { | 193 it != entries_.end() && entries_.size() >= max_entries_;) { |
178 entries_.erase(it++); | 194 Evict(it++, now, false); |
179 } | 195 } |
180 } | 196 } |
181 | 197 |
198 void Evict(typename EntryMap::iterator it, | |
199 const ExpirationType& now, | |
200 bool onGet) { | |
201 eviction_handler_.handle(it->first, it->second.first, it->second.second, | |
202 now, onGet); | |
203 entries_.erase(it); | |
204 } | |
205 | |
182 // Bound on total size of the cache. | 206 // Bound on total size of the cache. |
183 size_t max_entries_; | 207 size_t max_entries_; |
184 | 208 |
185 EntryMap entries_; | 209 EntryMap entries_; |
186 ExpirationCompare expiration_comp_; | 210 ExpirationCompare expiration_comp_; |
211 EvictionHandler eviction_handler_; | |
187 | 212 |
188 DISALLOW_COPY_AND_ASSIGN(ExpiringCache); | 213 DISALLOW_COPY_AND_ASSIGN(ExpiringCache); |
189 }; | 214 }; |
190 | 215 |
191 } // namespace net | 216 } // namespace net |
192 | 217 |
193 #endif // NET_BASE_EXPIRING_CACHE_H_ | 218 #endif // NET_BASE_EXPIRING_CACHE_H_ |
OLD | NEW |