1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891 |
- using System;
- using System.Collections;
- using System.Collections.Generic;
- using System.Diagnostics;
- using System.Runtime.CompilerServices;
- using System.Runtime.InteropServices;
- using System.Threading;
- using Unity.Burst;
- using Unity.Jobs;
- using Unity.Jobs.LowLevel.Unsafe;
- using Unity.Mathematics;
-
- namespace Unity.Collections.LowLevel.Unsafe
- {
-
- [BurstCompile]
- internal unsafe struct UnsafeDisposeJob : IJob
- {
- [NativeDisableUnsafePtrRestriction]
- public void* Ptr;
- public AllocatorManager.AllocatorHandle Allocator;
-
- public void Execute()
- {
- AllocatorManager.Free(Allocator, Ptr);
- }
- }
-
- [StructLayout(LayoutKind.Sequential)]
- internal unsafe struct UntypedUnsafeList
- {
- #pragma warning disable 169
- // <WARNING>
- // 'Header' of this struct must binary match `UntypedUnsafeList`, `UnsafeList`, `UnsafePtrList`, and `NativeArray` struct.
- [NativeDisableUnsafePtrRestriction]
- internal readonly void* Ptr;
- internal readonly int m_length;
- internal readonly int m_capacity;
- internal readonly AllocatorManager.AllocatorHandle Allocator;
- internal readonly int padding;
- #pragma warning restore 169
- }
-
- /// <summary>
- /// An unmanaged, resizable list.
- /// </summary>
- /// <typeparam name="T">The type of the elements.</typeparam>
- [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}, IsEmpty = {IsEmpty}")]
- [DebuggerTypeProxy(typeof(UnsafeListTDebugView<>))]
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- [StructLayout(LayoutKind.Sequential)]
- public unsafe struct UnsafeList<T>
- : INativeDisposable
- , INativeList<T>
- , IEnumerable<T> // Used by collection initializers.
- where T : unmanaged
- {
- // <WARNING>
- // 'Header' of this struct must binary match `UntypedUnsafeList`, `UnsafeList`, `UnsafePtrList`, and `NativeArray` struct.
- // Fields must match UntypedUnsafeList structure, please don't reorder and don't insert anything in between first 4 fields
-
- /// <summary>
- /// The internal buffer of this list.
- /// </summary>
- [NativeDisableUnsafePtrRestriction]
- public T* Ptr;
-
- /// <summary>
- /// The number of elements.
- /// </summary>
- public int m_length;
-
- /// <summary>
- /// The number of elements that can fit in the internal buffer.
- /// </summary>
- public int m_capacity;
-
- /// <summary>
- /// The allocator used to create the internal buffer.
- /// </summary>
- public AllocatorManager.AllocatorHandle Allocator;
-
- readonly int padding;
-
- /// <summary>
- /// The number of elements.
- /// </summary>
- /// <value>The number of elements.</value>
- public int Length
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- readonly get => CollectionHelper.AssumePositive(m_length);
-
- set
- {
- if (value > Capacity)
- {
- Resize(value);
- }
- else
- {
- m_length = value;
- }
- }
- }
-
- /// <summary>
- /// The number of elements that can fit in the internal buffer.
- /// </summary>
- /// <value>The number of elements that can fit in the internal buffer.</value>
- public int Capacity
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- readonly get => CollectionHelper.AssumePositive(m_capacity);
- set => SetCapacity(value);
- }
-
- /// <summary>
- /// The element at an index.
- /// </summary>
- /// <param name="index">An index.</param>
- /// <value>The element at the index.</value>
- public T this[int index]
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- CollectionHelper.CheckIndexInRange(index, m_length);
- return Ptr[CollectionHelper.AssumePositive(index)];
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- set
- {
- CollectionHelper.CheckIndexInRange(index, m_length);
- Ptr[CollectionHelper.AssumePositive(index)] = value;
- }
- }
-
- /// <summary>
- /// Returns a reference to the element at a given index.
- /// </summary>
- /// <param name="index">The index to access. Must be in the range of [0..Length).</param>
- /// <returns>A reference to the element at the index.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ref T ElementAt(int index)
- {
- CollectionHelper.CheckIndexInRange(index, m_length);
- return ref Ptr[CollectionHelper.AssumePositive(index)];
- }
-
- /// <summary>
- /// Initializes and returns an instance of UnsafeList.
- /// </summary>
- /// <param name="ptr">An existing byte array to set as the internal buffer.</param>
- /// <param name="length">The length.</param>
- public UnsafeList(T* ptr, int length) : this()
- {
- Ptr = ptr;
- m_length = length;
- m_capacity = length;
- Allocator = AllocatorManager.None;
- }
-
- /// <summary>
- /// Initializes and returns an instance of UnsafeList.
- /// </summary>
- /// <param name="initialCapacity">The initial capacity of the list.</param>
- /// <param name="allocator">The allocator to use.</param>
- /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
- public UnsafeList(int initialCapacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory)
- {
- Ptr = null;
- m_length = 0;
- m_capacity = 0;
- Allocator = allocator;
- padding = 0;
-
- SetCapacity(math.max(initialCapacity, 1));
-
- if (options == NativeArrayOptions.ClearMemory && Ptr != null)
- {
- var sizeOf = sizeof(T);
- UnsafeUtility.MemClear(Ptr, Capacity * sizeOf);
- }
- }
-
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(AllocatorManager.AllocatorHandle) })]
- internal static UnsafeList<T>* Create<U>(int initialCapacity, ref U allocator, NativeArrayOptions options) where U : unmanaged, AllocatorManager.IAllocator
- {
- UnsafeList<T>* listData = allocator.Allocate(default(UnsafeList<T>), 1);
- *listData = new UnsafeList<T>(initialCapacity, allocator.Handle, options);
- return listData;
- }
-
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(AllocatorManager.AllocatorHandle) })]
- internal static void Destroy<U>(UnsafeList<T>* listData, ref U allocator) where U : unmanaged, AllocatorManager.IAllocator
- {
- CheckNull(listData);
- listData->Dispose(ref allocator);
- allocator.Free(listData, sizeof(UnsafeList<T>), UnsafeUtility.AlignOf<UnsafeList<T>>(), 1);
- }
-
- /// <summary>
- /// Returns a new list.
- /// </summary>
- /// <param name="initialCapacity">The initial capacity of the list.</param>
- /// <param name="allocator">The allocator to use.</param>
- /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
- /// <returns>A pointer to the new list.</returns>
- public static UnsafeList<T>* Create(int initialCapacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory)
- {
- UnsafeList<T>* listData = AllocatorManager.Allocate<UnsafeList<T>>(allocator);
- *listData = new UnsafeList<T>(initialCapacity, allocator, options);
-
- return listData;
- }
-
- /// <summary>
- /// Destroys the list.
- /// </summary>
- /// <param name="listData">The list to destroy.</param>
- public static void Destroy(UnsafeList<T>* listData)
- {
- CheckNull(listData);
- var allocator = listData->Allocator;
- listData->Dispose();
- AllocatorManager.Free(allocator, listData);
- }
-
- /// <summary>
- /// Whether the list is empty.
- /// </summary>
- /// <value>True if the list is empty or the list has not been constructed.</value>
- public readonly bool IsEmpty
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => !IsCreated || m_length == 0;
- }
-
- /// <summary>
- /// Whether this list has been allocated (and not yet deallocated).
- /// </summary>
- /// <value>True if this list has been allocated (and not yet deallocated).</value>
- public readonly bool IsCreated
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => Ptr != null;
- }
-
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(AllocatorManager.AllocatorHandle) })]
- internal void Dispose<U>(ref U allocator) where U : unmanaged, AllocatorManager.IAllocator
- {
- allocator.Free(Ptr, m_capacity);
- Ptr = null;
- m_length = 0;
- m_capacity = 0;
- }
-
- /// <summary>
- /// Releases all resources (memory).
- /// </summary>
- public void Dispose()
- {
- if (!IsCreated)
- {
- return;
- }
-
- if (CollectionHelper.ShouldDeallocate(Allocator))
- {
- AllocatorManager.Free(Allocator, Ptr, m_capacity);
- Allocator = AllocatorManager.Invalid;
- }
-
- Ptr = null;
- m_length = 0;
- m_capacity = 0;
- }
-
- /// <summary>
- /// Creates and schedules a job that frees the memory of this list.
- /// </summary>
- /// <param name="inputDeps">The dependency for the new job.</param>
- /// <returns>The handle of the new job. The job depends upon `inputDeps` and frees the memory of this list.</returns>
- public JobHandle Dispose(JobHandle inputDeps)
- {
- if (!IsCreated)
- {
- return inputDeps;
- }
-
- if (CollectionHelper.ShouldDeallocate(Allocator))
- {
- var jobHandle = new UnsafeDisposeJob { Ptr = Ptr, Allocator = Allocator }.Schedule(inputDeps);
-
- Ptr = null;
- Allocator = AllocatorManager.Invalid;
-
- return jobHandle;
- }
-
- Ptr = null;
-
- return inputDeps;
- }
-
- /// <summary>
- /// Sets the length to 0.
- /// </summary>
- /// <remarks>Does not change the capacity.</remarks>
- public void Clear()
- {
- m_length = 0;
- }
-
- /// <summary>
- /// Sets the length, expanding the capacity if necessary.
- /// </summary>
- /// <param name="length">The new length.</param>
- /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
- public void Resize(int length, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory)
- {
- var oldLength = m_length;
-
- if (length > Capacity)
- {
- SetCapacity(length);
- }
-
- m_length = length;
-
- if (options == NativeArrayOptions.ClearMemory && oldLength < length)
- {
- var num = length - oldLength;
- byte* ptr = (byte*)Ptr;
- var sizeOf = sizeof(T);
- UnsafeUtility.MemClear(ptr + oldLength * sizeOf, num * sizeOf);
- }
- }
-
- void ResizeExact<U>(ref U allocator, int newCapacity) where U : unmanaged, AllocatorManager.IAllocator
- {
- newCapacity = math.max(0, newCapacity);
-
- CollectionHelper.CheckAllocator(Allocator);
- T* newPointer = null;
-
- var alignOf = UnsafeUtility.AlignOf<T>();
- var sizeOf = sizeof(T);
-
- if (newCapacity > 0)
- {
- newPointer = (T*)allocator.Allocate(sizeOf, alignOf, newCapacity);
-
- if (Ptr != null && m_capacity > 0)
- {
- var itemsToCopy = math.min(newCapacity, Capacity);
- var bytesToCopy = itemsToCopy * sizeOf;
- UnsafeUtility.MemCpy(newPointer, Ptr, bytesToCopy);
- }
- }
-
- allocator.Free(Ptr, Capacity);
-
- Ptr = newPointer;
- m_capacity = newCapacity;
- m_length = math.min(m_length, newCapacity);
- }
-
- void ResizeExact(int capacity)
- {
- ResizeExact(ref Allocator, capacity);
- }
-
- void SetCapacity<U>(ref U allocator, int capacity) where U : unmanaged, AllocatorManager.IAllocator
- {
- CollectionHelper.CheckCapacityInRange(capacity, Length);
-
- var sizeOf = sizeof(T);
- var newCapacity = math.max(capacity, CollectionHelper.CacheLineSize / sizeOf);
- newCapacity = math.ceilpow2(newCapacity);
-
- if (newCapacity == Capacity)
- {
- return;
- }
-
- ResizeExact(ref allocator, newCapacity);
- }
-
- /// <summary>
- /// Sets the capacity.
- /// </summary>
- /// <param name="capacity">The new capacity.</param>
- public void SetCapacity(int capacity)
- {
- SetCapacity(ref Allocator, capacity);
- }
-
- /// <summary>
- /// Sets the capacity to match the length.
- /// </summary>
- public void TrimExcess()
- {
- if (Capacity != m_length)
- {
- ResizeExact(m_length);
- }
- }
-
- /// <summary>
- /// Adds an element to the end of this list.
- /// </summary>
- /// <remarks>
- /// Increments the length by 1. Never increases the capacity.
- /// </remarks>
- /// <param name="value">The value to add to the end of the list.</param>
- /// <exception cref="InvalidOperationException">Thrown if incrementing the length would exceed the capacity.</exception>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void AddNoResize(T value)
- {
- CheckNoResizeHasEnoughCapacity(1);
- UnsafeUtility.WriteArrayElement(Ptr, m_length, value);
- m_length += 1;
- }
-
- /// <summary>
- /// Copies elements from a buffer to the end of this list.
- /// </summary>
- /// <remarks>
- /// Increments the length by `count`. Never increases the capacity.
- /// </remarks>
- /// <param name="ptr">The buffer to copy from.</param>
- /// <param name="count">The number of elements to copy from the buffer.</param>
- /// <exception cref="InvalidOperationExceptionv">Thrown if the increased length would exceed the capacity.</exception>
- public void AddRangeNoResize(void* ptr, int count)
- {
- CheckNoResizeHasEnoughCapacity(count);
- var sizeOf = sizeof(T);
- void* dst = (byte*)Ptr + m_length * sizeOf;
- UnsafeUtility.MemCpy(dst, ptr, count * sizeOf);
- m_length += count;
- }
-
- /// <summary>
- /// Copies the elements of another list to the end of this list.
- /// </summary>
- /// <param name="list">The other list to copy from.</param>
- /// <remarks>
- /// Increments the length by the length of the other list. Never increases the capacity.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- public void AddRangeNoResize(UnsafeList<T> list)
- {
- AddRangeNoResize(list.Ptr, CollectionHelper.AssumePositive(list.Length));
- }
-
- /// <summary>
- /// Adds an element to the end of the list.
- /// </summary>
- /// <param name="value">The value to add to the end of this list.</param>
- /// <remarks>
- /// Increments the length by 1. Increases the capacity if necessary.
- /// </remarks>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public void Add(in T value)
- {
- var idx = m_length;
- if (m_length < m_capacity)
- {
- Ptr[idx] = value;
- m_length++;
- return;
- }
-
- Resize(idx + 1);
- Ptr[idx] = value;
- }
-
- /// <summary>
- /// Copies the elements of a buffer to the end of this list.
- /// </summary>
- /// <param name="ptr">The buffer to copy from.</param>
- /// <param name="count">The number of elements to copy from the buffer.</param>
- /// <remarks>
- /// Increments the length by `count`. Increases the capacity if necessary.
- /// </remarks>
- public void AddRange(void* ptr, int count)
- {
- var idx = m_length;
-
- if (m_length + count > Capacity)
- {
- Resize(m_length + count);
- }
- else
- {
- m_length += count;
- }
-
- var sizeOf = sizeof(T);
- void* dst = (byte*)Ptr + idx * sizeOf;
- UnsafeUtility.MemCpy(dst, ptr, count * sizeOf);
- }
-
- /// <summary>
- /// Copies the elements of another list to the end of the list.
- /// </summary>
- /// <param name="list">The list to copy from.</param>
- /// <remarks>
- /// The length is increased by the length of the other list. Increases the capacity if necessary.
- /// </remarks>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- public void AddRange(UnsafeList<T> list)
- {
- AddRange(list.Ptr, list.Length);
- }
-
- /// <summary>
- /// Appends value count times to the end of this list.
- /// </summary>
- /// <param name="value">The value to add to the end of this list.</param>
- /// <param name="count">The number of times to replicate the value.</param>
- /// <remarks>
- /// Length is incremented by count. If necessary, the capacity is increased.
- /// </remarks>
- public void AddReplicate(in T value, int count)
- {
- var idx = m_length;
- if (m_length + count > Capacity)
- {
- Resize(m_length + count);
- }
- else
- {
- m_length += count;
- }
-
- fixed (void* ptr = &value)
- {
- UnsafeUtility.MemCpyReplicate(Ptr + idx, ptr, UnsafeUtility.SizeOf<T>(), count);
- }
- }
-
- /// <summary>
- /// Shifts elements toward the end of this list, increasing its length.
- /// </summary>
- /// <remarks>
- /// Right-shifts elements in the list so as to create 'free' slots at the beginning or in the middle.
- ///
- /// The length is increased by `end - begin`. If necessary, the capacity will be increased accordingly.
- ///
- /// If `end` equals `begin`, the method does nothing.
- ///
- /// 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.
- ///
- /// The indexes `begin` up to `end` are not cleared: they will contain whatever values they held prior.
- /// </remarks>
- /// <param name="begin">The index of the first element that will be shifted up.</param>
- /// <param name="end">The index where the first shifted element will end up.</param>
- /// <exception cref="ArgumentException">Thrown if `end < begin`.</exception>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if `begin` or `end` are out of bounds.</exception>
- public void InsertRangeWithBeginEnd(int begin, int end)
- {
- CheckBeginEndNoLength(begin, end);
-
- // Because we've checked begin and end in `CheckBeginEnd` above, we can now
- // assume they are positive.
- begin = CollectionHelper.AssumePositive(begin);
- end = CollectionHelper.AssumePositive(end);
-
- int items = end - begin;
- if (items < 1)
- {
- return;
- }
-
- var oldLength = m_length;
-
- if (m_length + items > Capacity)
- {
- Resize(m_length + items);
- }
- else
- {
- m_length += items;
- }
-
- var itemsToCopy = oldLength - begin;
-
- if (itemsToCopy < 1)
- {
- return;
- }
-
- var sizeOf = sizeof(T);
- var bytesToCopy = itemsToCopy * sizeOf;
- unsafe
- {
- byte* ptr = (byte*)Ptr;
- byte* dest = ptr + end * sizeOf;
- byte* src = ptr + begin * sizeOf;
- UnsafeUtility.MemMove(dest, src, bytesToCopy);
- }
- }
-
- /// <summary>
- /// Shifts elements toward the end of this list, increasing its length.
- /// </summary>
- /// <remarks>
- /// Right-shifts elements in the list so as to create 'free' slots at the beginning or in the middle.
- ///
- /// The length is increased by `count`. If necessary, the capacity will be increased accordingly.
- ///
- /// If `count` equals `0`, the method does nothing.
- ///
- /// 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.
- ///
- /// The indexes `index` up to `index + count` are not cleared: they will contain whatever values they held prior.
- /// </remarks>
- /// <param name="index">The index of the first element that will be shifted up.</param>
- /// <param name="count">The number of elements to insert.</param>
- /// <exception cref="ArgumentException">Thrown if `count` is negative.</exception>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if `index` is out of bounds.</exception>
- public void InsertRange(int index, int count) => InsertRangeWithBeginEnd(index, index + count);
-
- /// <summary>
- /// Copies the last element of this list to the specified index. Decrements the length by 1.
- /// </summary>
- /// <remarks>Useful as a cheap way to remove an element from this list when you don't care about preserving order.</remarks>
- /// <param name="index">The index to overwrite with the last element.</param>
- /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds.</exception>
- public void RemoveAtSwapBack(int index)
- {
- CollectionHelper.CheckIndexInRange(index, m_length);
-
- index = CollectionHelper.AssumePositive(index);
- int copyFrom = m_length - 1;
- T* dst = (T*)Ptr + index;
- T* src = (T*)Ptr + copyFrom;
- (*dst) = (*src);
- m_length -= 1;
- }
-
- /// <summary>
- /// Copies the last *N* elements of this list to a range in this list. Decrements the length by *N*.
- /// </summary>
- /// <remarks>
- /// Copies the last `count` elements to the indexes `index` up to `index + count`.
- ///
- /// Useful as a cheap way to remove elements from a list when you don't care about preserving order.
- /// </remarks>
- /// <param name="index">The index of the first element to overwrite.</param>
- /// <param name="count">The number of elements to copy and remove.</param>
- /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds</exception>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if `count` is negative,
- /// or `index + count` exceeds the length.</exception>
- public void RemoveRangeSwapBack(int index, int count)
- {
- CheckIndexCount(index, count);
-
- index = CollectionHelper.AssumePositive(index);
- count = CollectionHelper.AssumePositive(count);
-
- if (count > 0)
- {
- int copyFrom = math.max(m_length - count, index + count);
- var sizeOf = sizeof(T);
- void* dst = (byte*)Ptr + index * sizeOf;
- void* src = (byte*)Ptr + copyFrom * sizeOf;
- UnsafeUtility.MemCpy(dst, src, (m_length - copyFrom) * sizeOf);
- m_length -= count;
- }
- }
-
- /// <summary>
- /// Removes the element at an index, shifting everything above it down by one. Decrements the length by 1.
- /// </summary>
- /// <param name="index">The index of the element to remove.</param>
- /// <remarks>
- /// If you don't care about preserving the order of the elements, <see cref="RemoveAtSwapBack(int)"/> is a more efficient way to remove elements.
- /// </remarks>
- /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds.</exception>
- public void RemoveAt(int index)
- {
- CollectionHelper.CheckIndexInRange(index, m_length);
-
- index = CollectionHelper.AssumePositive(index);
-
- T* dst = Ptr + index;
- T* src = dst + 1;
- m_length--;
-
- // Because these tend to be smaller (< 1MB), and the cost of jumping context to native and back is
- // so high, this consistently optimizes to better code than UnsafeUtility.MemCpy
- for (int i = index; i < m_length; i++)
- {
- *dst++ = *src++;
- }
- }
-
- /// <summary>
- /// Removes *N* elements in a range, shifting everything above the range down by *N*. Decrements the length by *N*.
- /// </summary>
- /// <param name="index">The index of the first element to remove.</param>
- /// <param name="count">The number of elements to remove.</param>
- /// <remarks>
- /// If you don't care about preserving the order of the elements, `RemoveRangeSwapBackWithBeginEnd`
- /// is a more efficient way to remove elements.
- /// </remarks>
- /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds</exception>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if `count` is negative,
- /// or `index + count` exceeds the length.</exception>
- public void RemoveRange(int index, int count)
- {
- CheckIndexCount(index, count);
-
- index = CollectionHelper.AssumePositive(index);
- count = CollectionHelper.AssumePositive(count);
-
- if (count > 0)
- {
- int copyFrom = math.min(index + count, m_length);
- var sizeOf = sizeof(T);
- void* dst = (byte*)Ptr + index * sizeOf;
- void* src = (byte*)Ptr + copyFrom * sizeOf;
- UnsafeUtility.MemCpy(dst, src, (m_length - copyFrom) * sizeOf);
- m_length -= count;
- }
- }
-
- /// <summary>
- /// Returns a read only of this list.
- /// </summary>
- /// <returns>A read only of this list.</returns>
- public ReadOnly AsReadOnly()
- {
- return new ReadOnly(Ptr, Length);
- }
-
- /// <summary>
- /// A read only for an UnsafeList<T>.
- /// </summary>
- /// <remarks>
- /// Use <see cref="AsReadOnly"/> to create a read only for a list.
- /// </remarks>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- public unsafe struct ReadOnly
- : IEnumerable<T>
- {
- /// <summary>
- /// The internal buffer of the list.
- /// </summary>
- [NativeDisableUnsafePtrRestriction]
- public readonly T* Ptr;
-
- /// <summary>
- /// The number of elements.
- /// </summary>
- public readonly int Length;
-
- internal ReadOnly(T* ptr, int length)
- {
- Ptr = ptr;
- Length = length;
- }
-
- /// <summary>
- /// Returns an enumerator over the elements of the list.
- /// </summary>
- /// <returns>An enumerator over the elements of the list.</returns>
- public Enumerator GetEnumerator()
- {
- return new Enumerator { m_Ptr = Ptr, m_Length = Length, m_Index = -1 };
- }
-
- /// <summary>
- /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
- /// </summary>
- /// <returns>Throws NotImplementedException.</returns>
- /// <exception cref="NotImplementedException">Method is not implemented.</exception>
- IEnumerator IEnumerable.GetEnumerator()
- {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
- /// </summary>
- /// <returns>Throws NotImplementedException.</returns>
- /// <exception cref="NotImplementedException">Method is not implemented.</exception>
- IEnumerator<T> IEnumerable<T>.GetEnumerator()
- {
- throw new NotImplementedException();
- }
- }
-
- /// <summary>
- /// **Obsolete.** Use <see cref="AsReadOnly"/> instead.
- /// </summary>
- /// <returns>A parallel reader of this list.</returns>
- // [Obsolete("'AsParallelReader' has been deprecated; use 'AsReadOnly' instead. (UnityUpgradable) -> AsReadOnly")]
- public ParallelReader AsParallelReader()
- {
- return new ParallelReader(Ptr, Length);
- }
-
- /// <summary>
- /// **Obsolete.** Use <see cref="ReadOnly"/> instead.
- /// </summary>
- /// <remarks>
- /// Use <see cref="AsParallelReader"/> to create a parallel reader for a list.
- /// </remarks>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
- // [Obsolete("'ParallelReader' has been deprecated; use 'ReadOnly' instead. (UnityUpgradable) -> ReadOnly")]
- public unsafe struct ParallelReader
- {
- /// <summary>
- /// The internal buffer of the list.
- /// </summary>
- [NativeDisableUnsafePtrRestriction]
- public readonly T* Ptr;
-
- /// <summary>
- /// The number of elements.
- /// </summary>
- public readonly int Length;
-
- internal ParallelReader(T* ptr, int length)
- {
- Ptr = ptr;
- Length = length;
- }
- }
-
- /// <summary>
- /// Returns a parallel writer of this list.
- /// </summary>
- /// <returns>A parallel writer of this list.</returns>
- public ParallelWriter AsParallelWriter()
- {
- return new ParallelWriter((UnsafeList<T>*)UnsafeUtility.AddressOf(ref this));
- }
-
- /// <summary>
- /// A parallel writer for an UnsafeList<T>.
- /// </summary>
- /// <remarks>
- /// Use <see cref="AsParallelWriter"/> to create a parallel writer for a list.
- /// </remarks>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
- public unsafe struct ParallelWriter
- {
- /// <summary>
- /// The data of the list.
- /// </summary>
- public readonly void* Ptr
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- return ListData->Ptr;
- }
- }
-
- /// <summary>
- /// The UnsafeList to write to.
- /// </summary>
- [NativeDisableUnsafePtrRestriction]
- public UnsafeList<T>* ListData;
-
- internal unsafe ParallelWriter(UnsafeList<T>* listData)
- {
- ListData = listData;
- }
-
- /// <summary>
- /// Adds an element to the end of the list.
- /// </summary>
- /// <param name="value">The value to add to the end of the list.</param>
- /// <remarks>
- /// Increments the length by 1. Never increases the capacity.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown if incrementing the length would exceed the capacity.</exception>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- public void AddNoResize(T value)
- {
- var idx = Interlocked.Increment(ref ListData->m_length) - 1;
- ListData->CheckNoResizeHasEnoughCapacity(idx, 1);
- UnsafeUtility.WriteArrayElement(ListData->Ptr, idx, value);
- }
-
- /// <summary>
- /// Copies elements from a buffer to the end of the list.
- /// </summary>
- /// <param name="ptr">The buffer to copy from.</param>
- /// <param name="count">The number of elements to copy from the buffer.</param>
- /// <remarks>
- /// Increments the length by `count`. Never increases the capacity.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- public void AddRangeNoResize(void* ptr, int count)
- {
- var idx = Interlocked.Add(ref ListData->m_length, count) - count;
- ListData->CheckNoResizeHasEnoughCapacity(idx, count);
- void* dst = (byte*)ListData->Ptr + idx * sizeof(T);
- UnsafeUtility.MemCpy(dst, ptr, count * sizeof(T));
- }
-
- /// <summary>
- /// Copies the elements of another list to the end of this list.
- /// </summary>
- /// <param name="list">The other list to copy from.</param>
- /// <remarks>
- /// Increments the length by the length of the other list. Never increases the capacity.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- public void AddRangeNoResize(UnsafeList<T> list)
- {
- AddRangeNoResize(list.Ptr, list.Length);
- }
- }
-
- /// <summary>
- /// Copies all elements of specified container to this container.
- /// </summary>
- /// <param name="other">An container to copy into this container.</param>
- public void CopyFrom(in NativeArray<T> other)
- {
- Resize(other.Length);
- UnsafeUtility.MemCpy(Ptr, other.GetUnsafeReadOnlyPtr<T>(), UnsafeUtility.SizeOf<T>() * other.Length);
- }
-
- /// <summary>
- /// Copies all elements of specified container to this container.
- /// </summary>
- /// <param name="other">An container to copy into this container.</param>
- public void CopyFrom(in UnsafeList<T> other)
- {
- Resize(other.Length);
- UnsafeUtility.MemCpy(Ptr, other.Ptr, UnsafeUtility.SizeOf<T>() * other.Length);
- }
-
- /// <summary>
- /// Returns an enumerator over the elements of the list.
- /// </summary>
- /// <returns>An enumerator over the elements of the list.</returns>
- public Enumerator GetEnumerator()
- {
- return new Enumerator { m_Ptr = Ptr, m_Length = Length, m_Index = -1 };
- }
-
- /// <summary>
- /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
- /// </summary>
- /// <returns>Throws NotImplementedException.</returns>
- /// <exception cref="NotImplementedException">Method is not implemented.</exception>
- IEnumerator IEnumerable.GetEnumerator()
- {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
- /// </summary>
- /// <returns>Throws NotImplementedException.</returns>
- /// <exception cref="NotImplementedException">Method is not implemented.</exception>
- IEnumerator<T> IEnumerable<T>.GetEnumerator()
- {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// An enumerator over the elements of a list.
- /// </summary>
- /// <remarks>
- /// In an enumerator's initial state, <see cref="Current"/> is invalid.
- /// The first <see cref="MoveNext"/> call advances the enumerator to the first element of the list.
- /// </remarks>
- public struct Enumerator : IEnumerator<T>
- {
- internal T* m_Ptr;
- internal int m_Length;
- internal int m_Index;
-
- /// <summary>
- /// Does nothing.
- /// </summary>
- public void Dispose() { }
-
- /// <summary>
- /// Advances the enumerator to the next element of the list.
- /// </summary>
- /// <remarks>
- /// The first `MoveNext` call advances the enumerator to the first element of the list. Before this call, `Current` is not valid to read.
- /// </remarks>
- /// <returns>True if `Current` is valid to read after the call.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public bool MoveNext() => ++m_Index < m_Length;
-
- /// <summary>
- /// Resets the enumerator to its initial state.
- /// </summary>
- public void Reset() => m_Index = -1;
-
- /// <summary>
- /// The current element.
- /// </summary>
- /// <value>The current element.</value>
- public T Current
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => m_Ptr[m_Index];
- }
-
- object IEnumerator.Current => Current;
- }
-
- [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
- internal static void CheckNull(void* listData)
- {
- if (listData == null)
- {
- throw new InvalidOperationException("UnsafeList has yet to be created or has been destroyed!");
- }
- }
-
- [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
- void CheckIndexCount(int index, int count)
- {
- if (count < 0)
- {
- throw new ArgumentOutOfRangeException($"Value for count {count} must be positive.");
- }
-
- if (index < 0)
- {
- throw new IndexOutOfRangeException($"Value for index {index} must be positive.");
- }
-
- if (index > Length)
- {
- throw new IndexOutOfRangeException($"Value for index {index} is out of bounds.");
- }
-
- if (index + count > Length)
- {
- throw new ArgumentOutOfRangeException($"Value for count {count} is out of bounds.");
- }
- }
-
- [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
- void CheckBeginEndNoLength(int begin, int end)
- {
- if (begin > end)
- {
- throw new ArgumentException($"Value for begin {begin} index must less or equal to end {end}.");
- }
-
- if (begin < 0)
- {
- throw new ArgumentOutOfRangeException($"Value for begin {begin} must be positive.");
- }
- }
-
- [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
- void CheckBeginEnd(int begin, int end)
- {
- CheckBeginEndNoLength(begin, end);
-
- if (begin > Length)
- {
- throw new ArgumentOutOfRangeException($"Value for begin {begin} is out of bounds.");
- }
-
- if (end > Length)
- {
- throw new ArgumentOutOfRangeException($"Value for end {end} is out of bounds.");
- }
- }
-
- [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- void CheckNoResizeHasEnoughCapacity(int length)
- {
- CheckNoResizeHasEnoughCapacity(length, Length);
- }
-
- [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- void CheckNoResizeHasEnoughCapacity(int length, int index)
- {
- if (Capacity < index + length)
- {
- throw new InvalidOperationException($"AddNoResize assumes that list capacity is sufficient (Capacity {Capacity}, Length {Length}), requested length {length}!");
- }
- }
- }
-
- /// <summary>
- /// Provides extension methods for UnsafeList.
- /// </summary>
- [GenerateTestsForBurstCompatibility]
- public unsafe static class UnsafeListExtensions
- {
- /// <summary>
- /// Finds the index of the first occurrence of a particular value in this list.
- /// </summary>
- /// <typeparam name="T">The type of elements in this list.</typeparam>
- /// <typeparam name="U">The type of value to locate.</typeparam>
- /// <param name="list">This list.</param>
- /// <param name="value">A value to locate.</param>
- /// <returns>The zero-based index of the first occurrence of the value if it is found. Returns -1 if no occurrence is found.</returns>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
- public static int IndexOf<T, U>(this UnsafeList<T> list, U value) where T : unmanaged, IEquatable<U>
- {
- return NativeArrayExtensions.IndexOf<T, U>(list.Ptr, list.Length, value);
- }
-
- /// <summary>
- /// Returns true if a particular value is present in this list.
- /// </summary>
- /// <typeparam name="T">The type of elements in the list.</typeparam>
- /// <typeparam name="U">The type of value to locate.</typeparam>
- /// <param name="list">This list.</param>
- /// <param name="value">The value to locate.</param>
- /// <returns>True if the value is present in this list.</returns>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
- public static bool Contains<T, U>(this UnsafeList<T> list, U value) where T : unmanaged, IEquatable<U>
- {
- return list.IndexOf(value) != -1;
- }
-
- /// <summary>
- /// Finds the index of the first occurrence of a particular value in the list.
- /// </summary>
- /// <typeparam name="T">The type of elements in the list.</typeparam>
- /// <typeparam name="U">The type of value to locate.</typeparam>
- /// <param name="list">This reader of the list.</param>
- /// <param name="value">A value to locate.</param>
- /// <returns>The zero-based index of the first occurrence of the value if it is found. Returns -1 if no occurrence is found.</returns>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(int) })]
- public static int IndexOf<T, U>(this UnsafeList<T>.ReadOnly list, U value) where T : unmanaged, IEquatable<U>
- {
- return NativeArrayExtensions.IndexOf<T, U>(list.Ptr, list.Length, value);
- }
-
- /// <summary>
- /// Returns true if a particular value is present in the list.
- /// </summary>
- /// <typeparam name="T">The type of elements in the list.</typeparam>
- /// <typeparam name="U">The type of value to locate.</typeparam>
- /// <param name="list">This reader of the list.</param>
- /// <param name="value">The value to locate.</param>
- /// <returns>True if the value is present in the list.</returns>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(int) })]
- public static bool Contains<T, U>(this UnsafeList<T>.ReadOnly list, U value) where T : unmanaged, IEquatable<U>
- {
- return list.IndexOf(value) != -1;
- }
-
- /// <summary>
- /// **Obsolete.** Use <see cref="UnsafeList{T}.ReadOnly"/> instead.
- /// </summary>
- /// <typeparam name="T">The type of elements in the list.</typeparam>
- /// <typeparam name="U">The type of value to locate.</typeparam>
- /// <param name="list">This reader of the list.</param>
- /// <param name="value">A value to locate.</param>
- /// <returns>The zero-based index of the first occurrence of the value if it is found. Returns -1 if no occurrence is found.</returns>
- // [Obsolete("'UnsafeList<T>.ParallelReader' has been deprecated; use 'UnsafeList<T>.ReadOnly' instead.")]
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
- public static int IndexOf<T, U>(this UnsafeList<T>.ParallelReader list, U value) where T : unmanaged, IEquatable<U>
- {
- return NativeArrayExtensions.IndexOf<T, U>(list.Ptr, list.Length, value);
- }
-
- /// <summary>
- /// **Obsolete.** Use <see cref="UnsafeList{T}.ReadOnly"/> instead.
- /// </summary>
- /// <typeparam name="T">The type of elements in the list.</typeparam>
- /// <typeparam name="U">The type of value to locate.</typeparam>
- /// <param name="list">This reader of the list.</param>
- /// <param name="value">The value to locate.</param>
- /// <returns>True if the value is present in the list.</returns>
- // [Obsolete("'UnsafeList<T>.ParallelReader' has been deprecated; use 'UnsafeList<T>.ReadOnly' instead.")]
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
- public static bool Contains<T, U>(this UnsafeList<T>.ParallelReader list, U value) where T : unmanaged, IEquatable<U>
- {
- return list.IndexOf(value) != -1;
- }
-
- /// <summary>
- /// Returns true if this container and another have equal length and content.
- /// </summary>
- /// <typeparam name="T">The type of the source container's elements.</typeparam>
- /// <param name="container">The container to compare for equality.</param>
- /// <param name="other">The other container to compare for equality.</param>
- /// <returns>True if the containers have equal length and content.</returns>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new [] { typeof(int) })]
- public static bool ArraysEqual<T>(this UnsafeList<T> container, in UnsafeList<T> other)
- where T : unmanaged, IEquatable<T>
- {
- if (container.Length != other.Length)
- return false;
-
- for (int i = 0; i != container.Length; i++)
- {
- if (!container[i].Equals(other[i]))
- return false;
- }
-
- return true;
- }
-
- }
-
- internal sealed class UnsafeListTDebugView<T>
- where T : unmanaged
- {
- UnsafeList<T> Data;
-
- public UnsafeListTDebugView(UnsafeList<T> data)
- {
- Data = data;
- }
-
- public unsafe T[] Items
- {
- get
- {
- T[] result = new T[Data.Length];
-
- for (var i = 0; i < result.Length; ++i)
- {
- result[i] = Data.Ptr[i];
- }
-
- return result;
- }
- }
- }
-
- /// <summary>
- /// An unmanaged, resizable list of pointers.
- /// </summary>
- /// <typeparam name="T">The type of pointer element.</typeparam>
- [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}, IsEmpty = {IsEmpty}")]
- [DebuggerTypeProxy(typeof(UnsafePtrListDebugView<>))]
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- [StructLayout(LayoutKind.Sequential)]
- public unsafe struct UnsafePtrList<T>
- : INativeDisposable
- // IIndexable<T> and INativeList<T> can't be implemented because this[index] and ElementAt return T* instead of T.
- , IEnumerable<IntPtr> // Used by collection initializers.
- where T : unmanaged
- {
- // <WARNING>
- // 'Header' of this struct must binary match `UntypedUnsafeList`, `UnsafeList`, `UnsafePtrList`, and `NativeArray` struct.
- // Fields must match UntypedUnsafeList structure, please don't reorder and don't insert anything in between first 4 fields
-
- /// <summary>
- /// The internal buffer of this list.
- /// </summary>
- [NativeDisableUnsafePtrRestriction]
- public readonly T** Ptr;
-
- /// <summary>
- /// The number of elements.
- /// </summary>
- public readonly int m_length;
-
- /// <summary>
- /// The number of elements that can fit in the internal buffer.
- /// </summary>
- public readonly int m_capacity;
-
- /// <summary>
- /// The allocator used to create the internal buffer.
- /// </summary>
- public readonly AllocatorManager.AllocatorHandle Allocator;
-
- readonly int padding;
-
- /// <summary>
- /// The number of elements.
- /// </summary>
- /// <value>The number of elements.</value>
- public int Length
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- readonly get => this.ListDataRO().Length;
- set => this.ListData().Length = value;
- }
-
- /// <summary>
- /// The number of elements that can fit in the internal buffer.
- /// </summary>
- /// <value>The number of elements that can fit in the internal buffer.</value>
- public int Capacity
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- readonly get => this.ListDataRO().Capacity;
- set => this.ListData().Capacity = value;
- }
-
- /// <summary>
- /// The element at an index.
- /// </summary>
- /// <param name="index">An index.</param>
- /// <value>The element at the index.</value>
- public T* this[int index]
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get
- {
- CollectionHelper.CheckIndexInRange(index, Length);
- return Ptr[CollectionHelper.AssumePositive(index)];
- }
-
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- set
- {
- CollectionHelper.CheckIndexInRange(index, Length);
- Ptr[CollectionHelper.AssumePositive(index)] = value;
- }
- }
-
- /// <summary>
- /// Returns a reference to the element at a given index.
- /// </summary>
- /// <param name="index">The index to access. Must be in the range of [0..Length).</param>
- /// <returns>A reference to the element at the index.</returns>
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public ref T* ElementAt(int index)
- {
- CollectionHelper.CheckIndexInRange(index, Length);
- return ref Ptr[CollectionHelper.AssumePositive(index)];
- }
-
- /// <summary>
- /// Initializes and returns an instance of UnsafePtrList.
- /// </summary>
- /// <param name="ptr">An existing pointer array to set as the internal buffer.</param>
- /// <param name="length">The length.</param>
- public unsafe UnsafePtrList(T** ptr, int length) : this()
- {
- Ptr = ptr;
- m_length = length;
- m_capacity = length;
- Allocator = AllocatorManager.None;
- }
-
- /// <summary>
- /// Initializes and returns an instance of UnsafePtrList.
- /// </summary>
- /// <param name="initialCapacity">The initial capacity of the list.</param>
- /// <param name="allocator">The allocator to use.</param>
- /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
- public unsafe UnsafePtrList(int initialCapacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory)
- {
- Ptr = null;
- m_length = 0;
- m_capacity = 0;
- padding = 0;
- Allocator = AllocatorManager.None;
-
- this.ListData() = new UnsafeList<IntPtr>(initialCapacity, allocator, options);
- }
-
- /// <summary>
- /// Returns a new list of pointers.
- /// </summary>
- /// <param name="ptr">An existing pointer array to set as the internal buffer.</param>
- /// <param name="length">The length.</param>
- /// <returns>A pointer to the new list.</returns>
- public static UnsafePtrList<T>* Create(T** ptr, int length)
- {
- UnsafePtrList<T>* listData = AllocatorManager.Allocate<UnsafePtrList<T>>(AllocatorManager.Persistent);
- *listData = new UnsafePtrList<T>(ptr, length);
- return listData;
- }
-
- /// <summary>
- /// Returns a new list of pointers.
- /// </summary>
- /// <param name="initialCapacity">The initial capacity of the list.</param>
- /// <param name="allocator">The allocator to use.</param>
- /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
- /// <returns>A pointer to the new list.</returns>
- public static UnsafePtrList<T>* Create(int initialCapacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory)
- {
- UnsafePtrList<T>* listData = AllocatorManager.Allocate<UnsafePtrList<T>>(allocator);
- *listData = new UnsafePtrList<T>(initialCapacity, allocator, options);
- return listData;
- }
-
- /// <summary>
- /// Destroys the list.
- /// </summary>
- /// <param name="listData">The list to destroy.</param>
- public static void Destroy(UnsafePtrList<T>* listData)
- {
- UnsafeList<IntPtr>.CheckNull(listData);
- var allocator = listData->ListData().Allocator.Value == AllocatorManager.Invalid.Value
- ? AllocatorManager.Persistent
- : listData->ListData().Allocator
- ;
- listData->Dispose();
- AllocatorManager.Free(allocator, listData);
- }
-
- /// <summary>
- /// Whether the list is empty.
- /// </summary>
- /// <value>True if the list is empty or the list has not been constructed.</value>
- public readonly bool IsEmpty
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => !IsCreated || Length == 0;
- }
-
- /// <summary>
- /// Whether this list has been allocated (and not yet deallocated).
- /// </summary>
- /// <value>True if this list has been allocated (and not yet deallocated).</value>
- public readonly bool IsCreated
- {
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- get => Ptr != null;
- }
-
- /// <summary>
- /// Releases all resources (memory).
- /// </summary>
- public void Dispose()
- {
- this.ListData().Dispose();
- }
-
- /// <summary>
- /// Creates and schedules a job that frees the memory of this list.
- /// </summary>
- /// <param name="inputDeps">The dependency for the new job.</param>
- /// <returns>The handle of the new job. The job depends upon `inputDeps` and frees the memory of this list.</returns>
- public JobHandle Dispose(JobHandle inputDeps) => this.ListData().Dispose(inputDeps);
-
- /// <summary>
- /// Sets the length to 0.
- /// </summary>
- /// <remarks>Does not change the capacity.</remarks>
- public void Clear() => this.ListData().Clear();
-
- /// <summary>
- /// Sets the length, expanding the capacity if necessary.
- /// </summary>
- /// <param name="length">The new length.</param>
- /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
- public void Resize(int length, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory) => this.ListData().Resize(length, options);
-
- /// <summary>
- /// Sets the capacity.
- /// </summary>
- /// <param name="capacity">The new capacity.</param>
- public void SetCapacity(int capacity) => this.ListData().SetCapacity(capacity);
-
- /// <summary>
- /// Sets the capacity to match the length.
- /// </summary>
- public void TrimExcess() => this.ListData().TrimExcess();
-
- /// <summary>
- /// Returns the index of the first occurrence of a specific pointer in the list.
- /// </summary>
- /// <param name="ptr">The pointer to search for in the list.</param>
- /// <returns>The index of the first occurrence of the pointer. Returns -1 if it is not found in the list.</returns>
- public int IndexOf(void* ptr)
- {
- for (int i = 0; i < Length; ++i)
- {
- if (Ptr[i] == ptr) return i;
- }
-
- return -1;
- }
-
- /// <summary>
- /// Returns true if the list contains at least one occurrence of a specific pointer.
- /// </summary>
- /// <param name="ptr">The pointer to search for in the list.</param>
- /// <returns>True if the list contains at least one occurrence of the pointer.</returns>
- public bool Contains(void* ptr)
- {
- return IndexOf(ptr) != -1;
- }
-
- /// <summary>
- /// Adds a pointer to the end of this list.
- /// </summary>
- /// <remarks>
- /// Increments the length by 1. Never increases the capacity.
- /// </remarks>
- /// <param name="value">The pointer to add to the end of the list.</param>
- /// <exception cref="InvalidOperationException">Thrown if incrementing the length would exceed the capacity.</exception>
- public void AddNoResize(void* value)
- {
- this.ListData().AddNoResize((IntPtr)value);
- }
-
- /// <summary>
- /// Copies pointers from a buffer to the end of this list.
- /// </summary>
- /// <remarks>
- /// Increments the length by `count`. Never increases the capacity.
- /// </remarks>
- /// <param name="ptr">The buffer to copy from.</param>
- /// <param name="count">The number of pointers to copy from the buffer.</param>
- /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
- public void AddRangeNoResize(void** ptr, int count) => this.ListData().AddRangeNoResize(ptr, count);
-
- /// <summary>
- /// Copies the pointers of another list to the end of this list.
- /// </summary>
- /// <param name="list">The other list to copy from.</param>
- /// <remarks>
- /// Increments the length by the length of the other list. Never increases the capacity.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
- public void AddRangeNoResize(UnsafePtrList<T> list) => this.ListData().AddRangeNoResize(list.Ptr, list.Length);
-
- /// <summary>
- /// Adds a pointer to the end of the list.
- /// </summary>
- /// <param name="value">The pointer to add to the end of this list.</param>
- /// <remarks>
- /// Increments the length by 1. Increases the capacity if necessary.
- /// </remarks>
- public void Add(in IntPtr value)
- {
- this.ListData().Add(value);
- }
-
- /// <summary>
- /// Adds a pointer to the end of the list.
- /// </summary>
- /// <param name="value">The pointer to add to the end of this list.</param>
- /// <remarks>
- /// Increments the length by 1. Increases the capacity if necessary.
- /// </remarks>
- public void Add(void* value)
- {
- this.ListData().Add((IntPtr)value);
- }
-
- /// <summary>
- /// Adds elements from a buffer to this list.
- /// </summary>
- /// <param name="ptr">A pointer to the buffer.</param>
- /// <param name="length">The number of elements to add to the list.</param>
- public void AddRange(void* ptr, int length) => this.ListData().AddRange(ptr, length);
-
- /// <summary>
- /// Copies the elements of another list to the end of this list.
- /// </summary>
- /// <param name="list">The other list to copy from.</param>
- /// <remarks>
- /// Increments the length by the length of the other list. Increases the capacity if necessary.
- /// </remarks>
- public void AddRange(UnsafePtrList<T> list) => this.ListData().AddRange(list.ListData());
-
- /// <summary>
- /// Shifts pointers toward the end of this list, increasing its length.
- /// </summary>
- /// <remarks>
- /// Right-shifts pointers in the list so as to create 'free' slots at the beginning or in the middle.
- ///
- /// The length is increased by `end - begin`. If necessary, the capacity will be increased accordingly.
- ///
- /// If `end` equals `begin`, the method does nothing.
- ///
- /// 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.
- ///
- /// The indexes `begin` up to `end` are not cleared: they will contain whatever pointers they held prior.
- /// </remarks>
- /// <param name="begin">The index of the first pointer that will be shifted up.</param>
- /// <param name="end">The index where the first shifted pointer will end up.</param>
- /// <exception cref="ArgumentException">Thrown if `end < begin`.</exception>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if `begin` or `end` are out of bounds.</exception>
- public void InsertRangeWithBeginEnd(int begin, int end) => this.ListData().InsertRangeWithBeginEnd(begin, end);
-
- /// <summary>
- /// Copies the last pointer of this list to the specified index. Decrements the length by 1.
- /// </summary>
- /// <remarks>Useful as a cheap way to remove a pointer from this list when you don't care about preserving order.</remarks>
- /// <param name="index">The index to overwrite with the last pointer.</param>
- /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds.</exception>
- public void RemoveAtSwapBack(int index) => this.ListData().RemoveAtSwapBack(index);
-
- /// <summary>
- /// Copies the last *N* pointer of this list to a range in this list. Decrements the length by *N*.
- /// </summary>
- /// <remarks>
- /// Copies the last `count` pointers to the indexes `index` up to `index + count`.
- ///
- /// Useful as a cheap way to remove pointers from a list when you don't care about preserving order.
- /// </remarks>
- /// <param name="index">The index of the first pointer to overwrite.</param>
- /// <param name="count">The number of pointers to copy and remove.</param>
- /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds</exception>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if `count` is negative,
- /// or `index + count` exceeds the length.</exception>
- public void RemoveRangeSwapBack(int index, int count) => this.ListData().RemoveRangeSwapBack(index, count);
-
- /// <summary>
- /// Removes the pointer at an index, shifting everything above it down by one. Decrements the length by 1.
- /// </summary>
- /// <param name="index">The index of the pointer to remove.</param>
- /// <remarks>
- /// If you don't care about preserving the order of the pointers, <see cref="RemoveAtSwapBack(int)"/> is a more efficient way to remove pointers.
- /// </remarks>
- /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds.</exception>
- public void RemoveAt(int index) => this.ListData().RemoveAt(index);
-
- /// <summary>
- /// Removes *N* pointers in a range, shifting everything above the range down by *N*. Decrements the length by *N*.
- /// </summary>
- /// <param name="index">The index of the first pointer to remove.</param>
- /// <param name="count">The number of pointers to remove.</param>
- /// <remarks>
- /// If you don't care about preserving the order of the pointers, `RemoveRangeSwapBackWithBeginEnd`
- /// is a more efficient way to remove pointers.
- /// </remarks>
- /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds</exception>
- /// <exception cref="ArgumentOutOfRangeException">Thrown if `count` is negative,
- /// or `index + count` exceeds the length.</exception>
- public void RemoveRange(int index, int count) => this.ListData().RemoveRange(index, count);
-
- /// <summary>
- /// This method is not implemented. It will throw NotImplementedException if it is used.
- /// </summary>
- /// <remarks>Use Enumerator GetEnumerator() instead.</remarks>
- /// <returns>Throws NotImplementedException.</returns>
- /// <exception cref="NotImplementedException">Method is not implemented.</exception>
- IEnumerator IEnumerable.GetEnumerator()
- {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// This method is not implemented. It will throw NotImplementedException if it is used.
- /// </summary>
- /// <remarks>Use Enumerator GetEnumerator() instead.</remarks>
- /// <returns>Throws NotImplementedException.</returns>
- /// <exception cref="NotImplementedException">Method is not implemented.</exception>
- IEnumerator<IntPtr> IEnumerable<IntPtr>.GetEnumerator()
- {
- throw new NotImplementedException();
- }
-
- /// <summary>
- /// Returns a read only of this list.
- /// </summary>
- /// <returns>A read only of this list.</returns>
- public ReadOnly AsReadOnly()
- {
- return new ReadOnly(Ptr, Length);
- }
-
- /// <summary>
- /// A read only for an UnsafePtrList<T>.
- /// </summary>
- /// <remarks>
- /// Use <see cref="AsReadOnly"/> to create a read only for a list.
- /// </remarks>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- public unsafe struct ReadOnly
- {
- /// <summary>
- /// The internal buffer of the list.
- /// </summary>
- [NativeDisableUnsafePtrRestriction]
- public readonly T** Ptr;
-
- /// <summary>
- /// The number of elements.
- /// </summary>
- public readonly int Length;
-
- internal ReadOnly(T** ptr, int length)
- {
- Ptr = ptr;
- Length = length;
- }
-
- /// <summary>
- /// Returns the index of the first occurrence of a specific pointer in the list.
- /// </summary>
- /// <param name="ptr">The pointer to search for in the list.</param>
- /// <returns>The index of the first occurrence of the pointer. Returns -1 if it is not found in the list.</returns>
- public int IndexOf(void* ptr)
- {
- for (int i = 0; i < Length; ++i)
- {
- if (Ptr[i] == ptr) return i;
- }
- return -1;
- }
-
- /// <summary>
- /// Returns true if the list contains at least one occurrence of a specific pointer.
- /// </summary>
- /// <param name="ptr">The pointer to search for in the list.</param>
- /// <returns>True if the list contains at least one occurrence of the pointer.</returns>
- public bool Contains(void* ptr)
- {
- return IndexOf(ptr) != -1;
- }
- }
-
- /// <summary>
- /// **Obsolete**. Use <see cref="AsReadOnly"/> instead.
- /// </summary>
- /// <returns>A parallel reader of this list.</returns>
- // [Obsolete("'AsParallelReader' has been deprecated; use 'AsReadOnly' instead. (UnityUpgradable) -> AsReadOnly")]
- public ParallelReader AsParallelReader()
- {
- return new ParallelReader(Ptr, Length);
- }
-
- /// <summary>
- /// **Obsolete.** Use <see cref="ReadOnly"/> instead.
- /// </summary>
- /// <remarks>
- /// Use <see cref="AsParallelReader"/> to create a parallel reader for a list.
- /// </remarks>
- // [Obsolete("'ParallelReader' has been deprecated; use 'ReadOnly' instead. (UnityUpgradable) -> ReadOnly")]
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- public unsafe struct ParallelReader
- {
- /// <summary>
- /// The internal buffer of the list.
- /// </summary>
- [NativeDisableUnsafePtrRestriction]
- public readonly T** Ptr;
-
- /// <summary>
- /// The number of elements.
- /// </summary>
- public readonly int Length;
-
- internal ParallelReader(T** ptr, int length)
- {
- Ptr = ptr;
- Length = length;
- }
-
- /// <summary>
- /// Returns the index of the first occurrence of a specific pointer in the list.
- /// </summary>
- /// <param name="ptr">The pointer to search for in the list.</param>
- /// <returns>The index of the first occurrence of the pointer. Returns -1 if it is not found in the list.</returns>
- public int IndexOf(void* ptr)
- {
- for (int i = 0; i < Length; ++i)
- {
- if (Ptr[i] == ptr) return i;
- }
- return -1;
- }
-
- /// <summary>
- /// Returns true if the list contains at least one occurrence of a specific pointer.
- /// </summary>
- /// <param name="ptr">The pointer to search for in the list.</param>
- /// <returns>True if the list contains at least one occurrence of the pointer.</returns>
- public bool Contains(void* ptr)
- {
- return IndexOf(ptr) != -1;
- }
- }
-
- /// <summary>
- /// Returns a parallel writer of this list.
- /// </summary>
- /// <returns>A parallel writer of this list.</returns>
- public ParallelWriter AsParallelWriter()
- {
- return new ParallelWriter(Ptr, (UnsafeList<IntPtr>*)UnsafeUtility.AddressOf(ref this));
- }
-
- /// <summary>
- /// A parallel writer for an UnsafePtrList<T>.
- /// </summary>
- /// <remarks>
- /// Use <see cref="AsParallelWriter"/> to create a parallel writer for a list.
- /// </remarks>
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- public unsafe struct ParallelWriter
- {
- /// <summary>
- /// The data of the list.
- /// </summary>
- [NativeDisableUnsafePtrRestriction]
- public readonly T** Ptr;
-
- /// <summary>
- /// The UnsafeList to write to.
- /// </summary>
- [NativeDisableUnsafePtrRestriction]
- public UnsafeList<IntPtr>* ListData;
-
- internal unsafe ParallelWriter(T** ptr, UnsafeList<IntPtr>* listData)
- {
- Ptr = ptr;
- ListData = listData;
- }
-
- /// <summary>
- /// Adds a pointer to the end of the list.
- /// </summary>
- /// <param name="value">The pointer to add to the end of the list.</param>
- /// <remarks>
- /// Increments the length by 1. Never increases the capacity.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown if incrementing the length would exceed the capacity.</exception>
- public void AddNoResize(T* value) => ListData->AddNoResize((IntPtr)value);
-
- /// <summary>
- /// Copies pointers from a buffer to the end of the list.
- /// </summary>
- /// <param name="ptr">The buffer to copy from.</param>
- /// <param name="count">The number of pointers to copy from the buffer.</param>
- /// <remarks>
- /// Increments the length by `count`. Never increases the capacity.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
- public void AddRangeNoResize(T** ptr, int count) => ListData->AddRangeNoResize(ptr, count);
-
- /// <summary>
- /// Copies the pointers of another list to the end of this list.
- /// </summary>
- /// <param name="list">The other list to copy from.</param>
- /// <remarks>
- /// Increments the length by the length of the other list. Never increases the capacity.
- /// </remarks>
- /// <exception cref="InvalidOperationException">Thrown if the increased length would exceed the capacity.</exception>
- public void AddRangeNoResize(UnsafePtrList<T> list) => ListData->AddRangeNoResize(list.Ptr, list.Length);
- }
- }
-
- [GenerateTestsForBurstCompatibility]
- internal static class UnsafePtrListExtensions
- {
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static ref UnsafeList<IntPtr> ListData<T>(ref this UnsafePtrList<T> from) where T : unmanaged => ref UnsafeUtility.As<UnsafePtrList<T>, UnsafeList<IntPtr>>(ref from);
-
- [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
- [MethodImpl(MethodImplOptions.AggressiveInlining)]
- public static UnsafeList<IntPtr> ListDataRO<T>(this UnsafePtrList<T> from) where T : unmanaged => UnsafeUtility.As<UnsafePtrList<T>, UnsafeList<IntPtr>>(ref from);
- }
-
- internal sealed class UnsafePtrListDebugView<T>
- where T : unmanaged
- {
- UnsafePtrList<T> Data;
-
- public UnsafePtrListDebugView(UnsafePtrList<T> data)
- {
- Data = data;
- }
-
- public unsafe T*[] Items
- {
- get
- {
- T*[] result = new T*[Data.Length];
-
- for (var i = 0; i < result.Length; ++i)
- {
- result[i] = Data.Ptr[i];
- }
-
- return result;
- }
- }
- }
- }
|