haiku/headers/private/shared/LRUCache.h
Andrew Lindesay d75b4d610d HaikuDepot: Remove Custom List
Further removal of the use of custom list class;
this time with the package lists.

Relates To #15534
Change-Id: I1f01ed9d5ddbd7754097ce0adbf505d6ba17fd2f
Reviewed-on: https://review.haiku-os.org/c/haiku/+/3732
Reviewed-by: Adrien Destugues <pulkomandy@gmail.com>
2021-02-06 19:45:53 +00:00

210 lines
3.7 KiB
C++

/*
* Copyright 2020-2021, Andrew Lindesay <apl@lindesay.co.nz>.
* All rights reserved. Distributed under the terms of the MIT License.
*/
#ifndef LRU_CACHE_H
#define LRU_CACHE_H
#include <HashMap.h>
namespace BPrivate {
template<typename Key, typename Value>
class LRUOrderingNode {
private:
typedef LRUOrderingNode<Key, Value> LRUNode;
public:
LRUOrderingNode()
:
fKey(),
fValue(),
fOlder(NULL),
fNewer(NULL)
{
}
LRUOrderingNode(const Key& key, const Value& value)
:
fKey(key),
fValue(value),
fOlder(NULL),
fNewer(NULL)
{
}
Key fKey;
Value fValue;
LRUNode* fOlder;
LRUNode* fNewer;
};
/*! \brief This is a hash map that maintains a limited number of entries. Once
this number of entries has been exceeded then it will start to discard
entries. The first entries to be discarded are the ones that are the least
recently used; hence the prefix "LRU".
*/
template<typename Key, typename Value>
class LRUCache {
public:
typedef LRUOrderingNode<Key, Value> LRUNode;
LRUCache(int32 limit)
:
fNewestNode(NULL),
fOldestNode(NULL),
fLimit(limit)
{
if (fLimit < 0)
fLimit = 0;
}
~LRUCache()
{
Clear();
}
status_t InitCheck() const
{
return fMap.InitCheck();
}
status_t Put(const Key& key, const Value& value)
{
LRUNode* node = fMap.Get(key);
if (node != NULL) {
if (node->fValue != value) {
node->fValue = value;
_DisconnectNodeAndMakeNewest(node);
}
} else {
node = new(std::nothrow) LRUNode(key, value);
if (node == NULL)
return B_NO_MEMORY;
status_t result = fMap.Put(key, node);
if (result != B_OK) {
delete node;
return result;
}
_SetNewestNode(node);
_PurgeExcess();
}
return B_OK;
}
Value Remove(const Key& key)
{
LRUNode* node = fMap.Get(key);
if (node != NULL) {
_DisconnectNode(node);
Value result = node->fValue;
fMap.Remove(key);
delete node;
return result;
}
return Value();
}
void Clear()
{
fMap.Clear();
// this will delete the objects in map which are the nodes in the
// linked list; for this reason don't delete them here as well
// because this will lead to a double-free of the object.
fNewestNode = NULL;
fOldestNode = NULL;
}
Value Get(const Key& key)
{
LRUNode* node = fMap.Get(key);
if (node != NULL) {
_DisconnectNodeAndMakeNewest(node);
return node->fValue;
}
return Value();
}
bool ContainsKey(const Key& key) const
{
return fMap.ContainsKey(key);
}
int32 Size() const
{
return fMap.Size();
}
private:
void _DisconnectNodeAndMakeNewest(LRUNode* node) {
if (node != fNewestNode) {
_DisconnectNode(node);
node->fOlder = NULL;
node->fNewer = NULL;
_SetNewestNode(node);
}
}
void _DisconnectNode(LRUNode* node)
{
LRUNode *older = node->fOlder;
LRUNode *newer = node->fNewer;
if (newer != NULL)
newer->fOlder = older;
if (older != NULL)
older->fNewer = newer;
if (fNewestNode == node)
fNewestNode = older;
if (fOldestNode == node)
fOldestNode = newer;
}
void _SetNewestNode(LRUNode* node)
{
if (node != fNewestNode) {
node->fOlder = fNewestNode;
node->fNewer = NULL;
if (fNewestNode != NULL)
fNewestNode->fNewer = node;
fNewestNode = node;
if (fOldestNode == NULL)
fOldestNode = node;
}
}
void _PurgeOldestNode()
{
if (fOldestNode == NULL)
debugger("attempt to purge oldest node but there is none to purge");
Remove(fOldestNode->fKey);
}
void _PurgeExcess()
{
while(Size() > fLimit)
_PurgeOldestNode();
}
protected:
HashMap<Key, LRUNode*> fMap;
LRUNode* fNewestNode;
LRUNode* fOldestNode;
private:
int32 fLimit;
};
}; // namespace BPrivate
using BPrivate::LRUCache;
#endif // LRU_CACHE_H