ArrayHelpers.cs 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Unity.Collections;
  5. using Unity.Collections.LowLevel.Unsafe;
  6. namespace UnityEngine.InputSystem.Utilities
  7. {
  8. /// <summary>
  9. /// A collection of utility functions for working with arrays.
  10. /// </summary>
  11. /// <remarks>
  12. /// The goal of this collection is to make it easy to use arrays directly rather than resorting to
  13. /// <see cref="List{T}"/>.
  14. /// </remarks>
  15. internal static class ArrayHelpers
  16. {
  17. public static int LengthSafe<TValue>(this TValue[] array)
  18. {
  19. if (array == null)
  20. return 0;
  21. return array.Length;
  22. }
  23. public static void Clear<TValue>(this TValue[] array)
  24. {
  25. if (array == null)
  26. return;
  27. Array.Clear(array, 0, array.Length);
  28. }
  29. public static void Clear<TValue>(this TValue[] array, int count)
  30. {
  31. if (array == null)
  32. return;
  33. Array.Clear(array, 0, count);
  34. }
  35. public static void Clear<TValue>(this TValue[] array, ref int count)
  36. {
  37. if (array == null)
  38. return;
  39. Array.Clear(array, 0, count);
  40. count = 0;
  41. }
  42. public static void EnsureCapacity<TValue>(ref TValue[] array, int count, int capacity, int capacityIncrement = 10)
  43. {
  44. if (capacity == 0)
  45. return;
  46. if (array == null)
  47. {
  48. array = new TValue[Math.Max(capacity, capacityIncrement)];
  49. return;
  50. }
  51. var currentCapacity = array.Length - count;
  52. if (currentCapacity >= capacity)
  53. return;
  54. DuplicateWithCapacity(ref array, count, capacity, capacityIncrement);
  55. }
  56. public static void DuplicateWithCapacity<TValue>(ref TValue[] array, int count, int capacity, int capacityIncrement = 10)
  57. {
  58. if (array == null)
  59. {
  60. array = new TValue[Math.Max(capacity, capacityIncrement)];
  61. return;
  62. }
  63. var newSize = count + Math.Max(capacity, capacityIncrement);
  64. var newArray = new TValue[newSize];
  65. Array.Copy(array, newArray, count);
  66. array = newArray;
  67. }
  68. public static bool Contains<TValue>(TValue[] array, TValue value)
  69. {
  70. if (array == null)
  71. return false;
  72. var comparer = EqualityComparer<TValue>.Default;
  73. for (var i = 0; i < array.Length; ++i)
  74. if (comparer.Equals(array[i], value))
  75. return true;
  76. return false;
  77. }
  78. public static bool ContainsReference<TValue>(TValue[] array, TValue value)
  79. where TValue : class
  80. {
  81. if (array == null)
  82. return false;
  83. return ContainsReference(array, array.Length, value);
  84. }
  85. public static bool ContainsReference<TValue>(TValue[] array, int count, TValue value)
  86. where TValue : class
  87. {
  88. return IndexOfReference(array, value, count) != -1;
  89. }
  90. public static bool HaveEqualElements<TValue>(TValue[] first, TValue[] second, int count = int.MaxValue)
  91. {
  92. if (first == null || second == null)
  93. return second == first;
  94. var lengthFirst = Math.Min(count, first.Length);
  95. var lengthSecond = Math.Min(count, second.Length);
  96. if (lengthFirst != lengthSecond)
  97. return false;
  98. var comparer = EqualityComparer<TValue>.Default;
  99. for (var i = 0; i < lengthFirst; ++i)
  100. if (!comparer.Equals(first[i], second[i]))
  101. return false;
  102. return true;
  103. }
  104. ////REVIEW: remove this to get rid of default equality comparer?
  105. public static int IndexOf<TValue>(TValue[] array, TValue value, int startIndex = 0, int count = -1)
  106. {
  107. if (array == null)
  108. return -1;
  109. if (count < 0)
  110. count = array.Length - startIndex;
  111. var comparer = EqualityComparer<TValue>.Default;
  112. for (var i = startIndex; i < startIndex + count; ++i)
  113. if (comparer.Equals(array[i], value))
  114. return i;
  115. return -1;
  116. }
  117. public static int IndexOf<TValue>(this TValue[] array, Predicate<TValue> predicate)
  118. {
  119. if (array == null)
  120. return -1;
  121. var length = array.Length;
  122. for (var i = 0; i < length; ++i)
  123. if (predicate(array[i]))
  124. return i;
  125. return -1;
  126. }
  127. public static int IndexOfReference<TValue>(this TValue[] array, TValue value, int count = -1)
  128. where TValue : class
  129. {
  130. return IndexOfReference(array, value, 0, count);
  131. }
  132. public static int IndexOfReference<TValue>(this TValue[] array, TValue value, int startIndex, int count)
  133. where TValue : class
  134. {
  135. if (array == null)
  136. return -1;
  137. if (count < 0)
  138. count = array.Length - startIndex;
  139. for (var i = startIndex; i < startIndex + count; ++i)
  140. if (ReferenceEquals(array[i], value))
  141. return i;
  142. return -1;
  143. }
  144. public static int IndexOfValue<TValue>(this TValue[] array, TValue value, int startIndex = 0, int count = -1)
  145. where TValue : struct, IEquatable<TValue>
  146. {
  147. if (array == null)
  148. return -1;
  149. if (count < 0)
  150. count = array.Length - startIndex;
  151. for (var i = startIndex; i < startIndex + count; ++i)
  152. if (value.Equals(array[i]))
  153. return i;
  154. return -1;
  155. }
  156. public static unsafe void Resize<TValue>(ref NativeArray<TValue> array, int newSize, Allocator allocator)
  157. where TValue : struct
  158. {
  159. var oldSize = array.Length;
  160. if (oldSize == newSize)
  161. return;
  162. if (newSize == 0)
  163. {
  164. if (array.IsCreated)
  165. array.Dispose();
  166. array = new NativeArray<TValue>();
  167. return;
  168. }
  169. var newArray = new NativeArray<TValue>(newSize, allocator);
  170. if (oldSize != 0)
  171. {
  172. // Copy contents from old array.
  173. UnsafeUtility.MemCpy(newArray.GetUnsafePtr(), array.GetUnsafeReadOnlyPtr(),
  174. UnsafeUtility.SizeOf<TValue>() * (newSize < oldSize ? newSize : oldSize));
  175. array.Dispose();
  176. }
  177. array = newArray;
  178. }
  179. public static int Append<TValue>(ref TValue[] array, TValue value)
  180. {
  181. if (array == null)
  182. {
  183. array = new TValue[1];
  184. array[0] = value;
  185. return 0;
  186. }
  187. var length = array.Length;
  188. Array.Resize(ref array, length + 1);
  189. array[length] = value;
  190. return length;
  191. }
  192. public static int Append<TValue>(ref TValue[] array, IEnumerable<TValue> values)
  193. {
  194. if (array == null)
  195. {
  196. array = values.ToArray();
  197. return 0;
  198. }
  199. var oldLength = array.Length;
  200. var valueCount = values.Count();
  201. Array.Resize(ref array, oldLength + valueCount);
  202. var index = oldLength;
  203. foreach (var value in values)
  204. array[index++] = value;
  205. return oldLength;
  206. }
  207. // Append to an array that is considered immutable. This allows using 'values' as is
  208. // if 'array' is null.
  209. // Returns the index of the first newly added element in the resulting array.
  210. public static int AppendToImmutable<TValue>(ref TValue[] array, TValue[] values)
  211. {
  212. if (array == null)
  213. {
  214. array = values;
  215. return 0;
  216. }
  217. if (values != null && values.Length > 0)
  218. {
  219. var oldCount = array.Length;
  220. var valueCount = values.Length;
  221. Array.Resize(ref array, oldCount + valueCount);
  222. Array.Copy(values, 0, array, oldCount, valueCount);
  223. return oldCount;
  224. }
  225. return array.Length;
  226. }
  227. public static int AppendWithCapacity<TValue>(ref TValue[] array, ref int count, TValue value, int capacityIncrement = 10)
  228. {
  229. if (array == null)
  230. {
  231. array = new TValue[capacityIncrement];
  232. array[0] = value;
  233. ++count;
  234. return 0;
  235. }
  236. var capacity = array.Length;
  237. if (capacity == count)
  238. {
  239. capacity += capacityIncrement;
  240. Array.Resize(ref array, capacity);
  241. }
  242. var index = count;
  243. array[index] = value;
  244. ++count;
  245. return index;
  246. }
  247. public static int AppendListWithCapacity<TValue, TValues>(ref TValue[] array, ref int length, TValues values, int capacityIncrement = 10)
  248. where TValues : IReadOnlyList<TValue>
  249. {
  250. var numToAdd = values.Count;
  251. if (array == null)
  252. {
  253. var size = Math.Max(numToAdd, capacityIncrement);
  254. array = new TValue[size];
  255. for (var i = 0; i < numToAdd; ++i)
  256. array[i] = values[i];
  257. length += numToAdd;
  258. return 0;
  259. }
  260. var capacity = array.Length;
  261. if (capacity < length + numToAdd)
  262. {
  263. capacity += Math.Max(length + numToAdd, capacityIncrement);
  264. Array.Resize(ref array, capacity);
  265. }
  266. var index = length;
  267. for (var i = 0; i < numToAdd; ++i)
  268. array[index + i] = values[i];
  269. length += numToAdd;
  270. return index;
  271. }
  272. public static int AppendWithCapacity<TValue>(ref NativeArray<TValue> array, ref int count, TValue value,
  273. int capacityIncrement = 10, Allocator allocator = Allocator.Persistent)
  274. where TValue : struct
  275. {
  276. var capacity = array.Length;
  277. if (capacity == count)
  278. GrowBy(ref array, capacityIncrement > 1 ? capacityIncrement : 1, allocator);
  279. var index = count;
  280. array[index] = value;
  281. ++count;
  282. return index;
  283. }
  284. public static void InsertAt<TValue>(ref TValue[] array, int index, TValue value)
  285. {
  286. if (array == null)
  287. {
  288. ////REVIEW: allow growing array to specific size by inserting at arbitrary index?
  289. if (index != 0)
  290. throw new ArgumentOutOfRangeException(nameof(index));
  291. array = new TValue[1];
  292. array[0] = value;
  293. return;
  294. }
  295. // Reallocate.
  296. var oldLength = array.Length;
  297. Array.Resize(ref array, oldLength + 1);
  298. // Make room for element.
  299. if (index != oldLength)
  300. Array.Copy(array, index, array, index + 1, oldLength - index);
  301. array[index] = value;
  302. }
  303. public static void InsertAtWithCapacity<TValue>(ref TValue[] array, ref int count, int index, TValue value, int capacityIncrement = 10)
  304. {
  305. EnsureCapacity(ref array, count, count + 1, capacityIncrement);
  306. if (index != count)
  307. Array.Copy(array, index, array, index + 1, count - index);
  308. array[index] = value;
  309. ++count;
  310. }
  311. public static void PutAtIfNotSet<TValue>(ref TValue[] array, int index, Func<TValue> valueFn)
  312. {
  313. if (array.LengthSafe() < index + 1)
  314. Array.Resize(ref array, index + 1);
  315. if (EqualityComparer<TValue>.Default.Equals(array[index], default(TValue)))
  316. array[index] = valueFn();
  317. }
  318. // Adds 'count' entries to the array. Returns first index of newly added entries.
  319. public static int GrowBy<TValue>(ref TValue[] array, int count)
  320. {
  321. if (array == null)
  322. {
  323. array = new TValue[count];
  324. return 0;
  325. }
  326. var oldLength = array.Length;
  327. Array.Resize(ref array, oldLength + count);
  328. return oldLength;
  329. }
  330. public static unsafe int GrowBy<TValue>(ref NativeArray<TValue> array, int count, Allocator allocator = Allocator.Persistent)
  331. where TValue : struct
  332. {
  333. var length = array.Length;
  334. if (length == 0)
  335. {
  336. array = new NativeArray<TValue>(count, allocator);
  337. return 0;
  338. }
  339. var newArray = new NativeArray<TValue>(length + count, allocator);
  340. // CopyFrom() expects length to match. Copy manually.
  341. UnsafeUtility.MemCpy(newArray.GetUnsafePtr(), array.GetUnsafeReadOnlyPtr(), (long)length * UnsafeUtility.SizeOf<TValue>());
  342. array.Dispose();
  343. array = newArray;
  344. return length;
  345. }
  346. public static int GrowWithCapacity<TValue>(ref TValue[] array, ref int count, int growBy, int capacityIncrement = 10)
  347. {
  348. var length = array != null ? array.Length : 0;
  349. if (length < count + growBy)
  350. {
  351. if (capacityIncrement < growBy)
  352. capacityIncrement = growBy;
  353. GrowBy(ref array, capacityIncrement);
  354. }
  355. var offset = count;
  356. count += growBy;
  357. return offset;
  358. }
  359. public static int GrowWithCapacity<TValue>(ref NativeArray<TValue> array, ref int count, int growBy,
  360. int capacityIncrement = 10, Allocator allocator = Allocator.Persistent)
  361. where TValue : struct
  362. {
  363. var length = array.Length;
  364. if (length < count + growBy)
  365. {
  366. if (capacityIncrement < growBy)
  367. capacityIncrement = growBy;
  368. GrowBy(ref array, capacityIncrement, allocator);
  369. }
  370. var offset = count;
  371. count += growBy;
  372. return offset;
  373. }
  374. public static TValue[] Join<TValue>(TValue value, params TValue[] values)
  375. {
  376. // Determine length.
  377. var length = 0;
  378. if (value != null)
  379. ++length;
  380. if (values != null)
  381. length += values.Length;
  382. if (length == 0)
  383. return null;
  384. var array = new TValue[length];
  385. // Populate.
  386. var index = 0;
  387. if (value != null)
  388. array[index++] = value;
  389. if (values != null)
  390. Array.Copy(values, 0, array, index, values.Length);
  391. return array;
  392. }
  393. public static TValue[] Merge<TValue>(TValue[] first, TValue[] second)
  394. where TValue : IEquatable<TValue>
  395. {
  396. if (first == null)
  397. return second;
  398. if (second == null)
  399. return first;
  400. var merged = new List<TValue>();
  401. merged.AddRange(first);
  402. for (var i = 0; i < second.Length; ++i)
  403. {
  404. var secondValue = second[i];
  405. if (!merged.Exists(x => x.Equals(secondValue)))
  406. {
  407. merged.Add(secondValue);
  408. }
  409. }
  410. return merged.ToArray();
  411. }
  412. public static TValue[] Merge<TValue>(TValue[] first, TValue[] second, IEqualityComparer<TValue> comparer)
  413. {
  414. if (first == null)
  415. return second;
  416. if (second == null)
  417. return null;
  418. var merged = new List<TValue>();
  419. merged.AddRange(first);
  420. for (var i = 0; i < second.Length; ++i)
  421. {
  422. var secondValue = second[i];
  423. if (!merged.Exists(x => comparer.Equals(secondValue)))
  424. {
  425. merged.Add(secondValue);
  426. }
  427. }
  428. return merged.ToArray();
  429. }
  430. public static void EraseAt<TValue>(ref TValue[] array, int index)
  431. {
  432. Debug.Assert(array != null);
  433. Debug.Assert(index >= 0 && index < array.Length);
  434. var length = array.Length;
  435. if (index == 0 && length == 1)
  436. {
  437. array = null;
  438. return;
  439. }
  440. if (index < length - 1)
  441. Array.Copy(array, index + 1, array, index, length - index - 1);
  442. Array.Resize(ref array, length - 1);
  443. }
  444. public static void EraseAtWithCapacity<TValue>(TValue[] array, ref int count, int index)
  445. {
  446. Debug.Assert(array != null);
  447. Debug.Assert(count <= array.Length);
  448. Debug.Assert(index >= 0 && index < count);
  449. // If we're erasing from the beginning or somewhere in the middle, move
  450. // the array contents down from after the index.
  451. if (index < count - 1)
  452. {
  453. Array.Copy(array, index + 1, array, index, count - index - 1);
  454. }
  455. array[count - 1] = default; // Tail has been moved down by one.
  456. --count;
  457. }
  458. public static unsafe void EraseAtWithCapacity<TValue>(NativeArray<TValue> array, ref int count, int index)
  459. where TValue : struct
  460. {
  461. Debug.Assert(array.IsCreated);
  462. Debug.Assert(count <= array.Length);
  463. Debug.Assert(index >= 0 && index < count);
  464. // If we're erasing from the beginning or somewhere in the middle, move
  465. // the array contents down from after the index.
  466. if (index < count - 1)
  467. {
  468. var elementSize = UnsafeUtility.SizeOf<TValue>();
  469. var arrayPtr = (byte*)array.GetUnsafePtr();
  470. UnsafeUtility.MemCpy(arrayPtr + elementSize * index, arrayPtr + elementSize * (index + 1),
  471. (count - index - 1) * elementSize);
  472. }
  473. --count;
  474. }
  475. public static bool Erase<TValue>(ref TValue[] array, TValue value)
  476. {
  477. var index = IndexOf(array, value);
  478. if (index != -1)
  479. {
  480. EraseAt(ref array, index);
  481. return true;
  482. }
  483. return false;
  484. }
  485. /// <summary>
  486. /// Erase an element from the array by moving the tail element into its place.
  487. /// </summary>
  488. /// <param name="array">Array to modify. May be not <c>null</c>.</param>
  489. /// <param name="count">Current number of elements inside of array. May be less than <c>array.Length</c>.</param>
  490. /// <param name="index">Index of element to remove. Tail element will get moved into its place.</param>
  491. /// <typeparam name="TValue"></typeparam>
  492. /// <remarks>
  493. /// This method does not re-allocate the array. Instead <paramref name="count"/> is used
  494. /// to keep track of how many elements there actually are in the array.
  495. /// </remarks>
  496. public static void EraseAtByMovingTail<TValue>(TValue[] array, ref int count, int index)
  497. {
  498. Debug.Assert(array != null);
  499. Debug.Assert(index >= 0 && index < array.Length);
  500. Debug.Assert(count >= 0 && count <= array.Length);
  501. Debug.Assert(index < count);
  502. // Move tail, if necessary.
  503. if (index != count - 1)
  504. array[index] = array[count - 1];
  505. // Destroy current tail.
  506. if (count >= 1)
  507. array[count - 1] = default;
  508. --count;
  509. }
  510. public static TValue[] Copy<TValue>(TValue[] array)
  511. {
  512. if (array == null)
  513. return null;
  514. var length = array.Length;
  515. var result = new TValue[length];
  516. Array.Copy(array, result, length);
  517. return result;
  518. }
  519. public static TValue[] Clone<TValue>(TValue[] array)
  520. where TValue : ICloneable
  521. {
  522. if (array == null)
  523. return null;
  524. var count = array.Length;
  525. var result = new TValue[count];
  526. for (var i = 0; i < count; ++i)
  527. result[i] = (TValue)array[i].Clone();
  528. return result;
  529. }
  530. public static TNew[] Select<TOld, TNew>(TOld[] array, Func<TOld, TNew> converter)
  531. {
  532. if (array == null)
  533. return null;
  534. var length = array.Length;
  535. var result = new TNew[length];
  536. for (var i = 0; i < length; ++i)
  537. result[i] = converter(array[i]);
  538. return result;
  539. }
  540. private static void Swap<TValue>(ref TValue first, ref TValue second)
  541. {
  542. var temp = first;
  543. first = second;
  544. second = temp;
  545. }
  546. /// <summary>
  547. /// Swap the contents of two potentially overlapping slices within the array.
  548. /// </summary>
  549. /// <param name="array"></param>
  550. /// <param name="sourceIndex"></param>
  551. /// <param name="destinationIndex"></param>
  552. /// <param name="count"></param>
  553. /// <typeparam name="TValue"></typeparam>
  554. public static void SwapSlice<TValue>(TValue[] array, int sourceIndex, int destinationIndex, int count)
  555. {
  556. if (sourceIndex < destinationIndex)
  557. {
  558. for (var i = 0; i < count; ++i)
  559. Swap(ref array[sourceIndex + count - i - 1], ref array[destinationIndex + count - i - 1]);
  560. }
  561. else
  562. {
  563. for (var i = 0; i < count; ++i)
  564. Swap(ref array[sourceIndex + i], ref array[destinationIndex + i]);
  565. }
  566. }
  567. /// <summary>
  568. /// Move a slice in the array to a different place without allocating a temporary array.
  569. /// </summary>
  570. /// <param name="array"></param>
  571. /// <param name="sourceIndex"></param>
  572. /// <param name="destinationIndex"></param>
  573. /// <param name="count"></param>
  574. /// <typeparam name="TValue"></typeparam>
  575. /// <remarks>
  576. /// The slice is moved by repeatedly swapping slices until all the slices are where they
  577. /// are supposed to go. This is not super efficient but avoids having to allocate a temporary
  578. /// array on the heap.
  579. /// </remarks>
  580. public static void MoveSlice<TValue>(TValue[] array, int sourceIndex, int destinationIndex, int count)
  581. {
  582. if (count <= 0 || sourceIndex == destinationIndex)
  583. return;
  584. // Make sure we're moving from lower part of array to higher part so we only
  585. // have to deal with that scenario.
  586. if (sourceIndex > destinationIndex)
  587. Swap(ref sourceIndex, ref destinationIndex);
  588. while (destinationIndex != sourceIndex)
  589. {
  590. // Swap source and destination slice. Afterwards, the source slice is the right, final
  591. // place but the destination slice may not be.
  592. SwapSlice(array, sourceIndex, destinationIndex, count);
  593. // Slide destination window down.
  594. if (destinationIndex - sourceIndex >= count * 2)
  595. {
  596. // Slide down one whole window of count elements.
  597. destinationIndex -= count;
  598. }
  599. else
  600. {
  601. ////TODO: this can be improved by using halving instead and only doing the final step as a single element slide
  602. // Slide down by one element.
  603. --destinationIndex;
  604. }
  605. }
  606. }
  607. public static void EraseSliceWithCapacity<TValue>(ref TValue[] array, ref int length, int index, int count)
  608. {
  609. // Move elements down.
  610. if (count < length)
  611. Array.Copy(array, index + count, array, index, length - index - count);
  612. // Erase now vacant slots.
  613. for (var i = 0; i < count; ++i)
  614. array[length - i - 1] = default;
  615. length -= count;
  616. }
  617. public static void SwapElements<TValue>(this TValue[] array, int index1, int index2)
  618. {
  619. MemoryHelpers.Swap(ref array[index1], ref array[index2]);
  620. }
  621. public static void SwapElements<TValue>(this NativeArray<TValue> array, int index1, int index2)
  622. where TValue : struct
  623. {
  624. var temp = array[index1];
  625. array[index1] = array[index2];
  626. array[index2] = temp;
  627. }
  628. }
  629. }