diff --git a/src/EcsArc.cs b/src/EcsArc.cs index e6e96fa..3a3d9c4 100644 --- a/src/EcsArc.cs +++ b/src/EcsArc.cs @@ -7,13 +7,15 @@ namespace DCFApixels.DragonECS //Edge world //Relation entity //Relation component - public class EcsArc : IEcsWorldEventListener, IEcsEntityEventListener + public class EcsArc { private readonly EcsWorld _startWorld; private readonly EcsWorld _endWorld; private readonly EcsArcWorld _arcWorld; - private readonly VertexWorldHandler _startWorldHandler; + private readonly StartWorldHandler _startWorldHandler; + private readonly ArcWorldHandler _arcWorldHandler; + private readonly EndWorldHandler _endWorldHandler; private readonly SparseArray64 _relationsMatrix = new SparseArray64(); @@ -37,13 +39,16 @@ namespace DCFApixels.DragonECS _arkEntityInfos = new ArcEntityInfo[arcWorld.Capacity]; - _startWorldHandler = new VertexWorldHandler(this, _startWorld); + _startWorldHandler = new StartWorldHandler(this, _startWorld); + _arcWorldHandler = new ArcWorldHandler(this); + _endWorldHandler = new EndWorldHandler(this, _endWorld); _startWorld.AddListener(worldEventListener: _startWorldHandler); _startWorld.AddListener(entityEventListener: _startWorldHandler); - - _arcWorld.AddListener(worldEventListener: this); - _arcWorld.AddListener(entityEventListener: this); + _arcWorld.AddListener(worldEventListener: _arcWorldHandler); + _arcWorld.AddListener(entityEventListener: _arcWorldHandler); + _endWorld.AddListener(worldEventListener: _endWorldHandler); + _endWorld.AddListener(entityEventListener: _endWorldHandler); } #endregion @@ -128,36 +133,80 @@ namespace DCFApixels.DragonECS } #endregion - #region Callbacks - void IEcsWorldEventListener.OnWorldResize(int newSize) - { - Array.Resize(ref _arkEntityInfos, newSize); - } - void IEcsWorldEventListener.OnReleaseDelEntityBuffer(ReadOnlySpan buffer) - { - _arcWorld.ReleaseDelEntityBuffer(buffer.Length); - } - void IEcsWorldEventListener.OnWorldDestroy() { } - - void IEcsEntityEventListener.OnNewEntity(int entityID) { } - void IEcsEntityEventListener.OnDelEntity(int entityID) - { - ref ArcEntityInfo rel = ref _arkEntityInfos[entityID]; - if (_relationsMatrix.Contains(rel.start, rel.end)) - Del(rel.start, rel.end); - } - #endregion #region VertexWorldHandler - private class VertexWorldHandler : IEcsWorldEventListener, IEcsEntityEventListener + private class ArcWorldHandler : IEcsWorldEventListener, IEcsEntityEventListener { - private readonly EcsArc _source; - private readonly EcsWorld _world; + private readonly EcsArc _arc; - public VertexWorldHandler(EcsArc source, EcsWorld world) + public ArcWorldHandler(EcsArc arc) { - _source = source; - _world = world; + _arc = arc; + } + + #region Callbacks + public void OnDelEntity(int entityID) + { + ref ArcEntityInfo rel = ref _arc._arkEntityInfos[entityID]; + if (_arc._relationsMatrix.Contains(rel.start, rel.end)) + { + _arc.Del(rel.start, rel.end); + } + } + public void OnNewEntity(int entityID) + { + } + public void OnReleaseDelEntityBuffer(ReadOnlySpan buffer) + { + _arc._arcWorld.ReleaseDelEntityBuffer(buffer.Length); + } + public void OnWorldDestroy() + { + } + public void OnWorldResize(int newSize) + { + Array.Resize(ref _arc._arkEntityInfos, newSize); + } + #endregion + } + private class StartWorldHandler : IEcsWorldEventListener, IEcsEntityEventListener + { + private readonly EcsArc _arc; + private readonly EcsWorld _start; + + public StartWorldHandler(EcsArc arc, EcsWorld world) + { + _arc = arc; + _start = world; + } + + #region Callbacks + public void OnDelEntity(int entityID) + { + } + public void OnNewEntity(int entityID) + { + } + public void OnReleaseDelEntityBuffer(ReadOnlySpan buffer) + { + } + public void OnWorldDestroy() + { + } + public void OnWorldResize(int newSize) + { + } + #endregion + } + private class EndWorldHandler : IEcsWorldEventListener, IEcsEntityEventListener + { + private readonly EcsArc _arc; + private readonly EcsWorld _end; + + public EndWorldHandler(EcsArc arc, EcsWorld world) + { + _arc = arc; + _end = world; } #region Callbacks diff --git a/src/EcsJoinGroup.cs b/src/EcsJoinGroup_OLD.cs similarity index 98% rename from src/EcsJoinGroup.cs rename to src/EcsJoinGroup_OLD.cs index 2dea2d5..930d54e 100644 --- a/src/EcsJoinGroup.cs +++ b/src/EcsJoinGroup_OLD.cs @@ -2,7 +2,7 @@ namespace DCFApixels.DragonECS { - public class EcsJoinGroup + public class EcsJoinGroup_OLD { private EcsArc _source; diff --git a/src/Utils/ArcEntityInfo.cs b/src/Utils/ArcEntityInfo.cs index 56ff965..9b17b83 100644 --- a/src/Utils/ArcEntityInfo.cs +++ b/src/Utils/ArcEntityInfo.cs @@ -29,6 +29,12 @@ namespace DCFApixels.DragonECS start = startEntity; end = endEntity; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Deconstruct(out int start, out int end) + { + start = this.start; + end = this.end; + } #region operators [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Utils/BasketList.cs b/src/Utils/BasketList.cs new file mode 100644 index 0000000..9915b3a --- /dev/null +++ b/src/Utils/BasketList.cs @@ -0,0 +1,413 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DCFApixels.DragonECS +{ + public class BasketList + { + public const int RECYCLE = -1; + public const int HEAD = 0; + + private BasketInfo[] _baskets = new BasketInfo[64]; + private Node[] _nodes; + private int _recycledListLast = -1; + + private int _basketsCount = 0; + private int[] _recycledBuskets = new int[64]; + private int _recycledBusketsCount = 0; + + #region Constructors + public BasketList() : this(16) { } + public BasketList(int capacity) + { + Initialize(capacity); + } + #endregion + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Clear() + { + for (int i = 0; i < _nodes.Length; i++) + { + _nodes[i].next = 0; + } + for (int i = 0; i < _baskets.Length; i++) + { + _baskets[i] = default; + } + } + + [MethodImpl(MethodImplOptions.NoInlining)] + private void Resize(int newSize) + { + //int oldSize = _nodes.Length; + //Array.Resize(ref _nodes, newSize); + //int leftNode = newSize - 1; + //for (int i = newSize - 1, n = oldSize; i >= n; i--) + //{ + // Link(i, leftNode); + // leftNode = i; + //} + //LinkToRecycled(newSize - 1, oldSize); + + 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 = newSize - 1, n = 1; i >= n; i--) + //{ + // Link(i, leftNode); + // leftNode = i; + //} + //LinkToRecycled(newSize - 1, 1); + + _nodes = new Node[newSize]; + int leftNode = newSize - 1; + for (int i = 1; i < newSize; i++) + { + Link(i, leftNode); + leftNode = i; + } + LinkToRecycled(newSize - 1, 1); + } + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Set(int nodeIndex, int value) + { + _nodes[nodeIndex].value = value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Get(int nodeIndex) + { + return _nodes[nodeIndex].value; + } + //[MethodImpl(MethodImplOptions.AggressiveInlining)] + //public ref readonly Node GetNode(int nodeIndex) + //{ + // return ref _nodes[nodeIndex]; + //} + + + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private bool RemoveFromBasketAt(int basketIndex, int nodeIndex) + { +#if DEBUG + if (nodeIndex <= 0) + { + throw new ArgumentOutOfRangeException(); + } +#endif + ref BasketInfo basketInfo = ref _baskets[basketIndex]; + ref var node = ref _nodes[nodeIndex]; + int nextNode = node.next; + + Link(node.prev, nextNode); + LinkToRecycled(nodeIndex, nodeIndex); + if(basketInfo.nodeIndex == nodeIndex) + { + basketInfo.nodeIndex = nextNode; + } + return --basketInfo.count > 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private int InsertToBasket(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) + { + ref BasketInfo basket = ref _baskets[basketIndex]; + + int startNodeIndex = basket.nodeIndex; + int endNodeIndex = startNodeIndex; + ref Node startNode = ref _nodes[startNodeIndex]; + for (int i = 0, n = basket.count; i < n; i++) + { + endNodeIndex = _nodes[endNodeIndex].next; + } + ref Node endNode = ref _nodes[endNodeIndex]; + + if (_recycledListLast != -1) + { + //_nodes[_recycledListLast].next = startNodeIndex; //link recycled nodes; + //startNode.prev = _recycledListLast; + //_recycledListLast = endNodeIndex; + Link(_recycledListLast, startNodeIndex); + } + _recycledListLast = endNodeIndex; + + Link(startNode.prev, endNode.next); + + basket.count = 0; + } + + 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; + } + + //public int NewBasket() + //{ + // int newBasketIndex; + // if(_recycledBusketsCount > 0) + // { + // newBasketIndex = _recycledBuskets[--_recycledBusketsCount]; + // } + // else + // { + // newBasketIndex = _basketsCount; + // if(_basketsCount >= _baskets.Length) + // { + // Array.Resize(ref _baskets, _baskets.Length << 1); + // } + // } + // _basketsCount++; + // _baskets[newBasketIndex] = BasketInfo.Empty; + // return newBasketIndex; + //} + + + #region Node + [StructLayout(LayoutKind.Sequential, Pack = 4, Size = 8)] + public struct Node + { + public static readonly Node Empty = new Node() { value = 0, next = -1 }; + public int value; + /// next node index + public int next; + /// prev node index + public int prev; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Set(int value, int prev, int next) + { + this.value = value; + this.next = next; + this.prev = prev; + } + public override string ToString() => $"node({prev}<>{next} v:{value})"; + } + #endregion + + #region BasketInfo + private struct BasketInfo + { + public static readonly BasketInfo Empty = new BasketInfo() { nodeIndex = 0, count = 0, }; + public int nodeIndex; + public int count; + public override string ToString() => $"basket_info(i:{nodeIndex} c:{count})"; + } + #endregion + + #region Basket + public Basket this[int basketIndex] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => GetBasket(basketIndex); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Basket GetBasket(int basketIndex) + { + if (_baskets.Length <= basketIndex) + { + int newSize = GetHighBitNumber((uint)basketIndex) << 1; + Array.Resize(ref _baskets, newSize); + } + return new Basket(this, basketIndex); + } + public struct Basket : IEnumerable + { + private readonly BasketList _basketList; + public readonly int _basketIndex; + private int _count; + + public int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _count; + } + public int BasketIndex + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _basketIndex; + } + public int StartNodeIndex + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _basketList._baskets[_basketIndex].nodeIndex; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Basket(BasketList basketList, int basketIndex) + { + _basketList = basketList; + _basketIndex = basketIndex; + _count = _basketList._baskets[basketIndex].count; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Add(int value) + { + _count++; + return _basketList.InsertToBasket(_basketIndex, value); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RemoveAt(int nodeIndex) + { +#if DEBUG + if (_count <= 0) + throw new Exception(); +#endif + _basketList.RemoveFromBasketAt(_basketIndex, nodeIndex); + _count--; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() => new Enumerator(this); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + + public struct Enumerator : IEnumerator + { + private readonly Node[] _nodes; + private int _nodeIndex; + private int _nextNode; + private int _count; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(Basket iterator) + { + ref BasketInfo basketInfo = ref iterator._basketList._baskets[iterator._basketIndex]; + _nodes = iterator._basketList._nodes; + _nodeIndex = -1; + _nextNode = basketInfo.nodeIndex; + _count = basketInfo.count; + } + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _nodes[_nodeIndex].value; + } + object IEnumerator.Current => Current; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() + { + _nodeIndex = _nextNode; + _nextNode = _nodes[_nextNode].next; + return _nodeIndex > 0 && _count-- > 0; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() { } + } + } + #endregion + } +} diff --git a/src/Utils/EcsJoinGroup.cs b/src/Utils/EcsJoinGroup.cs new file mode 100644 index 0000000..b5468b8 --- /dev/null +++ b/src/Utils/EcsJoinGroup.cs @@ -0,0 +1,242 @@ +using System.Diagnostics; +using System.Runtime.CompilerServices; + +namespace DCFApixels.DragonECS.Relations.Utils +{ + [DebuggerTypeProxy(typeof(DebuggerProxy))] + internal class EcsJoinGroup + { + private EcsArc _arc; + private readonly bool _isLoop; + + private BasketList _startBaskets; + private BasketList _endBaskets; + private ArcInfo[] _arcMapping; + + public EcsJoinGroup(EcsArc arc) + { + _arc = arc; + _isLoop = arc.IsLoop; + + _startBaskets = new BasketList(); + if (_isLoop) + { + _endBaskets = _startBaskets; + } + else + { + _endBaskets = new BasketList(); + } + _arcMapping = new ArcInfo[arc.ArcWorld.Capacity]; + } + public void Clear() + { + _startBaskets.Clear(); + if (!_isLoop) + { + _endBaskets.Clear(); + } + } + public void Add(int arcEntityID) + { + var (startEntityID, endEntityID) = _arc.GetArcInfo(arcEntityID); + _startBaskets[startEntityID].Add(endEntityID); + _startBaskets[endEntityID].Add(startEntityID); + + + + + + + + + + + + + + var (startEntityID, endEntityID) = _arc.GetArcInfo(arcEntityID); + ArcInfo arcInfo = _arcMapping[arcEntityID]; + + ref var startSpan = ref _startMapping[startEntityID]; + if (startSpan.nodeIndex <= 0) + { + startSpan.nodeIndex = _startList.Add(endEntityID); + arcInfo.startNodeIndex = startSpan.nodeIndex; + } + else + { + arcInfo.startNodeIndex = _startList.InsertAfter(startSpan.nodeIndex, endEntityID); + } + startSpan.count++; + + ref var endSpan = ref _endMapping[endEntityID]; + if (endSpan.nodeIndex <= 0) + { + endSpan.nodeIndex = _endList.Add(startEntityID); + arcInfo.endNodeIndex = endSpan.nodeIndex; + } + else + { + arcInfo.endNodeIndex = _endList.InsertAfter(endSpan.nodeIndex, startEntityID); + } + endSpan.count++; + } + + public void Del(int arcEntityID) + { + var (startEntityID, endEntityID) = _arc.GetArcInfo(arcEntityID); + ref ArcInfo arcInfo = ref _arcMapping[arcEntityID]; + int nextIndex; + + ref var startSpan = ref _startMapping[startEntityID]; + nextIndex = _startList.RemoveIndexAndReturnNextIndex(arcInfo.startNodeIndex); + if (startSpan.nodeIndex == arcInfo.startNodeIndex) + { + startSpan.nodeIndex = nextIndex; + } + + if (!_isLoop) + { + ref var endSpan = ref _endMapping[endEntityID]; + nextIndex = _endList.RemoveIndexAndReturnNextIndex(arcInfo.endNodeIndex); ; + if (endSpan.nodeIndex == arcInfo.endNodeIndex) + { + endSpan.nodeIndex = nextIndex; + } + } + arcInfo = default; + } + public void DelEnd(int endEntityID) + { + DelPosInternal(_endMapping, _startList, _endList, endEntityID); + } + public void DelStart(int startEntityID) + { + DelPosInternal(_startMapping, _endList, _startList, startEntityID); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private void DelPosInternal(SpanInfo[] endMapping, IdsLinkedList startList, IdsLinkedList endList, int posEntityID) + { + ref var endSpan = ref endMapping[posEntityID]; + if (endSpan.nodeIndex <= 0) + { + return; + } + foreach (var startNodeIndex in endList.GetSpan(endSpan.nodeIndex, endSpan.count)) + { + int indx = startList.RemoveIndexAndReturnNextIndex(startNodeIndex); + if (endSpan.nodeIndex == startNodeIndex) + { + endSpan.nodeIndex = indx; + } + } + endList.RemoveSpan(endSpan.nodeIndex, endSpan.count); + endSpan = SpanInfo.Empty; + } + + + public IdsLinkedList.Span GetSpanFor(int startEntityID) + { + ref var head = ref _startMapping[startEntityID]; + if (head.nodeIndex <= 0) + return _startList.EmptySpan(); + else + return _startList.GetSpan(head.nodeIndex, head.count); + } + public IdsLinkedList.LongSpan GetLongSpanFor(EcsWorld world, int startEntityID) + { + ref var head = ref _startMapping[startEntityID]; + if (head.nodeIndex <= 0) + return _startList.EmptyLongSpan(world); + else + return _startList.GetLongSpan(world, head.nodeIndex, head.count); + } + + private struct SpanInfo + { + public static readonly SpanInfo Empty = default; + public int nodeIndex; + public int count; + } + + private struct ArcInfo + { + public int startNodeIndex; + public int endNodeIndex; + } + + #region DebuggerProxy + internal class DebuggerProxy + { + private EcsJoinGroup _basket; + + public SpanDebugInfo[] HeadSpans => GetSpans(_basket._startList, _basket._startMapping); + public SpanDebugInfo[] ValueSpans => GetSpans(_basket._endList, _basket._endMapping); + + private SpanDebugInfo[] GetSpans(IdsLinkedList list, SpanInfo[] mapping) + { + SpanDebugInfo[] result = new SpanDebugInfo[mapping.Length]; + for (int i = 0; i < mapping.Length; i++) + result[i] = new SpanDebugInfo(list, mapping[i].nodeIndex, mapping[i].count); + return result; + } + public DebuggerProxy(EcsJoinGroup basket) + { + _basket = basket; + } + public struct SpanDebugInfo + { + private IdsLinkedList _list; + public int index; + public int count; + public NodeDebugInfo[] Nodes + { + get + { + var result = new NodeDebugInfo[this.count]; + var nodes = _list.Nodes; + int index; + int count = this.count; + int next = this.index; + int i = 0; + while (true) + { + index = next; + next = nodes[next].next; + if (!(index > 0 && count-- > 0)) + break; + var node = nodes[index]; + result[i] = new NodeDebugInfo(index, node.prev, node.next, node.value); + i++; + } + return result; + } + } + public SpanDebugInfo(IdsLinkedList list, int index, int count) + { + _list = list; + this.index = index; + this.count = count; + } + public override string ToString() => $"[{index}] {count}"; + } + public struct NodeDebugInfo + { + public int index; + public int prev; + public int next; + public int value; + public NodeDebugInfo(int index, int prev, int next, int value) + { + this.index = index; + this.prev = prev; + this.next = next; + this.value = value; + } + public override string ToString() => $"[{index}] {prev}_{next} - {value}"; + } + } + #endregion + } +} diff --git a/src/Utils/IdsBasket.cs b/src/Utils/IdsBasket.cs deleted file mode 100644 index 28c2a28..0000000 --- a/src/Utils/IdsBasket.cs +++ /dev/null @@ -1,175 +0,0 @@ -using System; -using System.Diagnostics; - -namespace DCFApixels.DragonECS.Relations.Utils -{ - [DebuggerTypeProxy(typeof(DebuggerProxy))] - internal class IdsBasket - { - private IdsLinkedList _headList = new IdsLinkedList(4); - private IdsLinkedList _valueList = new IdsLinkedList(4); - private SpanInfo[] _headMapping; - private SpanInfo[] _valueMapping; - private int _size; - - public IdsBasket(int size) - { - _headMapping = new SpanInfo[size]; - _valueMapping = new SpanInfo[size]; - _size = size; - } - - public void Resize(int newSize) - { - Array.Resize(ref _headMapping, newSize); - Array.Resize(ref _valueMapping, newSize); - _size = newSize; - } - public void Clear() - { - _headList.Clear(); - _valueList.Clear(); - ArrayUtility.Fill(_headMapping, SpanInfo.Empty); - ArrayUtility.Fill(_valueMapping, SpanInfo.Empty); - _size = 0; - } - public void AddToHead(int headID, int id) - { - int _savedNode; - ref var headSpan = ref _headMapping[headID]; - if (headSpan.startNodeIndex <= 0) - { - _savedNode = _headList.Add(id); - headSpan.startNodeIndex = _savedNode; - } - else - { - _savedNode = _headList.InsertAfter(headSpan.startNodeIndex, id); - } - headSpan.count++; - - ref var valueSpan = ref _valueMapping[id]; - if (valueSpan.startNodeIndex <= 0) - valueSpan.startNodeIndex = _valueList.Add(_savedNode); - else - _valueList.InsertAfter(valueSpan.startNodeIndex, _savedNode); - valueSpan.count++; - } - public void Del(int id) - { - ref var valueSpan = ref _valueMapping[id]; - ref var headSpan = ref _headMapping[id]; - if (valueSpan.startNodeIndex <= 0) - return; - foreach (var nodeIndex in _valueList.GetSpan(valueSpan.startNodeIndex, valueSpan.count)) - _headList.Remove(nodeIndex); - _valueList.RemoveSpan(valueSpan.startNodeIndex, valueSpan.count); - valueSpan = SpanInfo.Empty; - } - - public void DelHead(int headID) - { - ref var headSpan = ref _headMapping[headID]; - _valueList.RemoveSpan(headSpan.startNodeIndex, headSpan.count); - headSpan = SpanInfo.Empty; - } - - public IdsLinkedList.Span GetSpanFor(int value) - { - ref var head = ref _headMapping[value]; - if (head.startNodeIndex <= 0) - return _headList.EmptySpan(); - else - return _headList.GetSpan(head.startNodeIndex, head.count); - } - public IdsLinkedList.LongSpan GetLongSpanFor(EcsWorld world, int value) - { - ref var head = ref _headMapping[value]; - if (head.startNodeIndex <= 0) - return _headList.EmptyLongSpan(world); - else - return _headList.GetLongSpan(world, head.startNodeIndex, head.count); - } - - private struct SpanInfo - { - public readonly static SpanInfo Empty = default; - public int startNodeIndex; - public int count; - } - - #region DebuggerProxy - internal class DebuggerProxy - { - private IdsBasket _basket; - - public SpanDebugInfo[] HeadSpans => GetSpans(_basket._headList, _basket._headMapping); - public SpanDebugInfo[] ValueSpans => GetSpans(_basket._valueList, _basket._valueMapping); - - private SpanDebugInfo[] GetSpans(IdsLinkedList list, SpanInfo[] mapping) - { - SpanDebugInfo[] result = new SpanDebugInfo[mapping.Length]; - for (int i = 0; i < mapping.Length; i++) - result[i] = new SpanDebugInfo(list, mapping[i].startNodeIndex, mapping[i].count); - return result; - } - public DebuggerProxy(IdsBasket basket) - { - _basket = basket; - } - - public struct SpanDebugInfo - { - private IdsLinkedList _list; - public int index; - public int count; - public NodeDebugInfo[] Nodes - { - get - { - var result = new NodeDebugInfo[this.count]; - var nodes = _list.Nodes; - int index; - int count = this.count; - int next = this.index; - int i = 0; - while (true) - { - index = next; - next = nodes[next].next; - if (!(index > 0 && count-- > 0)) - break; - var node = nodes[index]; - result[i] = new NodeDebugInfo(index, node.prev, node.next, node.value); - i++; - } - return result; - } - } - public SpanDebugInfo(IdsLinkedList list, int index, int count) - { - _list = list; - this.index = index; - this.count = count; - } - public override string ToString() => $"[{index}] {count}"; - } - public struct NodeDebugInfo - { - public int index; - public int prev; - public int next; - public int value; - public NodeDebugInfo(int index, int prev, int next, int value) - { - this.index = index; - this.prev = prev; - this.next = next; - this.value = value; - } - public override string ToString() => $"[{index}] {prev}_{next} - {value}"; - } - } - #endregion - } -} diff --git a/src/Utils/IdsLinkedList.cs b/src/Utils/IdsLinkedList.cs index 93cc45d..7c40095 100644 --- a/src/Utils/IdsLinkedList.cs +++ b/src/Utils/IdsLinkedList.cs @@ -2,6 +2,7 @@ using System.Collections; using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace DCFApixels.DragonECS.Relations.Utils @@ -19,10 +20,27 @@ namespace DCFApixels.DragonECS.Relations.Utils private int _recycledNodesCount; #region Properties - public int Count => _count; - public int Capacity => _nodes.Length; - public int Last => _lastNodeIndex; - public ReadOnlySpan Nodes => new ReadOnlySpan(_nodes); + public int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _count; + } + public int Capacity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _nodes.Length; + } + + public int Last + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _lastNodeIndex; + } + public ReadOnlySpan Nodes + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => new ReadOnlySpan(_nodes); + } #endregion #region Constructors @@ -33,11 +51,7 @@ namespace DCFApixels.DragonECS.Relations.Utils } #endregion - public void Resize(int newCapacity) - { - Array.Resize(ref _nodes, newCapacity + 10); - } - + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { for (int i = 0; i < _nodes.Length; i++) @@ -46,27 +60,41 @@ namespace DCFApixels.DragonECS.Relations.Utils _count = 0; } - public void Set(int nodeIndex, int value) => _nodes[nodeIndex].value = value; - public int Get(int nodeIndex) => _nodes[nodeIndex].value; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Set(int nodeIndex, int value) + { + _nodes[nodeIndex].value = value; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Get(int nodeIndex) + { + return _nodes[nodeIndex].value; + } /// Insert after /// new node index + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int InsertAfter(int nodeIndex, int value) { if (++_count >= _nodes.Length) + { Array.Resize(ref _nodes, _nodes.Length << 1); + } int newNodeIndex = _recycledNodesCount > 0 ? _recycledNodes[--_recycledNodesCount] : _count; ref Node prevNode = ref _nodes[nodeIndex]; ref Node nextNode = ref _nodes[prevNode.next]; if (prevNode.next == 0) + { _lastNodeIndex = newNodeIndex; + } _nodes[newNodeIndex].Set(value, nextNode.prev, prevNode.next); prevNode.next = newNodeIndex; nextNode.prev = newNodeIndex; return newNodeIndex; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public int InsertBefore(int nodeIndex, int value) { if (++_count >= _nodes.Length) @@ -81,7 +109,8 @@ namespace DCFApixels.DragonECS.Relations.Utils return newNodeIndex; } - public void Remove(int nodeIndex) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void RemoveIndex(int nodeIndex) { if (nodeIndex <= 0) throw new ArgumentOutOfRangeException(); @@ -95,7 +124,26 @@ namespace DCFApixels.DragonECS.Relations.Utils _recycledNodes[_recycledNodesCount++] = nodeIndex; _count--; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int RemoveIndexAndReturnNextIndex(int nodeIndex) + { + if (nodeIndex <= 0) + throw new ArgumentOutOfRangeException(); + ref var node = ref _nodes[nodeIndex]; + int nextNode = node.next; + _nodes[node.next].prev = node.prev; + _nodes[node.prev].next = nextNode; + + if (_recycledNodesCount >= _recycledNodes.Length) + Array.Resize(ref _recycledNodes, _recycledNodes.Length << 1); + _recycledNodes[_recycledNodesCount++] = nodeIndex; + _count--; + + return nextNode; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void RemoveSpan(int startNodeIndex, int count) { if (count <= 0) @@ -127,19 +175,33 @@ namespace DCFApixels.DragonECS.Relations.Utils _count -= count; } - public int Add(int id) => InsertAfter(_lastNodeIndex, id); - public ref readonly Node GetNode(int nodeIndex) => ref _nodes[nodeIndex]; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Add(int id) + { + return InsertAfter(_lastNodeIndex, id); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ref readonly Node GetNode(int nodeIndex) + { + return ref _nodes[nodeIndex]; + } #region Span/Enumerator IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public SpanEnumerator GetEnumerator() => new SpanEnumerator(_nodes, _nodes[Head].next, _count); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span GetSpan(int startNodeIndex, int count) => new Span(this, startNodeIndex, count); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span EmptySpan() => new Span(this, 0, 0); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public LongSpan GetLongs(EcsWorld world) => new LongSpan(world, this, _nodes[Head].next, _count); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public LongSpan GetLongSpan(EcsWorld world, int startNodeIndex, int count) => new LongSpan(world, this, startNodeIndex, count); + [MethodImpl(MethodImplOptions.AggressiveInlining)] public LongSpan EmptyLongSpan(EcsWorld world) => new LongSpan(world, this, 0, 0); public readonly ref struct Span @@ -147,12 +209,14 @@ namespace DCFApixels.DragonECS.Relations.Utils private readonly IdsLinkedList _source; private readonly int _startNodeIndex; private readonly int _count; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public Span(IdsLinkedList source, int startNodeIndex, int count) { _source = source; _startNodeIndex = startNodeIndex; _count = count; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public SpanEnumerator GetEnumerator() => new SpanEnumerator(_source._nodes, _startNodeIndex, _count); } public struct SpanEnumerator : IEnumerator @@ -161,6 +225,7 @@ namespace DCFApixels.DragonECS.Relations.Utils private int _count; private int _index; private int _next; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public SpanEnumerator(Node[] nodes, int startIndex, int count) { _nodes = nodes; @@ -168,15 +233,26 @@ namespace DCFApixels.DragonECS.Relations.Utils _count = count; _next = startIndex; } - public int Current => _nodes[_index].value; + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return _nodes[_index].value; + } + } + object IEnumerator.Current => Current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { _index = _next; _next = _nodes[_next].next; return _index > 0 && _count-- > 0; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] void IDisposable.Dispose() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] void IEnumerator.Reset() { _index = -1; @@ -189,6 +265,7 @@ namespace DCFApixels.DragonECS.Relations.Utils private readonly IdsLinkedList _source; private readonly int _startNodeIndex; private readonly int _count; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public LongSpan(EcsWorld world, IdsLinkedList source, int startNodeIndex, int count) { _world = world; @@ -196,6 +273,7 @@ namespace DCFApixels.DragonECS.Relations.Utils _startNodeIndex = startNodeIndex; _count = count; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public LongSpanEnumerator GetEnumerator() => new LongSpanEnumerator(_world, _source._nodes, _startNodeIndex, _count); } public struct LongSpanEnumerator : IEnumerator @@ -205,6 +283,7 @@ namespace DCFApixels.DragonECS.Relations.Utils private int _count; private int _index; private int _next; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public LongSpanEnumerator(EcsWorld world, Node[] nodes, int startIndex, int count) { _world = world; @@ -213,15 +292,26 @@ namespace DCFApixels.DragonECS.Relations.Utils _count = count; _next = startIndex; } - public entlong Current => _world.GetEntityLong(_nodes[_index].value); + public entlong Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { + return _world.GetEntityLong(_nodes[_index].value); + } + } + object IEnumerator.Current => Current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { _index = _next; _next = _nodes[_next].next; return _index > 0 && _count-- > 0; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] void IDisposable.Dispose() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] void IEnumerator.Reset() { _index = -1; @@ -240,6 +330,7 @@ namespace DCFApixels.DragonECS.Relations.Utils public int next; /// prev node index public int prev; + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Set(int value, int prev, int next) { this.value = value;