1+ #pragma once
2+
3+ #include < boost/intrusive/link_mode.hpp>
4+ #include < boost/intrusive/list.hpp>
5+ #include < boost/intrusive/list_hook.hpp>
6+ #include < boost/multi_index/hashed_index.hpp>
7+
8+ #include < list>
9+ #include < functional>
10+ #include < unordered_set>
11+ #include < iostream>
12+
13+ namespace lru_boost_list {
14+ using namespace boost ::multi_index;
15+
16+ template <typename Value>
17+ struct ValueWithHook : public boost ::intrusive::list_base_hook
18+ <boost::intrusive::link_mode
19+ <boost::intrusive::safe_link>>
20+ {
21+ static size_t id;
22+ Value value;
23+ size_t internal_id;
24+
25+ ValueWithHook () : internal_id(++id) {}
26+
27+ explicit ValueWithHook (const Value& val)
28+ : value(val), internal_id(++id) {
29+ #ifdef LRU_CONTAINER_DEBUG__
30+ std::cout << " created id " << internal_id << std::endl;
31+ #endif
32+ }
33+
34+ explicit ValueWithHook (Value&& val)
35+ : value(std::move(val)), internal_id(++id) {
36+ #ifdef LRU_CONTAINER_DEBUG__
37+ std::cout << " created id " << internal_id << std::endl;
38+ #endif
39+ }
40+
41+ operator Value&() { return value; }
42+ operator const Value&() const { return value; }
43+
44+ Value* operator ->() { return &value; }
45+ const Value* operator ->() const { return &value; }
46+
47+ Value& get () { return value; }
48+ const Value& get () const { return value; }
49+ };
50+
51+ struct internal_id_tag {};
52+
53+ template <
54+ typename Value,
55+ typename IndexSpecifierList,
56+ typename Allocator = std::allocator<ValueWithHook<Value>>
57+ >
58+ class LRUCacheContainer_BoostList {
59+ private:
60+ using CacheItem = ValueWithHook<Value>;
61+ using List = boost::intrusive::list<ValueWithHook<Value>>;
62+
63+ using ExtendedIndexSpecifierList = typename boost::mpl::push_back<
64+ IndexSpecifierList,
65+ hashed_unique<
66+ tag<internal_id_tag>,
67+ member<CacheItem, size_t , &CacheItem::internal_id>
68+ >
69+ >::type;
70+
71+ using Container = multi_index_container<
72+ CacheItem,
73+ ExtendedIndexSpecifierList,
74+ Allocator
75+ >;
76+
77+ Container container;
78+ size_t max_size;
79+
80+ List usage_list;
81+
82+ public:
83+ using value_type = Value;
84+ using cache_item_type = CacheItem;
85+
86+ LRUCacheContainer_BoostList (size_t max_size) : max_size(max_size) {}
87+
88+ template <typename ... Args>
89+ bool emplace (Args&&... args) {
90+ if (container.size () >= max_size) {
91+ evict_lru ();
92+ }
93+
94+ auto result = container.emplace (std::forward<Args>(args)...);
95+ auto &value = const_cast <ValueWithHook<Value>&>(*result.first );
96+ if (result.second ) {
97+ usage_list.push_back (value);
98+ } else {
99+ touch (value);
100+ }
101+ return result.second ;
102+ }
103+
104+ bool insert (const Value& value) {
105+ return emplace (value);
106+ }
107+
108+ bool insert (Value&& value) {
109+ return emplace (std::move (value));
110+ }
111+
112+ template <typename Tag, typename Key>
113+ typename Container::template index<Tag>::type::iterator find (const Key& key) {
114+ auto & primary_index = container.template get <Tag>();
115+ auto it = primary_index.find (key);
116+
117+ if (it != primary_index.end ()) {
118+ auto &value = const_cast <ValueWithHook<Value>&>(*it);
119+ touch (value);
120+ }
121+
122+ return it;
123+ }
124+
125+ template <typename Tag, typename Key>
126+ bool contains (const Key& key) {
127+ return this ->template find <Tag, Key>(key) != container.template get <Tag>().end ();
128+ }
129+
130+ template <typename Tag, typename Key>
131+ bool erase (const Key& key) {
132+ auto & primary_index = container.template get <Tag>();
133+ auto it = primary_index.find (key);
134+ if (it != primary_index.end ()) {
135+ usage_list.erase (usage_list.iterator_to (*it));
136+ }
137+ return container.template get <Tag>().erase (key) > 0 ;
138+ }
139+
140+ template <typename Tag>
141+ auto & get () {
142+ return container.template get <Tag>();
143+ }
144+
145+ template <typename Tag>
146+ const auto & get () const {
147+ return container.template get <Tag>();
148+ }
149+
150+ size_t size () const { return container.size (); }
151+ bool empty () const { return container.empty (); }
152+ size_t capacity () const { return max_size; }
153+
154+ void set_capacity (size_t new_capacity) {
155+ max_size = new_capacity;
156+ while (container.size () > max_size) {
157+ evict_lru ();
158+ }
159+ }
160+
161+ void clear () {
162+ container.clear ();
163+ }
164+
165+ private:
166+ void evict_lru () {
167+ if (!usage_list.empty ()) {
168+ size_t id_to_erase = usage_list.begin ()->internal_id ;
169+ usage_list.erase (usage_list.begin ());
170+ container.template get <internal_id_tag>().erase (id_to_erase);
171+ }
172+ }
173+
174+ void touch (CacheItem &item) {
175+ auto it = usage_list.iterator_to (item);
176+ if (it != usage_list.end ()) {
177+ usage_list.splice (usage_list.end (), usage_list, it);
178+ }
179+ }
180+ };
181+
182+ template <typename T>
183+ size_t ValueWithHook<T>::id = 0 ;
184+ }
0 commit comments