using System; using System.Collections.Generic; namespace ln.collections { public delegate bool CacheMayForget(ExtendedCache cache,V item); public class ExtendedCache { public event CacheMayForget OnCacheMayForget; private MappingBTree> cacheItems = new MappingBTree>((i) => i.Key); private BTreeValueSet> cacheAges = new BTreeValueSet>(); private int _targetSize = 4096; public int TargetSize { get => _targetSize; set { _targetSize = value; if (Count > _targetSize) Invalidate(); } } public int Count => cacheItems.Count; public ExtendedCache() { } public ExtendedCache(int targetSize) : this() { TargetSize = targetSize; } public void Clear() { cacheItems.Clear(); cacheAges.Clear(); } public void Invalidate() { if (Count <= _targetSize) return; foreach (ExtendedCache.ExtendedCacheItem eci in cacheAges.Values) { if (MayForget(eci.Value)) Invalidate(eci); if (Count <= _targetSize) break; } } private bool MayForget(V item) { foreach (CacheMayForget mayForget in OnCacheMayForget.GetInvocationList() ?? new CacheMayForget[0]) { if (!mayForget(this, item)) return false; } return true; } public V this[K key] { get { if (TryGet(key, out V value)) return value; throw new KeyNotFoundException(); } } public bool TryGet(K key, out V value) { if (cacheItems.TryGet(key, out ExtendedCacheItem eci)) { value = eci.Value; return true; } value = default(V); return false; } private void UpdateCacheItem(ExtendedCacheItem eci) { cacheAges.Remove(eci.Timestamp, eci); eci.Timestamp = DateTime.Now; cacheAges.Add(eci.Timestamp, eci); } public bool Ensure(K key, V value) { if (cacheItems.TryGet(key, out ExtendedCacheItem eci)) { if (!Object.ReferenceEquals(eci.Value, value)) return false; UpdateCacheItem(eci); return true; } else { eci = new ExtendedCacheItem(key, value); cacheItems.Add(eci); cacheAges.Add(eci.Timestamp, eci); return true; } } public bool Invalidate(K key) { if (cacheItems.TryGet(key, out ExtendedCacheItem eci)) { Invalidate(eci); return true; } return false; } public void Invalidate(ExtendedCacheItem eci) { cacheAges.Remove(eci.Timestamp, eci); cacheItems.RemoveKey(eci.Key); } public class ExtendedCacheItem { public DateTime Timestamp; public TK Key; public TV Value; public ExtendedCacheItem(TK key, TV value) { Key = key; Value = value; Timestamp = DateTime.Now; } } } }