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 &lt; 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&lt;T&gt;. 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&lt;T&gt;. 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 &lt; 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&lt;T&gt;. 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&lt;T&gt;. 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}