6#include <boost/intrusive/link_mode.hpp>
7#include <boost/intrusive/list.hpp>
8#include <boost/intrusive/list_hook.hpp>
9#include <boost/intrusive/unordered_set.hpp>
10#include <boost/intrusive/unordered_set_hook.hpp>
12#include <userver/utils/assert.hpp>
13#include <userver/utils/impl/intrusive_link_mode.hpp>
15USERVER_NAMESPACE_BEGIN
17namespace cache::
impl {
19struct EmptyPlaceholder {};
21using LinkMode = utils::impl::IntrusiveLinkMode;
22using LruListHook = boost::intrusive::list_base_hook<LinkMode>;
23using LruHashSetHook = boost::intrusive::unordered_set_base_hook<LinkMode>;
25template <
class Key,
class Value>
27class LruNode
final :
public LruListHook,
public LruHashSetHook {
29 template <
typename... Args>
30 explicit LruNode(Key&& key, Args&&... args)
31 : key_(std::move(key)), value_(std::forward<Args>(args)...) {}
33 explicit LruNode(Key&& key, Value&& value)
34 : key_(std::move(key)), value_(std::move(value)) {}
36 void SetKey(Key key) { key_ = std::move(key); }
38 void SetValue(Value&& value) { value_ = std::move(value); }
40 const Key& GetKey()
const noexcept {
return key_; }
42 const Value& GetValue()
const noexcept {
return value_; }
43 Value& GetValue()
noexcept {
return value_; }
52class LruNode<Key, EmptyPlaceholder>
final :
public LruListHook,
53 public LruHashSetHook {
55 explicit LruNode(Key&& key, EmptyPlaceholder )
56 : key_(std::move(key)) {}
58 void SetKey(Key key) { key_ = std::move(key); }
60 void SetValue(EmptyPlaceholder )
noexcept {}
62 const Key& GetKey()
const noexcept {
return key_; }
64 EmptyPlaceholder& GetValue()
const noexcept {
65 static EmptyPlaceholder kValue{};
73template <
class Key,
class Value>
74const Key& GetKey(
const LruNode<Key, Value>& node)
noexcept {
79const T& GetKey(
const T& key)
noexcept {
83template <
typename T,
typename U,
typename Hash = std::hash<T>,
84 typename Equal = std::equal_to<T>>
87 using NodeType = std::unique_ptr<LruNode<T, U>>;
89 explicit LruBase(size_t max_size,
const Hash& hash,
const Equal& equal);
90 ~LruBase() { Clear(); }
92 LruBase(LruBase&& other)
noexcept
93 : buckets_(std::move(other.buckets_)),
94 map_(std::move(other.map_)),
95 list_(std::move(other.list_)) {
96 other.buckets_.clear();
101 LruBase& operator=(LruBase&& other)
noexcept {
102 if (
this != &other) Clear();
104 swap(other.buckets_, buckets_);
105 swap(other.map_, map_);
106 swap(other.list_, list_);
111 LruBase(
const LruBase& lru) =
delete;
112 LruBase& operator=(
const LruBase& lru) =
delete;
114 bool Put(
const T& key, U value);
116 template <
typename... Args>
117 U* Emplace(
const T&, Args&&... args);
119 void Erase(
const T& key);
121 U* Get(
const T& key);
123 const T* GetLeastUsedKey()
const;
125 U* GetLeastUsedValue();
127 NodeType ExtractLeastUsedNode();
129 void SetMaxSize(size_t new_max_size);
131 void Clear()
noexcept;
133 template <
typename Function>
134 void VisitAll(Function&& func)
const;
136 template <
typename Function>
137 void VisitAll(Function&& func);
139 size_t GetSize()
const;
141 U& InsertNode(NodeType&& node)
noexcept;
142 NodeType ExtractNode(
const T& key)
noexcept;
144 std::size_t GetCapacity()
const;
147 using Node = LruNode<T, U>;
149 boost::intrusive::list<Node, boost::intrusive::constant_time_size<
false>>;
151 struct LruNodeHash : Hash {
152 LruNodeHash(
const Hash& h) : Hash{h} {}
154 template <
class NodeOrKey>
155 auto operator()(
const NodeOrKey& x)
const {
156 return Hash::operator()(
impl::GetKey(x));
160 struct LruNodeEqual : Equal {
161 LruNodeEqual(
const Equal& eq) : Equal{eq} {}
163 template <
class NodeOrKey1,
class NodeOrKey2>
164 auto operator()(
const NodeOrKey1& x,
const NodeOrKey2& y)
const {
165 return Equal::operator()(
impl::GetKey(x),
impl::GetKey(y));
169 using Map = boost::intrusive::unordered_set<
170 Node, boost::intrusive::constant_time_size<
true>,
171 boost::intrusive::hash<LruNodeHash>,
172 boost::intrusive::equal<LruNodeEqual>>;
174 using BucketTraits =
typename Map::bucket_traits;
175 using BucketType =
typename Map::bucket_type;
177 U& Add(
const T& key, U value);
178 void MarkRecentlyUsed(Node& node)
noexcept;
179 std::unique_ptr<Node> ExtractNode(
typename List::iterator it)
noexcept;
181 std::vector<BucketType> buckets_;
186template <
typename T,
typename U,
typename Hash,
typename Equal>
187LruBase<T, U, Hash, Equal>::LruBase(size_t max_size,
const Hash& hash,
189 : buckets_(max_size ? max_size : 1),
190 map_(BucketTraits(buckets_.data(), buckets_.size()), hash, eq) {
194template <
typename T,
typename U,
typename Hash,
typename Eq>
195bool LruBase<T, U, Hash, Eq>::Put(
const T& key, U value) {
196 auto it = map_.find(key, map_.hash_function(), map_.key_eq());
197 if (it != map_.end()) {
198 it->SetValue(std::move(value));
199 MarkRecentlyUsed(*it);
203 Add(key, std::move(value));
207template <
typename T,
typename U,
typename Hash,
typename Eq>
208template <
typename... Args>
209U* LruBase<T, U, Hash, Eq>::Emplace(
const T& key, Args&&... args) {
210 auto* existing = Get(key);
211 if (existing)
return existing;
213 if constexpr (std::is_move_assignable_v<U>) {
214 return &Add(key, U{std::forward<Args>(args)...});
216 auto node = std::make_unique<Node>(T{key}, std::forward<Args>(args)...);
217 if (map_.size() >= buckets_.size()) {
218 ExtractNode(list_.begin());
220 return &InsertNode(std::move(node));
224template <
typename T,
typename U,
typename Hash,
typename Eq>
225void LruBase<T, U, Hash, Eq>::Erase(
const T& key) {
226 auto it = map_.find(key, map_.hash_function(), map_.key_eq());
227 if (it == map_.end())
return;
228 ExtractNode(list_.iterator_to(*it));
231template <
typename T,
typename U,
typename Hash,
typename Eq>
232U* LruBase<T, U, Hash, Eq>::Get(
const T& key) {
233 auto it = map_.find(key, map_.hash_function(), map_.key_eq());
234 if (it == map_.end())
return nullptr;
235 MarkRecentlyUsed(*it);
236 return &it->GetValue();
239template <
typename T,
typename U,
typename Hash,
typename Eq>
240const T* LruBase<T, U, Hash, Eq>::GetLeastUsedKey()
const {
241 if (list_.empty())
return nullptr;
242 return &list_.front().GetKey();
245template <
typename T,
typename U,
typename Hash,
typename Eq>
246U* LruBase<T, U, Hash, Eq>::GetLeastUsedValue() {
247 if (list_.empty())
return nullptr;
248 return &list_.front().GetValue();
251template <
typename T,
typename U,
typename Hash,
typename Eq>
252typename LruBase<T, U, Hash, Eq>::NodeType
253LruBase<T, U, Hash, Eq>::ExtractLeastUsedNode() {
254 if (list_.empty())
return std::unique_ptr<LruNode<T, U>>();
255 return ExtractNode(list_.begin());
258template <
typename T,
typename U,
typename Hash,
typename Eq>
259void LruBase<T, U, Hash, Eq>::SetMaxSize(size_t new_max_size) {
261 if (!new_max_size) ++new_max_size;
263 if (buckets_.size() == new_max_size) {
267 while (map_.size() > new_max_size) {
268 ExtractNode(list_.begin());
271 std::vector<BucketType> new_buckets(new_max_size);
272 map_.rehash(BucketTraits(new_buckets.data(), new_max_size));
273 buckets_.swap(new_buckets);
276template <
typename T,
typename U,
typename Hash,
typename Eq>
277void LruBase<T, U, Hash, Eq>::Clear()
noexcept {
278 while (!list_.empty()) {
279 ExtractNode(list_.begin());
283template <
typename T,
typename U,
typename Hash,
typename Eq>
284template <
typename Function>
285void LruBase<T, U, Hash, Eq>::VisitAll(Function&& func)
const {
286 for (
const auto& node : map_) {
287 func(node.GetKey(), node.GetValue());
291template <
typename T,
typename U,
typename Hash,
typename Eq>
292template <
typename Function>
293void LruBase<T, U, Hash, Eq>::VisitAll(Function&& func) {
294 for (
auto& node : map_) {
295 func(node.GetKey(), node.GetValue());
299template <
typename T,
typename U,
typename Hash,
typename Eq>
300size_t LruBase<T, U, Hash, Eq>::GetSize()
const {
304template <
typename T,
typename U,
typename Hash,
typename Eq>
305std::size_t LruBase<T, U, Hash, Eq>::GetCapacity()
const {
306 return buckets_.size();
309template <
typename T,
typename U,
typename Hash,
typename Eq>
310U& LruBase<T, U, Hash, Eq>::Add(
const T& key, U value) {
311 if (map_.size() < buckets_.size()) {
312 auto node = std::make_unique<Node>(T{key}, std::move(value));
313 return InsertNode(std::move(node));
316 auto node = ExtractNode(list_.begin());
318 node->SetValue(std::move(value));
319 return InsertNode(std::move(node));
322template <
typename T,
typename U,
typename Hash,
typename Eq>
323void LruBase<T, U, Hash, Eq>::MarkRecentlyUsed(Node& node)
noexcept {
324 list_.splice(list_.end(), list_, list_.iterator_to(node));
327template <
typename T,
typename U,
typename Hash,
typename Eq>
328std::unique_ptr<LruNode<T, U>> LruBase<T, U, Hash, Eq>::ExtractNode(
329 typename List::iterator it)
noexcept {
332 std::unique_ptr<Node> ret(&*it);
333 map_.erase(map_.iterator_to(*it));
338template <
typename T,
typename U,
typename Hash,
typename Eq>
339U& LruBase<T, U, Hash, Eq>::InsertNode(
340 LruBase<T, U, Hash, Eq>::NodeType&& node)
noexcept {
343 auto [it, ok] = map_.insert(*node);
345 list_.insert(list_.end(), *node);
347 return node.release()->GetValue();
350template <
typename T,
typename U,
typename Hash,
typename Eq>
351typename LruBase<T, U, Hash, Eq>::NodeType LruBase<T, U, Hash, Eq>::ExtractNode(
352 const T& key)
noexcept {
353 auto it = map_.find(key, map_.hash_function(), map_.key_eq());
354 if (it == map_.end()) {
358 return ExtractNode(list_.iterator_to(*it));