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));