7#include <boost/container_hash/hash.hpp> 
    9#include <userver/cache/lru_map.hpp> 
   10#include <userver/dump/dumper.hpp> 
   11#include <userver/dump/operations.hpp> 
   12#include <userver/engine/mutex.hpp> 
   14USERVER_NAMESPACE_BEGIN
 
   19template <
typename T, 
typename U, 
typename Hash = std::hash<T>,
 
   20          typename Equal = std::equal_to<T>>
 
   36  NWayLRU(size_t ways, size_t way_size, 
const Hash& hash = Hash(),
 
   37          const Equal& equal = Equal());
 
   39  void Put(
const T& key, U value);
 
   41  template <
typename Validator>
 
   42  std::optional<U> Get(
const T& key, Validator validator);
 
   44  std::optional<U> Get(
const T& key) {
 
   45    return Get(key, [](
const U&) { 
return true; });
 
   48  U GetOr(
const T& key, 
const U& default_value);
 
   52  void InvalidateByKey(
const T& key);
 
   55  template <
typename Function>
 
   58  size_t GetSize() 
const;
 
   73    Way(Way&& other) 
noexcept : cache(std::move(other.cache)) {}
 
   76    Way(
const Hash& hash, 
const Equal& equal) : cache(1, hash, equal) {}
 
   78    mutable engine::Mutex mutex;
 
   79    LruMap<T, U, Hash, Equal> cache;
 
   82  Way& GetWay(
const T& key);
 
   86  std::vector<Way> caches_;
 
   88  std::shared_ptr<
dump::Dumper> dumper_{
nullptr};
 
   91template <
typename T, 
typename U, 
typename Hash, 
typename Eq>
 
   92NWayLRU<T, U, Hash, Eq>::
NWayLRU(size_t ways, size_t way_size, 
const Hash& hash,
 
   95  caches_.reserve(ways);
 
   96  for (size_t i = 0; i < ways; ++i) caches_.emplace_back(hash, equal);
 
   97  if (ways == 0) 
throw std::logic_error(
"Ways must be positive");
 
   99  for (
auto& way : caches_) way.cache.SetMaxSize(way_size);
 
  102template <
typename T, 
typename U, 
typename Hash, 
typename Eq>
 
  103void NWayLRU<T, U, Hash, Eq>::Put(
const T& key, U value) {
 
  104  auto& way = GetWay(key);
 
  106    std::unique_lock<engine::Mutex> lock(way.mutex);
 
  107    way.cache.Put(key, std::move(value));
 
  112template <
typename T, 
typename U, 
typename Hash, 
typename Eq>
 
  113template <
typename Validator>
 
  114std::optional<U> NWayLRU<T, U, Hash, Eq>::Get(
const T& key,
 
  115                                              Validator validator) {
 
  116  auto& way = GetWay(key);
 
  117  std::unique_lock<engine::Mutex> lock(way.mutex);
 
  118  auto* value = way.cache.Get(key);
 
  121    if (validator(*value)) 
return *value;
 
  122    way.cache.Erase(key);
 
  128template <
typename T, 
typename U, 
typename Hash, 
typename Eq>
 
  129void NWayLRU<T, U, Hash, Eq>::InvalidateByKey(
const T& key) {
 
  130  auto& way = GetWay(key);
 
  132    std::unique_lock<engine::Mutex> lock(way.mutex);
 
  133    way.cache.Erase(key);
 
  138template <
typename T, 
typename U, 
typename Hash, 
typename Eq>
 
  139U NWayLRU<T, U, Hash, Eq>::GetOr(
const T& key, 
const U& default_value) {
 
  140  auto& way = GetWay(key);
 
  141  std::unique_lock<engine::Mutex> lock(way.mutex);
 
  142  return way.cache.GetOr(key, default_value);
 
  145template <
typename T, 
typename U, 
typename Hash, 
typename Eq>
 
  146void NWayLRU<T, U, Hash, Eq>::Invalidate() {
 
  147  for (
auto& way : caches_) {
 
  148    std::unique_lock<engine::Mutex> lock(way.mutex);
 
  154template <
typename T, 
typename U, 
typename Hash, 
typename Eq>
 
  155template <
typename Function>
 
  156void NWayLRU<T, U, Hash, Eq>::
VisitAll(Function func) 
const {
 
  157  for (
const auto& way : caches_) {
 
  158    std::unique_lock<engine::Mutex> lock(way.mutex);
 
  159    way.cache.VisitAll(func);
 
  163template <
typename T, 
typename U, 
typename Hash, 
typename Eq>
 
  164size_t NWayLRU<T, U, Hash, Eq>::GetSize() 
const {
 
  166  for (
const auto& way : caches_) {
 
  167    std::unique_lock<engine::Mutex> lock(way.mutex);
 
  168    size += way.cache.GetSize();
 
  173template <
typename T, 
typename U, 
typename Hash, 
typename Eq>
 
  174void NWayLRU<T, U, Hash, Eq>::
UpdateWaySize(size_t way_size) {
 
  175  for (
auto& way : caches_) {
 
  176    std::unique_lock<engine::Mutex> lock(way.mutex);
 
  177    way.cache.SetMaxSize(way_size);
 
  181template <
typename T, 
typename U, 
typename Hash, 
typename Eq>
 
  182typename NWayLRU<T, U, Hash, Eq>::Way& NWayLRU<T, U, Hash, Eq>::GetWay(
 
  188  auto seed = hash_fn_(key);
 
  189  boost::hash_combine(seed, 0);
 
  190  auto n = seed % caches_.size();
 
  194template <
typename T, 
typename U, 
typename Hash, 
typename Equal>
 
  195void NWayLRU<T, U, Hash, Equal>::Write(
dump::
Writer& writer) 
const {
 
  196  writer.Write(caches_.size());
 
  198  for (
const Way& way : caches_) {
 
  199    std::unique_lock<engine::Mutex> lock(way.mutex);
 
  201    writer.Write(way.cache.GetSize());
 
  203    way.cache.VisitAll([&writer](
const T& key, 
const U& value) {
 
  210template <
typename T, 
typename U, 
typename Hash, 
typename Equal>
 
  211void NWayLRU<T, U, Hash, Equal>::Read(
dump::
Reader& reader) {
 
  214  const auto ways = reader.Read<std::size_t>();
 
  215  for (std::size_t i = 0; i < ways; ++i) {
 
  216    const auto elements_in_way = reader.Read<std::size_t>();
 
  217    for (std::size_t j = 0; j < elements_in_way; ++j) {
 
  218      auto key = reader.Read<T>();
 
  219      auto value = reader.Read<U>();
 
  220      Put(std::move(key), std::move(value));
 
  225template <
typename T, 
typename U, 
typename Hash, 
typename Equal>
 
  226void NWayLRU<T, U, Hash, Equal>::NotifyDumper() {
 
  227  if (dumper_ != 
nullptr) {
 
  228    dumper_->OnUpdateCompleted();
 
  232template <
typename T, 
typename U, 
typename Hash, 
typename Equal>
 
  233void NWayLRU<T, U, Hash, Equal>::
SetDumper(
 
  234    std::shared_ptr<
dump::Dumper> dumper) {
 
  235  dumper_ = std::move(dumper);