Skip to content
This repository has been archived by the owner on Jan 23, 2023. It is now read-only.

Add Sort(...) extension methods for Span<T> #16986

Closed
wants to merge 8 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/mscorlib/Resources/Strings.resx
Original file line number Diff line number Diff line change
Expand Up @@ -3718,4 +3718,10 @@
<data name="Arg_MustBeNullTerminatedString" xml:space="preserve">
<value>The string must be null-terminated.</value>
</data>
</root>
<data name="Arg_BogusIComparable" xml:space="preserve">
<value>Unable to sort because the IComparable.CompareTo() method returns inconsistent results. Either a value does not compare equal to itself, or one value repeatedly compared to another value yields different results. IComparable: '{0}'.</value>
</data>
<data name="Arg_ItemsMustHaveSameLengthAsKeys" xml:space="preserve">
<value>Items must have same length as keys.</value>
</data>
</root>
13 changes: 13 additions & 0 deletions src/mscorlib/shared/System.Private.CoreLib.Shared.projitems
Original file line number Diff line number Diff line change
Expand Up @@ -491,6 +491,19 @@
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.BinarySearch.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.Byte.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanSortHelpers.Common.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanSortHelpers.Keys.Comparison.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanSortHelpers.Keys.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanSortHelpers.Keys.IComparable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanSortHelpers.Keys.Specialized.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanSortHelpers.Keys.TComparer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanSortHelpers.Keys.TDirectComparer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanSortHelpers.KeysValues.Comparison.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanSortHelpers.KeysValues.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanSortHelpers.KeysValues.IComparable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanSortHelpers.KeysValues.Specialized.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanSortHelpers.KeysValues.TComparer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanSortHelpers.KeysValues.TDirectComparer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanHelpers.T.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\String.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\String.Manipulation.cs" />
Expand Down
76 changes: 76 additions & 0 deletions src/mscorlib/shared/System/MemoryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1304,5 +1304,81 @@ public static int BinarySearch<T, TComparer>(
value, comparer);
return BinarySearch(span, comparable);
}

/// <summary>
/// Sorts the elements in the entire <see cref="Span{T}" />
/// using the <see cref="IComparable" /> implementation of each
/// element of the <see cref= "Span{T}" />
/// </summary>
/// <param name="span">The <see cref="Span{T}"/> to sort.</param>
/// <exception cref = "InvalidOperationException">
/// One or more elements do not implement the <see cref="IComparable" /> interface.
/// </exception>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sort<T>(this Span<T> span) => SpanSortHelpersKeys.Sort(span);

/// <summary>
/// Sorts the elements in the entire <see cref="Span{T}" />
/// using the <typeparamref name="TComparer" />.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sort<T, TComparer>(this Span<T> span, TComparer comparer)
where TComparer : IComparer<T> =>
SpanSortHelpersKeys.Sort(span, comparer);

/// <summary>
/// Sorts the elements in the entire <see cref="Span{T}" />
/// using the <see cref="Comparison{T}" />.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sort<T>(this Span<T> span, Comparison<T> comparison)
{
if (comparison == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison);

SpanSortHelpersKeys.Sort(span, comparison);
}

/// <summary>
/// Sorts a pair of spans
/// (one contains the keys <see cref="Span{TKey}"/>
/// and the other contains the corresponding items <see cref="Span{TValue}"/>)
/// based on the keys in the first <see cref= "Span{TKey}" />
/// using the <see cref="IComparable" /> implementation of each
/// element of the <see cref= "Span{TKey}"/>.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sort<TKey, TValue>(this Span<TKey> keys, Span<TValue> items) =>
SpanSortHelpersKeysValues.Sort(keys, items);

/// <summary>
/// Sorts a pair of spans
/// (one contains the keys <see cref="Span{TKey}"/>
/// and the other contains the corresponding items <see cref="Span{TValue}"/>)
/// based on the keys in the first <see cref= "Span{TKey}" />
/// using the <typeparamref name="TComparer" />.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sort<TKey, TValue, TComparer>(this Span<TKey> keys,
Span<TValue> items, TComparer comparer)
where TComparer : IComparer<TKey> =>
SpanSortHelpersKeysValues.Sort(keys, items, comparer);

