diff --git a/src/Collections/EcsGroup.cs b/src/Collections/EcsGroup.cs index ee7fd30..857694f 100644 --- a/src/Collections/EcsGroup.cs +++ b/src/Collections/EcsGroup.cs @@ -196,7 +196,7 @@ namespace DCFApixels.DragonECS { if (_groupSparsePagePoolCount <= 0) { - return MemoryAllocator.AllocAndInit(EcsGroup.PAGE_SIZE).As(); + return MemoryAllocator.AllocAndInit(EcsGroup.PAGE_SIZE).Ptr; } var takedPage = _groupSparsePagePool[--_groupSparsePagePoolCount]; _groupSparsePagePool[_groupSparsePagePoolCount] = MemoryAllocator.Handler.Empty; @@ -228,7 +228,7 @@ namespace DCFApixels.DragonECS for (int i = 0; i < _groupSparsePagePoolCount; i++) { ref var page = ref _groupSparsePagePool[i]; - if (page.IsEmpty == false) + if (page.IsCreated) { MemoryAllocator.FreeAndClear(ref page); } @@ -291,7 +291,7 @@ namespace DCFApixels.DragonECS private int _count = 0; internal bool _isReleased = true; - internal static readonly int* _nullPage = MemoryAllocator.AllocAndInit(PageSlot.SIZE).As(); + internal static readonly int* _nullPage = MemoryAllocator.AllocAndInit(PageSlot.SIZE).Ptr; internal static readonly long _nullPagePtrFake = (long)_nullPage; #region Properties @@ -344,17 +344,6 @@ namespace DCFApixels.DragonECS #endif return _dense[++index]; } - // [MethodImpl(MethodImplOptions.AggressiveInlining)] - // set - // { - // // TODO добавить лок енумератора на изменение - //#if DEBUG || DRAGONECS_STABILITY_MODE - // if (index < 0 || index >= Count) { Throw.ArgumentOutOfRange(); } - //#endif - // var oldValue = _dense[index]; - // _dense[index] = value; - // _sparse[oldValue] = 0; - // } } #endregion @@ -394,7 +383,7 @@ namespace DCFApixels.DragonECS page.IndexesXOR = 0; page.Count = 0; } - _sparsePagesHandler.Dispose(); + _sparsePagesHandler.DisposeAndReset(); } } public void Dispose() @@ -555,7 +544,6 @@ namespace DCFApixels.DragonECS ref PageSlot page = ref _sparsePages[i]; if (page.Indexes != _nullPage) { - //TODO тут надо оптимизировать отчисткой не всего а по dense списку for (int j = 0; j < PageSlot.SIZE; j++) { page.Indexes[j] = 0; diff --git a/src/Collections/EcsSpan.cs b/src/Collections/EcsSpan.cs index d68ee27..a6d01ca 100644 --- a/src/Collections/EcsSpan.cs +++ b/src/Collections/EcsSpan.cs @@ -1,6 +1,7 @@ #if DISABLE_DEBUG #undef DEBUG #endif +using DCFApixels.DragonECS.Core; using DCFApixels.DragonECS.Core.Internal; using DCFApixels.DragonECS.Core.Unchecked; using System; @@ -127,6 +128,7 @@ namespace DCFApixels.DragonECS #endregion #region Other + public ReadOnlySpan AsSystemSpan() { return _values; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int First() { return _values[0]; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -170,6 +172,7 @@ namespace DCFApixels.DragonECS _worldID = span._worldID; } public DebuggerProxy(EcsLongsSpan span) : this(span.ToSpan()) { } + public DebuggerProxy(EcsUnsafeSpan span) : this(span.ToSpan()) { } } #endregion } @@ -316,4 +319,165 @@ namespace DCFApixels.DragonECS #pragma warning restore CS0809 // Устаревший член переопределяет неустаревший член #endregion } +} + +namespace DCFApixels.DragonECS.Core +{ +#if ENABLE_IL2CPP + using Unity.IL2CPP.CompilerServices; + [Il2CppSetOption(Option.NullChecks, false)] + [Il2CppSetOption(Option.ArrayBoundsChecks, false)] +#endif + [DebuggerTypeProxy(typeof(EcsSpan.DebuggerProxy))] + public unsafe readonly struct EcsUnsafeSpan + { + private readonly int* _values; + private readonly int _length; + private readonly short _worldID; + + #region Properties + public bool IsNull + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _worldID == 0; } + } + public short WorldID + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _worldID; } + } + public EcsWorld World + { + get { return EcsWorld.GetWorld(_worldID); } + } + public int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _length; } + } + public EcsLongsSpan Longs + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return ToSpan().Longs; } + } + public bool IsSourceEntities + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return ToSpan() == EcsWorld.GetWorld(_worldID).GetCurrentEntities_Internal(); } + } + + public int this[int index] + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if DEBUG + if ((uint)index >= (uint)_length || (uint)index < 0) + { + ThrowHelper.ThrowIndexOutOfRangeException(); + } +#elif DRAGONECS_STABILITY_MODE + return EcsConsts.NULL_ENTITY_ID; +#endif + return _values[index]; + } + } + #endregion + + #region Constructors + internal EcsUnsafeSpan(short worldID, int* array, int length) + { + _worldID = worldID; + _values = array; + _length = length; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal EcsUnsafeSpan(short worldID, int* array, int start, int length) + { + _worldID = worldID; + _values = array + start; + _length = length; + } + #endregion + + #region Slice/ToArray + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EcsUnsafeSpan Slice(int start) + { + if ((uint)start > (uint)_length) + { + ThrowHelper.ThrowArgumentOutOfRangeException(); + } + return new EcsUnsafeSpan(_worldID, _values, start, _length - start); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EcsUnsafeSpan Slice(int start, int length) + { + if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) + { + ThrowHelper.ThrowArgumentOutOfRangeException(); + } + return new EcsUnsafeSpan(_worldID, _values, start, length); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public EcsSpan ToSpan() { return new EcsSpan(_worldID, new ReadOnlySpan(_values, _length)); } + public int[] ToArray() { return new ReadOnlySpan(_values, _length).ToArray(); } + public int ToArray(ref int[] dynamicBuffer) + { + if (dynamicBuffer.Length < _length) + { + Array.Resize(ref dynamicBuffer, ArrayUtility.CeilPow2(_length)); + } + int i = 0; + foreach (var e in this) + { + dynamicBuffer[i++] = e; + } + return i; + } + public void ToCollection(ICollection collection) + { + foreach (var e in this) + { + collection.Add(e); + } + } + #endregion + + #region operators + public static bool operator ==(EcsUnsafeSpan left, EcsUnsafeSpan right) { return left._values == right._values; } + public static bool operator !=(EcsUnsafeSpan left, EcsUnsafeSpan right) { return left._values != right._values; } + public static implicit operator EcsSpan(EcsUnsafeSpan a) { return a.ToSpan(); } + #endregion + + #region Enumerator + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public ReadOnlySpan.Enumerator GetEnumerator() { return new ReadOnlySpan(_values, _length).GetEnumerator(); } + #endregion + + #region Other + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int First() { return _values[0]; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int Last() { return _values[_length - 1]; } + public override string ToString() + { + return CollectionUtility.EntitiesToString(ToArray(), "span"); + } + public override bool Equals(object obj) + { + return obj is EcsUnsafeSpan other && other == this; + } + public override int GetHashCode() + { + return *_values ^ _length ^ (_worldID << 16); + } + private static class ThrowHelper + { + public static void ThrowIndexOutOfRangeException() => throw new IndexOutOfRangeException(); + public static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(); + public static void ThrowInvalidOperationException() => throw new InvalidOperationException(); + } + #endregion + } } \ No newline at end of file diff --git a/src/EcsMask.cs b/src/EcsMask.cs index 8871d00..f5653b2 100644 --- a/src/EcsMask.cs +++ b/src/EcsMask.cs @@ -615,8 +615,8 @@ namespace DCFApixels.DragonECS } private void Cleanup(bool disposing) { - _bufferHandler.Dispose(); - _chunckBufferHandler.Dispose(); + _bufferHandler.DisposeAndReset(); + _chunckBufferHandler.DisposeAndReset(); } #endregion @@ -644,8 +644,7 @@ namespace DCFApixels.DragonECS if (_sortIncChunckBuffer.Length > 1) { - var comparer = new IncCountComparer(counts); - UnsafeArraySortHalperX.InsertionSort(sortIncBuffer.ptr, sortIncBuffer.Length, ref comparer); + sortIncBuffer.AsSpan().Sort(new IncCountComparer(counts)); ConvertToChuncks(preSortingBuffer, sortIncBuffer, _sortIncChunckBuffer); } if (_sortIncChunckBuffer.Length > 0) @@ -659,8 +658,7 @@ namespace DCFApixels.DragonECS if (_sortExcChunckBuffer.Length > 1) { - ExcCountComparer comparer = new ExcCountComparer(counts); - UnsafeArraySortHalperX.InsertionSort(sortExcBuffer.ptr, sortExcBuffer.Length, ref comparer); + sortExcBuffer.AsSpan().Sort(new ExcCountComparer(counts)); ConvertToChuncks(preSortingBuffer, sortExcBuffer, _sortExcChunckBuffer); } // Выражение IncCount < (AllEntitesCount - ExcCount) мало вероятно будет истинным. @@ -670,8 +668,7 @@ namespace DCFApixels.DragonECS if (_sortAnyChunckBuffer.Length > 1) { - ExcCountComparer comparer = new ExcCountComparer(counts); - UnsafeArraySortHalperX.InsertionSort(sortAnyBuffer.ptr, sortAnyBuffer.Length, ref comparer); + sortAnyBuffer.AsSpan().Sort(new ExcCountComparer(counts)); ConvertToChuncks(preSortingBuffer, sortAnyBuffer, _sortAnyChunckBuffer); } // Any не влияет на maxEntites если есть Inc и сложно высчитывается если нет Inc @@ -699,9 +696,13 @@ namespace DCFApixels.DragonECS #endregion #region IterateTo - //TODO Перемеиноваться в CacheTo + public EcsMaskFlags MaskFlags + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return _maskFlags; } + } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void IterateTo(EcsSpan source, EcsGroup group) + public void CacheTo(EcsSpan source, EcsGroup group) { switch (_maskFlags) { @@ -709,7 +710,7 @@ namespace DCFApixels.DragonECS group.CopyFrom(source); break; case EcsMaskFlags.Inc: - IterateOnlyInc(source).CopyTo(group); + IterateOnlyInc(source).CacheTo(group); break; case EcsMaskFlags.Exc: case EcsMaskFlags.Any: @@ -717,7 +718,7 @@ namespace DCFApixels.DragonECS case EcsMaskFlags.IncAny: case EcsMaskFlags.ExcAny: case EcsMaskFlags.IncExcAny: - Iterate(source).CopyTo(group); + Iterate(source).CacheTo(group); break; case EcsMaskFlags.Broken: group.Clear(); @@ -728,21 +729,21 @@ namespace DCFApixels.DragonECS } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int IterateTo(EcsSpan source, ref int[] array) + public int CacheTo(EcsSpan source, ref int[] array) { switch (_maskFlags) { case EcsMaskFlags.Empty: return source.ToArray(ref array); case EcsMaskFlags.Inc: - return IterateOnlyInc(source).CopyTo(ref array); + return IterateOnlyInc(source).CacheTo(ref array); case EcsMaskFlags.Exc: case EcsMaskFlags.Any: case EcsMaskFlags.IncExc: case EcsMaskFlags.IncAny: case EcsMaskFlags.ExcAny: case EcsMaskFlags.IncExcAny: - return Iterate(source).CopyTo(ref array); + return Iterate(source).CacheTo(ref array); case EcsMaskFlags.Broken: return new EcsSpan(World.ID, Array.Empty()).ToArray(ref array); default: @@ -772,7 +773,7 @@ namespace DCFApixels.DragonECS #region CopyTo [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyTo(EcsGroup group) + public void CacheTo(EcsGroup group) { group.Clear(); var enumerator = GetEnumerator(); @@ -782,7 +783,7 @@ namespace DCFApixels.DragonECS } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int CopyTo(ref int[] array) + public int CacheTo(ref int[] array) { int count = 0; var enumerator = GetEnumerator(); @@ -928,7 +929,7 @@ namespace DCFApixels.DragonECS #region CopyTo [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void CopyTo(EcsGroup group) + public void CacheTo(EcsGroup group) { group.Clear(); var enumerator = GetEnumerator(); @@ -938,7 +939,7 @@ namespace DCFApixels.DragonECS } } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public int CopyTo(ref int[] array) + public int CacheTo(ref int[] array) { int count = 0; var enumerator = GetEnumerator(); diff --git a/src/EcsWorld.cs b/src/EcsWorld.cs index b68ca3d..c511d9d 100644 --- a/src/EcsWorld.cs +++ b/src/EcsWorld.cs @@ -698,7 +698,7 @@ namespace DCFApixels.DragonECS } else { - poolIdsPtr = UnmanagedArrayUtility.New(count); + poolIdsPtr = MemoryAllocator.Alloc(count).Ptr; } UnsafeArray ua = UnsafeArray.Manual(poolIdsPtr, count); @@ -711,7 +711,7 @@ namespace DCFApixels.DragonECS if (count >= BUFFER_THRESHOLD) { - UnmanagedArrayUtility.Free(poolIdsPtr); + MemoryAllocator.Free(poolIdsPtr); } @@ -748,7 +748,7 @@ namespace DCFApixels.DragonECS } else { - poolIdsPtr = UnmanagedArrayUtility.New(count); + poolIdsPtr = MemoryAllocator.Alloc(count).Ptr; } GetComponentTypeIDsFor_Internal(fromEntityID, poolIdsPtr, count); @@ -759,7 +759,7 @@ namespace DCFApixels.DragonECS if (count >= BUFFER_THRESHOLD) { - UnmanagedArrayUtility.Free(poolIdsPtr); + MemoryAllocator.Free(poolIdsPtr); } //foreach (var pool in _pools) @@ -1082,7 +1082,7 @@ namespace DCFApixels.DragonECS } else { - poolIdsPtr = UnmanagedArrayUtility.New(count); + poolIdsPtr = MemoryAllocator.Alloc(count).Ptr; } GetComponentTypeIDsFor_Internal(entityID, poolIdsPtr, count); @@ -1105,7 +1105,7 @@ namespace DCFApixels.DragonECS if (count >= BUFFER_THRESHOLD) { - UnmanagedArrayUtility.Free(poolIdsPtr); + MemoryAllocator.Free(poolIdsPtr); } } public ReadOnlySpan GetComponentsFor(int entityID) diff --git a/src/EcsWorld.static.cs b/src/EcsWorld.static.cs index b53904a..5e18422 100644 --- a/src/EcsWorld.static.cs +++ b/src/EcsWorld.static.cs @@ -5,7 +5,6 @@ using DCFApixels.DragonECS.Core; using DCFApixels.DragonECS.Core.Internal; using System; using System.Collections.Generic; -using System.ComponentModel; using System.Linq; using System.Runtime.CompilerServices; @@ -31,6 +30,10 @@ namespace DCFApixels.DragonECS private StructList _worldComponentPools; private int _builtinWorldComponentsCount = 0; + public static int AllWorldsCount + { + get { return _worldIdDispenser.Count; } + } static EcsWorld() { _worlds[NULL_WORLD_ID] = new NullWorld(); diff --git a/src/Executors/EcsWhereExecutor.cs b/src/Executors/EcsWhereExecutor.cs index 9b94458..e0949ba 100644 --- a/src/Executors/EcsWhereExecutor.cs +++ b/src/Executors/EcsWhereExecutor.cs @@ -3,6 +3,7 @@ #endif using System; using System.Runtime.CompilerServices; +using static DCFApixels.DragonECS.Core.Internal.MemoryAllocator; #if ENABLE_IL2CPP using Unity.IL2CPP.CompilerServices; #endif @@ -13,13 +14,13 @@ namespace DCFApixels.DragonECS.Core.Internal [Il2CppSetOption(Option.NullChecks, false)] [Il2CppSetOption(Option.ArrayBoundsChecks, false)] #endif - internal sealed class EcsWhereExecutor : MaskQueryExecutor + internal sealed unsafe class EcsWhereExecutor : MaskQueryExecutor { private EcsMaskIterator _iterator; - private int[] _filteredAllEntities = new int[32]; + private HMem _filteredAllEntities = Alloc(32); private int _filteredAllEntitiesCount = 0; - private int[] _filteredEntities = null; + private HMem _filteredEntities = default; private int _filteredEntitiesCount = 0; private long _version; @@ -54,6 +55,14 @@ namespace DCFApixels.DragonECS.Core.Internal protected sealed override void OnDestroy() { if (_isDestroyed) { return; } + if (_filteredAllEntities.IsCreated) + { + _filteredAllEntities.DisposeAndReset(); + } + if (_filteredEntities.IsCreated) + { + _filteredEntities.DisposeAndReset(); + } _isDestroyed = true; _versionsChecker.Dispose(); } @@ -67,7 +76,7 @@ namespace DCFApixels.DragonECS.Core.Internal if (_versionsChecker.CheckAndNext() == false) { _version++; - _filteredAllEntitiesCount = _iterator.IterateTo(World.Entities, ref _filteredAllEntities); + _filteredAllEntitiesCount = _iterator.CacheTo(World.Entities, ref _filteredAllEntities); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -77,19 +86,19 @@ namespace DCFApixels.DragonECS.Core.Internal if (span.IsNull) { Throw.ArgumentNull(nameof(span)); } if (span.WorldID != World.ID) { Throw.Quiery_ArgumentDifferentWorldsException(); } #endif - if (_filteredEntities == null) + if (_filteredEntities.IsCreated == false) { - _filteredEntities = new int[32]; + _filteredEntities = Alloc(32); } - _filteredEntitiesCount = _iterator.IterateTo(span, ref _filteredEntities); + _filteredEntitiesCount = _iterator.CacheTo(span, ref _filteredEntities); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsSpan Execute() + public EcsUnsafeSpan Execute() { Execute_Iternal(); + var result = new EcsUnsafeSpan(World.ID, _filteredAllEntities.Ptr, _filteredAllEntitiesCount); #if DEBUG && DRAGONECS_DEEP_DEBUG - var newSpan = new EcsSpan(World.ID, _filteredAllEntities, _filteredAllEntitiesCount); using (EcsGroup group = EcsGroup.New(World)) { foreach (var e in World.Entities) @@ -100,29 +109,29 @@ namespace DCFApixels.DragonECS.Core.Internal } } - if (group.SetEquals(newSpan) == false) + if (group.SetEquals(result.ToSpan()) == false) { int[] array = new int[_filteredAllEntities.Length]; - var count = _iterator.IterateTo(World.Entities, ref array); + var count = _iterator.CacheTo(World.Entities, ref array); - EcsDebug.PrintError(newSpan.ToString() + "\r\n" + group.ToSpan().ToString()); + EcsDebug.PrintError(result.ToString() + "\r\n" + group.ToSpan().ToString()); Throw.DeepDebugException(); } } #endif - return new EcsSpan(World.ID, _filteredAllEntities, _filteredAllEntitiesCount); + return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsSpan ExecuteFor(EcsSpan span) + public EcsUnsafeSpan ExecuteFor(EcsSpan span) { if (span.IsSourceEntities) { return Execute(); } ExecuteFor_Iternal(span); + var result = new EcsUnsafeSpan(World.ID, _filteredEntities.Ptr, _filteredEntitiesCount); #if DEBUG && DRAGONECS_DEEP_DEBUG - var newSpan = new EcsSpan(World.ID, _filteredEntities, _filteredEntitiesCount); - foreach (var e in newSpan) + foreach (var e in result) { if (World.IsMatchesMask(Mask, e) == false) { @@ -130,26 +139,28 @@ namespace DCFApixels.DragonECS.Core.Internal } } #endif - return new EcsSpan(World.ID, _filteredEntities, _filteredEntitiesCount); + return result; } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsSpan Execute(Comparison comparison) + public EcsUnsafeSpan Execute(Comparison comparison) { Execute_Iternal(); - ArraySortHalperX.Sort(_filteredAllEntities, comparison, _filteredAllEntitiesCount); - return new EcsSpan(World.ID, _filteredAllEntities, _filteredAllEntitiesCount); + Span result = _filteredAllEntities.AsSpan(_filteredAllEntitiesCount); + result.Sort(comparison); + return new EcsUnsafeSpan(World.ID, _filteredAllEntities.Ptr, _filteredAllEntitiesCount); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public EcsSpan ExecuteFor(EcsSpan span, Comparison comparison) + public EcsUnsafeSpan ExecuteFor(EcsSpan source, Comparison comparison) { - if (span.IsSourceEntities) + if (source.IsSourceEntities) { return Execute(comparison); } - ExecuteFor_Iternal(span); - ArraySortHalperX.Sort(_filteredEntities, comparison, _filteredEntitiesCount); - return new EcsSpan(World.ID, _filteredEntities, _filteredEntitiesCount); + ExecuteFor_Iternal(source); + Span result = _filteredEntities.AsSpan(_filteredEntitiesCount); + result.Sort(comparison); + return new EcsUnsafeSpan(World.ID, _filteredEntities.Ptr, _filteredEntitiesCount); } #endregion } diff --git a/src/Executors/EcsWhereToGroupExecutor.cs b/src/Executors/EcsWhereToGroupExecutor.cs index aea581b..6b19643 100644 --- a/src/Executors/EcsWhereToGroupExecutor.cs +++ b/src/Executors/EcsWhereToGroupExecutor.cs @@ -66,7 +66,7 @@ namespace DCFApixels.DragonECS.Core.Internal if (_versionsChecker.CheckAndNext() == false) { _version++; - _iterator.IterateTo(World.Entities, _filteredAllGroup); + _iterator.CacheTo(World.Entities, _filteredAllGroup); #if DEBUG && DRAGONECS_DEEP_DEBUG if (_filteredGroup == null) { @@ -98,7 +98,7 @@ namespace DCFApixels.DragonECS.Core.Internal { _filteredGroup = EcsGroup.New(World); } - _iterator.IterateTo(span, _filteredGroup); + _iterator.CacheTo(span, _filteredGroup); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Executors/Queries.cs b/src/Executors/Queries.cs index 30c9644..f3c8318 100644 --- a/src/Executors/Queries.cs +++ b/src/Executors/Queries.cs @@ -84,6 +84,76 @@ namespace DCFApixels.DragonECS } #endregion + #region WhereUnsafe + public static EcsUnsafeSpan WhereUnsafe(this TCollection entities, out TAspect aspect) + where TAspect : new() + where TCollection : IEntityStorage + { + return entities.ToSpan().WhereUnsafe(out aspect); + } + public static EcsUnsafeSpan WhereUnsafe(this EcsReadonlyGroup group, out TAspect aspect) + where TAspect : new() + { + return group.ToSpan().WhereUnsafe(out aspect); + } + public static EcsUnsafeSpan WhereUnsafe(this EcsSpan span, out TAspect aspect) + where TAspect : new() + { + span.World.GetQueryCache(out EcsWhereExecutor executor, out aspect); + return executor.ExecuteFor(span); + } + + public static EcsUnsafeSpan WhereUnsafe(this TCollection entities, IComponentMask mask) + where TCollection : IEntityStorage + { + return entities.ToSpan().WhereUnsafe(mask); + } + public static EcsUnsafeSpan WhereUnsafe(this EcsReadonlyGroup group, IComponentMask mask) + { + return group.ToSpan().WhereUnsafe(mask); + } + public static EcsUnsafeSpan WhereUnsafe(this EcsSpan span, IComponentMask mask) + { + var executor = span.World.GetExecutorForMask(mask); + return executor.ExecuteFor(span); + } + #endregion + + #region WhereUnsafe with sort + public static EcsUnsafeSpan WhereUnsafe(this TCollection entities, out TAspect aspect, Comparison comparison) + where TAspect : new() + where TCollection : IEntityStorage + { + return entities.ToSpan().WhereUnsafe(out aspect, comparison); + } + public static EcsUnsafeSpan WhereUnsafe(this EcsReadonlyGroup group, out TAspect aspect, Comparison comparison) + where TAspect : new() + { + return group.ToSpan().WhereUnsafe(out aspect, comparison); + } + public static EcsUnsafeSpan WhereUnsafe(this EcsSpan span, out TAspect aspect, Comparison comparison) + where TAspect : new() + { + span.World.GetQueryCache(out EcsWhereExecutor executor, out aspect); + return executor.ExecuteFor(span, comparison); + } + + public static EcsUnsafeSpan WhereUnsafe(this TCollection entities, IComponentMask mask, Comparison comparison) + where TCollection : IEntityStorage + { + return entities.ToSpan().WhereUnsafe(mask, comparison); + } + public static EcsUnsafeSpan WhereUnsafe(this EcsReadonlyGroup group, IComponentMask mask, Comparison comparison) + { + return group.ToSpan().WhereUnsafe(mask, comparison); + } + public static EcsUnsafeSpan WhereUnsafe(this EcsSpan span, IComponentMask mask, Comparison comparison) + { + var executor = span.World.GetExecutorForMask(mask); + return executor.ExecuteFor(span); + } + #endregion + #region WhereToGroup public static EcsReadonlyGroup WhereToGroup(this TCollection entities, out TAspect aspect) where TAspect : new() diff --git a/src/Internal/Allocators/AllocatorUtility.cs b/src/Internal/Allocators/AllocatorUtility.cs index 27f608b..cfc092f 100644 --- a/src/Internal/Allocators/AllocatorUtility.cs +++ b/src/Internal/Allocators/AllocatorUtility.cs @@ -1,25 +1,89 @@ using System; - +using System.Runtime.CompilerServices; +using static DCFApixels.DragonECS.Core.Internal.MemoryAllocator; namespace DCFApixels.DragonECS.Core.Internal { - internal static class AllocatorUtility + internal static unsafe class AllocatorUtility { - public static unsafe void ClearAllocatedMemory(IntPtr ptr, int startByte, int lengthInBytes) + public static void ClearAllocatedMemory(IntPtr ptr, int startByte, int lengthInBytes) { ClearAllocatedMemory((byte*)ptr, startByte, lengthInBytes); } - public static unsafe void ClearAllocatedMemory(byte* ptr, int startByte, int lengthInBytes) + public static void ClearAllocatedMemory(byte* ptr, int startByte, int lengthInBytes) { -#if ENABLE_DUMMY_SPAN - lengthInBytes += startByte; - for (int i = startByte; i < lengthInBytes; i++) - { - ptr[i] = 0; - } -#else Span memorySpan = new Span(ptr + startByte, lengthInBytes); memorySpan.Clear(); -#endif + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CacheTo(this EcsMaskIterator it, EcsSpan source, ref HMem array) + { + switch (it.MaskFlags) + { + case EcsMaskFlags.Empty: + { + if(array.Length < source.Count) + { + array = Realloc(array, source.Count); + } + source.AsSystemSpan().CopyTo(array.AsSpan()); + return source.Count; + } + case EcsMaskFlags.Inc: + { + return it.IterateOnlyInc(source).CacheTo(ref array); + } + case EcsMaskFlags.Exc: + case EcsMaskFlags.Any: + case EcsMaskFlags.IncExc: + case EcsMaskFlags.IncAny: + case EcsMaskFlags.ExcAny: + case EcsMaskFlags.IncExcAny: + { + return it.Iterate(source).CacheTo(ref array); + } + case EcsMaskFlags.Broken: + { + return 0; + } + default: + { + Throw.UndefinedException(); + return 0; + } + } + } + + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CacheTo(this EcsMaskIterator.OnlyIncEnumerable e, ref HMem array) + { + int count = 0; + var enumerator = e.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (array.Length <= count) + { + array = Realloc(array, array.Length << 1); + } + array.Ptr[count++] = enumerator.Current; + } + return count; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static int CacheTo(this EcsMaskIterator.Enumerable e, ref HMem array) + { + int count = 0; + var enumerator = e.GetEnumerator(); + while (enumerator.MoveNext()) + { + if (array.Length <= count) + { + array = Realloc(array, array.Length << 1); + } + array.Ptr[count++] = enumerator.Current; + } + return count; } } } diff --git a/src/Internal/Allocators/MemoryAllocator.cs b/src/Internal/Allocators/MemoryAllocator.cs index 69ca188..5634461 100644 --- a/src/Internal/Allocators/MemoryAllocator.cs +++ b/src/Internal/Allocators/MemoryAllocator.cs @@ -2,6 +2,7 @@ #undef DEBUG #endif using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace DCFApixels.DragonECS.Core.Internal @@ -26,30 +27,30 @@ namespace DCFApixels.DragonECS.Core.Internal } #region AllocAndInit - public static Handler AllocAndInit(int count) where T : unmanaged + public static HMem AllocAndInit(int count) where T : unmanaged { - return AllocAndInit_Internal(Marshal.SizeOf() * count, typeof(T)); + return new HMem(AllocAndInit_Internal(Marshal.SizeOf() * count, typeof(T)), count); } - public static Handler AllocAndInit(int byteLength) + public static HMem AllocAndInit(int byteLength) { - return AllocAndInit_Internal(byteLength, null); + return new HMem(AllocAndInit_Internal(byteLength, null), byteLength); } public static Handler AllocAndInit_Internal(int byteLength, Type type) { Handler handler = Alloc_Internal(byteLength, type); - AllocatorUtility.ClearAllocatedMemory(handler.Ptr, 0, byteLength); + AllocatorUtility.ClearAllocatedMemory(handler.RawPtr, 0, byteLength); return handler; } #endregion #region Alloc - public static Handler Alloc(int count) where T : unmanaged + public static HMem Alloc(int count) where T : unmanaged { - return Alloc_Internal(Marshal.SizeOf() * count, typeof(T)); + return new HMem(Alloc_Internal(Marshal.SizeOf() * count, typeof(T)), count); } - public static Handler Alloc(int byteLength) + public static HMem Alloc(int byteLength) { - return Alloc_Internal(byteLength, null); + return new HMem(Alloc_Internal(byteLength, null), byteLength); ; } public static Handler Alloc_Internal(int byteLength, Type type) { @@ -84,54 +85,78 @@ namespace DCFApixels.DragonECS.Core.Internal #endregion #region ReallocAndInit - public static Handler ReallocAndInit(void* target, int oldCount, int newCount) where T : unmanaged + public static HMem ReallocAndInit(void* target, int oldCount, int newCount) where T : unmanaged { return ReallocAndInit(Handler.FromDataPtr(target), oldCount, newCount); } - public static Handler ReallocAndInit(void* target, int oldByteLength, int newByteLength) + public static HMem ReallocAndInit(void* target, int oldByteLength, int newByteLength) { return ReallocAndInit(Handler.FromDataPtr(target), oldByteLength, newByteLength); } - public static Handler ReallocAndInit(Handler target, int oldCount, int newCount) where T : unmanaged + public static HMem ReallocAndInit(HMem target, int newCount) where T : unmanaged { var size = Marshal.SizeOf(); - return ReallocAndInit_Internal(target, size * oldCount, size * newCount, typeof(T)); + return new HMem(ReallocAndInit_Internal(target, size * target.Length, size * newCount, typeof(T)), newCount); } - public static Handler ReallocAndInit(Handler target, int oldByteLength, int newByteLength) + public static HMem ReallocAndInit(Handler target, int oldCount, int newCount) where T : unmanaged { - return ReallocAndInit_Internal(target, oldByteLength, newByteLength, null); + var size = Marshal.SizeOf(); + return new HMem(ReallocAndInit_Internal(target, size * oldCount, size * newCount, typeof(T)), newCount); + } + public static HMem ReallocAndInit(Handler target, int oldByteLength, int newByteLength) + { + return new HMem(ReallocAndInit_Internal(target, oldByteLength, newByteLength, null), newByteLength); } private static Handler ReallocAndInit_Internal(Handler target, int oldByteLength, int newByteLength, Type newType) { Handler handler = Realloc_Internal(target, newByteLength, newType); - AllocatorUtility.ClearAllocatedMemory(handler.Ptr, oldByteLength, newByteLength - oldByteLength); + AllocatorUtility.ClearAllocatedMemory(handler.RawPtr, oldByteLength, newByteLength - oldByteLength); return handler; } #endregion #region Realloc - public static Handler Realloc(void* target, int newCount) where T : unmanaged + public static HMem Realloc(void* target, int newCount) where T : unmanaged { return Realloc(Handler.FromDataPtr(target), Marshal.SizeOf() * newCount); } - public static Handler Realloc(void* target, int newByteLength) + public static HMem Realloc(void* target, int newByteLength) { - return Realloc(Handler.FromDataPtr(target), newByteLength); + return new HMem(Realloc(Handler.FromDataPtr(target), newByteLength), newByteLength); } - public static Handler Realloc(Handler target, int newCount) where T : unmanaged + public static HMem Realloc(Handler target, int newCount) where T : unmanaged { - return Realloc_Internal(target, Marshal.SizeOf() * newCount, typeof(T)); + return new HMem(Realloc_Internal(target, Marshal.SizeOf() * newCount, typeof(T)), newCount); } - public static Handler Realloc(Handler target, int newByteLength) + public static HMem Realloc(Handler target, int newByteLength) { - return Realloc_Internal(target, newByteLength, null); + return new HMem(Realloc_Internal(target, newByteLength, null), newByteLength); } private static Handler Realloc_Internal(Handler target, int newByteLength, Type newType) { newByteLength = newByteLength == 0 ? 1 : newByteLength; - Meta* newHandledPtr = (Meta*)Marshal.ReAllocHGlobal((IntPtr)target.GetHandledPtr(), (IntPtr)newByteLength + sizeof(Meta)); + if (target.IsCreated == false) + { + return Alloc_Internal(newByteLength, newType); + } +#if DEBUG + int id = 0; + lock (_idDispenser) + { + if (_debugInfos.Length <= _idDispenser.Count) + { + Array.Resize(ref _debugInfos, ArrayUtility.NextPow2(_idDispenser.Count)); + } + id = _idDispenser.UseFree(); + } +#endif + Meta* newHandledPtr = (Meta*)Marshal.ReAllocHGlobal( + (IntPtr)target.GetHandledPtr(), + (IntPtr)newByteLength + sizeof(Meta)); Handler handler = Handler.FromHandledPtr(newHandledPtr); #if DEBUG + newHandledPtr->ID = id; + newHandledPtr->ByteLength = newByteLength; #if DRAGONECS_DEEP_DEBUG _debugInfos[newHandledPtr->ID].stackTrace = new System.Diagnostics.StackTrace(); #endif @@ -142,11 +167,39 @@ namespace DCFApixels.DragonECS.Core.Internal } #endregion + #region Clone + public static HMem From(HMem source) + where T : unmanaged + { + var result = Alloc(source.Length); + source.AsSpan().CopyTo(result.AsSpan()); + return result; + } + public static HMem From(T* ptr, int length) + where T : unmanaged + { + return From(new ReadOnlySpan(ptr, length)); + } + public static HMem From(ReadOnlySpan source) + where T : unmanaged + { + var result = Alloc(source.Length); + source.CopyTo(result.AsSpan()); + return result; + } + #endregion + #region Free public static void Free(Handler target) { Free_Internal(target.GetHandledPtr()); } + public static void FreeAndClear(ref HMem target) + where T : unmanaged + { + Free_Internal(target.Handler.GetHandledPtr()); + target = default; + } public static void FreeAndClear(ref Handler target) { Free_Internal(target.GetHandledPtr()); @@ -159,6 +212,10 @@ namespace DCFApixels.DragonECS.Core.Internal private static void Free_Internal(Meta* handledPtr) { #if DEBUG + if (handledPtr == null) + { + throw new ArgumentNullException(); + } lock (_idDispenser) { _idDispenser.Release(handledPtr->ID); @@ -210,11 +267,90 @@ namespace DCFApixels.DragonECS.Core.Internal } #endregion + public readonly struct HMem : IDisposable, IEquatable> + where T : unmanaged + { + public readonly T* Ptr; + public readonly int Length; + + internal HMem(Handler handler, int length) + { + Ptr = handler.As(); + Length = length; + } + + public bool IsCreated + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return Ptr != null; } + } + public IntPtr RawPtr + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return new IntPtr(Ptr); } + } + public Handler Handler + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return Handler.FromDataPtr(Ptr); } + } + + public HMem As() + where U : unmanaged + { + if (IsCreated) + { + return default; + } + + long totalBytes = (long)Length * sizeof(T); + long newLengthLong = totalBytes / sizeof(U); +#if DEBUG + if (totalBytes % sizeof(U) != 0) + { + throw new InvalidOperationException($"Cannot cast Memory<{typeof(T).Name}> to Memory<{typeof(U).Name}> because the size of the underlying memory ({totalBytes} bytes) is not a multiple of the size of {typeof(U).Name} ({sizeof(U)} bytes)."); + } + if (newLengthLong > int.MaxValue) + { + throw new InvalidOperationException($"Resulting length ({newLengthLong}) exceeds int.MaxValue."); + } +#endif + + return new HMem(Handler, (int)newLengthLong); + } + public void Dispose() + { + Handler.Dispose(); + } + + public override string ToString() { return Handler.DebuggerDisplay(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() { return RawPtr.GetHashCode(); } + public override bool Equals(object obj) { return obj is Handler h && h == this; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(HMem other) { return other.Ptr == Ptr; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(HMem a, HMem b) { return a.Ptr == b.Ptr; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(HMem a, HMem b) { return a.Ptr != b.Ptr; } + + public Span AsSpan() { return new Span(Ptr, Length); } + public Span AsSpan(int length) + { +#if DEBUG + if (length > Length) { Throw.UndefinedException(); } +#endif + return new Span(Ptr, length); + } + public static implicit operator Handler(HMem memory) { return memory.Handler; } + } + #if DEBUG [System.Diagnostics.DebuggerDisplay("{DebuggerDisplay()}")] [System.Diagnostics.DebuggerTypeProxy(typeof(DebuggerProxy))] #endif - public readonly struct Handler : IDisposable + public readonly struct Handler : IDisposable, IEquatable { public static readonly Handler Empty = new Handler(); internal readonly Meta* Data; // Data[-1] is meta; @@ -239,16 +375,37 @@ namespace DCFApixels.DragonECS.Core.Internal #endif } - public bool IsEmpty { get { return Data == null; } } - public IntPtr Ptr { get { return (IntPtr)Data; } } - public T* As() where T : unmanaged { return (T*)Ptr; } + public bool IsCreated + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return Data != null; } + } + public IntPtr RawPtr + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get { return (IntPtr)Data; } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public T* As() where T : unmanaged { return (T*)RawPtr; } - void IDisposable.Dispose() { Free((void*)Ptr); } + public void Dispose() { Free((void*)RawPtr); } + + public override string ToString() { return DebuggerDisplay(); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public override int GetHashCode() { return RawPtr.GetHashCode(); } + public override bool Equals(object obj) { return obj is Handler h && h == this; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public bool Equals(Handler other) { return other.Data == Data; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator ==(Handler a, Handler b) { return a.Data == b.Data; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool operator !=(Handler a, Handler b) { return a.Data != b.Data; } #region Debugger #if DEBUG #pragma warning disable IL3050 // Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling. - internal unsafe string DebuggerDisplay() + internal string DebuggerDisplay() { if (Data == null) { @@ -282,6 +439,7 @@ namespace DCFApixels.DragonECS.Core.Internal } return array; } + [StructLayout(LayoutKind.Explicit)] private unsafe struct Union { @@ -303,13 +461,13 @@ namespace DCFApixels.DragonECS.Core.Internal public HandlerDebugInfo[] OtherHandlersInfo; - public unsafe DebuggerProxy(Handler handler) + public DebuggerProxy(Handler handler) { - IsAlive = handler.Ptr.ToPointer() != null; + IsAlive = handler.RawPtr.ToPointer() != null; if (IsAlive == false) { return; } Meta = handler.GetHandledPtr()[0]; - _data = (byte*)handler.Ptr; + _data = (byte*)handler.RawPtr; DebugInfo = _debugInfos[Meta.ID]; if (DebugInfo.type == null) @@ -337,9 +495,15 @@ namespace DCFApixels.DragonECS.Core.Internal internal static class MemoryAllocatorHandlerExtensions { - public static void Dispose(this ref MemoryAllocator.Handler self) + public static void DisposeAndReset(this ref MemoryAllocator.Handler self) { MemoryAllocator.FreeAndClear(ref self); } + public static void DisposeAndReset(this ref MemoryAllocator.HMem self) + where T : unmanaged + { + self.Dispose(); + self = default; + } } } \ No newline at end of file diff --git a/src/Internal/Allocators/TempBuffer.cs b/src/Internal/Allocators/TempBuffer.cs index f777174..f86ea56 100644 --- a/src/Internal/Allocators/TempBuffer.cs +++ b/src/Internal/Allocators/TempBuffer.cs @@ -54,7 +54,7 @@ namespace DCFApixels.DragonECS.Core.Internal { MemoryAllocator.Free(_ptr); } - _ptr = MemoryAllocator.Alloc(byteSize).As(); + _ptr = MemoryAllocator.Alloc(byteSize).Ptr; _byteSize = byteSize; } return (T*)_ptr; diff --git a/src/Internal/ArrayUtility.cs b/src/Internal/ArrayUtility.cs index 1dbbb2d..e832420 100644 --- a/src/Internal/ArrayUtility.cs +++ b/src/Internal/ArrayUtility.cs @@ -6,7 +6,6 @@ using System.Collections; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; namespace DCFApixels.DragonECS.Core.Internal { @@ -270,114 +269,6 @@ namespace DCFApixels.DragonECS.Core.Internal } } - internal static unsafe class UnmanagedArrayUtility - { - private static class MetaCache - { - public readonly static int Size; - static MetaCache() - { - T def = default; - Size = Marshal.SizeOf(def); - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T* New(int capacity) where T : unmanaged - { - //Console.WriteLine($"{typeof(T).Name} - {Marshal.SizeOf()} - {capacity} - {Marshal.SizeOf() * capacity}"); - //return (T*)Marshal.AllocHGlobal(Marshal.SizeOf() * capacity).ToPointer(); - return MemoryAllocator.Alloc(capacity).As(); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void New(out T* ptr, int capacity) where T : unmanaged - { - //ptr = (T*)Marshal.AllocHGlobal(Marshal.SizeOf() * capacity).ToPointer(); - ptr = MemoryAllocator.Alloc(capacity).As(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T* NewAndInit(int capacity) where T : unmanaged - { - //int newSize = MetaCache.Size * capacity; - //byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer(); - // - //for (int i = 0; i < newSize; i++) - //{ - // *(newPointer + i) = 0; - //} - // - //return (T*)newPointer; - return MemoryAllocator.AllocAndInit(capacity).As(); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void NewAndInit(out T* ptr, int capacity) where T : unmanaged - { - //int newSize = MetaCache.Size * capacity; - //byte* newPointer = (byte*)Marshal.AllocHGlobal(newSize).ToPointer(); - // - //for (int i = 0; i < newSize; i++) - //{ - // *(newPointer + i) = 0; - //} - // - //ptr = (T*)newPointer; - ptr = MemoryAllocator.AllocAndInit(capacity).As(); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Free(void* pointer) - { - //Marshal.FreeHGlobal(new IntPtr(pointer)); - MemoryAllocator.Free(dataPtr: pointer); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Free(ref T* pointer, ref int length) where T : unmanaged - { - //Marshal.FreeHGlobal(new IntPtr(pointer)); - MemoryAllocator.Free(dataPtr: 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(MetaCache.Size * newCount))).ToPointer(); - return MemoryAllocator.Realloc(MemoryAllocator.Handler.FromDataPtr(oldPointer), newCount).As(); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static T* ResizeAndInit(void* oldPointer, int oldSize, int newSize) where T : unmanaged - { - //int sizeT = MetaCache.Size; - //T* result = (T*)Marshal.ReAllocHGlobal( - // new IntPtr(oldPointer), - // new IntPtr(sizeT * newSize)).ToPointer(); - //Init((byte*)result, sizeT * oldSize, sizeT * newSize); - //return result; - return MemoryAllocator.ReallocAndInit(MemoryAllocator.Handler.FromDataPtr(oldPointer), oldSize, newSize).As(); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static void Init(byte* pointer, int startByteIndex, int endByteIndex) - { - for (int i = startByteIndex; i < endByteIndex; i++) - { - *(pointer + i) = 0; - } - } - } public static class CollectionUtility { @@ -390,4 +281,24 @@ namespace DCFApixels.DragonECS.Core.Internal return $"{name}({range.Count()}) {{{string.Join(", ", range.Select(o => o.ToString()))}}})"; } } + + internal static class SpanUtility + { + public static void Sort(Span span, Comparison comparison) + { + span.Sort(new StructComparison(comparison)); + } + private readonly struct StructComparison : IComparer + { + public readonly Comparison Comparison; + public StructComparison(Comparison comparison) + { + Comparison = comparison; + } + public int Compare(T x, T y) + { + return Comparison(x, y); + } + } + } } \ No newline at end of file diff --git a/src/Internal/UnsafeArray.cs b/src/Internal/UnsafeArray.cs index 4ec388b..ce13c17 100644 --- a/src/Internal/UnsafeArray.cs +++ b/src/Internal/UnsafeArray.cs @@ -38,13 +38,13 @@ namespace DCFApixels.DragonECS.Core.Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] public UnsafeArray(int length) { - UnmanagedArrayUtility.New(out ptr, length); + ptr = MemoryAllocator.Alloc(length).Ptr; Length = length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public UnsafeArray(int length, bool isInit) { - UnmanagedArrayUtility.NewAndInit(out ptr, length); + ptr = MemoryAllocator.AllocAndInit(length).Ptr; Length = length; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -90,18 +90,20 @@ namespace DCFApixels.DragonECS.Core.Internal [MethodImpl(MethodImplOptions.AggressiveInlining)] public UnsafeArray Clone() { - return new UnsafeArray(UnmanagedArrayUtility.Clone(ptr, Length), Length); + return new UnsafeArray(MemoryAllocator.From(ptr, Length).Ptr, Length); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Dispose() { - UnmanagedArrayUtility.Free(ref ptr, ref Length); + MemoryAllocator.Free(ptr); + ptr = default; + Length = default; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void ReadonlyDispose() { - UnmanagedArrayUtility.Free(ptr); + MemoryAllocator.Free(ptr); } public override string ToString() { @@ -114,6 +116,9 @@ namespace DCFApixels.DragonECS.Core.Internal return CollectionUtility.AutoToString(elements, "ua"); } + public Span AsSpan() { return new Span(ptr, Length); } + public T[] ToArray() { return AsSpan().ToArray(); } + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } [MethodImpl(MethodImplOptions.AggressiveInlining)] diff --git a/src/Utils/ReadOnlySpanDummy.cs b/src/Utils/ReadOnlySpanDummy.cs deleted file mode 100644 index 4a4cdae..0000000 --- a/src/Utils/ReadOnlySpanDummy.cs +++ /dev/null @@ -1,202 +0,0 @@ -#if ENABLE_DUMMY_SPAN -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Runtime.CompilerServices; -using EditorBrowsableAttribute = System.ComponentModel.EditorBrowsableAttribute; -using EditorBrowsableState = System.ComponentModel.EditorBrowsableState; - -namespace DCFApixels.DragonECS -{ - internal static class ThrowHelper - { - public static void ThrowIndexOutOfRangeException() => throw new IndexOutOfRangeException(); - public static void ThrowArgumentOutOfRangeException() => throw new ArgumentOutOfRangeException(); - public static void ThrowInvalidOperationException() => throw new InvalidOperationException(); - } - [DebuggerDisplay("{ToString(),raw}")] - public readonly ref struct ReadOnlySpan - { - public static ReadOnlySpan Empty => new ReadOnlySpan(null); - - internal readonly T[] _array; - private readonly int _start; - private readonly int _length; - - #region Properties - public int Length => _length; - public bool IsEmpty => _length == 0; - public ref readonly T this[int index] - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get - { - if ((uint)index >= (uint)_length || (uint)index < 0) - ThrowHelper.ThrowIndexOutOfRangeException(); - return ref _array[index + _start]; - } - } - #endregion - - #region Constructors - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan(T[] array) - { - _array = array ?? Array.Empty(); - _start = 0; - _length = array.Length; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan(T[] array, int start, int length) - { - if (array == null) - { - if (start != 0 || length != 0) - ThrowHelper.ThrowArgumentOutOfRangeException(); - _array = Array.Empty(); - _start = 0; - _length = 0; - return; - } - - if ((uint)start > (uint)array.Length || (uint)length > (uint)(array.Length - start)) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - _array = array; - _start = start; - _length = length; - } - #endregion - - #region Object -#pragma warning disable CS0809 // - [Obsolete("Equals() on ReadOnlySpan will always throw an exception. Use the equality operator instead.")] - [EditorBrowsable(EditorBrowsableState.Never)] - public override bool Equals(object obj) => throw new NotSupportedException(); - [Obsolete("GetHashCode() on ReadOnlySpan will always throw an exception.")] - [EditorBrowsable(EditorBrowsableState.Never)] - public override int GetHashCode() => throw new NotSupportedException(); -#pragma warning restore CS0809 // - public override string ToString() - { - //if (typeof(T) == typeof(char)) - // return new string(new ReadOnlySpan(ref Unsafe.As(ref _reference), _length)); - return $"System.ReadOnlySpan<{typeof(T).Name}>[{_length}]"; - } - #endregion - - #region operators - public static bool operator !=(ReadOnlySpan left, ReadOnlySpan right) => !(left == right); - - public static implicit operator ReadOnlySpan(T[] array) => new ReadOnlySpan(array); - - public static implicit operator ReadOnlySpan(ArraySegment segment) => new ReadOnlySpan(segment.Array, segment.Offset, segment.Count); - public static bool operator ==(ReadOnlySpan left, ReadOnlySpan right) => left._length == right._length && left._array == right._array; - #endregion - - #region Enumerator - public Enumerator GetEnumerator() => new Enumerator(this); - public ref struct Enumerator - { - private readonly ReadOnlySpan _span; - private int _index; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal Enumerator(ReadOnlySpan span) - { - _span = span; - _index = span._start - 1; - } - public ref readonly T Current - { - [MethodImpl(MethodImplOptions.AggressiveInlining)] - get => ref _span[_index]; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - { - int index = _index + 1; - if (index < _span.Length) - { - _index = index; - return true; - } - return false; - } - } - #endregion - - #region Other - [EditorBrowsable(EditorBrowsableState.Never)] - public ref readonly T GetPinnableReference() - { - if (_length != 0) ThrowHelper.ThrowInvalidOperationException(); - return ref _array[0]; - } - - //[MethodImpl(MethodImplOptions.AggressiveInlining)] - //public void CopyTo(Span destination) - //{ - // if ((uint)_length <= (uint)destination.Length) - // { - // Buffer.Memmove(ref destination._reference, ref _reference, (uint)_length); - // } - // else - // { - // ThrowHelper.ThrowArgumentException_DestinationTooShort(); - // } - //} - - //public bool TryCopyTo(Span destination) - //{ - // bool retVal = false; - // if ((uint)_length <= (uint)destination.Length) - // { - // Buffer.Memmove(ref destination._reference, ref _reference, (uint)_length); - // retVal = true; - // } - // return retVal; - //} - public void CopyTo(T[] array) - { - if (_length > array.Length) - { - throw new ArgumentOutOfRangeException(); - } - - for (int i = 0; i < _length; i++) - { - array[i] = _array[i]; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan Slice(int start) - { - if ((uint)start > (uint)_length) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - return new ReadOnlySpan(_array, _start + start, _length - start); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public ReadOnlySpan Slice(int start, int length) - { - if ((uint)start > (uint)_length || (uint)length > (uint)(_length - start)) - ThrowHelper.ThrowArgumentOutOfRangeException(); - - return new ReadOnlySpan(_array, _start + start, length); - } - - public T[] ToArray() - { - if (_length == 0) - return Array.Empty(); - var result = new T[_length]; - Array.Copy(_array, _start, result, 0, _length); - return result; - } - #endregion - } -} -#endif \ No newline at end of file diff --git a/src/Utils/Uncheked/UncheckedUtility.cs b/src/Utils/Uncheked/UncheckedUtility.cs index e084377..76c5105 100644 --- a/src/Utils/Uncheked/UncheckedUtility.cs +++ b/src/Utils/Uncheked/UncheckedUtility.cs @@ -62,6 +62,14 @@ namespace DCFApixels.DragonECS.Core.Unchecked } #endregion + #region Unsafe Span + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static unsafe EcsUnsafeSpan CreateUnsafeSpan(short worldID, int* ptr, int length) + { + return new EcsUnsafeSpan(worldID, ptr, length); + } + #endregion + #region EcsGroup public static EcsGroup GetSourceInstance(EcsReadonlyGroup group) {