diff --git a/src/Collections/EcsGraph.cs b/src/Collections/EcsGraph.cs index 45fe88e..7795537 100644 --- a/src/Collections/EcsGraph.cs +++ b/src/Collections/EcsGraph.cs @@ -153,23 +153,25 @@ namespace DCFApixels.DragonECS #endregion #region Add/Del + public void Add(int startEntityID, int endEntityID, int relEntityID) + { + _relNodesMapping[relEntityID] = new RelNodesInfo( + _startBaskets.AddToBasket(startEntityID, relEntityID), + _endBaskets.AddToBasket(endEntityID, relEntityID)); + } public void Add(int relEntityID) { var (startEntityID, endEntityID) = _source.GetRelationInfo(relEntityID); - ref RelNodesInfo arcInfo = ref _relNodesMapping[relEntityID]; - - arcInfo.startNodeIndex = _startBaskets.AddToBasket(startEntityID, relEntityID); - arcInfo.endNodeIndex = _endBaskets.AddToBasket(endEntityID, relEntityID); + _relNodesMapping[relEntityID] = new RelNodesInfo( + _startBaskets.AddToBasket(startEntityID, relEntityID), + _endBaskets.AddToBasket(endEntityID, relEntityID)); } public void Del(int relEntityID) { var (startEntityID, endEntityID) = _source.GetRelationInfo(relEntityID); ref RelNodesInfo relInfo = ref _relNodesMapping[relEntityID]; _startBaskets.RemoveFromBasket(startEntityID, relInfo.startNodeIndex); - if (!_isLoop) - { - //_startBaskets.RemoveFromBasket(endEntityID, relInfo.endNodeIndex); - } + _endBaskets.RemoveFromBasket(endEntityID, relInfo.endNodeIndex); } public void DelStart(int startEntityID) { @@ -320,7 +322,11 @@ namespace DCFApixels.DragonECS public readonly static RelNodesInfo Empty = default; public int startNodeIndex; public int endNodeIndex; - + public RelNodesInfo(int startNodeIndex, int endNodeIndex) + { + this.startNodeIndex = startNodeIndex; + this.endNodeIndex = endNodeIndex; + } #region Object public override bool Equals(object obj) { diff --git a/src/EcsArc.cs b/src/EcsArc.cs index 5f303ba..bfb6fe6 100644 --- a/src/EcsArc.cs +++ b/src/EcsArc.cs @@ -134,7 +134,7 @@ namespace DCFApixels.DragonECS _relEntityInfos[relEntity] = new RelEntityInfo(startEntityID, endEntityID); _relEntities.Add(relEntity); - _entitiesGraph.Add(relEntity); + _entitiesGraph.Add(startEntityID, endEntityID, relEntity); return relEntity; } //public void DelRelation(int startEntityID, int endEntityID) diff --git a/src/Utils/BasketList.cs b/src/Utils/BasketList.cs index 49df24e..cbdd39e 100644 --- a/src/Utils/BasketList.cs +++ b/src/Utils/BasketList.cs @@ -1,4 +1,4 @@ -using System; +using DCFApixels.DragonECS.Relations.Internal; using System.Collections; using System.Collections.Generic; using System.Diagnostics; @@ -10,21 +10,27 @@ namespace DCFApixels.DragonECS [DebuggerTypeProxy(typeof(DebuggerProxy))] internal class BasketList { - public const int RECYCLE = -1; - public const int HEAD = 0; + public const int NULL = 0; - private BasketInfo[] _baskets = new BasketInfo[64]; - private Node[] _nodes; - private int _recycledListLast = -1; + private UnsafeArray _baskets = new UnsafeArray(64, true); + private UnsafeArray _nodes; + private int _recycledListHead = NULL; - #region Constructors + #region Constructors/Destroy public BasketList() : this(16) { } - public BasketList(int capacity) + public BasketList(int minCapacity) { - Initialize(capacity); + Initialize(ArrayUtility.NormalizeSizeToPowerOfTwo(minCapacity)); + } + //Dispose //GC.SuppressFinalize + ~BasketList() + { + _baskets.Dispose(); + _nodes.Dispose(); } #endregion + #region Clear [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { @@ -37,40 +43,14 @@ namespace DCFApixels.DragonECS _baskets[i] = default; } } + #endregion - [MethodImpl(MethodImplOptions.NoInlining)] - private void Resize(int newSize) - { - int oldSize = _nodes.Length; - Array.Resize(ref _nodes, newSize); - int leftNode = newSize - 1; - for (int i = oldSize; i < newSize; i++) - { - Link(i, leftNode); - leftNode = i; - } - LinkToRecycled(newSize - 1, oldSize); - } - [MethodImpl(MethodImplOptions.NoInlining)] - private void Initialize(int newSize) - { - _nodes = new Node[newSize]; - int leftNode = newSize - 1; - for (int i = 1; i < newSize; i++) - { - Link(i, leftNode); - leftNode = i; - } - LinkToRecycled(newSize - 1, 1); - } - + #region Other [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetBasketNodesCount(int basketIndex) { return _baskets[basketIndex].count; } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Set(int nodeIndex, int value) { @@ -81,16 +61,52 @@ namespace DCFApixels.DragonECS { return _nodes[nodeIndex].value; } - private Node GetNode(int nodeIndex) { return _nodes[nodeIndex]; } + #endregion + #region AddToBasket/TakeRecycledNode + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int TakeRecycledNode() + { + if (_recycledListHead == NULL) + { + ResizeNodes(_nodes.Length << 1); + } + int node = _recycledListHead; + _recycledListHead = _nodes[node].next; + return node; + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void RemoveFromBasket(int basketIndex, int nodeIndex) + public int AddToBasket(int basketIndex, int value) { + ref BasketInfo basketInfo = ref _baskets[basketIndex]; + int newNodeIndex = TakeRecycledNode(); + if (basketInfo.count == 0) + { + _nodes[newNodeIndex].SetValue_Prev(value, 0); + } + else + { + int nodeIndex = basketInfo.nodeIndex; + ref int nextNode_Prev = ref _nodes[nodeIndex].prev; + + _nodes[newNodeIndex].Set(value, nextNode_Prev, nodeIndex); + nextNode_Prev = newNodeIndex; + } + basketInfo.nodeIndex = newNodeIndex; + basketInfo.count++; + return newNodeIndex; + } + #endregion + + #region RemoveFromBasket/RemoveBasket + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RemoveFromBasket(int basketIndex, int nodeIndex) + {//нужно добавить ограничение на удаление повторяющейся ноды, иначе recycled ноды зацикливаются #if DEBUG if (nodeIndex <= 0) { @@ -112,68 +128,6 @@ namespace DCFApixels.DragonECS basketInfo.count--; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int AddToBasket(int basketIndex, int value) - { - ref BasketInfo basketInfo = ref _baskets[basketIndex]; - int newNodeIndex = TakeRecycledNode(); - if (basketInfo.count == 0) - { - basketInfo.nodeIndex = newNodeIndex; - _nodes[newNodeIndex].Set(value, 0, 0); - } - else - { - int nodeIndex = basketInfo.nodeIndex; - ref Node nextNode = ref _nodes[nodeIndex]; - - _nodes[newNodeIndex].Set(value, nextNode.prev, nodeIndex); - basketInfo.nodeIndex = newNodeIndex; - nextNode.prev = newNodeIndex; - } - basketInfo.count++; - return newNodeIndex; - } - - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private int TakeRecycledNode() - { - if (_recycledListLast == -1) - { - Resize(_nodes.Length << 1); - } - int node = _recycledListLast; - _recycledListLast = _nodes[node].prev; - return node; - } - //[MethodImpl(MethodImplOptions.AggressiveInlining)] - //private void Separate(int leftNodeIndex, int rightNodeIndex) - //{ - // _nodes[rightNodeIndex].prev = 0; - // _nodes[leftNodeIndex].next = 0; - //} - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void Link(int leftNodeIndex, int rightNodeIndex) - { - _nodes[rightNodeIndex].prev = leftNodeIndex; - _nodes[leftNodeIndex].next = rightNodeIndex; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void LinkToRecycled(int startNodeIndex, int endNodeIndex) - { - if (_recycledListLast <= -1) - { - _nodes[startNodeIndex].prev = RECYCLE; - } - else - { - Link(_recycledListLast, startNodeIndex); - } - _recycledListLast = endNodeIndex; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveBasket(int basketIndex) { @@ -193,55 +147,86 @@ namespace DCFApixels.DragonECS basket.count = 0; } + #endregion + #region Links + //[MethodImpl(MethodImplOptions.AggressiveInlining)] + //private void Separate(int leftNodeIndex, int rightNodeIndex) + //{ + // _nodes[rightNodeIndex].prev = 0; + // _nodes[leftNodeIndex].next = 0; + //} + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void Link(int leftNodeIndex, int rightNodeIndex) + { + _nodes[rightNodeIndex].prev = leftNodeIndex; + _nodes[leftNodeIndex].next = rightNodeIndex; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void LinkToRecycled(int startNodeIndex, int endNodeIndex) + { + if (_recycledListHead <= NULL) + { + _nodes[endNodeIndex].next = NULL; + } + else + { + Link(startNodeIndex, _recycledListHead); + } + _recycledListHead = startNodeIndex; + } + #endregion + + #region UpSize/Resize/Initialize [MethodImpl(MethodImplOptions.NoInlining)] + private void ResizeNodes(int newSize) + { + int oldSize = _nodes.Length; + UnsafeArray.Resize(ref _nodes, newSize); + InitNewNodes(oldSize, newSize); + } + [MethodImpl(MethodImplOptions.NoInlining)] + private void Initialize(int newSize) + { + _nodes = new UnsafeArray(newSize); + _nodes[0] = Node.Empty; + InitNewNodes(1, newSize); + } + [MethodImpl(MethodImplOptions.NoInlining)] + private void InitNewNodes(int oldSize, int newSize) + { + int leftNode = NULL; + for (int i = oldSize; i < newSize; i++) + { + Link(leftNode, i); + leftNode = i; + } + LinkToRecycled(oldSize, newSize - 1); + } + + public void UpNodesSize(int minSize) + { + if (minSize > _nodes.Length) + { + int newSize = ArrayUtility.NormalizeSizeToPowerOfTwo(minSize); + ResizeNodes(newSize); + } + } public void UpBasketsSize(int minSize) { if (minSize > _baskets.Length) { - int newSize = 1 << (GetHighBitNumber((uint)minSize - 1) + 1); - Array.Resize(ref _baskets, newSize); + int newSize = ArrayUtility.NormalizeSizeToPowerOfTwo(minSize); + UnsafeArray.ResizeAndInit(ref _baskets, newSize); } } - private static int GetHighBitNumber(uint bits) - { - if (bits == 0) - { - return -1; - } - int bit = 0; - if ((bits & 0xFFFF0000) != 0) - { - bits >>= 16; - bit |= 16; - } - if ((bits & 0xFF00) != 0) - { - bits >>= 8; - bit |= 8; - } - if ((bits & 0xF0) != 0) - { - bits >>= 4; - bit |= 4; - } - if ((bits & 0xC) != 0) - { - bits >>= 2; - bit |= 2; - } - if ((bits & 0x2) != 0) - { - bit |= 1; - } - return bit; - } + #endregion #region Node [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] public struct Node { - public static readonly Node Empty = new Node() { value = 0, next = -1 }; + public static readonly Node Empty = new Node() { value = 0, next = NULL }; public int value; /// next node index public int next; @@ -254,6 +239,18 @@ namespace DCFApixels.DragonECS this.next = next; this.prev = prev; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetValue_Prev(int value, int prev) + { + this.value = value; + this.prev = prev; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetPrev_Next(int prev, int next) + { + this.next = next; + this.prev = prev; + } public override string ToString() => $"node({prev}<>{next} v:{value})"; } #endregion @@ -301,7 +298,7 @@ namespace DCFApixels.DragonECS IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public struct Enumerator : IEnumerator { - private readonly Node[] _nodes; + private readonly UnsafeArray _nodes; private int _nodeIndex; private int _nextNodeIndex; private int _count; @@ -361,17 +358,21 @@ namespace DCFApixels.DragonECS { List result = new List(); Node curNode = new Node(); - curNode.index = _basketList._recycledListLast; + curNode.index = _basketList._recycledListHead; - while (curNode.index != -1) + for (int i = 0; i < _basketList._nodes.Length; i++) { + if (curNode.index == NULL) + { + break; + } BasketList.Node x = _basketList.GetNode(curNode.index); curNode.prev = x.prev; curNode.next = x.next; result.Add(curNode); - curNode = new Node(); - curNode.index = curNode.prev; + + curNode.index = curNode.next; } return result; } diff --git a/src/Utils/UnsafeArray.cs b/src/Utils/UnsafeArray.cs index 5339824..7cab469 100644 --- a/src/Utils/UnsafeArray.cs +++ b/src/Utils/UnsafeArray.cs @@ -1,5 +1,4 @@ -using DCFApixels.DragonECS.Utils; -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; @@ -32,19 +31,27 @@ namespace DCFApixels.DragonECS.Relations.Internal public ref T this[int index] { - get { return ref ptr[index]; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if DEBUG + if (index < 0 || index >= Length) + Throw.ArgumentOutOfRange(); +#endif + return ref ptr[index]; + } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public UnsafeArray(int length) { - UnmanagedArrayUtility.New(out ptr, length); + ptr = UnmanagedArrayUtility.New(length); Length = length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public UnsafeArray(int length, bool isInit) { - UnmanagedArrayUtility.NewAndInit(out ptr, length); + ptr = UnmanagedArrayUtility.NewAndInit(length); Length = length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -67,8 +74,7 @@ namespace DCFApixels.DragonECS.Relations.Internal } public override string ToString() { - T* ptr = this.ptr; - return CollectionUtility.AutoToString(EnumerableInt.Range(0, Length).Select(i => ptr[i]), "ua"); + return $"ua({Length}) ({string.Join(", ", this.ToArray())})"; } public static void Resize(ref UnsafeArray array, int newSize) @@ -113,12 +119,18 @@ namespace DCFApixels.DragonECS.Relations.Internal internal class DebuggerProxy { + public void* ptr; public T[] elements; public int length; public DebuggerProxy(UnsafeArray instance) { - elements = EnumerableInt.Range(0, instance.Length).Select(i => instance.ptr[i]).ToArray(); + ptr = instance.ptr; length = instance.Length; + elements = new T[length]; + for (int i = 0; i < length; i++) + { + elements[i] = instance[i]; + } } } }