2018-08-21 05:27:52 +00:00
|
|
|
/* Copyright 2017-2018 ccls Authors
|
|
|
|
|
|
|
|
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.
|
|
|
|
==============================================================================*/
|
|
|
|
|
2017-12-04 03:24:42 +00:00
|
|
|
#pragma once
|
|
|
|
|
2017-12-06 03:49:16 +00:00
|
|
|
#include <algorithm>
|
2017-12-06 03:32:33 +00:00
|
|
|
#include <cassert>
|
2017-12-04 03:24:42 +00:00
|
|
|
#include <limits>
|
|
|
|
#include <memory>
|
2017-12-06 03:32:33 +00:00
|
|
|
#include <vector>
|
2017-12-04 03:24:42 +00:00
|
|
|
|
|
|
|
// Cache that evicts old entries which have not been used recently. Implemented
|
|
|
|
// using array/linear search so this works well for small array sizes.
|
2018-08-09 17:08:14 +00:00
|
|
|
template <typename TKey, typename TValue> struct LruCache {
|
2017-12-04 03:24:42 +00:00
|
|
|
explicit LruCache(int max_entries);
|
|
|
|
|
2017-12-05 16:24:37 +00:00
|
|
|
// Fetches an entry for |key|. If it does not exist, |allocator| will be
|
|
|
|
// invoked to create one.
|
2017-12-04 03:24:42 +00:00
|
|
|
template <typename TAllocator>
|
2018-08-09 17:08:14 +00:00
|
|
|
std::shared_ptr<TValue> Get(const TKey &key, TAllocator allocator);
|
2017-12-04 03:24:42 +00:00
|
|
|
// Fetches the entry for |filename| and updates it's usage so it is less
|
|
|
|
// likely to be evicted.
|
2018-08-09 17:08:14 +00:00
|
|
|
std::shared_ptr<TValue> TryGet(const TKey &key);
|
2017-12-04 03:24:42 +00:00
|
|
|
// TryGetEntry, except the entry is removed from the cache.
|
2018-08-09 17:08:14 +00:00
|
|
|
std::shared_ptr<TValue> TryTake(const TKey &key);
|
2017-12-04 03:24:42 +00:00
|
|
|
// Inserts an entry. Evicts the oldest unused entry if there is no space.
|
2018-08-09 17:08:14 +00:00
|
|
|
void Insert(const TKey &key, const std::shared_ptr<TValue> &value);
|
2017-12-04 03:24:42 +00:00
|
|
|
|
2017-12-22 15:29:13 +00:00
|
|
|
// Call |func| on existing entries. If |func| returns false iteration
|
|
|
|
// temrinates early.
|
2018-08-09 17:08:14 +00:00
|
|
|
template <typename TFunc> void IterateValues(TFunc func);
|
2017-12-22 15:29:13 +00:00
|
|
|
|
2018-03-20 12:33:02 +00:00
|
|
|
// Empties the cache
|
|
|
|
void Clear(void);
|
|
|
|
|
2018-08-09 17:08:14 +00:00
|
|
|
private:
|
2017-12-04 03:24:42 +00:00
|
|
|
// 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;
|
2018-08-09 17:08:14 +00:00
|
|
|
bool operator<(const Entry &other) const { return score < other.score; }
|
2017-12-04 03:24:42 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
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>
|
2018-08-09 17:08:14 +00:00
|
|
|
std::shared_ptr<TValue> LruCache<TKey, TValue>::Get(const TKey &key,
|
2017-12-05 16:24:37 +00:00
|
|
|
TAllocator allocator) {
|
2017-12-04 03:24:42 +00:00
|
|
|
std::shared_ptr<TValue> result = TryGet(key);
|
|
|
|
if (!result)
|
|
|
|
Insert(key, result = allocator());
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename TKey, typename TValue>
|
2018-08-09 17:08:14 +00:00
|
|
|
std::shared_ptr<TValue> LruCache<TKey, TValue>::TryGet(const TKey &key) {
|
2017-12-04 03:24:42 +00:00
|
|
|
// Assign new score.
|
2018-08-09 17:08:14 +00:00
|
|
|
for (Entry &entry : entries_) {
|
2017-12-04 03:24:42 +00:00
|
|
|
if (entry.key == key) {
|
|
|
|
entry.score = next_score_;
|
2017-12-28 07:27:42 +00:00
|
|
|
IncrementScore();
|
2017-12-04 03:24:42 +00:00
|
|
|
return entry.value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
template <typename TKey, typename TValue>
|
2018-08-09 17:08:14 +00:00
|
|
|
std::shared_ptr<TValue> LruCache<TKey, TValue>::TryTake(const TKey &key) {
|
2017-12-04 03:24:42 +00:00
|
|
|
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>
|
2018-08-09 17:08:14 +00:00
|
|
|
void LruCache<TKey, TValue>::Insert(const TKey &key,
|
|
|
|
const std::shared_ptr<TValue> &value) {
|
2018-01-31 05:55:53 +00:00
|
|
|
if ((int)entries_.size() >= max_entries_)
|
2017-12-28 07:27:42 +00:00
|
|
|
entries_.erase(std::min_element(entries_.begin(), entries_.end()));
|
2017-12-04 03:24:42 +00:00
|
|
|
|
|
|
|
Entry entry;
|
|
|
|
entry.score = next_score_;
|
2017-12-28 07:27:42 +00:00
|
|
|
IncrementScore();
|
2017-12-04 03:24:42 +00:00
|
|
|
entry.key = key;
|
|
|
|
entry.value = value;
|
|
|
|
entries_.push_back(entry);
|
|
|
|
}
|
|
|
|
|
2017-12-22 15:29:13 +00:00
|
|
|
template <typename TKey, typename TValue>
|
|
|
|
template <typename TFunc>
|
|
|
|
void LruCache<TKey, TValue>::IterateValues(TFunc func) {
|
2018-08-09 17:08:14 +00:00
|
|
|
for (Entry &entry : entries_) {
|
2017-12-22 15:29:13 +00:00
|
|
|
if (!func(entry.value))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-04 03:24:42 +00:00
|
|
|
template <typename TKey, typename TValue>
|
|
|
|
void LruCache<TKey, TValue>::IncrementScore() {
|
|
|
|
// Overflow.
|
2017-12-28 07:27:42 +00:00
|
|
|
if (++next_score_ == 0) {
|
|
|
|
std::sort(entries_.begin(), entries_.end());
|
2018-08-09 17:08:14 +00:00
|
|
|
for (Entry &entry : entries_)
|
2017-12-04 03:24:42 +00:00
|
|
|
entry.score = next_score_++;
|
|
|
|
}
|
2017-12-04 22:45:36 +00:00
|
|
|
}
|
2018-03-20 12:33:02 +00:00
|
|
|
|
|
|
|
template <typename TKey, typename TValue>
|
|
|
|
void LruCache<TKey, TValue>::Clear(void) {
|
2018-08-09 17:08:14 +00:00
|
|
|
entries_.clear();
|
|
|
|
next_score_ = 0;
|
2018-03-20 12:33:02 +00:00
|
|
|
}
|