using Misaki.HighPerformance.LowLevel.Buffer; using Misaki.HighPerformance.LowLevel.Collections.Contracts; using Misaki.HighPerformance.LowLevel.Contracts; using Misaki.HighPerformance.LowLevel.Helpers; using System.Collections; using System.Runtime.CompilerServices; namespace Misaki.HighPerformance.LowLevel.Collections; /// /// A structure for managing an array of unmanaged types with unsafe memory operations. /// /// Represents a type that can be stored in an unmanaged memory context. public unsafe struct UnsafeArray : IUnsafeCollection where T : unmanaged { public struct Enumerator : IEnumerator { private UnsafeArray* _collection; private int _index; private T _value; public Enumerator(UnsafeArray* collection) { _collection = collection; _index = -1; _value = default; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { _index++; if (_index < _collection->_count) { _value = UnsafeUtilities.ReadArrayElement(_collection->_buffer, _index); return true; } _value = default; return false; } public void Reset() { _index = -1; } // Let NativeArray indexer check for out of range. public readonly T Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _value; } readonly object IEnumerator.Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => Current; } public void Dispose() { } } private T* _buffer; private int _count; private AllocationHandle* _handle; public readonly int Count => _count; public readonly ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (index < 0 || index >= _count) { throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range."); } return ref UnsafeUtilities.ReadArrayElementRef(_buffer, index); } } public readonly ref T this[uint index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { if (index >= _count) { throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range."); } return ref UnsafeUtilities.ReadArrayElementRef(_buffer, index); } } public readonly bool IsCreated { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => _buffer != null; } public IEnumerator GetEnumerator() => new Enumerator((UnsafeArray*)UnsafeUtilities.AddressOf(ref this)); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); /// /// Constructs an UnsafeArray with a default size of 1 and uses the Persistent allocator. /// public UnsafeArray() : this(0, Allocator.Invalid) { } /// /// Initializes a new instance of UnsafeArray with a specified number of elements and an allocation handle. /// /// Specifies the number of elements to allocate in the array, which must be greater than zero. /// A reference to an AllocationHandle that manages the memory allocation for the array. /// Specifies how the memory should be allocated. /// Thrown when the specified number of elements is less than or equal to zero. public UnsafeArray(int count, ref AllocationHandle handle, AllocationOption allocationOption = AllocationOption.None) { if (count <= 0) { throw new ArgumentOutOfRangeException(nameof(count), "Count must be greater than zero."); } _handle = (AllocationHandle*)Unsafe.AsPointer(ref handle); _buffer = (T*)handle.Alloc(_handle->Allocator, (uint)count * (uint)sizeof(T), (uint)AlignOf(), allocationOption); _count = count; } /// /// Initializes a new instance of UnsafeArray with a specified number of elements and an allocation type. /// /// Specifies the number of elements to allocate in the array, which must be greater than zero. /// Specifies the allocator to use for memory allocation, which determines the memory management strategy. /// Determines how the memory should be allocated. /// Thrown when the specified number of elements is less than or equal to zero. public UnsafeArray(int count, Allocator allocator, AllocationOption allocationOption = AllocationOption.None) : this(count, ref AllocationManager.GetAllocationHandle(allocator), allocationOption) { } /// /// Initializes an UnsafeArray with a pointer to a buffer and a count of elements. This does not copy the data. /// /// A pointer to the memory location that holds the elements of the array. /// The total size of the data. /// /// When using this constructor, the user is responsible for managing the memory pointed to by the buffer. /// Disposing of the UnsafeArray does not free the memory and only release the reference. The memory should be freed manually when no longer needed. /// Use constructor and if you are not sure what you are doing. /// public UnsafeArray(T* buffer, int count) { _buffer = buffer; _count = count; } /// public void Resize(int newSize) { if (newSize == _count) { return; } _buffer = (T*)_handle->Realloc(_handle->Allocator, _buffer, (uint)newSize, (uint)AlignOf()); _count = newSize; } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly void Clear() { MemClear(_buffer, (nuint)(_count * sizeof(T))); } /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public readonly void* GetUnsafePtr() { return _buffer; } /// public void Dispose() { if (!IsCreated) { return; } if (_handle != null) { _handle->Free(_handle->Allocator, _buffer); } _handle = null; _buffer = null; _count = 0; } }