A game about forced loneliness, made by TACStudios
1using System;
2using System.Collections.Generic;
3using System.Diagnostics;
4using Unity.Burst;
5using Unity.Collections.LowLevel.Unsafe;
6using Unity.Jobs;
7using Unity.Jobs.LowLevel.Unsafe;
8using Unity.Mathematics;
9
10namespace Unity.Collections
11{
12 /// <summary>
13 /// Extension methods for sorting collections.
14 /// </summary>
15 [GenerateTestsForBurstCompatibility]
16 public static class NativeSortExtension
17 {
18 /// <summary>
19 /// A comparer that uses IComparable.CompareTo(). For primitive types, this is an ascending sort.
20 /// </summary>
21 /// <typeparam name="T">Source type of elements</typeparam>
22 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
23 public struct DefaultComparer<T> : IComparer<T> where T : IComparable<T>
24 {
25 /// <summary>
26 /// Compares two values.
27 /// </summary>
28 /// <param name="x">First value to compare.</param>
29 /// <param name="y">Second value to compare.</param>
30 /// <returns>A signed integer that denotes the relative values of `x` and `y`:
31 /// 0 if they're equal, negative if `x < y`, and positive if `x > y`.</returns>
32 public int Compare(T x, T y) => x.CompareTo(y);
33 }
34
35 /// <summary>
36 /// Sorts an array in ascending order.
37 /// </summary>
38 /// <typeparam name="T">The type of the elements.</typeparam>
39 /// <param name="array">The array to sort.</param>
40 /// <param name="length">The number of elements to sort in the array.
41 /// Indexes greater than or equal to `length` won't be included in the sort.</param>
42 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
43 public unsafe static void Sort<T>(T* array, int length)
44 where T : unmanaged, IComparable<T>
45 {
46 IntroSort<T, DefaultComparer<T>>(array, length, new DefaultComparer<T>());
47 }
48
49 /// <summary>
50 /// Sorts an array using a custom comparison.
51 /// </summary>
52 /// <typeparam name="T">The type of the elements.</typeparam>
53 /// <typeparam name="U">The type of the comparer.</typeparam>
54 /// <param name="array">The array to sort.</param>
55 /// <param name="length">The number of elements to sort in the array.
56 /// Indexes greater than or equal to `length` won't be included in the sort.</param>
57 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
58 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
59 public unsafe static void Sort<T, U>(T* array, int length, U comp)
60 where T : unmanaged
61 where U : IComparer<T>
62 {
63 IntroSort<T, U>(array, length, comp);
64 }
65
66 /// <summary>
67 /// Returns a job which will sort an array in ascending order.
68 /// </summary>
69 /// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
70 /// <typeparam name="T">The type of the elements.</typeparam>
71 /// <param name="array">The array to sort.</param>
72 /// <param name="length">The number of elements to sort in the array.
73 /// Indexes greater than or equal to `length` won't be included in the sort.</param>
74 /// <returns>A job for sorting the array.</returns>
75 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
76 public unsafe static SortJob<T, DefaultComparer<T>> SortJob<T>(T* array, int length)
77 where T : unmanaged, IComparable<T>
78 {
79 return new SortJob<T, DefaultComparer<T>> { Data = array, Length = length, Comp = new DefaultComparer<T>() };
80 }
81
82 /// <summary>
83 /// Returns a job which will sort an array using a custom comparison.
84 /// </summary>
85 /// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
86 /// <typeparam name="T">The type of the elements.</typeparam>
87 /// <typeparam name="U">The type of the comparer.</typeparam>
88 /// <param name="array">The array to sort.</param>
89 /// <param name="length">The number of elements to sort in the array.
90 /// Indexes greater than or equal to `length` won't be included in the sort.</param>
91 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
92 /// <returns>A job for sorting the array.</returns>
93 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
94 public unsafe static SortJob<T, U> SortJob<T, U>(T* array, int length, U comp)
95 where T : unmanaged
96 where U : IComparer<T>
97 {
98 CheckComparer(array, length, comp);
99 return new SortJob<T, U>() { Data = array, Length = length, Comp = comp };
100 }
101
102 /// <summary>
103 /// Finds a value in a sorted array by binary search.
104 /// </summary>
105 /// <remarks>If the array is not sorted, the value might not be found, even if it's present in the array.</remarks>
106 /// <typeparam name="T">The type of the elements.</typeparam>
107 /// <param name="ptr">The array to search.</param>
108 /// <param name="value">The value to locate.</param>
109 /// <param name="length">The number of elements to search. Indexes greater than or equal to `length` won't be searched.</param>
110 /// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
111 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
112 public unsafe static int BinarySearch<T>(T* ptr, int length, T value)
113 where T : unmanaged, IComparable<T>
114 {
115 return BinarySearch(ptr, length, value, new DefaultComparer<T>());
116 }
117
118 /// <summary>
119 /// Finds a value in a sorted array by binary search using a custom comparison.
120 /// </summary>
121 /// <remarks>If the array is not sorted, the value might not be found, even if it's present in the array.</remarks>
122 /// <typeparam name="T">The type of the elements.</typeparam>
123 /// <typeparam name="U">The type of the comparer.</typeparam>
124 /// <param name="ptr">The array to search.</param>
125 /// <param name="value">The value to locate.</param>
126 /// <param name="length">The number of elements to search. Indexes greater than or equal to `length` won't be searched.</param>
127 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
128 /// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
129 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
130 public unsafe static int BinarySearch<T, U>(T* ptr, int length, T value, U comp)
131 where T : unmanaged
132 where U : IComparer<T>
133 {
134 CheckComparer(ptr, length, comp);
135 var offset = 0;
136
137 for (var l = length; l != 0; l >>= 1)
138 {
139 var idx = offset + (l >> 1);
140 var curr = ptr[idx];
141 var r = comp.Compare(value, curr);
142 if (r == 0)
143 {
144 return idx;
145 }
146
147 if (r > 0)
148 {
149 offset = idx + 1;
150 --l;
151 }
152 }
153
154 return ~offset;
155 }
156
157 /// <summary>
158 /// Sorts this array in ascending order.
159 /// </summary>
160 /// <typeparam name="T">The type of the elements.</typeparam>
161 /// <param name="array">The array to sort.</param>
162 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
163 public unsafe static void Sort<T>(this NativeArray<T> array)
164 where T : unmanaged, IComparable<T>
165 {
166 IntroSortStruct<T, DefaultComparer<T>>(array.GetUnsafePtr(), array.Length, new DefaultComparer<T>());
167 }
168
169 /// <summary>
170 /// Sorts this array using a custom comparison.
171 /// </summary>
172 /// <typeparam name="T">The type of the elements.</typeparam>
173 /// <typeparam name="U">The type of the comparer.</typeparam>
174 /// <param name="array">The array to sort.</param>
175 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
176 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
177 public unsafe static void Sort<T, U>(this NativeArray<T> array, U comp)
178 where T : unmanaged
179 where U : IComparer<T>
180 {
181 var ptr = (T*)array.GetUnsafePtr();
182 var len = array.Length;
183 CheckComparer(ptr, len, comp);
184 IntroSortStruct<T, U>(ptr, len, comp);
185 }
186
187 /// <summary>
188 /// Returns a job which will sort this array in ascending order.
189 /// </summary>
190 /// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
191 /// <typeparam name="T">The type of the elements.</typeparam>
192 /// <param name="array">The array to sort.</param>
193 /// <returns>A job for sorting this array.</returns>
194 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
195 public unsafe static SortJob<T, DefaultComparer<T>> SortJob<T>(this NativeArray<T> array)
196 where T : unmanaged, IComparable<T>
197 {
198 return SortJob((T*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array), array.Length, new DefaultComparer<T>());
199 }
200
201 /// <summary>
202 /// Returns a job which will sort this array using a custom comparison.
203 /// </summary>
204 /// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
205 /// <typeparam name="T">The type of the elements.</typeparam>
206 /// <typeparam name="U">The type of the comparer.</typeparam>
207 /// <param name="array">The array to sort.</param>
208 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
209 /// <returns>A job for sorting the array.</returns>
210 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
211 public unsafe static SortJob<T, U> SortJob<T, U>(this NativeArray<T> array, U comp)
212 where T : unmanaged
213 where U : IComparer<T>
214 {
215 var ptr = (T*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array);
216 var len = array.Length;
217 CheckComparer(ptr, len, comp);
218
219 return new SortJob<T, U>
220 {
221 Data = ptr,
222 Length = len,
223 Comp = comp
224 };
225 }
226
227 /// <summary>
228 /// Finds a value in this sorted array by binary search.
229 /// </summary>
230 /// <remarks>If the array is not sorted, the value might not be found, even if it's present in this array.</remarks>
231 /// <typeparam name="T">The type of the elements.</typeparam>
232 /// <param name="array">The array to search.</param>
233 /// <param name="value">The value to locate.</param>
234 /// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
235 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
236 public static int BinarySearch<T>(this NativeArray<T> array, T value)
237 where T : unmanaged, IComparable<T>
238 {
239 return array.BinarySearch(value, new DefaultComparer<T>());
240 }
241
242 /// <summary>
243 /// Finds a value in this sorted array by binary search using a custom comparison.
244 /// </summary>
245 /// <remarks>If the array is not sorted, the value might not be found, even if it's present in this array.
246 /// </remarks>
247 /// <typeparam name="T">The type of the elements.</typeparam>
248 /// <typeparam name="U">The comparer type.</typeparam>
249 /// <param name="array">The array to search.</param>
250 /// <param name="value">The value to locate.</param>
251 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
252 /// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
253 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
254 public unsafe static int BinarySearch<T, U>(this NativeArray<T> array, T value, U comp)
255 where T : unmanaged
256 where U : IComparer<T>
257 {
258 return BinarySearch((T*)NativeArrayUnsafeUtility.GetUnsafeReadOnlyPtr(array), array.Length, value, comp);
259 }
260
261 /// <summary>
262 /// Finds a value in this sorted array by binary search.
263 /// </summary>
264 /// <remarks>If the array is not sorted, the value might not be found, even if it's present in this array.</remarks>
265 /// <typeparam name="T">The type of the elements.</typeparam>
266 /// <param name="array">The array to search.</param>
267 /// <param name="value">The value to locate.</param>
268 /// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
269 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
270 public static int BinarySearch<T>(this NativeArray<T>.ReadOnly array, T value)
271 where T : unmanaged, IComparable<T>
272 {
273 return array.BinarySearch(value, new DefaultComparer<T>());
274 }
275
276 /// <summary>
277 /// Finds a value in this sorted array by binary search using a custom comparison.
278 /// </summary>
279 /// <remarks>If the array is not sorted, the value might not be found, even if it's present in this array.
280 /// </remarks>
281 /// <typeparam name="T">The type of the elements.</typeparam>
282 /// <typeparam name="U">The comparer type.</typeparam>
283 /// <param name="array">The array to search.</param>
284 /// <param name="value">The value to locate.</param>
285 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
286 /// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
287 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
288 public unsafe static int BinarySearch<T, U>(this NativeArray<T>.ReadOnly array, T value, U comp)
289 where T : unmanaged
290 where U : IComparer<T>
291 {
292 return BinarySearch((T*)NativeArrayUnsafeUtility.GetUnsafeReadOnlyPtr(array), array.Length, value, comp);
293 }
294
295 /// <summary>
296 /// Sorts this list in ascending order.
297 /// </summary>
298 /// <typeparam name="T">The type of the elements.</typeparam>
299 /// <param name="list">The list to sort.</param>
300 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
301 public unsafe static void Sort<T>(this NativeList<T> list)
302 where T : unmanaged, IComparable<T>
303 {
304 list.Sort(new DefaultComparer<T>());
305 }
306
307 /// <summary>
308 /// Sorts this list using a custom comparison.
309 /// </summary>
310 /// <typeparam name="T">The type of the elements.</typeparam>
311 /// <typeparam name="U">The type of the comparer.</typeparam>
312 /// <param name="list">The list to sort.</param>
313 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
314 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
315 public unsafe static void Sort<T, U>(this NativeList<T> list, U comp)
316 where T : unmanaged
317 where U : IComparer<T>
318 {
319 IntroSort<T, U>(list.GetUnsafePtr(), list.Length, comp);
320 }
321
322 /// <summary>
323 /// Returns a job which will sort this list in ascending order.
324 /// </summary>
325 /// <remarks>When `NativeList.Length` is not known at scheduling time use `SortJobDefer` instead.
326 /// This method does not schedule the job. Scheduling the job is left to you.</remarks>
327 /// <typeparam name="T">The type of the elements.</typeparam>
328 /// <param name="list">The list to sort.</param>
329 /// <returns>A job for sorting this list.</returns>
330 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
331 public unsafe static SortJob<T, DefaultComparer<T>> SortJob<T>(this NativeList<T> list)
332 where T : unmanaged, IComparable<T>
333 {
334 return SortJob(list, new DefaultComparer<T>());
335 }
336
337 /// <summary>
338 /// Returns a job which will sort this list using a custom comparison.
339 /// </summary>
340 /// <remarks>When `NativeList.Length` is not known at scheduling time use `SortJobDefer` instead.
341 /// This method does not schedule the job. Scheduling the job is left to you.</remarks>
342 /// <typeparam name="T">The type of the elements.</typeparam>
343 /// <typeparam name="U">The type of the comparer.</typeparam>
344 /// <param name="list">The list to sort.</param>
345 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
346 /// <returns>A job for sorting this list.</returns>
347 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
348 public unsafe static SortJob<T, U> SortJob<T, U>(this NativeList<T> list, U comp)
349 where T : unmanaged
350 where U : IComparer<T>
351 {
352 return SortJob(list.GetUnsafePtr(), list.Length, comp);
353 }
354
355 /// <summary>
356 /// Returns a job which will sort this list in ascending order.
357 /// </summary>
358 /// <remarks>`SortJobDefer` is intended for use when `NativeList.Length` is not known at scheduling time,
359 /// and it depends on completion of previosly scheduled job(s).
360 /// This method does not schedule the job. Scheduling the job is left to you.</remarks>
361 /// <typeparam name="T">The type of the elements.</typeparam>
362 /// <param name="list">The list to sort.</param>
363 /// <returns>A job for sorting this list.</returns>
364 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
365 public unsafe static SortJobDefer<T, DefaultComparer<T>> SortJobDefer<T>(this NativeList<T> list)
366 where T : unmanaged, IComparable<T>
367 {
368 return SortJobDefer(list, new DefaultComparer<T>());
369 }
370
371 /// <summary>
372 /// Returns a job which will sort this list using a custom comparison.
373 /// </summary>
374 /// <remarks>`SortJobDefer` is intended for use when `NativeList.Length` is not known at scheduling time,
375 /// and it depends on completion of previosly scheduled job(s).
376 /// This method does not schedule the job. Scheduling the job is left to you.</remarks>
377 /// <typeparam name="T">The type of the elements.</typeparam>
378 /// <typeparam name="U">The type of the comparer.</typeparam>
379 /// <param name="list">The list to sort.</param>
380 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
381 /// <returns>A job for sorting this list.</returns>
382 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
383 public unsafe static SortJobDefer<T, U> SortJobDefer<T, U>(this NativeList<T> list, U comp)
384 where T : unmanaged
385 where U : IComparer<T>
386 {
387 return new SortJobDefer<T, U> { Data = list, Comp = comp };
388 }
389
390 /// <summary>
391 /// Finds a value in this sorted list by binary search.
392 /// </summary>
393 /// <remarks>If this list is not sorted, the value might not be found, even if it's present in this list.</remarks>
394 /// <typeparam name="T">The type of the elements.</typeparam>
395 /// <param name="list">The list to search.</param>
396 /// <param name="value">The value to locate.</param>
397 /// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
398 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
399 public static int BinarySearch<T>(this NativeList<T> list, T value)
400 where T : unmanaged, IComparable<T>
401 {
402 return list.AsReadOnly().BinarySearch(value, new DefaultComparer<T>());
403 }
404
405 /// <summary>
406 /// Finds a value in this sorted list by binary search using a custom comparison.
407 /// </summary>
408 /// <remarks>If this list is not sorted, the value may not be found, even if it's present in this list.</remarks>
409 /// <typeparam name="T">The type of the elements.</typeparam>
410 /// <typeparam name="U">The type of the comparer.</typeparam>
411 /// <param name="list">The list to search.</param>
412 /// <param name="value">The value to locate.</param>
413 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
414 /// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
415 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
416 public unsafe static int BinarySearch<T, U>(this NativeList<T> list, T value, U comp)
417 where T : unmanaged
418 where U : IComparer<T>
419 {
420 return list.AsReadOnly().BinarySearch(value, comp);
421 }
422
423 /// <summary>
424 /// Sorts this list in ascending order.
425 /// </summary>
426 /// <typeparam name="T">The type of the elements.</typeparam>
427 /// <param name="list">The list to sort.</param>
428 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
429 public unsafe static void Sort<T>(this UnsafeList<T> list) where T : unmanaged, IComparable<T>
430 {
431 list.Sort(new DefaultComparer<T>());
432 }
433
434 /// <summary>
435 /// Sorts the list using a custom comparison.
436 /// </summary>
437 /// <typeparam name="T">The type of the elements.</typeparam>
438 /// <typeparam name="U">The type of the comparer.</typeparam>
439 /// <param name="list">The list to sort.</param>
440 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
441 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
442 public unsafe static void Sort<T, U>(this UnsafeList<T> list, U comp)
443 where T : unmanaged
444 where U : IComparer<T>
445 {
446 IntroSort<T, U>(list.Ptr, list.Length, comp);
447 }
448
449 /// <summary>
450 /// Returns a job which will sort this list in ascending order.
451 /// </summary>
452 /// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
453 /// <typeparam name="T">The type of the elements.</typeparam>
454 /// <param name="list">The list to sort.</param>
455 /// <returns>A job for sorting this list.</returns>
456 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
457 public unsafe static SortJob<T, DefaultComparer<T>> SortJob<T>(this UnsafeList<T> list)
458 where T : unmanaged, IComparable<T>
459 {
460 return SortJob(list.Ptr, list.Length, new DefaultComparer<T>());
461 }
462
463 /// <summary>
464 /// Returns a job which will sort this list using a custom comparison.
465 /// </summary>
466 /// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
467 /// <typeparam name="T">The type of the elements.</typeparam>
468 /// <typeparam name="U">The type of the comparer.</typeparam>
469 /// <param name="list">The list to sort.</param>
470 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
471 /// <returns>A job for sorting this list.</returns>
472 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
473 public unsafe static SortJob<T, U> SortJob<T, U>(this UnsafeList<T> list, U comp)
474 where T : unmanaged
475 where U : IComparer<T>
476 {
477 return SortJob(list.Ptr, list.Length, comp);
478 }
479
480 /// <summary>
481 /// Finds a value in this sorted list by binary search.
482 /// </summary>
483 /// <remarks>If this list is not sorted, the value might not be found, even if it's present in this list.</remarks>
484 /// <typeparam name="T">The type of the elements.</typeparam>
485 /// <param name="list">The list to search.</param>
486 /// <param name="value">The value to locate.</param>
487 /// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
488 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
489 public static int BinarySearch<T>(this UnsafeList<T> list, T value)
490 where T : unmanaged, IComparable<T>
491 {
492 return list.BinarySearch(value, new DefaultComparer<T>());
493 }
494
495 /// <summary>
496 /// Finds a value in this sorted list by binary search using a custom comparison.
497 /// </summary>
498 /// <remarks>If this list is not sorted, the value might not be found, even if it's present in this list.</remarks>
499 /// <typeparam name="T">The type of the elements.</typeparam>
500 /// <typeparam name="U">The type of the comparer.</typeparam>
501 /// <param name="list">The list to search.</param>
502 /// <param name="value">The value to locate.</param>
503 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
504 /// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
505 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
506 public unsafe static int BinarySearch<T, U>(this UnsafeList<T> list, T value, U comp)
507 where T : unmanaged
508 where U : IComparer<T>
509 {
510 return BinarySearch(list.Ptr, list.Length, value, comp);
511 }
512
513 /// <summary>
514 /// Sorts this slice in ascending order.
515 /// </summary>
516 /// <typeparam name="T">The type of the elements.</typeparam>
517 /// <param name="slice">The slice to sort.</param>
518 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
519 public unsafe static void Sort<T>(this NativeSlice<T> slice)
520 where T : unmanaged, IComparable<T>
521 {
522 slice.Sort(new DefaultComparer<T>());
523 }
524
525 /// <summary>
526 /// Sorts this slice using a custom comparison.
527 /// </summary>
528 /// <typeparam name="T">The type of the elements.</typeparam>
529 /// <typeparam name="U">The type of the comparer.</typeparam>
530 /// <param name="slice">The slice to sort.</param>
531 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
532 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
533 public unsafe static void Sort<T, U>(this NativeSlice<T> slice, U comp)
534 where T : unmanaged
535 where U : IComparer<T>
536 {
537 var ptr = (T*)slice.GetUnsafePtr();
538 var len = slice.Length;
539 CheckComparer(ptr, len, comp);
540
541 CheckStrideMatchesSize<T>(slice.Stride);
542 IntroSortStruct<T, U>(ptr, len, comp);
543 }
544
545 /// <summary>
546 /// Returns a job which will sort this slice in ascending order.
547 /// </summary>
548 /// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
549 /// <typeparam name="T">The type of the elements.</typeparam>
550 /// <param name="slice">The slice to sort.</param>
551 /// <returns>A job for sorting this slice.</returns>
552 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
553 public unsafe static SortJob<T, DefaultComparer<T>> SortJob<T>(this NativeSlice<T> slice)
554 where T : unmanaged, IComparable<T>
555 {
556 CheckStrideMatchesSize<T>(slice.Stride);
557 return SortJob((T*)slice.GetUnsafePtr(), slice.Length, new DefaultComparer<T>());
558 }
559
560 /// <summary>
561 /// Returns a job which will sort this slice using a custom comparison.
562 /// </summary>
563 /// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
564 /// <typeparam name="T">The type of the elements.</typeparam>
565 /// <typeparam name="U">The type of the comparer.</typeparam>
566 /// <param name="slice">The slice to sort.</param>
567 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
568 /// <returns>A job for sorting this slice.</returns>
569 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
570 public unsafe static SortJob<T, U> SortJob<T, U>(this NativeSlice<T> slice, U comp)
571 where T : unmanaged
572 where U : IComparer<T>
573 {
574 CheckStrideMatchesSize<T>(slice.Stride);
575 return SortJob((T*)slice.GetUnsafePtr(), slice.Length, comp);
576 }
577
578 /// <summary>
579 /// Finds a value in this sorted slice by binary search.
580 /// </summary>
581 /// <remarks>If this slice is not sorted, the value might not be found, even if it's present in this slice.</remarks>
582 /// <typeparam name="T">The type of the elements.</typeparam>
583 /// <param name="slice">The slice to search.</param>
584 /// <param name="value">The value to locate.</param>
585 /// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
586 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int) })]
587 public static int BinarySearch<T>(this NativeSlice<T> slice, T value)
588 where T : unmanaged, IComparable<T>
589 {
590 return slice.BinarySearch(value, new DefaultComparer<T>());
591 }
592
593 /// <summary>
594 /// Finds a value in this sorted slice by binary search using a custom comparison.
595 /// </summary>
596 /// <remarks>If this slice is not sorted, the value might not be found, even if it's present in this slice.</remarks>
597 /// <typeparam name="T">The type of the elements.</typeparam>
598 /// <typeparam name="U">The type of the comparer.</typeparam>
599 /// <param name="slice">The slice to search.</param>
600 /// <param name="value">The value to locate.</param>
601 /// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
602 /// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
603 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
604 public unsafe static int BinarySearch<T, U>(this NativeSlice<T> slice, T value, U comp)
605 where T : unmanaged
606 where U : IComparer<T>
607 {
608 return BinarySearch((T*)slice.GetUnsafeReadOnlyPtr(), slice.Length, value, comp);
609 }
610
611 /// -- Internals
612
613 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
614 unsafe internal static void IntroSort<T, U>(void* array, int length, U comp)
615 where T : unmanaged
616 where U : IComparer<T>
617 {
618 CheckComparer((T*)array, length, comp);
619 IntroSort_R<T, U>(array, 0, length - 1, 2 * CollectionHelper.Log2Floor(length), comp);
620 }
621
622 const int k_IntrosortSizeThreshold = 16;
623
624 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
625 unsafe internal static void IntroSort_R<T, U>(void* array, int lo, int hi, int depth, U comp)
626 where T : unmanaged
627 where U : IComparer<T>
628 {
629 while (hi > lo)
630 {
631 int partitionSize = hi - lo + 1;
632 if (partitionSize <= k_IntrosortSizeThreshold)
633 {
634 if (partitionSize == 1)
635 {
636 return;
637 }
638 if (partitionSize == 2)
639 {
640 SwapIfGreaterWithItems<T, U>(array, lo, hi, comp);
641 return;
642 }
643 if (partitionSize == 3)
644 {
645 SwapIfGreaterWithItems<T, U>(array, lo, hi - 1, comp);
646 SwapIfGreaterWithItems<T, U>(array, lo, hi, comp);
647 SwapIfGreaterWithItems<T, U>(array, hi - 1, hi, comp);
648 return;
649 }
650
651 InsertionSort<T, U>(array, lo, hi, comp);
652 return;
653 }
654
655 if (depth == 0)
656 {
657 HeapSort<T, U>(array, lo, hi, comp);
658 return;
659 }
660 depth--;
661
662 int p = Partition<T, U>(array, lo, hi, comp);
663 IntroSort_R<T, U>(array, p + 1, hi, depth, comp);
664 hi = p - 1;
665 }
666 }
667
668 unsafe static void InsertionSort<T, U>(void* array, int lo, int hi, U comp)
669 where T : unmanaged
670 where U : IComparer<T>
671 {
672 int i, j;
673 T t;
674 for (i = lo; i < hi; i++)
675 {
676 j = i;
677
678 t = UnsafeUtility.ReadArrayElement<T>(array, i + 1);
679 while (j >= lo && comp.Compare(t, UnsafeUtility.ReadArrayElement<T>(array, j)) < 0)
680 {
681 UnsafeUtility.WriteArrayElement(array, j + 1, UnsafeUtility.ReadArrayElement<T>(array, j));
682 j--;
683 }
684
685 UnsafeUtility.WriteArrayElement(array, j + 1, t);
686 }
687 }
688
689 unsafe static int Partition<T, U>(void* array, int lo, int hi, U comp)
690 where T : unmanaged
691 where U : IComparer<T>
692 {
693 int mid = lo + ((hi - lo) / 2);
694 SwapIfGreaterWithItems<T, U>(array, lo, mid, comp);
695 SwapIfGreaterWithItems<T, U>(array, lo, hi, comp);
696 SwapIfGreaterWithItems<T, U>(array, mid, hi, comp);
697
698 T pivot = UnsafeUtility.ReadArrayElement<T>(array, mid);
699 Swap<T>(array, mid, hi - 1);
700 int left = lo, right = hi - 1;
701
702 while (left < right)
703 {
704 while (left < hi && comp.Compare(pivot, UnsafeUtility.ReadArrayElement<T>(array, ++left)) > 0)
705 {
706 }
707
708 while (right > left && comp.Compare(pivot, UnsafeUtility.ReadArrayElement<T>(array, --right)) < 0)
709 {
710 }
711
712 if (left >= right)
713 break;
714
715 Swap<T>(array, left, right);
716 }
717
718 Swap<T>(array, left, (hi - 1));
719 return left;
720 }
721
722 unsafe static void HeapSort<T, U>(void* array, int lo, int hi, U comp)
723 where T : unmanaged
724 where U : IComparer<T>
725 {
726 int n = hi - lo + 1;
727
728 for (int i = n / 2; i >= 1; i--)
729 {
730 Heapify<T, U>(array, i, n, lo, comp);
731 }
732
733 for (int i = n; i > 1; i--)
734 {
735 Swap<T>(array, lo, lo + i - 1);
736 Heapify<T, U>(array, 1, i - 1, lo, comp);
737 }
738 }
739
740 unsafe static void Heapify<T, U>(void* array, int i, int n, int lo, U comp)
741 where T : unmanaged
742 where U : IComparer<T>
743 {
744 T val = UnsafeUtility.ReadArrayElement<T>(array, lo + i - 1);
745 int child;
746 while (i <= n / 2)
747 {
748 child = 2 * i;
749
750 if (child < n && (comp.Compare(UnsafeUtility.ReadArrayElement<T>(array, lo + child - 1), UnsafeUtility.ReadArrayElement<T>(array, (lo + child))) < 0))
751 {
752 child++;
753 }
754
755 if (comp.Compare(UnsafeUtility.ReadArrayElement<T>(array, (lo + child - 1)), val) < 0)
756 {
757 break;
758 }
759
760 UnsafeUtility.WriteArrayElement(array, lo + i - 1, UnsafeUtility.ReadArrayElement<T>(array, lo + child - 1));
761 i = child;
762 }
763
764 UnsafeUtility.WriteArrayElement(array, lo + i - 1, val);
765 }
766
767 unsafe static void Swap<T>(void* array, int lhs, int rhs) where T : unmanaged
768 {
769 T val = UnsafeUtility.ReadArrayElement<T>(array, lhs);
770 UnsafeUtility.WriteArrayElement(array, lhs, UnsafeUtility.ReadArrayElement<T>(array, rhs));
771 UnsafeUtility.WriteArrayElement(array, rhs, val);
772 }
773
774 unsafe static void SwapIfGreaterWithItems<T, U>(void* array, int lhs, int rhs, U comp)
775 where T : unmanaged
776 where U : IComparer<T>
777 {
778 if (lhs != rhs)
779 {
780 if (comp.Compare(UnsafeUtility.ReadArrayElement<T>(array, lhs), UnsafeUtility.ReadArrayElement<T>(array, rhs)) > 0)
781 {
782 Swap<T>(array, lhs, rhs);
783 }
784 }
785 }
786
787 unsafe static void IntroSortStruct<T, U>(void* array, int length, U comp)
788 where T : unmanaged
789 where U : IComparer<T>
790 {
791 IntroSortStruct_R<T, U>(array, 0, length - 1, 2 * CollectionHelper.Log2Floor(length), comp);
792 }
793
794 unsafe static void IntroSortStruct_R<T, U>(void* array, in int lo, in int _hi, int depth, U comp)
795 where T : unmanaged
796 where U : IComparer<T>
797 {
798 var hi = _hi;
799
800 while (hi > lo)
801 {
802 int partitionSize = hi - lo + 1;
803 if (partitionSize <= k_IntrosortSizeThreshold)
804 {
805 if (partitionSize == 1)
806 {
807 return;
808 }
809 if (partitionSize == 2)
810 {
811 SwapIfGreaterWithItemsStruct<T, U>(array, lo, hi, comp);
812 return;
813 }
814 if (partitionSize == 3)
815 {
816 SwapIfGreaterWithItemsStruct<T, U>(array, lo, hi - 1, comp);
817 SwapIfGreaterWithItemsStruct<T, U>(array, lo, hi, comp);
818 SwapIfGreaterWithItemsStruct<T, U>(array, hi - 1, hi, comp);
819 return;
820 }
821
822 InsertionSortStruct<T, U>(array, lo, hi, comp);
823 return;
824 }
825
826 if (depth == 0)
827 {
828 HeapSortStruct<T, U>(array, lo, hi, comp);
829 return;
830 }
831 depth--;
832
833 int p = PartitionStruct<T, U>(array, lo, hi, comp);
834 IntroSortStruct_R<T, U>(array, p + 1, hi, depth, comp);
835 hi = p - 1;
836 }
837 }
838
839 unsafe static void InsertionSortStruct<T, U>(void* array, in int lo, in int hi, U comp)
840 where T : unmanaged
841 where U : IComparer<T>
842 {
843 int i, j;
844 T t;
845 for (i = lo; i < hi; i++)
846 {
847 j = i;
848 t = UnsafeUtility.ReadArrayElement<T>(array, i + 1);
849 while (j >= lo && comp.Compare(t, UnsafeUtility.ReadArrayElement<T>(array, j)) < 0)
850 {
851 UnsafeUtility.WriteArrayElement(array, j + 1, UnsafeUtility.ReadArrayElement<T>(array, j));
852 j--;
853 }
854 UnsafeUtility.WriteArrayElement(array, j + 1, t);
855 }
856 }
857
858 unsafe static int PartitionStruct<T, U>(void* array, in int lo, in int hi, U comp)
859 where T : unmanaged
860 where U : IComparer<T>
861 {
862 int mid = lo + ((hi - lo) / 2);
863 SwapIfGreaterWithItemsStruct<T, U>(array, lo, mid, comp);
864 SwapIfGreaterWithItemsStruct<T, U>(array, lo, hi, comp);
865 SwapIfGreaterWithItemsStruct<T, U>(array, mid, hi, comp);
866
867 T pivot = UnsafeUtility.ReadArrayElement<T>(array, mid);
868 SwapStruct<T>(array, mid, hi - 1);
869 int left = lo, right = hi - 1;
870
871 while (left < right)
872 {
873 while (left < hi && comp.Compare(pivot, UnsafeUtility.ReadArrayElement<T>(array, ++left)) > 0)
874 {
875 }
876
877 while (right > left && comp.Compare(pivot, UnsafeUtility.ReadArrayElement<T>(array, --right)) < 0)
878 {
879 }
880
881 if (left >= right)
882 break;
883
884 SwapStruct<T>(array, left, right);
885 }
886
887 SwapStruct<T>(array, left, (hi - 1));
888 return left;
889 }
890
891 unsafe static void HeapSortStruct<T, U>(void* array, in int lo, in int hi, U comp)
892 where T : unmanaged
893 where U : IComparer<T>
894 {
895 int n = hi - lo + 1;
896
897 for (int i = n / 2; i >= 1; i--)
898 {
899 HeapifyStruct<T, U>(array, i, n, lo, comp);
900 }
901
902 for (int i = n; i > 1; i--)
903 {
904 SwapStruct<T>(array, lo, lo + i - 1);
905 HeapifyStruct<T, U>(array, 1, i - 1, lo, comp);
906 }
907 }
908
909 unsafe static void HeapifyStruct<T, U>(void* array, int i, int n, in int lo, U comp)
910 where T : unmanaged
911 where U : IComparer<T>
912 {
913 T val = UnsafeUtility.ReadArrayElement<T>(array, lo + i - 1);
914 int child;
915 while (i <= n / 2)
916 {
917 child = 2 * i;
918
919 if (child < n && (comp.Compare(UnsafeUtility.ReadArrayElement<T>(array, lo + child - 1), UnsafeUtility.ReadArrayElement<T>(array, (lo + child))) < 0))
920 {
921 child++;
922 }
923
924 if (comp.Compare(UnsafeUtility.ReadArrayElement<T>(array, (lo + child - 1)), val) < 0)
925 {
926 break;
927 }
928
929 UnsafeUtility.WriteArrayElement(array, lo + i - 1, UnsafeUtility.ReadArrayElement<T>(array, lo + child - 1));
930 i = child;
931 }
932
933 UnsafeUtility.WriteArrayElement(array, lo + i - 1, val);
934 }
935
936 unsafe static void SwapStruct<T>(void* array, int lhs, int rhs)
937 where T : unmanaged
938 {
939 T val = UnsafeUtility.ReadArrayElement<T>(array, lhs);
940 UnsafeUtility.WriteArrayElement(array, lhs, UnsafeUtility.ReadArrayElement<T>(array, rhs));
941 UnsafeUtility.WriteArrayElement(array, rhs, val);
942 }
943
944 unsafe static void SwapIfGreaterWithItemsStruct<T, U>(void* array, int lhs, int rhs, U comp)
945 where T : unmanaged
946 where U : IComparer<T>
947 {
948 if (lhs != rhs)
949 {
950 if (comp.Compare(UnsafeUtility.ReadArrayElement<T>(array, lhs), UnsafeUtility.ReadArrayElement<T>(array, rhs)) > 0)
951 {
952 SwapStruct<T>(array, lhs, rhs);
953 }
954 }
955 }
956
957 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
958 static void CheckStrideMatchesSize<T>(int stride) where T : unmanaged
959 {
960 if (stride != UnsafeUtility.SizeOf<T>())
961 {
962 throw new InvalidOperationException("Sort requires that stride matches the size of the source type");
963 }
964 }
965
966 [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")]
967 unsafe static void CheckComparer<T, U>(T* array, int length, U comp)
968 where T : unmanaged
969 where U : IComparer<T>
970 {
971 if (length > 0)
972 {
973 T a = array[0];
974
975 if (0 != comp.Compare(a, a))
976 {
977 throw new InvalidOperationException("Comparison function is incorrect. Compare(a, a) must return 0/equal.");
978 }
979
980 for (int i = 1, len = math.min(length, 8); i < len; ++i)
981 {
982 T b = array[i];
983
984 if (0 == comp.Compare(a, b) &&
985 0 == comp.Compare(b, a))
986 {
987 continue;
988 }
989
990 if (0 == comp.Compare(a, b))
991 {
992 throw new InvalidOperationException("Comparison function is incorrect. Compare(a, b) of two different values should not return 0/equal.");
993 }
994
995 if (0 == comp.Compare(b, a))
996 {
997 throw new InvalidOperationException("Comparison function is incorrect. Compare(b, a) of two different values should not return 0/equal.");
998 }
999
1000 if (comp.Compare(a, b) == comp.Compare(b, a))
1001 {
1002 throw new InvalidOperationException("Comparison function is incorrect. Compare(a, b) when a and b are different values should not return the same value as Compare(b, a).");
1003 }
1004
1005 break;
1006 }
1007 }
1008 }
1009 }
1010
1011 /// <summary>
1012 /// Returned by the `SortJob` methods of <see cref="Unity.Collections.NativeSortExtension"/>. Call `Schedule` to schedule the sorting.
1013 /// </summary>
1014 /// <remarks>
1015 /// When `RegisterGenericJobType` is used on SortJob, to complete registration you must register `SortJob<T,U>.SegmentSort` and `SortJob<T,U>.SegmentSortMerge`.
1016 /// </remarks>
1017 /// <typeparam name="T">The type of the elements to sort.</typeparam>
1018 /// <typeparam name="U">The type of the comparer.</typeparam>
1019 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(NativeSortExtension.DefaultComparer<int>) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
1020 public unsafe struct SortJob<T, U>
1021 where T : unmanaged
1022 where U : IComparer<T>
1023 {
1024 /// <summary>
1025 /// The data to sort.
1026 /// </summary>
1027 public T* Data;
1028
1029 /// <summary>
1030 /// Comparison function.
1031 /// </summary>
1032 public U Comp;
1033
1034 /// <summary>
1035 /// The length to sort.
1036 /// </summary>
1037 public int Length;
1038
1039 /// <summary>
1040 /// <undoc />
1041 /// </summary>
1042 [BurstCompile]
1043 public struct SegmentSort : IJobParallelFor
1044 {
1045 [NativeDisableUnsafePtrRestriction]
1046 internal T* Data;
1047 internal U Comp;
1048
1049 internal int Length;
1050 internal int SegmentWidth;
1051
1052 /// <summary>
1053 /// <undoc />
1054 /// </summary>
1055 /// <param name="index"><undoc /></param>
1056 public void Execute(int index)
1057 {
1058 var startIndex = index * SegmentWidth;
1059 var segmentLength = ((Length - startIndex) < SegmentWidth) ? (Length - startIndex) : SegmentWidth;
1060 NativeSortExtension.Sort(Data + startIndex, segmentLength, Comp);
1061 }
1062 }
1063
1064 /// <summary>
1065 /// <undoc />
1066 /// </summary>
1067 [BurstCompile]
1068 public struct SegmentSortMerge : IJob
1069 {
1070 [NativeDisableUnsafePtrRestriction]
1071 internal T* Data;
1072 internal U Comp;
1073
1074 internal int Length;
1075 internal int SegmentWidth;
1076
1077 /// <summary>
1078 /// <undoc />
1079 /// </summary>
1080 public void Execute()
1081 {
1082 var segmentCount = (Length + (SegmentWidth - 1)) / SegmentWidth;
1083 var segmentIndex = stackalloc int[segmentCount];
1084
1085 var resultCopy = (T*)Memory.Unmanaged.Allocate(UnsafeUtility.SizeOf<T>() * Length, 16, Allocator.Temp);
1086
1087 for (int sortIndex = 0; sortIndex < Length; sortIndex++)
1088 {
1089 // find next best
1090 int bestSegmentIndex = -1;
1091 T bestValue = default(T);
1092
1093 for (int i = 0; i < segmentCount; i++)
1094 {
1095 var startIndex = i * SegmentWidth;
1096 var offset = segmentIndex[i];
1097 var segmentLength = ((Length - startIndex) < SegmentWidth) ? (Length - startIndex) : SegmentWidth;
1098 if (offset == segmentLength)
1099 continue;
1100
1101 var nextValue = Data[startIndex + offset];
1102 if (bestSegmentIndex != -1)
1103 {
1104 if (Comp.Compare(nextValue, bestValue) > 0)
1105 continue;
1106 }
1107
1108 bestValue = nextValue;
1109 bestSegmentIndex = i;
1110 }
1111
1112 segmentIndex[bestSegmentIndex]++;
1113 resultCopy[sortIndex] = bestValue;
1114 }
1115
1116 UnsafeUtility.MemCpy(Data, resultCopy, UnsafeUtility.SizeOf<T>() * Length);
1117 }
1118 }
1119
1120 /// <summary>
1121 /// Schedules this job.
1122 /// </summary>
1123 /// <param name="inputDeps">Handle of a job to depend upon.</param>
1124 /// <returns>The handle of this newly scheduled job.</returns>
1125 public JobHandle Schedule(JobHandle inputDeps = default)
1126 {
1127 if (Length == 0)
1128 return inputDeps;
1129 var segmentCount = (Length + 1023) / 1024;
1130
1131#if UNITY_2022_2_14F1_OR_NEWER
1132 int maxThreadCount = JobsUtility.ThreadIndexCount;
1133#else
1134 int maxThreadCount = JobsUtility.MaxJobThreadCount;
1135#endif
1136 var workerCount = math.max(1, maxThreadCount);
1137 var workerSegmentCount = segmentCount / workerCount;
1138 var segmentSortJob = new SegmentSort { Data = Data, Comp = Comp, Length = Length, SegmentWidth = 1024 };
1139 var segmentSortJobHandle = segmentSortJob.Schedule(segmentCount, workerSegmentCount, inputDeps);
1140 var segmentSortMergeJob = new SegmentSortMerge { Data = Data, Comp = Comp, Length = Length, SegmentWidth = 1024 };
1141 var segmentSortMergeJobHandle = segmentSortMergeJob.Schedule(segmentSortJobHandle);
1142 return segmentSortMergeJobHandle;
1143 }
1144 }
1145
1146
1147 /// <summary>
1148 /// Returned by the `SortJobDefer` methods of <see cref="Unity.Collections.NativeSortExtension"/>. Call `Schedule` to schedule the sorting.
1149 /// </summary>
1150 /// <remarks>
1151 /// When `RegisterGenericJobType` is used on SortJobDefer, to complete registration you must register `SortJobDefer<T,U>.SegmentSort` and `SortJobDefer<T,U>.SegmentSortMerge`.
1152 /// </remarks>
1153 /// <typeparam name="T">The type of the elements to sort.</typeparam>
1154 /// <typeparam name="U">The type of the comparer.</typeparam>
1155 [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(int), typeof(NativeSortExtension.DefaultComparer<int>) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
1156 public unsafe struct SortJobDefer<T, U>
1157 where T : unmanaged
1158 where U : IComparer<T>
1159 {
1160 /// <summary>
1161 /// The data to sort.
1162 /// </summary>
1163 public NativeList<T> Data;
1164
1165 /// <summary>
1166 /// Comparison function.
1167 /// </summary>
1168 public U Comp;
1169
1170 /// <summary>
1171 /// <undoc />
1172 /// </summary>
1173 [BurstCompile]
1174 public struct SegmentSort : IJobParallelForDefer
1175 {
1176 [ReadOnly]
1177 internal NativeList<T> DataRO;
1178
1179 [NativeDisableUnsafePtrRestriction]
1180 internal UnsafeList<T>* Data;
1181
1182 internal U Comp;
1183 internal int SegmentWidth;
1184
1185 /// <summary>
1186 /// <undoc />
1187 /// </summary>
1188 /// <param name="index"><undoc /></param>
1189 public void Execute(int index)
1190 {
1191 var startIndex = index * SegmentWidth;
1192 var segmentLength = ((Data->Length - startIndex) < SegmentWidth) ? (Data->Length - startIndex) : SegmentWidth;
1193 NativeSortExtension.Sort(Data->Ptr + startIndex, segmentLength, Comp);
1194 }
1195 }
1196
1197 /// <summary>
1198 /// <undoc />
1199 /// </summary>
1200 [BurstCompile]
1201 public struct SegmentSortMerge : IJob
1202 {
1203 [NativeDisableUnsafePtrRestriction]
1204 internal NativeList<T> Data;
1205
1206 internal U Comp;
1207 internal int SegmentWidth;
1208
1209 /// <summary>
1210 /// <undoc />
1211 /// </summary>
1212 public void Execute()
1213 {
1214 var length = Data.Length;
1215 var ptr = Data.GetUnsafePtr();
1216 var segmentCount = (length + (SegmentWidth - 1)) / SegmentWidth;
1217 var segmentIndex = stackalloc int[segmentCount];
1218
1219 var resultCopy = (T*)Memory.Unmanaged.Allocate(UnsafeUtility.SizeOf<T>() * length, 16, Allocator.Temp);
1220
1221 for (int sortIndex = 0; sortIndex < length; sortIndex++)
1222 {
1223 // find next best
1224 int bestSegmentIndex = -1;
1225 T bestValue = default;
1226
1227 for (int i = 0; i < segmentCount; i++)
1228 {
1229 var startIndex = i * SegmentWidth;
1230 var offset = segmentIndex[i];
1231 var segmentLength = ((length - startIndex) < SegmentWidth) ? (length - startIndex) : SegmentWidth;
1232 if (offset == segmentLength)
1233 continue;
1234
1235 var nextValue = ptr[startIndex + offset];
1236 if (bestSegmentIndex != -1)
1237 {
1238 if (Comp.Compare(nextValue, bestValue) > 0)
1239 continue;
1240 }
1241
1242 bestValue = nextValue;
1243 bestSegmentIndex = i;
1244 }
1245
1246 segmentIndex[bestSegmentIndex]++;
1247 resultCopy[sortIndex] = bestValue;
1248 }
1249
1250 UnsafeUtility.MemCpy(ptr, resultCopy, UnsafeUtility.SizeOf<T>() * length);
1251 }
1252 }
1253
1254 /// <summary>
1255 /// Schedules this job.
1256 /// </summary>
1257 /// <param name="inputDeps">Handle of a job to depend upon.</param>
1258 /// <returns>The handle of this newly scheduled job.</returns>
1259 public JobHandle Schedule(JobHandle inputDeps = default)
1260 {
1261 var segmentSortJob = new SegmentSort { DataRO = Data, Data = Data.m_ListData, Comp = Comp, SegmentWidth = 1024 };
1262 var segmentSortJobHandle = segmentSortJob.ScheduleByRef(Data, 1024, inputDeps);
1263 var segmentSortMergeJob = new SegmentSortMerge { Data = Data, Comp = Comp, SegmentWidth = 1024 };
1264 var segmentSortMergeJobHandle = segmentSortMergeJob.Schedule(segmentSortJobHandle);
1265
1266 return segmentSortMergeJobHandle;
1267 }
1268 }
1269}