/// <summary>
/// Sorts a pair of spans
/// (one contains the keys <see cref="Span{TKey}"/>
/// and the other contains the corresponding items <see cref="Span{TValue}"/>)
/// based on the keys in the first <see cref= "Span{TKey}" />
/// using the <see cref="Comparison{TKey}" />.
/// </summary>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void Sort<TKey, TValue>(this Span<TKey> keys,
Span<TValue> items, Comparison<TKey> comparison)
{
if (comparison == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparison);

SpanSortHelpersKeysValues.Sort(keys, items, comparison);
}
}
}
125 changes: 125 additions & 0 deletions src/mscorlib/shared/System/SpanSortHelpers.Common.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

using System.Diagnostics;
using System.Runtime.CompilerServices;

#if !netstandard
using Internal.Runtime.CompilerServices;
#endif

namespace System
{
internal static partial class SpanSortHelpersCommon
{
// This is the threshold where Introspective sort switches to Insertion sort.
// Empirically, 16 seems to speed up most cases without slowing down others, at least for integers.
// Large value types may benefit from a smaller number.
internal const int IntrosortSizeThreshold = 16;

internal static int FloorLog2PlusOne(int n)
{
Debug.Assert(n >= 2);
int result = 2;
n >>= 2;
while (n > 0)
{
++result;
n >>= 1;
}
return result;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Swap<T>(ref T items, int i, int j)
{
Debug.Assert(i != j);
Swap(ref Unsafe.Add(ref items, i), ref Unsafe.Add(ref items, j));
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static void Swap<T>(ref T a, ref T b)
{
T temp = a;
a = b;
b = temp;
}

// This started out with just LessThan.
// However, due to bogus comparers, comparables etc.
// we need to preserve semantics completely to get same result.
internal interface IDirectComparer<in T>
{
bool GreaterThan(T x, T y);
bool LessThan(T x, T y);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use default implementation syntax? If yes perhaps we could get more short source code.

}

//
// Type specific DirectComparer(s) to ensure optimal code-gen
//
internal struct SByteDirectComparer : IDirectComparer<sbyte>
{
public bool GreaterThan(sbyte x, sbyte y) => x > y;
public bool LessThan(sbyte x, sbyte y) => x < y;
}
internal struct ByteDirectComparer : IDirectComparer<byte>
{
public bool GreaterThan(byte x, byte y) => x > y;
public bool LessThan(byte x, byte y) => x < y;
}
internal struct Int16DirectComparer : IDirectComparer<short>
{
public bool GreaterThan(short x, short y) => x > y;
public bool LessThan(short x, short y) => x < y;
}
internal struct UInt16DirectComparer : IDirectComparer<ushort>
{
public bool GreaterThan(ushort x, ushort y) => x > y;
public bool LessThan(ushort x, ushort y) => x < y;
}
internal struct Int32DirectComparer : IDirectComparer<int>
{
public bool GreaterThan(int x, int y) => x > y;
public bool LessThan(int x, int y) => x < y;
}
internal struct UInt32DirectComparer : IDirectComparer<uint>
{
public bool GreaterThan(uint x, uint y) => x > y;
public bool LessThan(uint x, uint y) => x < y;
}
internal struct Int64DirectComparer : IDirectComparer<long>
{
public bool GreaterThan(long x, long y) => x > y;
public bool LessThan(long x, long y) => x < y;
}
internal struct UInt64DirectComparer : IDirectComparer<ulong>
{
public bool GreaterThan(ulong x, ulong y) => x > y;
public bool LessThan(ulong x, ulong y) => x < y;
}
internal struct SingleDirectComparer : IDirectComparer<float>
{
public bool GreaterThan(float x, float y) => x > y;
public bool LessThan(float x, float y) => x < y;
}
internal struct DoubleDirectComparer : IDirectComparer<double>
{
public bool GreaterThan(double x, double y) => x > y;
public bool LessThan(double x, double y) => x < y;
}

internal interface IIsNaN<T>
{
bool IsNaN(T value);
}
internal struct SingleIsNaN : IIsNaN<float>
{
public bool IsNaN(float value) => float.IsNaN(value);
}
internal struct DoubleIsNaN : IIsNaN<double>
{
public bool IsNaN(double value) => double.IsNaN(value);
}
}
}
Loading