// Unity C# reference source // Copyright (c) Unity Technologies. For terms of use, see // https://unity3d.com/legal/licenses/Unity_Reference_Only_License using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.Runtime.InteropServices; using System.Runtime.CompilerServices; using Unity.Burst; using Unity.Jobs; using Unity.Collections.LowLevel.Unsafe; using UnityEngine; using UnityEngine.Bindings; using UnityEngine.Internal; using UnityEngine.Scripting; namespace Unity.Collections { public enum NativeArrayOptions { UninitializedMemory = 0, ClearMemory = 1 } [StructLayout(LayoutKind.Sequential)] [NativeContainer] [NativeContainerSupportsMinMaxWriteRestriction] [NativeContainerSupportsDeallocateOnJobCompletion] [NativeContainerSupportsDeferredConvertListToArray] [DebuggerDisplay("Length = {m_Length}")] [DebuggerTypeProxy(typeof(NativeArrayDebugView<>))] public unsafe struct NativeArray : IDisposable, IEnumerable, IEquatable> where T : struct { [NativeDisableUnsafePtrRestriction] internal void* m_Buffer; internal int m_Length; internal int m_MinIndex; internal int m_MaxIndex; internal AtomicSafetyHandle m_Safety; internal unsafe ref DisposeSentinel.Dummy m_DisposeSentinel { get { void* pointer = UnsafeUtility.Malloc(sizeof(DisposeSentinel.Dummy), 8, Allocator.Temp); return ref UnsafeUtility.AsRef(pointer); } } // TODO: Use SharedStatic for burst compatible static id once we have typehash intrinsic for unity in burst 1.6.5 and 1.7.0 static int s_staticSafetyId; [BurstDiscard] static void InitStaticSafetyId(ref AtomicSafetyHandle handle) { if (s_staticSafetyId == 0) s_staticSafetyId = AtomicSafetyHandle.NewStaticSafetyId>(); AtomicSafetyHandle.SetStaticSafetyId(ref handle, s_staticSafetyId); } internal Allocator m_AllocatorLabel; public NativeArray(int length, Allocator allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory) { Allocate(length, allocator, out this); if ((options & NativeArrayOptions.ClearMemory) == NativeArrayOptions.ClearMemory) UnsafeUtility.MemClear(m_Buffer, (long)Length * UnsafeUtility.SizeOf()); } public NativeArray(T[] array, Allocator allocator) { if (array == null) throw new ArgumentNullException(nameof(array)); Allocate(array.Length, allocator, out this); Copy(array, this); } public NativeArray(NativeArray array, Allocator allocator) { AtomicSafetyHandle.CheckReadAndThrow(array.m_Safety); Allocate(array.Length, allocator, out this); Copy(array, 0, this, 0, array.Length); } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] static void CheckAllocateArguments(int length, Allocator allocator) { // Native allocation is only valid for Temp, Job and Persistent. if (allocator <= Allocator.None) throw new ArgumentException("Allocator must be Temp, TempJob or Persistent", nameof(allocator)); // NativeArray constructor does not support custom allocator if (allocator >= Allocator.FirstUserIndex) throw new ArgumentException("Use CollectionHelper.CreateNativeArray in com.unity.collections package for custom allocator", nameof(allocator)); if (length < 0) throw new ArgumentOutOfRangeException(nameof(length), "Length must be >= 0"); } static void Allocate(int length, Allocator allocator, out NativeArray array) { long totalSize = UnsafeUtility.SizeOf() * (long)length; CheckAllocateArguments(length, allocator); array = default(NativeArray); IsUnmanagedAndThrow(); array.m_Buffer = UnsafeUtility.MallocTracked(totalSize, UnsafeUtility.AlignOf(), allocator, 0); array.m_Length = length; array.m_AllocatorLabel = allocator; array.m_MinIndex = 0; array.m_MaxIndex = length - 1; AtomicSafetyHandle.CreateHandle(out array.m_Safety, allocator); InitStaticSafetyId(ref array.m_Safety); InitNestedNativeContainer(array.m_Safety); } public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return m_Length; } } internal static void InitNestedNativeContainer(AtomicSafetyHandle handle) { if (UnsafeUtility.IsNativeContainerType()) { AtomicSafetyHandle.SetNestedContainer(handle, true); } } // NativeArray is not constrained to unmanaged so it must be checked [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] [BurstDiscard] internal static void IsUnmanagedAndThrow() { if (!UnsafeUtility.IsUnmanaged()) { throw new InvalidOperationException( $"{typeof(T)} used in NativeArray<{typeof(T)}> must be unmanaged (contain no managed types)."); } } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] [MethodImpl(MethodImplOptions.AggressiveInlining)] void CheckElementReadAccess(int index) { if (index < m_MinIndex || index > m_MaxIndex) FailOutOfRangeError(index); AtomicSafetyHandle.CheckReadAndThrow(m_Safety); } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] [MethodImpl(MethodImplOptions.AggressiveInlining)] void CheckElementWriteAccess(int index) { if (index < m_MinIndex || index > m_MaxIndex) FailOutOfRangeError(index); AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); } public T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { CheckElementReadAccess(index); return UnsafeUtility.ReadArrayElement(m_Buffer, index); } [MethodImpl(MethodImplOptions.AggressiveInlining)] [WriteAccessRequired] set { CheckElementWriteAccess(index); UnsafeUtility.WriteArrayElement(m_Buffer, index, value); } } public bool IsCreated { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => m_Buffer != null; } [WriteAccessRequired] public void Dispose() { if (m_AllocatorLabel != Allocator.None && !AtomicSafetyHandle.IsDefaultValue(m_Safety)) { AtomicSafetyHandle.CheckExistsAndThrow(m_Safety); } if (!IsCreated) { return; } if (m_AllocatorLabel == Allocator.Invalid) { throw new InvalidOperationException("The NativeArray can not be Disposed because it was not allocated with a valid allocator."); } if (m_AllocatorLabel >= Allocator.FirstUserIndex) { throw new InvalidOperationException("The NativeArray can not be Disposed because it was allocated with a custom allocator, use CollectionHelper.Dispose in com.unity.collections package."); } if (m_AllocatorLabel > Allocator.None) { AtomicSafetyHandle.DisposeHandle(ref m_Safety); UnsafeUtility.FreeTracked(m_Buffer, m_AllocatorLabel); m_AllocatorLabel = Allocator.Invalid; } m_Buffer = null; } public JobHandle Dispose(JobHandle inputDeps) { if (m_AllocatorLabel != Allocator.None && !AtomicSafetyHandle.IsDefaultValue(m_Safety)) { AtomicSafetyHandle.CheckExistsAndThrow(m_Safety); } if (!IsCreated) { return inputDeps; } if (m_AllocatorLabel >= Allocator.FirstUserIndex) { throw new InvalidOperationException("The NativeArray can not be Disposed because it was allocated with a custom allocator, use CollectionHelper.Dispose in com.unity.collections package."); } if (m_AllocatorLabel > Allocator.None) { // [DeallocateOnJobCompletion] is not supported, but we want the deallocation // to happen in a thread. DisposeSentinel needs to be cleared on main thread. // AtomicSafetyHandle can be destroyed after the job was scheduled (Job scheduling // will check that no jobs are writing to the container). var jobHandle = new NativeArrayDisposeJob { Data = new NativeArrayDispose { m_Buffer = m_Buffer, m_AllocatorLabel = m_AllocatorLabel, m_Safety = m_Safety } }.Schedule(inputDeps); AtomicSafetyHandle.Release(m_Safety); m_Buffer = null; m_AllocatorLabel = Allocator.Invalid; return jobHandle; } m_Buffer = null; return inputDeps; } [WriteAccessRequired] public void CopyFrom(T[] array) { Copy(array, this); } [WriteAccessRequired] public void CopyFrom(NativeArray array) { Copy(array, this); } public void CopyTo(T[] array) { Copy(this, array); } public void CopyTo(NativeArray array) { Copy(this, array); } public T[] ToArray() { var array = new T[Length]; Copy(this, array, Length); return array; } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] void FailOutOfRangeError(int index) { if (index < Length && (m_MinIndex != 0 || m_MaxIndex != Length - 1)) throw new IndexOutOfRangeException( $"Index {index} is out of restricted IJobParallelFor range [{m_MinIndex}...{m_MaxIndex}] in ReadWriteBuffer.\n" + "ReadWriteBuffers are restricted to only read & write the element at the job index. " + "You can use double buffering strategies to avoid race conditions due to " + "reading & writing in parallel to the same elements from a job."); throw new IndexOutOfRangeException($"Index {index} is out of range of '{Length}' Length."); } public Enumerator GetEnumerator() { return new Enumerator(ref this); } IEnumerator IEnumerable.GetEnumerator() { return new Enumerator(ref this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } [ExcludeFromDocs] public struct Enumerator : IEnumerator { NativeArray m_Array; int m_Index; T value; public Enumerator(ref NativeArray array) { m_Array = array; m_Index = -1; value = default; } public void Dispose() { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { m_Index++; if (m_Index < m_Array.m_Length) { AtomicSafetyHandle.CheckReadAndThrow(m_Array.m_Safety); value = UnsafeUtility.ReadArrayElement(m_Array.m_Buffer, m_Index); return true; } value = default; return false; } public void Reset() { m_Index = -1; } // Let NativeArray indexer check for out of range. public T Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return value; } } object IEnumerator.Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return Current; } } } public bool Equals(NativeArray other) { return m_Buffer == other.m_Buffer && m_Length == other.m_Length; } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; return obj is NativeArray && Equals((NativeArray)obj); } public override int GetHashCode() { unchecked { return ((int)m_Buffer * 397) ^ m_Length; } } public static bool operator==(NativeArray left, NativeArray right) { return left.Equals(right); } public static bool operator!=(NativeArray left, NativeArray right) { return !left.Equals(right); } public static void Copy(NativeArray src, NativeArray dst) { CheckCopyLengths(src.Length, dst.Length); CopySafe(src, 0, dst, 0, src.Length); } public static void Copy(ReadOnly src, NativeArray dst) { CheckCopyLengths(src.Length, dst.Length); CopySafe(src, 0, dst, 0, src.Length); } public static void Copy(T[] src, NativeArray dst) { CheckCopyLengths(src.Length, dst.Length); CopySafe(src, 0, dst, 0, src.Length); } public static void Copy(NativeArray src, T[] dst) { CheckCopyLengths(src.Length, dst.Length); CopySafe(src, 0, dst, 0, src.Length); } public static void Copy(ReadOnly src, T[] dst) { CheckCopyLengths(src.Length, dst.Length); CopySafe(src, 0, dst, 0, src.Length); } public static void Copy(NativeArray src, NativeArray dst, int length) { CopySafe(src, 0, dst, 0, length); } public static void Copy(ReadOnly src, NativeArray dst, int length) { CopySafe(src, 0, dst, 0, length); } public static void Copy(T[] src, NativeArray dst, int length) { CopySafe(src, 0, dst, 0, length); } public static void Copy(NativeArray src, T[] dst, int length) { CopySafe(src, 0, dst, 0, length); } public static void Copy(ReadOnly src, T[] dst, int length) { CopySafe(src, 0, dst, 0, length); } public static void Copy(NativeArray src, int srcIndex, NativeArray dst, int dstIndex, int length) { CopySafe(src, srcIndex, dst, dstIndex, length); } public static void Copy(ReadOnly src, int srcIndex, NativeArray dst, int dstIndex, int length) { CopySafe(src, srcIndex, dst, dstIndex, length); } public static void Copy(T[] src, int srcIndex, NativeArray dst, int dstIndex, int length) { CopySafe(src, srcIndex, dst, dstIndex, length); } public static void Copy(NativeArray src, int srcIndex, T[] dst, int dstIndex, int length) { CopySafe(src, srcIndex, dst, dstIndex, length); } public static void Copy(ReadOnly src, int srcIndex, T[] dst, int dstIndex, int length) { CopySafe(src, srcIndex, dst, dstIndex, length); } static void CopySafe(NativeArray src, int srcIndex, NativeArray dst, int dstIndex, int length) { AtomicSafetyHandle.CheckReadAndThrow(src.m_Safety); AtomicSafetyHandle.CheckWriteAndThrow(dst.m_Safety); CheckCopyArguments(src.Length, srcIndex, dst.Length, dstIndex, length); UnsafeUtility.MemCpy( (byte*)dst.m_Buffer + dstIndex * UnsafeUtility.SizeOf(), (byte*)src.m_Buffer + srcIndex * UnsafeUtility.SizeOf(), length * UnsafeUtility.SizeOf()); } static void CopySafe(ReadOnly src, int srcIndex, NativeArray dst, int dstIndex, int length) { AtomicSafetyHandle.CheckReadAndThrow(src.m_Safety); AtomicSafetyHandle.CheckWriteAndThrow(dst.m_Safety); CheckCopyArguments(src.Length, srcIndex, dst.Length, dstIndex, length); UnsafeUtility.MemCpy( (byte*)dst.m_Buffer + dstIndex * UnsafeUtility.SizeOf(), (byte*)src.m_Buffer + srcIndex * UnsafeUtility.SizeOf(), length * UnsafeUtility.SizeOf()); } static void CopySafe(T[] src, int srcIndex, NativeArray dst, int dstIndex, int length) { AtomicSafetyHandle.CheckWriteAndThrow(dst.m_Safety); CheckCopyPtr(src); CheckCopyArguments(src.Length, srcIndex, dst.Length, dstIndex, length); var handle = GCHandle.Alloc(src, GCHandleType.Pinned); var addr = handle.AddrOfPinnedObject(); UnsafeUtility.MemCpy( (byte*)dst.m_Buffer + dstIndex * UnsafeUtility.SizeOf(), (byte*)addr + srcIndex * UnsafeUtility.SizeOf(), length * UnsafeUtility.SizeOf()); handle.Free(); } static void CopySafe(NativeArray src, int srcIndex, T[] dst, int dstIndex, int length) { AtomicSafetyHandle.CheckReadAndThrow(src.m_Safety); CheckCopyPtr(dst); CheckCopyArguments(src.Length, srcIndex, dst.Length, dstIndex, length); var handle = GCHandle.Alloc(dst, GCHandleType.Pinned); var addr = handle.AddrOfPinnedObject(); UnsafeUtility.MemCpy( (byte*)addr + dstIndex * UnsafeUtility.SizeOf(), (byte*)src.m_Buffer + srcIndex * UnsafeUtility.SizeOf(), length * UnsafeUtility.SizeOf()); handle.Free(); } static void CopySafe(ReadOnly src, int srcIndex, T[] dst, int dstIndex, int length) { AtomicSafetyHandle.CheckReadAndThrow(src.m_Safety); CheckCopyPtr(dst); CheckCopyArguments(src.Length, srcIndex, dst.Length, dstIndex, length); var handle = GCHandle.Alloc(dst, GCHandleType.Pinned); var addr = handle.AddrOfPinnedObject(); UnsafeUtility.MemCpy( (byte*)addr + dstIndex * UnsafeUtility.SizeOf(), (byte*)src.m_Buffer + srcIndex * UnsafeUtility.SizeOf(), length * UnsafeUtility.SizeOf()); handle.Free(); } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] static void CheckCopyPtr(T[] ptr) { if (ptr == null) throw new ArgumentNullException(nameof(ptr)); } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] static void CheckCopyLengths(int srcLength, int dstLength) { if (srcLength != dstLength) throw new ArgumentException("source and destination length must be the same"); } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] static void CheckCopyArguments(int srcLength, int srcIndex, int dstLength, int dstIndex, int length) { if (length < 0) throw new ArgumentOutOfRangeException(nameof(length), "length must be equal or greater than zero."); if (srcIndex < 0 || srcIndex > srcLength || (srcIndex == srcLength && srcLength > 0)) throw new ArgumentOutOfRangeException(nameof(srcIndex), "srcIndex is outside the range of valid indexes for the source NativeArray."); if (dstIndex < 0 || dstIndex > dstLength || (dstIndex == dstLength && dstLength > 0)) throw new ArgumentOutOfRangeException(nameof(dstIndex), "dstIndex is outside the range of valid indexes for the destination NativeArray."); if (srcIndex + length > srcLength) throw new ArgumentException("length is greater than the number of elements from srcIndex to the end of the source NativeArray.", nameof(length)); if (srcIndex + length < 0) throw new ArgumentException("srcIndex + length causes an integer overflow"); if (dstIndex + length > dstLength) throw new ArgumentException("length is greater than the number of elements from dstIndex to the end of the destination NativeArray.", nameof(length)); if (dstIndex + length < 0) throw new ArgumentException("dstIndex + length causes an integer overflow"); } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] void CheckReinterpretLoadRange(int sourceIndex) where U : struct { long tsize = UnsafeUtility.SizeOf(); AtomicSafetyHandle.CheckReadAndThrow(m_Safety); long usize = UnsafeUtility.SizeOf(); long byteSize = Length * tsize; long firstByte = sourceIndex * tsize; long lastByte = firstByte + usize; if (firstByte < 0 || lastByte > byteSize) throw new ArgumentOutOfRangeException(nameof(sourceIndex), "loaded byte range must fall inside container bounds"); } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] void CheckReinterpretStoreRange(int destIndex) where U : struct { long tsize = UnsafeUtility.SizeOf(); AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); long usize = UnsafeUtility.SizeOf(); long byteSize = Length * tsize; long firstByte = destIndex * tsize; long lastByte = firstByte + usize; if (firstByte < 0 || lastByte > byteSize) throw new ArgumentOutOfRangeException(nameof(destIndex), "stored byte range must fall inside container bounds"); } public U ReinterpretLoad(int sourceIndex) where U : struct { CheckReinterpretLoadRange(sourceIndex); byte* src_ptr = ((byte*)m_Buffer) + ((long)UnsafeUtility.SizeOf()) * sourceIndex; return UnsafeUtility.ReadArrayElement(src_ptr, 0); } public void ReinterpretStore(int destIndex, U data) where U : struct { CheckReinterpretStoreRange(destIndex); byte* dst_ptr = ((byte*)m_Buffer) + ((long)UnsafeUtility.SizeOf()) * destIndex; UnsafeUtility.WriteArrayElement(dst_ptr, 0, data); } private NativeArray InternalReinterpret(int length) where U : struct { var result = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(m_Buffer, length, m_AllocatorLabel); NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref result, m_Safety); return result; } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] static void CheckReinterpretSize() where U : struct { if (UnsafeUtility.SizeOf() != UnsafeUtility.SizeOf()) { throw new InvalidOperationException($"Types {typeof(T)} and {typeof(U)} are different sizes - direct reinterpretation is not possible. If this is what you intended, use Reinterpret()"); } } public NativeArray Reinterpret() where U : struct { CheckReinterpretSize(); return InternalReinterpret(Length); } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] void CheckReinterpretSize(long tSize, long uSize, int expectedTypeSize, long byteLen, long uLen) { if (tSize != expectedTypeSize) { throw new InvalidOperationException($"Type {typeof(T)} was expected to be {expectedTypeSize} but is {tSize} bytes"); } if (uLen * uSize != byteLen) { throw new InvalidOperationException($"Types {typeof(T)} (array length {Length}) and {typeof(U)} cannot be aliased due to size constraints. The size of the types and lengths involved must line up."); } } public NativeArray Reinterpret(int expectedTypeSize) where U : struct { long tSize = UnsafeUtility.SizeOf(); long uSize = UnsafeUtility.SizeOf(); long byteLen = ((long)Length) * tSize; long uLen = byteLen / uSize; CheckReinterpretSize(tSize, uSize, expectedTypeSize, byteLen, uLen); return InternalReinterpret((int)uLen); } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] void CheckGetSubArrayArguments(int start, int length) { if (start < 0) { throw new ArgumentOutOfRangeException(nameof(start), "start must be >= 0"); } if (start + length > Length) { throw new ArgumentOutOfRangeException(nameof(length), $"sub array range {start}-{start + length - 1} is outside the range of the native array 0-{Length - 1}"); } if (start + length < 0) { throw new ArgumentException($"sub array range {start}-{start + length - 1} caused an integer overflow and is outside the range of the native array 0-{Length - 1}"); } } public NativeArray GetSubArray(int start, int length) { CheckGetSubArrayArguments(start, length); var result = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(((byte*)m_Buffer) + ((long)UnsafeUtility.SizeOf()) * start, length, Allocator.None); NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref result, m_Safety); return result; } public ReadOnly AsReadOnly() { return new ReadOnly(m_Buffer, m_Length, ref m_Safety); } [StructLayout(LayoutKind.Sequential)] [NativeContainer] [NativeContainerIsReadOnly] [DebuggerDisplay("Length = {Length}")] [DebuggerTypeProxy(typeof(NativeArrayReadOnlyDebugView<>))] public struct ReadOnly : IEnumerable { [NativeDisableUnsafePtrRestriction] internal void* m_Buffer; internal int m_Length; internal AtomicSafetyHandle m_Safety; internal ReadOnly(void* buffer, int length, ref AtomicSafetyHandle safety) { m_Buffer = buffer; m_Length = length; m_Safety = safety; } public int Length { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { return m_Length; } } public void CopyTo(T[] array) => Copy(this, array); public void CopyTo(NativeArray array) => Copy(this, array); public T[] ToArray() { var array = new T[m_Length]; Copy(this, array, m_Length); return array; } public NativeArray.ReadOnly Reinterpret() where U : struct { CheckReinterpretSize(); return new NativeArray.ReadOnly(m_Buffer, m_Length, ref m_Safety); } public T this[int index] { [MethodImpl(MethodImplOptions.AggressiveInlining)] get { CheckElementReadAccess(index); return UnsafeUtility.ReadArrayElement(m_Buffer, index); } } // This method does not copy T, but returns a readonly T. // It is marked as unsafe because the value returned by this method can become invalid at any time, for example, if the container was disposed. public ref readonly T UnsafeElementAt(int index) { CheckElementReadAccess(index); return ref UnsafeUtility.ArrayElementAsRef(m_Buffer, index); } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] [MethodImpl(MethodImplOptions.AggressiveInlining)] void CheckElementReadAccess(int index) { if ((uint)index >= (uint)m_Length) { throw new IndexOutOfRangeException($"Index {index} is out of range (must be between 0 and {m_Length-1})."); } AtomicSafetyHandle.CheckReadAndThrow(m_Safety); } public bool IsCreated { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => m_Buffer != null; } [ExcludeFromDocs] public struct Enumerator : IEnumerator { ReadOnly m_Array; int m_Index; T value; public Enumerator(in ReadOnly array) { m_Array = array; m_Index = -1; value = default; } public void Dispose() { } [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool MoveNext() { m_Index++; if (m_Index < m_Array.m_Length) { AtomicSafetyHandle.CheckReadAndThrow(m_Array.m_Safety); value = UnsafeUtility.ReadArrayElement(m_Array.m_Buffer, m_Index); return true; } value = default; return false; } public void Reset() { m_Index = -1; } // Let NativeArray indexer check for out of range. public T Current { [MethodImpl(MethodImplOptions.AggressiveInlining)] get => value; } object IEnumerator.Current => Current; } public Enumerator GetEnumerator() { return new Enumerator(this); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public readonly ReadOnlySpan AsReadOnlySpan() { AtomicSafetyHandle.CheckReadAndThrow(m_Safety); return new ReadOnlySpan(m_Buffer, m_Length); } public static implicit operator ReadOnlySpan(in ReadOnly source) { return source.AsReadOnlySpan(); } } [WriteAccessRequired] public readonly Span AsSpan() { AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); return new Span(m_Buffer, m_Length); } public readonly ReadOnlySpan AsReadOnlySpan() { AtomicSafetyHandle.CheckReadAndThrow(m_Safety); return new ReadOnlySpan(m_Buffer, m_Length); } public static implicit operator Span(in NativeArray source) { return source.AsSpan(); } public static implicit operator ReadOnlySpan(in NativeArray source) { return source.AsReadOnlySpan(); } } [NativeContainer] internal unsafe struct NativeArrayDispose { [NativeDisableUnsafePtrRestriction] internal void* m_Buffer; internal Allocator m_AllocatorLabel; internal AtomicSafetyHandle m_Safety; public void Dispose() { UnsafeUtility.FreeTracked(m_Buffer, m_AllocatorLabel); } } // [BurstCompile] - can't use attribute since it's inside com.unity.Burst. [NativeClass(null)] internal struct NativeArrayDisposeJob : IJob { internal NativeArrayDispose Data; public void Execute() { Data.Dispose(); } [RequiredByNativeCode] internal static void RegisterNativeArrayDisposeJobReflectionData() { // Necessary so we may schedule NativeArrayDisposeJob from // burst compiled codepaths IJobExtensions.EarlyJobInit(); } } /// /// DebuggerTypeProxy for /// internal unsafe sealed class NativeArrayDebugView where T : struct { NativeArray m_Array; public NativeArrayDebugView(NativeArray array) { m_Array = array; } public T[] Items { get { if (!m_Array.IsCreated) { return default; } // Trying to avoid safety checks, so that container can be read in debugger if it's safety handle // is in write-only mode. var length = m_Array.m_Length; var dst = new T[length]; var handle = GCHandle.Alloc(dst, GCHandleType.Pinned); var addr = handle.AddrOfPinnedObject(); UnsafeUtility.MemCpy((void*)addr, m_Array.m_Buffer, length * UnsafeUtility.SizeOf()); handle.Free(); return dst; } } } /// /// DebuggerTypeProxy for /// internal sealed class NativeArrayReadOnlyDebugView where T : struct { NativeArray.ReadOnly m_Array; public NativeArrayReadOnlyDebugView(NativeArray.ReadOnly array) { m_Array = array; } public T[] Items { get { if (!m_Array.IsCreated) { return default; } return m_Array.ToArray(); } } } } namespace Unity.Collections.LowLevel.Unsafe { public static class NativeArrayUnsafeUtility { public static AtomicSafetyHandle GetAtomicSafetyHandle(NativeArray array) where T : struct { return array.m_Safety; } public static void SetAtomicSafetyHandle(ref NativeArray array, AtomicSafetyHandle safety) where T : struct { array.m_Safety = safety; } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] private static void CheckConvertArguments(int length) where T : struct { if (length < 0) throw new ArgumentOutOfRangeException(nameof(length), "Length must be >= 0"); NativeArray.IsUnmanagedAndThrow(); } /// Internal method used typically by other systems to provide a view on them. /// The caller is still the owner of the data. public static unsafe NativeArray ConvertExistingDataToNativeArray(void* dataPointer, int length, Allocator allocator) where T : struct { CheckConvertArguments(length); var newArray = new NativeArray { m_Buffer = dataPointer, m_Length = length, m_AllocatorLabel = allocator, m_MinIndex = 0, m_MaxIndex = length - 1, }; return newArray; } public static unsafe void* GetUnsafePtr(this NativeArray nativeArray) where T : struct { AtomicSafetyHandle.CheckWriteAndThrow(nativeArray.m_Safety); return nativeArray.m_Buffer; } public static unsafe void* GetUnsafeReadOnlyPtr(this NativeArray nativeArray) where T : struct { AtomicSafetyHandle.CheckReadAndThrow(nativeArray.m_Safety); return nativeArray.m_Buffer; } public static unsafe void* GetUnsafeReadOnlyPtr(this NativeArray.ReadOnly nativeArray) where T : struct { AtomicSafetyHandle.CheckReadAndThrow(nativeArray.m_Safety); return nativeArray.m_Buffer; } public static unsafe void* GetUnsafeBufferPointerWithoutChecks(NativeArray nativeArray) where T : struct { return nativeArray.m_Buffer; } } }