using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace Nez.Unmanaged.Collections { [DebuggerDisplay("Length = {" + nameof(Length) + "}")] [DebuggerTypeProxy(typeof(NativeArrayDebugView<>))] public unsafe struct NativeArray<T> : IDisposable, IEnumerable<T>, IEnumerable where T : unmanaged { public struct Enumerator : IEnumerator<T>, IEnumerator, IDisposable { private NativeArray<T> _array; private int _index; object IEnumerator.Current => Current; public T Current => _array[_index]; public Enumerator(ref NativeArray<T> array) { _array = array; _index = -1; } public void Dispose() { } public bool MoveNext() { _index++; return _index < _array.Length; } public void Reset() => _index = -1; } public int Length => _length; internal unsafe void* _buffer; internal int _length; internal Allocator _allocator; internal DisposeSentinel _DisposeSentinel; public unsafe T this[int index] { get => UnsafeUtility.ReadArrayElement<T>(_buffer, index); set => UnsafeUtility.WriteArrayElement<T>(_buffer, index, value); } public unsafe NativeArray(int length, Allocator allocator = Allocator.Persistent, NativeArrayOptions options = NativeArrayOptions.ClearMemory) { var bufferSize = Marshal.SizeOf<T>() * length; _buffer = (void*)UnsafeUtility.Alloc(bufferSize, allocator); if (options == NativeArrayOptions.ClearMemory) UnsafeUtility.Memset(_buffer, 0, bufferSize); _length = length; _allocator = allocator; DisposeSentinel.Create(out _DisposeSentinel, allocator); } public unsafe void* GetUnsafePtr() => _buffer; public void Dispose() { DisposeSentinel.Dispose(ref _DisposeSentinel); UnsafeUtility.Free((IntPtr)_buffer); _buffer = null; _length = 0; } public T[] ToArray() { var array = new T[Length]; Copy(this, array, Length); return array; } #region IEnumerable public Enumerator GetEnumerator() => new Enumerator(ref this); IEnumerator<T> IEnumerable<T>.GetEnumerator() => new Enumerator(ref this); IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #endregion #region Equals public static bool operator ==(NativeArray<T> left, NativeArray<T> right) => left.Equals(right); public static bool operator !=(NativeArray<T> left, NativeArray<T> right) => !left.Equals(right); public bool Equals(NativeArray<T> other) { return _buffer == other._buffer && _length == other._length; } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; return obj is NativeArray<T> && Equals((NativeArray<T>)obj); } public override int GetHashCode() { unchecked { return ((int)_buffer * 397) ^ _length; } } #endregion #region Copying public static void Copy(T[] src, NativeArray<T> dst) { if (src.Length != dst.Length) throw new ArgumentException("source and destination length must be the same"); Copy(src, 0, dst, 0, src.Length); } public static void Copy(NativeArray<T> src, T[] dst) { if (src.Length != dst.Length) throw new ArgumentException("source and destination length must be the same"); Copy(src, 0, dst, 0, src.Length); } public static void Copy(NativeArray<T> src, NativeArray<T> dst) { if (src.Length != dst.Length) throw new ArgumentException("source and destination length must be the same"); Copy(src, 0, dst, 0, src.Length); } public static void Copy(NativeArray<T> src, T[] dst, int length) { Copy(src, 0, dst, 0, length); } public static void Copy(NativeArray<T> src, NativeArray<T> dst, int length) { Copy(src, 0, dst, 0, length); } public static void Copy(NativeArray<T> src, int srcIndex, NativeArray<T> dst, int dstIndex, int length) { var size = Unsafe.SizeOf<T>(); var destAddr = (byte*)dst._buffer + dstIndex * size; var srcAddr = (byte*)src._buffer + srcIndex * size; Unsafe.CopyBlock(destAddr, srcAddr, (uint)(length * size)); } public static void Copy(T[] src, int srcIndex, NativeArray<T> dst, int dstIndex, int length) { var handle = GCHandle.Alloc(src, GCHandleType.Pinned); var addr = handle.AddrOfPinnedObject(); var size = Unsafe.SizeOf<T>(); var destAddr = (byte*)dst._buffer + dstIndex * size; var srcAddr = (byte*)addr + srcIndex * size; Unsafe.CopyBlock(destAddr, srcAddr, (uint)(length * size)); handle.Free(); } public static void Copy(NativeArray<T> src, int srcIndex, T[] dst, int dstIndex, int length) { var handle = GCHandle.Alloc(dst, GCHandleType.Pinned); var addr = handle.AddrOfPinnedObject(); var size = Unsafe.SizeOf<T>(); var srcAddr = (byte*)src._buffer + srcIndex * size; var dstAddr = (byte*)addr + dstIndex * size; Unsafe.CopyBlock(dstAddr, srcAddr, (uint)(length * size)); handle.Free(); } #endregion internal sealed class NativeArrayDebugView<TU> where TU : unmanaged { private NativeArray<TU> _array; public NativeArrayDebugView(NativeArray<TU> array) => _array = array; public TU[] Items => _array.ToArray(); } } }