149 lines
3.9 KiB
C#
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;
|
|
}
|
|
}
|
|
}
|
|
} |