Pangamma

LruCache - dotnet10 - c#

Jan 25th, 2026 (edited)
2,975
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 3.59 KB | Source Code | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Xml.Linq;
  4.  
  5. namespace DiscordTranslator.Bot.Logic
  6. {
  7.     /// <summary>
  8.     /// A Least Recently Used cache with optional forced expiry/eviction.
  9.     /// </summary>
  10.     public class LruCache<K, V> where K : notnull where V : notnull
  11.     {
  12.         private readonly int _capacity;
  13.         private readonly Dictionary<K, LinkedListNode<LRUCacheItem>> _map = [];
  14.         private readonly LinkedList<LRUCacheItem> _lru = new();
  15.         private readonly Lock _sync = new();
  16.  
  17.         public int Count
  18.         {
  19.             get { lock (_sync) { return _lru.Count; } }
  20.         }
  21.  
  22.         public LruCache(int capacity)
  23.         {
  24.             if (capacity <= 0) throw new ArgumentOutOfRangeException(nameof(capacity), "Capacity must be positive.");
  25.             _capacity = capacity;
  26.         }
  27.  
  28.         /// <summary>
  29.         /// Try to get a value by key and cast it to T (where T : V).
  30.         /// Returns true and sets 'val' when found and castable; otherwise false and val = default.
  31.         /// </summary>
  32.         public bool TryGetValue<T>(K key, out T? val) where T : V
  33.         {
  34.             lock (_sync)
  35.             {
  36.                 if (_map.TryGetValue(key, out var node))
  37.                 {
  38.                     // Move to back (most recently used)
  39.                     _lru.Remove(node);
  40.                     _lru.AddLast(node);
  41.  
  42.                     if (node.Value.Value is T t)
  43.                     {
  44.                         val = t;
  45.                         return true;
  46.                     }
  47.                 }
  48.  
  49.                 val = default;
  50.                 return false;
  51.             }
  52.         }
  53.  
  54.         public void Set(K key, V value)
  55.         {
  56.             lock (_sync)
  57.             {
  58.                 if (_map.TryGetValue(key, out var existing))
  59.                 {
  60.                     existing.Value = existing.Value with { Value = value };
  61.  
  62.                     // Refresh LRU position
  63.                     _lru.Remove(existing);
  64.                     _lru.AddLast(existing);
  65.                 }
  66.                 else
  67.                 {
  68.                     if (_map.Count >= _capacity)
  69.                         RemoveFirst(); // evict LRU
  70.  
  71.                     var item = new LRUCacheItem(key, value);
  72.                     var node = new LinkedListNode<LRUCacheItem>(item);
  73.                     _lru.AddLast(node);
  74.                     _map[key] = node;
  75.                 }
  76.             }
  77.         }
  78.  
  79.         public void Remove(K key)
  80.         {
  81.             lock (_sync)
  82.             {
  83.                 if (_map.TryGetValue(key, out var node))
  84.                 {
  85.                     _lru.Remove(node);
  86.                     _map.Remove(key);
  87.                     if (node.Value is IDisposable dispVal)
  88.                     {
  89.                         dispVal.Dispose();
  90.                     }
  91.                 }
  92.             }
  93.         }
  94.  
  95.         private void RemoveFirst()
  96.         {
  97.             // Evict least recently used (front)
  98.             if (_lru.First is { } first)
  99.             {
  100.                 _lru.RemoveFirst();
  101.                 // Remove from map using the evicted key
  102.                 _map.Remove(first.Value.Key);
  103.  
  104.                 if (first.Value is IDisposable dispVal)
  105.                 {
  106.                     dispVal.Dispose();
  107.                 }
  108.             }
  109.             // If list was empty, nothing to evict (shouldn't happen with capacity checks)
  110.         }
  111.  
  112.         internal sealed record LRUCacheItem(K Key, V Value)
  113.         {
  114.             public override string ToString() => $"{Key} => {Value}";
  115.         }
  116.     }
  117.  
  118. }
Tags: lrucache
Advertisement