A game about forced loneliness, made by TACStudios
at master 730 lines 30 kB view raw
1using System; 2using System.Collections.Generic; 3using Unity.Collections; 4using Unity.Collections.LowLevel.Unsafe; 5 6namespace UnityEngine.Rendering 7{ 8 /// <summary> 9 /// Static class with unsafe utility functions. 10 /// </summary> 11 public static unsafe class CoreUnsafeUtils 12 { 13 /// <summary> 14 /// Fixed Buffer String Queue class. 15 /// </summary> 16 public struct FixedBufferStringQueue 17 { 18 byte* m_ReadCursor; 19 byte* m_WriteCursor; 20 21 readonly byte* m_BufferEnd; 22 readonly byte* m_BufferStart; 23 readonly int m_BufferLength; 24 25 /// <summary> 26 /// Number of element in the queue. 27 /// </summary> 28 public int Count { get; private set; } 29 30 /// <summary> 31 /// Constructor. 32 /// </summary> 33 /// <param name="ptr">Buffer pointer.</param> 34 /// <param name="length">Length of the provided allocated buffer in byte.</param> 35 public FixedBufferStringQueue(byte* ptr, int length) 36 { 37 m_BufferStart = ptr; 38 m_BufferLength = length; 39 40 m_BufferEnd = m_BufferStart + m_BufferLength; 41 m_ReadCursor = m_BufferStart; 42 m_WriteCursor = m_BufferStart; 43 Count = 0; 44 Clear(); 45 } 46 47 /// <summary> 48 /// Try to push a new element in the queue. 49 /// </summary> 50 /// <param name="v">Element to push in the queue.</param> 51 /// <returns>True if the new element could be pushed in the queue. False if reserved memory was not enough.</returns> 52 public bool TryPush(string v) 53 { 54 var size = v.Length * sizeof(char) + sizeof(int); 55 if (m_WriteCursor + size >= m_BufferEnd) 56 return false; 57 58 *(int*)m_WriteCursor = v.Length; 59 m_WriteCursor += sizeof(int); 60 61 var charPtr = (char*)m_WriteCursor; 62 for (int i = 0; i < v.Length; ++i, ++charPtr) 63 *charPtr = v[i]; 64 65 m_WriteCursor += sizeof(char) * v.Length; 66 ++Count; 67 68 return true; 69 } 70 71 /// <summary> 72 /// Pop an element of the queue. 73 /// </summary> 74 /// <param name="v">Output result string.</param> 75 /// <returns>True if an element was succesfuly poped.</returns> 76 public bool TryPop(out string v) 77 { 78 var size = *(int*)m_ReadCursor; 79 if (size != 0) 80 { 81 m_ReadCursor += sizeof(int); 82 v = new string((char*)m_ReadCursor, 0, size); 83 m_ReadCursor += size * sizeof(char); 84 return true; 85 } 86 87 v = default; 88 return false; 89 } 90 91 /// <summary> 92 /// Clear the queue. 93 /// </summary> 94 public void Clear() 95 { 96 m_WriteCursor = m_BufferStart; 97 m_ReadCursor = m_BufferStart; 98 Count = 0; 99 UnsafeUtility.MemClear(m_BufferStart, m_BufferLength); 100 } 101 } 102 103 /// <summary> 104 /// Key Getter interface. 105 /// </summary> 106 /// <typeparam name="TValue">Value</typeparam> 107 /// <typeparam name="TKey">Key</typeparam> 108 public interface IKeyGetter<TValue, TKey> 109 { 110 /// <summary>Getter</summary> 111 /// <param name="v">The value</param> 112 /// <returns>The key</returns> 113 TKey Get(ref TValue v); 114 } 115 116 internal struct DefaultKeyGetter<T> : IKeyGetter<T, T> 117 { public T Get(ref T v) { return v; } } 118 119 // Note: this is a workaround needed to circumvent some AOT issues when building for xbox 120 internal struct UintKeyGetter : IKeyGetter<uint, uint> 121 { public uint Get(ref uint v) { return v; } } 122 internal struct UlongKeyGetter : IKeyGetter<ulong, ulong> 123 { public ulong Get(ref ulong v) { return v; } } 124 125 126 /// <summary> 127 /// Extension method to copy elements of a list into a buffer. 128 /// </summary> 129 /// <typeparam name="T">Type of the provided List.</typeparam> 130 /// <param name="list">Input List.</param> 131 /// <param name="dest">Destination buffer.</param> 132 /// <param name="count">Number of elements to copy.</param> 133 public static void CopyTo<T>(this List<T> list, void* dest, int count) 134 where T : struct 135 { 136 var c = Mathf.Min(count, list.Count); 137 for (int i = 0; i < c; ++i) 138 UnsafeUtility.WriteArrayElement<T>(dest, i, list[i]); 139 } 140 141 /// <summary> 142 /// Extension method to copy elements of an array into a buffer. 143 /// </summary> 144 /// <typeparam name="T">Type of the provided array.</typeparam> 145 /// <param name="list">Input List.</param> 146 /// <param name="dest">Destination buffer.</param> 147 /// <param name="count">Number of elements to copy.</param> 148 public static void CopyTo<T>(this T[] list, void* dest, int count) 149 where T : struct 150 { 151 var c = Mathf.Min(count, list.Length); 152 for (int i = 0; i < c; ++i) 153 UnsafeUtility.WriteArrayElement<T>(dest, i, list[i]); 154 } 155 156 private static void CalculateRadixParams(int radixBits, out int bitStates) 157 { 158#if DEVELOPMENT_BUILD || UNITY_EDITOR 159 if (radixBits != 2 && radixBits != 4 && radixBits != 8) 160 throw new Exception("Radix bits must be 2, 4 or 8 for uint radix sort."); 161#endif 162 bitStates = 1 << radixBits; 163 } 164 165 private static int CalculateRadixSupportSize(int bitStates, int arrayLength) 166 { 167 return bitStates * 3 + arrayLength; 168 } 169 170 private static unsafe void CalculateRadixSortSupportArrays( 171 int bitStates, int arrayLength, uint* supportArray, 172 out uint* bucketIndices, out uint* bucketSizes, out uint* bucketPrefix, out uint* arrayOutput) 173 { 174 bucketIndices = supportArray; 175 bucketSizes = bucketIndices + bitStates; 176 bucketPrefix = bucketSizes + bitStates; 177 arrayOutput = bucketPrefix + bitStates; 178 } 179 180 private static unsafe void MergeSort(uint* array, uint* support, int length) 181 { 182 for (int k = 1; k < length; k *= 2) 183 { 184 for (int left = 0; left + k < length; left += k * 2) 185 { 186 int right = left + k; 187 int rightend = right + k; 188 if (rightend > length) 189 rightend = length; 190 int m = left; 191 int i = left; 192 int j = right; 193 while (i < right && j < rightend) 194 { 195 if (array[i] <= array[j]) 196 { 197 support[m] = array[i++]; 198 } 199 else 200 { 201 support[m] = array[j++]; 202 } 203 m++; 204 } 205 while (i < right) 206 { 207 support[m] = array[i++]; 208 m++; 209 } 210 while (j < rightend) 211 { 212 support[m] = array[j++]; 213 m++; 214 } 215 for (m = left; m < rightend; m++) 216 { 217 array[m] = support[m]; 218 } 219 } 220 } 221 } 222 223 /// <summary> 224 /// Merge sort - non recursive 225 /// </summary> 226 /// <param name="arr">Array to sort.</param> 227 /// <param name="sortSize">Size of the array to sort. If greater than array capacity, it will get clamped.</param> 228 /// <param name="supportArray">Secondary array reference, used to store intermediate merge results.</param> 229 public static unsafe void MergeSort(uint[] arr, int sortSize, ref uint[] supportArray) 230 { 231 sortSize = Math.Min(sortSize, arr.Length); 232 if (arr == null || sortSize == 0) 233 return; 234 235 if (supportArray == null || supportArray.Length < sortSize) 236 supportArray = new uint[sortSize]; 237 238 fixed (uint* arrPtr = arr) 239 fixed (uint* supportPtr = supportArray) 240 CoreUnsafeUtils.MergeSort(arrPtr, supportPtr, sortSize); 241 } 242 243 /// <summary> 244 /// Merge sort - non recursive 245 /// </summary> 246 /// <param name="arr">Array to sort.</param> 247 /// <param name="sortSize">Size of the array to sort. If greater than array capacity, it will get clamped.</param> 248 /// <param name="supportArray">Secondary array reference, used to store intermediate merge results.</param> 249 public static unsafe void MergeSort(NativeArray<uint> arr, int sortSize, ref NativeArray<uint> supportArray) 250 { 251 sortSize = Math.Min(sortSize, arr.Length); 252 if (!arr.IsCreated || sortSize == 0) 253 return; 254 255 if (!supportArray.IsCreated || supportArray.Length < sortSize) 256 supportArray.ResizeArray(arr.Length); 257 258 CoreUnsafeUtils.MergeSort((uint*)arr.GetUnsafePtr(), (uint*)supportArray.GetUnsafePtr(), sortSize); 259 } 260 261 private static unsafe void InsertionSort(uint* arr, int length) 262 { 263 for (int i = 0; i < length; ++i) 264 { 265 for (int j = i; j >= 1; --j) 266 { 267 if (arr[j] >= arr[j - 1]) 268 break; 269 270 var tmp = arr[j]; 271 arr[j] = arr[j - 1]; 272 arr[j - 1] = tmp; 273 } 274 } 275 } 276 277 /// <summary> 278 /// Insertion sort 279 /// </summary> 280 /// <param name="arr">Array to sort.</param> 281 /// <param name="sortSize">Size of the array to sort. If greater than array capacity, it will get clamped.</param> 282 public static unsafe void InsertionSort(uint[] arr, int sortSize) 283 { 284 sortSize = Math.Min(arr.Length, sortSize); 285 if (arr == null || sortSize == 0) 286 return; 287 288 fixed (uint* ptr = arr) 289 CoreUnsafeUtils.InsertionSort(ptr, sortSize); 290 } 291 292 /// <summary> 293 /// Insertion sort 294 /// </summary> 295 /// <param name="arr">Array to sort.</param> 296 /// <param name="sortSize">Size of the array to sort. If greater than array capacity, it will get clamped.</param> 297 public static unsafe void InsertionSort(NativeArray<uint> arr, int sortSize) 298 { 299 sortSize = Math.Min(arr.Length, sortSize); 300 if (!arr.IsCreated || sortSize == 0) 301 return; 302 303 CoreUnsafeUtils.InsertionSort((uint*)arr.GetUnsafePtr(), sortSize); 304 } 305 306 private static unsafe void RadixSort(uint* array, uint* support, int radixBits, int bitStates, int length) 307 { 308 uint mask = (uint)(bitStates - 1); 309 CalculateRadixSortSupportArrays(bitStates, length, support, out uint* bucketIndices, out uint* bucketSizes, out uint* bucketPrefix, out uint* arrayOutput); 310 311 int buckets = (sizeof(uint) * 8) / radixBits; 312 uint* targetBuffer = arrayOutput; 313 uint* inputBuffer = array; 314 for (int b = 0; b < buckets; ++b) 315 { 316 int shift = b * radixBits; 317 for (int s = 0; s < 3 * bitStates; ++s) 318 bucketIndices[s] = 0;//bucketSizes and bucketPrefix get zeroed, since we walk 3x the bit states 319 320 for (int i = 0; i < length; ++i) 321 bucketSizes[((inputBuffer[i] >> shift) & mask)]++; 322 323 for (int s = 1; s < bitStates; ++s) 324 bucketPrefix[s] = bucketPrefix[s - 1] + bucketSizes[s - 1]; 325 326 for (int i = 0; i < length; ++i) 327 { 328 uint val = inputBuffer[i]; 329 uint bucket = (val >> shift) & mask; 330 targetBuffer[bucketPrefix[bucket] + bucketIndices[bucket]++] = val; 331 } 332 333 uint* tmp = inputBuffer; 334 inputBuffer = targetBuffer; 335 targetBuffer = tmp; 336 } 337 } 338 339 /// <summary> 340 /// Radix sort or bucket sort, stable and non in place. 341 /// </summary> 342 /// <param name="arr">Array to sort.</param> 343 /// <param name="sortSize">Size of the array to sort. If greater than array capacity, it will get clamped.</param> 344 /// <param name="supportArray">Array of uints that is used for support data. The algorithm will automatically allocate it if necessary.</param> 345 /// <param name="radixBits">Number of bits to use for each bucket. Can only be 8, 4 or 2.</param> 346 public static unsafe void RadixSort(uint[] arr, int sortSize, ref uint[] supportArray, int radixBits = 8) 347 { 348 sortSize = Math.Min(sortSize, arr.Length); 349 CalculateRadixParams(radixBits, out int bitStates); 350 if (arr == null || sortSize == 0) 351 return; 352 353 int supportSize = CalculateRadixSupportSize(bitStates, sortSize); 354 if (supportArray == null || supportArray.Length < supportSize) 355 supportArray = new uint[supportSize]; 356 357 fixed (uint* ptr = arr) 358 fixed (uint* supportArrayPtr = supportArray) 359 CoreUnsafeUtils.RadixSort(ptr, supportArrayPtr, radixBits, bitStates, sortSize); 360 } 361 362 /// <summary> 363 /// Radix sort or bucket sort, stable and non in place. 364 /// </summary> 365 /// <param name="array">Array to sort.</param> 366 /// <param name="sortSize">Size of the array to sort. If greater than array capacity, it will get clamped.</param> 367 /// <param name="supportArray">Array of uints that is used for support data. The algorithm will automatically allocate it if necessary.</param> 368 /// <param name="radixBits">Number of bits to use for each bucket. Can only be 8, 4 or 2.</param> 369 public static unsafe void RadixSort(NativeArray<uint> array, int sortSize, ref NativeArray<uint> supportArray, int radixBits = 8) 370 { 371 sortSize = Math.Min(sortSize, array.Length); 372 CalculateRadixParams(radixBits, out int bitStates); 373 if (!array.IsCreated || sortSize == 0) 374 return; 375 376 int supportSize = CalculateRadixSupportSize(bitStates, sortSize); 377 if (!supportArray.IsCreated || supportArray.Length < supportSize) 378 supportArray.ResizeArray((int)supportSize); 379 380 CoreUnsafeUtils.RadixSort((uint*)array.GetUnsafePtr(), (uint*)supportArray.GetUnsafePtr(), radixBits, bitStates, sortSize); 381 } 382 383 /// <summary> 384 /// Quick Sort 385 /// </summary> 386 /// <param name="arr">uint array.</param> 387 /// <param name="left">Left boundary.</param> 388 /// <param name="right">Left boundary.</param> 389 public static unsafe void QuickSort(uint[] arr, int left, int right) 390 { 391 fixed (uint* ptr = arr) 392 CoreUnsafeUtils.QuickSort<uint, uint, UintKeyGetter>(ptr, left, right); 393 } 394 395 /// <summary> 396 /// Quick Sort 397 /// </summary> 398 /// <param name="arr">ulong array.</param> 399 /// <param name="left">Left boundary.</param> 400 /// <param name="right">Left boundary.</param> 401 public static unsafe void QuickSort(ulong[] arr, int left, int right) 402 { 403 fixed (ulong* ptr = arr) 404 CoreUnsafeUtils.QuickSort<ulong, ulong, UlongKeyGetter>(ptr, left, right); 405 } 406 407 /// <summary> 408 /// Quick sort. 409 /// </summary> 410 /// <typeparam name="T">Type to compare.</typeparam> 411 /// <param name="count">Number of element.</param> 412 /// <param name="data">Buffer to sort.</param> 413 public static void QuickSort<T>(int count, void* data) 414 where T : struct, IComparable<T> 415 { 416 QuickSort<T, T, DefaultKeyGetter<T>>(data, 0, count - 1); 417 } 418 419 /// <summary> 420 /// Quick sort. 421 /// </summary> 422 /// <typeparam name="TValue">Value type.</typeparam> 423 /// <typeparam name="TKey">Key Type.</typeparam> 424 /// <typeparam name="TGetter">Getter type.</typeparam> 425 /// <param name="count">Number of element.</param> 426 /// <param name="data">Data to sort.</param> 427 public static void QuickSort<TValue, TKey, TGetter>(int count, void* data) 428 where TKey : struct, IComparable<TKey> 429 where TValue : struct 430 where TGetter : struct, IKeyGetter<TValue, TKey> 431 { 432 QuickSort<TValue, TKey, TGetter>(data, 0, count - 1); 433 } 434 435 /// <summary> 436 /// Quick sort. 437 /// </summary> 438 /// <typeparam name="TValue">Value type.</typeparam> 439 /// <typeparam name="TKey">Key Type.</typeparam> 440 /// <typeparam name="TGetter">Getter type.</typeparam> 441 /// <param name="data">Data to sort.</param> 442 /// <param name="left">Left boundary.</param> 443 /// <param name="right">Right boundary.</param> 444 public static void QuickSort<TValue, TKey, TGetter>(void* data, int left, int right) 445 where TKey : struct, IComparable<TKey> 446 where TValue : struct 447 where TGetter : struct, IKeyGetter<TValue, TKey> 448 { 449 // For Recursion 450 if (left < right) 451 { 452 int pivot = Partition<TValue, TKey, TGetter>(data, left, right); 453 454 if (pivot >= 1) 455 QuickSort<TValue, TKey, TGetter>(data, left, pivot); 456 457 if (pivot + 1 < right) 458 QuickSort<TValue, TKey, TGetter>(data, pivot + 1, right); 459 } 460 } 461 462 /// <summary> 463 /// Index of an element in a buffer. 464 /// </summary> 465 /// <typeparam name="T">Data type.</typeparam> 466 /// <param name="data">Data buffer.</param> 467 /// <param name="count">Number of elements.</param> 468 /// <param name="v">Element to test against.</param> 469 /// <returns>The first index of the provided element.</returns> 470 public static int IndexOf<T>(void* data, int count, T v) 471 where T : struct, IEquatable<T> 472 { 473 for (int i = 0; i < count; ++i) 474 { 475 if (UnsafeUtility.ReadArrayElement<T>(data, i).Equals(v)) 476 return i; 477 } 478 return -1; 479 } 480 481 /// <summary> 482 /// Compare hashes of two collections and provide 483 /// a list of indices <paramref name="removeIndices"/> to remove in <paramref name="oldHashes"/> 484 /// and a list of indices <paramref name="addIndices"/> to add in <paramref name="newHashes"/>. 485 /// 486 /// Assumes that <paramref name="newHashes"/> and <paramref name="oldHashes"/> are sorted. 487 /// </summary> 488 /// <typeparam name="TOldValue">Old value type.</typeparam> 489 /// <typeparam name="TOldGetter">Old getter type.</typeparam> 490 /// <typeparam name="TNewValue">New value type.</typeparam> 491 /// <typeparam name="TNewGetter">New getter type.</typeparam> 492 /// <param name="oldHashCount">Number of hashes in <paramref name="oldHashes"/>.</param> 493 /// <param name="oldHashes">Previous hashes to compare.</param> 494 /// <param name="newHashCount">Number of hashes in <paramref name="newHashes"/>.</param> 495 /// <param name="newHashes">New hashes to compare.</param> 496 /// <param name="addIndices">Indices of element to add in <paramref name="newHashes"/> will be written here.</param> 497 /// <param name="removeIndices">Indices of element to remove in <paramref name="oldHashes"/> will be written here.</param> 498 /// <param name="addCount">Number of elements to add will be written here.</param> 499 /// <param name="remCount">Number of elements to remove will be written here.</param> 500 /// <returns>The number of operations to perform (<paramref name="addCount"/><c> + </c><paramref name="remCount"/>)</returns> 501 502 public static int CompareHashes<TOldValue, TOldGetter, TNewValue, TNewGetter>( 503 int oldHashCount, void* oldHashes, 504 int newHashCount, void* newHashes, 505 // assume that the capacity of indices is >= max(oldHashCount, newHashCount) 506 int* addIndices, int* removeIndices, 507 out int addCount, out int remCount 508 ) 509 where TOldValue : struct 510 where TNewValue : struct 511 where TOldGetter : struct, IKeyGetter<TOldValue, Hash128> 512 where TNewGetter : struct, IKeyGetter<TNewValue, Hash128> 513 { 514 var oldGetter = new TOldGetter(); 515 var newGetter = new TNewGetter(); 516 517 addCount = 0; 518 remCount = 0; 519 // Check combined hashes 520 if (oldHashCount == newHashCount) 521 { 522 var oldHash = new Hash128(); 523 var newHash = new Hash128(); 524 CombineHashes<TOldValue, TOldGetter>(oldHashCount, oldHashes, &oldHash); 525 CombineHashes<TNewValue, TNewGetter>(newHashCount, newHashes, &newHash); 526 if (oldHash == newHash) 527 return 0; 528 } 529 530 var numOperations = 0; 531 532 var oldI = 0; 533 var newI = 0; 534 535 while (oldI < oldHashCount || newI < newHashCount) 536 { 537 // At the end of old array. 538 if (oldI == oldHashCount) 539 { 540 // No more hashes in old array. Add remaining entries from new array. 541 for (; newI < newHashCount; ++newI) 542 { 543 addIndices[addCount++] = newI; 544 ++numOperations; 545 } 546 continue; 547 } 548 549 // At end of new array. 550 if (newI == newHashCount) 551 { 552 // No more hashes in old array. Remove remaining entries from old array. 553 for (; oldI < oldHashCount; ++oldI) 554 { 555 removeIndices[remCount++] = oldI; 556 ++numOperations; 557 } 558 continue; 559 } 560 561 // Both arrays have data. 562 var newVal = UnsafeUtility.ReadArrayElement<TNewValue>(newHashes, newI); 563 var oldVal = UnsafeUtility.ReadArrayElement<TOldValue>(oldHashes, oldI); 564 var newKey = newGetter.Get(ref newVal); 565 var oldKey = oldGetter.Get(ref oldVal); 566 if (newKey == oldKey) 567 { 568 // Matching hash, skip. 569 ++newI; 570 ++oldI; 571 continue; 572 } 573 574 // Both arrays have data, but hashes do not match. 575 if (newKey < oldKey) 576 { 577 // oldIter is the greater hash. Push "add" jobs from the new array until reaching the oldIter hash. 578 while (newI < newHashCount && newKey < oldKey) 579 { 580 addIndices[addCount++] = newI; 581 ++newI; 582 ++numOperations; 583 newVal = UnsafeUtility.ReadArrayElement<TNewValue>(newHashes, newI); 584 newKey = newGetter.Get(ref newVal); 585 } 586 } 587 else 588 { 589 // newIter is the greater hash. Push "remove" jobs from the old array until reaching the newIter hash. 590 while (oldI < oldHashCount && oldKey < newKey) 591 { 592 removeIndices[remCount++] = oldI; 593 ++numOperations; 594 ++oldI; 595 } 596 } 597 } 598 599 return numOperations; 600 } 601 602 /// <summary> 603 /// Compare hashes. 604 /// </summary> 605 /// <param name="oldHashCount">Number of hashes in <paramref name="oldHashes"/>.</param> 606 /// <param name="oldHashes">Previous hashes to compare.</param> 607 /// <param name="newHashCount">Number of hashes in <paramref name="newHashes"/>.</param> 608 /// <param name="newHashes">New hashes to compare.</param> 609 /// <param name="addIndices">Indices of element to add in <paramref name="newHashes"/> will be written here.</param> 610 /// <param name="removeIndices">Indices of element to remove in <paramref name="oldHashes"/> will be written here.</param> 611 /// <param name="addCount">Number of elements to add will be written here.</param> 612 /// <param name="remCount">Number of elements to remove will be written here.</param> 613 /// <returns>The number of operations to perform (<paramref name="addCount"/><c> + </c><paramref name="remCount"/>)</returns> 614 public static int CompareHashes( 615 int oldHashCount, Hash128* oldHashes, 616 int newHashCount, Hash128* newHashes, 617 // assume that the capacity of indices is >= max(oldHashCount, newHashCount) 618 int* addIndices, int* removeIndices, 619 out int addCount, out int remCount 620 ) 621 { 622 return CompareHashes<Hash128, DefaultKeyGetter<Hash128>, Hash128, DefaultKeyGetter<Hash128>>( 623 oldHashCount, oldHashes, 624 newHashCount, newHashes, 625 addIndices, removeIndices, 626 out addCount, out remCount 627 ); 628 } 629 630 /// <summary>Combine all of the hashes of a collection of hashes.</summary> 631 /// <typeparam name="TValue">Value type.</typeparam> 632 /// <typeparam name="TGetter">Getter type.</typeparam> 633 /// <param name="count">Number of hash to combine.</param> 634 /// <param name="hashes">Hashes to combine.</param> 635 /// <param name="outHash">Hash to update.</param> 636 public static void CombineHashes<TValue, TGetter>(int count, void* hashes, Hash128* outHash) 637 where TValue : struct 638 where TGetter : struct, IKeyGetter<TValue, Hash128> 639 { 640 var getter = new TGetter(); 641 for (int i = 0; i < count; ++i) 642 { 643 var v = UnsafeUtility.ReadArrayElement<TValue>(hashes, i); 644 var h = getter.Get(ref v); 645 HashUtilities.AppendHash(ref h, ref *outHash); 646 } 647 } 648 649 /// <summary> 650 /// Combine hashes. 651 /// </summary> 652 /// <param name="count">Number of hash to combine.</param> 653 /// <param name="hashes">Hashes to combine.</param> 654 /// <param name="outHash">Hash to update.</param> 655 public static void CombineHashes(int count, Hash128* hashes, Hash128* outHash) 656 { 657 CombineHashes<Hash128, DefaultKeyGetter<Hash128>>(count, hashes, outHash); 658 } 659 660 // Just a sort function that doesn't allocate memory 661 // Note: Should be replace by a radix sort for positive integer 662 static int Partition<TValue, TKey, TGetter>(void* data, int left, int right) 663 where TKey : struct, IComparable<TKey> 664 where TValue : struct 665 where TGetter : struct, IKeyGetter<TValue, TKey> 666 { 667 var getter = default(TGetter); 668 var pivotvalue = UnsafeUtility.ReadArrayElement<TValue>(data, left); 669 var pivot = getter.Get(ref pivotvalue); 670 671 --left; 672 ++right; 673 while (true) 674 { 675 var c = 0; 676 var lvalue = default(TValue); 677 var lkey = default(TKey); 678 do 679 { 680 ++left; 681 lvalue = UnsafeUtility.ReadArrayElement<TValue>(data, left); 682 lkey = getter.Get(ref lvalue); 683 c = lkey.CompareTo(pivot); 684 } 685 while (c < 0); 686 687 var rvalue = default(TValue); 688 var rkey = default(TKey); 689 do 690 { 691 --right; 692 rvalue = UnsafeUtility.ReadArrayElement<TValue>(data, right); 693 rkey = getter.Get(ref rvalue); 694 c = rkey.CompareTo(pivot); 695 } 696 while (c > 0); 697 698 if (left < right) 699 { 700 UnsafeUtility.WriteArrayElement(data, right, lvalue); 701 UnsafeUtility.WriteArrayElement(data, left, rvalue); 702 } 703 else 704 { 705 return right; 706 } 707 } 708 } 709 710 /// <summary> 711 /// Checks for duplicates in an array. 712 /// </summary> 713 /// <param name="arr">Input array.</param> 714 /// <returns>True if there is any duplicate in the input array.</returns> 715 public static unsafe bool HaveDuplicates(int[] arr) 716 { 717 int* copy = stackalloc int[arr.Length]; 718 arr.CopyTo<int>(copy, arr.Length); 719 QuickSort<int>(arr.Length, copy); 720 for (int i = arr.Length - 1; i > 0; --i) 721 { 722 if (UnsafeUtility.ReadArrayElement<int>(copy, i).CompareTo(UnsafeUtility.ReadArrayElement<int>(copy, i - 1)) == 0) 723 { 724 return true; 725 } 726 } 727 return false; 728 } 729 } 730}