using System; using System.Diagnostics; using Unity.Collections.LowLevel.Unsafe; using Unity.Burst; using static Unity.Collections.AllocatorManager; namespace Unity.Collections { /// /// Extension methods for NativeArray. /// [GenerateTestsForBurstCompatibility] public unsafe static class NativeArrayExtensions { /// /// Provides a Burst compatible id for NativeArray types. Used by the Job Safety System. /// /// public struct NativeArrayStaticId where T : unmanaged { internal static readonly SharedStatic s_staticSafetyId = SharedStatic.GetOrCreate>(); } /// /// Returns true if a particular value is present in this array. /// /// The type of elements in this array. /// The value type. /// The array to search. /// The value to locate. /// True if the value is present in this array. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })] public static bool Contains(this NativeArray array, U value) where T : unmanaged, IEquatable { return IndexOf(array.GetUnsafeReadOnlyPtr(), array.Length, value) != -1; } /// /// Finds the index of the first occurrence of a particular value in this array. /// /// The type of elements in this array. /// The value type. /// The array to search. /// The value to locate. /// The index of the first occurrence of the value in this array. Returns -1 if no occurrence is found. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })] public static int IndexOf(this NativeArray array, U value) where T : unmanaged, IEquatable { return IndexOf(array.GetUnsafeReadOnlyPtr(), array.Length, value); } /// /// Returns true if a particular value is present in this array. /// /// The type of elements in this array. /// The value type. /// The array to search. /// The value to locate. /// True if the value is present in this array. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })] public static bool Contains(this NativeArray.ReadOnly array, U value) where T : unmanaged, IEquatable { return IndexOf(array.GetUnsafeReadOnlyPtr(), array.m_Length, value) != -1; } /// /// Finds the index of the first occurrence of a particular value in this array. /// /// The type of elements in this array. /// The type of value to locate. /// The array to search. /// The value to locate. /// The index of the first occurrence of the value in this array. Returns -1 if no occurrence is found. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })] public static int IndexOf(this NativeArray.ReadOnly array, U value) where T : unmanaged, IEquatable { return IndexOf(array.GetUnsafeReadOnlyPtr(), array.m_Length, value); } /// /// Returns true if a particular value is present in a buffer. /// /// The type of elements in the buffer. /// The value type. /// The buffer. /// Number of elements in the buffer. /// The value to locate. /// True if the value is present in the buffer. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })] public static bool Contains(void* ptr, int length, U value) where T : unmanaged, IEquatable { return IndexOf(ptr, length, value) != -1; } /// /// Finds the index of the first occurrence of a particular value in a buffer. /// /// The type of elements in the buffer. /// The value type. /// A buffer. /// Number of elements in the buffer. /// The value to locate. /// The index of the first occurrence of the value in the buffer. Returns -1 if no occurrence is found. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })] public static int IndexOf(void* ptr, int length, U value) where T : unmanaged, IEquatable { for (int i = 0; i != length; i++) { if (UnsafeUtility.ReadArrayElement(ptr, i).Equals(value)) return i; } return -1; } /// /// Copies all elements of specified container to array. /// /// The type of elements in this container. /// Container to copy to. /// An container to copy into this array. /// Thrown if the array and container have unequal length. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] public static void CopyFrom(this ref NativeArray container, NativeList other) where T : unmanaged, IEquatable { container.CopyFrom(other.AsArray()); } /// /// Copies all elements of specified container to array. /// /// The type of elements in this container. /// Container to copy to. /// An container to copy into this array. /// Thrown if the array and container have unequal length. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] public static void CopyFrom(this ref NativeArray container, in NativeHashSet other) where T : unmanaged, IEquatable { using (var array = other.ToNativeArray(Allocator.TempJob)) { container.CopyFrom(array); } } /// /// Copies all elements of specified container to array. /// /// The type of elements in this container. /// Container to copy to. /// An container to copy into this array. /// Thrown if the array and container have unequal length. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] public static void CopyFrom(this ref NativeArray container, in UnsafeHashSet other) where T : unmanaged, IEquatable { using (var array = other.ToNativeArray(Allocator.TempJob)) { container.CopyFrom(array); } } /// /// Returns the reinterpretation of this array into another kind of NativeArray. /// See [Array reinterpretation](https://docs.unity3d.com/Packages/com.unity.collections@latest?subfolder=/manual/allocation.html#array-reinterpretation). /// /// The array to reinterpret. /// Type of elements in the array. /// Type of elements in the reinterpreted array. /// The reinterpretation of this array into another kind of NativeArray. /// Thrown if this array's capacity cannot be evenly divided by `sizeof(U)`. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })] public static NativeArray Reinterpret(this NativeArray array) where U : unmanaged where T : unmanaged { var tSize = UnsafeUtility.SizeOf(); var uSize = UnsafeUtility.SizeOf(); var byteLen = ((long)array.Length) * tSize; var uLen = byteLen / uSize; CheckReinterpretSize(ref array); var ptr = NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array); var result = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(ptr, (int)uLen, Allocator.None); #if ENABLE_UNITY_COLLECTIONS_CHECKS var handle = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(array); NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref result, handle); #endif return result; } /// /// Returns true if this array and another have equal length and content. /// /// The type of the source array's elements. /// The array to compare for equality. /// The other array to compare for equality. /// True if the arrays have equal length and content. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })] public static bool ArraysEqual(this NativeArray container, NativeArray other) where T : unmanaged, IEquatable { if (container.Length != other.Length) return false; for (int i = 0; i != container.Length; i++) { if (!container[i].Equals(other[i])) return false; } return true; } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] static void CheckReinterpretSize(ref NativeArray array) where U : unmanaged where T : unmanaged { var tSize = UnsafeUtility.SizeOf(); var uSize = UnsafeUtility.SizeOf(); var byteLen = ((long)array.Length) * tSize; var uLen = byteLen / uSize; if (uLen * uSize != byteLen) { throw new InvalidOperationException($"Types {typeof(T)} (array length {array.Length}) and {typeof(U)} cannot be aliased due to size constraints. The size of the types and lengths involved must line up."); } } [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] internal static void Initialize(ref this NativeArray array, int length, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory) where T : unmanaged { AllocatorHandle handle = allocator; array = default; array.m_Buffer = handle.AllocateStruct(default(T), length); array.m_Length = length; array.m_AllocatorLabel = allocator.IsAutoDispose ? Allocator.None : allocator.ToAllocator; if (options == NativeArrayOptions.ClearMemory) { UnsafeUtility.MemClear(array.m_Buffer, array.m_Length * UnsafeUtility.SizeOf()); } #if ENABLE_UNITY_COLLECTIONS_CHECKS array.m_MinIndex = 0; array.m_MaxIndex = length - 1; array.m_Safety = CollectionHelper.CreateSafetyHandle(allocator); CollectionHelper.SetStaticSafetyId>(ref array.m_Safety, ref NativeArrayStaticId.s_staticSafetyId.Data); handle.AddSafetyHandle(array.m_Safety); #endif } [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(AllocatorManager.AllocatorHandle) })] internal static void Initialize(ref this NativeArray array, int length, ref U allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory) where T : unmanaged where U : unmanaged, AllocatorManager.IAllocator { array = default; array.m_Buffer = allocator.AllocateStruct(default(T), length); array.m_Length = length; array.m_AllocatorLabel = allocator.IsAutoDispose ? Allocator.None : allocator.ToAllocator; if (options == NativeArrayOptions.ClearMemory) { UnsafeUtility.MemClear(array.m_Buffer, array.m_Length * UnsafeUtility.SizeOf()); } #if ENABLE_UNITY_COLLECTIONS_CHECKS array.m_MinIndex = 0; array.m_MaxIndex = length - 1; array.m_Safety = CollectionHelper.CreateSafetyHandle(allocator.ToAllocator); CollectionHelper.SetStaticSafetyId>(ref array.m_Safety, ref NativeArrayStaticId.s_staticSafetyId.Data); allocator.Handle.AddSafetyHandle(array.m_Safety); #endif } [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })] internal static void DisposeCheckAllocator(ref this NativeArray array) where T : unmanaged { if (array.m_Buffer == null) { throw new ObjectDisposedException("The NativeArray is already disposed."); } if (!AllocatorManager.IsCustomAllocator(array.m_AllocatorLabel)) { array.Dispose(); } else { #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.DisposeHandle(ref array.m_Safety); #endif AllocatorManager.Free(array.m_AllocatorLabel, array.m_Buffer); array.m_AllocatorLabel = Allocator.Invalid; array.m_Buffer = null; } } } }