blob: c03d203352d444201f6e5c9c7a1ab95546d62885 [file] [log] [blame]
/*
* Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GAPIC_MRU_CACHE_H
#define GAPIC_MRU_CACHE_H
#include <unordered_map>
namespace gapic {
// MRUCache is an implementation of a most-recent-used key-value cache.
// All operations are O(1) complexity.
template <typename K, typename V>
class MRUCache {
public:
typedef K key_type;
typedef V value_type;
// Constructs the cache with the specified maximum capacity.
inline MRUCache(size_t capacity = 16);
inline ~MRUCache();
// adds the key-value pair into the cache and makes this the most recently
// used entry. If the cache is already full before calling add() then the
// least recently used entry is evicted from the cache.
inline void add(const K& key, const V& value);
// get looks up the entry for the specified key.
// If an entry with the given key exists in the cache then its value is
// assigned to value, the entry becomes the most recently used and true is
// returned. If no entry with the given key exists in the cache then false
// is returned.
inline bool get(const K& key, V& value);
// clear removes all items from the cache.
inline void clear();
// size returns the number of entries in the cache.
inline size_t size() const;
// capacity returns the maximum capacity for the cache.
inline size_t capacity() const;
private:
MRUCache(const MRUCache&) = delete;
MRUCache& operator=(MRUCache const&) = delete;
struct Item {
inline Item();
inline void unlink();
inline void link(Item* prev);
K key;
V value;
Item* prev;
Item* next;
};
typedef std::unordered_map<K, Item*> Map;
Map mItems; // Dummy item used for head / tail of linked-list.
Item mList;
size_t mCapacity;
};
template <typename K, typename V>
inline MRUCache<K, V>::Item::Item()
: prev(nullptr), next(nullptr) {}
template <typename K, typename V>
inline void MRUCache<K, V>::Item::unlink() {
if (prev != nullptr) {
prev->next = next;
next->prev = prev;
next = prev = nullptr;
}
}
template <typename K, typename V>
inline void MRUCache<K, V>::Item::link(Item* prev) {
unlink();
next = prev->next;
next->prev = this;
this->prev = prev;
prev->next = this;
}
template <typename K, typename V>
inline MRUCache<K, V>::MRUCache(size_t capacity /* = 16 */)
: mCapacity(capacity) {
mList.next = mList.prev = &mList;
}
template <typename K, typename V>
inline MRUCache<K, V>::~MRUCache<K, V>() {
clear();
}
template <typename K, typename V>
inline void MRUCache<K, V>::add(const K& key, const V& value) {
auto it = mItems.find(key);
if (it == mItems.end()) {
Item* item;
if (size() < capacity()) {
item = new Item();
} else {
item = mList.prev;
item->unlink();
mItems.erase(item->key);
}
item->key = key;
item->value = value;
item->link(&mList);
mItems[item->key] = item;
} else {
it->second->unlink();
it->second->link(&mList);
it->second->value = value;
}
}
template <typename K, typename V>
inline bool MRUCache<K, V>::get(const K& key, V& value) {
auto it = mItems.find(key);
if (it == mItems.end()) {
return false;
}
it->second->unlink();
it->second->link(&mList);
value = it->second->value;
return true;
}
template <typename K, typename V>
inline void MRUCache<K, V>::clear() {
for (auto it : mItems) {
auto item = it.second;
item->unlink();
delete item;
}
mItems.clear();
}
template <typename K, typename V>
inline size_t MRUCache<K, V>::size() const {
return mItems.size();
}
template <typename K, typename V>
inline size_t MRUCache<K, V>::capacity() const {
return mCapacity;
}
} // namespace gapic
#endif // GAPIC_MRU_CACHE_H