124 lines
3.8 KiB
C++
124 lines
3.8 KiB
C++
/*
|
|
* Copyright (C) 2021 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.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <list>
|
|
#include <sstream>
|
|
#include <unordered_map>
|
|
|
|
namespace android::mediametrics {
|
|
|
|
/**
|
|
* LruSet keeps a set of the last "Size" elements added or accessed.
|
|
*
|
|
* (Lru stands for least-recently-used eviction policy).
|
|
*
|
|
* Runs in O(1) time for add, remove, and check. Internally implemented
|
|
* with an unordered_map and a list. In order to remove elements,
|
|
* a list iterator is stored in the unordered_map
|
|
* (noting that std::list::erase() contractually
|
|
* does not affect iterators other than the one erased).
|
|
*/
|
|
|
|
template <typename T>
|
|
class LruSet {
|
|
const size_t mMaxSize;
|
|
std::list<T> mAccessOrder; // front is the most recent, back is the oldest.
|
|
// item T with its access order iterator.
|
|
std::unordered_map<T, typename std::list<T>::iterator> mMap;
|
|
|
|
public:
|
|
/**
|
|
* Constructs a LruSet which checks whether the element was
|
|
* accessed or added recently.
|
|
*
|
|
* The parameter maxSize is used to cap growth of LruSet;
|
|
* eviction is based on least recently used LRU.
|
|
* If maxSize is zero, the LruSet contains no elements
|
|
* and check() always returns false.
|
|
*
|
|
* \param maxSize the maximum number of elements that are tracked.
|
|
*/
|
|
explicit LruSet(size_t maxSize) : mMaxSize(maxSize) {}
|
|
|
|
/**
|
|
* Returns the number of entries in the LruSet.
|
|
*
|
|
* This is a number between 0 and maxSize.
|
|
*/
|
|
size_t size() const {
|
|
return mMap.size();
|
|
}
|
|
|
|
/** Clears the container contents. */
|
|
void clear() {
|
|
mMap.clear();
|
|
mAccessOrder.clear();
|
|
}
|
|
|
|
/** Returns a string dump of the last n entries. */
|
|
std::string dump(size_t n) const {
|
|
std::stringstream ss;
|
|
auto it = mAccessOrder.cbegin();
|
|
for (size_t i = 0; i < n && it != mAccessOrder.cend(); ++i) {
|
|
ss << *it++ << "\n";
|
|
}
|
|
return ss.str();
|
|
}
|
|
|
|
/** Adds a new item to the set. */
|
|
void add(const T& t) {
|
|
if (mMaxSize == 0) return;
|
|
auto it = mMap.find(t);
|
|
if (it != mMap.end()) { // already exists.
|
|
mAccessOrder.erase(it->second); // move-to-front on the chronologically ordered list.
|
|
} else if (mAccessOrder.size() >= mMaxSize) {
|
|
const T last = mAccessOrder.back();
|
|
mAccessOrder.pop_back();
|
|
mMap.erase(last);
|
|
}
|
|
mAccessOrder.push_front(t);
|
|
mMap[t] = mAccessOrder.begin();
|
|
}
|
|
|
|
/**
|
|
* Removes an item from the set.
|
|
*
|
|
* \param t item to be removed.
|
|
* \return false if the item doesn't exist.
|
|
*/
|
|
bool remove(const T& t) {
|
|
auto it = mMap.find(t);
|
|
if (it == mMap.end()) return false;
|
|
mAccessOrder.erase(it->second);
|
|
mMap.erase(it);
|
|
return true;
|
|
}
|
|
|
|
/** Returns true if t is present (and moves the access order of t to the front). */
|
|
bool check(const T& t) { // not const, as it adjusts the least-recently-used order.
|
|
auto it = mMap.find(t);
|
|
if (it == mMap.end()) return false;
|
|
mAccessOrder.erase(it->second);
|
|
mAccessOrder.push_front(it->first);
|
|
it->second = mAccessOrder.begin();
|
|
return true;
|
|
}
|
|
};
|
|
|
|
} // namespace android::mediametrics
|