using System.Collections.Generic;
namespace ZeroLevel.Services.Cache
{
///
/// A least-recently-used cache stored like a dictionary.
///
/// The type of the key to the cached item.
/// The type of the cached item.
public sealed class LRUCache
{
///
/// Default maximum number of elements to cache.
///
private const int DefaultCapacity = 255;
private readonly object _lockObj = new object();
private readonly int _capacity;
private readonly Dictionary _cacheMap;
private readonly LinkedList _cacheList;
///
/// Initializes a new instance of the class.
///
public LRUCache()
: this(DefaultCapacity)
{
}
///
/// Initializes a new instance of the class.
///
/// Maximum number of elements to cache.
public LRUCache(int capacity)
{
_capacity = capacity > 0 ? capacity : DefaultCapacity;
_cacheMap = new Dictionary();
_cacheList = new LinkedList();
}
///
/// Gets the value associated with the specified key.
///
/// The key of the value to get.
/// When this method returns, contains the value associated with
/// the specified key, if the key is found; otherwise, the default value for the
/// type of the parameter.
/// true if contains an element with the specified key; otherwise, false.
public bool TryGet(TKey key, out TValue value)
{
lock (_lockObj)
{
if (_cacheMap.TryGetValue(key, out var entry))
{
Touch(entry.Node);
value = entry.Value;
return true;
}
}
value = default(TValue)!;
return false;
}
///
/// Adds the specified key and value to the cache.
///
/// The key of the element to add.
/// The value of the element to add.
public void Set(TKey key, TValue value)
{
lock (_lockObj)
{
if (!_cacheMap.TryGetValue(key, out var entry))
{
LinkedListNode node;
if (_cacheMap.Count >= _capacity)
{
node = _cacheList.Last;
_cacheMap.Remove(node.Value);
_cacheList.RemoveLast();
node.Value = key;
}
else
{
node = new LinkedListNode(key);
}
_cacheList.AddFirst(node);
_cacheMap.Add(key, new Entry(node, value));
}
else
{
entry.Value = value;
_cacheMap[key] = entry;
Touch(entry.Node);
}
}
}
private void Touch(LinkedListNode node)
{
if (node != _cacheList.First)
{
_cacheList.Remove(node);
_cacheList.AddFirst(node);
}
}
private struct Entry
{
public LinkedListNode Node;
public TValue Value;
public Entry(LinkedListNode node, TValue value)
{
this.Node = node;
this.Value = value;
}
}
}
}