A game about forced loneliness, made by TACStudios
1using System;
2using System.Diagnostics;
3using Unity.Collections.LowLevel.Unsafe;
4using Unity.Burst;
5using static Unity.Collections.AllocatorManager;
6
7namespace Unity.Collections
8{
9 /// <summary>
10 /// Extension methods for NativeArray.
11 /// </summary>
12 [GenerateTestsForBurstCompatibility]
13 public unsafe static class NativeArrayExtensions
14 {
15 /// <summary>
16 /// Provides a Burst compatible id for NativeArray<typeparamref name="T"/> types. Used by the Job Safety System.
17 /// </summary>
18 /// <typeparam name="T"></typeparam>
19 public struct NativeArrayStaticId<T>
20 where T : unmanaged
21 {
22 internal static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<NativeArray<T>>();
23 }
24
25 /// <summary>
26 /// Returns true if a particular value is present in this array.
27 /// </summary>
28 /// <typeparam name="T">The type of elements in this array.</typeparam>
29 /// <typeparam name="U">The value type.</typeparam>
30 /// <param name="array">The array to search.</param>
31 /// <param name="value">The value to locate.</param>
32 /// <returns>True if the value is present in this array.</returns>
33 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
34 public static bool Contains<T, U>(this NativeArray<T> array, U value) where T : unmanaged, IEquatable<U>
35 {
36 return IndexOf<T, U>(array.GetUnsafeReadOnlyPtr(), array.Length, value) != -1;
37 }
38
39 /// <summary>
40 /// Finds the index of the first occurrence of a particular value in this array.
41 /// </summary>
42 /// <typeparam name="T">The type of elements in this array.</typeparam>
43 /// <typeparam name="U">The value type.</typeparam>
44 /// <param name="array">The array to search.</param>
45 /// <param name="value">The value to locate.</param>
46 /// <returns>The index of the first occurrence of the value in this array. Returns -1 if no occurrence is found.</returns>
47 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
48 public static int IndexOf<T, U>(this NativeArray<T> array, U value) where T : unmanaged, IEquatable<U>
49 {
50 return IndexOf<T, U>(array.GetUnsafeReadOnlyPtr(), array.Length, value);
51 }
52
53 /// <summary>
54 /// Returns true if a particular value is present in this array.
55 /// </summary>
56 /// <typeparam name="T">The type of elements in this array.</typeparam>
57 /// <typeparam name="U">The value type.</typeparam>
58 /// <param name="array">The array to search.</param>
59 /// <param name="value">The value to locate.</param>
60 /// <returns>True if the value is present in this array.</returns>
61 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
62 public static bool Contains<T, U>(this NativeArray<T>.ReadOnly array, U value) where T : unmanaged, IEquatable<U>
63 {
64 return IndexOf<T, U>(array.GetUnsafeReadOnlyPtr(), array.m_Length, value) != -1;
65 }
66
67 /// <summary>
68 /// Finds the index of the first occurrence of a particular value in this array.
69 /// </summary>
70 /// <typeparam name="T">The type of elements in this array.</typeparam>
71 /// <typeparam name="U">The type of value to locate.</typeparam>
72 /// <param name="array">The array to search.</param>
73 /// <param name="value">The value to locate.</param>
74 /// <returns>The index of the first occurrence of the value in this array. Returns -1 if no occurrence is found.</returns>
75 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
76 public static int IndexOf<T, U>(this NativeArray<T>.ReadOnly array, U value) where T : unmanaged, IEquatable<U>
77 {
78 return IndexOf<T, U>(array.GetUnsafeReadOnlyPtr(), array.m_Length, value);
79 }
80
81 /// <summary>
82 /// Returns true if a particular value is present in a buffer.
83 /// </summary>
84 /// <typeparam name="T">The type of elements in the buffer.</typeparam>
85 /// <typeparam name="U">The value type.</typeparam>
86 /// <param name="ptr">The buffer.</param>
87 /// <param name="length">Number of elements in the buffer.</param>
88 /// <param name="value">The value to locate.</param>
89 /// <returns>True if the value is present in the buffer.</returns>
90 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
91 public static bool Contains<T, U>(void* ptr, int length, U value) where T : unmanaged, IEquatable<U>
92 {
93 return IndexOf<T, U>(ptr, length, value) != -1;
94 }
95
96 /// <summary>
97 /// Finds the index of the first occurrence of a particular value in a buffer.
98 /// </summary>
99 /// <typeparam name="T">The type of elements in the buffer.</typeparam>
100 /// <typeparam name="U">The value type.</typeparam>
101 /// <param name="ptr">A buffer.</param>
102 /// <param name="length">Number of elements in the buffer.</param>
103 /// <param name="value">The value to locate.</param>
104 /// <returns>The index of the first occurrence of the value in the buffer. Returns -1 if no occurrence is found.</returns>
105 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
106 public static int IndexOf<T, U>(void* ptr, int length, U value) where T : unmanaged, IEquatable<U>
107 {
108 for (int i = 0; i != length; i++)
109 {
110 if (UnsafeUtility.ReadArrayElement<T>(ptr, i).Equals(value))
111 return i;
112 }
113 return -1;
114 }
115
116 /// <summary>
117 /// Copies all elements of specified container to array.
118 /// </summary>
119 /// <typeparam name="T">The type of elements in this container.</typeparam>
120 /// <param name="container">Container to copy to.</param>
121 /// <param name="other">An container to copy into this array.</param>
122 /// <exception cref="ArgumentException">Thrown if the array and container have unequal length.</exception>
123 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
124 public static void CopyFrom<T>(this ref NativeArray<T> container, NativeList<T> other)
125 where T : unmanaged, IEquatable<T>
126 {
127 container.CopyFrom(other.AsArray());
128 }
129
130 /// <summary>
131 /// Copies all elements of specified container to array.
132 /// </summary>
133 /// <typeparam name="T">The type of elements in this container.</typeparam>
134 /// <param name="container">Container to copy to.</param>
135 /// <param name="other">An container to copy into this array.</param>
136 /// <exception cref="ArgumentException">Thrown if the array and container have unequal length.</exception>
137 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
138 public static void CopyFrom<T>(this ref NativeArray<T> container, in NativeHashSet<T> other)
139 where T : unmanaged, IEquatable<T>
140 {
141 using (var array = other.ToNativeArray(Allocator.TempJob))
142 {
143 container.CopyFrom(array);
144 }
145 }
146
147 /// <summary>
148 /// Copies all elements of specified container to array.
149 /// </summary>
150 /// <typeparam name="T">The type of elements in this container.</typeparam>
151 /// <param name="container">Container to copy to.</param>
152 /// <param name="other">An container to copy into this array.</param>
153 /// <exception cref="ArgumentException">Thrown if the array and container have unequal length.</exception>
154 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
155 public static void CopyFrom<T>(this ref NativeArray<T> container, in UnsafeHashSet<T> other)
156 where T : unmanaged, IEquatable<T>
157 {
158 using (var array = other.ToNativeArray(Allocator.TempJob))
159 {
160 container.CopyFrom(array);
161 }
162 }
163
164 /// <summary>
165 /// Returns the reinterpretation of this array into another kind of NativeArray.
166 /// See [Array reinterpretation](https://docs.unity3d.com/Packages/com.unity.collections@latest?subfolder=/manual/allocation.html#array-reinterpretation).
167 /// </summary>
168 /// <param name="array">The array to reinterpret.</param>
169 /// <typeparam name="T">Type of elements in the array.</typeparam>
170 /// <typeparam name="U">Type of elements in the reinterpreted array.</typeparam>
171 /// <returns>The reinterpretation of this array into another kind of NativeArray.</returns>
172 /// <exception cref="InvalidOperationException">Thrown if this array's capacity cannot be evenly divided by `sizeof(U)`.</exception>
173 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
174 public static NativeArray<U> Reinterpret<T, U>(this NativeArray<T> array)
175 where U : unmanaged
176 where T : unmanaged
177 {
178 var tSize = UnsafeUtility.SizeOf<T>();
179 var uSize = UnsafeUtility.SizeOf<U>();
180
181 var byteLen = ((long)array.Length) * tSize;
182 var uLen = byteLen / uSize;
183
184 CheckReinterpretSize<T, U>(ref array);
185
186 var ptr = NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array);
187 var result = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<U>(ptr, (int)uLen, Allocator.None);
188
189#if ENABLE_UNITY_COLLECTIONS_CHECKS
190 var handle = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(array);
191 NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref result, handle);
192#endif
193
194 return result;
195 }
196
197 /// <summary>
198 /// Returns true if this array and another have equal length and content.
199 /// </summary>
200 /// <typeparam name="T">The type of the source array's elements.</typeparam>
201 /// <param name="container">The array to compare for equality.</param>
202 /// <param name="other">The other array to compare for equality.</param>
203 /// <returns>True if the arrays have equal length and content.</returns>
204 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
205 public static bool ArraysEqual<T>(this NativeArray<T> container, NativeArray<T> other)
206 where T : unmanaged, IEquatable<T>
207 {
208 if (container.Length != other.Length)
209 return false;
210
211 for (int i = 0; i != container.Length; i++)
212 {
213 if (!container[i].Equals(other[i]))
214 return false;
215 }
216
217 return true;
218 }
219
220 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
221 static void CheckReinterpretSize<T, U>(ref NativeArray<T> array)
222 where U : unmanaged
223 where T : unmanaged
224 {
225 var tSize = UnsafeUtility.SizeOf<T>();
226 var uSize = UnsafeUtility.SizeOf<U>();
227
228 var byteLen = ((long)array.Length) * tSize;
229 var uLen = byteLen / uSize;
230
231 if (uLen * uSize != byteLen)
232 {
233 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.");
234 }
235 }
236
237 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
238 internal static void Initialize<T>(ref this NativeArray<T> array,
239 int length,
240 AllocatorManager.AllocatorHandle allocator,
241 NativeArrayOptions options = NativeArrayOptions.ClearMemory)
242 where T : unmanaged
243 {
244 AllocatorHandle handle = allocator;
245 array = default;
246 array.m_Buffer = handle.AllocateStruct(default(T), length);
247 array.m_Length = length;
248 array.m_AllocatorLabel = allocator.IsAutoDispose ? Allocator.None : allocator.ToAllocator;
249 if (options == NativeArrayOptions.ClearMemory)
250 {
251 UnsafeUtility.MemClear(array.m_Buffer, array.m_Length * UnsafeUtility.SizeOf<T>());
252 }
253
254#if ENABLE_UNITY_COLLECTIONS_CHECKS
255 array.m_MinIndex = 0;
256 array.m_MaxIndex = length - 1;
257 array.m_Safety = CollectionHelper.CreateSafetyHandle(allocator);
258
259 CollectionHelper.SetStaticSafetyId<NativeArray<T>>(ref array.m_Safety, ref NativeArrayStaticId<T>.s_staticSafetyId.Data);
260 handle.AddSafetyHandle(array.m_Safety);
261#endif
262 }
263
264 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(AllocatorManager.AllocatorHandle) })]
265 internal static void Initialize<T, U>(ref this NativeArray<T> array,
266 int length,
267 ref U allocator,
268 NativeArrayOptions options = NativeArrayOptions.ClearMemory)
269 where T : unmanaged
270 where U : unmanaged, AllocatorManager.IAllocator
271 {
272 array = default;
273 array.m_Buffer = allocator.AllocateStruct(default(T), length);
274 array.m_Length = length;
275 array.m_AllocatorLabel = allocator.IsAutoDispose ? Allocator.None : allocator.ToAllocator;
276 if (options == NativeArrayOptions.ClearMemory)
277 {
278 UnsafeUtility.MemClear(array.m_Buffer, array.m_Length * UnsafeUtility.SizeOf<T>());
279 }
280
281#if ENABLE_UNITY_COLLECTIONS_CHECKS
282 array.m_MinIndex = 0;
283 array.m_MaxIndex = length - 1;
284 array.m_Safety = CollectionHelper.CreateSafetyHandle(allocator.ToAllocator);
285
286 CollectionHelper.SetStaticSafetyId<NativeArray<T>>(ref array.m_Safety, ref NativeArrayStaticId<T>.s_staticSafetyId.Data);
287 allocator.Handle.AddSafetyHandle(array.m_Safety);
288#endif
289 }
290
291 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
292 internal static void DisposeCheckAllocator<T>(ref this NativeArray<T> array)
293 where T : unmanaged
294 {
295 if (array.m_Buffer == null)
296 {
297 throw new ObjectDisposedException("The NativeArray is already disposed.");
298 }
299
300 if (!AllocatorManager.IsCustomAllocator(array.m_AllocatorLabel))
301 {
302 array.Dispose();
303 }
304 else
305 {
306#if ENABLE_UNITY_COLLECTIONS_CHECKS
307 AtomicSafetyHandle.DisposeHandle(ref array.m_Safety);
308#endif
309 AllocatorManager.Free(array.m_AllocatorLabel, array.m_Buffer);
310 array.m_AllocatorLabel = Allocator.Invalid;
311
312 array.m_Buffer = null;
313 }
314 }
315 }
316}