libime
lrucache.h
1 /*
2  * SPDX-FileCopyrightText: 2017-2017 CSSlayer <wengxt@gmail.com>
3  *
4  * SPDX-License-Identifier: LGPL-2.1-or-later
5  */
6 #ifndef _FCITX_LIBIME_CORE_LRU_H_
7 #define _FCITX_LIBIME_CORE_LRU_H_
8 
9 #include <cstddef>
10 #include <list>
11 #include <utility>
12 #include <boost/container_hash/hash.hpp>
13 #include <boost/unordered_map.hpp>
14 
15 namespace libime {
16 
17 // A simple LRU cache.
18 template <typename K, typename V, typename H = boost::hash<K>>
19 class LRUCache {
20 public:
21  using key_type = K;
22  using value_type = V;
23  // we use boost's unordered_map is for the heterogeneous lookup
24  // functionality.
25  using dict_type =
26  boost::unordered_map<K, std::pair<V, typename std::list<K>::iterator>,
27  H>;
28 
29  LRUCache(size_t sz = 80) : sz_(sz) {}
30 
31  size_t size() const { return dict_.size(); }
32 
33  size_t capacity() const { return sz_; }
34 
35  bool empty() const { return dict_.empty(); }
36 
37  bool contains(const key_type &key) {
38  return dict_.find(key) != dict_.end();
39  }
40 
41  template <typename... Args>
42  value_type *insert(const key_type &key, Args &&...args) {
43  auto iter = dict_.find(key);
44  if (iter == dict_.end()) {
45  if (size() >= sz_) {
46  evict();
47  }
48 
49  order_.push_front(key);
50  auto r = dict_.emplace(
51  key, std::make_pair(value_type(std::forward<Args>(args)...),
52  order_.begin()));
53  return &r.first->second.first;
54  }
55  return nullptr;
56  }
57 
58  void erase(const key_type &key) {
59  auto i = dict_.find(key);
60  if (i == dict_.end()) {
61  return;
62  }
63  order_.erase(i->second.second);
64  dict_.erase(i);
65  }
66 
67  // find will refresh the item, so it is not const.
68  value_type *find(const key_type &key) {
69  // lookup value in the cache
70  auto i = dict_.find(key);
71  return find_helper(i);
72  }
73 
74  template <class CompatibleKey, class CompatibleHash,
75  class CompatiblePredicate>
76  value_type *find(CompatibleKey const &k, CompatibleHash const &h,
77  CompatiblePredicate const &p) {
78  return find_helper(dict_.find(k, h, p));
79  }
80 
81  void clear() {
82  dict_.clear();
83  order_.clear();
84  }
85 
86 private:
87  void evict() {
88  // evict item from the end of most recently used list
89  auto i = std::prev(order_.end());
90  dict_.erase(*i);
91  order_.erase(i);
92  }
93 
94  value_type *find_helper(typename dict_type::iterator i) {
95  if (i == dict_.end()) {
96  // value not in cache
97  return nullptr;
98  }
99 
100  // return the value, but first update its place in the most
101  // recently used list
102  auto j = i->second.second;
103  if (j != order_.begin()) {
104  order_.splice(order_.begin(), order_, j, std::next(j));
105  j = order_.begin();
106  i->second.second = j;
107  }
108  return &i->second.first;
109  }
110 
111  dict_type dict_;
112  std::list<K> order_;
113  // Maximum size of the cache.
114  size_t sz_;
115 };
116 } // namespace libime
117 
118 #endif // _FCITX_LIBIME_CORE_LRU_H_