using System; using System.Runtime.CompilerServices; using Svelto.Common; using Allocator = Svelto.Common.Allocator; namespace Svelto.ECS.DataStructures { public struct NativeDynamicArray : IDisposable { public bool isValid { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { unsafe { return _list != null; } } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int Count() where T : struct { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); if (_hashType != TypeHash.hash) throw new Exception($"NativeDynamicArray: not expected type used"); #endif return (_list->count / MemoryUtilities.SizeOf()); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int Size() { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); #endif return (_list->count); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public int Capacity() where T : struct { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); if (_hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not expected type used"); #endif return (_list->capacity / MemoryUtilities.SizeOf()); } } public static NativeDynamicArray Alloc(uint newLength = 0) where T : struct { return Alloc(Allocator.Persistent, newLength); } public static NativeDynamicArray Alloc(Allocator allocator, uint newLength = 0) where T : struct { unsafe { #if DEBUG && !PROFILE_SVELTO var rtnStruc = new NativeDynamicArray {_hashType = TypeHash.hash}; #else NativeDynamicArray rtnStruc = default; #endif UnsafeArray* listData = (UnsafeArray*) MemoryUtilities.Alloc(1, allocator); //clear to nullify the pointers //MemoryUtilities.MemClear((IntPtr) listData, structSize); rtnStruc._allocator = allocator; listData->Realloc(newLength, allocator); rtnStruc._list = listData; return rtnStruc; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T Get(uint index) where T : struct { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); if (_hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not expected type used"); if (index >= Count()) throw new Exception($"NativeDynamicArray: out of bound access, index {index} count {Count()}"); #endif return ref _list->Get(index); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T Get(int index) where T : struct { return ref Get((uint) index); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Set(uint index, in T value) where T : struct { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); if (_hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not expected type used"); if (index >= Capacity()) throw new Exception($"NativeDynamicArray: out of bound access, index {index} capacity {Capacity()}"); #endif _list->Set(index, value); } } public unsafe void Dispose() { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); #endif _list->Dispose(_allocator); MemoryUtilities.Free((IntPtr) _list, _allocator); _list = null; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Add(in T item) where T : struct { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); if (_hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not expected type used"); #endif if (Count() == Capacity()) { _list->Realloc((uint) ((Capacity() + 1) * 1.5f), _allocator); } _list->Add(item); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public ref T AddAt(uint index) where T : struct { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); if (_hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not expected type used"); #endif var structSize = (uint) MemoryUtilities.SizeOf(); if (index >= Capacity()) _list->Realloc((uint) ((index + 1) * 1.5f), _allocator); var writeIndex = (index + 1) * structSize; if (_list->count < writeIndex) _list->SetCountTo(writeIndex); return ref _list->Get(index); } } public void Resize(uint newCapacity) where T : struct { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); if (_hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not expected type used"); #endif _list->Realloc((uint) newCapacity, _allocator); } } public void SetCount(uint count) where T : struct { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); if (_hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not expected type used"); #endif uint structSize = (uint) MemoryUtilities.SizeOf(); uint size = (uint) (count * structSize); _list->SetCountTo((uint) size); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddWithoutGrow(in T item) where T : struct { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); if (_hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not expected type used"); var structSize = (uint) MemoryUtilities.SizeOf(); if (_list->space - (int)structSize < 0) throw new Exception("NativeDynamicArray: no writing authorized"); #endif _list->Add(item); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void UnorderedRemoveAt(uint index) where T : struct { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); if (_hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not expected type used"); if (Count() == 0) throw new Exception("NativeDynamicArray: empty array invalid operation"); #endif var indexToMove = Count() - 1; if (index < indexToMove) { Set(index, Get((uint) indexToMove)); } _list->Pop(); } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void FastClear() { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); #endif _list->Clear(); } } public unsafe T* ToPTR() where T : unmanaged { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); if (_hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not expected type used"); #endif return (T*) _list->ptr; } public IntPtr ToIntPTR() where T : struct { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); if (_hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not expected type used"); #endif return (IntPtr) _list->ptr; } } public T[] ToManagedArray() where T : unmanaged { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); if (_hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not expected type used"); #endif var count = Count(); var ret = new T[count]; var lengthToCopyInBytes = count * MemoryUtilities.SizeOf(); fixed (void* handle = ret) { Unsafe.CopyBlock(handle, _list->ptr, (uint) lengthToCopyInBytes); } return ret; } } public T[] ToManagedArrayUntrimmed() where T : unmanaged { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); if (_hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not expected type used"); #endif var capacity = Capacity(); var lengthToCopyInBytes = capacity * MemoryUtilities.SizeOf(); var ret = new T[capacity]; fixed (void* handle = ret) { Unsafe.CopyBlock(handle, _list->ptr, (uint) lengthToCopyInBytes); } return ret; } } public void RemoveAt(uint index) where T : struct { unsafe { #if DEBUG && !PROFILE_SVELTO if (_list == null) throw new Exception("NativeDynamicArray: null-access"); if (_hashType != TypeHash.hash) throw new Exception("NativeDynamicArray: not expected type used"); #endif var sizeOf = MemoryUtilities.SizeOf(); //Unsafe.CopyBlock may not be memory overlapping safe (memcpy vs memmove) Buffer.MemoryCopy(_list->ptr + (index + 1) * sizeOf, _list->ptr + index * sizeOf, _list->count , (uint) ((Count() - (index + 1)) * sizeOf)); _list->Pop(); } } public void MemClear() { unsafe { MemoryUtilities.MemClear((IntPtr) _list->ptr, (uint) _list->capacity); } } #if UNITY_COLLECTIONS || UNITY_JOBS #if UNITY_BURST [global::Unity.Burst.NoAlias] #endif [global::Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction] #endif unsafe UnsafeArray* _list; #if DEBUG && !PROFILE_SVELTO int _hashType; #endif Allocator _allocator; } }