A game about forced loneliness, made by TACStudios
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}