暫無描述
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

UnsafeList.cs 68KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Diagnostics;
  5. using System.Runtime.InteropServices;
  6. using System.Threading;
  7. using Unity.Burst;
  8. using Unity.Jobs;
  9. using Unity.Mathematics;
  10. namespace Unity.Collections.LowLevel.Unsafe
  11. {
  12. [BurstCompile]
  13. internal unsafe struct UnsafeDisposeJob : IJob
  14. {
  15. [NativeDisableUnsafePtrRestriction]
  16. public void* Ptr;
  17. public AllocatorManager.AllocatorHandle Allocator;
  18. public void Execute()
  19. {
  20. AllocatorManager.Free(Allocator, Ptr);
  21. }
  22. }
  23. internal unsafe struct UntypedUnsafeList
  24. {
  25. #pragma warning disable 169
  26. [NativeDisableUnsafePtrRestriction]
  27. public void* Ptr;
  28. public int m_length;
  29. public int m_capacity;
  30. public AllocatorManager.AllocatorHandle Allocator;
  31. internal int obsolete_length;
  32. internal int obsolete_capacity;
  33. #pragma warning restore 169
  34. }
  35. /// <summary>
  36. /// An unmanaged, resizable list.
  37. /// </summary>
  38. /// <typeparam name="T">The type of the elements.</typeparam>
  39. [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}, IsEmpty = {IsEmpty}")]
  40. [DebuggerTypeProxy(typeof(UnsafeListTDebugView<>))]
  41. [StructLayout(LayoutKind.Sequential)]
  42. [BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
  43. public unsafe struct UnsafeList<T>
  44. : INativeDisposable
  45. , INativeList<T>
  46. , IEnumerable<T> // Used by collection initializers.
  47. where T : unmanaged
  48. {
  49. // <WARNING>
  50. // 'Header' of this struct must binary match 'UntypedUnsafeList' struct
  51. // Fields must match UntypedUnsafeList structure, please don't reorder and don't insert anything in between first 4 fields
  52. /// <summary>
  53. /// The internal buffer of this list.
  54. /// </summary>
  55. [NativeDisableUnsafePtrRestriction]
  56. public T* Ptr;
  57. /// <summary>
  58. /// The number of elements.
  59. /// </summary>
  60. public int m_length;
  61. /// <summary>
  62. /// The number of elements that can fit in the internal buffer.
  63. /// </summary>
  64. public int m_capacity;
  65. /// <summary>
  66. /// The allocator used to create the internal buffer.
  67. /// </summary>
  68. public AllocatorManager.AllocatorHandle Allocator;
  69. [Obsolete("Use Length property (UnityUpgradable) -> Length", true)]
  70. public int length;
  71. [Obsolete("Use Capacity property (UnityUpgradable) -> Capacity", true)]
  72. public int capacity;
  73. /// <summary>
  74. /// The number of elements.
  75. /// </summary>
  76. /// <value>The number of elements.</value>
  77. public int Length
  78. {
  79. get
  80. {
  81. return CollectionHelper.AssumePositive(m_length);
  82. }
  83. set
  84. {
  85. if (value > Capacity)
  86. {
  87. Resize(value);
  88. }
  89. else
  90. {
  91. m_length = value;
  92. }
  93. }
  94. }
  95. /// <summary>
  96. /// The number of elements that can fit in the internal buffer.
  97. /// </summary>
  98. /// <value>The number of elements that can fit in the internal buffer.</value>
  99. public int Capacity
  100. {
  101. get
  102. {
  103. return CollectionHelper.AssumePositive(m_capacity);
  104. }
  105. set
  106. {
  107. SetCapacity(value);
  108. }
  109. }
  110. /// <summary>
  111. /// The element at an index.
  112. /// </summary>
  113. /// <param name="index">An index.</param>
  114. /// <value>The element at the index.</value>
  115. public T this[int index]
  116. {
  117. get
  118. {
  119. CollectionHelper.CheckIndexInRange(index, Length);
  120. return Ptr[CollectionHelper.AssumePositive(index)];
  121. }
  122. set
  123. {
  124. CollectionHelper.CheckIndexInRange(index, Length);
  125. Ptr[CollectionHelper.AssumePositive(index)] = value;
  126. }
  127. }
  128. /// <summary>
  129. /// Returns a reference to the element at a given index.
  130. /// </summary>
  131. /// <param name="index">The index to access. Must be in the range of [0..Length).</param>
  132. /// <returns>A reference to the element at the index.</returns>
  133. public ref T ElementAt(int index)
  134. {
  135. CollectionHelper.CheckIndexInRange(index, Length);
  136. return ref Ptr[CollectionHelper.AssumePositive(index)];
  137. }
  138. /// <summary>
  139. /// Initializes and returns an instance of UnsafeList.
  140. /// </summary>
  141. /// <param name="ptr">An existing byte array to set as the internal buffer.</param>
  142. /// <param name="length">The length.</param>
  143. public UnsafeList(T* ptr, int length) : this()
  144. {
  145. Ptr = ptr;
  146. this.m_length = length;
  147. m_capacity = 0;
  148. Allocator = AllocatorManager.None;
  149. }
  150. /// <summary>
  151. /// Initializes and returns an instance of UnsafeList.
  152. /// </summary>
  153. /// <param name="initialCapacity">The initial capacity of the list.</param>
  154. /// <param name="allocator">The allocator to use.</param>
  155. /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
  156. public UnsafeList(int initialCapacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory) : this()
  157. {
  158. Ptr = null;
  159. m_length = 0;
  160. m_capacity = 0;
  161. Allocator = allocator;
  162. if (initialCapacity != 0)
  163. {
  164. SetCapacity(initialCapacity);
  165. }
  166. if (options == NativeArrayOptions.ClearMemory && Ptr != null)
  167. {
  168. var sizeOf = sizeof(T);
  169. UnsafeUtility.MemClear(Ptr, Capacity * sizeOf);
  170. }
  171. }
  172. [BurstCompatible(GenericTypeArguments = new [] { typeof(AllocatorManager.AllocatorHandle) })]
  173. internal void Initialize<U>(int initialCapacity, ref U allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory) where U : unmanaged, AllocatorManager.IAllocator
  174. {
  175. Ptr = null;
  176. m_length = 0;
  177. m_capacity = 0;
  178. Allocator = AllocatorManager.None;
  179. Initialize(initialCapacity, ref allocator, options);
  180. }
  181. [BurstCompatible(GenericTypeArguments = new [] { typeof(AllocatorManager.AllocatorHandle) })]
  182. internal static UnsafeList<T> New<U>(int initialCapacity, ref U allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory) where U : unmanaged, AllocatorManager.IAllocator
  183. {
  184. UnsafeList<T> instance = default;
  185. instance.Initialize(initialCapacity, ref allocator, options);
  186. return instance;
  187. }
  188. [BurstCompatible(GenericTypeArguments = new[] { typeof(AllocatorManager.AllocatorHandle) })]
  189. internal static UnsafeList<T>* Create<U>(int initialCapacity, ref U allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory) where U : unmanaged, AllocatorManager.IAllocator
  190. {
  191. UnsafeList<T>* listData = allocator.Allocate(default(UnsafeList<T>), 1);
  192. UnsafeUtility.MemClear(listData, sizeof(UnsafeList<T>));
  193. listData->Allocator = allocator.Handle;
  194. if (initialCapacity != 0)
  195. {
  196. listData->SetCapacity(ref allocator, initialCapacity);
  197. }
  198. if (options == NativeArrayOptions.ClearMemory
  199. && listData->Ptr != null)
  200. {
  201. var sizeOf = sizeof(T);
  202. UnsafeUtility.MemClear(listData->Ptr, listData->Capacity * sizeOf);
  203. }
  204. return listData;
  205. }
  206. [BurstCompatible(GenericTypeArguments = new[] { typeof(AllocatorManager.AllocatorHandle) })]
  207. internal static void Destroy<U>(UnsafeList<T>* listData, ref U allocator) where U : unmanaged, AllocatorManager.IAllocator
  208. {
  209. CheckNull(listData);
  210. listData->Dispose(ref allocator);
  211. allocator.Free(listData, sizeof(UnsafeList<T>), UnsafeUtility.AlignOf<UnsafeList<T>>(), 1);
  212. }
  213. /// <summary>
  214. /// Returns a new list.
  215. /// </summary>
  216. /// <param name="initialCapacity">The initial capacity of the list.</param>
  217. /// <param name="allocator">The allocator to use.</param>
  218. /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
  219. /// <returns>A pointer to the new list.</returns>
  220. public static UnsafeList<T>* Create(int initialCapacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory)
  221. {
  222. UnsafeList<T>* listData = AllocatorManager.Allocate<UnsafeList<T>>(allocator);
  223. *listData = new UnsafeList<T>(initialCapacity, allocator, options);
  224. return listData;
  225. }
  226. /// <summary>
  227. /// Destroys the list.
  228. /// </summary>
  229. /// <param name="listData">The list to destroy.</param>
  230. public static void Destroy(UnsafeList<T>* listData)
  231. {
  232. CheckNull(listData);
  233. var allocator = listData->Allocator;
  234. listData->Dispose();
  235. AllocatorManager.Free(allocator, listData);
  236. }
  237. /// <summary>
  238. /// Whether the list is empty.
  239. /// </summary>
  240. /// <value>True if the list is empty or the list has not been constructed.</value>
  241. public bool IsEmpty => !IsCreated || m_length == 0;
  242. /// <summary>
  243. /// Whether this list has been allocated (and not yet deallocated).
  244. /// </summary>
  245. /// <value>True if this list has been allocated (and not yet deallocated).</value>
  246. public bool IsCreated => Ptr != null;
  247. [BurstCompatible(GenericTypeArguments = new[] { typeof(AllocatorManager.AllocatorHandle) })]
  248. internal void Dispose<U>(ref U allocator) where U : unmanaged, AllocatorManager.IAllocator
  249. {
  250. allocator.Free(Ptr, m_length);
  251. Ptr = null;
  252. m_length = 0;
  253. m_capacity = 0;
  254. }
  255. /// <summary>
  256. /// Releases all resources (memory).
  257. /// </summary>
  258. public void Dispose()
  259. {
  260. if (CollectionHelper.ShouldDeallocate(Allocator))
  261. {
  262. AllocatorManager.Free(Allocator, Ptr);
  263. Allocator = AllocatorManager.Invalid;
  264. }
  265. Ptr = null;
  266. m_length = 0;
  267. m_capacity = 0;
  268. }
  269. /// <summary>
  270. /// Creates and schedules a job that frees the memory of this list.
  271. /// </summary>
  272. /// <param name="inputDeps">The dependency for the new job.</param>
  273. /// <returns>The handle of the new job. The job depends upon `inputDeps` and frees the memory of this list.</returns>
  274. [NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
  275. public JobHandle Dispose(JobHandle inputDeps)
  276. {
  277. if (CollectionHelper.ShouldDeallocate(Allocator))
  278. {
  279. var jobHandle = new UnsafeDisposeJob { Ptr = Ptr, Allocator = Allocator }.Schedule(inputDeps);
  280. Ptr = null;
  281. Allocator = AllocatorManager.Invalid;
  282. return jobHandle;
  283. }
  284. Ptr = null;
  285. return inputDeps;
  286. }
  287. /// <summary>
  288. /// Sets the length to 0.
  289. /// </summary>
  290. /// <remarks>Does not change the capacity.</remarks>
  291. public void Clear()
  292. {
  293. m_length = 0;
  294. }
  295. /// <summary>
  296. /// Sets the length, expanding the capacity if necessary.
  297. /// </summary>
  298. /// <param name="length">The new length.</param>
  299. /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
  300. public void Resize(int length, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory)
  301. {
  302. var oldLength = m_length;
  303. if (length > Capacity)
  304. {
  305. SetCapacity(length);
  306. }
  307. m_length = length;
  308. if (options == NativeArrayOptions.ClearMemory && oldLength < length)
  309. {
  310. var num = length - oldLength;
  311. byte* ptr = (byte*)Ptr;
  312. var sizeOf = sizeof(T);
  313. UnsafeUtility.MemClear(ptr + oldLength * sizeOf, num * sizeOf);
  314. }
  315. }
  316. void Realloc<U>(ref U allocator, int newCapacity) where U : unmanaged, AllocatorManager.IAllocator
  317. {
  318. CollectionHelper.CheckAllocator(Allocator);
  319. T* newPointer = null;
  320. var alignOf = UnsafeUtility.AlignOf<T>();
  321. var sizeOf = sizeof(T);
  322. if (newCapacity > 0)
  323. {
  324. newPointer = (T*)allocator.Allocate(sizeOf, alignOf, newCapacity);
  325. if (m_capacity > 0)
  326. {
  327. var itemsToCopy = math.min(newCapacity, Capacity);
  328. var bytesToCopy = itemsToCopy * sizeOf;
  329. UnsafeUtility.MemCpy(newPointer, Ptr, bytesToCopy);
  330. }
  331. }
  332. allocator.Free(Ptr, Capacity);
  333. Ptr = newPointer;
  334. m_capacity = newCapacity;
  335. m_length = math.min(m_length, newCapacity);
  336. }
  337. void Realloc(int capacity)
  338. {
  339. Realloc(ref Allocator, capacity);
  340. }
  341. void SetCapacity<U>(ref U allocator, int capacity) where U : unmanaged, AllocatorManager.IAllocator
  342. {
  343. CollectionHelper.CheckCapacityInRange(capacity, Length);
  344. var sizeOf = sizeof(T);
  345. var newCapacity = math.max(capacity, 64 / sizeOf);
  346. newCapacity = math.ceilpow2(newCapacity);
  347. if (newCapacity == Capacity)
  348. {
  349. return;
  350. }
  351. Realloc(ref allocator, newCapacity);
  352. }
  353. /// <summary>
  354. /// Sets the capacity.
  355. /// </summary>
  356. /// <param name="capacity">The new capacity.</param>
  357. public void SetCapacity(int capacity)
  358. {
  359. SetCapacity(ref Allocator, capacity);
  360. }
  361. /// <summary>
  362. /// Sets the capacity to match the length.
  363. /// </summary>
  364. public void TrimExcess()
  365. {
  366. if (Capacity != m_length)
  367. {
  368. Realloc(m_length);
  369. }
  370. }
  371. /// <summary>
  372. /// Adds an element to the end of this list.
  373. /// </summary>
  374. /// <remarks>
  375. /// Increments the length by 1. Never increases the capacity.
  376. /// </remarks>
  377. /// <param name="value">The value to add to the end of the list.</param>
  378. /// <exception cref="Exception">Thrown if incrementing the length would exceed the capacity.</exception>
  379. public void AddNoResize(T value)
  380. {
  381. CheckNoResizeHasEnoughCapacity(1);
  382. UnsafeUtility.WriteArrayElement(Ptr, m_length, value);
  383. m_length += 1;
  384. }
  385. /// <summary>
  386. /// Copies elements from a buffer to the end of this list.
  387. /// </summary>
  388. /// <remarks>
  389. /// Increments the length by `count`. Never increases the capacity.
  390. /// </remarks>
  391. /// <param name="ptr">The buffer to copy from.</param>
  392. /// <param name="count">The number of elements to copy from the buffer.</param>
  393. /// <exception cref="Exception">Thrown if the increased length would exceed the capacity.</exception>
  394. public void AddRangeNoResize(void* ptr, int count)
  395. {
  396. CheckNoResizeHasEnoughCapacity(count);
  397. var sizeOf = sizeof(T);
  398. void* dst = (byte*)Ptr + m_length * sizeOf;
  399. UnsafeUtility.MemCpy(dst, ptr, count * sizeOf);
  400. m_length += count;
  401. }
  402. /// <summary>
  403. /// Copies the elements of another list to the end of this list.
  404. /// </summary>
  405. /// <param name="list">The other list to copy from.</param>
  406. /// <remarks>
  407. /// Increments the length by the length of the other list. Never increases the capacity.
  408. /// </remarks>
  409. /// <exception cref="Exception">Thrown if the increased length would exceed the capacity.</exception>
  410. [BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
  411. public void AddRangeNoResize(UnsafeList<T> list)
  412. {
  413. AddRangeNoResize(list.Ptr, CollectionHelper.AssumePositive(list.m_length));
  414. }
  415. /// <summary>
  416. /// Adds an element to the end of the list.
  417. /// </summary>
  418. /// <param name="value">The value to add to the end of this list.</param>
  419. /// <remarks>
  420. /// Increments the length by 1. Increases the capacity if necessary.
  421. /// </remarks>
  422. public void Add(in T value)
  423. {
  424. var idx = m_length;
  425. if (m_length + 1 > Capacity)
  426. {
  427. Resize(idx + 1);
  428. }
  429. else
  430. {
  431. m_length += 1;
  432. }
  433. UnsafeUtility.WriteArrayElement(Ptr, idx, value);
  434. }
  435. /// <summary>
  436. /// Copies the elements of a buffer to the end of this list.
  437. /// </summary>
  438. /// <param name="ptr">The buffer to copy from.</param>
  439. /// <param name="count">The number of elements to copy from the buffer.</param>
  440. /// <remarks>
  441. /// Increments the length by `count`. Increases the capacity if necessary.
  442. /// </remarks>
  443. public void AddRange(void* ptr, int count)
  444. {
  445. var idx = m_length;
  446. if (m_length + count > Capacity)
  447. {
  448. Resize(m_length + count);
  449. }
  450. else
  451. {
  452. m_length += count;
  453. }
  454. var sizeOf = sizeof(T);
  455. void* dst = (byte*)Ptr + idx * sizeOf;
  456. UnsafeUtility.MemCpy(dst, ptr, count * sizeOf);
  457. }
  458. /// <summary>
  459. /// Copies the elements of another list to the end of the list.
  460. /// </summary>
  461. /// <param name="list">The list to copy from.</param>
  462. /// <remarks>
  463. /// The length is increased by the length of the other list. Increases the capacity if necessary.
  464. /// </remarks>
  465. [BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
  466. public void AddRange(UnsafeList<T> list)
  467. {
  468. AddRange(list.Ptr, list.Length);
  469. }
  470. /// <summary>
  471. /// Shifts elements toward the end of this list, increasing its length.
  472. /// </summary>
  473. /// <remarks>
  474. /// Right-shifts elements in the list so as to create 'free' slots at the beginning or in the middle.
  475. ///
  476. /// The length is increased by `end - begin`. If necessary, the capacity will be increased accordingly.
  477. ///
  478. /// If `end` equals `begin`, the method does nothing.
  479. ///
  480. /// 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.
  481. ///
  482. /// The indexes `begin` up to `end` are not cleared: they will contain whatever values they held prior.
  483. /// </remarks>
  484. /// <param name="begin">The index of the first element that will be shifted up.</param>
  485. /// <param name="end">The index where the first shifted element will end up.</param>
  486. /// <exception cref="ArgumentException">Thrown if `end &lt; begin`.</exception>
  487. /// <exception cref="ArgumentOutOfRangeException">Thrown if `begin` or `end` are out of bounds.</exception>
  488. public void InsertRangeWithBeginEnd(int begin, int end)
  489. {
  490. CheckBeginEnd(begin, end);
  491. int items = end - begin;
  492. if (items < 1)
  493. {
  494. return;
  495. }
  496. var oldLength = m_length;
  497. if (m_length + items > Capacity)
  498. {
  499. Resize(m_length + items);
  500. }
  501. else
  502. {
  503. m_length += items;
  504. }
  505. var itemsToCopy = oldLength - begin;
  506. if (itemsToCopy < 1)
  507. {
  508. return;
  509. }
  510. var sizeOf = sizeof(T);
  511. var bytesToCopy = itemsToCopy * sizeOf;
  512. unsafe
  513. {
  514. byte* ptr = (byte*)Ptr;
  515. byte* dest = ptr + end * sizeOf;
  516. byte* src = ptr + begin * sizeOf;
  517. UnsafeUtility.MemMove(dest, src, bytesToCopy);
  518. }
  519. }
  520. /// <summary>
  521. /// Copies the last element of this list to the specified index. Decrements the length by 1.
  522. /// </summary>
  523. /// <remarks>Useful as a cheap way to remove an element from this list when you don't care about preserving order.</remarks>
  524. /// <param name="index">The index to overwrite with the last element.</param>
  525. /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds.</exception>
  526. public void RemoveAtSwapBack(int index)
  527. {
  528. RemoveRangeSwapBack(index, 1);
  529. }
  530. /// <summary>
  531. /// Copies the last *N* elements of this list to a range in this list. Decrements the length by *N*.
  532. /// </summary>
  533. /// <remarks>
  534. /// Copies the last `count` elements to the indexes `index` up to `index + count`.
  535. ///
  536. /// Useful as a cheap way to remove elements from a list when you don't care about preserving order.
  537. /// </remarks>
  538. /// <param name="index">The index of the first element to overwrite.</param>
  539. /// <param name="count">The number of elements to copy and remove.</param>
  540. /// <exception cref="ArgumentOutOfRangeException">Thrown if `index` is out of bounds, `count` is negative,
  541. /// or `index + count` exceeds the length.</exception>
  542. public void RemoveRangeSwapBack(int index, int count)
  543. {
  544. CheckIndexCount(index, count);
  545. if (count > 0)
  546. {
  547. int copyFrom = math.max(m_length - count, index + count);
  548. var sizeOf = sizeof(T);
  549. void* dst = (byte*)Ptr + index * sizeOf;
  550. void* src = (byte*)Ptr + copyFrom * sizeOf;
  551. UnsafeUtility.MemCpy(dst, src, (m_length - copyFrom) * sizeOf);
  552. m_length -= count;
  553. }
  554. }
  555. /// <summary>
  556. /// Copies the last *N* elements of this list to a range in this list. Decrements the length by *N*.
  557. /// </summary>
  558. /// <remarks>
  559. /// Copies the last `end - begin` elements to the indexes `begin` up to `end`.
  560. ///
  561. /// Useful as a cheap way to remove elements from a list when you don't care about preserving order.
  562. ///
  563. /// Does nothing if `end - begin` is less than 1.
  564. /// </remarks>
  565. /// <param name="begin">The index of the first element to overwrite.</param>
  566. /// <param name="end">The index one greater than the last element to overwrite.</param>
  567. /// <exception cref="ArgumentOutOfRangeException">Thrown if `begin` or `end` are out of bounds.</exception>
  568. [Obsolete("RemoveRangeSwapBackWithBeginEnd(begin, end) is deprecated, use RemoveRangeSwapBack(index, count) instead. (RemovedAfter 2021-06-02)", false)]
  569. public void RemoveRangeSwapBackWithBeginEnd(int begin, int end)
  570. {
  571. CheckBeginEnd(begin, end);
  572. int itemsToRemove = end - begin;
  573. if (itemsToRemove > 0)
  574. {
  575. int copyFrom = math.max(m_length - itemsToRemove, end);
  576. var sizeOf = sizeof(T);
  577. void* dst = (byte*)Ptr + begin * sizeOf;
  578. void* src = (byte*)Ptr + copyFrom * sizeOf;
  579. UnsafeUtility.MemCpy(dst, src, (m_length - copyFrom) * sizeOf);
  580. m_length -= itemsToRemove;
  581. }
  582. }
  583. /// <summary>
  584. /// Removes the element at an index, shifting everything above it down by one. Decrements the length by 1.
  585. /// </summary>
  586. /// <param name="index">The index of the element to remove.</param>
  587. /// <remarks>
  588. /// If you don't care about preserving the order of the elements, <see cref="RemoveAtSwapBack(int)"/> is a more efficient way to remove elements.
  589. /// </remarks>
  590. /// <exception cref="ArgumentOutOfRangeException">Thrown if `index` is out of bounds.</exception>
  591. public void RemoveAt(int index)
  592. {
  593. RemoveRange(index, 1);
  594. }
  595. /// <summary>
  596. /// Removes *N* elements in a range, shifting everything above the range down by *N*. Decrements the length by *N*.
  597. /// </summary>
  598. /// <param name="index">The index of the first element to remove.</param>
  599. /// <param name="count">The number of elements to remove.</param>
  600. /// <remarks>
  601. /// If you don't care about preserving the order of the elements, <see cref="RemoveRangeSwapBackWithBeginEnd"/>
  602. /// is a more efficient way to remove elements.
  603. /// </remarks>
  604. /// <exception cref="ArgumentOutOfRangeException">Thrown if `index` is out of bounds, `count` is negative,
  605. /// or `index + count` exceeds the length.</exception>
  606. public void RemoveRange(int index, int count)
  607. {
  608. CheckIndexCount(index, count);
  609. if (count > 0)
  610. {
  611. int copyFrom = math.min(index + count, m_length);
  612. var sizeOf = sizeof(T);
  613. void* dst = (byte*)Ptr + index * sizeOf;
  614. void* src = (byte*)Ptr + copyFrom * sizeOf;
  615. UnsafeUtility.MemCpy(dst, src, (m_length - copyFrom) * sizeOf);
  616. m_length -= count;
  617. }
  618. }
  619. /// <summary>
  620. /// Removes *N* elements in a range, shifting everything above it down by *N*. Decrements the length by *N*.
  621. /// </summary>
  622. /// <param name="begin">The index of the first element to remove.</param>
  623. /// <param name="end">The index one greater than the last element to remove.</param>
  624. /// <remarks>
  625. /// If you don't care about preserving the order of the elements, <see cref="RemoveRangeSwapBackWithBeginEnd"/> is a more efficient way to remove elements.
  626. /// </remarks>
  627. /// <exception cref="ArgumentException">Thrown if `end &lt; begin`.</exception>
  628. /// <exception cref="ArgumentOutOfRangeException">Thrown if `begin` or `end` are out of bounds.</exception>
  629. [Obsolete("RemoveRangeWithBeginEnd(begin, end) is deprecated, use RemoveRange(index, count) instead. (RemovedAfter 2021-06-02)", false)]
  630. public void RemoveRangeWithBeginEnd(int begin, int end)
  631. {
  632. CheckBeginEnd(begin, end);
  633. int itemsToRemove = end - begin;
  634. if (itemsToRemove > 0)
  635. {
  636. int copyFrom = math.min(begin + itemsToRemove, m_length);
  637. var sizeOf = sizeof(T);
  638. void* dst = (byte*)Ptr + begin * sizeOf;
  639. void* src = (byte*)Ptr + copyFrom * sizeOf;
  640. UnsafeUtility.MemCpy(dst, src, (m_length - copyFrom) * sizeOf);
  641. m_length -= itemsToRemove;
  642. }
  643. }
  644. /// <summary>
  645. /// Returns a parallel reader of this list.
  646. /// </summary>
  647. /// <returns>A parallel reader of this list.</returns>
  648. public ParallelReader AsParallelReader()
  649. {
  650. return new ParallelReader(Ptr, Length);
  651. }
  652. /// <summary>
  653. /// A parallel reader for an UnsafeList&lt;T&gt;.
  654. /// </summary>
  655. /// <remarks>
  656. /// Use <see cref="AsParallelReader"/> to create a parallel reader for a list.
  657. /// </remarks>
  658. [BurstCompatible(GenericTypeArguments = new [] { typeof(int) })]
  659. public unsafe struct ParallelReader
  660. {
  661. /// <summary>
  662. /// The internal buffer of the list.
  663. /// </summary>
  664. [NativeDisableUnsafePtrRestriction]
  665. public readonly T* Ptr;
  666. /// <summary>
  667. /// The number of elements.
  668. /// </summary>
  669. public readonly int Length;
  670. internal ParallelReader(T* ptr, int length)
  671. {
  672. Ptr = ptr;
  673. Length = length;
  674. }
  675. }
  676. /// <summary>
  677. /// Returns a parallel writer of this list.
  678. /// </summary>
  679. /// <returns>A parallel writer of this list.</returns>
  680. public ParallelWriter AsParallelWriter()
  681. {
  682. return new ParallelWriter((UnsafeList<T>*)UnsafeUtility.AddressOf(ref this));
  683. }
  684. /// <summary>
  685. /// A parallel writer for an UnsafeList&lt;T&gt;.
  686. /// </summary>
  687. /// <remarks>
  688. /// Use <see cref="AsParallelWriter"/> to create a parallel writer for a list.
  689. /// </remarks>
  690. [BurstCompatible(GenericTypeArguments = new [] { typeof(int) })]
  691. public unsafe struct ParallelWriter
  692. {
  693. /// <summary>
  694. /// The data of the list.
  695. /// </summary>
  696. public readonly void* Ptr => ListData->Ptr;
  697. /// <summary>
  698. /// The UnsafeList to write to.
  699. /// </summary>
  700. [NativeDisableUnsafePtrRestriction]
  701. public UnsafeList<T>* ListData;
  702. internal unsafe ParallelWriter(UnsafeList<T>* listData)
  703. {
  704. ListData = listData;
  705. }
  706. /// <summary>
  707. /// Adds an element to the end of the list.
  708. /// </summary>
  709. /// <param name="value">The value to add to the end of the list.</param>
  710. /// <remarks>
  711. /// Increments the length by 1. Never increases the capacity.
  712. /// </remarks>
  713. /// <exception cref="Exception">Thrown if incrementing the length would exceed the capacity.</exception>
  714. [BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
  715. public void AddNoResize(T value)
  716. {
  717. var idx = Interlocked.Increment(ref ListData->m_length) - 1;
  718. ListData->CheckNoResizeHasEnoughCapacity(idx, 1);
  719. UnsafeUtility.WriteArrayElement(ListData->Ptr, idx, value);
  720. }
  721. /// <summary>
  722. /// Copies elements from a buffer to the end of the list.
  723. /// </summary>
  724. /// <param name="ptr">The buffer to copy from.</param>
  725. /// <param name="count">The number of elements to copy from the buffer.</param>
  726. /// <remarks>
  727. /// Increments the length by `count`. Never increases the capacity.
  728. /// </remarks>
  729. /// <exception cref="Exception">Thrown if the increased length would exceed the capacity.</exception>
  730. [BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
  731. public void AddRangeNoResize(void* ptr, int count)
  732. {
  733. var idx = Interlocked.Add(ref ListData->m_length, count) - count;
  734. ListData->CheckNoResizeHasEnoughCapacity(idx, count);
  735. void* dst = (byte*)ListData->Ptr + idx * sizeof(T);
  736. UnsafeUtility.MemCpy(dst, ptr, count * sizeof(T));
  737. }
  738. /// <summary>
  739. /// Copies the elements of another list to the end of this list.
  740. /// </summary>
  741. /// <param name="list">The other list to copy from.</param>
  742. /// <remarks>
  743. /// Increments the length by the length of the other list. Never increases the capacity.
  744. /// </remarks>
  745. /// <exception cref="Exception">Thrown if the increased length would exceed the capacity.</exception>
  746. [BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
  747. public void AddRangeNoResize(UnsafeList<T> list)
  748. {
  749. AddRangeNoResize(list.Ptr, list.Length);
  750. }
  751. }
  752. /// <summary>
  753. /// Overwrites the elements of this list with the elements of an equal-length array.
  754. /// </summary>
  755. /// <param name="array">An array to copy into this list.</param>
  756. public void CopyFrom(UnsafeList<T> array)
  757. {
  758. Resize(array.Length);
  759. UnsafeUtility.MemCpy(Ptr, array.Ptr, UnsafeUtility.SizeOf<T>() * Length);
  760. }
  761. /// <summary>
  762. /// Returns an enumerator over the elements of the list.
  763. /// </summary>
  764. /// <returns>An enumerator over the elements of the list.</returns>
  765. public Enumerator GetEnumerator()
  766. {
  767. return new Enumerator { m_Ptr = Ptr, m_Length = Length, m_Index = -1 };
  768. }
  769. /// <summary>
  770. /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
  771. /// </summary>
  772. /// <returns>Throws NotImplementedException.</returns>
  773. /// <exception cref="NotImplementedException">Method is not implemented.</exception>
  774. IEnumerator IEnumerable.GetEnumerator()
  775. {
  776. throw new NotImplementedException();
  777. }
  778. /// <summary>
  779. /// This method is not implemented. Use <see cref="GetEnumerator"/> instead.
  780. /// </summary>
  781. /// <returns>Throws NotImplementedException.</returns>
  782. /// <exception cref="NotImplementedException">Method is not implemented.</exception>
  783. IEnumerator<T> IEnumerable<T>.GetEnumerator()
  784. {
  785. throw new NotImplementedException();
  786. }
  787. /// <summary>
  788. /// An enumerator over the elements of a list.
  789. /// </summary>
  790. /// <remarks>
  791. /// In an enumerator's initial state, <see cref="Current"/> is invalid.
  792. /// The first <see cref="MoveNext"/> call advances the enumerator to the first element of the list.
  793. /// </remarks>
  794. public struct Enumerator : IEnumerator<T>
  795. {
  796. internal T* m_Ptr;
  797. internal int m_Length;
  798. internal int m_Index;
  799. /// <summary>
  800. /// Does nothing.
  801. /// </summary>
  802. public void Dispose() { }
  803. /// <summary>
  804. /// Advances the enumerator to the next element of the list.
  805. /// </summary>
  806. /// <remarks>
  807. /// The first `MoveNext` call advances the enumerator to the first element of the list. Before this call, `Current` is not valid to read.
  808. /// </remarks>
  809. /// <returns>True if `Current` is valid to read after the call.</returns>
  810. public bool MoveNext() => ++m_Index < m_Length;
  811. /// <summary>
  812. /// Resets the enumerator to its initial state.
  813. /// </summary>
  814. public void Reset() => m_Index = -1;
  815. /// <summary>
  816. /// The current element.
  817. /// </summary>
  818. /// <value>The current element.</value>
  819. public T Current => m_Ptr[m_Index];
  820. object IEnumerator.Current => Current;
  821. }
  822. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  823. internal static void CheckNull(void* listData)
  824. {
  825. if (listData == null)
  826. {
  827. throw new Exception("UnsafeList has yet to be created or has been destroyed!");
  828. }
  829. }
  830. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  831. void CheckIndexCount(int index, int count)
  832. {
  833. if (count < 0)
  834. {
  835. throw new ArgumentOutOfRangeException($"Value for cound {count} must be positive.");
  836. }
  837. if (index < 0)
  838. {
  839. throw new ArgumentOutOfRangeException($"Value for index {index} must be positive.");
  840. }
  841. if (index > Length)
  842. {
  843. throw new ArgumentOutOfRangeException($"Value for index {index} is out of bounds.");
  844. }
  845. if (index+count > Length)
  846. {
  847. throw new ArgumentOutOfRangeException($"Value for count {count} is out of bounds.");
  848. }
  849. }
  850. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  851. void CheckBeginEnd(int begin, int end)
  852. {
  853. if (begin > end)
  854. {
  855. throw new ArgumentException($"Value for begin {begin} index must less or equal to end {end}.");
  856. }
  857. if (begin < 0)
  858. {
  859. throw new ArgumentOutOfRangeException($"Value for begin {begin} must be positive.");
  860. }
  861. if (begin > Length)
  862. {
  863. throw new ArgumentOutOfRangeException($"Value for begin {begin} is out of bounds.");
  864. }
  865. if (end > Length)
  866. {
  867. throw new ArgumentOutOfRangeException($"Value for end {end} is out of bounds.");
  868. }
  869. }
  870. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  871. void CheckNoResizeHasEnoughCapacity(int length)
  872. {
  873. CheckNoResizeHasEnoughCapacity(length, Length);
  874. }
  875. [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
  876. void CheckNoResizeHasEnoughCapacity(int length, int index)
  877. {
  878. if (Capacity < index + length)
  879. {
  880. throw new Exception($"AddNoResize assumes that list capacity is sufficient (Capacity {Capacity}, Length {Length}), requested length {length}!");
  881. }
  882. }
  883. }
  884. /// <summary>
  885. /// Provides extension methods for UnsafeList.
  886. /// </summary>
  887. [BurstCompatible]
  888. public unsafe static class UnsafeListExtensions
  889. {
  890. /// <summary>
  891. /// Finds the index of the first occurrence of a particular value in this list.
  892. /// </summary>
  893. /// <typeparam name="T">The type of elements in this list.</typeparam>
  894. /// <typeparam name="U">The type of value to locate.</typeparam>
  895. /// <param name="list">This list.</param>
  896. /// <param name="value">A value to locate.</param>
  897. /// <returns>The zero-based index of the first occurrence of the value if it is found. Returns -1 if no occurrence is found.</returns>
  898. [BurstCompatible(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
  899. public static int IndexOf<T, U>(this UnsafeList<T> list, U value) where T : unmanaged, IEquatable<U>
  900. {
  901. return NativeArrayExtensions.IndexOf<T, U>(list.Ptr, list.Length, value);
  902. }
  903. /// <summary>
  904. /// Returns true if a particular value is present in this list.
  905. /// </summary>
  906. /// <typeparam name="T">The type of elements in the list.</typeparam>
  907. /// <typeparam name="U">The type of value to locate.</typeparam>
  908. /// <param name="list">This list.</param>
  909. /// <param name="value">The value to locate.</param>
  910. /// <returns>True if the value is present in this list.</returns>
  911. [BurstCompatible(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
  912. public static bool Contains<T, U>(this UnsafeList<T> list, U value) where T : unmanaged, IEquatable<U>
  913. {
  914. return list.IndexOf(value) != -1;
  915. }
  916. /// <summary>
  917. /// Finds the index of the first occurrence of a particular value in the list.
  918. /// </summary>
  919. /// <typeparam name="T">The type of elements in the list.</typeparam>
  920. /// <typeparam name="U">The type of value to locate.</typeparam>
  921. /// <param name="list">This reader of the list.</param>
  922. /// <param name="value">A value to locate.</param>
  923. /// <returns>The zero-based index of the first occurrence of the value if it is found. Returns -1 if no occurrence is found.</returns>
  924. [BurstCompatible(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
  925. public static int IndexOf<T, U>(this UnsafeList<T>.ParallelReader list, U value) where T : unmanaged, IEquatable<U>
  926. {
  927. return NativeArrayExtensions.IndexOf<T, U>(list.Ptr, list.Length, value);
  928. }
  929. /// <summary>
  930. /// Returns true if a particular value is present in the list.
  931. /// </summary>
  932. /// <typeparam name="T">The type of elements in the list.</typeparam>
  933. /// <typeparam name="U">The type of value to locate.</typeparam>
  934. /// <param name="list">This reader of the list.</param>
  935. /// <param name="value">The value to locate.</param>
  936. /// <returns>True if the value is present in the list.</returns>
  937. [BurstCompatible(GenericTypeArguments = new [] { typeof(int), typeof(int) })]
  938. public static bool Contains<T, U>(this UnsafeList<T>.ParallelReader list, U value) where T : unmanaged, IEquatable<U>
  939. {
  940. return list.IndexOf(value) != -1;
  941. }
  942. /// <summary>
  943. /// Returns true if this array and another have equal length and content.
  944. /// </summary>
  945. /// <typeparam name="T">The type of the source array's elements.</typeparam>
  946. /// <param name="array">The array to compare for equality.</param>
  947. /// <param name="other">The other array to compare for equality.</param>
  948. /// <returns>True if the arrays have equal length and content.</returns>
  949. [BurstCompatible(GenericTypeArguments = new [] { typeof(int) })]
  950. public static bool ArraysEqual<T>(this UnsafeList<T> array, UnsafeList<T> other) where T : unmanaged, IEquatable<T>
  951. {
  952. if (array.Length != other.Length)
  953. return false;
  954. for (int i = 0; i != array.Length; i++)
  955. {
  956. if (!array[i].Equals(other[i]))
  957. return false;
  958. }
  959. return true;
  960. }
  961. }
  962. internal sealed class UnsafeListTDebugView<T>
  963. where T : unmanaged
  964. {
  965. UnsafeList<T> Data;
  966. public UnsafeListTDebugView(UnsafeList<T> data)
  967. {
  968. Data = data;
  969. }
  970. public unsafe T[] Items
  971. {
  972. get
  973. {
  974. T[] result = new T[Data.Length];
  975. for (var i = 0; i < result.Length; ++i)
  976. {
  977. result[i] = Data.Ptr[i];
  978. }
  979. return result;
  980. }
  981. }
  982. }
  983. /// <summary>
  984. /// An unmanaged, resizable list of pointers.
  985. /// </summary>
  986. /// <typeparam name="T">The type of pointer element.</typeparam>
  987. [DebuggerDisplay("Length = {Length}, Capacity = {Capacity}, IsCreated = {IsCreated}, IsEmpty = {IsEmpty}")]
  988. [DebuggerTypeProxy(typeof(UnsafePtrListTDebugView<>))]
  989. [StructLayout(LayoutKind.Sequential)]
  990. [BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
  991. public unsafe struct UnsafePtrList<T>
  992. : INativeDisposable
  993. // IIndexable<T> and INativeList<T> can't be implemented because this[index] and ElementAt return T* instead of T.
  994. , IEnumerable<IntPtr> // Used by collection initializers.
  995. where T : unmanaged
  996. {
  997. /// <summary>
  998. /// The internal buffer of this list.
  999. /// </summary>
  1000. [NativeDisableUnsafePtrRestriction]
  1001. public readonly T** Ptr;
  1002. /// <summary>
  1003. /// The number of elements.
  1004. /// </summary>
  1005. public readonly int m_length;
  1006. /// <summary>
  1007. /// The number of elements that can fit in the internal buffer.
  1008. /// </summary>
  1009. public readonly int m_capacity;
  1010. /// <summary>
  1011. /// The allocator used to create the internal buffer.
  1012. /// </summary>
  1013. public readonly AllocatorManager.AllocatorHandle Allocator;
  1014. [Obsolete("Use Length property (UnityUpgradable) -> Length", true)]
  1015. public int length;
  1016. [Obsolete("Use Capacity property (UnityUpgradable) -> Capacity", true)]
  1017. public int capacity;
  1018. /// <summary>
  1019. /// The number of elements.
  1020. /// </summary>
  1021. /// <value>The number of elements.</value>
  1022. public int Length
  1023. {
  1024. get
  1025. {
  1026. return this.ListData().Length;
  1027. }
  1028. set
  1029. {
  1030. this.ListData().Length = value;
  1031. }
  1032. }
  1033. /// <summary>
  1034. /// The number of elements that can fit in the internal buffer.
  1035. /// </summary>
  1036. /// <value>The number of elements that can fit in the internal buffer.</value>
  1037. public int Capacity
  1038. {
  1039. get
  1040. {
  1041. return this.ListData().Capacity;
  1042. }
  1043. set
  1044. {
  1045. this.ListData().Capacity = value;
  1046. }
  1047. }
  1048. /// <summary>
  1049. /// The element at an index.
  1050. /// </summary>
  1051. /// <param name="index">An index.</param>
  1052. /// <value>The element at the index.</value>
  1053. public T* this[int index]
  1054. {
  1055. get
  1056. {
  1057. CollectionHelper.CheckIndexInRange(index, Length);
  1058. return Ptr[CollectionHelper.AssumePositive(index)];
  1059. }
  1060. set
  1061. {
  1062. CollectionHelper.CheckIndexInRange(index, Length);
  1063. Ptr[CollectionHelper.AssumePositive(index)] = value;
  1064. }
  1065. }
  1066. /// <summary>
  1067. /// Returns a reference to the element at a given index.
  1068. /// </summary>
  1069. /// <param name="index">The index to access. Must be in the range of [0..Length).</param>
  1070. /// <returns>A reference to the element at the index.</returns>
  1071. public ref T* ElementAt(int index)
  1072. {
  1073. CollectionHelper.CheckIndexInRange(index, Length);
  1074. return ref Ptr[CollectionHelper.AssumePositive(index)];
  1075. }
  1076. /// <summary>
  1077. /// Initializes and returns an instance of UnsafePtrList.
  1078. /// </summary>
  1079. /// <param name="ptr">An existing pointer array to set as the internal buffer.</param>
  1080. /// <param name="length">The length.</param>
  1081. public unsafe UnsafePtrList(T** ptr, int length) : this()
  1082. {
  1083. Ptr = ptr;
  1084. this.m_length = length;
  1085. this.m_capacity = length;
  1086. Allocator = AllocatorManager.None;
  1087. }
  1088. /// <summary>
  1089. /// Initializes and returns an instance of UnsafePtrList.
  1090. /// </summary>
  1091. /// <param name="initialCapacity">The initial capacity of the list.</param>
  1092. /// <param name="allocator">The allocator to use.</param>
  1093. /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
  1094. public unsafe UnsafePtrList(int initialCapacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory) : this()
  1095. {
  1096. Ptr = null;
  1097. m_length = 0;
  1098. m_capacity = 0;
  1099. Allocator = AllocatorManager.None;
  1100. this.ListData() = new UnsafeList<IntPtr>(initialCapacity, allocator, options);
  1101. }
  1102. /// <summary>
  1103. /// Returns a new list of pointers.
  1104. /// </summary>
  1105. /// <param name="ptr">An existing pointer array to set as the internal buffer.</param>
  1106. /// <param name="length">The length.</param>
  1107. /// <returns>A pointer to the new list.</returns>
  1108. public static UnsafePtrList<T>* Create(T** ptr, int length)
  1109. {
  1110. UnsafePtrList<T>* listData = AllocatorManager.Allocate<UnsafePtrList<T>>(AllocatorManager.Persistent);
  1111. *listData = new UnsafePtrList<T>(ptr, length);
  1112. return listData;
  1113. }
  1114. /// <summary>
  1115. /// Returns a new list of pointers.
  1116. /// </summary>
  1117. /// <param name="initialCapacity">The initial capacity of the list.</param>
  1118. /// <param name="allocator">The allocator to use.</param>
  1119. /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
  1120. /// <returns>A pointer to the new list.</returns>
  1121. public static UnsafePtrList<T>* Create(int initialCapacity, AllocatorManager.AllocatorHandle allocator, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory)
  1122. {
  1123. UnsafePtrList<T>* listData = AllocatorManager.Allocate<UnsafePtrList<T>>(allocator);
  1124. *listData = new UnsafePtrList<T>(initialCapacity, allocator, options);
  1125. return listData;
  1126. }
  1127. /// <summary>
  1128. /// Destroys the list.
  1129. /// </summary>
  1130. /// <param name="listData">The list to destroy.</param>
  1131. public static void Destroy(UnsafePtrList<T>* listData)
  1132. {
  1133. UnsafeList<IntPtr>.CheckNull(listData);
  1134. var allocator = listData->ListData().Allocator.Value == AllocatorManager.Invalid.Value
  1135. ? AllocatorManager.Persistent
  1136. : listData->ListData().Allocator
  1137. ;
  1138. listData->Dispose();
  1139. AllocatorManager.Free(allocator, listData);
  1140. }
  1141. /// <summary>
  1142. /// Whether the list is empty.
  1143. /// </summary>
  1144. /// <value>True if the list is empty or the list has not been constructed.</value>
  1145. public bool IsEmpty => !IsCreated || Length == 0;
  1146. /// <summary>
  1147. /// Whether this list has been allocated (and not yet deallocated).
  1148. /// </summary>
  1149. /// <value>True if this list has been allocated (and not yet deallocated).</value>
  1150. public bool IsCreated => Ptr != null;
  1151. /// <summary>
  1152. /// Releases all resources (memory).
  1153. /// </summary>
  1154. public void Dispose()
  1155. {
  1156. this.ListData().Dispose();
  1157. }
  1158. /// <summary>
  1159. /// Creates and schedules a job that frees the memory of this list.
  1160. /// </summary>
  1161. /// <param name="inputDeps">The dependency for the new job.</param>
  1162. /// <returns>The handle of the new job. The job depends upon `inputDeps` and frees the memory of this list.</returns>
  1163. [NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
  1164. public JobHandle Dispose(JobHandle inputDeps) => this.ListData().Dispose(inputDeps);
  1165. /// <summary>
  1166. /// Sets the length to 0.
  1167. /// </summary>
  1168. /// <remarks>Does not change the capacity.</remarks>
  1169. public void Clear() => this.ListData().Clear();
  1170. /// <summary>
  1171. /// Sets the length, expanding the capacity if necessary.
  1172. /// </summary>
  1173. /// <param name="length">The new length.</param>
  1174. /// <param name="options">Whether newly allocated bytes should be zeroed out.</param>
  1175. public void Resize(int length, NativeArrayOptions options = NativeArrayOptions.UninitializedMemory) => this.ListData().Resize(length, options);
  1176. /// <summary>
  1177. /// Sets the capacity.
  1178. /// </summary>
  1179. /// <param name="capacity">The new capacity.</param>
  1180. public void SetCapacity(int capacity) => this.ListData().SetCapacity(capacity);
  1181. /// <summary>
  1182. /// Sets the capacity to match the length.
  1183. /// </summary>
  1184. public void TrimExcess() => this.ListData().TrimExcess();
  1185. /// <summary>
  1186. /// Returns the index of the first occurrence of a specific pointer in the list.
  1187. /// </summary>
  1188. /// <param name="ptr">The pointer to search for in the list.</param>
  1189. /// <returns>The index of the first occurrence of the pointer. Returns -1 if it is not found in the list.</returns>
  1190. public int IndexOf(void* ptr)
  1191. {
  1192. for (int i = 0; i < Length; ++i)
  1193. {
  1194. if (Ptr[i] == ptr) return i;
  1195. }
  1196. return -1;
  1197. }
  1198. /// <summary>
  1199. /// Returns true if the list contains at least one occurrence of a specific pointer.
  1200. /// </summary>
  1201. /// <param name="ptr">The pointer to search for in the list.</param>
  1202. /// <returns>True if the list contains at least one occurrence of the pointer.</returns>
  1203. public bool Contains(void* ptr)
  1204. {
  1205. return IndexOf(ptr) != -1;
  1206. }
  1207. /// <summary>
  1208. /// Adds a pointer to the end of this list.
  1209. /// </summary>
  1210. /// <remarks>
  1211. /// Increments the length by 1. Never increases the capacity.
  1212. /// </remarks>
  1213. /// <param name="value">The pointer to add to the end of the list.</param>
  1214. /// <exception cref="Exception">Thrown if incrementing the length would exceed the capacity.</exception>
  1215. public void AddNoResize(void* value)
  1216. {
  1217. this.ListData().AddNoResize((IntPtr)value);
  1218. }
  1219. /// <summary>
  1220. /// Copies pointers from a buffer to the end of this list.
  1221. /// </summary>
  1222. /// <remarks>
  1223. /// Increments the length by `count`. Never increases the capacity.
  1224. /// </remarks>
  1225. /// <param name="ptr">The buffer to copy from.</param>
  1226. /// <param name="count">The number of pointers to copy from the buffer.</param>
  1227. /// <exception cref="Exception">Thrown if the increased length would exceed the capacity.</exception>
  1228. public void AddRangeNoResize(void** ptr, int count) => this.ListData().AddRangeNoResize(ptr, count);
  1229. /// <summary>
  1230. /// Copies the pointers of another list to the end of this list.
  1231. /// </summary>
  1232. /// <param name="list">The other list to copy from.</param>
  1233. /// <remarks>
  1234. /// Increments the length by the length of the other list. Never increases the capacity.
  1235. /// </remarks>
  1236. /// <exception cref="Exception">Thrown if the increased length would exceed the capacity.</exception>
  1237. public void AddRangeNoResize(UnsafePtrList<T> list) => this.ListData().AddRangeNoResize(list.Ptr, list.Length);
  1238. /// <summary>
  1239. /// Adds a pointer to the end of the list.
  1240. /// </summary>
  1241. /// <param name="value">The pointer to add to the end of this list.</param>
  1242. /// <remarks>
  1243. /// Increments the length by 1. Increases the capacity if necessary.
  1244. /// </remarks>
  1245. public void Add(in IntPtr value)
  1246. {
  1247. this.ListData().Add(value);
  1248. }
  1249. /// <summary>
  1250. /// Adds a pointer to the end of the list.
  1251. /// </summary>
  1252. /// <param name="value">The pointer to add to the end of this list.</param>
  1253. /// <remarks>
  1254. /// Increments the length by 1. Increases the capacity if necessary.
  1255. /// </remarks>
  1256. public void Add(void* value)
  1257. {
  1258. this.ListData().Add((IntPtr)value);
  1259. }
  1260. /// <summary>
  1261. /// Adds elements from a buffer to this list.
  1262. /// </summary>
  1263. /// <param name="ptr">A pointer to the buffer.</param>
  1264. /// <param name="length">The number of elements to add to the list.</param>
  1265. public void AddRange(void* ptr, int length) => this.ListData().AddRange(ptr, length);
  1266. /// <summary>
  1267. /// Copies the elements of another list to the end of this list.
  1268. /// </summary>
  1269. /// <param name="list">The other list to copy from.</param>
  1270. /// <remarks>
  1271. /// Increments the length by the length of the other list. Increases the capacity if necessary.
  1272. /// </remarks>
  1273. public void AddRange(UnsafePtrList<T> list) => this.ListData().AddRange(list.ListData());
  1274. /// <summary>
  1275. /// Shifts pointers toward the end of this list, increasing its length.
  1276. /// </summary>
  1277. /// <remarks>
  1278. /// Right-shifts pointers in the list so as to create 'free' slots at the beginning or in the middle.
  1279. ///
  1280. /// The length is increased by `end - begin`. If necessary, the capacity will be increased accordingly.
  1281. ///
  1282. /// If `end` equals `begin`, the method does nothing.
  1283. ///
  1284. /// 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.
  1285. ///
  1286. /// The indexes `begin` up to `end` are not cleared: they will contain whatever pointers they held prior.
  1287. /// </remarks>
  1288. /// <param name="begin">The index of the first pointer that will be shifted up.</param>
  1289. /// <param name="end">The index where the first shifted pointer will end up.</param>
  1290. /// <exception cref="ArgumentException">Thrown if `end &lt; begin`.</exception>
  1291. /// <exception cref="ArgumentOutOfRangeException">Thrown if `begin` or `end` are out of bounds.</exception>
  1292. public void InsertRangeWithBeginEnd(int begin, int end) => this.ListData().InsertRangeWithBeginEnd(begin, end);
  1293. /// <summary>
  1294. /// Copies the last pointer of this list to the specified index. Decrements the length by 1.
  1295. /// </summary>
  1296. /// <remarks>Useful as a cheap way to remove a pointer from this list when you don't care about preserving order.</remarks>
  1297. /// <param name="index">The index to overwrite with the last pointer.</param>
  1298. /// <exception cref="IndexOutOfRangeException">Thrown if `index` is out of bounds.</exception>
  1299. public void RemoveAtSwapBack(int index) => this.ListData().RemoveAtSwapBack(index);
  1300. /// <summary>
  1301. /// Copies the last *N* pointer of this list to a range in this list. Decrements the length by *N*.
  1302. /// </summary>
  1303. /// <remarks>
  1304. /// Copies the last `count` pointers to the indexes `index` up to `index + count`.
  1305. ///
  1306. /// Useful as a cheap way to remove pointers from a list when you don't care about preserving order.
  1307. /// </remarks>
  1308. /// <param name="index">The index of the first pointer to overwrite.</param>
  1309. /// <param name="count">The number of pointers to copy and remove.</param>
  1310. /// <exception cref="ArgumentOutOfRangeException">Thrown if `index` is out of bounds, `count` is negative,
  1311. /// or `index + count` exceeds the length.</exception>
  1312. public void RemoveRangeSwapBack(int index, int count) => this.ListData().RemoveRangeSwapBack(index, count);
  1313. /// <summary>
  1314. /// Copies the last *N* pointers of this list to a range in this list. Decrements the length by *N*.
  1315. /// </summary>
  1316. /// <remarks>
  1317. /// Copies the last `end - begin` pointers to the indexes `begin` up to `end`.
  1318. ///
  1319. /// Useful as a cheap way to remove pointers from a list when you don't care about preserving order.
  1320. ///
  1321. /// Does nothing if `end - begin` is less than 1.
  1322. /// </remarks>
  1323. /// <param name="begin">The index of the first pointers to overwrite.</param>
  1324. /// <param name="end">The index one greater than the last pointers to overwrite.</param>
  1325. /// <exception cref="ArgumentOutOfRangeException">Thrown if `begin` or `end` are out of bounds.</exception>
  1326. [Obsolete("RemoveRangeSwapBackWithBeginEnd(begin, end) is deprecated, use RemoveRangeSwapBack(index, count) instead. (RemovedAfter 2021-06-02)", false)]
  1327. public void RemoveRangeSwapBackWithBeginEnd(int begin, int end) => this.ListData().RemoveRangeSwapBackWithBeginEnd(begin, end);
  1328. /// <summary>
  1329. /// Removes the pointer at an index, shifting everything above it down by one. Decrements the length by 1.
  1330. /// </summary>
  1331. /// <param name="index">The index of the pointer to remove.</param>
  1332. /// <remarks>
  1333. /// If you don't care about preserving the order of the pointers, <see cref="RemoveAtSwapBack(int)"/> is a more efficient way to remove pointers.
  1334. /// </remarks>
  1335. /// <exception cref="ArgumentOutOfRangeException">Thrown if `index` is out of bounds.</exception>
  1336. public void RemoveAt(int index) => this.ListData().RemoveAt(index);
  1337. /// <summary>
  1338. /// Removes *N* pointers in a range, shifting everything above the range down by *N*. Decrements the length by *N*.
  1339. /// </summary>
  1340. /// <param name="index">The index of the first pointer to remove.</param>
  1341. /// <param name="count">The number of pointers to remove.</param>
  1342. /// <remarks>
  1343. /// If you don't care about preserving the order of the pointers, <see cref="RemoveRangeSwapBackWithBeginEnd"/>
  1344. /// is a more efficient way to remove pointers.
  1345. /// </remarks>
  1346. /// <exception cref="ArgumentOutOfRangeException">Thrown if `index` is out of bounds, `count` is negative,
  1347. /// or `index + count` exceeds the length.</exception>
  1348. public void RemoveRange(int index, int count) => this.ListData().RemoveRange(index, count);
  1349. /// <summary>
  1350. /// Removes *N* pointers in a range, shifting everything above it down by *N*. Decrements the length by *N*.
  1351. /// </summary>
  1352. /// <param name="begin">The index of the first pointer to remove.</param>
  1353. /// <param name="end">The index one greater than the last pointer to remove.</param>
  1354. /// <remarks>
  1355. /// If you don't care about preserving the order of the pointers, <see cref="RemoveRangeSwapBackWithBeginEnd"/> is a more efficient way to remove pointers.
  1356. /// </remarks>
  1357. /// <exception cref="ArgumentException">Thrown if `end &lt; begin`.</exception>
  1358. /// <exception cref="ArgumentOutOfRangeException">Thrown if `begin` or `end` are out of bounds.</exception>
  1359. [Obsolete("RemoveRangeWithBeginEnd(begin, end) is deprecated, use RemoveRange(index, count) instead. (RemovedAfter 2021-06-02)", false)]
  1360. public void RemoveRangeWithBeginEnd(int begin, int end) => this.ListData().RemoveRangeWithBeginEnd(begin, end);
  1361. /// <summary>
  1362. /// This method is not implemented. It will throw NotImplementedException if it is used.
  1363. /// </summary>
  1364. /// <remarks>Use Enumerator GetEnumerator() instead.</remarks>
  1365. /// <returns>Throws NotImplementedException.</returns>
  1366. /// <exception cref="NotImplementedException">Method is not implemented.</exception>
  1367. IEnumerator IEnumerable.GetEnumerator()
  1368. {
  1369. throw new NotImplementedException();
  1370. }
  1371. /// <summary>
  1372. /// This method is not implemented. It will throw NotImplementedException if it is used.
  1373. /// </summary>
  1374. /// <remarks>Use Enumerator GetEnumerator() instead.</remarks>
  1375. /// <returns>Throws NotImplementedException.</returns>
  1376. /// <exception cref="NotImplementedException">Method is not implemented.</exception>
  1377. IEnumerator<IntPtr> IEnumerable<IntPtr>.GetEnumerator()
  1378. {
  1379. throw new NotImplementedException();
  1380. }
  1381. /// <summary>
  1382. /// Returns a parallel reader of this list.
  1383. /// </summary>
  1384. /// <returns>A parallel reader of this list.</returns>
  1385. public ParallelReader AsParallelReader()
  1386. {
  1387. return new ParallelReader(Ptr, Length);
  1388. }
  1389. /// <summary>
  1390. /// A parallel reader for an UnsafePtrList&lt;T&gt;.
  1391. /// </summary>
  1392. /// <remarks>
  1393. /// Use <see cref="AsParallelReader"/> to create a parallel reader for a list.
  1394. /// </remarks>
  1395. [BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
  1396. public unsafe struct ParallelReader
  1397. {
  1398. /// <summary>
  1399. /// The internal buffer of the list.
  1400. /// </summary>
  1401. [NativeDisableUnsafePtrRestriction]
  1402. public readonly T** Ptr;
  1403. /// <summary>
  1404. /// The number of elements.
  1405. /// </summary>
  1406. public readonly int Length;
  1407. internal ParallelReader(T** ptr, int length)
  1408. {
  1409. Ptr = ptr;
  1410. Length = length;
  1411. }
  1412. /// <summary>
  1413. /// Returns the index of the first occurrence of a specific pointer in the list.
  1414. /// </summary>
  1415. /// <param name="ptr">The pointer to search for in the list.</param>
  1416. /// <returns>The index of the first occurrence of the pointer. Returns -1 if it is not found in the list.</returns>
  1417. public int IndexOf(void* ptr)
  1418. {
  1419. for (int i = 0; i < Length; ++i)
  1420. {
  1421. if (Ptr[i] == ptr) return i;
  1422. }
  1423. return -1;
  1424. }
  1425. /// <summary>
  1426. /// Returns true if the list contains at least one occurrence of a specific pointer.
  1427. /// </summary>
  1428. /// <param name="ptr">The pointer to search for in the list.</param>
  1429. /// <returns>True if the list contains at least one occurrence of the pointer.</returns>
  1430. public bool Contains(void* ptr)
  1431. {
  1432. return IndexOf(ptr) != -1;
  1433. }
  1434. }
  1435. /// <summary>
  1436. /// Returns a parallel writer of this list.
  1437. /// </summary>
  1438. /// <returns>A parallel writer of this list.</returns>
  1439. public ParallelWriter AsParallelWriter()
  1440. {
  1441. return new ParallelWriter(Ptr, (UnsafeList<IntPtr>*)UnsafeUtility.AddressOf(ref this));
  1442. }
  1443. /// <summary>
  1444. /// A parallel writer for an UnsafePtrList&lt;T&gt;.
  1445. /// </summary>
  1446. /// <remarks>
  1447. /// Use <see cref="AsParallelWriter"/> to create a parallel writer for a list.
  1448. /// </remarks>
  1449. [BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
  1450. public unsafe struct ParallelWriter
  1451. {
  1452. /// <summary>
  1453. /// The data of the list.
  1454. /// </summary>
  1455. [NativeDisableUnsafePtrRestriction]
  1456. public readonly T** Ptr;
  1457. /// <summary>
  1458. /// The UnsafeList to write to.
  1459. /// </summary>
  1460. [NativeDisableUnsafePtrRestriction]
  1461. public UnsafeList<IntPtr>* ListData;
  1462. internal unsafe ParallelWriter(T** ptr, UnsafeList<IntPtr>* listData)
  1463. {
  1464. Ptr = ptr;
  1465. ListData = listData;
  1466. }
  1467. /// <summary>
  1468. /// Adds a pointer to the end of the list.
  1469. /// </summary>
  1470. /// <param name="value">The pointer to add to the end of the list.</param>
  1471. /// <remarks>
  1472. /// Increments the length by 1. Never increases the capacity.
  1473. /// </remarks>
  1474. /// <exception cref="Exception">Thrown if incrementing the length would exceed the capacity.</exception>
  1475. public void AddNoResize(T* value) => ListData->AddNoResize((IntPtr)value);
  1476. /// <summary>
  1477. /// Copies pointers from a buffer to the end of the list.
  1478. /// </summary>
  1479. /// <param name="ptr">The buffer to copy from.</param>
  1480. /// <param name="count">The number of pointers to copy from the buffer.</param>
  1481. /// <remarks>
  1482. /// Increments the length by `count`. Never increases the capacity.
  1483. /// </remarks>
  1484. /// <exception cref="Exception">Thrown if the increased length would exceed the capacity.</exception>
  1485. public void AddRangeNoResize(T** ptr, int count) => ListData->AddRangeNoResize(ptr, count);
  1486. /// <summary>
  1487. /// Copies the pointers of another list to the end of this list.
  1488. /// </summary>
  1489. /// <param name="list">The other list to copy from.</param>
  1490. /// <remarks>
  1491. /// Increments the length by the length of the other list. Never increases the capacity.
  1492. /// </remarks>
  1493. /// <exception cref="Exception">Thrown if the increased length would exceed the capacity.</exception>
  1494. public void AddRangeNoResize(UnsafePtrList<T> list) => ListData->AddRangeNoResize(list.Ptr, list.Length);
  1495. }
  1496. }
  1497. [BurstCompatible]
  1498. internal static class UnsafePtrListTExtensions
  1499. {
  1500. [BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
  1501. public static ref UnsafeList<IntPtr> ListData<T>(ref this UnsafePtrList<T> from) where T : unmanaged => ref UnsafeUtility.As<UnsafePtrList<T>, UnsafeList<IntPtr>>(ref from);
  1502. }
  1503. internal sealed class UnsafePtrListTDebugView<T>
  1504. where T : unmanaged
  1505. {
  1506. UnsafePtrList<T> Data;
  1507. public UnsafePtrListTDebugView(UnsafePtrList<T> data)
  1508. {
  1509. Data = data;
  1510. }
  1511. public unsafe T*[] Items
  1512. {
  1513. get
  1514. {
  1515. T*[] result = new T*[Data.Length];
  1516. for (var i = 0; i < result.Length; ++i)
  1517. {
  1518. result[i] = Data.Ptr[i];
  1519. }
  1520. return result;
  1521. }
  1522. }
  1523. }
  1524. }