namespace o3tl
{ namespace detail
{ // Helper base class to keep total cost for lru_map with custom item size. // Custom size is specified by the ValueSize functor, the default of each // item counting as 1 is specified using the void type. template <class ValueSize> class lru_map_base
{ public: // Returns total of ValueSize for all items.
size_t total_size() const { return mCurrentSize; }
protected:
size_t mCurrentSize = 0; // sum of ValueSize for all items
};
// By default cost of each item is 1, so it doesn't need to be tracked. template <> class lru_map_base<void>
{
};
} // namespace
/** LRU map * *Similartounordered_map(itactuallyusesit)withadditionallyfunctionality *whichremovestheentriesthathavebeen"leastrecentlyused"whenthesize *hitsthespecifiedcapacity. * *ItonlyimplementstheminimalmethodsneededandtheimplementationisNOT *threadsafe. * *TheimplementationisassimpleaspossiblebutitstillusesO(1)complexity *formostoftheoperationswithacombinationunorderedmapandlinkedlist. * *ItisoptionallypossibletospecifyafunctionforValueSizetemplate *argument(thatcanbecalledas'size_tfunc(Value)')thatwillreturn *asize(cost)foraniteminsteadofthedefaultsizeof1foreachitem. *Thesizeofanitemmustnotchangeforanitem(ifneeded,re-insert *theitem).Anewlyinserteditemisguaranteedtobeinthecontainer, *evenifitssizeexceedsthemaximumsize. *
**/ template <typename Key, typename Value, class KeyHash = std::hash<Key>, class KeyEqual = std::equal_to<Key>, class ValueSize = void> class lru_map final : public detail::lru_map_base<ValueSize>
{ public: typedeftypename std::pair<Key, Value> key_value_pair_t;
void addSize(const Value& value)
{ // by default total size is equal to number of items if constexpr (!std::is_void_v<ValueSize>) this->mCurrentSize += ValueSize()(value);
}
void removeSize(const Value& value)
{ // by default total size is equal to number of items if constexpr (!std::is_void_v<ValueSize>)
{
size_t itemSize = ValueSize()(value);
assert(itemSize <= this->mCurrentSize); this->mCurrentSize -= itemSize;
}
}
void removeOldestItem()
{
removeSize(mLruList.back().second); // remove from map
mLruMap.erase(mLruList.back().first); // remove from list
mLruList.pop_back();
}
void checkLRUItemInsert()
{ if constexpr (std::is_void_v<ValueSize>)
{ // One added, so it's enough to remove one, if needed. if (mLruMap.size() > mMaxSize)
removeOldestItem();
} else
{ // This must leave at least one item (it's called from insert). while (this->mCurrentSize > mMaxSize && mLruMap.size() > 1)
removeOldestItem();
}
}
void checkLRUItemUpdate()
{ // Item update does not change total size by default. if constexpr (!std::is_void_v<ValueSize>)
{ // This must leave at least one item (it's called from insert). while (this->mCurrentSize > mMaxSize && mLruMap.size() > 1)
removeOldestItem();
}
}
void checkLRUMaxSize()
{ if constexpr (std::is_void_v<ValueSize>)
{ while (mLruMap.size() > mMaxSize)
removeOldestItem();
} else
{ while (this->mCurrentSize > mMaxSize)
removeOldestItem();
}
}
lru_map(size_t nMaxSize)
: mMaxSize(nMaxSize)
{
assert(mMaxSize > 0);
}
~lru_map()
{
clearSize(); // Some code .e.g. SalBitmap likes to remove itself from a cache during it's destructor, which means we // get calls into lru_map while we are in destruction, so use the swap-and-clear idiom to avoid those problems.
mLruMap.clear();
list_t().swap(mLruList);
}
void insert(key_value_pair_t& rPair)
{
map_iterator_t i = mLruMap.find(rPair.first);
if (i == mLruMap.end()) // doesn't exist -> add to queue and map
{
addSize(rPair.second); // add to front of the list
mLruList.push_front(rPair); // add the list position (iterator) to the map auto it = mLruList.begin();
mLruMap[it->first] = it;
checkLRUItemInsert();
} else// already exists -> replace value
{ // update total cost
removeSize(i->second->second);
addSize(rPair.second); // replace value
i->second->second = rPair.second; // bring to front of the lru list
mLruList.splice(mLruList.begin(), mLruList, i->second);
checkLRUItemUpdate();
}
}
void insert(key_value_pair_t&& rPair)
{
map_iterator_t i = mLruMap.find(rPair.first);
if (i == mLruMap.end()) // doesn't exist -> add to list and map
{
addSize(rPair.second); // add to front of the list
mLruList.push_front(std::move(rPair)); // add the list position (iterator) to the map auto it = mLruList.begin();
mLruMap[it->first] = it;
checkLRUItemInsert();
} else// already exists -> replace value
{
removeSize(i->second->second);
addSize(rPair.second); // replace value
i->second->second = std::move(rPair.second); // push to back of the lru list
mLruList.splice(mLruList.begin(), mLruList, i->second);
checkLRUItemUpdate();
}
}
list_const_iterator_t find(const Key& key)
{ const map_iterator_t i = mLruMap.find(key); if (i == mLruMap.cend()) // can't find entry for the key
{ // return empty iterator return mLruList.cend();
} else
{ // push to back of the lru list
mLruList.splice(mLruList.begin(), mLruList, i->second); return i->second;
}
}
// reverse-iterates the list removing all items matching the predicate template <class UnaryPredicate> void remove_if(UnaryPredicate pred)
{ auto it = mLruList.rbegin(); while (it != mLruList.rend())
{ if (pred(*it))
{
removeSize(it->second);
mLruMap.erase(it->first);
it = decltype(it){ mLruList.erase(std::next(it).base()) };
} else
++it;
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.