Turi Create  4.0
lru.hpp
1 /* Copyright © 2017 Apple Inc. All rights reserved.
2  *
3  * Use of this source code is governed by a BSD-3-clause license that can
4  * be found in the LICENSE.txt file or at https://opensource.org/licenses/BSD-3-Clause
5  */
6 #include <utility>
7 #include <boost/multi_index_container.hpp>
8 #include <boost/multi_index/tag.hpp>
9 #include <boost/multi_index/member.hpp>
10 #include <boost/multi_index/hashed_index.hpp>
11 #include <boost/multi_index/sequenced_index.hpp>
12 
13 
14 namespace turi {
15 
16 /**
17  * \ingroup util
18  * A simple general purpose LRU cache implementation.
19  */
20 template <typename Key, typename Value>
21 struct lru_cache {
22  typedef std::pair<Key, Value> value_type;
23 
24  typedef boost::multi_index_container <value_type,
25  boost::multi_index::indexed_by <
26  boost::multi_index::hashed_unique<BOOST_MULTI_INDEX_MEMBER(value_type, Key, first)>,
27  boost::multi_index::sequenced<>
28  >
29  > cache_type;
30 
31  typedef typename cache_type::template nth_index<1>::type::const_iterator iterator;
32  typedef iterator const_iterator;
33 
34  typedef typename cache_type::template nth_index<1>::type::const_reverse_iterator reverse_iterator;
35  typedef reverse_iterator const_reverse_iterator;
36 
37  lru_cache() = default;
38  lru_cache(const lru_cache&) = default;
39  lru_cache(lru_cache&&) = default;
40  lru_cache& operator=(const lru_cache&) = default;
41  lru_cache& operator=(lru_cache&&) = default;
42 
43  /**
44  * queries for a particular key. If it does not exist {false, Value()} is
45  * returned. If it exists {true, value} is returned and the key is bumped
46  * to the front of the cache.
47  */
48  std::pair<bool, Value> query(const Key& key) {
49  auto& hash_container = m_cache.template get<0>();
50  auto& seq_container = m_cache.template get<1>();
51  auto iter = hash_container.find(key);
52  if (iter == hash_container.end()) {
53  ++m_misses;
54  return {false, Value()};
55  } else {
56  ++m_hits;
57  seq_container.relocate(seq_container.begin(), m_cache.template project<1>(iter));
58  return {true, iter->second};
59  }
60  }
61 
62  /**
63  * Inserts a key into the cache. If the key already exists, it is overwritten.
64  * If the size of the cache is larger than the limit, the least recently
65  * used items are erased.
66  */
67  void insert(const Key& key, const Value& value) {
68  auto& hash_container = m_cache.template get<0>();
69  auto& seq_container = m_cache.template get<1>();
70  auto iter = hash_container.find(key);
71  if (iter == hash_container.end()) {
72  seq_container.push_front(value_type{key, value});
73  if (size() > get_size_limit()) {
74  seq_container.pop_back();
75  }
76  } else {
77  hash_container.replace(iter, value_type{key, value});
78  seq_container.relocate(seq_container.begin(), m_cache.template project<1>(iter));
79  }
80  }
81 
82  void erase(const Key& key) {
83  auto& hash_container = m_cache.template get<0>();
84  auto iter = hash_container.find(key);
85  if (iter != hash_container.end()) {
86  hash_container.erase(iter);
87  }
88  }
89 
90  /**
91  * Retuns an iterator to the data
92  */
93  const_iterator begin() const {
94  auto& seq_container = m_cache.template get<1>();
95  return seq_container.begin();
96  }
97 
98  /**
99  * Retuns an iterator to the data
100  */
101  const_iterator end() const {
102  auto& seq_container = m_cache.template get<1>();
103  return seq_container.end();
104  }
105 
106  /** Iterator to cache in reverse */
107  const_reverse_iterator rbegin() const {
108  auto& seq_container = m_cache.template get<1>();
109  return seq_container.rbegin();
110  }
111 
112  /** Iterator to cache in reverse */
113  const_reverse_iterator rend() const {
114  auto& seq_container = m_cache.template get<1>();
115  return seq_container.rend();
116  }
117 
118 
119  /// Returns the current size of the cache
120  size_t size() const {
121  return m_cache.size();
122  }
123 
124  /// Sets the upper limit on the size of the cache
125  void set_size_limit(size_t limit) {
126  m_limit = limit;
127  }
128 
129  /// Gets the upper limit of the size of the cache
130  size_t get_size_limit() const {
131  return m_limit;
132  }
133 
134  /// Returns the number of query hits
135  size_t hits() const {
136  return m_hits;
137  }
138 
139 
140  /// Returns the number of query misses
141  size_t misses() const {
142  return m_misses;
143  }
144 
145  private:
146  cache_type m_cache;
147  size_t m_limit = (size_t)(-1);
148  size_t m_hits = 0;
149  size_t m_misses = 0;
150 };
151 } // namespace turi
void insert(const Key &key, const Value &value)
Definition: lru.hpp:67
void set_size_limit(size_t limit)
Sets the upper limit on the size of the cache.
Definition: lru.hpp:125
const_iterator end() const
Definition: lru.hpp:101
size_t misses() const
Returns the number of query misses.
Definition: lru.hpp:141
const_iterator begin() const
Definition: lru.hpp:93
const_reverse_iterator rend() const
Definition: lru.hpp:113
size_t hits() const
Returns the number of query hits.
Definition: lru.hpp:135
size_t size() const
Returns the current size of the cache.
Definition: lru.hpp:120
const_reverse_iterator rbegin() const
Definition: lru.hpp:107
std::pair< bool, Value > query(const Key &key)
Definition: lru.hpp:48
size_t get_size_limit() const
Gets the upper limit of the size of the cache.
Definition: lru.hpp:130