Fangrui Song
6 years ago
6 changed files with 44 additions and 148 deletions
@ -1,136 +0,0 @@ |
|||||
// Copyright 2017-2018 ccls Authors
|
|
||||
// SPDX-License-Identifier: Apache-2.0
|
|
||||
|
|
||||
#pragma once |
|
||||
|
|
||||
#include <algorithm> |
|
||||
#include <cassert> |
|
||||
#include <limits> |
|
||||
#include <memory> |
|
||||
#include <vector> |
|
||||
|
|
||||
// Cache that evicts old entries which have not been used recently. Implemented
|
|
||||
// using array/linear search so this works well for small array sizes.
|
|
||||
template <typename TKey, typename TValue> struct LruCache { |
|
||||
explicit LruCache(int max_entries); |
|
||||
|
|
||||
// Fetches an entry for |key|. If it does not exist, |allocator| will be
|
|
||||
// invoked to create one.
|
|
||||
template <typename TAllocator> |
|
||||
std::shared_ptr<TValue> Get(const TKey &key, TAllocator allocator); |
|
||||
// Fetches the entry for |filename| and updates it's usage so it is less
|
|
||||
// likely to be evicted.
|
|
||||
std::shared_ptr<TValue> TryGet(const TKey &key); |
|
||||
// TryGetEntry, except the entry is removed from the cache.
|
|
||||
std::shared_ptr<TValue> TryTake(const TKey &key); |
|
||||
// Inserts an entry. Evicts the oldest unused entry if there is no space.
|
|
||||
void Insert(const TKey &key, const std::shared_ptr<TValue> &value); |
|
||||
|
|
||||
// Call |func| on existing entries. If |func| returns false iteration
|
|
||||
// temrinates early.
|
|
||||
template <typename TFunc> void IterateValues(TFunc func); |
|
||||
|
|
||||
// Empties the cache
|
|
||||
void Clear(void); |
|
||||
|
|
||||
private: |
|
||||
// There is a global score counter, when we access an element we increase
|
|
||||
// its score to the current global value, so it has the highest overall
|
|
||||
// score. This means that the oldest/least recently accessed value has the
|
|
||||
// lowest score.
|
|
||||
//
|
|
||||
// There is a bit of special logic to handle score overlow.
|
|
||||
struct Entry { |
|
||||
uint32_t score = 0; |
|
||||
TKey key; |
|
||||
std::shared_ptr<TValue> value; |
|
||||
bool operator<(const Entry &other) const { return score < other.score; } |
|
||||
}; |
|
||||
|
|
||||
void IncrementScore(); |
|
||||
|
|
||||
std::vector<Entry> entries_; |
|
||||
int max_entries_ = 1; |
|
||||
uint32_t next_score_ = 0; |
|
||||
}; |
|
||||
|
|
||||
template <typename TKey, typename TValue> |
|
||||
LruCache<TKey, TValue>::LruCache(int max_entries) : max_entries_(max_entries) { |
|
||||
assert(max_entries > 0); |
|
||||
} |
|
||||
|
|
||||
template <typename TKey, typename TValue> |
|
||||
template <typename TAllocator> |
|
||||
std::shared_ptr<TValue> LruCache<TKey, TValue>::Get(const TKey &key, |
|
||||
TAllocator allocator) { |
|
||||
std::shared_ptr<TValue> result = TryGet(key); |
|
||||
if (!result) |
|
||||
Insert(key, result = allocator()); |
|
||||
return result; |
|
||||
} |
|
||||
|
|
||||
template <typename TKey, typename TValue> |
|
||||
std::shared_ptr<TValue> LruCache<TKey, TValue>::TryGet(const TKey &key) { |
|
||||
// Assign new score.
|
|
||||
for (Entry &entry : entries_) { |
|
||||
if (entry.key == key) { |
|
||||
entry.score = next_score_; |
|
||||
IncrementScore(); |
|
||||
return entry.value; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return nullptr; |
|
||||
} |
|
||||
|
|
||||
template <typename TKey, typename TValue> |
|
||||
std::shared_ptr<TValue> LruCache<TKey, TValue>::TryTake(const TKey &key) { |
|
||||
for (size_t i = 0; i < entries_.size(); ++i) { |
|
||||
if (entries_[i].key == key) { |
|
||||
std::shared_ptr<TValue> copy = entries_[i].value; |
|
||||
entries_.erase(entries_.begin() + i); |
|
||||
return copy; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
return nullptr; |
|
||||
} |
|
||||
|
|
||||
template <typename TKey, typename TValue> |
|
||||
void LruCache<TKey, TValue>::Insert(const TKey &key, |
|
||||
const std::shared_ptr<TValue> &value) { |
|
||||
if ((int)entries_.size() >= max_entries_) |
|
||||
entries_.erase(std::min_element(entries_.begin(), entries_.end())); |
|
||||
|
|
||||
Entry entry; |
|
||||
entry.score = next_score_; |
|
||||
IncrementScore(); |
|
||||
entry.key = key; |
|
||||
entry.value = value; |
|
||||
entries_.push_back(entry); |
|
||||
} |
|
||||
|
|
||||
template <typename TKey, typename TValue> |
|
||||
template <typename TFunc> |
|
||||
void LruCache<TKey, TValue>::IterateValues(TFunc func) { |
|
||||
for (Entry &entry : entries_) { |
|
||||
if (!func(entry.value)) |
|
||||
break; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
template <typename TKey, typename TValue> |
|
||||
void LruCache<TKey, TValue>::IncrementScore() { |
|
||||
// Overflow.
|
|
||||
if (++next_score_ == 0) { |
|
||||
std::sort(entries_.begin(), entries_.end()); |
|
||||
for (Entry &entry : entries_) |
|
||||
entry.score = next_score_++; |
|
||||
} |
|
||||
} |
|
||||
|
|
||||
template <typename TKey, typename TValue> |
|
||||
void LruCache<TKey, TValue>::Clear(void) { |
|
||||
entries_.clear(); |
|
||||
next_score_ = 0; |
|
||||
} |
|
Loading…
Reference in new issue