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.Jobs;
10using Unity.Jobs.LowLevel.Unsafe;
11using Unity.Mathematics;
12
13namespace Unity.Collections.LowLevel.Unsafe
14{
15
16 [BurstCompile]
17 internal unsafe struct UnsafeDisposeJob : IJob
18 {
19 [NativeDisableUnsafePtrRestriction]
20 public void* Ptr;
21 public AllocatorManager.AllocatorHandle Allocator;
22
23 public void Execute()
24 {
25 AllocatorManager.Free(Allocator, Ptr);
26 }
27 }
28
29 [StructLayout(LayoutKind.Sequential)]
30 internal unsafe struct UntypedUnsafeList
31 {
32#pragma warning disable 169
33 // <WARNING>
34 // 'Header' of this struct must binary match `UntypedUnsafeList`, `UnsafeList`, `UnsafePtrList`, and `NativeArray` struct.
35 [NativeDisableUnsafePtrRestriction]
36 internal readonly void* Ptr;
37 internal readonly int m_length;
38 internal readonly int m_capacity;
39 internal readonly AllocatorManager.AllocatorHandle Allocator;
40 internal readonly int padding;
41#pragma warning restore 169
42 }
43
44 /// <summary>
45 /// An unmanaged, resizable list.
46 /// </summary>
47 /// <typeparam name="T">The type of the elements.</typeparam>
48 [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}, IsEmpty = {IsEmpty}")]
49 [DebuggerTypeProxy(typeof(UnsafeListTDebugView<>))]
50 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
51 [StructLayout(LayoutKind.Sequential)]
52 public unsafe struct UnsafeList<T>
53 : INativeDisposable
54 , INativeList<T>
55 , IEnumerable<T> // Used by collection initializers.
56 where T : unmanaged
57 {
58 // <WARNING>
59 // 'Header' of this struct must binary match `UntypedUnsafeList`, `UnsafeList`, `UnsafePtrList`, and `NativeArray` struct.
60 // Fields must match UntypedUnsafeList structure, please don't reorder and don't insert anything in between first 4 fields
61
62 /// <summary>
63 /// The internal buffer of this list.
64 /// </summary>
65 [NativeDisableUnsafePtrRestriction]
66 public T* Ptr;
67
68 /// <summary>
69 /// The number of elements.
70 /// </summary>
71 public int m_length;
72
73 /// <summary>
74 /// The number of elements that can fit in the internal buffer.
75 /// </summary>
76 public int m_capacity;
77
78 /// <summary>
79 /// The allocator used to create the internal buffer.
80 /// </summary>
81 public AllocatorManager.AllocatorHandle Allocator;
82
83 readonly int padding;
84
85 /// <summary>
86 /// The number of elements.
87 /// </summary>
88 /// <value>The number of elements.</value>
89 public int Length
90 {
91 [MethodImpl(MethodImplOptions.AggressiveInlining)]
92 readonly get => CollectionHelper.AssumePositive(m_length);
93
94 set
95 {
96 if (value > Capacity)
97 {
98 Resize(value);
99 }
100 else
101 {
102 m_length = value;
103 }
104 }
105 }
106
107 /// <summary>
108 /// The number of elements that can fit in the internal buffer.
109 /// </summary>
110 /// <value>The number of elements that can fit in the internal buffer.</value>
111 public int Capacity
112 {
113 [MethodImpl(MethodImplOptions.AggressiveInlining)]
114 readonly get => CollectionHelper.AssumePositive(m_capacity);
115 set => SetCapacity(value);
116 }
117
118 /// <summary>
119 /// The element at an index.
120 /// </summary>
121 /// <param name="index">An index.</param>
122 /// <value>The element at the index.</value>
123 public T this[int index]
124 {
125 [MethodImpl(MethodImplOptions.AggressiveInlining)]
126 get
127 {
128 CollectionHelper.CheckIndexInRange(index, m_length);
129 return Ptr[CollectionHelper.AssumePositive(index)];
130 }
131
132 [MethodImpl(MethodImplOptions.AggressiveInlining)]
133 set
134 {
135 CollectionHelper.CheckIndexInRange(index, m_length);
136 Ptr[CollectionHelper.AssumePositive(index)] = value;
137 }
138 }
139
140 /// <summary>
141 /// Returns a reference to the element at a given index.
142 /// </summary>
143 /// <param name="index">The index to access. Must be in the range of [0..Length).</param>
144 /// <returns>A reference to the element at the index.</returns>
145 [MethodImpl(MethodImplOptions.AggressiveInlining)]
146 public ref T ElementAt(int index)
147 {
148 CollectionHelper.CheckIndexInRange(index, m_length);
149 return ref Ptr[CollectionHelper.AssumePositive(index)];
150 }
151
152 /// <summary>
153 /// Initializes and returns an instance of UnsafeList.
154 /// </summary>
155 /// <param name="ptr">An existing byte array to set as the internal buffer.</param>
156 /// <param name="length">The length.</param>
157 public UnsafeList(T* ptr, int length) : this()
158 {
159 Ptr = ptr;
160 m_length = length;
161 m_capacity = length;
162 Allocator = AllocatorManager.None;
163 }
164
165 /// <summary>
166 /// Initializes and returns an instance of UnsafeList.
167 /// </summary>
168 /// <param name="initialCapacity">The initial capacity of the list.</param>
169 /// <param name="allocator">The allocator to use.</param>
170 /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
171 public UnsafeList(int initialCapacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory)
172 {
173 Ptr = null;
174 m_length = 0;
175 m_capacity = 0;
176 Allocator = allocator;
177 padding = 0;
178
179 SetCapacity(math.max(initialCapacity, 1));
180
181 if (options == NativeArrayOptions.ClearMemory && Ptr != null)
182 {
183 var sizeOf = sizeof(T);
184 UnsafeUtility.MemClear(Ptr, Capacity * sizeOf);
185 }
186 }
187
188 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(AllocatorManager.AllocatorHandle) })]
189 internal static UnsafeList<T>* Create<U>(int initialCapacity, ref U allocator, NativeArrayOptions options) where U : unmanaged, AllocatorManager.IAllocator
190 {
191 UnsafeList<T>* listData = allocator.Allocate(default(UnsafeList<T>), 1);
192 *listData = new UnsafeList<T>(initialCapacity, allocator.Handle, options);
193 return listData;
194 }
195
196 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(AllocatorManager.AllocatorHandle) })]
197 internal static void Destroy<U>(UnsafeList<T>* listData, ref U allocator) where U : unmanaged, AllocatorManager.IAllocator
198 {
199 CheckNull(listData);
200 listData->Dispose(ref allocator);
201 allocator.Free(listData, sizeof(UnsafeList<T>), UnsafeUtility.AlignOf<UnsafeList<T>>(), 1);
202 }
203
204 /// <summary>
205 /// Returns a new list.
206 /// </summary>
207 /// <param name="initialCapacity">The initial capacity of the list.</param>
208 /// <param name="allocator">The allocator to use.</param>
209 /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
210 /// <returns>A pointer to the new list.</returns>
211 public static UnsafeList<T>* Create(int initialCapacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory)
212 {
213 UnsafeList<T>* listData = AllocatorManager.Allocate<UnsafeList<T>>(allocator);
214 *listData = new UnsafeList<T>(initialCapacity, allocator, options);
215
216 return listData;
217 }
218
219 /// <summary>
220 /// Destroys the list.
221 /// </summary>
222 /// <param name="listData">The list to destroy.</param>
223 public static void Destroy(UnsafeList<T>* listData)
224 {
225 CheckNull(listData);
226 var allocator = listData->Allocator;
227 listData->Dispose();
228 AllocatorManager.Free(allocator, listData);
229 }
230
231 /// <summary>
232 /// Whether the list is empty.
233 /// </summary>
234 /// <value>True if the list is empty or the list has not been constructed.</value>
235 public readonly bool IsEmpty
236 {
237 [MethodImpl(MethodImplOptions.AggressiveInlining)]
238 get => !IsCreated || m_length == 0;
239 }
240
241 /// <summary>
242 /// Whether this list has been allocated (and not yet deallocated).
243 /// </summary>
244 /// <value>True if this list has been allocated (and not yet deallocated).</value>
245 public readonly bool IsCreated
246 {
247 [MethodImpl(MethodImplOptions.AggressiveInlining)]
248 get => Ptr != null;
249 }
250
251 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(AllocatorManager.AllocatorHandle) })]
252 internal void Dispose<U>(ref U allocator) where U : unmanaged, AllocatorManager.IAllocator
253 {
254 allocator.Free(Ptr, m_capacity);
255 Ptr = null;
256 m_length = 0;
257 m_capacity = 0;
258 }
259
260 /// <summary>
261 /// Releases all resources (memory).
262 /// </summary>
263 public void Dispose()
264 {
265 if (!IsCreated)
266 {
267 return;
268 }
269
270 if (CollectionHelper.ShouldDeallocate(Allocator))
271 {
272 AllocatorManager.Free(Allocator, Ptr, m_capacity);
273 Allocator = AllocatorManager.Invalid;
274 }
275
276 Ptr = null;
277 m_length = 0;
278 m_capacity = 0;
279 }
280
281 /// <summary>
282 /// Creates and schedules a job that frees the memory of this list.
283 /// </summary>
284 /// <param name="inputDeps">The dependency for the new job.</param>
285 /// <returns>The handle of the new job. The job depends upon `inputDeps` and frees the memory of this list.</returns>
286 public JobHandle Dispose(JobHandle inputDeps)
287 {
288 if (!IsCreated)
289 {
290 return inputDeps;
291 }
292
293 if (CollectionHelper.ShouldDeallocate(Allocator))
294 {
295 var jobHandle = new UnsafeDisposeJob { Ptr = Ptr, Allocator = Allocator }.Schedule(inputDeps);
296
297 Ptr = null;
298 Allocator = AllocatorManager.Invalid;
299
300 return jobHandle;
301 }
302
303 Ptr = null;
304
305 return inputDeps;
306 }
307
308 /// <summary>
309 /// Sets the length to 0.
310 /// </summary>
311 /// <remarks>Does not change the capacity.</remarks>
312 public void Clear()
313 {
314 m_length = 0;
315 }
316
317 /// <summary>
318 /// Sets the length, expanding the capacity if necessary.
319 /// </summary>
320 /// <param name="length">The new length.</param>
321 /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
322 public void Resize(int length, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory)
323 {
324 var oldLength = m_length;
325
326 if (length > Capacity)
327 {
328 SetCapacity(length);
329 }
330
331 m_length = length;
332
333 if (options == NativeArrayOptions.ClearMemory && oldLength < length)
334 {
335 var num = length - oldLength;
336 byte* ptr = (byte*)Ptr;
337 var sizeOf = sizeof(T);
338 UnsafeUtility.MemClear(ptr + oldLength * sizeOf, num * sizeOf);
339 }
340 }
341
342 void ResizeExact<U>(ref U allocator, int newCapacity) where U : unmanaged, AllocatorManager.IAllocator
343 {
344 newCapacity = math.max(0, newCapacity);
345
346 CollectionHelper.CheckAllocator(Allocator);
347 T* newPointer = null;
348
349 var alignOf = UnsafeUtility.AlignOf<T>();
350 var sizeOf = sizeof(T);
351
352 if (newCapacity > 0)
353 {
354 newPointer = (T*)allocator.Allocate(sizeOf, alignOf, newCapacity);
355
356 if (Ptr != null && m_capacity > 0)
357 {
358 var itemsToCopy = math.min(newCapacity, Capacity);
359 var bytesToCopy = itemsToCopy * sizeOf;
360 UnsafeUtility.MemCpy(newPointer, Ptr, bytesToCopy);
361 }
362 }
363
364 allocator.Free(Ptr, Capacity);
365
366 Ptr = newPointer;
367 m_capacity = newCapacity;
368 m_length = math.min(m_length, newCapacity);
369 }
370
371 void ResizeExact(int capacity)
372 {
373 ResizeExact(ref Allocator, capacity);
374 }
375
376 void SetCapacity<U>(ref U allocator, int capacity) where U : unmanaged, AllocatorManager.IAllocator
377 {
378 CollectionHelper.CheckCapacityInRange(capacity, Length);
379
380 var sizeOf = sizeof(T);
381 var newCapacity = math.max(capacity, CollectionHelper.CacheLineSize / sizeOf);
382 newCapacity = math.ceilpow2(newCapacity);
383
384 if (newCapacity == Capacity)
385 {
386 return;
387 }
388
389 ResizeExact(ref allocator, newCapacity);
390 }
391
392 /// <summary>
393 /// Sets the capacity.
394 /// </summary>
395 /// <param name="capacity">The new capacity.</param>
396 public void SetCapacity(int capacity)
397 {
398 SetCapacity(ref Allocator, capacity);
399 }
400
401 /// <summary>
402 /// Sets the capacity to match the length.
403 /// </summary>
404 public void TrimExcess()
405 {
406 if (Capacity != m_length)
407 {
408 ResizeExact(m_length);
409 }
410 }
411
412 /// <summary>
413 /// Adds an element to the end of this list.
414 /// </summary>
415 /// <remarks>
416 /// Increments the length by 1. Never increases the capacity.
417 /// </remarks>
418 /// <param name="value">The value to add to the end of the list.</param>
419 /// <exception cref="InvalidOperationException">Thrown if incrementing the length would exceed the capacity.</exception>
420 [MethodImpl(MethodImplOptions.AggressiveInlining)]
421 public void AddNoResize(T value)
422 {
423 CheckNoResizeHasEnoughCapacity(1);
424 UnsafeUtility.WriteArrayElement(Ptr, m_length, value);
425 m_length += 1;
426 }
427
428 /// <summary>
429 /// Copies elements from a buffer to the end of this list.
430 /// </summary>
431 /// <remarks>
432 /// Increments the length by `count`. Never increases the capacity.
433 /// </remarks>
434 /// <param name="ptr">The buffer to copy from.</param>
435 /// <param name="count">The number of elements to copy from the buffer.</param>
436 /// <exception cref="InvalidOperationExceptionv">Thrown if the increased length would exceed the capacity.</exception>
437 public void AddRangeNoResize(void* ptr, int count)
438 {
439 CheckNoResizeHasEnoughCapacity(count);
440 var sizeOf = sizeof(T);
441 void* dst = (byte*)Ptr + m_length * sizeOf;
442 UnsafeUtility.MemCpy(dst, ptr, count * sizeOf);
443 m_length += count;
444 }
445
446 /// <summary>
447 /// Copies the elements of another list to the end of this list.
448 /// </summary>
449 /// <param name="list">The other list to copy from.</param>
450 /// <remarks>
451 /// Increments the length by the length of the other list. Never increases the capacity.
452 /// </remarks>
453 /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
454 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
455 public void AddRangeNoResize(UnsafeList<T> list)
456 {
457 AddRangeNoResize(list.Ptr, CollectionHelper.AssumePositive(list.Length));
458 }
459
460 /// <summary>
461 /// Adds an element to the end of the list.
462 /// </summary>
463 /// <param name="value">The value to add to the end of this list.</param>
464 /// <remarks>
465 /// Increments the length by 1. Increases the capacity if necessary.
466 /// </remarks>
467 [MethodImpl(MethodImplOptions.AggressiveInlining)]
468 public void Add(in T value)
469 {
470 var idx = m_length;
471 if (m_length < m_capacity)
472 {
473 Ptr[idx] = value;
474 m_length++;
475 return;
476 }
477
478 Resize(idx + 1);
479 Ptr[idx] = value;
480 }
481
482 /// <summary>
483 /// Copies the elements of a buffer to the end of this list.
484 /// </summary>
485 /// <param name="ptr">The buffer to copy from.</param>
486 /// <param name="count">The number of elements to copy from the buffer.</param>
487 /// <remarks>
488 /// Increments the length by `count`. Increases the capacity if necessary.
489 /// </remarks>
490 public void AddRange(void* ptr, int count)
491 {
492 var idx = m_length;
493
494 if (m_length + count > Capacity)
495 {
496 Resize(m_length + count);
497 }
498 else
499 {
500 m_length += count;
501 }
502
503 var sizeOf = sizeof(T);
504 void* dst = (byte*)Ptr + idx * sizeOf;
505 UnsafeUtility.MemCpy(dst, ptr, count * sizeOf);
506 }
507
508 /// <summary>
509 /// Copies the elements of another list to the end of the list.
510 /// </summary>
511 /// <param name="list">The list to copy from.</param>
512 /// <remarks>
513 /// The length is increased by the length of the other list. Increases the capacity if necessary.
514 /// </remarks>
515 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
516 public void AddRange(UnsafeList<T> list)
517 {
518 AddRange(list.Ptr, list.Length);
519 }
520
521 /// <summary>
522 /// Appends value count times to the end of this list.
523 /// </summary>
524 /// <param name="value">The value to add to the end of this list.</param>
525 /// <param name="count">The number of times to replicate the value.</param>
526 /// <remarks>
527 /// Length is incremented by count. If necessary, the capacity is increased.
528 /// </remarks>
529 public void AddReplicate(in T value, int count)
530 {
531 var idx = m_length;
532 if (m_length + count > Capacity)
533 {
534 Resize(m_length + count);
535 }
536 else
537 {
538 m_length += count;
539 }
540
541 fixed (void* ptr = &value)
542 {
543 UnsafeUtility.MemCpyReplicate(Ptr + idx, ptr, UnsafeUtility.SizeOf<T>(), count);
544 }
545 }
546
547 /// <summary>
548 /// Shifts elements toward the end of this list, increasing its length.
549 /// </summary>
550 /// <remarks>
551 /// Right-shifts elements in the list so as to create 'free' slots at the beginning or in the middle.
552 ///
553 /// The length is increased by `end - begin`. If necessary, the capacity will be increased accordingly.
554 ///
555 /// If `end` equals `begin`, the method does nothing.
556 ///
557 /// 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.
558 ///
559 /// The indexes `begin` up to `end` are not cleared: they will contain whatever values they held prior.
560 /// </remarks>
561 /// <param name="begin">The index of the first element that will be shifted up.</param>
562 /// <param name="end">The index where the first shifted element will end up.</param>
563 /// <exception cref="ArgumentException">Thrown if `end < begin`.</exception>
564 /// <exception cref="ArgumentOutOfRangeException">Thrown if `begin` or `end` are out of bounds.</exception>
565 public void InsertRangeWithBeginEnd(int begin, int end)
566 {
567 CheckBeginEndNoLength(begin, end);
568
569 // Because we've checked begin and end in `CheckBeginEnd` above, we can now
570 // assume they are positive.
571 begin = CollectionHelper.AssumePositive(begin);
572 end = CollectionHelper.AssumePositive(end);
573
574 int items = end - begin;
575 if (items < 1)
576 {
577 return;
578 }
579
580 var oldLength = m_length;
581
582 if (m_length + items > Capacity)
583 {
584 Resize(m_length + items);
585 }
586 else
587 {
588 m_length += items;
589 }
590
591 var itemsToCopy = oldLength - begin;
592
593 if (itemsToCopy < 1)
594 {
595 return;
596 }
597
598 var sizeOf = sizeof(T);
599 var bytesToCopy = itemsToCopy * sizeOf;
600 unsafe
601 {
602 byte* ptr = (byte*)Ptr;
603 byte* dest = ptr + end * sizeOf;
604 byte* src = ptr + begin * sizeOf;
605 UnsafeUtility.MemMove(dest, src, bytesToCopy);
606 }
607 }
608
609 /// <summary>
610 /// Shifts elements toward the end of this list, increasing its length.
611 /// </summary>
612 /// <remarks>
613 /// Right-shifts elements in the list so as to create 'free' slots at the beginning or in the middle.
614 ///
615 /// The length is increased by `count`. If necessary, the capacity will be increased accordingly.
616 ///
617 /// If `count` equals `0`, the method does nothing.
618 ///
619 /// 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.
620 ///
621 /// The indexes `index` up to `index + count` are not cleared: they will contain whatever values they held prior.
622 /// </remarks>
623 /// <param name="index">The index of the first element that will be shifted up.</param>
624 /// <param name="count">The number of elements to insert.</param>
625 /// <exception cref="ArgumentException">Thrown if `count` is negative.</exception>
626 /// <exception cref="ArgumentOutOfRangeException">Thrown if `index` is out of bounds.</exception>
627 public void InsertRange(int index, int count) => InsertRangeWithBeginEnd(index, index + count);
628
629 /// <summary>
630 /// Copies the last element of this list to the specified index. Decrements the length by 1.
631 /// </summary>
632 /// <remarks>Useful as a cheap way to remove an element from this list when you don't care about preserving order.</remarks>
633 /// <param name="index">The index to overwrite with the last element.</param>
634 /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds.</exception>
635 public void RemoveAtSwapBack(int index)
636 {
637 CollectionHelper.CheckIndexInRange(index, m_length);
638
639 index = CollectionHelper.AssumePositive(index);
640 int copyFrom = m_length - 1;
641 T* dst = (T*)Ptr + index;
642 T* src = (T*)Ptr + copyFrom;
643 (*dst) = (*src);
644 m_length -= 1;
645 }
646
647 /// <summary>
648 /// Copies the last *N* elements of this list to a range in this list. Decrements the length by *N*.
649 /// </summary>
650 /// <remarks>
651 /// Copies the last `count` elements to the indexes `index` up to `index + count`.
652 ///
653 /// Useful as a cheap way to remove elements from a list when you don't care about preserving order.
654 /// </remarks>
655 /// <param name="index">The index of the first element to overwrite.</param>
656 /// <param name="count">The number of elements to copy and remove.</param>
657 /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds</exception>
658 /// <exception cref="ArgumentOutOfRangeException">Thrown if `count` is negative,
659 /// or `index + count` exceeds the length.</exception>
660 public void RemoveRangeSwapBack(int index, int count)
661 {
662 CheckIndexCount(index, count);
663
664 index = CollectionHelper.AssumePositive(index);
665 count = CollectionHelper.AssumePositive(count);
666
667 if (count > 0)
668 {
669 int copyFrom = math.max(m_length - count, index + count);
670 var sizeOf = sizeof(T);
671 void* dst = (byte*)Ptr + index * sizeOf;
672 void* src = (byte*)Ptr + copyFrom * sizeOf;
673 UnsafeUtility.MemCpy(dst, src, (m_length - copyFrom) * sizeOf);
674 m_length -= count;
675 }
676 }
677
678 /// <summary>
679 /// Removes the element at an index, shifting everything above it down by one. Decrements the length by 1.
680 /// </summary>
681 /// <param name="index">The index of the element to remove.</param>
682 /// <remarks>
683 /// If you don't care about preserving the order of the elements, <see cref="RemoveAtSwapBack(int)"/> is a more efficient way to remove elements.
684 /// </remarks>
685 /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds.</exception>
686 public void RemoveAt(int index)
687 {
688 CollectionHelper.CheckIndexInRange(index, m_length);
689
690 index = CollectionHelper.AssumePositive(index);
691
692 T* dst = Ptr + index;
693 T* src = dst + 1;
694 m_length--;
695
696 // Because these tend to be smaller (< 1MB), and the cost of jumping context to native and back is
697 // so high, this consistently optimizes to better code than UnsafeUtility.MemCpy
698 for (int i = index; i < m_length; i++)
699 {
700 *dst++ = *src++;
701 }
702 }
703
704 /// <summary>
705 /// Removes *N* elements in a range, shifting everything above the range down by *N*. Decrements the length by *N*.
706 /// </summary>
707 /// <param name="index">The index of the first element to remove.</param>
708 /// <param name="count">The number of elements to remove.</param>
709 /// <remarks>
710 /// If you don't care about preserving the order of the elements, `RemoveRangeSwapBackWithBeginEnd`
711 /// is a more efficient way to remove elements.
712 /// </remarks>
713 /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds</exception>
714 /// <exception cref="ArgumentOutOfRangeException">Thrown if `count` is negative,
715 /// or `index + count` exceeds the length.</exception>
716 public void RemoveRange(int index, int count)
717 {
718 CheckIndexCount(index, count);
719
720 index = CollectionHelper.AssumePositive(index);
721 count = CollectionHelper.AssumePositive(count);
722
723 if (count > 0)
724 {
725 int copyFrom = math.min(index + count, m_length);
726 var sizeOf = sizeof(T);
727 void* dst = (byte*)Ptr + index * sizeOf;
728 void* src = (byte*)Ptr + copyFrom * sizeOf;
729 UnsafeUtility.MemCpy(dst, src, (m_length - copyFrom) * sizeOf);
730 m_length -= count;
731 }
732 }
733
734 /// <summary>
735 /// Returns a read only of this list.
736 /// </summary>
737 /// <returns>A read only of this list.</returns>
738 public ReadOnly AsReadOnly()
739 {
740 return new ReadOnly(Ptr, Length);
741 }
742
743 /// <summary>
744 /// A read only for an UnsafeList<T>.
745 /// </summary>
746 /// <remarks>
747 /// Use <see cref="AsReadOnly"/> to create a read only for a list.
748 /// </remarks>
749 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
750 public unsafe struct ReadOnly
751 : IEnumerable<T>
752 {
753 /// <summary>
754 /// The internal buffer of the list.
755 /// </summary>
756 [NativeDisableUnsafePtrRestriction]
757 public readonly T* Ptr;
758
759 /// <summary>
760 /// The number of elements.
761 /// </summary>
762 public readonly int Length;
763
764 /// <summary>
765 /// Whether this list has been allocated (and not yet deallocated).
766 /// </summary>
767 /// <value>True if this list has been allocated (and not yet deallocated).</value>
768 public readonly bool IsCreated
769 {
770 [MethodImpl(MethodImplOptions.AggressiveInlining)]
771 get => Ptr != null;
772 }
773
774 /// <summary>
775 /// Whether the list is empty.
776 /// </summary>
777 /// <value>True if the list is empty or the list has not been constructed.</value>
778 public readonly bool IsEmpty
779 {
780 [MethodImpl(MethodImplOptions.AggressiveInlining)]
781 get => !IsCreated || Length == 0;
782 }
783
784 internal ReadOnly(T* ptr, int length)
785 {
786 Ptr = ptr;
787 Length = length;
788 }
789
790 /// <summary>
791 /// Returns an enumerator over the elements of the list.
792 /// </summary>
793 /// <returns>An enumerator over the elements of the list.</returns>
794 public Enumerator GetEnumerator()
795 {
796 return new Enumerator { m_Ptr = Ptr, m_Length = Length, m_Index = -1 };
797 }
798
799 /// <summary>
800 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
801 /// </summary>
802 /// <returns>Throws NotImplementedException.</returns>
803 /// <exception cref="NotImplementedException">Method is not implemented.</exception>
804 IEnumerator IEnumerable.GetEnumerator()
805 {
806 throw new NotImplementedException();
807 }
808
809 /// <summary>
810 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
811 /// </summary>
812 /// <returns>Throws NotImplementedException.</returns>
813 /// <exception cref="NotImplementedException">Method is not implemented.</exception>
814 IEnumerator<T> IEnumerable<T>.GetEnumerator()
815 {
816 throw new NotImplementedException();
817 }
818 }
819
820 /// <summary>
821 /// **Obsolete.** Use <see cref="AsReadOnly"/> instead.
822 /// </summary>
823 /// <returns>A parallel reader of this list.</returns>
824// [Obsolete("'AsParallelReader' has been deprecated; use 'AsReadOnly' instead. (UnityUpgradable) -> AsReadOnly")]
825 public ParallelReader AsParallelReader()
826 {
827 return new ParallelReader(Ptr, Length);
828 }
829
830 /// <summary>
831 /// **Obsolete.** Use <see cref="ReadOnly"/> instead.
832 /// </summary>
833 /// <remarks>
834 /// Use <see cref="AsParallelReader"/> to create a parallel reader for a list.
835 /// </remarks>
836 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
837// [Obsolete("'ParallelReader' has been deprecated; use 'ReadOnly' instead. (UnityUpgradable) -> ReadOnly")]
838 public unsafe struct ParallelReader
839 {
840 /// <summary>
841 /// The internal buffer of the list.
842 /// </summary>
843 [NativeDisableUnsafePtrRestriction]
844 public readonly T* Ptr;
845
846 /// <summary>
847 /// The number of elements.
848 /// </summary>
849 public readonly int Length;
850
851 internal ParallelReader(T* ptr, int length)
852 {
853 Ptr = ptr;
854 Length = length;
855 }
856 }
857
858 /// <summary>
859 /// Returns a parallel writer of this list.
860 /// </summary>
861 /// <returns>A parallel writer of this list.</returns>
862 public ParallelWriter AsParallelWriter()
863 {
864 return new ParallelWriter((UnsafeList<T>*)UnsafeUtility.AddressOf(ref this));
865 }
866
867 /// <summary>
868 /// A parallel writer for an UnsafeList<T>.
869 /// </summary>
870 /// <remarks>
871 /// Use <see cref="AsParallelWriter"/> to create a parallel writer for a list.
872 /// </remarks>
873 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
874 public unsafe struct ParallelWriter
875 {
876 /// <summary>
877 /// The data of the list.
878 /// </summary>
879 public readonly void* Ptr
880 {
881 [MethodImpl(MethodImplOptions.AggressiveInlining)]
882 get
883 {
884 return ListData->Ptr;
885 }
886 }
887
888 /// <summary>
889 /// The UnsafeList to write to.
890 /// </summary>
891 [NativeDisableUnsafePtrRestriction]
892 public UnsafeList<T>* ListData;
893
894 internal unsafe ParallelWriter(UnsafeList<T>* listData)
895 {
896 ListData = listData;
897 }
898
899 /// <summary>
900 /// Adds an element to the end of the list.
901 /// </summary>
902 /// <param name="value">The value to add to the end of the list.</param>
903 /// <remarks>
904 /// Increments the length by 1. Never increases the capacity.
905 /// </remarks>
906 /// <exception cref="InvalidOperationException">Thrown if incrementing the length would exceed the capacity.</exception>
907 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
908 public void AddNoResize(T value)
909 {
910 var idx = Interlocked.Increment(ref ListData->m_length) - 1;
911 ListData->CheckNoResizeHasEnoughCapacity(idx, 1);
912 UnsafeUtility.WriteArrayElement(ListData->Ptr, idx, value);
913 }
914
915 /// <summary>
916 /// Copies elements from a buffer to the end of the list.
917 /// </summary>
918 /// <param name="ptr">The buffer to copy from.</param>
919 /// <param name="count">The number of elements to copy from the buffer.</param>
920 /// <remarks>
921 /// Increments the length by `count`. Never increases the capacity.
922 /// </remarks>
923 /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
924 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
925 public void AddRangeNoResize(void* ptr, int count)
926 {
927 var idx = Interlocked.Add(ref ListData->m_length, count) - count;
928 ListData->CheckNoResizeHasEnoughCapacity(idx, count);
929 void* dst = (byte*)ListData->Ptr + idx * sizeof(T);
930 UnsafeUtility.MemCpy(dst, ptr, count * sizeof(T));
931 }
932
933 /// <summary>
934 /// Copies the elements of another list to the end of this list.
935 /// </summary>
936 /// <param name="list">The other list to copy from.</param>
937 /// <remarks>
938 /// Increments the length by the length of the other list. Never increases the capacity.
939 /// </remarks>
940 /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
941 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
942 public void AddRangeNoResize(UnsafeList<T> list)
943 {
944 AddRangeNoResize(list.Ptr, list.Length);
945 }
946 }
947
948 /// <summary>
949 /// Copies all elements of specified container to this container.
950 /// </summary>
951 /// <param name="other">An container to copy into this container.</param>
952 public void CopyFrom(in NativeArray<T> other)
953 {
954 Resize(other.Length);
955 UnsafeUtility.MemCpy(Ptr, other.GetUnsafeReadOnlyPtr<T>(), UnsafeUtility.SizeOf<T>() * other.Length);
956 }
957
958 /// <summary>
959 /// Copies all elements of specified container to this container.
960 /// </summary>
961 /// <param name="other">An container to copy into this container.</param>
962 public void CopyFrom(in UnsafeList<T> other)
963 {
964 Resize(other.Length);
965 UnsafeUtility.MemCpy(Ptr, other.Ptr, UnsafeUtility.SizeOf<T>() * other.Length);
966 }
967
968 /// <summary>
969 /// Returns an enumerator over the elements of the list.
970 /// </summary>
971 /// <returns>An enumerator over the elements of the list.</returns>
972 public Enumerator GetEnumerator()
973 {
974 return new Enumerator { m_Ptr = Ptr, m_Length = Length, m_Index = -1 };
975 }
976
977 /// <summary>
978 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
979 /// </summary>
980 /// <returns>Throws NotImplementedException.</returns>
981 /// <exception cref="NotImplementedException">Method is not implemented.</exception>
982 IEnumerator IEnumerable.GetEnumerator()
983 {
984 throw new NotImplementedException();
985 }
986
987 /// <summary>
988 /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
989 /// </summary>
990 /// <returns>Throws NotImplementedException.</returns>
991 /// <exception cref="NotImplementedException">Method is not implemented.</exception>
992 IEnumerator<T> IEnumerable<T>.GetEnumerator()
993 {
994 throw new NotImplementedException();
995 }
996
997 /// <summary>
998 /// An enumerator over the elements of a list.
999 /// </summary>
1000 /// <remarks>
1001 /// In an enumerator's initial state, <see cref="Current"/> is invalid.
1002 /// The first <see cref="MoveNext"/> call advances the enumerator to the first element of the list.
1003 /// </remarks>
1004 public struct Enumerator : IEnumerator<T>
1005 {
1006 internal T* m_Ptr;
1007 internal int m_Length;
1008 internal int m_Index;
1009
1010 /// <summary>
1011 /// Does nothing.
1012 /// </summary>
1013 public void Dispose() { }
1014
1015 /// <summary>
1016 /// Advances the enumerator to the next element of the list.
1017 /// </summary>
1018 /// <remarks>
1019 /// The first `MoveNext` call advances the enumerator to the first element of the list. Before this call, `Current` is not valid to read.
1020 /// </remarks>
1021 /// <returns>True if `Current` is valid to read after the call.</returns>
1022 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1023 public bool MoveNext() => ++m_Index < m_Length;
1024
1025 /// <summary>
1026 /// Resets the enumerator to its initial state.
1027 /// </summary>
1028 public void Reset() => m_Index = -1;
1029
1030 /// <summary>
1031 /// The current element.
1032 /// </summary>
1033 /// <value>The current element.</value>
1034 public T Current
1035 {
1036 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1037 get => m_Ptr[m_Index];
1038 }
1039
1040 object IEnumerator.Current => Current;
1041 }
1042
1043 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
1044 internal static void CheckNull(void* listData)
1045 {
1046 if (listData == null)
1047 {
1048 throw new InvalidOperationException("UnsafeList has yet to be created or has been destroyed!");
1049 }
1050 }
1051
1052 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
1053 void CheckIndexCount(int index, int count)
1054 {
1055 if (count < 0)
1056 {
1057 throw new ArgumentOutOfRangeException($"Value for count {count} must be positive.");
1058 }
1059
1060 if (index < 0)
1061 {
1062 throw new IndexOutOfRangeException($"Value for index {index} must be positive.");
1063 }
1064
1065 if (index > Length)
1066 {
1067 throw new IndexOutOfRangeException($"Value for index {index} is out of bounds.");
1068 }
1069
1070 if (index + count > Length)
1071 {
1072 throw new ArgumentOutOfRangeException($"Value for count {count} is out of bounds.");
1073 }
1074 }
1075
1076 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
1077 void CheckBeginEndNoLength(int begin, int end)
1078 {
1079 if (begin > end)
1080 {
1081 throw new ArgumentException($"Value for begin {begin} index must less or equal to end {end}.");
1082 }
1083
1084 if (begin < 0)
1085 {
1086 throw new ArgumentOutOfRangeException($"Value for begin {begin} must be positive.");
1087 }
1088 }
1089
1090 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
1091 void CheckBeginEnd(int begin, int end)
1092 {
1093 CheckBeginEndNoLength(begin, end);
1094
1095 if (begin > Length)
1096 {
1097 throw new ArgumentOutOfRangeException($"Value for begin {begin} is out of bounds.");
1098 }
1099
1100 if (end > Length)
1101 {
1102 throw new ArgumentOutOfRangeException($"Value for end {end} is out of bounds.");
1103 }
1104 }
1105
1106 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
1107 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1108 void CheckNoResizeHasEnoughCapacity(int length)
1109 {
1110 CheckNoResizeHasEnoughCapacity(length, Length);
1111 }
1112
1113 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
1114 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1115 void CheckNoResizeHasEnoughCapacity(int length, int index)
1116 {
1117 if (Capacity < index + length)
1118 {
1119 throw new InvalidOperationException($"AddNoResize assumes that list capacity is sufficient (Capacity {Capacity}, Length {Length}), requested length {length}!");
1120 }
1121 }
1122 }
1123
1124 /// <summary>
1125 /// Provides extension methods for UnsafeList.
1126 /// </summary>
1127 [GenerateTestsForBurstCompatibility]
1128 public unsafe static class UnsafeListExtensions
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 this list.</typeparam>
1134 /// <typeparam name="U">The type of value to locate.</typeparam>
1135 /// <param name="list">This list.</param>
1136 /// <param name="value">A value to locate.</param>
1137 /// <returns>The zero-based index of the first occurrence of the value if it is found. Returns -1 if no occurrence is found.</returns>
1138 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
1139 public static int IndexOf<T, U>(this UnsafeList<T> list, U value) where T : unmanaged, IEquatable<U>
1140 {
1141 return NativeArrayExtensions.IndexOf<T, U>(list.Ptr, list.Length, value);
1142 }
1143
1144 /// <summary>
1145 /// Returns true if a particular value is present in this list.
1146 /// </summary>
1147 /// <typeparam name="T">The type of elements in the list.</typeparam>
1148 /// <typeparam name="U">The type of value to locate.</typeparam>
1149 /// <param name="list">This list.</param>
1150 /// <param name="value">The value to locate.</param>
1151 /// <returns>True if the value is present in this list.</returns>
1152 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
1153 public static bool Contains<T, U>(this UnsafeList<T> list, U value) where T : unmanaged, IEquatable<U>
1154 {
1155 return list.IndexOf(value) != -1;
1156 }
1157
1158 /// <summary>
1159 /// Finds the index of the first occurrence of a particular value in the list.
1160 /// </summary>
1161 /// <typeparam name="T">The type of elements in the list.</typeparam>
1162 /// <typeparam name="U">The type of value to locate.</typeparam>
1163 /// <param name="list">This reader of the list.</param>
1164 /// <param name="value">A value to locate.</param>
1165 /// <returns>The zero-based index of the first occurrence of the value if it is found. Returns -1 if no occurrence is found.</returns>
1166 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(int) })]
1167 public static int IndexOf<T, U>(this UnsafeList<T>.ReadOnly list, U value) where T : unmanaged, IEquatable<U>
1168 {
1169 return NativeArrayExtensions.IndexOf<T, U>(list.Ptr, list.Length, value);
1170 }
1171
1172 /// <summary>
1173 /// Returns true if a particular value is present in the list.
1174 /// </summary>
1175 /// <typeparam name="T">The type of elements in the list.</typeparam>
1176 /// <typeparam name="U">The type of value to locate.</typeparam>
1177 /// <param name="list">This reader of the list.</param>
1178 /// <param name="value">The value to locate.</param>
1179 /// <returns>True if the value is present in the list.</returns>
1180 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(int) })]
1181 public static bool Contains<T, U>(this UnsafeList<T>.ReadOnly list, U value) where T : unmanaged, IEquatable<U>
1182 {
1183 return list.IndexOf(value) != -1;
1184 }
1185
1186 /// <summary>
1187 /// **Obsolete.** Use <see cref="UnsafeList{T}.ReadOnly"/> instead.
1188 /// </summary>
1189 /// <typeparam name="T">The type of elements in the list.</typeparam>
1190 /// <typeparam name="U">The type of value to locate.</typeparam>
1191 /// <param name="list">This reader of the list.</param>
1192 /// <param name="value">A value to locate.</param>
1193 /// <returns>The zero-based index of the first occurrence of the value if it is found. Returns -1 if no occurrence is found.</returns>
1194// [Obsolete("'UnsafeList<T>.ParallelReader' has been deprecated; use 'UnsafeList<T>.ReadOnly' instead.")]
1195 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
1196 public static int IndexOf<T, U>(this UnsafeList<T>.ParallelReader list, U value) where T : unmanaged, IEquatable<U>
1197 {
1198 return NativeArrayExtensions.IndexOf<T, U>(list.Ptr, list.Length, value);
1199 }
1200
1201 /// <summary>
1202 /// **Obsolete.** Use <see cref="UnsafeList{T}.ReadOnly"/> instead.
1203 /// </summary>
1204 /// <typeparam name="T">The type of elements in the list.</typeparam>
1205 /// <typeparam name="U">The type of value to locate.</typeparam>
1206 /// <param name="list">This reader of the list.</param>
1207 /// <param name="value">The value to locate.</param>
1208 /// <returns>True if the value is present in the list.</returns>
1209// [Obsolete("'UnsafeList<T>.ParallelReader' has been deprecated; use 'UnsafeList<T>.ReadOnly' instead.")]
1210 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
1211 public static bool Contains<T, U>(this UnsafeList<T>.ParallelReader list, U value) where T : unmanaged, IEquatable<U>
1212 {
1213 return list.IndexOf(value) != -1;
1214 }
1215
1216 /// <summary>
1217 /// Returns true if this container and another have equal length and content.
1218 /// </summary>
1219 /// <typeparam name="T">The type of the source container's elements.</typeparam>
1220 /// <param name="container">The container to compare for equality.</param>
1221 /// <param name="other">The other container to compare for equality.</param>
1222 /// <returns>True if the containers have equal length and content.</returns>
1223 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
1224 public static bool ArraysEqual<T>(this UnsafeList<T> container, in UnsafeList<T> other)
1225 where T : unmanaged, IEquatable<T>
1226 {
1227 if (container.Length != other.Length)
1228 return false;
1229
1230 for (int i = 0; i != container.Length; i++)
1231 {
1232 if (!container[i].Equals(other[i]))
1233 return false;
1234 }
1235
1236 return true;
1237 }
1238
1239 }
1240
1241 internal sealed class UnsafeListTDebugView<T>
1242 where T : unmanaged
1243 {
1244 UnsafeList<T> Data;
1245
1246 public UnsafeListTDebugView(UnsafeList<T> data)
1247 {
1248 Data = data;
1249 }
1250
1251 public unsafe T[] Items
1252 {
1253 get
1254 {
1255 T[] result = new T[Data.Length];
1256
1257 for (var i = 0; i < result.Length; ++i)
1258 {
1259 result[i] = Data.Ptr[i];
1260 }
1261
1262 return result;
1263 }
1264 }
1265 }
1266
1267 /// <summary>
1268 /// An unmanaged, resizable list of pointers.
1269 /// </summary>
1270 /// <typeparam name="T">The type of pointer element.</typeparam>
1271 [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}, IsEmpty = {IsEmpty}")]
1272 [DebuggerTypeProxy(typeof(UnsafePtrListDebugView<>))]
1273 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
1274 [StructLayout(LayoutKind.Sequential)]
1275 public unsafe struct UnsafePtrList<T>
1276 : INativeDisposable
1277 // IIndexable<T> and INativeList<T> can't be implemented because this[index] and ElementAt return T* instead of T.
1278 , IEnumerable<IntPtr> // Used by collection initializers.
1279 where T : unmanaged
1280 {
1281 // <WARNING>
1282 // 'Header' of this struct must binary match `UntypedUnsafeList`, `UnsafeList`, `UnsafePtrList`, and `NativeArray` struct.
1283 // Fields must match UntypedUnsafeList structure, please don't reorder and don't insert anything in between first 4 fields
1284
1285 /// <summary>
1286 /// The internal buffer of this list.
1287 /// </summary>
1288 [NativeDisableUnsafePtrRestriction]
1289 public readonly T** Ptr;
1290
1291 /// <summary>
1292 /// The number of elements.
1293 /// </summary>
1294 public readonly int m_length;
1295
1296 /// <summary>
1297 /// The number of elements that can fit in the internal buffer.
1298 /// </summary>
1299 public readonly int m_capacity;
1300
1301 /// <summary>
1302 /// The allocator used to create the internal buffer.
1303 /// </summary>
1304 public readonly AllocatorManager.AllocatorHandle Allocator;
1305
1306 readonly int padding;
1307
1308 /// <summary>
1309 /// The number of elements.
1310 /// </summary>
1311 /// <value>The number of elements.</value>
1312 public int Length
1313 {
1314 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1315 readonly get => this.ListDataRO().Length;
1316 set => this.ListData().Length = value;
1317 }
1318
1319 /// <summary>
1320 /// The number of elements that can fit in the internal buffer.
1321 /// </summary>
1322 /// <value>The number of elements that can fit in the internal buffer.</value>
1323 public int Capacity
1324 {
1325 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1326 readonly get => this.ListDataRO().Capacity;
1327 set => this.ListData().Capacity = value;
1328 }
1329
1330 /// <summary>
1331 /// The element at an index.
1332 /// </summary>
1333 /// <param name="index">An index.</param>
1334 /// <value>The element at the index.</value>
1335 public T* this[int index]
1336 {
1337 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1338 get
1339 {
1340 CollectionHelper.CheckIndexInRange(index, Length);
1341 return Ptr[CollectionHelper.AssumePositive(index)];
1342 }
1343
1344 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1345 set
1346 {
1347 CollectionHelper.CheckIndexInRange(index, Length);
1348 Ptr[CollectionHelper.AssumePositive(index)] = value;
1349 }
1350 }
1351
1352 /// <summary>
1353 /// Returns a reference to the element at a given index.
1354 /// </summary>
1355 /// <param name="index">The index to access. Must be in the range of [0..Length).</param>
1356 /// <returns>A reference to the element at the index.</returns>
1357 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1358 public ref T* ElementAt(int index)
1359 {
1360 CollectionHelper.CheckIndexInRange(index, Length);
1361 return ref Ptr[CollectionHelper.AssumePositive(index)];
1362 }
1363
1364 /// <summary>
1365 /// Initializes and returns an instance of UnsafePtrList.
1366 /// </summary>
1367 /// <param name="ptr">An existing pointer array to set as the internal buffer.</param>
1368 /// <param name="length">The length.</param>
1369 public unsafe UnsafePtrList(T** ptr, int length) : this()
1370 {
1371 Ptr = ptr;
1372 m_length = length;
1373 m_capacity = length;
1374 Allocator = AllocatorManager.None;
1375 }
1376
1377 /// <summary>
1378 /// Initializes and returns an instance of UnsafePtrList.
1379 /// </summary>
1380 /// <param name="initialCapacity">The initial capacity of the list.</param>
1381 /// <param name="allocator">The allocator to use.</param>
1382 /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
1383 public unsafe UnsafePtrList(int initialCapacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory)
1384 {
1385 Ptr = null;
1386 m_length = 0;
1387 m_capacity = 0;
1388 padding = 0;
1389 Allocator = AllocatorManager.None;
1390
1391 this.ListData() = new UnsafeList<IntPtr>(initialCapacity, allocator, options);
1392 }
1393
1394 /// <summary>
1395 /// Returns a new list of pointers.
1396 /// </summary>
1397 /// <param name="ptr">An existing pointer array to set as the internal buffer.</param>
1398 /// <param name="length">The length.</param>
1399 /// <returns>A pointer to the new list.</returns>
1400 public static UnsafePtrList<T>* Create(T** ptr, int length)
1401 {
1402 UnsafePtrList<T>* listData = AllocatorManager.Allocate<UnsafePtrList<T>>(AllocatorManager.Persistent);
1403 *listData = new UnsafePtrList<T>(ptr, length);
1404 return listData;
1405 }
1406
1407 /// <summary>
1408 /// Returns a new list of pointers.
1409 /// </summary>
1410 /// <param name="initialCapacity">The initial capacity of the list.</param>
1411 /// <param name="allocator">The allocator to use.</param>
1412 /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
1413 /// <returns>A pointer to the new list.</returns>
1414 public static UnsafePtrList<T>* Create(int initialCapacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory)
1415 {
1416 UnsafePtrList<T>* listData = AllocatorManager.Allocate<UnsafePtrList<T>>(allocator);
1417 *listData = new UnsafePtrList<T>(initialCapacity, allocator, options);
1418 return listData;
1419 }
1420
1421 /// <summary>
1422 /// Destroys the list.
1423 /// </summary>
1424 /// <param name="listData">The list to destroy.</param>
1425 public static void Destroy(UnsafePtrList<T>* listData)
1426 {
1427 UnsafeList<IntPtr>.CheckNull(listData);
1428 var allocator = listData->ListData().Allocator.Value == AllocatorManager.Invalid.Value
1429 ? AllocatorManager.Persistent
1430 : listData->ListData().Allocator
1431 ;
1432 listData->Dispose();
1433 AllocatorManager.Free(allocator, listData);
1434 }
1435
1436 /// <summary>
1437 /// Whether the list is empty.
1438 /// </summary>
1439 /// <value>True if the list is empty or the list has not been constructed.</value>
1440 public readonly bool IsEmpty
1441 {
1442 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1443 get => !IsCreated || Length == 0;
1444 }
1445
1446 /// <summary>
1447 /// Whether this list has been allocated (and not yet deallocated).
1448 /// </summary>
1449 /// <value>True if this list has been allocated (and not yet deallocated).</value>
1450 public readonly bool IsCreated
1451 {
1452 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1453 get => Ptr != null;
1454 }
1455
1456 /// <summary>
1457 /// Releases all resources (memory).
1458 /// </summary>
1459 public void Dispose()
1460 {
1461 this.ListData().Dispose();
1462 }
1463
1464 /// <summary>
1465 /// Creates and schedules a job that frees the memory of this list.
1466 /// </summary>
1467 /// <param name="inputDeps">The dependency for the new job.</param>
1468 /// <returns>The handle of the new job. The job depends upon `inputDeps` and frees the memory of this list.</returns>
1469 public JobHandle Dispose(JobHandle inputDeps) => this.ListData().Dispose(inputDeps);
1470
1471 /// <summary>
1472 /// Sets the length to 0.
1473 /// </summary>
1474 /// <remarks>Does not change the capacity.</remarks>
1475 public void Clear() => this.ListData().Clear();
1476
1477 /// <summary>
1478 /// Sets the length, expanding the capacity if necessary.
1479 /// </summary>
1480 /// <param name="length">The new length.</param>
1481 /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
1482 public void Resize(int length, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory) => this.ListData().Resize(length, options);
1483
1484 /// <summary>
1485 /// Sets the capacity.
1486 /// </summary>
1487 /// <param name="capacity">The new capacity.</param>
1488 public void SetCapacity(int capacity) => this.ListData().SetCapacity(capacity);
1489
1490 /// <summary>
1491 /// Sets the capacity to match the length.
1492 /// </summary>
1493 public void TrimExcess() => this.ListData().TrimExcess();
1494
1495 /// <summary>
1496 /// Returns the index of the first occurrence of a specific pointer in the list.
1497 /// </summary>
1498 /// <param name="ptr">The pointer to search for in the list.</param>
1499 /// <returns>The index of the first occurrence of the pointer. Returns -1 if it is not found in the list.</returns>
1500 public int IndexOf(void* ptr)
1501 {
1502 for (int i = 0; i < Length; ++i)
1503 {
1504 if (Ptr[i] == ptr) return i;
1505 }
1506
1507 return -1;
1508 }
1509
1510 /// <summary>
1511 /// Returns true if the list contains at least one occurrence of a specific pointer.
1512 /// </summary>
1513 /// <param name="ptr">The pointer to search for in the list.</param>
1514 /// <returns>True if the list contains at least one occurrence of the pointer.</returns>
1515 public bool Contains(void* ptr)
1516 {
1517 return IndexOf(ptr) != -1;
1518 }
1519
1520 /// <summary>
1521 /// Adds a pointer to the end of this list.
1522 /// </summary>
1523 /// <remarks>
1524 /// Increments the length by 1. Never increases the capacity.
1525 /// </remarks>
1526 /// <param name="value">The pointer to add to the end of the list.</param>
1527 /// <exception cref="InvalidOperationException">Thrown if incrementing the length would exceed the capacity.</exception>
1528 public void AddNoResize(void* value)
1529 {
1530 this.ListData().AddNoResize((IntPtr)value);
1531 }
1532
1533 /// <summary>
1534 /// Copies pointers from a buffer to the end of this list.
1535 /// </summary>
1536 /// <remarks>
1537 /// Increments the length by `count`. Never increases the capacity.
1538 /// </remarks>
1539 /// <param name="ptr">The buffer to copy from.</param>
1540 /// <param name="count">The number of pointers to copy from the buffer.</param>
1541 /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
1542 public void AddRangeNoResize(void** ptr, int count) => this.ListData().AddRangeNoResize(ptr, count);
1543
1544 /// <summary>
1545 /// Copies the pointers of another list to the end of this list.
1546 /// </summary>
1547 /// <param name="list">The other list to copy from.</param>
1548 /// <remarks>
1549 /// Increments the length by the length of the other list. Never increases the capacity.
1550 /// </remarks>
1551 /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
1552 public void AddRangeNoResize(UnsafePtrList<T> list) => this.ListData().AddRangeNoResize(list.Ptr, list.Length);
1553
1554 /// <summary>
1555 /// Adds a pointer to the end of the list.
1556 /// </summary>
1557 /// <param name="value">The pointer to add to the end of this list.</param>
1558 /// <remarks>
1559 /// Increments the length by 1. Increases the capacity if necessary.
1560 /// </remarks>
1561 public void Add(in IntPtr value)
1562 {
1563 this.ListData().Add(value);
1564 }
1565
1566 /// <summary>
1567 /// Adds a pointer to the end of the list.
1568 /// </summary>
1569 /// <param name="value">The pointer to add to the end of this list.</param>
1570 /// <remarks>
1571 /// Increments the length by 1. Increases the capacity if necessary.
1572 /// </remarks>
1573 public void Add(void* value)
1574 {
1575 this.ListData().Add((IntPtr)value);
1576 }
1577
1578 /// <summary>
1579 /// Adds elements from a buffer to this list.
1580 /// </summary>
1581 /// <param name="ptr">A pointer to the buffer.</param>
1582 /// <param name="length">The number of elements to add to the list.</param>
1583 public void AddRange(void* ptr, int length) => this.ListData().AddRange(ptr, length);
1584
1585 /// <summary>
1586 /// Copies the elements of another list to the end of this list.
1587 /// </summary>
1588 /// <param name="list">The other list to copy from.</param>
1589 /// <remarks>
1590 /// Increments the length by the length of the other list. Increases the capacity if necessary.
1591 /// </remarks>
1592 public void AddRange(UnsafePtrList<T> list) => this.ListData().AddRange(list.ListData());
1593
1594 /// <summary>
1595 /// Shifts pointers toward the end of this list, increasing its length.
1596 /// </summary>
1597 /// <remarks>
1598 /// Right-shifts pointers in the list so as to create 'free' slots at the beginning or in the middle.
1599 ///
1600 /// The length is increased by `end - begin`. If necessary, the capacity will be increased accordingly.
1601 ///
1602 /// If `end` equals `begin`, the method does nothing.
1603 ///
1604 /// The pointer at index `begin` will be copied to index `end`, the pointer at index `begin + 1` will be copied to `end + 1`, and so forth.
1605 ///
1606 /// The indexes `begin` up to `end` are not cleared: they will contain whatever pointers they held prior.
1607 /// </remarks>
1608 /// <param name="begin">The index of the first pointer that will be shifted up.</param>
1609 /// <param name="end">The index where the first shifted pointer will end up.</param>
1610 /// <exception cref="ArgumentException">Thrown if `end < begin`.</exception>
1611 /// <exception cref="ArgumentOutOfRangeException">Thrown if `begin` or `end` are out of bounds.</exception>
1612 public void InsertRangeWithBeginEnd(int begin, int end) => this.ListData().InsertRangeWithBeginEnd(begin, end);
1613
1614 /// <summary>
1615 /// Copies the last pointer of this list to the specified index. Decrements the length by 1.
1616 /// </summary>
1617 /// <remarks>Useful as a cheap way to remove a pointer from this list when you don't care about preserving order.</remarks>
1618 /// <param name="index">The index to overwrite with the last pointer.</param>
1619 /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds.</exception>
1620 public void RemoveAtSwapBack(int index) => this.ListData().RemoveAtSwapBack(index);
1621
1622 /// <summary>
1623 /// Copies the last *N* pointer of this list to a range in this list. Decrements the length by *N*.
1624 /// </summary>
1625 /// <remarks>
1626 /// Copies the last `count` pointers to the indexes `index` up to `index + count`.
1627 ///
1628 /// Useful as a cheap way to remove pointers from a list when you don't care about preserving order.
1629 /// </remarks>
1630 /// <param name="index">The index of the first pointer to overwrite.</param>
1631 /// <param name="count">The number of pointers to copy and remove.</param>
1632 /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds</exception>
1633 /// <exception cref="ArgumentOutOfRangeException">Thrown if `count` is negative,
1634 /// or `index + count` exceeds the length.</exception>
1635 public void RemoveRangeSwapBack(int index, int count) => this.ListData().RemoveRangeSwapBack(index, count);
1636
1637 /// <summary>
1638 /// Removes the pointer at an index, shifting everything above it down by one. Decrements the length by 1.
1639 /// </summary>
1640 /// <param name="index">The index of the pointer to remove.</param>
1641 /// <remarks>
1642 /// If you don't care about preserving the order of the pointers, <see cref="RemoveAtSwapBack(int)"/> is a more efficient way to remove pointers.
1643 /// </remarks>
1644 /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds.</exception>
1645 public void RemoveAt(int index) => this.ListData().RemoveAt(index);
1646
1647 /// <summary>
1648 /// Removes *N* pointers in a range, shifting everything above the range down by *N*. Decrements the length by *N*.
1649 /// </summary>
1650 /// <param name="index">The index of the first pointer to remove.</param>
1651 /// <param name="count">The number of pointers to remove.</param>
1652 /// <remarks>
1653 /// If you don't care about preserving the order of the pointers, `RemoveRangeSwapBackWithBeginEnd`
1654 /// is a more efficient way to remove pointers.
1655 /// </remarks>
1656 /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds</exception>
1657 /// <exception cref="ArgumentOutOfRangeException">Thrown if `count` is negative,
1658 /// or `index + count` exceeds the length.</exception>
1659 public void RemoveRange(int index, int count) => this.ListData().RemoveRange(index, count);
1660
1661 /// <summary>
1662 /// This method is not implemented. It will throw NotImplementedException if it is used.
1663 /// </summary>
1664 /// <remarks>Use Enumerator GetEnumerator() instead.</remarks>
1665 /// <returns>Throws NotImplementedException.</returns>
1666 /// <exception cref="NotImplementedException">Method is not implemented.</exception>
1667 IEnumerator IEnumerable.GetEnumerator()
1668 {
1669 throw new NotImplementedException();
1670 }
1671
1672 /// <summary>
1673 /// This method is not implemented. It will throw NotImplementedException if it is used.
1674 /// </summary>
1675 /// <remarks>Use Enumerator GetEnumerator() instead.</remarks>
1676 /// <returns>Throws NotImplementedException.</returns>
1677 /// <exception cref="NotImplementedException">Method is not implemented.</exception>
1678 IEnumerator<IntPtr> IEnumerable<IntPtr>.GetEnumerator()
1679 {
1680 throw new NotImplementedException();
1681 }
1682
1683 /// <summary>
1684 /// Returns a read only of this list.
1685 /// </summary>
1686 /// <returns>A read only of this list.</returns>
1687 public ReadOnly AsReadOnly()
1688 {
1689 return new ReadOnly(Ptr, Length);
1690 }
1691
1692 /// <summary>
1693 /// A read only for an UnsafePtrList<T>.
1694 /// </summary>
1695 /// <remarks>
1696 /// Use <see cref="AsReadOnly"/> to create a read only for a list.
1697 /// </remarks>
1698 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
1699 public unsafe struct ReadOnly
1700 {
1701 /// <summary>
1702 /// The internal buffer of the list.
1703 /// </summary>
1704 [NativeDisableUnsafePtrRestriction]
1705 public readonly T** Ptr;
1706
1707 /// <summary>
1708 /// The number of elements.
1709 /// </summary>
1710 public readonly int Length;
1711
1712 /// <summary>
1713 /// Whether this list has been allocated (and not yet deallocated).
1714 /// </summary>
1715 /// <value>True if this list has been allocated (and not yet deallocated).</value>
1716 public readonly bool IsCreated
1717 {
1718 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1719 get => Ptr != null;
1720 }
1721
1722 /// <summary>
1723 /// Whether the list is empty.
1724 /// </summary>
1725 /// <value>True if the list is empty or the list has not been constructed.</value>
1726 public readonly bool IsEmpty
1727 {
1728 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1729 get => !IsCreated || Length == 0;
1730 }
1731
1732 internal ReadOnly(T** ptr, int length)
1733 {
1734 Ptr = ptr;
1735 Length = length;
1736 }
1737
1738 /// <summary>
1739 /// Returns the index of the first occurrence of a specific pointer in the list.
1740 /// </summary>
1741 /// <param name="ptr">The pointer to search for in the list.</param>
1742 /// <returns>The index of the first occurrence of the pointer. Returns -1 if it is not found in the list.</returns>
1743 public int IndexOf(void* ptr)
1744 {
1745 for (int i = 0; i < Length; ++i)
1746 {
1747 if (Ptr[i] == ptr) return i;
1748 }
1749 return -1;
1750 }
1751
1752 /// <summary>
1753 /// Returns true if the list contains at least one occurrence of a specific pointer.
1754 /// </summary>
1755 /// <param name="ptr">The pointer to search for in the list.</param>
1756 /// <returns>True if the list contains at least one occurrence of the pointer.</returns>
1757 public bool Contains(void* ptr)
1758 {
1759 return IndexOf(ptr) != -1;
1760 }
1761 }
1762
1763 /// <summary>
1764 /// **Obsolete**. Use <see cref="AsReadOnly"/> instead.
1765 /// </summary>
1766 /// <returns>A parallel reader of this list.</returns>
1767// [Obsolete("'AsParallelReader' has been deprecated; use 'AsReadOnly' instead. (UnityUpgradable) -> AsReadOnly")]
1768 public ParallelReader AsParallelReader()
1769 {
1770 return new ParallelReader(Ptr, Length);
1771 }
1772
1773 /// <summary>
1774 /// **Obsolete.** Use <see cref="ReadOnly"/> instead.
1775 /// </summary>
1776 /// <remarks>
1777 /// Use <see cref="AsParallelReader"/> to create a parallel reader for a list.
1778 /// </remarks>
1779// [Obsolete("'ParallelReader' has been deprecated; use 'ReadOnly' instead. (UnityUpgradable) -> ReadOnly")]
1780 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
1781 public unsafe struct ParallelReader
1782 {
1783 /// <summary>
1784 /// The internal buffer of the list.
1785 /// </summary>
1786 [NativeDisableUnsafePtrRestriction]
1787 public readonly T** Ptr;
1788
1789 /// <summary>
1790 /// The number of elements.
1791 /// </summary>
1792 public readonly int Length;
1793
1794 internal ParallelReader(T** ptr, int length)
1795 {
1796 Ptr = ptr;
1797 Length = length;
1798 }
1799
1800 /// <summary>
1801 /// Returns the index of the first occurrence of a specific pointer in the list.
1802 /// </summary>
1803 /// <param name="ptr">The pointer to search for in the list.</param>
1804 /// <returns>The index of the first occurrence of the pointer. Returns -1 if it is not found in the list.</returns>
1805 public int IndexOf(void* ptr)
1806 {
1807 for (int i = 0; i < Length; ++i)
1808 {
1809 if (Ptr[i] == ptr) return i;
1810 }
1811 return -1;
1812 }
1813
1814 /// <summary>
1815 /// Returns true if the list contains at least one occurrence of a specific pointer.
1816 /// </summary>
1817 /// <param name="ptr">The pointer to search for in the list.</param>
1818 /// <returns>True if the list contains at least one occurrence of the pointer.</returns>
1819 public bool Contains(void* ptr)
1820 {
1821 return IndexOf(ptr) != -1;
1822 }
1823 }
1824
1825 /// <summary>
1826 /// Returns a parallel writer of this list.
1827 /// </summary>
1828 /// <returns>A parallel writer of this list.</returns>
1829 public ParallelWriter AsParallelWriter()
1830 {
1831 return new ParallelWriter(Ptr, (UnsafeList<IntPtr>*)UnsafeUtility.AddressOf(ref this));
1832 }
1833
1834 /// <summary>
1835 /// A parallel writer for an UnsafePtrList<T>.
1836 /// </summary>
1837 /// <remarks>
1838 /// Use <see cref="AsParallelWriter"/> to create a parallel writer for a list.
1839 /// </remarks>
1840 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
1841 public unsafe struct ParallelWriter
1842 {
1843 /// <summary>
1844 /// The data of the list.
1845 /// </summary>
1846 [NativeDisableUnsafePtrRestriction]
1847 public readonly T** Ptr;
1848
1849 /// <summary>
1850 /// The UnsafeList to write to.
1851 /// </summary>
1852 [NativeDisableUnsafePtrRestriction]
1853 public UnsafeList<IntPtr>* ListData;
1854
1855 internal unsafe ParallelWriter(T** ptr, UnsafeList<IntPtr>* listData)
1856 {
1857 Ptr = ptr;
1858 ListData = listData;
1859 }
1860
1861 /// <summary>
1862 /// Adds a pointer to the end of the list.
1863 /// </summary>
1864 /// <param name="value">The pointer to add to the end of the list.</param>
1865 /// <remarks>
1866 /// Increments the length by 1. Never increases the capacity.
1867 /// </remarks>
1868 /// <exception cref="InvalidOperationException">Thrown if incrementing the length would exceed the capacity.</exception>
1869 public void AddNoResize(T* value) => ListData->AddNoResize((IntPtr)value);
1870
1871 /// <summary>
1872 /// Copies pointers from a buffer to the end of the list.
1873 /// </summary>
1874 /// <param name="ptr">The buffer to copy from.</param>
1875 /// <param name="count">The number of pointers to copy from the buffer.</param>
1876 /// <remarks>
1877 /// Increments the length by `count`. Never increases the capacity.
1878 /// </remarks>
1879 /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
1880 public void AddRangeNoResize(T** ptr, int count) => ListData->AddRangeNoResize(ptr, count);
1881
1882 /// <summary>
1883 /// Copies the pointers of another list to the end of this list.
1884 /// </summary>
1885 /// <param name="list">The other list to copy from.</param>
1886 /// <remarks>
1887 /// Increments the length by the length of the other list. Never increases the capacity.
1888 /// </remarks>
1889 /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
1890 public void AddRangeNoResize(UnsafePtrList<T> list) => ListData->AddRangeNoResize(list.Ptr, list.Length);
1891 }
1892 }
1893
1894 [GenerateTestsForBurstCompatibility]
1895 internal static class UnsafePtrListExtensions
1896 {
1897 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
1898 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1899 public static ref UnsafeList<IntPtr> ListData<T>(ref this UnsafePtrList<T> from) where T : unmanaged => ref UnsafeUtility.As<UnsafePtrList<T>, UnsafeList<IntPtr>>(ref from);
1900
1901 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
1902 [MethodImpl(MethodImplOptions.AggressiveInlining)]
1903 public static UnsafeList<IntPtr> ListDataRO<T>(this UnsafePtrList<T> from) where T : unmanaged => UnsafeUtility.As<UnsafePtrList<T>, UnsafeList<IntPtr>>(ref from);
1904 }
1905
1906 internal sealed class UnsafePtrListDebugView<T>
1907 where T : unmanaged
1908 {
1909 UnsafePtrList<T> Data;
1910
1911 public UnsafePtrListDebugView(UnsafePtrList<T> data)
1912 {
1913 Data = data;
1914 }
1915
1916 public unsafe T*[] Items
1917 {
1918 get
1919 {
1920 T*[] result = new T*[Data.Length];
1921
1922 for (var i = 0; i < result.Length; ++i)
1923 {
1924 result[i] = Data.Ptr[i];
1925 }
1926
1927 return result;
1928 }
1929 }
1930 }
1931}