using DCFApixels.DragonECS.Relations.Internal; using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace DCFApixels.DragonECS { [DebuggerTypeProxy(typeof(DebuggerProxy))] internal 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; #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 = 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); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int GetBasketNodesCount(int basketIndex) { return _baskets[basketIndex].count; } [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 void RemoveFromBasket(int basketIndex, int nodeIndex) { #if DEBUG if (nodeIndex <= 0) { //Throw.ArgumentOutOfRange(); } #endif ref BasketInfo basketInfo = ref _baskets[basketIndex]; if (basketInfo.count <= 4) { } ref var node = ref _nodes[nodeIndex]; int nextNode = node.next; Link(node.prev, nextNode); LinkToRecycled(nodeIndex, nodeIndex); if(basketInfo.nodeIndex == nodeIndex) { basketInfo.nodeIndex = nextNode; } 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) { 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]; LinkToRecycled(startNodeIndex, endNodeIndex); Link(startNode.prev, endNode.next); basket.count = 0; } [MethodImpl(MethodImplOptions.NoInlining)] private void UpBasketsSize(int minSize) { int newSize = GetHighBitNumber((uint)minSize) << 1; Array.Resize(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; } #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 BasketIterator this[int basketIndex] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => GetBasketIterator(basketIndex); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public BasketIterator GetBasketIterator(int basketIndex) { if (_baskets.Length <= basketIndex) { UpBasketsSize(basketIndex); } return new BasketIterator(this, basketIndex); } public readonly struct BasketIterator : IEnumerable { private readonly BasketList _basketList; private readonly int _basketIndex; public int Count { get { return _basketList._baskets[_basketIndex].count; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public BasketIterator(BasketList basketList, int basketIndex) { _basketList = basketList; _basketIndex = basketIndex; } [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 _nextNodeIndex; private int _count; [MethodImpl(MethodImplOptions.AggressiveInlining)] public Enumerator(BasketIterator iterator) { ref BasketInfo basketInfo = ref iterator._basketList._baskets[iterator._basketIndex]; _nodes = iterator._basketList._nodes; _nodeIndex = -1; _nextNodeIndex = 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 = _nextNodeIndex; _nextNodeIndex = _nodes[_nextNodeIndex].next; return _nodeIndex > 0 && _count-- > 0; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Reset() { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { } } } #endregion #region DebuggerProxy private class DebuggerProxy { private BasketList _basketList; public IEnumerable Baskets { get { List result = new List(); for (int i = 0; i < _basketList._baskets.Length; i++) { if (_basketList._baskets[i].count > 0) { result.Add(new BasketIteratorDebbugerProxy(_basketList[i])); } } return result; } } public DebuggerProxy(BasketList basketList) { _basketList = basketList; } public struct BasketIteratorDebbugerProxy { private BasketIterator _iterrator; public int Count => _iterrator.Count; public IEnumerable RelEntities { get { List result = new List(); foreach (var e in _iterrator) { result.Add(e); } return result; } } public BasketIteratorDebbugerProxy(BasketIterator iterrator) { _iterrator = iterrator; } public override string ToString() { return $"count: {_iterrator.Count}"; } } } #endregion } }