From 08b25ed671c4fd328400df3fda61d088e3de9b69 Mon Sep 17 00:00:00 2001 From: Mikhail <99481254+DCFApixels@users.noreply.github.com> Date: Sat, 3 Feb 2024 21:37:11 +0800 Subject: [PATCH] stash --- src/Builtin/EcsArcWorld.cs | 16 +- src/Collections/{EcsJoin.cs => EcsGraph.cs} | 100 ++++-------- src/EcsArc.cs | 74 +++++---- src/EcsWorldConfig.cs | 10 ++ src/EcsWorldGraph.cs | 18 +- src/Utils/ArrayUtility.cs | 172 +++++++++++++++++++- src/Utils/BasketList.cs | 19 +-- src/Utils/Exceptions.cs | 5 + src/Utils/Rel.cs | 3 +- src/Utils/SparseArray64.cs | 59 +++++-- src/Utils/UnsafeArray.cs | 119 ++++++++++++++ 11 files changed, 448 insertions(+), 147 deletions(-) rename src/Collections/{EcsJoin.cs => EcsGraph.cs} (83%) create mode 100644 src/EcsWorldConfig.cs create mode 100644 src/Utils/UnsafeArray.cs diff --git a/src/Builtin/EcsArcWorld.cs b/src/Builtin/EcsArcWorld.cs index 090272c..0cbc005 100644 --- a/src/Builtin/EcsArcWorld.cs +++ b/src/Builtin/EcsArcWorld.cs @@ -1,11 +1,21 @@ namespace DCFApixels.DragonECS { - public abstract class EcsArcWorld : EcsWorld { } + public abstract class EcsArcWorld : EcsWorld + { + public EcsArcWorld() : base(null) { } + public EcsArcWorld(IEcsWorldConfig config) : base(config) { } + } public sealed class EcsLoopArcWorld : EcsArcWorld where TWorld : EcsWorld - { } + { + public EcsLoopArcWorld() : base(null) { } + public EcsLoopArcWorld(IEcsWorldConfig config) : base(config) { } + } public sealed class EcsArcWorld : EcsArcWorld where TStartWorld : EcsWorld where TEndWorld : EcsWorld - { } + { + public EcsArcWorld() : base(null) { } + public EcsArcWorld(IEcsWorldConfig config) : base(config) { } + } } diff --git a/src/Collections/EcsJoin.cs b/src/Collections/EcsGraph.cs similarity index 83% rename from src/Collections/EcsJoin.cs rename to src/Collections/EcsGraph.cs index fef327a..45fe88e 100644 --- a/src/Collections/EcsJoin.cs +++ b/src/Collections/EcsGraph.cs @@ -2,13 +2,13 @@ using System; using System.ComponentModel; using System.Runtime.CompilerServices; -using static DCFApixels.DragonECS.Relations.Utils.EcsJoin; +using static DCFApixels.DragonECS.EcsGraph; -namespace DCFApixels.DragonECS.Relations.Utils +namespace DCFApixels.DragonECS { - public readonly ref struct EcsReadonlyJoin + public readonly ref struct EcsReadonlyGraph { - private readonly EcsJoin _source; + private readonly EcsGraph _source; #region Properties public bool IsNull => _source == null; @@ -51,7 +51,7 @@ namespace DCFApixels.DragonECS.Relations.Utils #region Constructors [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsReadonlyJoin(EcsJoin source) { _source = source; } + public EcsReadonlyGraph(EcsGraph source) { _source = source; } #endregion #region Methods @@ -67,7 +67,7 @@ namespace DCFApixels.DragonECS.Relations.Utils #region Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal EcsJoin GetSource_Internal() => _source; + internal EcsGraph GetSource_Internal() => _source; #endregion #region Other @@ -86,20 +86,20 @@ namespace DCFApixels.DragonECS.Relations.Utils #endregion } //[DebuggerTypeProxy(typeof(DebuggerProxy))] - public class EcsJoin + public class EcsGraph { private readonly EcsArc _source; private readonly bool _isLoop; private readonly BasketList _startBaskets; private readonly BasketList _endBaskets; - private readonly RelNodesInfo[] _relNodesMapping; + private RelNodesInfo[] _relNodesMapping; #region Properties - public EcsReadonlyJoin Readonly + public EcsReadonlyGraph Readonly { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return new EcsReadonlyJoin(this); } + get { return new EcsReadonlyGraph(this); } } public EcsArc Arc { @@ -140,20 +140,12 @@ namespace DCFApixels.DragonECS.Relations.Utils #endregion #region Constructors - public EcsJoin(EcsArc arc) + public EcsGraph(EcsArc arc) { _source = arc; _isLoop = arc.IsLoop; _startBaskets = new BasketList(); - //if (_isLoop) - //{ - // _endBaskets = _startBaskets; - //} - //else - //{ - // _endBaskets = new BasketList(); - //} _endBaskets = new BasketList(); _relNodesMapping = new RelNodesInfo[arc.ArcWorld.Capacity]; @@ -167,14 +159,7 @@ namespace DCFApixels.DragonECS.Relations.Utils ref RelNodesInfo arcInfo = ref _relNodesMapping[relEntityID]; arcInfo.startNodeIndex = _startBaskets.AddToBasket(startEntityID, relEntityID); - //if (_isLoop) - //{ - // arcInfo.endNodeIndex = arcInfo.startNodeIndex; - //} - //else - //{ arcInfo.endNodeIndex = _endBaskets.AddToBasket(endEntityID, relEntityID); - //} } public void Del(int relEntityID) { @@ -214,59 +199,21 @@ namespace DCFApixels.DragonECS.Relations.Utils foreach (var relEntityID in _startBaskets.GetBasketIterator(startEntityID)) { - //int endEntityID = _source.GetRelEnd(relEntityID); - //int endNodeIndex = _relNodesMapping[relEntityID].endNodeIndex; - //int revereceRelEntitiy = _endBaskets.Get(endNodeIndex); - // - ////_endBaskets.RemoveFromBasket(endEntityID, endNodeIndex); - // - //arc.ArcWorld.DelEntity(relEntityID); - //if(!_isLoop) - //{ - // arc.ArcWorld.DelEntity(revereceRelEntitiy); - //} - - arc.ArcWorld.DelEntity(relEntityID); - //if (_isLoop) - //{ - // int endNodeIndex = _relNodesMapping[relEntityID].endNodeIndex; - // int revereceRelEntitiy = _endBaskets.Get(endNodeIndex); - // arc.ArcWorld.DelEntity(revereceRelEntitiy); - //} + arc.ArcWorld.TryDelEntity(relEntityID); } - //_startBaskets.RemoveBasket(startEntityID); } [MethodImpl(MethodImplOptions.AggressiveInlining)] private void DelEndAndDelRelEntities(int endEntityID, EcsArc arc) { foreach (var relEntityID in _endBaskets.GetBasketIterator(endEntityID)) { - //int startEntityID = _source.GetRelStart(relEntityID); - //int startNodeIndex = _relNodesMapping[relEntityID].startNodeIndex; - //int revereceRelEntitiy = _startBaskets.Get(startNodeIndex); - // - ////_startBaskets.RemoveFromBasket(startEntityID, startNodeIndex); - // - //arc.ArcWorld.DelEntity(relEntityID); - //if(!_isLoop) - //{ - // arc.ArcWorld.DelEntity(revereceRelEntitiy); - //} - - arc.ArcWorld.DelEntity(relEntityID); - //if (_isLoop) - //{ - // int startNodeIndex = _relNodesMapping[relEntityID].startNodeIndex; - // int revereceRelEntitiy = _startBaskets.Get(startNodeIndex); - // arc.ArcWorld.DelEntity(revereceRelEntitiy); - //} + arc.ArcWorld.TryDelEntity(relEntityID); } - //_endBaskets.RemoveBasket(endEntityID); } public struct FriendEcsArc { - private EcsJoin _join; - public FriendEcsArc(EcsArc arc, EcsJoin join) + private EcsGraph _join; + public FriendEcsArc(EcsArc arc, EcsGraph join) { if (arc.IsInit_Internal != false) { @@ -403,7 +350,22 @@ namespace DCFApixels.DragonECS.Relations.Utils #region Operators [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static implicit operator EcsReadonlyJoin(EcsJoin a) => a.Readonly; + public static implicit operator EcsReadonlyGraph(EcsGraph a) => a.Readonly; + #endregion + + #region UpSize + public void UpArcSize(int minSize) + { + Array.Resize(ref _relNodesMapping, minSize); + } + public void UpStartSize(int minSize) + { + _startBaskets.UpBasketsSize(minSize); + } + public void UpEndSize(int minSize) + { + _endBaskets.UpBasketsSize(minSize); + } #endregion #region DebuggerProxy diff --git a/src/EcsArc.cs b/src/EcsArc.cs index 3a44d80..8256d43 100644 --- a/src/EcsArc.cs +++ b/src/EcsArc.cs @@ -1,6 +1,5 @@ using DCFApixels.DragonECS.Relations.Internal; using DCFApixels.DragonECS.Relations.Utils; -using Leopotam.EcsLite; using System; using System.Runtime.CompilerServices; @@ -23,8 +22,8 @@ namespace DCFApixels.DragonECS private readonly SparseArray64 _relationsMatrix = new SparseArray64(); - private EcsJoin _joinEntities; - private EcsJoin.FriendEcsArc _joinEntitiesFriend; + private EcsGraph _entitiesGraph; + private EcsGraph.FriendEcsArc _entitiesGraphFriend; private EcsGroup _relEntities; private RelEntityInfo[] _relEntityInfos; //N * (N - 1) / 2 @@ -63,10 +62,10 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return _relEntities.Readonly; } } - public EcsReadonlyJoin JoinEntities + public EcsReadonlyGraph EntitiesGraph { [MethodImpl(MethodImplOptions.AggressiveInlining)] - get { return _joinEntities.Readonly; } + get { return _entitiesGraph.Readonly; } } public bool IsLoop { @@ -86,8 +85,13 @@ namespace DCFApixels.DragonECS _relEntityInfos = new RelEntityInfo[arcWorld.Capacity]; - _arcWorldHandler = new ArcWorldHandler(this); + _relEntities = EcsGroup.New(_arcWorld); + _entitiesGraph = new EcsGraph(this); + + _entitiesGraphFriend = new EcsGraph.FriendEcsArc(this, _entitiesGraph); + + _arcWorldHandler = new ArcWorldHandler(this); if (_isLoop) { _loopWorldHandler = new LoopWorldHandler(this); @@ -98,11 +102,6 @@ namespace DCFApixels.DragonECS _endWorldHandler = new EndWorldHandler(this); } - - _relEntities = EcsGroup.New(_arcWorld); - _joinEntities = new EcsJoin(this); - - _joinEntitiesFriend = new EcsJoin.FriendEcsArc(this, _joinEntities); _isInit = true; } public void Destroy() @@ -117,7 +116,7 @@ namespace DCFApixels.DragonECS _startWorldHandler.Destroy(); _endWorldHandler.Destroy(); } - + } #endregion @@ -126,7 +125,7 @@ namespace DCFApixels.DragonECS { if (HasRelation(startEntityID, endEntityID)) { - Throw.UndefinedRelationException(); + Throw.RelationAlreadyExists(); } int relEntity = _arcWorld.NewEntity(); @@ -134,7 +133,7 @@ namespace DCFApixels.DragonECS _relEntityInfos[relEntity] = new RelEntityInfo(startEntityID, endEntityID); _relEntities.Add(relEntity); - _joinEntities.Add(relEntity); + _entitiesGraph.Add(relEntity); return relEntity; } public void DelRelation(int startEntityID, int endEntityID) @@ -147,17 +146,6 @@ namespace DCFApixels.DragonECS { Throw.UndefinedRelationException(); } - //if (!_relationsMatrix.TryGetValue(startEntityID, endEntityID, out int relEntity)) - //{ - // Throw.UndefinedRelationException(); - //} - //_joinEntities.Del(relEntity); - // - //_relationsMatrix.Remove(startEntityID, endEntityID); - //_arcWorld.DelEntity(relEntity); - // - //_relEntityInfos[relEntity] = RelEntityInfo.Empty; - //_relEntities.Remove(relEntity); } private void ClearRelation_Internal(int startEntityID, int endEntityID) @@ -165,7 +153,7 @@ namespace DCFApixels.DragonECS if (_relationsMatrix.TryGetValue(startEntityID, endEntityID, out int relEntity)) { _relEntities.Remove(relEntity); - _joinEntities.Del(relEntity); + _entitiesGraph.Del(relEntity); _relationsMatrix.Remove(startEntityID, endEntityID); _relEntityInfos[relEntity] = RelEntityInfo.Empty; } @@ -273,6 +261,7 @@ namespace DCFApixels.DragonECS public void OnWorldResize(int arcWorldNewSize) { Array.Resize(ref _arc._relEntityInfos, arcWorldNewSize); + _arc._entitiesGraph.UpArcSize(arcWorldNewSize); } #endregion } @@ -283,6 +272,7 @@ namespace DCFApixels.DragonECS { _arc = arc; _arc.StartWorld.AddListener(this); + OnWorldResize(_arc.StartWorld.Capacity); } public void Destroy() { @@ -293,12 +283,15 @@ namespace DCFApixels.DragonECS { foreach (var startEntityID in startEntityBuffer) { - _arc._joinEntitiesFriend.DelStartAndDelRelEntities(startEntityID, _arc); + _arc._entitiesGraphFriend.DelStartAndDelRelEntities(startEntityID, _arc); } - _arc._arcWorld.ReleaseDelEntityBuffer(startEntityBuffer.Length); + _arc._arcWorld.ReleaseDelEntityBufferAll(); } public void OnWorldDestroy() { } - public void OnWorldResize(int startWorldNewSize) { } + public void OnWorldResize(int startWorldNewSize) + { + _arc._entitiesGraph.UpStartSize(startWorldNewSize); + } #endregion } private class EndWorldHandler : IEcsWorldEventListener @@ -308,6 +301,7 @@ namespace DCFApixels.DragonECS { _arc = arc; _arc.EndWorld.AddListener(this); + OnWorldResize(_arc.EndWorld.Capacity); } public void Destroy() { @@ -318,12 +312,15 @@ namespace DCFApixels.DragonECS { foreach (var endEntityID in endEntityBuffer) { - _arc._joinEntitiesFriend.DelEndAndDelRelEntities(endEntityID, _arc); + _arc._entitiesGraphFriend.DelEndAndDelRelEntities(endEntityID, _arc); } - _arc._arcWorld.ReleaseDelEntityBuffer(endEntityBuffer.Length); + _arc._arcWorld.ReleaseDelEntityBufferAll(); } public void OnWorldDestroy() { } - public void OnWorldResize(int endWorldNewSize) { } + public void OnWorldResize(int endWorldNewSize) + { + _arc._entitiesGraph.UpEndSize(endWorldNewSize); + } #endregion } private class LoopWorldHandler : IEcsWorldEventListener @@ -333,6 +330,7 @@ namespace DCFApixels.DragonECS { _arc = arc; _arc.StartWorld.AddListener(this); + OnWorldResize(_arc.StartWorld.Capacity); } public void Destroy() { @@ -343,13 +341,17 @@ namespace DCFApixels.DragonECS { foreach (var startEntityID in startEntityBuffer) { - _arc._joinEntitiesFriend.DelStartAndDelRelEntities(startEntityID, _arc); - _arc._joinEntitiesFriend.DelEndAndDelRelEntities(startEntityID, _arc); + _arc._entitiesGraphFriend.DelStartAndDelRelEntities(startEntityID, _arc); + _arc._entitiesGraphFriend.DelEndAndDelRelEntities(startEntityID, _arc); } - _arc._arcWorld.ReleaseDelEntityBuffer(startEntityBuffer.Length); + _arc._arcWorld.ReleaseDelEntityBufferAll(); } public void OnWorldDestroy() { } - public void OnWorldResize(int startWorldNewSize) { } + public void OnWorldResize(int startWorldNewSize) + { + _arc._entitiesGraph.UpStartSize(startWorldNewSize); + _arc._entitiesGraph.UpEndSize(startWorldNewSize); + } #endregion } #endregion diff --git a/src/EcsWorldConfig.cs b/src/EcsWorldConfig.cs new file mode 100644 index 0000000..4b29540 --- /dev/null +++ b/src/EcsWorldConfig.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace DCFApixels.DragonECS +{ + public static class EcsWorldConfigRelationsExtensions + { + + } +} diff --git a/src/EcsWorldGraph.cs b/src/EcsWorldGraph.cs index 8ec4186..804233c 100644 --- a/src/EcsWorldGraph.cs +++ b/src/EcsWorldGraph.cs @@ -6,7 +6,7 @@ namespace DCFApixels.DragonECS { public static class EcsWorldGraph { - private static readonly SparseArray64 _matrix = new SparseArray64(4); + private static readonly SparseArray _matrix = new SparseArray(4); private static EcsArc[] _arcsMapping = new EcsArc[4]; #region Register/Unregister @@ -103,7 +103,7 @@ namespace DCFApixels.DragonECS } - public static EcsArc SetLoopArcAuto(this TWorld self, out EcsLoopArcWorld arcWorld) + public static EcsArc SetLoopArcAuto(this TWorld self, out EcsLoopArcWorld arcWorld, IEcsWorldConfig config = null) where TWorld : EcsWorld { if (self == null) @@ -114,10 +114,10 @@ namespace DCFApixels.DragonECS { EcsDebug.PrintWarning($"{nameof(TWorld)} is not {self.GetType().Name}"); } - arcWorld = new EcsLoopArcWorld(); + arcWorld = new EcsLoopArcWorld(config); return Register(self, self, arcWorld); } - public static EcsArc SetArcAuto(this TStartWorld start, TEndWorld end, out EcsArcWorld arcWorld) + public static EcsArc SetArcAuto(this TStartWorld start, TEndWorld end, out EcsArcWorld arcWorld, IEcsWorldConfig config = null) where TStartWorld : EcsWorld where TEndWorld : EcsWorld { @@ -129,19 +129,19 @@ namespace DCFApixels.DragonECS { EcsDebug.PrintWarning($"{nameof(TStartWorld)} is not {start.GetType().Name} or {nameof(TEndWorld)} is not {end.GetType().Name}"); } - arcWorld = new EcsArcWorld(); + arcWorld = new EcsArcWorld(config); return Register(start, end, arcWorld); } - public static EcsArc SetLoopArcAuto(this TWorld self) + public static EcsArc SetLoopArcAuto(this TWorld self, IEcsWorldConfig config = null) where TWorld : EcsWorld { - return SetLoopArcAuto(self, out _); + return SetLoopArcAuto(self, out _, config); } - public static EcsArc SetArcAuto(this TStartWorld start, TEndWorld end) + public static EcsArc SetArcAuto(this TStartWorld start, TEndWorld end, IEcsWorldConfig config = null) where TStartWorld : EcsWorld where TEndWorld : EcsWorld { - return SetArcAuto(start, end, out _); + return SetArcAuto(start, end, out _, config); } public static EcsArc SetLoopArc(this EcsWorld self, EcsArcWorld arc) => SetArc(self, self, arc); diff --git a/src/Utils/ArrayUtility.cs b/src/Utils/ArrayUtility.cs index 19d26b1..b204bff 100644 --- a/src/Utils/ArrayUtility.cs +++ b/src/Utils/ArrayUtility.cs @@ -1,15 +1,185 @@ -namespace DCFApixels.DragonECS.Relations.Utils +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +namespace DCFApixels.DragonECS.Relations.Internal { internal static class ArrayUtility { + 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 static int NormalizeSizeToPowerOfTwo(int minSize) + { + return 1 << (GetHighBitNumber((uint)minSize - 1u) + 1); + } public static void Fill(T[] array, T value, int startIndex = 0, int length = -1) { if (length < 0) + { length = array.Length; + } else + { length = startIndex + length; + } for (int i = startIndex; i < length; i++) + { array[i] = value; + } + } + } + internal readonly struct EnumerableInt : IEnumerable + { + public readonly int start; + public readonly int length; + private EnumerableInt(int start, int length) + { + this.start = start; + this.length = length; + } + public static EnumerableInt Range(int start, int length) => new EnumerableInt(start, length); + public static EnumerableInt StartEnd(int start, int end) => new EnumerableInt(start, end - start); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() => new Enumerator(start, start + length); + public struct Enumerator : IEnumerator + { + private readonly int _max; + private int _current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(int max, int current) + { + _max = max; + _current = current - 1; + } + public int Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _current; + } + object IEnumerator.Current => Current; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() => ++_current < _max; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() { } + } + } + internal static unsafe class UnmanagedArrayUtility + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T* New(int capacity) where T : unmanaged + { + return (T*)Marshal.AllocHGlobal(Marshal.SizeOf() * capacity).ToPointer(); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void New(out T* ptr, int capacity) where T : unmanaged + { + ptr = (T*)Marshal.AllocHGlobal(Marshal.SizeOf(default) * capacity).ToPointer(); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T* NewAndInit(int capacity) where T : unmanaged + { + int newSize = Marshal.SizeOf(typeof(T)) * capacity; + byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer(); + + for (int i = 0; i < newSize; i++) + *(newPointer + i) = 0; + + return (T*)newPointer; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void NewAndInit(out T* ptr, int capacity) where T : unmanaged + { + int newSize = Marshal.SizeOf(typeof(T)) * capacity; + byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer(); + + for (int i = 0; i < newSize; i++) + *(newPointer + i) = 0; + + ptr = (T*)newPointer; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Free(void* pointer) + { + Marshal.FreeHGlobal(new IntPtr(pointer)); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Free(ref T* pointer, ref int length) where T : unmanaged + { + Marshal.FreeHGlobal(new IntPtr(pointer)); + pointer = null; + length = 0; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T* Clone(T* sourcePtr, int length) where T : unmanaged + { + T* clone = New(length); + for (int i = 0; i < length; i++) + { + clone[i] = sourcePtr[i]; + } + return clone; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T* Resize(void* oldPointer, int newCount) where T : unmanaged + { + return (T*)(Marshal.ReAllocHGlobal( + new IntPtr(oldPointer), + new IntPtr(Marshal.SizeOf(default) * newCount))).ToPointer(); + } + } + + public static class CollectionUtility + { + public static string EntitiesToString(IEnumerable range, string name) + { + return $"{name}({range.Count()}) {{{string.Join(", ", range.OrderBy(o => o))}}})"; + } + public static string AutoToString(IEnumerable range, string name) + { + return $"{name}({range.Count()}) {{{string.Join(", ", range.Select(o => o.ToString()))}}})"; } } } \ No newline at end of file diff --git a/src/Utils/BasketList.cs b/src/Utils/BasketList.cs index e5451cf..ebcb1a8 100644 --- a/src/Utils/BasketList.cs +++ b/src/Utils/BasketList.cs @@ -1,9 +1,7 @@ -using DCFApixels.DragonECS.Relations.Internal; -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -102,7 +100,7 @@ namespace DCFApixels.DragonECS Link(node.prev, nextNode); LinkToRecycled(nodeIndex, nodeIndex); - if(basketInfo.nodeIndex == nodeIndex) + if (basketInfo.nodeIndex == nodeIndex) { basketInfo.nodeIndex = nextNode; } @@ -192,10 +190,13 @@ namespace DCFApixels.DragonECS } [MethodImpl(MethodImplOptions.NoInlining)] - private void UpBasketsSize(int minSize) + public void UpBasketsSize(int minSize) { - int newSize = GetHighBitNumber((uint)minSize) << 1; - Array.Resize(ref _baskets, newSize); + if (minSize > _baskets.Length) + { + int newSize = 1 << (GetHighBitNumber((uint)minSize - 1) + 1); + Array.Resize(ref _baskets, newSize); + } } private static int GetHighBitNumber(uint bits) { @@ -271,10 +272,6 @@ namespace DCFApixels.DragonECS [MethodImpl(MethodImplOptions.AggressiveInlining)] public BasketIterator GetBasketIterator(int basketIndex) { - if (_baskets.Length <= basketIndex) - { - UpBasketsSize(basketIndex); - } return new BasketIterator(this, basketIndex); } public readonly struct BasketIterator : IEnumerable diff --git a/src/Utils/Exceptions.cs b/src/Utils/Exceptions.cs index df7fe8e..ace14c7 100644 --- a/src/Utils/Exceptions.cs +++ b/src/Utils/Exceptions.cs @@ -26,6 +26,11 @@ namespace DCFApixels.DragonECS.Relations.Internal { throw new EcsRelationException(); } + [MethodImpl(MethodImplOptions.NoInlining)] + internal static void RelationAlreadyExists() + { + throw new EcsRelationException("This relation already exists."); + } } } diff --git a/src/Utils/Rel.cs b/src/Utils/Rel.cs index ae3e094..92b97ca 100644 --- a/src/Utils/Rel.cs +++ b/src/Utils/Rel.cs @@ -1,5 +1,4 @@ -using System; -using System.Runtime.CompilerServices; +using System.Runtime.CompilerServices; namespace DCFApixels.DragonECS { diff --git a/src/Utils/SparseArray64.cs b/src/Utils/SparseArray64.cs index f0b6a4e..d4c76e3 100644 --- a/src/Utils/SparseArray64.cs +++ b/src/Utils/SparseArray64.cs @@ -1,6 +1,7 @@ //SparseArray64. Analogous to Dictionary, but faster. //Benchmark result of indexer.get speed test with 300 elements: //[Dictinary: 6.705us] [SparseArray64: 2.512us]. +using DCFApixels.DragonECS.Relations.Internal; using System; using System.Diagnostics; using System.Diagnostics.Contracts; @@ -9,14 +10,15 @@ using System.Runtime.InteropServices; namespace DCFApixels.DragonECS.Relations.Utils { - internal class SparseArray64 + internal unsafe class SparseArray64 + where TValue : unmanaged { public const int MIN_CAPACITY_BITS_OFFSET = 4; public const int MIN_CAPACITY = 1 << MIN_CAPACITY_BITS_OFFSET; private const int EMPTY = -1; - private int[] _buckets = Array.Empty(); - private Entry[] _entries = Array.Empty(); + private UnsafeArray _buckets; + private UnsafeArray _entries; private int _count; @@ -28,26 +30,45 @@ namespace DCFApixels.DragonECS.Relations.Utils #region Properties public TValue this[long keyX, long keyY] { - get => _entries[FindEntry(keyX + (keyY << 32))].value; - set => Insert(keyX + (keyY << 32), value); + get + { + //TODO Проверить необходимость проверки на Null + return _entries.ptr[FindEntry(keyX + (keyY << 32))].value; + } + set + { + Insert(keyX + (keyY << 32), value); + } } public TValue this[long key] { - get => _entries[FindEntry(key)].value; - set => Insert(key, value); + get + { + //TODO Проверить необходимость проверки на Null + return _entries.ptr[FindEntry(key)].value; + } + set + { + Insert(key, value); + } } - public int Count => _count; + public int Count + { + get { return _count; } + } #endregion #region Constructors public SparseArray64(int minCapacity = MIN_CAPACITY) { minCapacity = NormalizeCapacity(minCapacity); - _buckets = new int[minCapacity]; + _buckets = new UnsafeArray(minCapacity); for (int i = 0; i < minCapacity; i++) + { _buckets[i] = EMPTY; - _entries = new Entry[minCapacity]; + } + _entries = new UnsafeArray(minCapacity); _modBitMask = (minCapacity - 1) & 0x7FFFFFFF; } #endregion @@ -148,7 +169,7 @@ namespace DCFApixels.DragonECS.Relations.Utils value = default; return false; } - value = _entries[index].value; + value = _entries.ptr[index].value; return true; } public bool TryGetValue(long keyX, long keyY, out TValue value) @@ -159,7 +180,7 @@ namespace DCFApixels.DragonECS.Relations.Utils value = default; return false; } - value = _entries[index].value; + value = _entries.ptr[index].value; return true; } #endregion @@ -184,7 +205,10 @@ namespace DCFApixels.DragonECS.Relations.Utils { _buckets[i] = -1; } - Array.Clear(_entries, 0, _count); + for (int i = 0; i < _count; i++) + { + _entries[i] = default; + } _count = 0; } } @@ -197,12 +221,15 @@ namespace DCFApixels.DragonECS.Relations.Utils _modBitMask = (newSize - 1) & 0x7FFFFFFF; Contract.Assert(newSize >= _entries.Length); - int[] newBuckets = new int[newSize]; + UnsafeArray newBuckets = new UnsafeArray(newSize); for (int i = 0; i < newBuckets.Length; i++) newBuckets[i] = EMPTY; - Entry[] newEntries = new Entry[newSize]; - Array.Copy(_entries, 0, newEntries, 0, _count); + UnsafeArray newEntries = UnsafeArray.Resize(_entries, newSize); + //UnsafeArray newEntries = new UnsafeArray(newSize); + //Array.Copy(_entries, 0, newEntries, 0, _count); + + for (int i = 0; i < _count; i++) { if (newEntries[i].hashKey >= 0) diff --git a/src/Utils/UnsafeArray.cs b/src/Utils/UnsafeArray.cs new file mode 100644 index 0000000..9cf4c22 --- /dev/null +++ b/src/Utils/UnsafeArray.cs @@ -0,0 +1,119 @@ +using DCFApixels.DragonECS.Utils; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.CompilerServices; + +namespace DCFApixels.DragonECS.Relations.Internal +{ + [DebuggerTypeProxy(typeof(UnsafeArray<>.DebuggerProxy))] + internal unsafe struct UnsafeArray : IDisposable, IEnumerable + where T : unmanaged + { + internal T* ptr; + internal int Length; + + public ref T this[int index] + { + get { return ref ptr[index]; } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UnsafeArray(int length) + { + UnmanagedArrayUtility.New(out ptr, length); + Length = length; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UnsafeArray(int length, bool isInit) + { + UnmanagedArrayUtility.NewAndInit(out ptr, length); + Length = length; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private UnsafeArray(T* ptr, int length) + { + this.ptr = ptr; + Length = length; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public UnsafeArray Clone() + { + return new UnsafeArray(UnmanagedArrayUtility.Clone(ptr, Length), Length); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() + { + UnmanagedArrayUtility.Free(ref ptr, ref Length); + } + public override string ToString() + { + T* ptr = this.ptr; + return CollectionUtility.AutoToString(EnumerableInt.Range(0, Length).Select(i => ptr[i]), "ua"); + } + + public static void Resize(ref UnsafeArray array, int newSize) + { + array.ptr = UnmanagedArrayUtility.Resize(array.ptr, newSize); + array.Length = newSize; + } + public static UnsafeArray Resize(UnsafeArray array, int newSize) + { + return new UnsafeArray(UnmanagedArrayUtility.Resize(array.ptr, newSize), newSize); + } + + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator GetEnumerator() => new Enumerator(ptr, Length); + public struct Enumerator : IEnumerator + { + private readonly T* _ptr; + private readonly int _length; + private int _index; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Enumerator(T* ptr, int length) + { + _ptr = ptr; + _length = length; + _index = -1; + } + public T Current + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get => _ptr[_index]; + } + object IEnumerator.Current => Current; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool MoveNext() => ++_index < _length; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Reset() { } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void Dispose() { } + } + + internal class DebuggerProxy + { + public T[] elements; + public int length; + public DebuggerProxy(UnsafeArray instance) + { + elements = EnumerableInt.Range(0, instance.Length).Select(i => instance.ptr[i]).ToArray(); + length = instance.Length; + } + } + } + + internal static class UnsafeArrayExtentions + { + public static void Resize(ref UnsafeArray self, int newSize) + where T : unmanaged + { + UnsafeArray.Resize(ref self, newSize); + } + } +}