b486678290
Library -Artifacts
1232 lines
59 KiB
C#
1232 lines
59 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using Unity.Burst;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
using Unity.Jobs;
|
|
using Unity.Jobs.LowLevel.Unsafe;
|
|
using Unity.Mathematics;
|
|
|
|
namespace Unity.Collections
|
|
{
|
|
/// <summary>
|
|
/// Extension methods for sorting collections.
|
|
/// </summary>
|
|
[BurstCompatible]
|
|
public static class NativeSortExtension
|
|
{
|
|
/// <summary>
|
|
/// A comparer that uses IComparable.CompareTo(). For primitive types, this is an ascending sort.
|
|
/// </summary>
|
|
/// <typeparam name="T">Source type of elements</typeparam>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
|
|
public struct DefaultComparer<T> : IComparer<T> where T : IComparable<T>
|
|
{
|
|
/// <summary>
|
|
/// Compares two values.
|
|
/// </summary>
|
|
/// <param name="x">First value to compare.</param>
|
|
/// <param name="y">Second value to compare.</param>
|
|
/// <returns>A signed integer that denotes the relative values of `x` and `y`:
|
|
/// 0 if they're equal, negative if `x < y`, and positive if `x > y`.</returns>
|
|
public int Compare(T x, T y) => x.CompareTo(y);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts an array in ascending order.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <param name="array">The array to sort.</param>
|
|
/// <param name="length">The number of elements to sort in the array.
|
|
/// Indexes greater than or equal to `length` won't be included in the sort.</param>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
|
|
public unsafe static void Sort<T>(T* array, int length)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
IntroSort<T, DefaultComparer<T>>(array, length, new DefaultComparer<T>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts an array using a custom comparison.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <typeparam name="U">The type of the comparer.</typeparam>
|
|
/// <param name="array">The array to sort.</param>
|
|
/// <param name="length">The number of elements to sort in the array.
|
|
/// Indexes greater than or equal to `length` won't be included in the sort.</param>
|
|
/// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
|
|
public unsafe static void Sort<T, U>(T* array, int length, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
IntroSort<T, U>(array, length, comp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates and schedules a job that will sort an array in ascending order.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of elements.</typeparam>
|
|
/// <param name="array">The array to sort.</param>
|
|
/// <param name="length">The number of elements to sort in the array.
|
|
/// Indexes greater than or equal to `length` won't be included in the sort.</param>
|
|
/// <param name="inputDeps">A job handle which the newly created job will depend upon.</param>
|
|
/// <returns>The handle of the new job which will sort the array.</returns>
|
|
[NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
|
|
[Obsolete("Instead call SortJob(T*, int).Schedule(JobHandle). (RemovedAfter 2021-06-20)", false)]
|
|
public unsafe static JobHandle Sort<T>(T* array, int length, JobHandle inputDeps)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
return Sort(array, length, new DefaultComparer<T>(), inputDeps);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a job which will sort an array in ascending order.
|
|
/// </summary>
|
|
/// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <param name="array">The array to sort.</param>
|
|
/// <param name="length">The number of elements to sort in the array.
|
|
/// Indexes greater than or equal to `length` won't be included in the sort.</param>
|
|
/// <returns>A job for sorting the array.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
|
|
public unsafe static SortJob<T, DefaultComparer<T>> SortJob<T>(T* array, int length)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
return new SortJob<T, DefaultComparer<T>> {Data = array, Length = length, Comp = new DefaultComparer<T>()};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts an array using a custom comparison function.
|
|
/// </summary>
|
|
/// <typeparam name="T">Source type of elements</typeparam>
|
|
/// <typeparam name="U">The comparer type.</typeparam>
|
|
/// <param name="array">Array to perform sort.</param>
|
|
/// <param name="length">Number of elements to perform sort.</param>
|
|
/// <param name="comp">A comparison function that determines whether one element in the array is less than, equal to, or greater than another element.</param>
|
|
/// <param name="inputDeps">The job handle or handles for any scheduled jobs that use this container.</param>
|
|
/// <returns>A new job handle containing the prior handles as well as the handle for the job that sorts
|
|
/// the container.</returns>
|
|
[NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
|
|
[Obsolete("Instead call SortJob(T*, int, U).Schedule(JobHandle). (RemovedAfter 2021-06-20)", false)]
|
|
public unsafe static JobHandle Sort<T, U>(T* array, int length, U comp, JobHandle inputDeps)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
if (length == 0)
|
|
{
|
|
return inputDeps;
|
|
}
|
|
|
|
var segmentCount = (length + 1023) / 1024;
|
|
|
|
var workerCount = math.max(1, JobsUtility.MaxJobThreadCount);
|
|
var workerSegmentCount = segmentCount / workerCount;
|
|
var segmentSortJob = new SegmentSort<T, U> { Data = array, Comp = comp, Length = length, SegmentWidth = 1024 };
|
|
var segmentSortJobHandle = segmentSortJob.Schedule(segmentCount, workerSegmentCount, inputDeps);
|
|
var segmentSortMergeJob = new SegmentSortMerge<T, U> { Data = array, Comp = comp, Length = length, SegmentWidth = 1024 };
|
|
var segmentSortMergeJobHandle = segmentSortMergeJob.Schedule(segmentSortJobHandle);
|
|
return segmentSortMergeJobHandle;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a job which will sort an array using a custom comparison.
|
|
/// </summary>
|
|
/// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <typeparam name="U">The type of the comparer.</typeparam>
|
|
/// <param name="array">The array to sort.</param>
|
|
/// <param name="length">The number of elements to sort in the array.
|
|
/// Indexes greater than or equal to `length` won't be included in the sort.</param>
|
|
/// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
|
|
/// <returns>A job for sorting the array.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
|
|
public unsafe static SortJob<T, U> SortJob<T, U>(T* array, int length, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
return new SortJob<T, U>() {Data = array, Length = length, Comp = comp};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds a value in a sorted array by binary search.
|
|
/// </summary>
|
|
/// <remarks>If the array is not sorted, the value might not be found, even if it's present in the array.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <param name="ptr">The array to search.</param>
|
|
/// <param name="value">The value to locate.</param>
|
|
/// <param name="length">The number of elements to search. Indexes greater than or equal to `length` won't be searched.</param>
|
|
/// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
|
|
public unsafe static int BinarySearch<T>(T* ptr, int length, T value)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
return BinarySearch(ptr, length, value, new DefaultComparer<T>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds a value in a sorted array by binary search using a custom comparison.
|
|
/// </summary>
|
|
/// <remarks>If the array is not sorted, the value might not be found, even if it's present in the array.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <typeparam name="U">The type of the comparer.</typeparam>
|
|
/// <param name="ptr">The array to search.</param>
|
|
/// <param name="value">The value to locate.</param>
|
|
/// <param name="length">The number of elements to search. Indexes greater than or equal to `length` won't be searched.</param>
|
|
/// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
|
|
/// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
|
|
public unsafe static int BinarySearch<T, U>(T* ptr, int length, T value, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
var offset = 0;
|
|
|
|
for (var l = length; l != 0; l >>= 1)
|
|
{
|
|
var idx = offset + (l >> 1);
|
|
var curr = ptr[idx];
|
|
var r = comp.Compare(value, curr);
|
|
if (r == 0)
|
|
{
|
|
return idx;
|
|
}
|
|
|
|
if (r > 0)
|
|
{
|
|
offset = idx + 1;
|
|
--l;
|
|
}
|
|
}
|
|
|
|
return ~offset;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts this array in ascending order.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <param name="array">The array to sort.</param>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
|
|
public unsafe static void Sort<T>(this NativeArray<T> array)
|
|
where T : struct, IComparable<T>
|
|
{
|
|
IntroSortStruct<T, DefaultComparer<T>>(array.GetUnsafePtr(), array.Length, new DefaultComparer<T>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts this array using a custom comparison.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <typeparam name="U">The type of the comparer.</typeparam>
|
|
/// <param name="array">The array to sort.</param>
|
|
/// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
|
|
public unsafe static void Sort<T, U>(this NativeArray<T> array, U comp)
|
|
where T : struct
|
|
where U : IComparer<T>
|
|
{
|
|
IntroSortStruct<T, U>(array.GetUnsafePtr(), array.Length, comp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts this array in ascending order.
|
|
/// </summary>
|
|
/// <typeparam name="T">Source type of elements</typeparam>
|
|
/// <param name="array">The array to sort.</param>
|
|
/// <param name="inputDeps">The job handle or handles for any scheduled jobs that use this container.</param>
|
|
/// <returns>A new job handle containing the prior handles as well as the handle for the job that sorts
|
|
/// the container.</returns>
|
|
[NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
|
|
[Obsolete("Instead call SortJob(this NativeArray<T>).Schedule(JobHandle). (RemovedAfter 2021-06-20)", false)]
|
|
public unsafe static JobHandle Sort<T>(this NativeArray<T> array, JobHandle inputDeps)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
return Sort((T*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array), array.Length, new DefaultComparer<T>(), inputDeps);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a job which will sort this array in ascending order.
|
|
/// </summary>
|
|
/// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <param name="array">The array to sort.</param>
|
|
/// <returns>A job for sorting this array.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
|
|
public unsafe static SortJob<T, DefaultComparer<T>> SortJob<T>(this NativeArray<T> array)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
return SortJob((T*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array), array.Length, new DefaultComparer<T>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts this array using a custom comparison function.
|
|
/// </summary>
|
|
/// <typeparam name="T">Source type of elements</typeparam>
|
|
/// <typeparam name="U">The comparer type.</typeparam>
|
|
/// <param name="array">The array to sort.</param>
|
|
/// <param name="comp">A comparison function that determines whether one element in the array is less than, equal to, or greater than another element.</param>
|
|
/// <param name="inputDeps">The job handle or handles for any scheduled jobs that use this container.</param>
|
|
/// <returns>A new job handle containing the prior handles as well as the handle for the job that sorts
|
|
/// the container.</returns>
|
|
[NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
|
|
[Obsolete("Instead call SortJob(this NativeArray<T>, U).Schedule(JobHandle). (RemovedAfter 2021-06-20)", false)]
|
|
public unsafe static JobHandle Sort<T, U>(this NativeArray<T> array, U comp, JobHandle inputDeps)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
return Sort((T*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array), array.Length, comp, inputDeps);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a job which will sort this array using a custom comparison.
|
|
/// </summary>
|
|
/// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <typeparam name="U">The type of the comparer.</typeparam>
|
|
/// <param name="array">The array to sort.</param>
|
|
/// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
|
|
/// <returns>A job for sorting the array.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
|
|
public unsafe static SortJob<T, U> SortJob<T, U>(this NativeArray<T> array, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
return new SortJob<T, U>
|
|
{
|
|
Data = (T*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array),
|
|
Length = array.Length,
|
|
Comp = comp
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds a value in this sorted array by binary search.
|
|
/// </summary>
|
|
/// <remarks>If the array is not sorted, the value might not be found, even if it's present in this array.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <param name="array">The array to search.</param>
|
|
/// <param name="value">The value to locate.</param>
|
|
/// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
|
|
public static int BinarySearch<T>(this NativeArray<T> array, T value)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
return array.BinarySearch(value, new DefaultComparer<T>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds a value in this sorted array by binary search using a custom comparison.
|
|
/// </summary>
|
|
/// <remarks>If the array is not sorted, the value might not be found, even if it's present in this array.
|
|
/// </remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <typeparam name="U">The comparer type.</typeparam>
|
|
/// <param name="array">The array to search.</param>
|
|
/// <param name="value">The value to locate.</param>
|
|
/// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
|
|
/// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
|
|
public unsafe static int BinarySearch<T, U>(this NativeArray<T> array, T value, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
return BinarySearch((T*)NativeArrayUnsafeUtility.GetUnsafeBufferPointerWithoutChecks(array), array.Length, value, comp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts this list in ascending order.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <param name="list">The list to sort.</param>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
|
|
public unsafe static void Sort<T>(this NativeList<T> list)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
list.Sort(new DefaultComparer<T>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts this list using a custom comparison.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <typeparam name="U">The type of the comparer.</typeparam>
|
|
/// <param name="list">The list to sort.</param>
|
|
/// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
|
|
public unsafe static void Sort<T, U>(this NativeList<T> list, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
IntroSort<T, U>(list.GetUnsafePtr(), list.Length, comp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts this list in ascending order.
|
|
/// </summary>
|
|
/// <typeparam name="T">Source type of elements</typeparam>
|
|
/// <param name="array">The container to perform sort.</param>
|
|
/// <param name="inputDeps">The job handle or handles for any scheduled jobs that use this container.</param>
|
|
/// <returns>A new job handle containing the prior handles as well as the handle for the job that sorts
|
|
/// the container.</returns>
|
|
[NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
|
|
[Obsolete("Instead call SortJob(this NativeList<T>).Schedule(JobHandle). (RemovedAfter 2021-06-20)", false)]
|
|
public unsafe static JobHandle Sort<T>(this NativeList<T> array, JobHandle inputDeps)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
return array.Sort(new DefaultComparer<T>(), inputDeps);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a job which will sort this list in ascending order.
|
|
/// </summary>
|
|
/// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <param name="list">The list to sort.</param>
|
|
/// <returns>A job for sorting this list.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
|
|
public unsafe static SortJob<T, DefaultComparer<T>> SortJob<T>(this NativeList<T> list)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
return SortJob((T*)list.GetUnsafePtr(), list.Length,new DefaultComparer<T>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts this list using a custom comparison function.
|
|
/// </summary>
|
|
/// <typeparam name="T">Source type of elements</typeparam>
|
|
/// <typeparam name="U">The comparer type.</typeparam>
|
|
/// <param name="list">The container to perform sort.</param>
|
|
/// <param name="comp">A comparison function that determines whether one element in the array is less than, equal to, or greater than another element.</param>
|
|
/// <param name="inputDeps">The job handle or handles for any scheduled jobs that use this container.</param>
|
|
/// <returns>A new job handle containing the prior handles as well as the handle for the job that sorts
|
|
/// the container.</returns>
|
|
[NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
|
|
[Obsolete("Instead call SortJob(this NativeList<T>, U).Schedule(JobHandle). (RemovedAfter 2021-06-20)", false)]
|
|
public unsafe static JobHandle Sort<T, U>(this NativeList<T> list, U comp, JobHandle inputDeps)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
return Sort((T*)list.GetUnsafePtr(), list.Length, comp, inputDeps);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a job which will sort this list using a custom comparison.
|
|
/// </summary>
|
|
/// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <typeparam name="U">The type of the comparer.</typeparam>
|
|
/// <param name="list">The list to sort.</param>
|
|
/// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
|
|
/// <returns>A job for sorting this list.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
|
|
public unsafe static SortJob<T, U> SortJob<T, U>(this NativeList<T> list, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
return SortJob((T*)list.GetUnsafePtr(), list.Length, comp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds a value in this sorted list by binary search.
|
|
/// </summary>
|
|
/// <remarks>If this list is not sorted, the value might not be found, even if it's present in this list.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <param name="list">The list to search.</param>
|
|
/// <param name="value">The value to locate.</param>
|
|
/// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
|
|
public static int BinarySearch<T>(this NativeList<T> list, T value)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
return list.BinarySearch(value, new DefaultComparer<T>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds a value in this sorted list by binary search using a custom comparison.
|
|
/// </summary>
|
|
/// <remarks>If this list is not sorted, the value may not be found, even if it's present in this list.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <typeparam name="U">The type of the comparer.</typeparam>
|
|
/// <param name="list">The list to search.</param>
|
|
/// <param name="value">The value to locate.</param>
|
|
/// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
|
|
/// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
|
|
public unsafe static int BinarySearch<T, U>(this NativeList<T> list, T value, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
return BinarySearch((T*)list.GetUnsafePtr(), list.Length, value, comp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts this list in ascending order.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <param name="list">The list to sort.</param>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
|
|
public unsafe static void Sort<T>(this UnsafeList<T> list) where T : unmanaged, IComparable<T>
|
|
{
|
|
list.Sort(new DefaultComparer<T>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts the list using a custom comparison.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <typeparam name="U">The type of the comparer.</typeparam>
|
|
/// <param name="list">The list to sort.</param>
|
|
/// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
|
|
public unsafe static void Sort<T, U>(this UnsafeList<T> list, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
IntroSort<T, U>(list.Ptr, list.Length, comp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts this list in ascending order.
|
|
/// </summary>
|
|
/// <typeparam name="T">Source type of elements</typeparam>
|
|
/// <param name="list">The container to perform sort.</param>
|
|
/// <param name="inputDeps">The job handle or handles for any scheduled jobs that use this container.</param>
|
|
/// <returns>A new job handle containing the prior handles as well as the handle for the job that sorts
|
|
/// the container.</returns>
|
|
[NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
|
|
[Obsolete("Instead call SortJob(this UnsafeList<T>).Schedule(JobHandle). (RemovedAfter 2021-06-20)", false)]
|
|
public unsafe static JobHandle Sort<T>(this UnsafeList<T> list, JobHandle inputDeps)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
return list.Sort(new DefaultComparer<T>(), inputDeps);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a job which will sort this list in ascending order.
|
|
/// </summary>
|
|
/// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <param name="list">The list to sort.</param>
|
|
/// <returns>A job for sorting this list.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
|
|
public unsafe static SortJob<T, DefaultComparer<T>> SortJob<T>(this UnsafeList<T> list)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
return SortJob((T*)list.Ptr, list.Length, new DefaultComparer<T>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts this list using a custom comparison function.
|
|
/// </summary>
|
|
/// <typeparam name="T">Source type of elements</typeparam>
|
|
/// <typeparam name="U">The comparer type.</typeparam>
|
|
/// <param name="list">The container to perform sort.</param>
|
|
/// <param name="comp">A comparison function that determines whether one element in the array is less than, equal to, or greater than another element.</param>
|
|
/// <param name="inputDeps">The job handle or handles for any scheduled jobs that use this container.</param>
|
|
/// <returns>A new job handle containing the prior handles as well as the handle for the job that sorts
|
|
/// the container.</returns>
|
|
[NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
|
|
[Obsolete("Instead call SortJob(this UnsafeList<T>, U).Schedule(JobHandle). (RemovedAfter 2021-06-20)", false)]
|
|
public unsafe static JobHandle Sort<T, U>(this UnsafeList<T> list, U comp, JobHandle inputDeps)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
return Sort(list.Ptr, list.Length, comp, inputDeps);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a job which will sort this list using a custom comparison.
|
|
/// </summary>
|
|
/// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <typeparam name="U">The type of the comparer.</typeparam>
|
|
/// <param name="list">The list to sort.</param>
|
|
/// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
|
|
/// <returns>A job for sorting this list.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
|
|
public unsafe static SortJob<T, U> SortJob<T, U>(this UnsafeList<T> list, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
return SortJob(list.Ptr, list.Length, comp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds a value in this sorted list by binary search.
|
|
/// </summary>
|
|
/// <remarks>If this list is not sorted, the value might not be found, even if it's present in this list.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <param name="list">The list to search.</param>
|
|
/// <param name="value">The value to locate.</param>
|
|
/// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
|
|
public static int BinarySearch<T>(this UnsafeList<T> list, T value)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
return list.BinarySearch(value, new DefaultComparer<T>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds a value in this sorted list by binary search using a custom comparison.
|
|
/// </summary>
|
|
/// <remarks>If this list is not sorted, the value might not be found, even if it's present in this list.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <typeparam name="U">The type of the comparer.</typeparam>
|
|
/// <param name="list">The list to search.</param>
|
|
/// <param name="value">The value to locate.</param>
|
|
/// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
|
|
/// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
|
|
public unsafe static int BinarySearch<T, U>(this UnsafeList<T> list, T value, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
return BinarySearch(list.Ptr, list.Length, value, comp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts this slice in ascending order.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <param name="slice">The slice to sort.</param>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
|
|
public unsafe static void Sort<T>(this NativeSlice<T> slice)
|
|
where T : struct, IComparable<T>
|
|
{
|
|
slice.Sort(new DefaultComparer<T>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts this slice using a custom comparison.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <typeparam name="U">The type of the comparer.</typeparam>
|
|
/// <param name="slice">The slice to sort.</param>
|
|
/// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
|
|
public unsafe static void Sort<T, U>(this NativeSlice<T> slice, U comp)
|
|
where T : struct
|
|
where U : IComparer<T>
|
|
{
|
|
CheckStrideMatchesSize<T>(slice.Stride);
|
|
IntroSortStruct<T, U>(slice.GetUnsafePtr(), slice.Length, comp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts this slice in ascending order.
|
|
/// </summary>
|
|
/// <typeparam name="T">Source type of elements</typeparam>
|
|
/// <param name="slice">The container to perform sort.</param>
|
|
/// <param name="inputDeps">The job handle or handles for any scheduled jobs that use this container.</param>
|
|
/// <returns>A new job handle containing the prior handles as well as the handle for the job that sorts
|
|
/// the container.</returns>
|
|
[NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
|
|
[Obsolete("Instead call SortJob(this NativeSlice<T>).Schedule(JobHandle). (RemovedAfter 2021-06-20)", false)]
|
|
public unsafe static JobHandle Sort<T>(this NativeSlice<T> slice, JobHandle inputDeps)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
return slice.Sort(new DefaultComparer<T>(), inputDeps);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a job which will sort this slice in ascending order.
|
|
/// </summary>
|
|
/// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <param name="slice">The slice to sort.</param>
|
|
/// <returns>A job for sorting this slice.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
|
|
public unsafe static SortJob<T, DefaultComparer<T>> SortJob<T>(this NativeSlice<T> slice)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
CheckStrideMatchesSize<T>(slice.Stride);
|
|
return SortJob((T*)slice.GetUnsafePtr(), slice.Length, new DefaultComparer<T>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Sorts this slice using a custom comparison function.
|
|
/// </summary>
|
|
/// <typeparam name="T">Source type of elements</typeparam>
|
|
/// <typeparam name="U">The comparer type.</typeparam>
|
|
/// <param name="slice">The container to perform sort.</param>
|
|
/// <param name="comp">A comparison function that determines whether one element in the array is less than, equal to, or greater than another element.</param>
|
|
/// <param name="inputDeps">The job handle or handles for any scheduled jobs that use this container.</param>
|
|
/// <returns>A new job handle containing the prior handles as well as the handle for the job that sorts
|
|
/// the container.</returns>
|
|
[NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
|
|
[Obsolete("Instead call SortJob(this NativeSlice<T>, U).Schedule(JobHandle). (RemovedAfter 2021-06-20)", false)]
|
|
public unsafe static JobHandle Sort<T, U>(this NativeSlice<T> slice, U comp, JobHandle inputDeps)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
return Sort((T*)slice.GetUnsafePtr(), slice.Length, comp, inputDeps);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns a job which will sort this slice using a custom comparison.
|
|
/// </summary>
|
|
/// <remarks>This method does not schedule the job. Scheduling the job is left to you.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <typeparam name="U">The type of the comparer.</typeparam>
|
|
/// <param name="slice">The slice to sort.</param>
|
|
/// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
|
|
/// <returns>A job for sorting this slice.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
|
|
public unsafe static SortJob<T, U> SortJob<T, U>(this NativeSlice<T> slice, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
CheckStrideMatchesSize<T>(slice.Stride);
|
|
return SortJob((T*)slice.GetUnsafePtr(), slice.Length, comp);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds a value in this sorted slice by binary search.
|
|
/// </summary>
|
|
/// <remarks>If this slice is not sorted, the value might not be found, even if it's present in this slice.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <param name="slice">The slice to search.</param>
|
|
/// <param name="value">The value to locate.</param>
|
|
/// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int) })]
|
|
public static int BinarySearch<T>(this NativeSlice<T> slice, T value)
|
|
where T : unmanaged, IComparable<T>
|
|
{
|
|
return slice.BinarySearch(value, new DefaultComparer<T>());
|
|
}
|
|
|
|
/// <summary>
|
|
/// Finds a value in this sorted slice by binary search using a custom comparison.
|
|
/// </summary>
|
|
/// <remarks>If this slice is not sorted, the value might not be found, even if it's present in this slice.</remarks>
|
|
/// <typeparam name="T">The type of the elements.</typeparam>
|
|
/// <typeparam name="U">The type of the comparer.</typeparam>
|
|
/// <param name="slice">The slice to search.</param>
|
|
/// <param name="value">The value to locate.</param>
|
|
/// <param name="comp">The comparison function used to determine the relative order of the elements.</param>
|
|
/// <returns>If found, the index of the located value. If not found, the return value is negative.</returns>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
|
|
public unsafe static int BinarySearch<T, U>(this NativeSlice<T> slice, T value, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
return BinarySearch((T*)slice.GetUnsafePtr(), slice.Length, value, comp);
|
|
}
|
|
|
|
/// -- Internals
|
|
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(DefaultComparer<int>) })]
|
|
unsafe internal static void IntroSort<T, U>(void* array, int length, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
IntroSort<T, U>(array, 0, length - 1, 2 * CollectionHelper.Log2Floor(length), comp);
|
|
}
|
|
|
|
const int k_IntrosortSizeThreshold = 16;
|
|
unsafe static void IntroSort<T, U>(void* array, int lo, int hi, int depth, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
while (hi > lo)
|
|
{
|
|
int partitionSize = hi - lo + 1;
|
|
if (partitionSize <= k_IntrosortSizeThreshold)
|
|
{
|
|
if (partitionSize == 1)
|
|
{
|
|
return;
|
|
}
|
|
if (partitionSize == 2)
|
|
{
|
|
SwapIfGreaterWithItems<T, U>(array, lo, hi, comp);
|
|
return;
|
|
}
|
|
if (partitionSize == 3)
|
|
{
|
|
SwapIfGreaterWithItems<T, U>(array, lo, hi - 1, comp);
|
|
SwapIfGreaterWithItems<T, U>(array, lo, hi, comp);
|
|
SwapIfGreaterWithItems<T, U>(array, hi - 1, hi, comp);
|
|
return;
|
|
}
|
|
|
|
InsertionSort<T, U>(array, lo, hi, comp);
|
|
return;
|
|
}
|
|
|
|
if (depth == 0)
|
|
{
|
|
HeapSort<T, U>(array, lo, hi, comp);
|
|
return;
|
|
}
|
|
depth--;
|
|
|
|
int p = Partition<T, U>(array, lo, hi, comp);
|
|
IntroSort<T, U>(array, p + 1, hi, depth, comp);
|
|
hi = p - 1;
|
|
}
|
|
}
|
|
|
|
unsafe static void InsertionSort<T, U>(void* array, int lo, int hi, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
int i, j;
|
|
T t;
|
|
for (i = lo; i < hi; i++)
|
|
{
|
|
j = i;
|
|
t = UnsafeUtility.ReadArrayElement<T>(array, i + 1);
|
|
while (j >= lo && comp.Compare(t, UnsafeUtility.ReadArrayElement<T>(array, j)) < 0)
|
|
{
|
|
UnsafeUtility.WriteArrayElement<T>(array, j + 1, UnsafeUtility.ReadArrayElement<T>(array, j));
|
|
j--;
|
|
}
|
|
UnsafeUtility.WriteArrayElement<T>(array, j + 1, t);
|
|
}
|
|
}
|
|
|
|
unsafe static int Partition<T, U>(void* array, int lo, int hi, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
int mid = lo + ((hi - lo) / 2);
|
|
SwapIfGreaterWithItems<T, U>(array, lo, mid, comp);
|
|
SwapIfGreaterWithItems<T, U>(array, lo, hi, comp);
|
|
SwapIfGreaterWithItems<T, U>(array, mid, hi, comp);
|
|
|
|
T pivot = UnsafeUtility.ReadArrayElement<T>(array, mid);
|
|
Swap<T>(array, mid, hi - 1);
|
|
int left = lo, right = hi - 1;
|
|
|
|
while (left < right)
|
|
{
|
|
while (comp.Compare(pivot, UnsafeUtility.ReadArrayElement<T>(array, ++left)) > 0) ;
|
|
while (comp.Compare(pivot, UnsafeUtility.ReadArrayElement<T>(array, --right)) < 0) ;
|
|
|
|
if (left >= right)
|
|
break;
|
|
|
|
Swap<T>(array, left, right);
|
|
}
|
|
|
|
Swap<T>(array, left, (hi - 1));
|
|
return left;
|
|
}
|
|
|
|
unsafe static void HeapSort<T, U>(void* array, int lo, int hi, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
int n = hi - lo + 1;
|
|
|
|
for (int i = n / 2; i >= 1; i--)
|
|
{
|
|
Heapify<T, U>(array, i, n, lo, comp);
|
|
}
|
|
|
|
for (int i = n; i > 1; i--)
|
|
{
|
|
Swap<T>(array, lo, lo + i - 1);
|
|
Heapify<T, U>(array, 1, i - 1, lo, comp);
|
|
}
|
|
}
|
|
|
|
unsafe static void Heapify<T, U>(void* array, int i, int n, int lo, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
T val = UnsafeUtility.ReadArrayElement<T>(array, lo + i - 1);
|
|
int child;
|
|
while (i <= n / 2)
|
|
{
|
|
child = 2 * i;
|
|
if (child < n && (comp.Compare(UnsafeUtility.ReadArrayElement<T>(array, lo + child - 1), UnsafeUtility.ReadArrayElement<T>(array, (lo + child))) < 0))
|
|
{
|
|
child++;
|
|
}
|
|
if (comp.Compare(UnsafeUtility.ReadArrayElement<T>(array, (lo + child - 1)), val) < 0)
|
|
break;
|
|
|
|
UnsafeUtility.WriteArrayElement(array, lo + i - 1, UnsafeUtility.ReadArrayElement<T>(array, lo + child - 1));
|
|
i = child;
|
|
}
|
|
UnsafeUtility.WriteArrayElement(array, lo + i - 1, val);
|
|
}
|
|
|
|
unsafe static void Swap<T>(void* array, int lhs, int rhs) where T : unmanaged
|
|
{
|
|
T val = UnsafeUtility.ReadArrayElement<T>(array, lhs);
|
|
UnsafeUtility.WriteArrayElement(array, lhs, UnsafeUtility.ReadArrayElement<T>(array, rhs));
|
|
UnsafeUtility.WriteArrayElement(array, rhs, val);
|
|
}
|
|
|
|
unsafe static void SwapIfGreaterWithItems<T, U>(void* array, int lhs, int rhs, U comp)
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
if (lhs != rhs)
|
|
{
|
|
if (comp.Compare(UnsafeUtility.ReadArrayElement<T>(array, lhs), UnsafeUtility.ReadArrayElement<T>(array, rhs)) > 0)
|
|
{
|
|
Swap<T>(array, lhs, rhs);
|
|
}
|
|
}
|
|
}
|
|
|
|
unsafe static void IntroSortStruct<T, U>(void* array, int length, U comp)
|
|
where T : struct
|
|
where U : IComparer<T>
|
|
{
|
|
IntroSortStruct<T, U>(array, 0, length - 1, 2 * CollectionHelper.Log2Floor(length), comp);
|
|
}
|
|
|
|
unsafe static void IntroSortStruct<T, U>(void* array, int lo, int hi, int depth, U comp)
|
|
where T : struct
|
|
where U : IComparer<T>
|
|
{
|
|
while (hi > lo)
|
|
{
|
|
int partitionSize = hi - lo + 1;
|
|
if (partitionSize <= k_IntrosortSizeThreshold)
|
|
{
|
|
if (partitionSize == 1)
|
|
{
|
|
return;
|
|
}
|
|
if (partitionSize == 2)
|
|
{
|
|
SwapIfGreaterWithItemsStruct<T, U>(array, lo, hi, comp);
|
|
return;
|
|
}
|
|
if (partitionSize == 3)
|
|
{
|
|
SwapIfGreaterWithItemsStruct<T, U>(array, lo, hi - 1, comp);
|
|
SwapIfGreaterWithItemsStruct<T, U>(array, lo, hi, comp);
|
|
SwapIfGreaterWithItemsStruct<T, U>(array, hi - 1, hi, comp);
|
|
return;
|
|
}
|
|
|
|
InsertionSortStruct<T, U>(array, lo, hi, comp);
|
|
return;
|
|
}
|
|
|
|
if (depth == 0)
|
|
{
|
|
HeapSortStruct<T, U>(array, lo, hi, comp);
|
|
return;
|
|
}
|
|
depth--;
|
|
|
|
int p = PartitionStruct<T, U>(array, lo, hi, comp);
|
|
IntroSortStruct<T, U>(array, p + 1, hi, depth, comp);
|
|
hi = p - 1;
|
|
}
|
|
}
|
|
|
|
unsafe static void InsertionSortStruct<T, U>(void* array, int lo, int hi, U comp)
|
|
where T : struct
|
|
where U : IComparer<T>
|
|
{
|
|
int i, j;
|
|
T t;
|
|
for (i = lo; i < hi; i++)
|
|
{
|
|
j = i;
|
|
t = UnsafeUtility.ReadArrayElement<T>(array, i + 1);
|
|
while (j >= lo && comp.Compare(t, UnsafeUtility.ReadArrayElement<T>(array, j)) < 0)
|
|
{
|
|
UnsafeUtility.WriteArrayElement<T>(array, j + 1, UnsafeUtility.ReadArrayElement<T>(array, j));
|
|
j--;
|
|
}
|
|
UnsafeUtility.WriteArrayElement<T>(array, j + 1, t);
|
|
}
|
|
}
|
|
|
|
unsafe static int PartitionStruct<T, U>(void* array, int lo, int hi, U comp)
|
|
where T : struct
|
|
where U : IComparer<T>
|
|
{
|
|
int mid = lo + ((hi - lo) / 2);
|
|
SwapIfGreaterWithItemsStruct<T, U>(array, lo, mid, comp);
|
|
SwapIfGreaterWithItemsStruct<T, U>(array, lo, hi, comp);
|
|
SwapIfGreaterWithItemsStruct<T, U>(array, mid, hi, comp);
|
|
|
|
T pivot = UnsafeUtility.ReadArrayElement<T>(array, mid);
|
|
SwapStruct<T>(array, mid, hi - 1);
|
|
int left = lo, right = hi - 1;
|
|
|
|
while (left < right)
|
|
{
|
|
while (comp.Compare(pivot, UnsafeUtility.ReadArrayElement<T>(array, ++left)) > 0) ;
|
|
while (comp.Compare(pivot, UnsafeUtility.ReadArrayElement<T>(array, --right)) < 0) ;
|
|
|
|
if (left >= right)
|
|
break;
|
|
|
|
SwapStruct<T>(array, left, right);
|
|
}
|
|
|
|
SwapStruct<T>(array, left, (hi - 1));
|
|
return left;
|
|
}
|
|
|
|
unsafe static void HeapSortStruct<T, U>(void* array, int lo, int hi, U comp)
|
|
where T : struct
|
|
where U : IComparer<T>
|
|
{
|
|
int n = hi - lo + 1;
|
|
|
|
for (int i = n / 2; i >= 1; i--)
|
|
{
|
|
HeapifyStruct<T, U>(array, i, n, lo, comp);
|
|
}
|
|
|
|
for (int i = n; i > 1; i--)
|
|
{
|
|
SwapStruct<T>(array, lo, lo + i - 1);
|
|
HeapifyStruct<T, U>(array, 1, i - 1, lo, comp);
|
|
}
|
|
}
|
|
|
|
unsafe static void HeapifyStruct<T, U>(void* array, int i, int n, int lo, U comp)
|
|
where T : struct
|
|
where U : IComparer<T>
|
|
{
|
|
T val = UnsafeUtility.ReadArrayElement<T>(array, lo + i - 1);
|
|
int child;
|
|
while (i <= n / 2)
|
|
{
|
|
child = 2 * i;
|
|
if (child < n && (comp.Compare(UnsafeUtility.ReadArrayElement<T>(array, lo + child - 1), UnsafeUtility.ReadArrayElement<T>(array, (lo + child))) < 0))
|
|
{
|
|
child++;
|
|
}
|
|
if (comp.Compare(UnsafeUtility.ReadArrayElement<T>(array, (lo + child - 1)), val) < 0)
|
|
break;
|
|
|
|
UnsafeUtility.WriteArrayElement(array, lo + i - 1, UnsafeUtility.ReadArrayElement<T>(array, lo + child - 1));
|
|
i = child;
|
|
}
|
|
UnsafeUtility.WriteArrayElement(array, lo + i - 1, val);
|
|
}
|
|
|
|
unsafe static void SwapStruct<T>(void* array, int lhs, int rhs)
|
|
where T : struct
|
|
{
|
|
T val = UnsafeUtility.ReadArrayElement<T>(array, lhs);
|
|
UnsafeUtility.WriteArrayElement(array, lhs, UnsafeUtility.ReadArrayElement<T>(array, rhs));
|
|
UnsafeUtility.WriteArrayElement(array, rhs, val);
|
|
}
|
|
|
|
unsafe static void SwapIfGreaterWithItemsStruct<T, U>(void* array, int lhs, int rhs, U comp)
|
|
where T : struct
|
|
where U : IComparer<T>
|
|
{
|
|
if (lhs != rhs)
|
|
{
|
|
if (comp.Compare(UnsafeUtility.ReadArrayElement<T>(array, lhs), UnsafeUtility.ReadArrayElement<T>(array, rhs)) > 0)
|
|
{
|
|
SwapStruct<T>(array, lhs, rhs);
|
|
}
|
|
}
|
|
}
|
|
|
|
[BurstCompile]
|
|
unsafe struct SegmentSort<T, U> : IJobParallelFor
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
[NativeDisableUnsafePtrRestriction]
|
|
public T* Data;
|
|
public U Comp;
|
|
|
|
public int Length;
|
|
public int SegmentWidth;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
var startIndex = index * SegmentWidth;
|
|
var segmentLength = ((Length - startIndex) < SegmentWidth) ? (Length - startIndex) : SegmentWidth;
|
|
Sort(Data + startIndex, segmentLength, Comp);
|
|
}
|
|
}
|
|
|
|
[BurstCompile]
|
|
unsafe struct SegmentSortMerge<T, U> : IJob
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
[NativeDisableUnsafePtrRestriction]
|
|
public T* Data;
|
|
public U Comp;
|
|
|
|
public int Length;
|
|
public int SegmentWidth;
|
|
|
|
public void Execute()
|
|
{
|
|
var segmentCount = (Length + (SegmentWidth - 1)) / SegmentWidth;
|
|
var segmentIndex = stackalloc int[segmentCount];
|
|
|
|
var resultCopy = (T*)Memory.Unmanaged.Allocate(UnsafeUtility.SizeOf<T>() * Length, 16, Allocator.Temp);
|
|
|
|
for (int sortIndex = 0; sortIndex < Length; sortIndex++)
|
|
{
|
|
// find next best
|
|
int bestSegmentIndex = -1;
|
|
T bestValue = default(T);
|
|
|
|
for (int i = 0; i < segmentCount; i++)
|
|
{
|
|
var startIndex = i * SegmentWidth;
|
|
var offset = segmentIndex[i];
|
|
var segmentLength = ((Length - startIndex) < SegmentWidth) ? (Length - startIndex) : SegmentWidth;
|
|
if (offset == segmentLength)
|
|
continue;
|
|
|
|
var nextValue = Data[startIndex + offset];
|
|
if (bestSegmentIndex != -1)
|
|
{
|
|
if (Comp.Compare(nextValue, bestValue) > 0)
|
|
continue;
|
|
}
|
|
|
|
bestValue = nextValue;
|
|
bestSegmentIndex = i;
|
|
}
|
|
|
|
segmentIndex[bestSegmentIndex]++;
|
|
resultCopy[sortIndex] = bestValue;
|
|
}
|
|
|
|
UnsafeUtility.MemCpy(Data, resultCopy, UnsafeUtility.SizeOf<T>() * Length);
|
|
}
|
|
}
|
|
|
|
[Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")]
|
|
static void CheckStrideMatchesSize<T>(int stride) where T : struct
|
|
{
|
|
if (stride != UnsafeUtility.SizeOf<T>())
|
|
{
|
|
throw new InvalidOperationException("Sort requires that stride matches the size of the source type");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returned by the `SortJob` methods of <see cref="Unity.Collections.NativeSortExtension"/>. Call `Schedule` to schedule the sorting.
|
|
/// </summary>
|
|
/// <typeparam name="T">The type of the elements to sort.</typeparam>
|
|
/// <typeparam name="U">The type of the comparer.</typeparam>
|
|
[BurstCompatible(GenericTypeArguments = new[] { typeof(int), typeof(NativeSortExtension.DefaultComparer<int>) }, RequiredUnityDefine = "UNITY_2020_2_OR_NEWER" /* Due to job scheduling on 2020.1 using statics */)]
|
|
public unsafe struct SortJob<T, U>
|
|
where T : unmanaged
|
|
where U : IComparer<T>
|
|
{
|
|
/// <summary>
|
|
/// The data to sort.
|
|
/// </summary>
|
|
public T* Data;
|
|
|
|
/// <summary>
|
|
/// Comparison function.
|
|
/// </summary>
|
|
public U Comp;
|
|
|
|
/// <summary>
|
|
/// The length to sort.
|
|
/// </summary>
|
|
public int Length;
|
|
|
|
[BurstCompile]
|
|
struct SegmentSort : IJobParallelFor
|
|
{
|
|
[NativeDisableUnsafePtrRestriction]
|
|
public T* Data;
|
|
public U Comp;
|
|
|
|
public int Length;
|
|
public int SegmentWidth;
|
|
|
|
public void Execute(int index)
|
|
{
|
|
var startIndex = index * SegmentWidth;
|
|
var segmentLength = ((Length - startIndex) < SegmentWidth) ? (Length - startIndex) : SegmentWidth;
|
|
NativeSortExtension.Sort(Data + startIndex, segmentLength, Comp);
|
|
}
|
|
}
|
|
|
|
[BurstCompile]
|
|
struct SegmentSortMerge : IJob
|
|
{
|
|
[NativeDisableUnsafePtrRestriction]
|
|
public T* Data;
|
|
public U Comp;
|
|
|
|
public int Length;
|
|
public int SegmentWidth;
|
|
|
|
public void Execute()
|
|
{
|
|
var segmentCount = (Length + (SegmentWidth - 1)) / SegmentWidth;
|
|
var segmentIndex = stackalloc int[segmentCount];
|
|
|
|
var resultCopy = (T*)Memory.Unmanaged.Allocate(UnsafeUtility.SizeOf<T>() * Length, 16, Allocator.Temp);
|
|
|
|
for (int sortIndex = 0; sortIndex < Length; sortIndex++)
|
|
{
|
|
// find next best
|
|
int bestSegmentIndex = -1;
|
|
T bestValue = default(T);
|
|
|
|
for (int i = 0; i < segmentCount; i++)
|
|
{
|
|
var startIndex = i * SegmentWidth;
|
|
var offset = segmentIndex[i];
|
|
var segmentLength = ((Length - startIndex) < SegmentWidth) ? (Length - startIndex) : SegmentWidth;
|
|
if (offset == segmentLength)
|
|
continue;
|
|
|
|
var nextValue = Data[startIndex + offset];
|
|
if (bestSegmentIndex != -1)
|
|
{
|
|
if (Comp.Compare(nextValue, bestValue) > 0)
|
|
continue;
|
|
}
|
|
|
|
bestValue = nextValue;
|
|
bestSegmentIndex = i;
|
|
}
|
|
|
|
segmentIndex[bestSegmentIndex]++;
|
|
resultCopy[sortIndex] = bestValue;
|
|
}
|
|
|
|
UnsafeUtility.MemCpy(Data, resultCopy, UnsafeUtility.SizeOf<T>() * Length);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Schedules this job.
|
|
/// </summary>
|
|
/// <param name="inputDeps">Handle of a job to depend upon.</param>
|
|
/// <returns>The handle of this newly scheduled job.</returns>
|
|
[NotBurstCompatible /* This is not burst compatible because of IJob's use of a static IntPtr. Should switch to IJobBurstSchedulable in the future */]
|
|
public JobHandle Schedule(JobHandle inputDeps = default)
|
|
{
|
|
if (Length == 0)
|
|
return inputDeps;
|
|
var segmentCount = (Length + 1023) / 1024;
|
|
var workerCount = math.max(1, JobsUtility.MaxJobThreadCount);
|
|
var workerSegmentCount = segmentCount / workerCount;
|
|
var segmentSortJob = new SegmentSort { Data = Data, Comp = Comp, Length = Length, SegmentWidth = 1024 };
|
|
var segmentSortJobHandle = segmentSortJob.Schedule(segmentCount, workerSegmentCount, inputDeps);
|
|
var segmentSortMergeJob = new SegmentSortMerge { Data = Data, Comp = Comp, Length = Length, SegmentWidth = 1024 };
|
|
var segmentSortMergeJobHandle = segmentSortMergeJob.Schedule(segmentSortJobHandle);
|
|
return segmentSortMergeJobHandle;
|
|
}
|
|
}
|
|
}
|