using Misaki.HighPerformance.Unsafe.Collections.Contracts; using Misaki.HighPerformance.Unsafe.Helpers; using Misaki.HighPerformance.Unsafe.Services; using System.Collections; using System.Runtime.CompilerServices; namespace Misaki.HighPerformance.Unsafe.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 { private 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 readonly Allocator _allocator; public readonly int Count => _count; public readonly ref T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => 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(); /// /// Initializes a new instance of UnsafeArray with a specified number of elements and an allocation type. It /// allocates memory and optionally clears it. /// /// Specifies the number of elements to allocate in the array, which must be greater than zero. /// Determines how the allocated memory should be initialized, either uninitialized or cleared. /// Thrown when the specified number of elements is less than or equal to zero. public UnsafeArray(int count, Allocator allocator, AllocationOption allocationType = AllocationOption.UnInitialized) { if (count <= 0) { throw new ArgumentOutOfRangeException(nameof(count), "Count must be greater than zero."); } _buffer = AllocationManager.Allocate((uint)count, (uint)AlignOf(), allocator, allocationType); _count = count; if (allocationType == AllocationOption.Clear) { Clear(); } } /// /// Initializes an UnsafeArray with a pointer to a buffer and a count of elements. The count is adjusted based on /// the size of the type T. /// /// A pointer to the memory location that holds the elements of the array. /// The total size of the data in bytes, which is divided by the size of type T to determine the number of elements. public UnsafeArray(void* buffer, int count) { _buffer = (T*)buffer; _count = count; } public void Resize(int newSize) { if (newSize == _count) { return; } _buffer = (T*)AlignedRealloc(_buffer, (nuint)(newSize * sizeof(T)), 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() { AllocationManager.Free(_buffer, _allocator); _buffer = null; _count = 0; } }