| // Copyright 2018 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #ifndef PERIDOT_BIN_LEDGER_CACHE_LRU_CACHE_H_ |
| #define PERIDOT_BIN_LEDGER_CACHE_LRU_CACHE_H_ |
| |
| #include <functional> |
| #include <list> |
| #include <map> |
| #include <set> |
| #include <vector> |
| |
| #include <lib/fit/function.h> |
| #include <lib/fxl/logging.h> |
| |
| namespace cache { |
| // Implements a self-populating LRU cache. |
| // |
| // This class allows the user to provide a generator for its data, and will |
| // cache a given number of entries, discarding least used entries once its reach |
| // its maximum capacity. |
| // |
| // K is the type of the key of the cached data. |
| // V is the type of the cached data. |
| // S is the type of the success status for the data generator. |
| template <typename K, typename V, typename S> |
| class LRUCache { |
| public: |
| // Constructor. |
| // |
| // |size| is the maximum capacity of the cache. |
| // |ok_status| is the success status of the generator. |
| // |generator| generates the value to be cached for the given key. It takes a |
| // callback to returns its result. It must return |ok_status| as a status |
| // when the request is successful. Any other return value is considered a |
| // failure. |
| LRUCache(size_t size, S ok_status, |
| fit::function<void(K, fit::function<void(S, V)>)> generator) |
| : size_(size), ok_status_(ok_status), generator_(std::move(generator)) {} |
| |
| // Retrieves the value for |key| and returns it to |callback|. |
| // |
| // If the value is cached, |callback| will be called synchronously. Otherwise, |
| // |generator| will be called, and depending on its implementation, |callback| |
| // might be called synchronously or not. |
| void Get(const K& key, fit::function<void(S, const V&)> callback) { |
| auto iterator = map_.find(key); |
| if (iterator != map_.end()) { |
| // Move the list iterator to the front. |
| auto list_iterator = iterator->second; |
| values_.splice(values_.begin(), values_, list_iterator); |
| |
| // Call back. |
| callback(ok_status_, iterator->second->second); |
| return; |
| } |
| auto request_iterator = requests_.find(key); |
| if (request_iterator != requests_.end()) { |
| request_iterator->second.push_back(std::move(callback)); |
| return; |
| } |
| requests_[key].push_back(std::move(callback)); |
| generator_(key, [this, key](S status, V value) { |
| auto request_iterator = requests_.find(key); |
| FXL_DCHECK(request_iterator != requests_.end()); |
| auto callbacks = std::move(request_iterator->second); |
| requests_.erase(request_iterator); |
| |
| if (status != ok_status_) { |
| for (const auto& callback : callbacks) { |
| callback(status, value); |
| } |
| return; |
| } |
| |
| values_.emplace_front(key, value); |
| map_[std::move(key)] = values_.begin(); |
| if (values_.size() > size_) { |
| map_.erase(values_.rbegin()->first); |
| values_.pop_back(); |
| } |
| for (const auto& callback : callbacks) { |
| callback(status, value); |
| } |
| }); |
| } |
| |
| private: |
| using ValueList = std::list<std::pair<K, V>>; |
| ValueList values_; |
| std::map<K, typename ValueList::iterator> map_; |
| std::map<K, std::vector<fit::function<void(S, const V&)>>> requests_; |
| size_t size_; |
| S ok_status_; |
| fit::function<void(K, fit::function<void(S, V)>)> generator_; |
| }; |
| |
| } // namespace cache |
| |
| #endif // PERIDOT_BIN_LEDGER_CACHE_LRU_CACHE_H_ |