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 &lt; y`, and positive if `x &gt; 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&lt;T,U&gt;.SegmentSort` and `SortJob&lt;T,U&gt;.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&lt;T,U&gt;.SegmentSort` and `SortJobDefer&lt;T,U&gt;.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}