userver: /data/code/userver/libraries/multi-index-lru/include/userver/multi-index-lru/container.hpp Source File
Loading...
Searching...
No Matches
container.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/multi-index-lru/container.hpp
4/// @brief @copybrief multi_index_lru::Container
5
6#include <vector>
7
8#include "impl/mpl_helpers.hpp"
9
10USERVER_NAMESPACE_BEGIN
11
12namespace multi_index_lru {
13
14template <typename Value, typename IndexSpecifierList, typename Allocator = std::allocator<Value>>
15class ExpirableContainer;
16
17/// @ingroup userver_containers
18///
19/// @brief MultiIndex LRU container
20template <typename Value, typename IndexSpecifierList, typename Allocator = std::allocator<Value>>
21class Container {
22public:
23 explicit Container(size_t max_size)
24 : max_size_(max_size)
25 {}
26
27 /// @brief Constructs element in-place
28 /// @returns pair<iterator, bool>
29 template <typename... Args>
30 auto emplace(Args&&... args) {
31 auto& seq_index = get_sequenced();
32
33 std::pair<decltype(seq_index.begin()), bool> result;
34
35 if (free_nodes_.size() > 0) {
36 auto node = std::move(free_nodes_.back());
37 free_nodes_.pop_back();
38
39 node.value() = Value(std::forward<Args>(args)...);
40 auto ret = seq_index.insert(seq_index.begin(), std::move(node));
41 result = {ret.position, ret.inserted};
42 } else {
43 result = seq_index.emplace_front(std::forward<Args>(args)...);
44 }
45 if (!result.second) {
46 seq_index.relocate(seq_index.begin(), result.first);
47 } else if (seq_index.size() > max_size_) {
48 free_nodes_.emplace_back(seq_index.extract(std::prev(seq_index.end())));
49 }
50 return result;
51 }
52
53 bool insert(const Value& value) { return emplace(value).second; }
54
55 bool insert(Value&& value) { return emplace(std::move(value)).second; }
56
57 template <typename Tag, typename Key>
58 auto find(const Key& key) {
59 auto& primary_index = get_index<Tag>();
60 auto it = primary_index.find(key);
61
62 if (it != primary_index.end()) {
63 auto& seq_index = get_sequenced();
64 auto seq_it = container_.template project<0>(it);
65 seq_index.relocate(seq_index.begin(), seq_it);
66 }
67
68 return it;
69 }
70
71 template <typename Tag, typename Key>
72 auto find_no_update(const Key& key) const {
73 return get_index<Tag>().find(key);
74 }
75
76 /// @brief Returns range of matching elements, updates all
77 template <typename Tag, typename Key>
78 auto equal_range(const Key& key) {
79 auto& primary_index = get_index<Tag>();
80
81 auto [begin, end] = primary_index.equal_range(key);
82 auto it = begin;
83
84 auto& seq_index = get_sequenced();
85 while (it != end) {
86 seq_index.relocate(seq_index.begin(), project_to_sequenced(it));
87 ++it;
88 }
89
90 return std::pair{begin, end};
91 }
92
93 /// @brief Returns range of matching elements without updates
94 template <typename Tag, typename Key>
95 auto equal_range_no_update(const Key& key) const {
96 return get_index<Tag>().equal_range(key);
97 }
98
99 template <typename Tag, typename Key>
100 bool contains(const Key& key) {
101 return this->template find<Tag, Key>(key) != end<Tag>();
102 }
103
104 template <typename Tag, typename Key>
105 bool contains_no_update(const Key& key) const {
106 return this->template find_no_update<Tag, Key>(key) != end<Tag>();
107 }
108
109 /// Removes the @b it from container, leaving the node in an internal pool. The key and value are not destroyed
110 /// and are reused on next insertion.
111 template <typename IteratorType>
112 auto erase(IteratorType it) {
113 auto it_0 = project_to_sequenced(it);
114 auto& seq_index = get_sequenced();
115 if (it_0 == seq_index.end()) {
116 return it;
117 }
118
119 ++it;
120 free_nodes_.emplace_back(seq_index.extract(it_0));
121 return it;
122 }
123
124 /// Removes the @b key from container, leaving the node in an internal pool. The key and value are not destroyed
125 /// and are reused on next insertion.
126 template <typename Tag, typename Key>
127 bool erase(const Key& key) {
128 auto it = find_no_update<Tag, Key>(key);
129 return erase(it) != end<Tag>();
130 }
131
132 std::size_t size() const noexcept { return container_.size(); }
133
134 bool empty() const noexcept { return container_.empty(); }
135
136 std::size_t capacity() const noexcept { return max_size_; }
137
138 void set_capacity(std::size_t new_capacity) {
139 max_size_ = new_capacity;
140 if (container_.size() <= max_size_) {
141 return;
142 }
143
145 auto& seq_index = get_sequenced();
146 while (container_.size() > max_size_) {
147 // Not putting the node to `free_nodes_`, because the container is already full
148 seq_index.extract(std::prev(seq_index.end()));
149 }
150 }
151
152 /// Clears the internal nodes pool
153 void shrink_to_fit() { free_nodes_.clear(); }
154
155 /// Removes all elements from the container, leaving the node in an internal pool. The keys and values are
156 /// not destroyed and are reused on next insertion.
157 void clear() {
158 auto& seq_index = get_sequenced();
159 free_nodes_.reserve(free_nodes_.size() + container_.size());
160 while (!container_.empty()) {
161 free_nodes_.emplace_back(seq_index.extract(std::prev(seq_index.end())));
162 }
163 }
164
165 /// @brief Returns end iterator for the specified `Tag` index
166 template <typename Tag>
167 auto end() const {
168 return get_index<Tag>().end();
169 }
170
171 auto find_last_accessed_no_update() const { return std::prev(get_sequenced().end()); }
172
173private:
174 using ExtendedIndexSpecifierList = impl::add_index_t<boost::multi_index::sequenced<>, IndexSpecifierList>;
175 using BoostContainer = boost::multi_index::multi_index_container<Value, ExtendedIndexSpecifierList, Allocator>;
176
177 BoostContainer container_;
178 std::size_t max_size_;
179 std::vector<typename BoostContainer::node_type> free_nodes_;
180
181 auto& get_sequenced() noexcept { return container_.template get<0>(); }
182
183 const auto& get_sequenced() const noexcept { return container_.template get<0>(); }
184
185 template <typename Tag>
186 auto& get_index() {
187 return container_.template get<Tag>();
188 }
189
190 template <typename Tag>
191 const auto& get_index() const noexcept {
192 return container_.template get<Tag>();
193 }
194
195 template <typename IterT>
196 auto project_to_sequenced(IterT it) {
197 return container_.template project<0>(it);
198 }
199};
200
201} // namespace multi_index_lru
202
203USERVER_NAMESPACE_END