CoreUnsafeUtils.cs 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489
  1. using System;
  2. using System.Collections.Generic;
  3. using Unity.Collections.LowLevel.Unsafe;
  4. namespace UnityEngine.Rendering
  5. {
  6. /// <summary>
  7. /// Static class with unsafe utility functions.
  8. /// </summary>
  9. public static unsafe class CoreUnsafeUtils
  10. {
  11. /// <summary>
  12. /// Fixed Buffer String Queue class.
  13. /// </summary>
  14. public struct FixedBufferStringQueue
  15. {
  16. byte* m_ReadCursor;
  17. byte* m_WriteCursor;
  18. readonly byte* m_BufferEnd;
  19. readonly byte* m_BufferStart;
  20. readonly int m_BufferLength;
  21. /// <summary>
  22. /// Number of element in the queue.
  23. /// </summary>
  24. public int Count { get; private set; }
  25. /// <summary>
  26. /// Constructor.
  27. /// </summary>
  28. /// <param name="ptr">Buffer pointer.</param>
  29. /// <param name="length">Length of the provided allocated buffer in byte.</param>
  30. public FixedBufferStringQueue(byte* ptr, int length)
  31. {
  32. m_BufferStart = ptr;
  33. m_BufferLength = length;
  34. m_BufferEnd = m_BufferStart + m_BufferLength;
  35. m_ReadCursor = m_BufferStart;
  36. m_WriteCursor = m_BufferStart;
  37. Count = 0;
  38. Clear();
  39. }
  40. /// <summary>
  41. /// Try to push a new element in the queue.
  42. /// </summary>
  43. /// <param name="v">Element to push in the queue.</param>
  44. /// <returns>True if the new element could be pushed in the queue. False if reserved memory was not enough.</returns>
  45. public bool TryPush(string v)
  46. {
  47. var size = v.Length * sizeof(char) + sizeof(int);
  48. if (m_WriteCursor + size >= m_BufferEnd)
  49. return false;
  50. *(int*)m_WriteCursor = v.Length;
  51. m_WriteCursor += sizeof(int);
  52. var charPtr = (char*)m_WriteCursor;
  53. for (int i = 0; i < v.Length; ++i, ++charPtr)
  54. *charPtr = v[i];
  55. m_WriteCursor += sizeof(char) * v.Length;
  56. ++Count;
  57. return true;
  58. }
  59. /// <summary>
  60. /// Pop an element of the queue.
  61. /// </summary>
  62. /// <param name="v">Output result string.</param>
  63. /// <returns>True if an element was succesfuly poped.</returns>
  64. public bool TryPop(out string v)
  65. {
  66. var size = *(int*)m_ReadCursor;
  67. if (size != 0)
  68. {
  69. m_ReadCursor += sizeof(int);
  70. v = new string((char*)m_ReadCursor, 0, size);
  71. m_ReadCursor += size * sizeof(char);
  72. return true;
  73. }
  74. v = default;
  75. return false;
  76. }
  77. /// <summary>
  78. /// Clear the queue.
  79. /// </summary>
  80. public void Clear()
  81. {
  82. m_WriteCursor = m_BufferStart;
  83. m_ReadCursor = m_BufferStart;
  84. Count = 0;
  85. UnsafeUtility.MemClear(m_BufferStart, m_BufferLength);
  86. }
  87. }
  88. /// <summary>
  89. /// Key Getter interface.
  90. /// </summary>
  91. /// <typeparam name="TValue">Value</typeparam>
  92. /// <typeparam name="TKey">Key</typeparam>
  93. public interface IKeyGetter<TValue, TKey>
  94. {
  95. /// <summary>Getter</summary>
  96. /// <param name="v">The value</param>
  97. /// <returns>The key</returns>
  98. TKey Get(ref TValue v);
  99. }
  100. internal struct DefaultKeyGetter<T> : IKeyGetter<T, T>
  101. { public T Get(ref T v) { return v; } }
  102. // Note: this is a workaround needed to circumvent some AOT issues when building for xbox
  103. internal struct UintKeyGetter : IKeyGetter<uint, uint>
  104. { public uint Get(ref uint v) { return v; } }
  105. /// <summary>
  106. /// Extension method to copy elements of a list into a buffer.
  107. /// </summary>
  108. /// <typeparam name="T">Type of the provided List.</typeparam>
  109. /// <param name="list">Input List.</param>
  110. /// <param name="dest">Destination buffer.</param>
  111. /// <param name="count">Number of elements to copy.</param>
  112. public static void CopyTo<T>(this List<T> list, void* dest, int count)
  113. where T : struct
  114. {
  115. var c = Mathf.Min(count, list.Count);
  116. for (int i = 0; i < c; ++i)
  117. UnsafeUtility.WriteArrayElement<T>(dest, i, list[i]);
  118. }
  119. /// <summary>
  120. /// Extension method to copy elements of an array into a buffer.
  121. /// </summary>
  122. /// <typeparam name="T">Type of the provided array.</typeparam>
  123. /// <param name="list">Input List.</param>
  124. /// <param name="dest">Destination buffer.</param>
  125. /// <param name="count">Number of elements to copy.</param>
  126. public static void CopyTo<T>(this T[] list, void* dest, int count)
  127. where T : struct
  128. {
  129. var c = Mathf.Min(count, list.Length);
  130. for (int i = 0; i < c; ++i)
  131. UnsafeUtility.WriteArrayElement<T>(dest, i, list[i]);
  132. }
  133. /// <summary>
  134. /// Quick Sort
  135. /// </summary>
  136. /// <param name="arr">uint array.</param>
  137. /// <param name="left">Left boundary.</param>
  138. /// <param name="right">Left boundary.</param>
  139. public static unsafe void QuickSort(uint[] arr, int left, int right)
  140. {
  141. fixed (uint* ptr = arr)
  142. CoreUnsafeUtils.QuickSort<uint, uint, UintKeyGetter>(ptr, left, right);
  143. }
  144. /// <summary>
  145. /// Quick sort.
  146. /// </summary>
  147. /// <typeparam name="T">Type to compare.</typeparam>
  148. /// <param name="count">Number of element.</param>
  149. /// <param name="data">Buffer to sort.</param>
  150. public static void QuickSort<T>(int count, void* data)
  151. where T : struct, IComparable<T>
  152. {
  153. QuickSort<T, T, DefaultKeyGetter<T>>(data, 0, count - 1);
  154. }
  155. /// <summary>
  156. /// Quick sort.
  157. /// </summary>
  158. /// <typeparam name="TValue">Value type.</typeparam>
  159. /// <typeparam name="TKey">Key Type.</typeparam>
  160. /// <typeparam name="TGetter">Getter type.</typeparam>
  161. /// <param name="count">Number of element.</param>
  162. /// <param name="data">Data to sort.</param>
  163. public static void QuickSort<TValue, TKey, TGetter>(int count, void* data)
  164. where TKey : struct, IComparable<TKey>
  165. where TValue : struct
  166. where TGetter : struct, IKeyGetter<TValue, TKey>
  167. {
  168. QuickSort<TValue, TKey, TGetter>(data, 0, count - 1);
  169. }
  170. /// <summary>
  171. /// Quick sort.
  172. /// </summary>
  173. /// <typeparam name="TValue">Value type.</typeparam>
  174. /// <typeparam name="TKey">Key Type.</typeparam>
  175. /// <typeparam name="TGetter">Getter type.</typeparam>
  176. /// <param name="data">Data to sort.</param>
  177. /// <param name="left">Left boundary.</param>
  178. /// <param name="right">Right boundary.</param>
  179. public static void QuickSort<TValue, TKey, TGetter>(void* data, int left, int right)
  180. where TKey : struct, IComparable<TKey>
  181. where TValue : struct
  182. where TGetter : struct, IKeyGetter<TValue, TKey>
  183. {
  184. // For Recursion
  185. if (left < right)
  186. {
  187. int pivot = Partition<TValue, TKey, TGetter>(data, left, right);
  188. if (pivot >= 1)
  189. QuickSort<TValue, TKey, TGetter>(data, left, pivot);
  190. if (pivot + 1 < right)
  191. QuickSort<TValue, TKey, TGetter>(data, pivot + 1, right);
  192. }
  193. }
  194. /// <summary>
  195. /// Index of an element in a buffer.
  196. /// </summary>
  197. /// <typeparam name="T">Data type.</typeparam>
  198. /// <param name="data">Data buffer.</param>
  199. /// <param name="count">Number of elements.</param>
  200. /// <param name="v">Element to test against.</param>
  201. /// <returns>The first index of the provided element.</returns>
  202. public static int IndexOf<T>(void* data, int count, T v)
  203. where T : struct, IEquatable<T>
  204. {
  205. for (int i = 0; i < count; ++i)
  206. {
  207. if (UnsafeUtility.ReadArrayElement<T>(data, i).Equals(v))
  208. return i;
  209. }
  210. return -1;
  211. }
  212. /// <summary>
  213. /// Compare hashes of two collections and provide
  214. /// a list of indices <paramref name="removeIndices"/> to remove in <paramref name="oldHashes"/>
  215. /// and a list of indices <paramref name="addIndices"/> to add in <paramref name="newHashes"/>.
  216. ///
  217. /// Assumes that <paramref name="newHashes"/> and <paramref name="oldHashes"/> are sorted.
  218. /// </summary>
  219. /// <typeparam name="TOldValue">Old value type.</typeparam>
  220. /// <typeparam name="TOldGetter">Old getter type.</typeparam>
  221. /// <typeparam name="TNewValue">New value type.</typeparam>
  222. /// <typeparam name="TNewGetter">New getter type.</typeparam>
  223. /// <param name="oldHashCount">Number of hashes in <paramref name="oldHashes"/>.</param>
  224. /// <param name="oldHashes">Previous hashes to compare.</param>
  225. /// <param name="newHashCount">Number of hashes in <paramref name="newHashes"/>.</param>
  226. /// <param name="newHashes">New hashes to compare.</param>
  227. /// <param name="addIndices">Indices of element to add in <paramref name="newHashes"/> will be written here.</param>
  228. /// <param name="removeIndices">Indices of element to remove in <paramref name="oldHashes"/> will be written here.</param>
  229. /// <param name="addCount">Number of elements to add will be written here.</param>
  230. /// <param name="remCount">Number of elements to remove will be written here.</param>
  231. /// <returns>The number of operation to perform (<code><paramref name="addCount"/> + <paramref name="remCount"/></code>)</returns>
  232. public static int CompareHashes<TOldValue, TOldGetter, TNewValue, TNewGetter>(
  233. int oldHashCount, void* oldHashes,
  234. int newHashCount, void* newHashes,
  235. // assume that the capacity of indices is >= max(oldHashCount, newHashCount)
  236. int* addIndices, int* removeIndices,
  237. out int addCount, out int remCount
  238. )
  239. where TOldValue : struct
  240. where TNewValue : struct
  241. where TOldGetter : struct, IKeyGetter<TOldValue, Hash128>
  242. where TNewGetter : struct, IKeyGetter<TNewValue, Hash128>
  243. {
  244. var oldGetter = new TOldGetter();
  245. var newGetter = new TNewGetter();
  246. addCount = 0;
  247. remCount = 0;
  248. // Check combined hashes
  249. if (oldHashCount == newHashCount)
  250. {
  251. var oldHash = new Hash128();
  252. var newHash = new Hash128();
  253. CombineHashes<TOldValue, TOldGetter>(oldHashCount, oldHashes, &oldHash);
  254. CombineHashes<TNewValue, TNewGetter>(newHashCount, newHashes, &newHash);
  255. if (oldHash == newHash)
  256. return 0;
  257. }
  258. var numOperations = 0;
  259. var oldI = 0;
  260. var newI = 0;
  261. while (oldI < oldHashCount || newI < newHashCount)
  262. {
  263. // At the end of old array.
  264. if (oldI == oldHashCount)
  265. {
  266. // No more hashes in old array. Add remaining entries from new array.
  267. for (; newI < newHashCount; ++newI)
  268. {
  269. addIndices[addCount++] = newI;
  270. ++numOperations;
  271. }
  272. continue;
  273. }
  274. // At end of new array.
  275. if (newI == newHashCount)
  276. {
  277. // No more hashes in old array. Remove remaining entries from old array.
  278. for (; oldI < oldHashCount; ++oldI)
  279. {
  280. removeIndices[remCount++] = oldI;
  281. ++numOperations;
  282. }
  283. continue;
  284. }
  285. // Both arrays have data.
  286. var newVal = UnsafeUtility.ReadArrayElement<TNewValue>(newHashes, newI);
  287. var oldVal = UnsafeUtility.ReadArrayElement<TOldValue>(oldHashes, oldI);
  288. var newKey = newGetter.Get(ref newVal);
  289. var oldKey = oldGetter.Get(ref oldVal);
  290. if (newKey == oldKey)
  291. {
  292. // Matching hash, skip.
  293. ++newI;
  294. ++oldI;
  295. continue;
  296. }
  297. // Both arrays have data, but hashes do not match.
  298. if (newKey < oldKey)
  299. {
  300. // oldIter is the greater hash. Push "add" jobs from the new array until reaching the oldIter hash.
  301. while (newI < newHashCount && newKey < oldKey)
  302. {
  303. addIndices[addCount++] = newI;
  304. ++newI;
  305. ++numOperations;
  306. newVal = UnsafeUtility.ReadArrayElement<TNewValue>(newHashes, newI);
  307. newKey = newGetter.Get(ref newVal);
  308. }
  309. }
  310. else
  311. {
  312. // newIter is the greater hash. Push "remove" jobs from the old array until reaching the newIter hash.
  313. while (oldI < oldHashCount && oldKey < newKey)
  314. {
  315. removeIndices[remCount++] = oldI;
  316. ++numOperations;
  317. ++oldI;
  318. }
  319. }
  320. }
  321. return numOperations;
  322. }
  323. /// <summary>
  324. /// Compare hashes.
  325. /// </summary>
  326. /// <param name="oldHashCount">Number of hashes in <paramref name="oldHashes"/>.</param>
  327. /// <param name="oldHashes">Previous hashes to compare.</param>
  328. /// <param name="newHashCount">Number of hashes in <paramref name="newHashes"/>.</param>
  329. /// <param name="newHashes">New hashes to compare.</param>
  330. /// <param name="addIndices">Indices of element to add in <paramref name="newHashes"/> will be written here.</param>
  331. /// <param name="removeIndices">Indices of element to remove in <paramref name="oldHashes"/> will be written here.</param>
  332. /// <param name="addCount">Number of elements to add will be written here.</param>
  333. /// <param name="remCount">Number of elements to remove will be written here.</param>
  334. /// <returns>The number of operation to perform (<code><paramref name="addCount"/> + <paramref name="remCount"/></code>)</returns>
  335. public static int CompareHashes(
  336. int oldHashCount, Hash128* oldHashes,
  337. int newHashCount, Hash128* newHashes,
  338. // assume that the capacity of indices is >= max(oldHashCount, newHashCount)
  339. int* addIndices, int* removeIndices,
  340. out int addCount, out int remCount
  341. )
  342. {
  343. return CompareHashes<Hash128, DefaultKeyGetter<Hash128>, Hash128, DefaultKeyGetter<Hash128>>(
  344. oldHashCount, oldHashes,
  345. newHashCount, newHashes,
  346. addIndices, removeIndices,
  347. out addCount, out remCount
  348. );
  349. }
  350. /// <summary>Combine all of the hashes of a collection of hashes.</summary>
  351. /// <typeparam name="TValue">Value type.</typeparam>
  352. /// <typeparam name="TGetter">Getter type.</typeparam>
  353. /// <param name="count">Number of hash to combine.</param>
  354. /// <param name="hashes">Hashes to combine.</param>
  355. /// <param name="outHash">Hash to update.</param>
  356. public static void CombineHashes<TValue, TGetter>(int count, void* hashes, Hash128* outHash)
  357. where TValue : struct
  358. where TGetter : struct, IKeyGetter<TValue, Hash128>
  359. {
  360. var getter = new TGetter();
  361. for (int i = 0; i < count; ++i)
  362. {
  363. var v = UnsafeUtility.ReadArrayElement<TValue>(hashes, i);
  364. var h = getter.Get(ref v);
  365. HashUtilities.AppendHash(ref h, ref *outHash);
  366. }
  367. }
  368. /// <summary>
  369. /// Combine hashes.
  370. /// </summary>
  371. /// <param name="count">Number of hash to combine.</param>
  372. /// <param name="hashes">Hashes to combine.</param>
  373. /// <param name="outHash">Hash to update.</param>
  374. public static void CombineHashes(int count, Hash128* hashes, Hash128* outHash)
  375. {
  376. CombineHashes<Hash128, DefaultKeyGetter<Hash128>>(count, hashes, outHash);
  377. }
  378. // Just a sort function that doesn't allocate memory
  379. // Note: Should be replace by a radix sort for positive integer
  380. static int Partition<TValue, TKey, TGetter>(void* data, int left, int right)
  381. where TKey : struct, IComparable<TKey>
  382. where TValue : struct
  383. where TGetter : struct, IKeyGetter<TValue, TKey>
  384. {
  385. var getter = default(TGetter);
  386. var pivotvalue = UnsafeUtility.ReadArrayElement<TValue>(data, left);
  387. var pivot = getter.Get(ref pivotvalue);
  388. --left;
  389. ++right;
  390. while (true)
  391. {
  392. var c = 0;
  393. var lvalue = default(TValue);
  394. var lkey = default(TKey);
  395. do
  396. {
  397. ++left;
  398. lvalue = UnsafeUtility.ReadArrayElement<TValue>(data, left);
  399. lkey = getter.Get(ref lvalue);
  400. c = lkey.CompareTo(pivot);
  401. }
  402. while (c < 0);
  403. var rvalue = default(TValue);
  404. var rkey = default(TKey);
  405. do
  406. {
  407. --right;
  408. rvalue = UnsafeUtility.ReadArrayElement<TValue>(data, right);
  409. rkey = getter.Get(ref rvalue);
  410. c = rkey.CompareTo(pivot);
  411. }
  412. while (c > 0);
  413. if (left < right)
  414. {
  415. UnsafeUtility.WriteArrayElement(data, right, lvalue);
  416. UnsafeUtility.WriteArrayElement(data, left, rvalue);
  417. }
  418. else
  419. {
  420. return right;
  421. }
  422. }
  423. }
  424. /// <summary>
  425. /// Checks for duplicates in an array.
  426. /// </summary>
  427. /// <param name="arr">Input array.</param>
  428. /// <returns>True if there is any duplicate in the input array.</returns>
  429. public static unsafe bool HaveDuplicates(int[] arr)
  430. {
  431. int* copy = stackalloc int[arr.Length];
  432. arr.CopyTo<int>(copy, arr.Length);
  433. QuickSort<int>(arr.Length, copy);
  434. for (int i = arr.Length - 1; i > 0; --i)
  435. {
  436. if (UnsafeUtility.ReadArrayElement<int>(copy, i).CompareTo(UnsafeUtility.ReadArrayElement<int>(copy, i - 1)) == 0)
  437. {
  438. return true;
  439. }
  440. }
  441. return false;
  442. }
  443. }
  444. }