diff --git a/Dashboard.Common/Collections/TypeAtom.cs b/Dashboard.Common/Collections/TypeAtom.cs new file mode 100644 index 0000000..446b792 --- /dev/null +++ b/Dashboard.Common/Collections/TypeAtom.cs @@ -0,0 +1,95 @@ +using System.Collections.Concurrent; +using System.Collections.Immutable; + +namespace Dashboard.Collections +{ + /// + /// Helper class for better type access performance. + /// + public record TypeAtom + { + + public Type Type { get; } + public int Id { get; } + public ImmutableHashSet Ancestors { get; } + + // Makes it so TypeAtom doesn't get pissed at me + private protected TypeAtom() + { + throw new NotSupportedException(); + } + + private TypeAtom(Type type) + { + Type = type; + + HashSet ancestors = new HashSet(); + FindAncestors(Type, ancestors); + ancestors.Add(this); + Ancestors = ancestors.ToImmutableHashSet(); + + lock (s_lockObject) + { + Id = s_counter++; + s_atoms.Add(Id, this); + s_types.Add(Type, this); + } + } + + private static readonly object s_lockObject = new object(); + private static readonly Dictionary s_atoms = new Dictionary(); + private static readonly Dictionary s_types = new Dictionary(); + + private static int s_counter = 0; + + public static TypeAtom? Get(int id) => s_atoms.GetValueOrDefault(id); + public static TypeAtom Get(Type type) + { + if (s_types.TryGetValue(type, out TypeAtom? id)) + return id; + + // Type is not registered, try to acquire lock. + lock (s_lockObject) + { + // Maybe somebody else registered this type whilst acquiring the lock. + if (s_types.TryGetValue(type, out id)) + return id; + + // Register the type if applicable and leave. + return new TypeAtom(type); + } + + } + + private static void FindAncestors(Type type, HashSet destination) + { + // Traverse the object tree for all possible aliases. + if (type.BaseType != null) + { + foreach (TypeAtom ancestor in Get(type.BaseType).Ancestors) + { + destination.Add(ancestor); + } + } + + foreach (Type trait in type.GetInterfaces()) + { + TypeAtom atom = Get(trait); + destination.Add(atom); + } + } + } + + /// + /// Helper class for better type access performance. + /// + public sealed record TypeAtom : TypeAtom + { + public static TypeAtom Atom { get; } = Get(typeof(T)); + public new static int Id => Atom.Id; + public new static Type Type => Atom.Type; + public new static ImmutableHashSet Ancestors => Atom.Ancestors; + + private TypeAtom() { } + } +} diff --git a/Dashboard.Common/Collections/TypeHashSet.cs b/Dashboard.Common/Collections/TypeHashSet.cs new file mode 100644 index 0000000..3761509 --- /dev/null +++ b/Dashboard.Common/Collections/TypeHashSet.cs @@ -0,0 +1,44 @@ +using System.Collections; + +namespace Dashboard.Collections +{ + public class TypeHashSet(bool hierarchical = false) : IEnumerable + { + private readonly HashSet _set = new HashSet(); + + public bool Contains() => _set.Contains(TypeAtom.Id); + + public bool Set() + { + if (!_set.Add(TypeAtom.Id)) + return false; + + if (hierarchical) + foreach (TypeAtom ancestor in TypeAtom.Ancestors) + { + _set.Add(ancestor.Id); + } + + return true; + } + + public bool Reset() + { + if (!_set.Remove(TypeAtom.Id)) + return false; + + if (hierarchical) + foreach (TypeAtom ancestor in TypeAtom.Ancestors) + { + _set.Remove(ancestor.Id); + } + + return true; + } + + public void Clear() => _set.Clear(); + + public IEnumerator GetEnumerator() => _set.Select(x => TypeAtom.Get(x)!).GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + } +}