diff --git a/src/DebugUtils/MetaAttributes/MetaIDAttribute.cs b/src/DebugUtils/MetaAttributes/MetaIDAttribute.cs index 55f4b71..05593e9 100644 --- a/src/DebugUtils/MetaAttributes/MetaIDAttribute.cs +++ b/src/DebugUtils/MetaAttributes/MetaIDAttribute.cs @@ -4,6 +4,10 @@ using DCFApixels.DragonECS.Core; using DCFApixels.DragonECS.Internal; using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; using System.Runtime.InteropServices; using System.Text.RegularExpressions; @@ -37,6 +41,12 @@ namespace DCFApixels.DragonECS [ThreadStatic] private static bool _isInit; + public static CollisionList FindMetaIDCollisions(IEnumerable metas) + { + return new CollisionList(metas); + } + + public static bool IsGenericID(string id) { return Regex.IsMatch(id, @"^[^,<>\s]*$"); @@ -74,5 +84,197 @@ namespace DCFApixels.DragonECS { return IDToAttribute(GenerateNewUniqueID()); } + + #region CollisionList + [DebuggerTypeProxy(typeof(DebuggerProxy))] + public class CollisionList : IEnumerable + { + private ListInternal[] _linkedLists; + private Entry[] _linkedEntries; + private int _collisionsCount; + private int _listsCount; + public int CollisionsCount + { + get { return _collisionsCount; } + } + public int ListsCount + { + get { return _listsCount; } + } + public bool IsHasAnyCollision + { + get { return _listsCount > 0; } + } + + public CollisionList(IEnumerable metas) + { + var metasCount = metas.Count(); + Dictionary listIndexes = new Dictionary(metasCount); + _linkedLists = new ListInternal[metasCount]; + _linkedEntries = new Entry[metasCount]; + + bool hasCollision = false; + + _listsCount = 0; + foreach (var meta in metas) + { + if (listIndexes.TryGetValue(meta.MetaID, out int headIndex)) + { + hasCollision = true; + } + else + { + headIndex = _listsCount++; + listIndexes.Add(meta.MetaID, headIndex); + } + ref var list = ref _linkedLists[headIndex]; + int nodeIndex = _collisionsCount++; + ref Entry entry = ref _linkedEntries[nodeIndex]; + if (list.count == 0) + { + entry.next = -1; + } + entry.meta = meta; + list.head = headIndex; + list.count++; + } + + if (hasCollision) + { + for (int i = 0; i < _listsCount; i++) + { + ref var list = ref _linkedLists[i]; + if (list.count <= 1) + { + _linkedLists[i] = _linkedLists[--_listsCount]; + } + } + } + else + { + _listsCount = 0; + } + } + + private struct ListInternal + { + public int count; + public int head; + } + public struct Entry + { + public TypeMeta meta; + public int next; + } + + #region Enumerator + public Enumerator GetEnumerator() { return new Enumerator(); } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + public struct Enumerator : IEnumerator + { + private readonly CollisionList _collisions; + private readonly int _count; + private int _index; + public List Current + { + get + { + var list = _collisions._linkedLists[_index]; + return new List(_collisions, list.head, list.count); + } + } + object IEnumerator.Current { get { return Current; } } + public Enumerator(CollisionList collisions, int count) + { + _collisions = collisions; + _count = count; + _index = -1; + } + public bool MoveNext() { return _index++ < _count; } + public void Dispose() { } + public void Reset() { _index = -1; } + } + #endregion + + public readonly struct List : IEnumerable + { + private readonly CollisionList _collisions; + private readonly int _head; + private readonly int _count; + public int Count + { + get { return _count; } + } + internal List(CollisionList collisions, int head, int count) + { + _collisions = collisions; + _head = count == 0 ? -1 : head; + _head = head; + _count = count; + } + + #region Enumerator + public Enumerator GetEnumerator() { return new Enumerator(_collisions._linkedEntries, _head); } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + public struct Enumerator : IEnumerator + { + private readonly Entry[] _linkedEntries; + private readonly int _head; + private int _nextIndex; + private int _index; + public TypeMeta Current { get { return _linkedEntries[_index].meta; } } + object IEnumerator.Current { get { return Current; } } + public Enumerator(Entry[] linkedEntries, int head) + { + _linkedEntries = linkedEntries; + _head = head; + _nextIndex = _head; + _index = -1; + } + public bool MoveNext() + { + if (_nextIndex < 0) { return false; } + _index = _nextIndex; + _nextIndex = _linkedEntries[_index].next; + return true; + } + public void Dispose() { } + public void Reset() + { + _nextIndex = _head; + _index = -1; + } + } + #endregion + } + + #region DebuggerProxy + private class DebuggerProxy + { + private CollisionList _collisions; + public TypeMeta[][] Lists; + public DebuggerProxy(CollisionList collisions) + { + _collisions = collisions; + Lists = new TypeMeta[collisions.ListsCount][]; + int i = 0; + foreach (var list in collisions) + { + int j = 0; + Lists[i] = new TypeMeta[list.Count]; + foreach (var typeMeta in list) + { + Lists[i][j] = typeMeta; + j++; + } + i++; + } + } + } + #endregion + } + #endregion } } \ No newline at end of file