using Misaki.HighPerformance.LowLevel.Utilities; using System.Collections; using System.Diagnostics; using System.Runtime.CompilerServices; namespace Misaki.HighPerformance.LowLevel.Collections; /// /// Provides a read-only, unsafe view over a contiguous region of unmanaged memory as an array of elements of type T. /// Enables efficient, low-level access to memory without copying or additional safety checks. /// /// /// This read only collection does not own the memory it points to. The user is responsible for ensuring the memory remains valid for the lifetime of this structure. /// The goal of this struc is similar to , but it can be used in contexts where spans are not allowed, such as fields in structs and shared across threads. /// /// The type of elements in the collection. Must be an unmanaged type. public readonly unsafe struct ReadOnlyUnsafeCollection : IEnumerable where T : unmanaged { public struct Enumerator : IEnumerator { private readonly ReadOnlyUnsafeCollection _collection; private int _index; public readonly T Current => _collection[_index]; readonly object IEnumerator.Current => Current; public Enumerator(ref readonly ReadOnlyUnsafeCollection array) { _collection = array; _index = -1; } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { _index++; return _index < _collection.Count; } public void Reset() { _index = -1; } public void Dispose() { } } private readonly T* _buffer; private readonly int _count; public int Count => _count; public ref readonly T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { CheckIndexBounds(index); return ref UnsafeUtility.ReadArrayElementRef(_buffer, index); } } public ref readonly T this[uint index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { CheckIndexBounds((int)index); return ref UnsafeUtility.ReadArrayElementRef(_buffer, index); } } public Enumerator GetEnumerator() => new Enumerator(in this); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); public ReadOnlyUnsafeCollection(T* buffer, int count) { _buffer = buffer; _count = count; } [MethodImpl(MethodImplOptions.AggressiveInlining)] [Conditional("ENABLE_COLLECTION_CHECKS")] private readonly void CheckIndexBounds(int index) { if (index >= _count) { throw new ArgumentOutOfRangeException(nameof(index), "Index is out of range."); } } /// /// Returns a read-only span that represents the valid elements in the underlying buffer. /// /// A containing the elements of the buffer up to the current count. public ReadOnlySpan AsSpan() { return new ReadOnlySpan(_buffer, _count); } /// /// Reinterprets the underlying collection as a read-only collection of a different unmanaged type without copying the data. /// /// The unmanaged type to reinterpret the collection elements as. /// A new ReadOnlyUnsafeCollection that provides a read-only view of the same memory, interpreted as elements of type U. /// Thrown if the total size of the underlying collection is not a multiple of the size of type U, making the reinterpretation invalid. public ReadOnlyUnsafeCollection Reinterpret() where U : unmanaged { var totalSize = (nuint)(Count * sizeof(T)); if (totalSize % (nuint)sizeof(U) != 0) { throw new InvalidOperationException("Cannot reinterpret collection: size mismatch."); } var newCount = (int)(totalSize / (nuint)sizeof(U)); return new ReadOnlyUnsafeCollection((U*)_buffer, newCount); } }