ln.collections/ln.collections/ExtendedCache.cs

149 lines
3.9 KiB
C#

using System;
using System.Collections.Generic;
namespace ln.collections
{
public delegate bool CacheMayForget<K,V>(ExtendedCache<K,V> cache,V item);
public class ExtendedCache<K,V>
{
public event CacheMayForget<K, V> OnCacheMayForget;
private MappingBTree<K, ExtendedCacheItem<K, V>> cacheItems =
new MappingBTree<K, ExtendedCacheItem<K, V>>((i) => i.Key);
private BTreeValueSet<DateTime, ExtendedCacheItem<K, V>> cacheAges =
new BTreeValueSet<DateTime, ExtendedCacheItem<K, V>>();
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<K,V>.ExtendedCacheItem<K,V> eci in cacheAges.Values)
{
if (MayForget(eci.Value))
Invalidate(eci);
if (Count <= _targetSize)
break;
}
}
private bool MayForget(V item)
{
foreach (CacheMayForget<K,V> mayForget in OnCacheMayForget.GetInvocationList() ?? new CacheMayForget<K, V>[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<K, V> eci))
{
value = eci.Value;
return true;
}
value = default(V);
return false;
}
private void UpdateCacheItem(ExtendedCacheItem<K, V> 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<K, V> eci))
{
if (!Object.ReferenceEquals(eci.Value, value))
return false;
UpdateCacheItem(eci);
return true;
}
else
{
eci = new ExtendedCacheItem<K, V>(key, value);
cacheItems.Add(eci);
cacheAges.Add(eci.Timestamp, eci);
return true;
}
}
public bool Invalidate(K key)
{
if (cacheItems.TryGet(key, out ExtendedCacheItem<K, V> eci))
{
Invalidate(eci);
return true;
}
return false;
}
public void Invalidate(ExtendedCacheItem<K, V> eci)
{
cacheAges.Remove(eci.Timestamp, eci);
cacheItems.RemoveKey(eci.Key);
}
public class ExtendedCacheItem<TK, TV>
{
public DateTime Timestamp;
public TK Key;
public TV Value;
public ExtendedCacheItem(TK key, TV value)
{
Key = key;
Value = value;
Timestamp = DateTime.Now;
}
}
}
}