1 // Copyright 2010-2018, Google Inc.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above
11 // copyright notice, this list of conditions and the following disclaimer
12 // in the documentation and/or other materials provided with the
13 // distribution.
14 // * Neither the name of Google Inc. nor the names of its
15 // contributors may be used to endorse or promote products derived from
16 // this software without specific prior written permission.
17 //
18 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
30 #ifndef MOZC_STORAGE_LRU_CACHE_H_
31 #define MOZC_STORAGE_LRU_CACHE_H_
32
33 #include <cstring>
34 #include <map>
35 #include <string>
36
37 #include "base/logging.h"
38 #include "base/port.h"
39
40 namespace mozc {
41 namespace storage {
42
43 // Note: this class keeps some resources inside of the Key/Value, even if
44 // such a entry is erased. Be careful to use for such classes.
45 // TODO(yukawa): Make this class final once we stop supporting GCC 4.6.
46 template<typename Key, typename Value>
47 class LRUCache {
48 public:
49 // Constructs a new LRUCache that can hold at most max_elements
50 explicit LRUCache(size_t max_elements);
51
52 // Cleans up all allocated resources.
53 ~LRUCache();
54
55 // Every Element is either on the free list or the lru list. The
56 // free list is singly-linked and only uses the next pointer, while
57 // the LRU list is doubly-linked and uses both next and prev.
58 struct Element {
59 Element* next;
60 Element* prev;
61 Key key;
62 Value value;
63 };
64
65 // Adds the specified key/value pair into the cache, putting it at the head
66 // of the LRU list.
67 void Insert(const Key &key, const Value& value);
68
69 // Adds the specified key and return the Element added to the cache.
70 // Caller needs to set the value
71 Element* Insert(const Key &key);
72
73 // Returns the cached value associated with the key, or NULL if the cache
74 // does not contain an entry for that key. The caller does not assume
75 // ownership of the returned value. The reference returned by Lookup() could
76 // be invalidated by a call to Insert(), so the caller must take care to not
77 // access the value if Insert() could have been called after Lookup().
78 const Value* Lookup(const Key &key);
79
80 // return non-const Value
81 Value *MutableLookup(const Key &key);
82
83 // Lookup/MutableLookup don't change the LRU order.
84 const Value *LookupWithoutInsert(const Key &key) const;
85 Value *MutableLookupWithoutInsert(const Key &key) const;
86
87 // Removes the cache entry specified by key. Returns true if the entry was
88 // in the cache, otherwise returns false.
89 bool Erase(const Key &key);
90
91 // Removes all entries from the cache. Note that this does not release the
92 // memory associates with the blocks, but just pushes all the elements onto
93 // the free list.
94 void Clear();
95
96 // Returns the number of entries currently in the cache.
97 size_t Size() const;
98
99 bool HasKey(const Key &key) const;
100
101 // Returns the head of LRU list
Head()102 const Element *Head() const { return lru_head_; }
MutableHead()103 Element *MutableHead() const { return lru_head_; }
104
105 // Returns the tail of LRU list
Tail()106 const Element *Tail() const { return lru_tail_; }
107
108 private:
109 // Allocates a new block containing next_block_size_ elements, updates
110 // next_block_size_ appropriately, and pushes the elements in the new block
111 // onto the free list.
112 void AddBlock();
113
114 // Pushes an element onto the head of the free list.
115 void PushFreeList(Element* element);
116
117 // Pops an element from the head of the free list.
118 Element* PopFreeList();
119
120 // Returns a free element, popping from the free list if possible, or
121 // allocating a new element if the free list is empty. If there are already
122 // max_elements_ in use this will return NULL.
123 Element* NextFreeElement();
124
125 // Returns the Element* associated with key, or NULL if no element with this
126 // key is found.
127 Element* LookupInternal(const Key &key) const;
128
129 // Removes the specified element from the LRU list.
130 void RemoveFromLRU(Element* element);
131
132 // Adds the specified element to the head of the LRU list.
133 void PushLRUHead(Element* element);
134
135 // Evict is similar to Erase, except that it adds the eviction callback and
136 // element to a list of eviction calls, and takes an element so that another
137 // lookup is not necessary.
138 bool Evict(Element* element);
139
140 typedef std::map<Key, Element*> Table;
141
142 Table* table_;
143 Element* free_list_; // singly linked list of Element
144 Element* lru_head_; // head of doubly linked list of Element
145 Element* lru_tail_; // tail of doubly linked list of Element
146 Element* blocks_[10]; // blocks of Element, with each block being twice
147 // as big as the previous block. This allows the
148 // cache to use a small amount of memory when it
149 // contains few items, but still have low malloc
150 // overhead per insert. The number of blocks is
151 // arbitrary, but 10 blocks allows the largest
152 // block to be 2^10 times as large as the smallest
153 // block, which seems like more than enough.
154 size_t block_count_; // how many entries in blocks_ have been used
155 size_t block_capacity_; // how many Elements can be stored in current blocks
156 size_t next_block_size_; // size of the next block to allocate
157 size_t max_elements_; // maximum elements to hold
158
159 DISALLOW_COPY_AND_ASSIGN(LRUCache);
160 };
161
162
163 template<typename Key, typename Value>
AddBlock()164 void LRUCache<Key, Value>::AddBlock() {
165 const size_t max_blocks = sizeof(blocks_) / sizeof(blocks_[0]);
166 if (block_count_ < max_blocks && block_capacity_ < max_elements_) {
167 blocks_[block_count_] = new Element[next_block_size_];
168 block_capacity_ += next_block_size_;
169 // Add new elements to free list
170 for (size_t i = 0; i < next_block_size_; ++i) {
171 Element* e = &((blocks_[block_count_])[i]);
172 e->prev = NULL; // free list is not doubly linked
173 e->next = free_list_;
174 free_list_ = e;
175 }
176 block_count_++;
177 size_t blocks_remaining = max_blocks - block_count_;
178 if (blocks_remaining > 0) {
179 next_block_size_ = (next_block_size_ << 1);
180 size_t elements_remaining = max_elements_ - block_capacity_;
181 if (next_block_size_ > (elements_remaining / blocks_remaining)) {
182 next_block_size_ = elements_remaining / blocks_remaining;
183 }
184 if (block_capacity_ + next_block_size_ > max_elements_) {
185 next_block_size_ = max_elements_ - block_capacity_;
186 }
187 }
188 }
189 }
190
191 template<typename Key, typename Value>
PushFreeList(Element * element)192 void LRUCache<Key, Value>::PushFreeList(Element* element) {
193 element->prev = NULL;
194 element->next = free_list_;
195 free_list_ = element;
196 }
197
198 template<typename Key, typename Value>
199 typename LRUCache<Key, Value>::Element*
PopFreeList()200 LRUCache<Key, Value>::PopFreeList() {
201 Element* r = free_list_;
202 if (r != NULL) {
203 CHECK(r->prev == NULL);
204 free_list_ = r->next;
205 if (free_list_ != NULL) {
206 free_list_->prev = NULL;
207 }
208 r->next = NULL;
209 }
210 return r;
211 }
212
213 template<typename Key, typename Value>
214 typename LRUCache<Key, Value>::Element*
NextFreeElement()215 LRUCache<Key, Value>::NextFreeElement() {
216 Element* r = PopFreeList();
217 if (r == NULL) {
218 AddBlock();
219 r = PopFreeList();
220 }
221 return r;
222 }
223
224 template<typename Key, typename Value>
225 typename LRUCache<Key, Value>::Element*
LookupInternal(const Key & key)226 LRUCache<Key, Value>::LookupInternal(const Key &key) const {
227 typename Table::iterator iter = table_->find(key);
228 if (iter != table_->end()) {
229 return iter->second;
230 }
231 return NULL;
232 }
233
234 template<typename Key, typename Value>
RemoveFromLRU(Element * element)235 void LRUCache<Key, Value>::RemoveFromLRU(Element* element) {
236 if (lru_head_ == element) {
237 lru_head_ = element->next;
238 }
239 if (lru_tail_ == element) {
240 lru_tail_ = element->prev;
241 }
242 if (element->prev != NULL) {
243 element->prev->next = element->next;
244 }
245 if (element->next != NULL) {
246 element->next->prev = element->prev;
247 }
248 element->prev = NULL;
249 element->next = NULL;
250 }
251
252 template<typename Key, typename Value>
PushLRUHead(Element * element)253 void LRUCache<Key, Value>::PushLRUHead(Element* element) {
254 if (lru_head_ == element) {
255 // element is already at head, so do nothing.
256 return;
257 }
258 RemoveFromLRU(element);
259 element->next = lru_head_;
260 lru_head_ = element;
261 if (element->next != NULL) {
262 element->next->prev = element;
263 }
264 if (lru_tail_ == NULL) {
265 lru_tail_ = element;
266 }
267 }
268
269 template<typename Key, typename Value>
Evict(Element * e)270 bool LRUCache<Key, Value>::Evict(Element* e) {
271 if (e != NULL) {
272 int erased = table_->erase(e->key);
273 CHECK_EQ(erased, 1);
274 RemoveFromLRU(e);
275 PushFreeList(e);
276 return true;
277 }
278 return false;
279 }
280
281 template<typename Key, typename Value>
LRUCache(size_t max_elements)282 LRUCache<Key, Value>::LRUCache(size_t max_elements)
283 : free_list_(NULL),
284 lru_head_(NULL),
285 lru_tail_(NULL),
286 block_count_(0),
287 block_capacity_(0),
288 max_elements_(max_elements) {
289 ::memset(blocks_, 0, sizeof(blocks_));
290 table_ = new Table;
291 CHECK(table_);
292 if (max_elements_ <= 128) {
293 next_block_size_ = max_elements_;
294 } else {
295 next_block_size_ = 64;
296 size_t num_blocks = sizeof(blocks_) / sizeof(blocks_[0]);
297 // The default starting block size is 64, which is 2^6. Every block is
298 // twice as big as the previous (see AddBlock()), so the size of the last
299 // block would be 2^(6 + num_blocks) if the first block was of size 64. If
300 // max_elements is large enough that 64 is too low of a starting size,
301 // figure that out here.
302 while ((next_block_size_ << num_blocks) < max_elements) {
303 next_block_size_ = (next_block_size_ << 1);
304 }
305 }
306 }
307
308 template<typename Key, typename Value>
~LRUCache()309 LRUCache<Key, Value>::~LRUCache() {
310 // To free all the memory that I have allocated I need to delete table_ and
311 // any used entries in blocks_.
312 delete table_;
313 for (size_t i = 0; i < block_count_; ++i) {
314 delete [] blocks_[i];
315 }
316 }
317
318 template<typename Key, typename Value>
Insert(const Key & key,const Value & value)319 void LRUCache<Key, Value>::Insert(const Key& key,
320 const Value& value) {
321 Element *e = Insert(key);
322 if (e != NULL) {
323 e->value = value;
324 }
325 }
326
327 template<typename Key, typename Value>
328 typename LRUCache<Key, Value>::Element *
Insert(const Key & key)329 LRUCache<Key, Value>::Insert(const Key& key) {
330 bool erased = false;
331 Element* e = LookupInternal(key);
332 if (e != NULL) {
333 erased = Evict(e);
334 CHECK(erased);
335 }
336
337 e = NextFreeElement();
338 if (e == NULL) {
339 // no free elements, I have to replace an existing element
340 e = lru_tail_;
341 erased = Evict(e);
342 CHECK(erased);
343 e = NextFreeElement();
344 CHECK(e != NULL);
345 }
346 e->key = key;
347 (*table_)[key] = e;
348 PushLRUHead(e);
349
350 return e;
351 }
352
353 template<typename Key, typename Value>
MutableLookup(const Key & key)354 Value* LRUCache<Key, Value>::MutableLookup(const Key& key) {
355 Element* e = LookupInternal(key);
356 if (e != NULL) {
357 PushLRUHead(e);
358 return &(e->value);
359 }
360 return NULL;
361 }
362
363 template<typename Key, typename Value>
Lookup(const Key & key)364 const Value* LRUCache<Key, Value>::Lookup(const Key& key) {
365 return MutableLookup(key);
366 }
367
368 template<typename Key, typename Value>
MutableLookupWithoutInsert(const Key & key)369 Value* LRUCache<Key, Value>::MutableLookupWithoutInsert(const Key& key) const {
370 Element *e = LookupInternal(key);
371 if (e != NULL) {
372 return &(e->value);
373 }
374 return NULL;
375 }
376
377 template<typename Key, typename Value>
LookupWithoutInsert(const Key & key)378 const Value* LRUCache<Key, Value>::LookupWithoutInsert(const Key& key) const {
379 return MutableLookupWithoutInsert(key);
380 }
381
382 template<typename Key, typename Value>
Erase(const Key & key)383 bool LRUCache<Key, Value>::Erase(const Key& key) {
384 Element* e = LookupInternal(key);
385 return Evict(e);
386 }
387
388 template<typename Key, typename Value>
Clear()389 void LRUCache<Key, Value>::Clear() {
390 table_->clear();
391 Element* e = lru_head_;
392 while (e != NULL) {
393 Element* next = e->next;
394 PushFreeList(e);
395 e = next;
396 }
397 lru_head_ = lru_tail_ = NULL;
398 }
399
400 template<typename Key, typename Value>
HasKey(const Key & key)401 bool LRUCache<Key, Value>::HasKey(const Key& key) const {
402 return (table_->find(key) != table_->end());
403 }
404
405 template<typename Key, typename Value>
Size()406 size_t LRUCache<Key, Value>::Size() const {
407 return table_->size();
408 }
409
410 } // namespace storage
411 } // namespace mozc
412 #endif // MOZC_STORAGE_LRU_CACHE_H_
413