using System.Collections.Concurrent; using System.Collections.Immutable; using Connected.Interop; namespace Connected.Caching; internal class Entries { private readonly Lazy> _items = new(); private ConcurrentDictionary Items => _items.Value; public ImmutableList Keys => Items.Keys.ToImmutableList(); public int Count => Items.Count; public bool Any() { return Items.Any(); } public void Scave() { var expired = new HashSet(); 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 All() { var r = new List(); 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 GetEnumerator() { return new EntryEnumerator(Items); } public IEntry? Get(string key) { return Find(key); } public IEntry? First() { if (!Any()) return default; return Items.First().Value; } public IEntry? Get(Func predicate) { return Find(predicate); } public IEntry? Get(Func predicate) { return Find(predicate); } public ImmutableList? Remove(Func predicate) { if (Where(predicate) is not ImmutableList ds || ds.IsEmpty) return default; var result = new HashSet(); 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? Where(Func predicate) { var values = Items.Select(f => f.Value.Instance).Cast(); 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(); 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(Func predicate) { var instances = Items.Select(f => f.Value?.Instance).Cast(); 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(Func predicate) { var instances = Items.Select(f => f.Value?.Instance).Cast(); 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); } }