From 2f31a6cd06afdfa7619523a67e44d12c7f87a8e4 Mon Sep 17 00:00:00 2001 From: Harald Wolff-Thobaben Date: Sat, 17 Jul 2021 20:42:01 +0200 Subject: [PATCH] Added ExtendedCache --- ln.collections/ExtendedCache.cs | 149 ++++++++++++++++++++++++++++++++ 1 file changed, 149 insertions(+) create mode 100644 ln.collections/ExtendedCache.cs diff --git a/ln.collections/ExtendedCache.cs b/ln.collections/ExtendedCache.cs new file mode 100644 index 0000000..e14ecee --- /dev/null +++ b/ln.collections/ExtendedCache.cs @@ -0,0 +1,149 @@ +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; + } + } + } +} \ No newline at end of file