163 lines
4.5 KiB
C++
163 lines
4.5 KiB
C++
#pragma once
|
|
|
|
/*
|
|
LRU (Least Recently Used) HashMap that is optimized for embedded devices.
|
|
This hashmap has a maximum size and will automatically evict the least
|
|
recently used items when it reaches capacity.
|
|
*/
|
|
|
|
#include "fl/hash_map.h"
|
|
#include "fl/type_traits.h"
|
|
|
|
namespace fl {
|
|
|
|
template <typename Key, typename T, typename Hash = Hash<Key>,
|
|
typename KeyEqual = EqualTo<Key>,
|
|
int INLINED_COUNT = FASTLED_HASHMAP_INLINED_COUNT>
|
|
class HashMapLru {
|
|
private:
|
|
// Wrapper for values that includes access time tracking
|
|
struct ValueWithTimestamp {
|
|
T value;
|
|
u32 last_access_time;
|
|
|
|
ValueWithTimestamp() : last_access_time(0) {}
|
|
ValueWithTimestamp(const T &v, u32 time)
|
|
: value(v), last_access_time(time) {}
|
|
};
|
|
|
|
public:
|
|
HashMapLru(fl::size max_size) : mMaxSize(max_size), mCurrentTime(0) {
|
|
// Ensure max size is at least 1
|
|
if (mMaxSize < 1)
|
|
mMaxSize = 1;
|
|
}
|
|
|
|
void setMaxSize(fl::size max_size) {
|
|
while (mMaxSize < max_size) {
|
|
// Evict oldest items until we reach the new max size
|
|
evictOldest();
|
|
}
|
|
mMaxSize = max_size;
|
|
}
|
|
|
|
void swap(HashMapLru &other) {
|
|
fl::swap(mMap, other.mMap);
|
|
fl::swap(mMaxSize, other.mMaxSize);
|
|
fl::swap(mCurrentTime, other.mCurrentTime);
|
|
}
|
|
|
|
// Insert or update a key-value pair
|
|
void insert(const Key &key, const T &value) {
|
|
// Only evict if we're at capacity AND this is a new key
|
|
const ValueWithTimestamp *existing = mMap.find_value(key);
|
|
|
|
auto curr = mCurrentTime++;
|
|
|
|
if (existing) {
|
|
// Update the value and access time
|
|
ValueWithTimestamp &vwt =
|
|
const_cast<ValueWithTimestamp &>(*existing);
|
|
vwt.value = value;
|
|
vwt.last_access_time = curr;
|
|
return;
|
|
}
|
|
if (mMap.size() >= mMaxSize) {
|
|
evictOldest();
|
|
}
|
|
|
|
// Insert or update the value with current timestamp
|
|
ValueWithTimestamp vwt(value, mCurrentTime);
|
|
mMap.insert(key, vwt);
|
|
}
|
|
|
|
// Get value for key, returns nullptr if not found
|
|
T *find_value(const Key &key) {
|
|
ValueWithTimestamp *vwt = mMap.find_value(key);
|
|
if (vwt) {
|
|
// Update access time
|
|
auto curr = mCurrentTime++;
|
|
vwt->last_access_time = curr;
|
|
return &vwt->value;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
// Get value for key, returns nullptr if not found (const version)
|
|
const T *find_value(const Key &key) const {
|
|
const ValueWithTimestamp *vwt = mMap.find_value(key);
|
|
return vwt ? &vwt->value : nullptr;
|
|
}
|
|
|
|
// Access operator - creates entry if not exists
|
|
T &operator[](const Key &key) {
|
|
// If we're at capacity and this is a new key, evict oldest
|
|
auto curr = mCurrentTime++;
|
|
|
|
auto entry = mMap.find_value(key);
|
|
if (entry) {
|
|
// Update access time
|
|
entry->last_access_time = curr;
|
|
return entry->value;
|
|
}
|
|
|
|
if (mMap.size() >= mMaxSize) {
|
|
evictOldest();
|
|
}
|
|
|
|
// Get or create entry and update timestamp
|
|
// mCurrentTime++;
|
|
ValueWithTimestamp &vwt = mMap[key];
|
|
vwt.last_access_time = curr;
|
|
return vwt.value;
|
|
}
|
|
|
|
// Remove a key
|
|
bool remove(const Key &key) { return mMap.remove(key); }
|
|
|
|
// Clear the map
|
|
void clear() {
|
|
mMap.clear();
|
|
mCurrentTime = 0;
|
|
}
|
|
|
|
// Size accessors
|
|
fl::size size() const { return mMap.size(); }
|
|
bool empty() const { return mMap.empty(); }
|
|
fl::size capacity() const { return mMaxSize; }
|
|
|
|
private:
|
|
// Evict the least recently used item
|
|
void evictOldest() {
|
|
if (mMap.empty())
|
|
return;
|
|
|
|
// Find the entry with the oldest timestamp
|
|
Key oldest_key;
|
|
u32 oldest_time = UINT32_MAX;
|
|
bool found = false;
|
|
|
|
for (auto it = mMap.begin(); it != mMap.end(); ++it) {
|
|
const auto &entry = *it;
|
|
const ValueWithTimestamp &vwt = entry.second;
|
|
|
|
if (vwt.last_access_time < oldest_time) {
|
|
oldest_time = vwt.last_access_time;
|
|
oldest_key = entry.first;
|
|
found = true;
|
|
}
|
|
}
|
|
|
|
// Remove the oldest entry
|
|
if (found) {
|
|
mMap.remove(oldest_key);
|
|
}
|
|
}
|
|
|
|
HashMap<Key, ValueWithTimestamp, Hash, KeyEqual, INLINED_COUNT> mMap;
|
|
fl::size mMaxSize;
|
|
u32 mCurrentTime;
|
|
};
|
|
|
|
} // namespace fl
|