A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections;
3using System.Collections.Generic;
4using System.Diagnostics;
5using System.Runtime.CompilerServices;
6using System.Runtime.InteropServices;
7using System.Threading;
8using Unity.Burst;
9using Unity.Collections.LowLevel.Unsafe;
10using Unity.Jobs;
11
12namespace Unity.Collections
13{
14 /// <summary>
15 /// An indexable collection.
16 /// </summary>
17 /// <typeparam name="T">The type of the elements in the collection.</typeparam>
18 public interface IIndexable<T> where T : unmanaged
19 {
20 /// <summary>
21 /// The current number of elements in the collection.
22 /// </summary>
23 /// <value>The current number of elements in the collection.</value>
24 int Length { get; set; }
25
26 /// <summary>
27 /// Returns a reference to the element at a given index.
28 /// </summary>
29 /// <param name="index">The index to access. Must be in the range of [0..Length).</param>
30 /// <returns>A reference to the element at the index.</returns>
31 ref T ElementAt(int index);
32 }
33
34 /// <summary>
35 /// A resizable list.
36 /// </summary>
37 /// <typeparam name="T">The type of the elements.</typeparam>
38 public interface INativeList<T> : IIndexable<T> where T : unmanaged
39 {
40 /// <summary>
41 /// The number of elements that fit in the current allocation.
42 /// </summary>
43 /// <value>The number of elements that fit in the current allocation.</value>
44 /// <param name="value">A new capacity.</param>
45 int Capacity { get; set; }
46
47 /// <summary>
48 /// Whether this list is empty.
49 /// </summary>
50 /// <value>True if this list is empty.</value>
51 bool IsEmpty { get; }
52
53 /// <summary>
54 /// The element at an index.
55 /// </summary>
56 /// <param name="index">An index.</param>
57 /// <value>The element at the index.</value>
58 /// <exception cref="IndexOutOfRangeException">Thrown if index is out of bounds.</exception>
59 T this[int index] { get; set; }
60
61 /// <summary>
62 /// Sets the length to 0.
63 /// </summary>
64 /// <remarks>Does not change the capacity.</remarks>
65 void Clear();
66 }
67
68 /// <summary>
69 /// An unmanaged, resizable list.
70 /// </summary>
71 /// <remarks>The elements are stored contiguously in a buffer rather than as linked nodes.</remarks>
72 /// <typeparam name="T">The type of the elements.</typeparam>
73 [StructLayout(LayoutKind.Sequential)]
74 [NativeContainer]
75 [DebuggerDisplay("Length = {m_ListData == null ? default : m_ListData->Length}, Capacity = {m_ListData == null ? default : m_ListData->Capacity}")]
76 [DebuggerTypeProxy(typeof(NativeListDebugView<>))]
77 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
78 public unsafe struct NativeList<T>
79 : INativeDisposable
80 , INativeList<T>
81 , IEnumerable<T> // Used by collection initializers.
82 where T : unmanaged
83 {
84#if ENABLE_UNITY_COLLECTIONS_CHECKS
85 internal AtomicSafetyHandle m_Safety;
86 internal int m_SafetyIndexHint;
87 internal static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<NativeList<T>>();
88#endif
89
90 [NativeDisableUnsafePtrRestriction]
91 internal UnsafeList<T>* m_ListData;
92
93 /// <summary>
94 /// Initializes and returns a NativeList with a capacity of one.
95 /// </summary>
96 /// <param name="allocator">The allocator to use.</param>
97 public NativeList(AllocatorManager.AllocatorHandle allocator)
98 : this(1, allocator)
99 {
100 }
101
102 /// <summary>
103 /// Initializes and returns a NativeList.
104 /// </summary>
105 /// <param name="initialCapacity">The initial capacity of the list.</param>
106 /// <param name="allocator">The allocator to use.</param>
107 public NativeList(int initialCapacity, AllocatorManager.AllocatorHandle allocator)
108 {
109 this = default;
110 AllocatorManager.AllocatorHandle temp = allocator;
111 Initialize(initialCapacity, ref temp);
112 }
113
114 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(AllocatorManager.AllocatorHandle) })]
115 internal void Initialize<U>(int initialCapacity, ref U allocator) where U : unmanaged, AllocatorManager.IAllocator
116 {
117 var totalSize = sizeof(T) * (long)initialCapacity;
118#if ENABLE_UNITY_COLLECTIONS_CHECKS
119 CollectionHelper.CheckAllocator(allocator.Handle);
120 CheckInitialCapacity(initialCapacity);
121 CheckTotalSize(initialCapacity, totalSize);
122
123 m_Safety = CollectionHelper.CreateSafetyHandle(allocator.Handle);
124 CollectionHelper.InitNativeContainer<T>(m_Safety);
125
126 CollectionHelper.SetStaticSafetyId<NativeList<T>>(ref m_Safety, ref s_staticSafetyId.Data);
127
128 m_SafetyIndexHint = (allocator.Handle).AddSafetyHandle(m_Safety);
129
130 AtomicSafetyHandle.SetBumpSecondaryVersionOnScheduleWrite(m_Safety, true);
131#endif
132 m_ListData = UnsafeList<T>.Create(initialCapacity, ref allocator, NativeArrayOptions.UninitializedMemory);
133 }
134
135 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(AllocatorManager.AllocatorHandle) })]
136 internal static NativeList<T> New<U>(int initialCapacity, ref U allocator) where U : unmanaged, AllocatorManager.IAllocator
137 {
138 var nativelist = new NativeList<T>();
139 nativelist.Initialize(initialCapacity, ref allocator);
140 return nativelist;
141 }
142
143 /// <summary>
144 /// The element at a given index.
145 /// </summary>
146 /// <param name="index">An index into this list.</param>
147 /// <value>The value to store at the `index`.</value>
148 /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds.</exception>
149 public T this[int index]
150 {
151 [MethodImpl(MethodImplOptions.AggressiveInlining)]
152 get
153 {
154#if ENABLE_UNITY_COLLECTIONS_CHECKS
155 AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
156#endif
157 return (*m_ListData)[index];
158 }
159
160 [MethodImpl(MethodImplOptions.AggressiveInlining)]
161 set
162 {
163#if ENABLE_UNITY_COLLECTIONS_CHECKS
164 AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
165#endif
166 (*m_ListData)[index] = value;
167 }
168 }
169
170 /// <summary>
171 /// Returns a reference to the element at an index.
172 /// </summary>
173 /// <param name="index">An index.</param>
174 /// <returns>A reference to the element at the index.</returns>
175 /// <exception cref="IndexOutOfRangeException">Thrown if index is out of bounds.</exception>
176 public ref T ElementAt(int index)
177 {
178#if ENABLE_UNITY_COLLECTIONS_CHECKS
179 AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
180#endif
181 return ref m_ListData->ElementAt(index);
182 }
183
184 /// <summary>
185 /// The count of elements.
186 /// </summary>
187 /// <value>The current count of elements. Always less than or equal to the capacity.</value>
188 /// <remarks>To decrease the memory used by a list, set <see cref="Capacity"/> after reducing the length of the list.</remarks>
189 /// <param name="value>">The new length. If the new length is greater than the current capacity, the capacity is increased.
190 /// Newly allocated memory is cleared.</param>
191 public int Length
192 {
193 [MethodImpl(MethodImplOptions.AggressiveInlining)]
194 readonly get
195 {
196#if ENABLE_UNITY_COLLECTIONS_CHECKS
197 AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
198#endif
199 return CollectionHelper.AssumePositive(m_ListData->Length);
200 }
201
202 set
203 {
204 // Unity 2022.2.16f1 removes the global temp safety handle so only
205 // from this version onward is it not a breaking change to perform this check
206 // since all previous versions did not have this safety check and will
207 // likely break from safe usage that is blurred by sharing the temp safety handle
208 // between multiple containers
209#if ENABLE_UNITY_COLLECTIONS_CHECKS && UNITY_2022_2_16F1_OR_NEWER
210
211 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
212#endif
213 m_ListData->Resize(value, NativeArrayOptions.ClearMemory);
214 }
215 }
216
217 /// <summary>
218 /// The number of elements that fit in the current allocation.
219 /// </summary>
220 /// <value>The number of elements that fit in the current allocation.</value>
221 /// <param name="value">The new capacity. Must be greater or equal to the length.</param>
222 /// <exception cref="ArgumentOutOfRangeException">Thrown if the new capacity is smaller than the length.</exception>
223 public int Capacity
224 {
225 [MethodImpl(MethodImplOptions.AggressiveInlining)]
226 readonly get
227 {
228#if ENABLE_UNITY_COLLECTIONS_CHECKS
229 AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
230#endif
231 return m_ListData->Capacity;
232 }
233
234 set
235 {
236#if ENABLE_UNITY_COLLECTIONS_CHECKS
237 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
238#endif
239 m_ListData->Capacity = value;
240 }
241 }
242
243 /// <summary>
244 /// Returns the internal unsafe list.
245 /// </summary>
246 /// <remarks>Internally, the elements of a NativeList are stored in an UnsafeList.</remarks>
247 /// <returns>The internal unsafe list.</returns>
248 public UnsafeList<T>* GetUnsafeList() => m_ListData;
249
250 /// <summary>
251 /// Appends an element to the end of this list.
252 /// </summary>
253 /// <param name="value">The value to add to the end of this list.</param>
254 /// <remarks>
255 /// Length is incremented by 1. Will not increase the capacity.
256 /// </remarks>
257 /// <exception cref="InvalidOperationException">Thrown if incrementing the length would exceed the capacity.</exception>
258 public void AddNoResize(T value)
259 {
260#if ENABLE_UNITY_COLLECTIONS_CHECKS
261 AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
262#endif
263 m_ListData->AddNoResize(value);
264 }
265
266 /// <summary>
267 /// Appends elements from a buffer to the end of this list.
268 /// </summary>
269 /// <param name="ptr">The buffer to copy from.</param>
270 /// <param name="count">The number of elements to copy from the buffer.</param>
271 /// <remarks>
272 /// Length is increased by the count. Will not increase the capacity.
273 /// </remarks>
274 /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
275 public void AddRangeNoResize(void* ptr, int count)
276 {
277#if ENABLE_UNITY_COLLECTIONS_CHECKS
278 AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
279#endif
280 CheckArgPositive(count);
281 m_ListData->AddRangeNoResize(ptr, count);
282 }
283
284 /// <summary>
285 /// Appends the elements of another list to the end of this list.
286 /// </summary>
287 /// <param name="list">The other list to copy from.</param>
288 /// <remarks>
289 /// Length is increased by the length of the other list. Will not increase the capacity.
290 /// </remarks>
291 /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
292 public void AddRangeNoResize(NativeList<T> list)
293 {
294#if ENABLE_UNITY_COLLECTIONS_CHECKS
295 AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
296#endif
297 m_ListData->AddRangeNoResize(*list.m_ListData);
298 }
299
300 /// <summary>
301 /// Appends an element to the end of this list.
302 /// </summary>
303 /// <param name="value">The value to add to the end of this list.</param>
304 /// <remarks>
305 /// Length is incremented by 1. If necessary, the capacity is increased.
306 /// </remarks>
307 public void Add(in T value)
308 {
309#if ENABLE_UNITY_COLLECTIONS_CHECKS
310 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
311#endif
312 m_ListData->Add(in value);
313 }
314
315 /// <summary>
316 /// Appends the elements of an array to the end of this list.
317 /// </summary>
318 /// <param name="array">The array to copy from.</param>
319 /// <remarks>
320 /// Length is increased by the number of new elements. Does not increase the capacity.
321 /// </remarks>
322 /// <exception cref="ArgumentOutOfRangeException">Thrown if the increased length would exceed the capacity.</exception>
323 public void AddRange(NativeArray<T> array)
324 {
325 AddRange(array.GetUnsafeReadOnlyPtr(), array.Length);
326 }
327
328 /// <summary>
329 /// Appends the elements of a buffer to the end of this list.
330 /// </summary>
331 /// <param name="ptr">The buffer to copy from.</param>
332 /// <param name="count">The number of elements to copy from the buffer.</param>
333 /// <exception cref="ArgumentOutOfRangeException">Thrown if count is negative.</exception>
334 public void AddRange(void* ptr, int count)
335 {
336#if ENABLE_UNITY_COLLECTIONS_CHECKS
337 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
338#endif
339 CheckArgPositive(count);
340 m_ListData->AddRange(ptr, CollectionHelper.AssumePositive(count));
341 }
342
343 /// <summary>
344 /// Appends value count times to the end of this list.
345 /// </summary>
346 /// <param name="value">The value to add to the end of this list.</param>
347 /// <param name="count">The number of times to replicate the value.</param>
348 /// <remarks>
349 /// Length is incremented by count. If necessary, the capacity is increased.
350 /// </remarks>
351 /// <exception cref="ArgumentOutOfRangeException">Thrown if count is negative.</exception>
352 public void AddReplicate(in T value, int count)
353 {
354#if ENABLE_UNITY_COLLECTIONS_CHECKS
355 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
356#endif
357 CheckArgPositive(count);
358 m_ListData->AddReplicate(in value, CollectionHelper.AssumePositive(count));
359 }
360
361 /// <summary>
362 /// Shifts elements toward the end of this list, increasing its length.
363 /// </summary>
364 /// <remarks>
365 /// Right-shifts elements in the list so as to create 'free' slots at the beginning or in the middle.
366 ///
367 /// The length is increased by `end - begin`. If necessary, the capacity will be increased accordingly.
368 ///
369 /// If `end` equals `begin`, the method does nothing.
370 ///
371 /// The element at index `begin` will be copied to index `end`, the element at index `begin + 1` will be copied to `end + 1`, and so forth.
372 ///
373 /// The indexes `begin` up to `end` are not cleared: they will contain whatever values they held prior.
374 /// </remarks>
375 /// <param name="begin">The index of the first element that will be shifted up.</param>
376 /// <param name="end">The index where the first shifted element will end up.</param>
377 /// <exception cref="ArgumentException">Thrown if `end < begin`.</exception>
378 /// <exception cref="ArgumentOutOfRangeException">Thrown if `begin` or `end` are out of bounds.</exception>
379 public void InsertRangeWithBeginEnd(int begin, int end)
380 {
381#if ENABLE_UNITY_COLLECTIONS_CHECKS
382 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
383#endif
384 m_ListData->InsertRangeWithBeginEnd(begin, end);
385 }
386
387 /// <summary>
388 /// Shifts elements toward the end of this list, increasing its length.
389 /// </summary>
390 /// <remarks>
391 /// Right-shifts elements in the list so as to create 'free' slots at the beginning or in the middle.
392 ///
393 /// The length is increased by `count`. If necessary, the capacity will be increased accordingly.
394 ///
395 /// If `count` equals `0`, the method does nothing.
396 ///
397 /// The element at index `index` will be copied to index `index + count`, the element at index `index + 1` will be copied to `index + count + 1`, and so forth.
398 ///
399 /// The indexes `index` up to `index + count` are not cleared: they will contain whatever values they held prior.
400 /// </remarks>
401 /// <param name="index">The index of the first element that will be shifted up.</param>
402 /// <param name="count">The number of elements to insert.</param>
403 /// <exception cref="ArgumentException">Thrown if `count` is negative.</exception>
404 /// <exception cref="ArgumentOutOfRangeException">Thrown if `index` is out of bounds.</exception>
405 public void InsertRange(int index, int count) => InsertRangeWithBeginEnd(index, index + count);
406
407 /// <summary>
408 /// Copies the last element of this list to the specified index. Decrements the length by 1.
409 /// </summary>
410 /// <remarks>Useful as a cheap way to remove an element from this list when you don't care about preserving order.</remarks>
411 /// <param name="index">The index to overwrite with the last element.</param>
412 /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds.</exception>
413 public void RemoveAtSwapBack(int index)
414 {
415#if ENABLE_UNITY_COLLECTIONS_CHECKS
416 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
417#endif
418 m_ListData->RemoveAtSwapBack(index);
419 }
420
421 /// <summary>
422 /// Copies the last *N* elements of this list to a range in this list. Decrements the length by *N*.
423 /// </summary>
424 /// <remarks>
425 /// Copies the last `count` elements to the indexes `index` up to `index + count`.
426 ///
427 /// Useful as a cheap way to remove elements from a list when you don't care about preserving order.
428 /// </remarks>
429 /// <param name="index">The index of the first element to overwrite.</param>
430 /// <param name="count">The number of elements to copy and remove.</param>
431 /// <exception cref="ArgumentOutOfRangeException">Thrown if `index` is out of bounds, `count` is negative,
432 /// or `index + count` exceeds the length.</exception>
433 public void RemoveRangeSwapBack(int index, int count)
434 {
435#if ENABLE_UNITY_COLLECTIONS_CHECKS
436 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
437#endif
438 m_ListData->RemoveRangeSwapBack(index, count);
439 }
440
441 /// <summary>
442 /// Removes the element at an index, shifting everything above it down by one. Decrements the length by 1.
443 /// </summary>
444 /// <param name="index">The index of the item to remove.</param>
445 /// <remarks>
446 /// If you don't care about preserving the order of the elements, <see cref="RemoveAtSwapBack(int)"/> is a more efficient way to remove elements.
447 /// </remarks>
448 /// <exception cref="ArgumentOutOfRangeException">Thrown if `index` is out of bounds.</exception>
449 public void RemoveAt(int index)
450 {
451#if ENABLE_UNITY_COLLECTIONS_CHECKS
452 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
453#endif
454 m_ListData->RemoveAt(index);
455 }
456
457 /// <summary>
458 /// Removes *N* elements in a range, shifting everything above the range down by *N*. Decrements the length by *N*.
459 /// </summary>
460 /// <param name="index">The index of the first element to remove.</param>
461 /// <param name="count">The number of elements to remove.</param>
462 /// <remarks>
463 /// If you don't care about preserving the order of the elements, `RemoveRangeSwapBackWithBeginEnd`
464 /// is a more efficient way to remove elements.
465 /// </remarks>
466 /// <exception cref="ArgumentOutOfRangeException">Thrown if `index` is out of bounds, `count` is negative,
467 /// or `index + count` exceeds the length.</exception>
468 public void RemoveRange(int index, int count)
469 {
470#if ENABLE_UNITY_COLLECTIONS_CHECKS
471 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
472#endif
473 m_ListData->RemoveRange(index, count);
474 }
475
476 /// <summary>
477 /// Whether this list is empty.
478 /// </summary>
479 /// <value>True if the list is empty or if the list has not been constructed.</value>
480 public readonly bool IsEmpty
481 {
482 [MethodImpl(MethodImplOptions.AggressiveInlining)]
483 get => m_ListData == null || m_ListData->Length == 0;
484 }
485
486 /// <summary>
487 /// Whether this list has been allocated (and not yet deallocated).
488 /// </summary>
489 /// <value>True if this list has been allocated (and not yet deallocated).</value>
490 public readonly bool IsCreated
491 {
492 [MethodImpl(MethodImplOptions.AggressiveInlining)]
493 get => m_ListData != null;
494 }
495
496 /// <summary>
497 /// Releases all resources (memory and safety handles).
498 /// </summary>
499 public void Dispose()
500 {
501#if ENABLE_UNITY_COLLECTIONS_CHECKS
502 if (!AtomicSafetyHandle.IsDefaultValue(m_Safety))
503 {
504 AtomicSafetyHandle.CheckExistsAndThrow(m_Safety);
505 }
506#endif
507 if (!IsCreated)
508 {
509 return;
510 }
511
512#if ENABLE_UNITY_COLLECTIONS_CHECKS
513 CollectionHelper.DisposeSafetyHandle(ref m_Safety);
514#endif
515 UnsafeList<T>.Destroy(m_ListData);
516 m_ListData = null;
517 }
518
519 /// <summary>
520 /// Releases all resources (memory and safety handles).
521 /// <typeparam name="U">The type of allocator.</typeparam>
522 /// <param name="allocator">The allocator that was used to allocate this list.</param>
523 /// </summary>
524 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(AllocatorManager.AllocatorHandle) })]
525 internal void Dispose<U>(ref U allocator) where U : unmanaged, AllocatorManager.IAllocator
526 {
527#if ENABLE_UNITY_COLLECTIONS_CHECKS
528 if (!AtomicSafetyHandle.IsDefaultValue(m_Safety))
529 {
530 AtomicSafetyHandle.CheckExistsAndThrow(m_Safety);
531 }
532#endif
533 if (!IsCreated)
534 {
535 return;
536 }
537
538 CheckHandleMatches(allocator.Handle);
539#if ENABLE_UNITY_COLLECTIONS_CHECKS
540 CollectionHelper.DisposeSafetyHandle(ref m_Safety);
541#endif
542 UnsafeList<T>.Destroy(m_ListData, ref allocator);
543 m_ListData = null;
544 }
545
546 /// <summary>
547 /// Creates and schedules a job that releases all resources (memory and safety handles) of this list.
548 /// </summary>
549 /// <param name="inputDeps">The dependency for the new job.</param>
550 /// <returns>The handle of the new job. The job depends upon `inputDeps` and releases all resources (memory and safety handles) of this list.</returns>
551 public JobHandle Dispose(JobHandle inputDeps)
552 {
553#if ENABLE_UNITY_COLLECTIONS_CHECKS
554 if (!AtomicSafetyHandle.IsDefaultValue(m_Safety))
555 {
556 AtomicSafetyHandle.CheckExistsAndThrow(m_Safety);
557 }
558#endif
559 if (!IsCreated)
560 {
561 return inputDeps;
562 }
563
564#if ENABLE_UNITY_COLLECTIONS_CHECKS
565 var jobHandle = new NativeListDisposeJob { Data = new NativeListDispose { m_ListData = (UntypedUnsafeList*)m_ListData, m_Safety = m_Safety } }.Schedule(inputDeps);
566 AtomicSafetyHandle.Release(m_Safety);
567#else
568 var jobHandle = new NativeListDisposeJob { Data = new NativeListDispose { m_ListData = (UntypedUnsafeList*)m_ListData } }.Schedule(inputDeps);
569#endif
570 m_ListData = null;
571
572 return jobHandle;
573 }
574
575 /// <summary>
576 /// Sets the length to 0.
577 /// </summary>
578 /// <remarks>Does not change the capacity.</remarks>
579 public void Clear()
580 {
581#if ENABLE_UNITY_COLLECTIONS_CHECKS
582 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
583#endif
584 m_ListData->Clear();
585 }
586
587 /// <summary>
588 /// **Obsolete.** Use <see cref="AsArray"/> method to do explicit cast instead.
589 /// </summary>
590 /// <remarks>
591 /// Returns a native array that aliases the content of a list.
592 /// </remarks>
593 /// <param name="nativeList">The list to alias.</param>
594 /// <returns>A native array that aliases the content of the list.</returns>
595 [Obsolete("Implicit cast from `NativeList<T>` to `NativeArray<T>` has been deprecated; Use '.AsArray()' method to do explicit cast instead.", false)]
596 public static implicit operator NativeArray<T>(NativeList<T> nativeList)
597 {
598 return nativeList.AsArray();
599 }
600
601 /// <summary>
602 /// Returns a native array that aliases the content of this list.
603 /// </summary>
604 /// <returns>A native array that aliases the content of this list.</returns>
605 public NativeArray<T> AsArray()
606 {
607#if ENABLE_UNITY_COLLECTIONS_CHECKS
608 AtomicSafetyHandle.CheckGetSecondaryDataPointerAndThrow(m_Safety);
609 var arraySafety = m_Safety;
610 AtomicSafetyHandle.UseSecondaryVersion(ref arraySafety);
611#endif
612 var array = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T>(m_ListData->Ptr, m_ListData->Length, Allocator.None);
613
614#if ENABLE_UNITY_COLLECTIONS_CHECKS
615 NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref array, arraySafety);
616#endif
617 return array;
618 }
619
620 /// <summary>
621 /// Returns an array that aliases this list. The length of the array is updated when the length of
622 /// this array is updated in a prior job.
623 /// </summary>
624 /// <remarks>
625 /// Useful when a job populates a list that is then used by another job.
626 ///
627 /// If you pass both jobs the same list, you have to complete the first job before you schedule the second:
628 /// otherwise, the second job doesn't see the first job's changes to the list's length.
629 ///
630 /// If instead you pass the second job a deferred array that aliases the list, the array's length is kept in sync with
631 /// the first job's changes to the list's length. Consequently, the first job doesn't have to
632 /// be completed before you can schedule the second: the second job simply has to depend upon the first.
633 /// </remarks>
634 /// <returns>An array that aliases this list and whose length can be specially modified across jobs.</returns>
635 /// <example>
636 /// The following example populates a list with integers in one job and passes that data to a second job as
637 /// a deferred array. If we tried to pass the list directly to the second job, that job would not see any
638 /// modifications made to the list by the first job. To avoid this, we instead pass the second job a deferred array that aliases the list.
639 /// <code>
640 /// using UnityEngine;
641 /// using Unity.Jobs;
642 /// using Unity.Collections;
643 ///
644 /// public class DeferredArraySum : MonoBehaviour
645 ///{
646 /// public struct Populate : IJob
647 /// {
648 /// public NativeList<int> list;
649 ///
650 /// public void Execute()
651 /// {
652 /// for (int i = list.Length; i < list.Capacity; i++)
653 /// {
654 /// list.Add(i);
655 /// }
656 /// }
657 /// }
658 ///
659 /// // Sums all numbers from deferred.
660 /// public struct Sum : IJob
661 /// {
662 /// [ReadOnly] public NativeArray<int> deferred;
663 /// public NativeArray<int> sum;
664 ///
665 /// public void Execute()
666 /// {
667 /// sum[0] = 0;
668 /// for (int i = 0; i < deferred.Length; i++)
669 /// {
670 /// sum[0] += deferred[i];
671 /// }
672 /// }
673 /// }
674 ///
675 /// void Start()
676 /// {
677 /// var list = new NativeList<int>(100, Allocator.TempJob);
678 /// var deferred = list.AsDeferredJobArray(),
679 /// var output = new NativeArray<int>(1, Allocator.TempJob);
680 ///
681 /// // The Populate job increases the list's length from 0 to 100.
682 /// var populate = new Populate { list = list }.Schedule();
683 ///
684 /// // At time of scheduling, the length of the deferred array given to Sum is 0.
685 /// // When Populate increases the list's length, the deferred array's length field in the
686 /// // Sum job is also modified, even though it has already been scheduled.
687 /// var sum = new Sum { deferred = deferred, sum = output }.Schedule(populate);
688 ///
689 /// sum.Complete();
690 ///
691 /// Debug.Log("Result: " + output[0]);
692 ///
693 /// list.Dispose();
694 /// output.Dispose();
695 /// }
696 /// }
697 /// </code>
698 /// </example>
699 public NativeArray<T> AsDeferredJobArray()
700 {
701#if ENABLE_UNITY_COLLECTIONS_CHECKS
702 AtomicSafetyHandle.CheckExistsAndThrow(m_Safety);
703#endif
704 byte* buffer = (byte*)m_ListData;
705 // We use the first bit of the pointer to infer that the array is in list mode
706 // Thus the job scheduling code will need to patch it.
707 buffer += 1;
708 var array = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray<T>(buffer, 0, Allocator.Invalid);
709
710#if ENABLE_UNITY_COLLECTIONS_CHECKS
711 NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref array, m_Safety);
712#endif
713
714 return array;
715 }
716
717 /// <summary>
718 /// Returns an array containing a copy of this list's content.
719 /// </summary>
720 /// <param name="allocator">The allocator to use.</param>
721 /// <returns>An array containing a copy of this list's content.</returns>
722 public NativeArray<T> ToArray(AllocatorManager.AllocatorHandle allocator)
723 {
724 NativeArray<T> result = CollectionHelper.CreateNativeArray<T>(Length, allocator, NativeArrayOptions.UninitializedMemory);
725#if ENABLE_UNITY_COLLECTIONS_CHECKS
726 AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
727 AtomicSafetyHandle.CheckWriteAndThrow(result.m_Safety);
728#endif
729 UnsafeUtility.MemCpy((byte*)result.m_Buffer, (byte*)m_ListData->Ptr, Length * UnsafeUtility.SizeOf<T>());
730 return result;
731 }
732
733 /// <summary>
734 /// Copies all elements of specified container to this container.
735 /// </summary>
736 /// <param name="other">An container to copy into this container.</param>
737 public void CopyFrom(in NativeArray<T> other)
738 {
739#if ENABLE_UNITY_COLLECTIONS_CHECKS
740 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
741 AtomicSafetyHandle.CheckReadAndThrow(other.m_Safety);
742#endif
743 m_ListData->CopyFrom(other);
744 }
745
746 /// <summary>
747 /// Copies all elements of specified container to this container.
748 /// </summary>
749 /// <param name="other">An container to copy into this container.</param>
750 public void CopyFrom(in UnsafeList<T> other)
751 {
752#if ENABLE_UNITY_COLLECTIONS_CHECKS
753 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
754#endif
755 m_ListData->CopyFrom(other);
756 }
757
758 /// <summary>
759 /// Copies all elements of specified container to this container.
760 /// </summary>
761 /// <param name="other">An container to copy into this container.</param>
762 public void CopyFrom(in NativeList<T> other)
763 {
764 CopyFrom(*other.m_ListData);
765 }
766
767 /// <summary>
768 /// Returns an enumerator over the elements of this list.
769 /// </summary>
770 /// <returns>An enumerator over the elements of this list.</returns>
771 public NativeArray<T>.Enumerator GetEnumerator()
772 {
773 var array = AsArray();
774 return new NativeArray<T>.Enumerator(ref array);
775 }
776
777 /// <summary>
778 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
779 /// </summary>
780 /// <returns>Throws NotImplementedException.</returns>
781 /// <exception cref="NotImplementedException">Method is not implemented.</exception>
782 IEnumerator IEnumerable.GetEnumerator()
783 {
784 throw new NotImplementedException();
785 }
786
787 /// <summary>
788 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
789 /// </summary>
790 /// <returns>Throws NotImplementedException.</returns>
791 /// <exception cref="NotImplementedException">Method is not implemented.</exception>
792 IEnumerator<T> IEnumerable<T>.GetEnumerator()
793 {
794 throw new NotImplementedException();
795 }
796
797 /// <summary>
798 /// Sets the length of this list, increasing the capacity if necessary.
799 /// </summary>
800 /// <param name="length">The new length of this list.</param>
801 /// <param name="options">Whether to clear any newly allocated bytes to all zeroes.</param>
802 public void Resize(int length, NativeArrayOptions options)
803 {
804#if ENABLE_UNITY_COLLECTIONS_CHECKS
805 AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_Safety);
806#endif
807 m_ListData->Resize(length, options);
808 }
809
810 /// <summary>
811 /// Sets the length of this list, increasing the capacity if necessary.
812 /// </summary>
813 /// <remarks>Does not clear newly allocated bytes.</remarks>
814 /// <param name="length">The new length of this list.</param>
815 public void ResizeUninitialized(int length)
816 {
817 Resize(length, NativeArrayOptions.UninitializedMemory);
818 }
819
820 /// <summary>
821 /// Sets the capacity.
822 /// </summary>
823 /// <param name="capacity">The new capacity.</param>
824 public void SetCapacity(int capacity)
825 {
826 m_ListData->SetCapacity(capacity);
827 }
828
829 /// <summary>
830 /// Sets the capacity to match the length.
831 /// </summary>
832 public void TrimExcess()
833 {
834 m_ListData->TrimExcess();
835 }
836
837 /// <summary>
838 /// Returns a read only of this list.
839 /// </summary>
840 /// <returns>A read only of this list.</returns>
841 public NativeArray<T>.ReadOnly AsReadOnly()
842 {
843#if ENABLE_UNITY_COLLECTIONS_CHECKS
844 return new NativeArray<T>.ReadOnly(m_ListData->Ptr, m_ListData->Length, ref m_Safety);
845#else
846 return new NativeArray<T>.ReadOnly(m_ListData->Ptr, m_ListData->Length);
847#endif
848 }
849
850 /// <summary>
851 /// Returns a parallel reader of this list.
852 /// </summary>
853 /// <returns>A parallel reader of this list.</returns>
854// [Obsolete("'AsParallelReader' has been deprecated; use 'AsReadOnly' instead. (UnityUpgradable) -> AsReadOnly")]
855 public NativeArray<T>.ReadOnly AsParallelReader()
856 {
857#if ENABLE_UNITY_COLLECTIONS_CHECKS
858 return new NativeArray<T>.ReadOnly(m_ListData->Ptr, m_ListData->Length, ref m_Safety);
859#else
860 return new NativeArray<T>.ReadOnly(m_ListData->Ptr, m_ListData->Length);
861#endif
862 }
863
864 /// <summary>
865 /// Returns a parallel writer of this list.
866 /// </summary>
867 /// <returns>A parallel writer of this list.</returns>
868 public ParallelWriter AsParallelWriter()
869 {
870#if ENABLE_UNITY_COLLECTIONS_CHECKS
871 return new ParallelWriter(m_ListData, ref m_Safety);
872#else
873 return new ParallelWriter(m_ListData);
874#endif
875 }
876
877 /// <summary>
878 /// A parallel writer for a NativeList.
879 /// </summary>
880 /// <remarks>
881 /// Use <see cref="AsParallelWriter"/> to create a parallel writer for a list.
882 /// </remarks>
883 [NativeContainer]
884 [NativeContainerIsAtomicWriteOnly]
885 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
886 public unsafe struct ParallelWriter
887 {
888 /// <summary>
889 /// The data of the list.
890 /// </summary>
891 public readonly void* Ptr
892 {
893 [MethodImpl(MethodImplOptions.AggressiveInlining)]
894 get => ListData->Ptr;
895 }
896
897 /// <summary>
898 /// The internal unsafe list.
899 /// </summary>
900 /// <value>The internal unsafe list.</value>
901 [NativeDisableUnsafePtrRestriction]
902 public UnsafeList<T>* ListData;
903
904#if ENABLE_UNITY_COLLECTIONS_CHECKS
905 internal AtomicSafetyHandle m_Safety;
906 internal static readonly SharedStatic<int> s_staticSafetyId = SharedStatic<int>.GetOrCreate<ParallelWriter>();
907
908 [GenerateTestsForBurstCompatibility(CompileTarget = GenerateTestsForBurstCompatibilityAttribute.BurstCompatibleCompileTarget.Editor)]
909 internal unsafe ParallelWriter(UnsafeList<T>* listData, ref AtomicSafetyHandle safety)
910 {
911 ListData = listData;
912 m_Safety = safety;
913 CollectionHelper.SetStaticSafetyId<ParallelWriter>(ref m_Safety, ref s_staticSafetyId.Data);
914 }
915#else
916 internal unsafe ParallelWriter(UnsafeList<T>* listData)
917 {
918 ListData = listData;
919 }
920#endif
921
922 /// <summary>
923 /// Appends an element to the end of this list.
924 /// </summary>
925 /// <param name="value">The value to add to the end of this list.</param>
926 /// <remarks>
927 /// Increments the length by 1 unless doing so would exceed the current capacity.
928 /// </remarks>
929 /// <exception cref="InvalidOperationException">Thrown if adding an element would exceed the capacity.</exception>
930 public void AddNoResize(T value)
931 {
932#if ENABLE_UNITY_COLLECTIONS_CHECKS
933 AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
934#endif
935 var idx = Interlocked.Increment(ref ListData->m_length) - 1;
936 CheckSufficientCapacity(ListData->Capacity, idx + 1);
937
938 UnsafeUtility.WriteArrayElement(ListData->Ptr, idx, value);
939 }
940
941 /// <summary>
942 /// Appends elements from a buffer to the end of this list.
943 /// </summary>
944 /// <param name="ptr">The buffer to copy from.</param>
945 /// <param name="count">The number of elements to copy from the buffer.</param>
946 /// <remarks>
947 /// Increments the length by `count` unless doing so would exceed the current capacity.
948 /// </remarks>
949 /// <exception cref="InvalidOperationException">Thrown if adding the elements would exceed the capacity.</exception>
950 public void AddRangeNoResize(void* ptr, int count)
951 {
952 CheckArgPositive(count);
953
954#if ENABLE_UNITY_COLLECTIONS_CHECKS
955 AtomicSafetyHandle.CheckWriteAndThrow(m_Safety);
956#endif
957 var idx = Interlocked.Add(ref ListData->m_length, count) - count;
958 CheckSufficientCapacity(ListData->Capacity, idx + count);
959
960 var sizeOf = sizeof(T);
961 void* dst = (byte*)ListData->Ptr + idx * sizeOf;
962 UnsafeUtility.MemCpy(dst, ptr, count * sizeOf);
963 }
964
965 /// <summary>
966 /// Appends the elements of another list to the end of this list.
967 /// </summary>
968 /// <param name="list">The other list to copy from.</param>
969 /// <remarks>
970 /// Increments the length of this list by the length of the other list unless doing so would exceed the current capacity.
971 /// </remarks>
972 /// <exception cref="InvalidOperationException">Thrown if adding the elements would exceed the capacity.</exception>
973 public void AddRangeNoResize(UnsafeList<T> list)
974 {
975 AddRangeNoResize(list.Ptr, list.Length);
976 }
977
978 /// <summary>
979 /// Appends the elements of another list to the end of this list.
980 /// </summary>
981 /// <param name="list">The other list to copy from.</param>
982 /// <remarks>
983 /// Increments the length of this list by the length of the other list unless doing so would exceed the current capacity.
984 /// </remarks>
985 /// <exception cref="InvalidOperationException">Thrown if adding the elements would exceed the capacity.</exception>
986 public void AddRangeNoResize(NativeList<T> list)
987 {
988 AddRangeNoResize(*list.m_ListData);
989 }
990 }
991
992 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
993 static void CheckInitialCapacity(int initialCapacity)
994 {
995 if (initialCapacity < 0)
996 throw new ArgumentOutOfRangeException(nameof(initialCapacity), "Capacity must be >= 0");
997 }
998
999 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
1000 static void CheckTotalSize(int initialCapacity, long totalSize)
1001 {
1002 // Make sure we cannot allocate more than int.MaxValue (2,147,483,647 bytes)
1003 // because the underlying UnsafeUtility.Malloc is expecting a int.
1004 // TODO: change UnsafeUtility.Malloc to accept a UIntPtr length instead to match C++ API
1005 if (totalSize > int.MaxValue)
1006 throw new ArgumentOutOfRangeException(nameof(initialCapacity), $"Capacity * sizeof(T) cannot exceed {int.MaxValue} bytes");
1007 }
1008
1009 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
1010 static void CheckSufficientCapacity(int capacity, int length)
1011 {
1012 if (capacity < length)
1013 throw new InvalidOperationException($"Length {length} exceeds Capacity {capacity}");
1014 }
1015
1016 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
1017 static void CheckIndexInRange(int value, int length)
1018 {
1019 if (value < 0)
1020 throw new IndexOutOfRangeException($"Value {value} must be positive.");
1021
1022 if ((uint)value >= (uint)length)
1023 throw new IndexOutOfRangeException(
1024 $"Value {value} is out of range in NativeList of '{length}' Length.");
1025 }
1026
1027 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
1028 static void CheckArgPositive(int value)
1029 {
1030 if (value < 0)
1031 throw new ArgumentOutOfRangeException($"Value {value} must be positive.");
1032 }
1033
1034 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
1035 void CheckHandleMatches(AllocatorManager.AllocatorHandle handle)
1036 {
1037 if(m_ListData == null)
1038 throw new ArgumentOutOfRangeException($"Allocator handle {handle} can't match because container is not initialized.");
1039 if(m_ListData->Allocator.Index != handle.Index)
1040 throw new ArgumentOutOfRangeException($"Allocator handle {handle} can't match because container handle index doesn't match.");
1041 if(m_ListData->Allocator.Version != handle.Version)
1042 throw new ArgumentOutOfRangeException($"Allocator handle {handle} matches container handle index, but has different version.");
1043 }
1044 }
1045
1046 [NativeContainer]
1047 [GenerateTestsForBurstCompatibility]
1048 internal unsafe struct NativeListDispose
1049 {
1050 [NativeDisableUnsafePtrRestriction]
1051 public UntypedUnsafeList* m_ListData;
1052
1053#if ENABLE_UNITY_COLLECTIONS_CHECKS
1054 internal AtomicSafetyHandle m_Safety;
1055#endif
1056
1057 public void Dispose()
1058 {
1059 var listData = (UnsafeList<int>*)m_ListData;
1060 UnsafeList<int>.Destroy(listData);
1061 }
1062 }
1063
1064 [BurstCompile]
1065 [GenerateTestsForBurstCompatibility]
1066 internal unsafe struct NativeListDisposeJob : IJob
1067 {
1068 internal NativeListDispose Data;
1069
1070 public void Execute()
1071 {
1072 Data.Dispose();
1073 }
1074 }
1075
1076 sealed unsafe class NativeListDebugView<T> where T : unmanaged
1077 {
1078 UnsafeList<T>* Data;
1079
1080 public NativeListDebugView(NativeList<T> array)
1081 {
1082 Data = array.m_ListData;
1083 }
1084
1085 public T[] Items
1086 {
1087 get
1088 {
1089 if (Data == null)
1090 {
1091 return default;
1092 }
1093
1094 // Trying to avoid safety checks, so that container can be read in debugger if it's safety handle
1095 // is in write-only mode.
1096 var length = Data->Length;
1097 var dst = new T[length];
1098
1099 fixed (T* pDst = &dst[0])
1100 {
1101 UnsafeUtility.MemCpy(pDst, Data->Ptr, length * UnsafeUtility.SizeOf<T>());
1102 }
1103
1104 return dst;
1105 }
1106 }
1107 }
1108
1109 /// <summary>
1110 /// Provides extension methods for UnsafeList.
1111 /// </summary>
1112 [GenerateTestsForBurstCompatibility]
1113 public unsafe static class NativeListExtensions
1114 {
1115 /// <summary>
1116 /// Returns true if a particular value is present in this list.
1117 /// </summary>
1118 /// <typeparam name="T">The type of elements in this list.</typeparam>
1119 /// <typeparam name="U">The value type.</typeparam>
1120 /// <param name="list">The list to search.</param>
1121 /// <param name="value">The value to locate.</param>
1122 /// <returns>True if the value is present in this list.</returns>
1123 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(int) })]
1124 public static bool Contains<T, U>(this NativeList<T> list, U value)
1125 where T : unmanaged, IEquatable<U>
1126 {
1127 return NativeArrayExtensions.IndexOf<T, U>(list.GetUnsafeReadOnlyPtr(), list.Length, value) != -1;
1128 }
1129
1130 /// <summary>
1131 /// Finds the index of the first occurrence of a particular value in this list.
1132 /// </summary>
1133 /// <typeparam name="T">The type of elements in the list.</typeparam>
1134 /// <typeparam name="U">The value type.</typeparam>
1135 /// <param name="list">The list to search.</param>
1136 /// <param name="value">The value to locate.</param>
1137 /// <returns>The index of the first occurrence of the value in this list. Returns -1 if no occurrence is found.</returns>
1138 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(int) })]
1139 public static int IndexOf<T, U>(this NativeList<T> list, U value)
1140 where T : unmanaged, IEquatable<U>
1141 {
1142 return NativeArrayExtensions.IndexOf<T, U>(list.GetUnsafeReadOnlyPtr(), list.Length, value);
1143 }
1144
1145 /// <summary>
1146 /// Returns true if this container and another have equal length and content.
1147 /// </summary>
1148 /// <typeparam name="T">The type of the source container's elements.</typeparam>
1149 /// <param name="container">The container to compare for equality.</param>
1150 /// <param name="other">The other container to compare for equality.</param>
1151 /// <returns>True if the containers have equal length and content.</returns>
1152 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
1153 public static bool ArraysEqual<T>(this NativeArray<T> container, in NativeList<T> other)
1154 where T : unmanaged, IEquatable<T>
1155 {
1156 return container.ArraysEqual(other.AsArray());
1157 }
1158
1159 /// <summary>
1160 /// Returns true if this container and another have equal length and content.
1161 /// </summary>
1162 /// <typeparam name="T">The type of the source container's elements.</typeparam>
1163 /// <param name="container">The container to compare for equality.</param>
1164 /// <param name="other">The other container to compare for equality.</param>
1165 /// <returns>True if the containers have equal length and content.</returns>
1166 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
1167 public static bool ArraysEqual<T>(this NativeList<T> container, in NativeArray<T> other)
1168 where T : unmanaged, IEquatable<T>
1169 {
1170 return other.ArraysEqual(container);
1171 }
1172
1173 /// <summary>
1174 /// Returns true if this container and another have equal length and content.
1175 /// </summary>
1176 /// <typeparam name="T">The type of the source container's elements.</typeparam>
1177 /// <param name="container">The container to compare for equality.</param>
1178 /// <param name="other">The other container to compare for equality.</param>
1179 /// <returns>True if the containers have equal length and content.</returns>
1180 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
1181 public static bool ArraysEqual<T>(this NativeList<T> container, in NativeList<T> other)
1182 where T : unmanaged, IEquatable<T>
1183 {
1184 return container.AsArray().ArraysEqual(other.AsArray());
1185 }
1186
1187 /// <summary>
1188 /// Returns true if this container and another have equal length and content.
1189 /// </summary>
1190 /// <typeparam name="T">The type of the source container's elements.</typeparam>
1191 /// <param name="container">The container to compare for equality.</param>
1192 /// <param name="other">The other container to compare for equality.</param>
1193 /// <returns>True if the containers have equal length and content.</returns>
1194 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
1195 public static bool ArraysEqual<T>(this NativeList<T> container, in UnsafeList<T> other)
1196 where T : unmanaged, IEquatable<T>
1197 {
1198#if ENABLE_UNITY_COLLECTIONS_CHECKS
1199 AtomicSafetyHandle.CheckReadAndThrow(container.m_Safety);
1200#endif
1201 return container.m_ListData->ArraysEqual(other);
1202 }
1203 }
1204}
1205
1206namespace Unity.Collections.LowLevel.Unsafe
1207{
1208 /// <summary>
1209 /// Provides unsafe utility methods for NativeList.
1210 /// </summary>
1211 [GenerateTestsForBurstCompatibility]
1212 public unsafe static class NativeListUnsafeUtility
1213 {
1214 /// <summary>
1215 /// Returns a pointer to this list's internal buffer.
1216 /// </summary>
1217 /// <remarks>Performs a job safety check for read-write access.</remarks>
1218 /// <param name="list">The list.</param>
1219 /// <typeparam name="T">The type of the elements.</typeparam>
1220 /// <returns>A pointer to this list's internal buffer.</returns>
1221 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
1222 public static T* GetUnsafePtr<T>(this NativeList<T> list) where T : unmanaged
1223 {
1224#if ENABLE_UNITY_COLLECTIONS_CHECKS
1225 AtomicSafetyHandle.CheckWriteAndThrow(list.m_Safety);
1226#endif
1227 return list.m_ListData->Ptr;
1228 }
1229
1230 /// <summary>
1231 /// Returns a pointer to this list's internal buffer.
1232 /// </summary>
1233 /// <remarks>Performs a job safety check for read-only access.</remarks>
1234 /// <param name="list">The list.</param>
1235 /// <typeparam name="T">The type of the elements.</typeparam>
1236 /// <returns>A pointer to this list's internal buffer.</returns>
1237 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
1238 public static unsafe T* GetUnsafeReadOnlyPtr<T>(this NativeList<T> list) where T : unmanaged
1239 {
1240#if ENABLE_UNITY_COLLECTIONS_CHECKS
1241 AtomicSafetyHandle.CheckReadAndThrow(list.m_Safety);
1242#endif
1243 return list.m_ListData->Ptr;
1244 }
1245
1246#if ENABLE_UNITY_COLLECTIONS_CHECKS
1247 /// <summary>
1248 /// Returns this list's <see cref="AtomicSafetyHandle"/>.
1249 /// </summary>
1250 /// <param name="list">The list.</param>
1251 /// <typeparam name="T">The type of the elements.</typeparam>
1252 /// <returns>The atomic safety handle for this list.</returns>
1253 /// <remarks>
1254 /// The job safety checks use a native collection's atomic safety handle to assert safety.
1255 ///
1256 /// This method is only available if the symbol `ENABLE_UNITY_COLLECTIONS_CHECKS` is defined.</remarks>
1257 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) }, RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS", CompileTarget = GenerateTestsForBurstCompatibilityAttribute.BurstCompatibleCompileTarget.Editor)]
1258 public static AtomicSafetyHandle GetAtomicSafetyHandle<T>(ref NativeList<T> list) where T : unmanaged
1259 {
1260 return list.m_Safety;
1261 }
1262
1263#endif
1264
1265 /// <summary>
1266 /// Returns a pointer to this list's internal unsafe list.
1267 /// </summary>
1268 /// <remarks>Performs no job safety checks.</remarks>
1269 /// <param name="list">The list.</param>
1270 /// <typeparam name="T">The type of the elements.</typeparam>
1271 /// <returns>A pointer to this list's internal unsafe list.</returns>
1272 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
1273 public static void* GetInternalListDataPtrUnchecked<T>(ref NativeList<T> list) where T : unmanaged
1274 {
1275 return list.m_ListData;
1276 }
1277 }
1278}