// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschränkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== using System; using System.Collections.Generic; namespace Squidex.Infrastructure.Caching { public sealed class LRUCache { private readonly Dictionary>> cacheMap = new Dictionary>>(); private readonly LinkedList> cacheHistory = new LinkedList>(); private readonly int capacity; private readonly Action itemEvicted; public LRUCache(int capacity, Action itemEvicted = null) { Guard.GreaterThan(capacity, 0, nameof(capacity)); this.capacity = capacity; this.itemEvicted = itemEvicted ?? new Action((key, value) => { }); } public bool Set(TKey key, TValue value) { if (cacheMap.TryGetValue(key, out var node)) { node.Value.Value = value; cacheHistory.Remove(node); cacheHistory.AddLast(node); cacheMap[key] = node; return true; } if (cacheMap.Count >= capacity) { RemoveFirst(); } var cacheItem = new LRUCacheItem { Key = key, Value = value }; node = new LinkedListNode>(cacheItem); cacheMap.Add(key, node); cacheHistory.AddLast(node); return false; } public bool Remove(TKey key) { if (cacheMap.TryGetValue(key, out var node)) { cacheMap.Remove(key); cacheHistory.Remove(node); return true; } return false; } public bool TryGetValue(TKey key, out object value) { value = null; if (cacheMap.TryGetValue(key, out var node)) { value = node.Value.Value; cacheHistory.Remove(node); cacheHistory.AddLast(node); return true; } return false; } public bool Contains(TKey key) { return cacheMap.ContainsKey(key); } private void RemoveFirst() { var node = cacheHistory.First; itemEvicted(node.Value.Key, node.Value.Value); cacheMap.Remove(node.Value.Key); cacheHistory.RemoveFirst(); } } }