diff --git a/src/xrpld/core/detail/LRUMap.h b/src/xrpld/core/detail/LRUMap.h index 772ea36b8f..71ca5fa49f 100644 --- a/src/xrpld/core/detail/LRUMap.h +++ b/src/xrpld/core/detail/LRUMap.h @@ -20,73 +20,114 @@ #ifndef RIPPLE_APP_LRU_MAP_H_INCLUDED #define RIPPLE_APP_LRU_MAP_H_INCLUDED +#include #include -#include +#include +#include #include namespace ripple { -template +template < + class Key, + class Value, + class Hash = std::hash, + class KeyEq = std::equal_to> class LRUMap { + using List = std::list; + using DataMap = std::unordered_map; + using PosMap = + std::unordered_map; + public: explicit LRUMap(std::size_t capacity) : capacity_(capacity) { - // TODO: check capacity_ > 0 + if (!capacity_) + throw std::invalid_argument( + "LRUMap capacity must be positive."); // TODO XRPL_ASSERT + data_.reserve(capacity_); + pos_.reserve(capacity_); } Value& operator[](Key const& key) { - auto it = data_.find(key); - if (it != data_.end()) + if (auto it = data_.find(key); it != data_.end()) { - bump_to_front(key); + auto lit = pos_.at(key); + // promote + usage_.splice(usage_.begin(), usage_, lit); return it->second; } if (data_.size() >= capacity_) { - std::size_t excess = (data_.size() + 1) - capacity_; - for (std::size_t i = 0; i < excess; ++i) - { - auto lru = usage_list_.back(); - usage_list_.pop_back(); - data_.erase(lru); - } + auto const& lru_key = usage_.back(); + data_.erase(lru_key); + pos_.erase(lru_key); + usage_.pop_back(); } - usage_list_.push_front(key); - return data_[key]; + usage_.emplace_front(key); + pos_.emplace(key, usage_.begin()); + auto [it, _] = data_.emplace(key, Value{}); + return it->second; } - auto + Value* + get(Key const& key) + { + auto it = data_.find(key); + if (it == data_.end()) + return nullptr; + auto lit = pos_.at(key); + usage_.splice(usage_.begin(), usage_, lit); + return &it->second; + } + + // TODO: remove + Value const* + peek(Key const& key) const + { + auto it = data_.find(key); + return it == data_.end() ? nullptr : &it->second; + } + + using iterator = typename DataMap::iterator; + using const_iterator = typename DataMap::const_iterator; + + iterator find(Key const& key) { return data_.find(key); } - auto + + const_iterator find(Key const& key) const { return data_.find(key); } - auto + iterator begin() { return data_.begin(); } - auto + + const_iterator begin() const { return data_.begin(); } - auto + + iterator end() { return data_.end(); } - auto + + const_iterator end() const { return data_.end(); @@ -98,54 +139,73 @@ public: auto it = data_.find(key); if (it == data_.end()) return false; - for (auto list_it = usage_list_.begin(); list_it != usage_list_.end(); - ++list_it) - { - if (*list_it == key) - { - usage_list_.erase(list_it); - break; - } - } + usage_.erase(pos_.at(key)); + pos_.erase(key); data_.erase(it); return true; } + template + Value& + put(Key const& key, Args&&... args) // assign/construct + promote + { + if (auto it = data_.find(key); it != data_.end()) + { + it->second = Value(std::forward(args)...); + auto lit = pos_.at(key); + usage_.splice(usage_.begin(), usage_, lit); + return it->second; + } + if (data_.size() >= capacity_) + { + auto const& lru_key = usage_.back(); + data_.erase(lru_key); + pos_.erase(lru_key); + usage_.pop_back(); + } + usage_.emplace_front(key); + pos_.emplace(key, usage_.begin()); + auto [it, _] = data_.emplace(key, Value(std::forward(args)...)); + return it->second; + } + + bool + contains(Key const& key) const + { + return data_.find(key) != data_.end(); + } + std::size_t size() const noexcept { return data_.size(); } + std::size_t capacity() const noexcept { return capacity_; } + + bool + empty() const noexcept + { + return data_.empty(); + } + void clear() { data_.clear(); - usage_list_.clear(); + pos_.clear(); + usage_.clear(); } private: - void - bump_to_front(Key const& key) - { - for (auto it = usage_list_.begin(); it != usage_list_.end(); ++it) - { - if (*it == key) - { - usage_list_.erase(it); - usage_list_.push_front(key); - return; - } - } - } - - std::size_t capacity_; - std::map data_; - std::list usage_list_; + std::size_t const capacity_; + DataMap data_; // Key -> Value (this is what callers iterate/find over) + PosMap pos_; // Key -> list position + List usage_; // recency order }; } // namespace ripple