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