You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Zero/ZeroLevel/Services/Cache/LRUCache.cs

123 lines
4.0 KiB

using System.Collections.Generic;
namespace ZeroLevel.Services.Cache
{
/// <summary>
/// A least-recently-used cache stored like a dictionary.
/// </summary>
/// <typeparam name="TKey">The type of the key to the cached item.</typeparam>
/// <typeparam name="TValue">The type of the cached item.</typeparam>
public sealed class LRUCache<TKey, TValue>
{
/// <summary>
/// Default maximum number of elements to cache.
/// </summary>
private const int DefaultCapacity = 255;
private readonly object _lockObj = new object();
private readonly int _capacity;
private readonly Dictionary<TKey, Entry> _cacheMap;
private readonly LinkedList<TKey> _cacheList;
/// <summary>
/// Initializes a new instance of the <see cref="LRUCache{TKey, TValue}"/> class.
/// </summary>
public LRUCache()
: this(DefaultCapacity)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="LRUCache{TKey, TValue}"/> class.
/// </summary>
/// <param name="capacity">Maximum number of elements to cache.</param>
public LRUCache(int capacity)
{
_capacity = capacity > 0 ? capacity : DefaultCapacity;
_cacheMap = new Dictionary<TKey, Entry>();
_cacheList = new LinkedList<TKey>();
}
/// <summary>
/// Gets the value associated with the specified key.
/// </summary>
/// <param name="key">The key of the value to get.</param>
/// <param name="value">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 <paramref name="value" /> parameter.</param>
/// <returns>true if contains an element with the specified key; otherwise, false.</returns>
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;
}
}
5 months ago
value = default(TValue)!;
return false;
}
/// <summary>
/// Adds the specified key and value to the cache.
/// </summary>
/// <param name="key">The key of the element to add.</param>
/// <param name="value">The value of the element to add.</param>
public void Set(TKey key, TValue value)
{
lock (_lockObj)
{
if (!_cacheMap.TryGetValue(key, out var entry))
{
LinkedListNode<TKey> node;
if (_cacheMap.Count >= _capacity)
{
node = _cacheList.Last;
_cacheMap.Remove(node.Value);
_cacheList.RemoveLast();
node.Value = key;
}
else
{
node = new LinkedListNode<TKey>(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<TKey> node)
{
if (node != _cacheList.First)
{
_cacheList.Remove(node);
_cacheList.AddFirst(node);
}
}
private struct Entry
{
public LinkedListNode<TKey> Node;
public TValue Value;
public Entry(LinkedListNode<TKey> node, TValue value)
{
this.Node = node;
this.Value = value;
}
}
}
}

Powered by TurnKey Linux.