diff --git a/src/Internal/ArraySortHalperX.cs b/src/Internal/ArraySortHalperX.cs deleted file mode 100644 index 293007d..0000000 --- a/src/Internal/ArraySortHalperX.cs +++ /dev/null @@ -1,272 +0,0 @@ -#if DISABLE_DEBUG -#undef DEBUG -#endif -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; -#if ENABLE_IL2CPP -using Unity.IL2CPP.CompilerServices; -#endif - -namespace DCFApixels.DragonECS.Core.Internal -{ - internal interface IStructComparer : IComparer - { - // a > b = return > 0 - // int Compare(T a, T b); - } - -#if ENABLE_IL2CPP - [Il2CppSetOption(Option.NullChecks, false)] - [Il2CppSetOption(Option.ArrayBoundsChecks, false)] -#endif - internal static class ArraySortHalperX - { - private const int IntrosortSizeThreshold = 16; - - #region IStructComparer - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SwapIfGreater(T[] items, ref TComparer comparer, int i, int j) where TComparer : IStructComparer - { - if (comparer.Compare(items[i], items[j]) > 0) - { - T key = items[i]; - items[i] = items[j]; - items[j] = key; - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void InsertionSort(T[] items, ref TComparer comparer) where TComparer : IStructComparer - { - for (int i = 0; i < items.Length - 1; i++) - { - T t = items[i + 1]; - - int j = i; - while (j >= 0 && comparer.Compare(t, items[j]) < 0) - { - items[j + 1] = items[j]; - j--; - } - - items[j + 1] = t; - } - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Sort(T[] items, ref TComparer comparer) where TComparer : IStructComparer - { - int length = items.Length; - if (length == 1) - { - return; - } - - if (length <= IntrosortSizeThreshold) - { - - if (length == 2) - { - SwapIfGreater(items, ref comparer, 0, 1); - return; - } - - if (length == 3) - { - SwapIfGreater(items, ref comparer, 0, 1); - SwapIfGreater(items, ref comparer, 0, 2); - SwapIfGreater(items, ref comparer, 1, 2); - return; - } - - InsertionSort(items, ref comparer); - return; - } - - IStructComparer packed = comparer; - Array.Sort(items, 0, items.Length, packed); - comparer = (TComparer)packed; - } - #endregion - - #region Comparison - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SwapIfGreater(T[] items, Comparison comparison, int i, int j) - { - if (comparison(items[i], items[j]) > 0) - { - T key = items[i]; - items[i] = items[j]; - items[j] = key; - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void InsertionSort(T[] items, int length, Comparison comparison) - { - for (int i = 0; i < length - 1; i++) - { - T t = items[i + 1]; - - int j = i; - while (j >= 0 && comparison(t, items[j]) < 0) - { - items[j + 1] = items[j]; - j--; - } - - items[j + 1] = t; - } - } - -#if ENABLE_IL2CPP - [Il2CppSetOption(Option.NullChecks, false)] - [Il2CppSetOption(Option.ArrayBoundsChecks, false)] -#endif - private class ComparisonHach : IComparer - { - public static readonly ComparisonHach Instance = new ComparisonHach(); - public Comparison comparison; - private ComparisonHach() { } - public int Compare(T x, T y) { return comparison(x, y); } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Sort(T[] items, Comparison comparison) - { - Sort(items, comparison, items.Length); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void Sort(T[] items, Comparison comparison, int length) - { - if (length <= IntrosortSizeThreshold) - { - if (length == 1) - { - return; - } - if (length == 2) - { - SwapIfGreater(items, comparison, 0, 1); - return; - } - if (length == 3) - { - SwapIfGreater(items, comparison, 0, 1); - SwapIfGreater(items, comparison, 0, 2); - SwapIfGreater(items, comparison, 1, 2); - return; - } - InsertionSort(items, length, comparison); - return; - } - ComparisonHach.Instance.comparison = comparison; - Array.Sort(items, 0, length, ComparisonHach.Instance); - } - #endregion - } - -#if ENABLE_IL2CPP - [Il2CppSetOption(Option.NullChecks, false)] - [Il2CppSetOption(Option.ArrayBoundsChecks, false)] -#endif - internal static unsafe class UnsafeArraySortHalperX where T : unmanaged - { - private const int IntrosortSizeThreshold = 16; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void SwapIfGreater(T* items, ref TComparer comparer, int i, int j) where TComparer : IStructComparer - { - if (comparer.Compare(items[i], items[j]) > 0) - { - T key = items[i]; - items[i] = items[j]; - items[j] = key; - } - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static void InsertionSort_Unchecked(T* items, int length, ref TComparer comparer) where TComparer : IStructComparer - { - for (int i = 0; i < length - 1; i++) - { - T t = items[i + 1]; - - int j = i; - while (j >= 0 && comparer.Compare(t, items[j]) < 0) - { - items[j + 1] = items[j]; - j--; - } - - items[j + 1] = t; - } - } - //[MethodImpl(MethodImplOptions.AggressiveInlining)] - //public static void OptimizedBubbleSort_Unchecked(T* items, int length, ref TComparer comparer) where TComparer : IComparerX - //{ - // for (int i = 0, n = length - 1; i < n; i++) - // { - // bool noSwaped = true; - // for (int j = 0; j < n - i;) - // { - // ref T j0 = ref items[j++]; - // if(comparer.Compare(j0, items[j]) < 0) - // { - // T tmp = items[j]; - // items[j] = j0; - // j0 = tmp; - // noSwaped = false; - // } - // } - // if (noSwaped) - // break; - // } - //} - public static void InsertionSort(T* items, int length, ref TComparer comparer) where TComparer : IStructComparer - { - if (length == 1) - { - return; - } - - if (length == 2) - { - SwapIfGreater(items, ref comparer, 0, 1); - return; - } - - if (length == 3) - { - SwapIfGreater(items, ref comparer, 0, 1); - SwapIfGreater(items, ref comparer, 0, 2); - SwapIfGreater(items, ref comparer, 1, 2); - return; - } - - InsertionSort_Unchecked(items, length, ref comparer); - } - //public static void OptimizedBubbleSort(T* items, int length, ref TComparer comparer) where TComparer : IComparerX - //{ - // if (length == 1) - // { - // return; - // } - // - // if (length == 2) - // { - // SwapIfGreater(items, ref comparer, 0, 1); - // return; - // } - // - // if (length == 3) - // { - // SwapIfGreater(items, ref comparer, 0, 1); - // SwapIfGreater(items, ref comparer, 0, 2); - // SwapIfGreater(items, ref comparer, 1, 2); - // return; - // } - // - // OptimizedBubbleSort_Unchecked(items, length, ref comparer); - //} - } -} - - diff --git a/src/Internal/ArraySortUtility.cs b/src/Internal/ArraySortUtility.cs new file mode 100644 index 0000000..9b5529f --- /dev/null +++ b/src/Internal/ArraySortUtility.cs @@ -0,0 +1,246 @@ +#if DISABLE_DEBUG +#undef DEBUG +#endif +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Numerics; +using System.Runtime.CompilerServices; +#if ENABLE_IL2CPP +using Unity.IL2CPP.CompilerServices; +#endif + +namespace DCFApixels.DragonECS.Core.Internal +{ + // a > b = return > 0 + // int Compare(T a, T b); + + //IComparer comparer + //if (comparer == null) + //{ + // comparer = Comparer.Default; + //} +#if ENABLE_IL2CPP + [Il2CppSetOption(Option.NullChecks, false)] + [Il2CppSetOption(Option.ArrayBoundsChecks, false)] +#endif + internal static class ArraySortUtility + where TComparer : struct, IComparer + { + private const int IntrosortSizeThreshold = 16; + + #region Public + public static void Sort(Span keys, ref TComparer comparer) + { + // Add a try block here to detect IComparers (or their + // underlying IComparables, etc) that are bogus. + IntrospectiveSort(keys, ref comparer); + } + public static int BinarySearch(T[] array, int index, int length, T value, ref TComparer comparer) + { + return InternalBinarySearch(array, index, length, value, ref comparer); + } + #endregion + + #region Internal + internal static int InternalBinarySearch(T[] array, int index, int length, T value, ref TComparer comparer) + { + Debug.Assert(array != null, "Check the arguments in the caller!"); + Debug.Assert(index >= 0 && length >= 0 && (array.Length - index >= length), "Check the arguments in the caller!"); + + int lo = index; + int hi = index + length - 1; + while (lo <= hi) + { + int i = lo + ((hi - lo) >> 1); + int order = comparer.Compare(array[i], value); + + if (order == 0) return i; + if (order < 0) + { + lo = i + 1; + } + else + { + hi = i - 1; + } + } + + return ~lo; + } + + private static void SwapIfGreater(Span keys, ref TComparer comparer, int i, int j) + { + Debug.Assert(i != j); + if (comparer.Compare(keys[i], keys[j]) > 0) + { + T key = keys[i]; + keys[i] = keys[j]; + keys[j] = key; + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static void Swap(Span a, int i, int j) + { + Debug.Assert(i != j); + T t = a[i]; + a[i] = a[j]; + a[j] = t; + } + + internal static void IntrospectiveSort(Span keys, ref TComparer comparer) + { + //Debug.Assert(comparer != null); + if (keys.Length > 1) + { + IntroSort(keys, 2 * (BitOperations.Log2((uint)keys.Length) + 1), ref comparer); + } + } + + // IntroSort is recursive; block it from being inlined into itself as + // this is currenly not profitable. + [MethodImpl(MethodImplOptions.NoInlining)] + private static void IntroSort(Span keys, int depthLimit, ref TComparer comparer) + { + Debug.Assert(!keys.IsEmpty); + Debug.Assert(depthLimit >= 0); + //Debug.Assert(comparer != null); + + int partitionSize = keys.Length; + while (partitionSize > 1) + { + if (partitionSize <= IntrosortSizeThreshold) + { + + if (partitionSize == 2) + { + SwapIfGreater(keys, ref comparer, 0, 1); + return; + } + + if (partitionSize == 3) + { + SwapIfGreater(keys, ref comparer, 0, 1); + SwapIfGreater(keys, ref comparer, 0, 2); + SwapIfGreater(keys, ref comparer, 1, 2); + return; + } + + InsertionSort(keys.Slice(0, partitionSize), ref comparer); + return; + } + + if (depthLimit == 0) + { + HeapSort(keys.Slice(0, partitionSize), ref comparer); + return; + } + depthLimit--; + + int p = PickPivotAndPartition(keys.Slice(0, partitionSize), ref comparer); + + // Note we've already partitioned around the pivot and do not have to move the pivot again. + IntroSort(keys.Slice(p + 1, partitionSize - (p + 1)), depthLimit, ref comparer); + partitionSize = p; + } + } + + private static int PickPivotAndPartition(Span keys, ref TComparer comparer) + { + Debug.Assert(keys.Length >= IntrosortSizeThreshold); + //Debug.Assert(comparer != null); + + int hi = keys.Length - 1; + + // Compute median-of-three. But also partition them, since we've done the comparison. + int middle = hi >> 1; + + // Sort lo, mid and hi appropriately, then pick mid as the pivot. + SwapIfGreater(keys, ref comparer, 0, middle); // swap the low with the mid point + SwapIfGreater(keys, ref comparer, 0, hi); // swap the low with the high + SwapIfGreater(keys, ref comparer, middle, hi); // swap the middle with the high + + T pivot = keys[middle]; + Swap(keys, middle, hi - 1); + int left = 0, right = hi - 1; // We already partitioned lo and hi and put the pivot in hi - 1. And we pre-increment & decrement below. + + while (left < right) + { + while (comparer.Compare(keys[++left], pivot) < 0) ; + while (comparer.Compare(pivot, keys[--right]) < 0) ; + + if (left >= right) + break; + + Swap(keys, left, right); + } + + // Put pivot in the right location. + if (left != hi - 1) + { + Swap(keys, left, hi - 1); + } + return left; + } + + private static void HeapSort(Span keys, ref TComparer comparer) + { + //Debug.Assert(comparer != null); + Debug.Assert(!keys.IsEmpty); + + int n = keys.Length; + for (int i = n >> 1; i >= 1; i--) + { + DownHeap(keys, i, n, ref comparer); + } + + for (int i = n; i > 1; i--) + { + Swap(keys, 0, i - 1); + DownHeap(keys, 1, i - 1, ref comparer); + } + } + + private static void DownHeap(Span keys, int i, int n, ref TComparer comparer) + { + //Debug.Assert(comparer != null); + + T d = keys[i - 1]; + while (i <= n >> 1) + { + int child = 2 * i; + if (child < n && comparer.Compare(keys[child - 1], keys[child]) < 0) + { + child++; + } + + if (!(comparer.Compare(d, keys[child - 1]) < 0)) + break; + + keys[i - 1] = keys[child - 1]; + i = child; + } + + keys[i - 1] = d; + } + + private static void InsertionSort(Span keys, ref TComparer comparer) + { + for (int i = 0; i < keys.Length - 1; i++) + { + T t = keys[i + 1]; + + int j = i; + while (j >= 0 && comparer.Compare(t, keys[j]) < 0) + { + keys[j + 1] = keys[j]; + j--; + } + + keys[j + 1] = t; + } + } + #endregion + } +} diff --git a/src/Internal/ArrayUtility.cs b/src/Internal/ArrayUtility.cs index e832420..8457514 100644 --- a/src/Internal/ArrayUtility.cs +++ b/src/Internal/ArrayUtility.cs @@ -284,21 +284,26 @@ namespace DCFApixels.DragonECS.Core.Internal internal static class SpanUtility { + internal static StructComparison ToStruct(this Comparison self) + { + return new StructComparison(self); + } public static void Sort(Span span, Comparison comparison) { - span.Sort(new StructComparison(comparison)); + var c = comparison.ToStruct(); + ArraySortUtility>.Sort(span, ref c); } - private readonly struct StructComparison : IComparer + } + internal readonly struct StructComparison : IComparer + { + public readonly Comparison Comparison; + public StructComparison(Comparison comparison) { - public readonly Comparison Comparison; - public StructComparison(Comparison comparison) - { - Comparison = comparison; - } - public int Compare(T x, T y) - { - return Comparison(x, y); - } + Comparison = comparison; + } + public int Compare(T x, T y) + { + return Comparison(x, y); } } } \ No newline at end of file