You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Connected.Framework/Connected.Caching/Entries.cs

260 lines
4.7 KiB

2 years ago
using System.Collections.Concurrent;
using System.Collections.Immutable;
using Connected.Interop;
namespace Connected.Caching;
internal class Entries
{
private readonly Lazy<ConcurrentDictionary<string, IEntry>> _items = new();
private ConcurrentDictionary<string, IEntry> Items => _items.Value;
public ImmutableList<string> Keys => Items.Keys.ToImmutableList();
public int Count => Items.Count;
public bool Any()
{
return Items.Any();
}
public void Scave()
{
var expired = new HashSet<string>();
foreach (var i in Items)
{
var r = i.Value;
if (r is null || r.Expired)
expired.Add(i.Key);
}
foreach (var i in expired)
Remove(i);
}
public ImmutableList<T> All<T>()
{
var r = new List<T>();
var expired = Items.Where(f => f.Value.Expired);
foreach (var e in expired)
Remove(e.Value.Id);
var instances = Items.Select(f => f.Value.Instance);
foreach (var i in instances)
{
if (TypeConversion.TryConvert(i, out T? result) && result is not null)
r.Add(result);
}
return r.ToImmutableList();
}
public void Remove(string key)
{
if (Items.IsEmpty)
return;
if (Items.TryRemove(key, out IEntry? v))
v.Dispose();
}
public void Set(string key, object? instance, TimeSpan duration, bool slidingExpiration)
{
Items[key] = new Entry(key, instance, duration, slidingExpiration);
}
public IEnumerator<T> GetEnumerator<T>()
{
return new EntryEnumerator<T>(Items);
}
public IEntry? Get(string key)
{
return Find(key);
}
public IEntry? First()
{
if (!Any())
return default;
return Items.First().Value;
}
public IEntry? Get<T>(Func<T, bool> predicate)
{
return Find(predicate);
}
public IEntry? Get<T>(Func<dynamic, bool> predicate)
{
return Find<T>(predicate);
}
public ImmutableList<string>? Remove<T>(Func<T, bool> predicate)
{
if (Where(predicate) is not ImmutableList<string> ds || ds.IsEmpty)
return default;
var result = new HashSet<string>();
foreach (var i in ds)
{
var key = Items.FirstOrDefault(f => InstanceEquals(f.Value?.Instance, i)).Key;
RemoveInternal(key);
result.Add(key);
}
return result.ToImmutableList();
}
public ImmutableList<T>? Where<T>(Func<T, bool> predicate)
{
var values = Items.Select(f => f.Value.Instance).Cast<T>();
if (values is null || !values.Any())
return default;
var filtered = values.Where(predicate);
if (filtered is null || !filtered.Any())
return default;
var r = new List<T>();
foreach (var i in filtered)
{
var ce = Items.FirstOrDefault(f => InstanceEquals(f.Value?.Instance, i));
if (ce.Value is null)
continue;
if (ce.Value.Expired)
{
RemoveInternal(ce.Value.Id);
continue;
}
ce.Value.Hit();
r.Add(i);
}
return r.ToImmutableList();
}
private void RemoveInternal(string key)
{
if (Items.TryRemove(key, out IEntry? d))
d.Dispose();
}
private IEntry? Find<T>(Func<T, bool> predicate)
{
var instances = Items.Select(f => f.Value?.Instance).Cast<T>();
if (instances is null || !instances.Any())
return default;
if (instances.FirstOrDefault(predicate) is not T instance)
return default;
var ce = Items.Values.FirstOrDefault(f => InstanceEquals(f.Instance, instance));
if (ce is null)
return default;
if (ce.Expired)
{
RemoveInternal(ce.Id);
return default;
}
ce.Hit();
return ce;
}
private IEntry? Find<T>(Func<dynamic, bool> predicate)
{
var instances = Items.Select(f => f.Value?.Instance).Cast<dynamic>();
if (instances is null || !instances.Any())
return default;
if (instances.FirstOrDefault(predicate) is not T instance)
return default;
if (Items.Values.FirstOrDefault(f => InstanceEquals(f.Instance, instance)) is not IEntry ce)
return default;
if (ce.Expired)
{
RemoveInternal(ce.Id);
return default;
}
ce.Hit();
return ce;
}
private IEntry? Find(string key)
{
if (!Items.ContainsKey(key))
return default;
if (Items.TryGetValue(key, out IEntry? d))
{
if (d.Expired)
{
RemoveInternal(key);
return default;
}
d.Hit();
return d;
}
else
{
RemoveInternal(key);
return default;
}
}
public bool Exists(string key)
{
return Find(key) is not null;
}
public void Clear()
{
foreach (var i in Items)
Remove(i.Key);
}
private static bool InstanceEquals(object? left, object? right)
{
/*
* TODO: implement IEquality check
*/
if (left is null || right is null)
return false;
if (left.GetType().IsPrimitive)
return left == right;
if (left is string && right is string)
return string.Compare(left.ToString(), right.ToString(), false) == 0;
if (left.GetType().IsValueType && right.GetType().IsValueType)
return left.Equals(right);
return ReferenceEqualityComparer.Instance.Equals(left, right);
}
}