A game about forced loneliness, made by TACStudios
1using System;
2using System.Diagnostics;
3using System.Runtime.InteropServices;
4using Unity.Collections.LowLevel.Unsafe;
5using Unity.Burst;
6using Unity.Burst.CompilerServices;
7using Unity.Jobs;
8using Unity.Jobs.LowLevel.Unsafe;
9using Unity.Mathematics;
10using System.Reflection;
11using System.Runtime.CompilerServices;
12
13namespace Unity.Collections
14{
15 /// <summary>
16 /// For scheduling release of unmanaged resources.
17 /// </summary>
18 public interface INativeDisposable : IDisposable
19 {
20 /// <summary>
21 /// Creates and schedules a job that will release all resources (memory and safety handles) of this collection.
22 /// </summary>
23 /// <param name="inputDeps">A job handle which the newly scheduled job will depend upon.</param>
24 /// <returns>The handle of a new job that will release all resources (memory and safety handles) of this collection.</returns>
25 JobHandle Dispose(JobHandle inputDeps);
26 }
27
28 /// <summary>
29 /// Provides helper methods for collections.
30 /// </summary>
31 [GenerateTestsForBurstCompatibility]
32 public static class CollectionHelper
33 {
34 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
35 internal static void CheckAllocator(AllocatorManager.AllocatorHandle allocator)
36 {
37 if (!ShouldDeallocate(allocator))
38 throw new ArgumentException($"Allocator {allocator} must not be None or Invalid");
39 }
40
41 /// <summary>
42 /// The size in bytes of the current platform's L1 cache lines.
43 /// </summary>
44 /// <value>The size in bytes of the current platform's L1 cache lines.</value>
45 public const int CacheLineSize = JobsUtility.CacheLineSize;
46
47 [StructLayout(LayoutKind.Explicit)]
48 internal struct LongDoubleUnion
49 {
50 [FieldOffset(0)]
51 internal long longValue;
52
53 [FieldOffset(0)]
54 internal double doubleValue;
55 }
56
57 /// <summary>
58 /// Returns the binary logarithm of the `value`, but the result is rounded down to the nearest integer.
59 /// </summary>
60 /// <param name="value">The value.</param>
61 /// <returns>The binary logarithm of the `value`, but the result is rounded down to the nearest integer.</returns>
62 public static int Log2Floor(int value)
63 {
64 return 31 - math.lzcnt((uint)value);
65 }
66
67 /// <summary>
68 /// Returns the binary logarithm of the `value`, but the result is rounded up to the nearest integer.
69 /// </summary>
70 /// <param name="value">The value.</param>
71 /// <returns>The binary logarithm of the `value`, but the result is rounded up to the nearest integer.</returns>
72 public static int Log2Ceil(int value)
73 {
74 return 32 - math.lzcnt((uint)value - 1);
75 }
76
77 /// <summary>
78 /// Returns an allocation size in bytes that factors in alignment.
79 /// </summary>
80 /// <example><code>
81 /// // 55 aligned to 16 is 64.
82 /// int size = CollectionHelper.Align(55, 16);
83 /// </code></example>
84 /// <param name="size">The size to align.</param>
85 /// <param name="alignmentPowerOfTwo">A non-zero, positive power of two.</param>
86 /// <returns>The smallest integer that is greater than or equal to `size` and is a multiple of `alignmentPowerOfTwo`.</returns>
87 /// <exception cref="ArgumentException">Thrown if `alignmentPowerOfTwo` is not a non-zero, positive power of two.</exception>
88 public static int Align(int size, int alignmentPowerOfTwo)
89 {
90 if (alignmentPowerOfTwo == 0)
91 return size;
92
93 CheckIntPositivePowerOfTwo(alignmentPowerOfTwo);
94
95 return (size + alignmentPowerOfTwo - 1) & ~(alignmentPowerOfTwo - 1);
96 }
97
98 /// <summary>
99 /// Returns an allocation size in bytes that factors in alignment.
100 /// </summary>
101 /// <example><code>
102 /// // 55 aligned to 16 is 64.
103 /// ulong size = CollectionHelper.Align(55, 16);
104 /// </code></example>
105 /// <param name="size">The size to align.</param>
106 /// <param name="alignmentPowerOfTwo">A non-zero, positive power of two.</param>
107 /// <returns>The smallest integer that is greater than or equal to `size` and is a multiple of `alignmentPowerOfTwo`.</returns>
108 /// <exception cref="ArgumentException">Thrown if `alignmentPowerOfTwo` is not a non-zero, positive power of two.</exception>
109 public static ulong Align(ulong size, ulong alignmentPowerOfTwo)
110 {
111 if (alignmentPowerOfTwo == 0)
112 return size;
113
114 CheckUlongPositivePowerOfTwo(alignmentPowerOfTwo);
115
116 return (size + alignmentPowerOfTwo - 1) & ~(alignmentPowerOfTwo - 1);
117 }
118
119 /// <summary>
120 /// Returns true if the address represented by the pointer has a given alignment.
121 /// </summary>
122 /// <param name="p">The pointer.</param>
123 /// <param name="alignmentPowerOfTwo">A non-zero, positive power of two.</param>
124 /// <returns>True if the address is a multiple of `alignmentPowerOfTwo`.</returns>
125 /// <exception cref="ArgumentException">Thrown if `alignmentPowerOfTwo` is not a non-zero, positive power of two.</exception>
126 public static unsafe bool IsAligned(void* p, int alignmentPowerOfTwo)
127 {
128 CheckIntPositivePowerOfTwo(alignmentPowerOfTwo);
129 return ((ulong)p & ((ulong)alignmentPowerOfTwo - 1)) == 0;
130 }
131
132 /// <summary>
133 /// Returns true if an offset has a given alignment.
134 /// </summary>
135 /// <param name="offset">An offset</param>
136 /// <param name="alignmentPowerOfTwo">A non-zero, positive power of two.</param>
137 /// <returns>True if the offset is a multiple of `alignmentPowerOfTwo`.</returns>
138 /// <exception cref="ArgumentException">Thrown if `alignmentPowerOfTwo` is not a non-zero, positive power of two.</exception>
139 public static bool IsAligned(ulong offset, int alignmentPowerOfTwo)
140 {
141 CheckIntPositivePowerOfTwo(alignmentPowerOfTwo);
142 return (offset & ((ulong)alignmentPowerOfTwo - 1)) == 0;
143 }
144
145 /// <summary>
146 /// Returns true if a positive value is a non-zero power of two.
147 /// </summary>
148 /// <remarks>Result is invalid if `value < 0`.</remarks>
149 /// <param name="value">A positive value.</param>
150 /// <returns>True if the value is a non-zero, positive power of two.</returns>
151 public static bool IsPowerOfTwo(int value)
152 {
153 return (value & (value - 1)) == 0;
154 }
155
156 /// <summary>
157 /// Returns a (non-cryptographic) hash of a memory block.
158 /// </summary>
159 /// <remarks>The hash function used is [djb2](http://web.archive.org/web/20190508211657/http://www.cse.yorku.ca/~oz/hash.html).</remarks>
160 /// <param name="ptr">A buffer.</param>
161 /// <param name="bytes">The number of bytes to hash.</param>
162 /// <returns>A hash of the bytes.</returns>
163 public static unsafe uint Hash(void* ptr, int bytes)
164 {
165 // djb2 - Dan Bernstein hash function
166 // http://web.archive.org/web/20190508211657/http://www.cse.yorku.ca/~oz/hash.html
167 byte* str = (byte*)ptr;
168 ulong hash = 5381;
169 while (bytes > 0)
170 {
171 ulong c = str[--bytes];
172 hash = ((hash << 5) + hash) + c;
173 }
174 return (uint)hash;
175 }
176
177 [ExcludeFromBurstCompatTesting("Used only for debugging, and uses managed strings")]
178 internal static void WriteLayout(Type type)
179 {
180 Console.WriteLine($" Offset | Bytes | Name Layout: {0}", type.Name);
181 var fields = type.GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
182 foreach (var field in fields)
183 {
184 Console.WriteLine(" {0, 6} | {1, 6} | {2}"
185 , Marshal.OffsetOf(type, field.Name)
186 , Marshal.SizeOf(field.FieldType)
187 , field.Name
188 );
189 }
190 }
191
192 internal static bool ShouldDeallocate(AllocatorManager.AllocatorHandle allocator)
193 {
194 // Allocator.Invalid == container is not initialized.
195 // Allocator.None == container is initialized, but container doesn't own data.
196 return allocator.ToAllocator > Allocator.None;
197 }
198
199 /// <summary>
200 /// Tell Burst that an integer can be assumed to map to an always positive value.
201 /// </summary>
202 /// <param name="value">The integer that is always positive.</param>
203 /// <returns>Returns `x`, but allows the compiler to assume it is always positive.</returns>
204 [return: AssumeRange(0, int.MaxValue)]
205 [MethodImpl(MethodImplOptions.AggressiveInlining)]
206 internal static int AssumePositive(int value)
207 {
208 return value;
209 }
210
211 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
212 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS", GenericTypeArguments = new[] { typeof(NativeArray<int>) })]
213 internal static void CheckIsUnmanaged<T>()
214 {
215 if (!UnsafeUtility.IsUnmanaged<T>())
216 {
217 throw new ArgumentException($"{typeof(T)} used in native collection is not blittable or not primitive");
218 }
219 }
220
221#if ENABLE_UNITY_COLLECTIONS_CHECKS
222 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS", GenericTypeArguments = new[] { typeof(NativeArray<int>) })]
223 internal static void InitNativeContainer<T>(AtomicSafetyHandle handle)
224 {
225 if (UnsafeUtility.IsNativeContainerType<T>())
226 AtomicSafetyHandle.SetNestedContainer(handle, true);
227 }
228#endif
229
230 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
231 internal static void CheckIntPositivePowerOfTwo(int value)
232 {
233 var valid = (value > 0) && ((value & (value - 1)) == 0);
234 if (!valid)
235 {
236 throw new ArgumentException($"Alignment requested: {value} is not a non-zero, positive power of two.");
237 }
238 }
239
240 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
241 internal static void CheckUlongPositivePowerOfTwo(ulong value)
242 {
243 var valid = (value > 0) && ((value & (value - 1)) == 0);
244 if (!valid)
245 {
246 throw new ArgumentException($"Alignment requested: {value} is not a non-zero, positive power of two.");
247 }
248 }
249
250 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
251 [MethodImpl(MethodImplOptions.AggressiveInlining)]
252 internal static void CheckIndexInRange(int index, int length)
253 {
254 // This checks both < 0 and >= Length with one comparison
255 if ((uint)index >= (uint)length)
256 throw new IndexOutOfRangeException($"Index {index} is out of range in container of '{length}' Length.");
257 }
258
259 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
260 internal static void CheckCapacityInRange(int capacity, int length)
261 {
262 if (capacity < 0)
263 throw new ArgumentOutOfRangeException($"Capacity {capacity} must be positive.");
264
265 if (capacity < length)
266 throw new ArgumentOutOfRangeException($"Capacity {capacity} is out of range in container of '{length}' Length.");
267 }
268
269 /// <summary>
270 /// Create a NativeArray, using a provided allocator that implements IAllocator.
271 /// </summary>
272 /// <typeparam name="T">The type of the elements.</typeparam>
273 /// <typeparam name="U">The type of allocator.</typeparam>
274 /// <param name="length">The number of elements to allocate.</param>
275 /// <param name="allocator">The allocator to use.</param>
276 /// <param name="options">Options for allocation, such as whether to clear the memory.</param>
277 /// <returns>Returns the NativeArray that was created.</returns>
278 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(AllocatorManager.AllocatorHandle) })]
279 public static NativeArray<T> CreateNativeArray<T, U>(int length, ref U allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory)
280 where T : unmanaged
281 where U : unmanaged, AllocatorManager.IAllocator
282 {
283 NativeArray<T> nativeArray;
284 if (!allocator.IsCustomAllocator)
285 {
286 nativeArray = new NativeArray<T>(length, allocator.ToAllocator, options);
287 }
288 else
289 {
290 nativeArray = new NativeArray<T>();
291 nativeArray.Initialize(length, ref allocator, options);
292 }
293 return nativeArray;
294 }
295
296 /// <summary>
297 /// Create a NativeArray, using a provided AllocatorHandle.
298 /// </summary>
299 /// <typeparam name="T">The type of the elements.</typeparam>
300 /// <param name="length">The number of elements to allocate.</param>
301 /// <param name="allocator">The AllocatorHandle to use.</param>
302 /// <param name="options">Options for allocation, such as whether to clear the memory.</param>
303 /// <returns>Returns the NativeArray that was created.</returns>
304 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
305 public static NativeArray<T> CreateNativeArray<T>(int length, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.ClearMemory)
306 where T : unmanaged
307 {
308 NativeArray<T> nativeArray;
309 if(!AllocatorManager.IsCustomAllocator(allocator))
310 {
311 nativeArray = new NativeArray<T>(length, allocator.ToAllocator, options);
312 }
313 else
314 {
315 nativeArray = new NativeArray<T>();
316 nativeArray.Initialize(length, allocator, options);
317 }
318 return nativeArray;
319 }
320
321 /// <summary>
322 /// Create a NativeArray from another NativeArray, using a provided AllocatorHandle.
323 /// </summary>
324 /// <typeparam name="T">The type of the elements.</typeparam>
325 /// <param name="array">The NativeArray to make a copy of.</param>
326 /// <param name="allocator">The AllocatorHandle to use.</param>
327 /// <returns>Returns the NativeArray that was created.</returns>
328 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
329 public static NativeArray<T> CreateNativeArray<T>(NativeArray<T> array, AllocatorManager.AllocatorHandle allocator)
330 where T : unmanaged
331 {
332 NativeArray<T> nativeArray;
333 if (!AllocatorManager.IsCustomAllocator(allocator))
334 {
335 nativeArray = new NativeArray<T>(array, allocator.ToAllocator);
336 }
337 else
338 {
339 nativeArray = new NativeArray<T>();
340 nativeArray.Initialize(array.Length, allocator);
341 nativeArray.CopyFrom(array);
342 }
343 return nativeArray;
344 }
345
346 /// <summary>
347 /// Create a NativeArray from a managed array, using a provided AllocatorHandle.
348 /// </summary>
349 /// <typeparam name="T">The type of the elements.</typeparam>
350 /// <param name="array">The managed array to make a copy of.</param>
351 /// <param name="allocator">The AllocatorHandle to use.</param>
352 /// <returns>Returns the NativeArray that was created.</returns>
353 [ExcludeFromBurstCompatTesting("Managed array")]
354 public static NativeArray<T> CreateNativeArray<T>(T[] array, AllocatorManager.AllocatorHandle allocator)
355 where T : unmanaged
356 {
357 NativeArray<T> nativeArray;
358 if (!AllocatorManager.IsCustomAllocator(allocator))
359 {
360 nativeArray = new NativeArray<T>(array, allocator.ToAllocator);
361 }
362 else
363 {
364 nativeArray = new NativeArray<T>();
365 nativeArray.Initialize(array.Length, allocator);
366 nativeArray.CopyFrom(array);
367 }
368 return nativeArray;
369 }
370
371 /// <summary>
372 /// Create a NativeArray from a managed array, using a provided Allocator.
373 /// </summary>
374 /// <typeparam name="T">The type of the elements.</typeparam>
375 /// <typeparam name="U">The type of allocator.</typeparam>
376 /// <param name="array">The managed array to make a copy of.</param>
377 /// <param name="allocator">The Allocator to use.</param>
378 /// <returns>Returns the NativeArray that was created.</returns>
379 [ExcludeFromBurstCompatTesting("Managed array")]
380 public static NativeArray<T> CreateNativeArray<T, U>(T[] array, ref U allocator)
381 where T : unmanaged
382 where U : unmanaged, AllocatorManager.IAllocator
383 {
384 NativeArray<T> nativeArray;
385 if (!allocator.IsCustomAllocator)
386 {
387 nativeArray = new NativeArray<T>(array, allocator.ToAllocator);
388 }
389 else
390 {
391 nativeArray = new NativeArray<T>();
392 nativeArray.Initialize(array.Length, ref allocator);
393 nativeArray.CopyFrom(array);
394 }
395 return nativeArray;
396 }
397
398 /// <summary>
399 /// Dispose a NativeArray from an AllocatorHandle where it is allocated.
400 /// </summary>
401 /// <typeparam name="T">The type of the elements.</typeparam>
402 /// <param name="nativeArray">The NativeArray to make a copy of.</param>
403 /// <param name="allocator">The AllocatorHandle used to allocate the NativeArray.</param>
404 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
405 public static void DisposeNativeArray<T>(NativeArray<T> nativeArray, AllocatorManager.AllocatorHandle allocator)
406 where T : unmanaged
407 {
408 nativeArray.DisposeCheckAllocator();
409 }
410
411 /// <summary>
412 /// Dispose a NativeArray from an AllocatorHandle where it is allocated.
413 /// </summary>
414 /// <typeparam name="T">The type of the elements.</typeparam>
415 /// <param name="nativeArray">The NativeArray to be disposed.</param>
416 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
417 public static void Dispose<T>(NativeArray<T> nativeArray)
418 where T : unmanaged
419 {
420 nativeArray.DisposeCheckAllocator();
421 }
422
423 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
424 static void CheckConvertArguments<T>(int length) where T : unmanaged
425 {
426 if (length < 0)
427 throw new ArgumentOutOfRangeException(nameof(length), "Length must be >= 0");
428
429 if (!UnsafeUtility.IsUnmanaged<T>())
430 {
431 throw new InvalidOperationException(
432 $"{typeof(T)} used in NativeArray<{typeof(T)}> must be unmanaged (contain no managed types).");
433 }
434 }
435
436#if ENABLE_UNITY_COLLECTIONS_CHECKS
437 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
438 static void InitNestedNativeContainer<T>(AtomicSafetyHandle handle)
439 where T : unmanaged
440 {
441 if (UnsafeUtility.IsNativeContainerType<T>())
442 {
443 AtomicSafetyHandle.SetNestedContainer(handle, true);
444 }
445 }
446#endif
447
448 /// <summary>
449 /// Convert existing data into a NativeArray.
450 /// </summary>
451 /// <typeparam name="T">The type of the elements.</typeparam>
452 /// <param name="dataPointer">Pointer to the data to be converted.</param>
453 /// <param name="length">The count of elements.</param>
454 /// <param name="allocator">The Allocator to use.</param>
455 /// <param name="setTempMemoryHandle">Use temporary memory atomic safety handle.</param>
456 /// <returns>Returns the NativeArray that was created.</returns>
457 /// <remarks>The caller is still the owner of the data.</remarks>
458 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
459 public static unsafe NativeArray<T> ConvertExistingDataToNativeArray<T>(void* dataPointer, int length, AllocatorManager.AllocatorHandle allocator, bool setTempMemoryHandle = false)
460 where T : unmanaged
461 {
462#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG
463 CheckConvertArguments<T>(length);
464#endif
465 NativeArray<T> nativeArray = default;
466
467 nativeArray.m_Buffer = dataPointer;
468 nativeArray.m_Length = length;
469 if (!allocator.IsCustomAllocator)
470 {
471 nativeArray.m_AllocatorLabel = allocator.ToAllocator;
472 }
473 else
474 {
475 nativeArray.m_AllocatorLabel = Allocator.None;
476 }
477
478#if ENABLE_UNITY_COLLECTIONS_CHECKS
479 nativeArray.m_MinIndex = 0;
480 nativeArray.m_MaxIndex = length - 1;
481 if (setTempMemoryHandle)
482 {
483 NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref nativeArray, AtomicSafetyHandle.GetTempMemoryHandle());
484 }
485#endif
486 return nativeArray;
487 }
488
489 /// <summary>
490 /// Convert NativeList into a NativeArray.
491 /// </summary>
492 /// <typeparam name="T">The type of the elements.</typeparam>
493 /// <param name="nativeList">NativeList to be converted.</param>
494 /// <param name="length">The count of elements.</param>
495 /// <param name="allocator">The Allocator to use.</param>
496 /// <returns>Returns the NativeArray that was created.</returns>
497 /// <remarks>There is a caveat if users would like to transfer memory ownership from the NativeList to the converted NativeArray.
498 /// NativeList implementation includes two memory allocations, one holds its header, another holds the list data.
499 /// After convertion, the converted NativeArray holds the list data and dispose the array only free the list data.
500 /// Users need to manually free the list header to avoid memory leaks, for example after convertion call,
501 /// AllocatorManager.Free(allocator, nativeList.m_ListData); </remarks>
502 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
503 public static unsafe NativeArray<T> ConvertExistingNativeListToNativeArray<T>(ref NativeList<T> nativeList, int length, AllocatorManager.AllocatorHandle allocator)
504 where T : unmanaged
505 {
506 NativeArray<T> nativeArray = ConvertExistingDataToNativeArray<T>(nativeList.GetUnsafePtr(), length, allocator);
507
508#if ENABLE_UNITY_COLLECTIONS_CHECKS
509 var safetyHandle = NativeListUnsafeUtility.GetAtomicSafetyHandle(ref nativeList);
510 NativeArrayUnsafeUtility.SetAtomicSafetyHandle<T>(ref nativeArray, safetyHandle);
511 InitNestedNativeContainer<T>(nativeArray.m_Safety);
512#endif
513 return nativeArray;
514 }
515
516#if ENABLE_UNITY_COLLECTIONS_CHECKS
517 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) }, RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS", CompileTarget = GenerateTestsForBurstCompatibilityAttribute.BurstCompatibleCompileTarget.Editor)]
518 internal static AtomicSafetyHandle GetNativeArraySafetyHandle<T>(ref NativeArray<T> nativeArray)
519 where T : unmanaged
520 {
521 return nativeArray.m_Safety;
522 }
523#endif
524
525 /// <summary>
526 /// Create a NativeParallelMultiHashMap from a managed array, using a provided Allocator.
527 /// </summary>
528 /// <typeparam name="TKey">The type of the keys.</typeparam>
529 /// <typeparam name="TValue">The type of the values.</typeparam>
530 /// <typeparam name="U">The type of allocator.</typeparam>
531 /// <param name="length">The desired capacity of the NativeParallelMultiHashMap.</param>
532 /// <param name="allocator">The Allocator to use.</param>
533 /// <returns>Returns the NativeParallelMultiHashMap that was created.</returns>
534 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(int), typeof(AllocatorManager.AllocatorHandle) })]
535 public static NativeParallelMultiHashMap<TKey, TValue> CreateNativeParallelMultiHashMap<TKey, TValue, U>(int length, ref U allocator)
536 where TKey : unmanaged, IEquatable<TKey>
537 where TValue : unmanaged
538 where U : unmanaged, AllocatorManager.IAllocator
539 {
540 var container = new NativeParallelMultiHashMap<TKey, TValue>();
541 container.Initialize(length, ref allocator);
542 return container;
543 }
544
545 /// <summary>
546 /// Empty job type used for Burst compilation testing
547 /// </summary>
548 [BurstCompile]
549 public struct DummyJob : IJob
550 {
551 /// <summary>
552 /// Empty job execute function used for Burst compilation testing
553 /// </summary>
554 public void Execute()
555 {
556 }
557 }
558
559 /// <summary>
560 /// Checks that reflection data was properly registered for a job.
561 /// </summary>
562 /// <remarks>This should be called before instantiating JobsUtility.JobScheduleParameters in order to report to the user if they need to take action.</remarks>
563 /// <param name="reflectionData">The reflection data pointer.</param>
564 /// <typeparam name="T">Job type</typeparam>
565 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS",
566 GenericTypeArguments = new[] { typeof(DummyJob) })]
567 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
568 public static void CheckReflectionDataCorrect<T>(IntPtr reflectionData)
569 {
570#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG
571 bool burstCompiled = true;
572 CheckReflectionDataCorrectInternal<T>(reflectionData, ref burstCompiled);
573 if (burstCompiled && reflectionData == IntPtr.Zero)
574 throw new InvalidOperationException("Reflection data was not set up by an Initialize() call. For generic job types, please include [assembly: RegisterGenericJobType(typeof(MyJob<MyJobSpecialization>))] in your source file.");
575#endif
576 }
577
578#if ENABLE_UNITY_COLLECTIONS_CHECKS
579 /// <summary>
580 /// Creates a new AtomicSafetyHandle that is valid until [[CollectionHelper.DisposeSafetyHandle]] is called.
581 /// </summary>
582 /// <param name="allocator">The AllocatorHandle to use.</param>
583 /// <returns>Safety handle.</returns>
584 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS")]
585 public static AtomicSafetyHandle CreateSafetyHandle(AllocatorManager.AllocatorHandle allocator)
586 {
587 if (allocator.IsCustomAllocator)
588 {
589 return AtomicSafetyHandle.Create();
590 }
591
592 return (allocator.ToAllocator == Allocator.Temp) ? AtomicSafetyHandle.GetTempMemoryHandle() : AtomicSafetyHandle.Create();
593 }
594
595 /// <summary>
596 /// Disposes a previously created AtomicSafetyHandle.
597 /// </summary>
598 /// <param name="handle">Safety handle.</param>
599 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS")]
600 public static void DisposeSafetyHandle(ref AtomicSafetyHandle handle)
601 {
602 AtomicSafetyHandle.CheckDeallocateAndThrow(handle);
603 // If the safety handle is for a temp allocation, create a new safety handle for this instance which can be marked as invalid
604 // Setting it to new AtomicSafetyHandle is not enough since the handle needs a valid node pointer in order to give the correct errors
605 if (AtomicSafetyHandle.IsTempMemoryHandle(handle))
606 {
607 int staticSafetyId = handle.staticSafetyId;
608 handle = AtomicSafetyHandle.Create();
609 handle.staticSafetyId = staticSafetyId;
610 }
611 AtomicSafetyHandle.Release(handle);
612 }
613
614 static unsafe void CreateStaticSafetyIdInternal(ref int id, in FixedString512Bytes name)
615 {
616 id = AtomicSafetyHandle.NewStaticSafetyId(name.GetUnsafePtr(), name.Length);
617 }
618
619 [BurstDiscard]
620 static void CreateStaticSafetyIdInternal<T>(ref int id)
621 {
622 CreateStaticSafetyIdInternal(ref id, typeof(T).ToString());
623 }
624
625 /// <summary>
626 /// Assigns the provided static safety ID to an [[AtomicSafetyHandle]]. The ID's owner type name and any custom error messages are used by the job debugger when reporting errors involving the target handle.
627 /// </summary>
628 /// <remarks>This is preferable to AtomicSafetyHandle.NewStaticSafetyId as it is compatible with burst.</remarks>
629 /// <typeparam name="T">Type of container safety handle refers to.</typeparam>
630 /// <param name="handle">Safety handle.</param>
631 /// <param name="sharedStaticId">The static safety ID to associate with the provided handle. This ID must have been allocated with ::ref::NewStaticSafetyId.</param>
632 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS", GenericTypeArguments = new[] { typeof(NativeArray<int>) })]
633 public static void SetStaticSafetyId<T>(ref AtomicSafetyHandle handle, ref int sharedStaticId)
634 {
635 if (sharedStaticId == 0)
636 {
637 // This will eventually either work with burst supporting a subset of typeof()
638 // or something similar to Burst.BurstRuntime.GetTypeName() will be implemented
639 // JIRA issue DOTS-5685
640
641 CreateStaticSafetyIdInternal<T>(ref sharedStaticId);
642 }
643
644 AtomicSafetyHandle.SetStaticSafetyId(ref handle, sharedStaticId);
645 }
646
647 /// <summary>
648 /// Assigns the provided static safety ID to an [[AtomicSafetyHandle]]. The ID's owner type name and any custom error messages are used by the job debugger when reporting errors involving the target handle.
649 /// </summary>
650 /// <remarks>This is preferable to AtomicSafetyHandle.NewStaticSafetyId as it is compatible with burst.</remarks>
651 /// <param name="handle">Safety handle.</param>
652 /// <param name="sharedStaticId">The static safety ID to associate with the provided handle. This ID must have been allocated with ::ref::NewStaticSafetyId.</param>
653 /// <param name="name">The name of the resource type.</param>
654 [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS")]
655 public static unsafe void SetStaticSafetyId(ref AtomicSafetyHandle handle, ref int sharedStaticId, FixedString512Bytes name)
656 {
657 if (sharedStaticId == 0)
658 {
659 CreateStaticSafetyIdInternal(ref sharedStaticId, name);
660 }
661
662 AtomicSafetyHandle.SetStaticSafetyId(ref handle, sharedStaticId);
663 }
664#endif
665 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
666 [BurstDiscard]
667 static void CheckReflectionDataCorrectInternal<T>(IntPtr reflectionData, ref bool burstCompiled)
668 {
669 if (reflectionData == IntPtr.Zero)
670 throw new InvalidOperationException($"Reflection data was not set up by an Initialize() call. For generic job types, please include [assembly: RegisterGenericJobType(typeof({typeof(T)}))] in your source file.");
671 burstCompiled = false;
672 }
673 }
674}