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 3 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
6 changes: 6 additions & 0 deletions 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>
<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>
Copy link
Member

Choose a reason for hiding this comment

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

nit add period at the end.

</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.KeysValues.TDirectComparer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\SpanSortHelpers.Common.cs" />
Copy link
Member

Choose a reason for hiding this comment

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

nit: sort order alphabetically

<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\SpanHelpers.T.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\String.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\String.Manipulation.cs" />
Expand Down
85 changes: 85 additions & 0 deletions src/mscorlib/shared/System/MemoryExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1304,5 +1304,90 @@ 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);
}
}
}
175 changes: 175 additions & 0 deletions src/mscorlib/shared/System/SpanSortHelpers.Common.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;

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

namespace System
{
// TODO: Rename to SpanSortHelpers before move to corefx
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.

bool LessThanEqual(T x, T y); // TODO: Delete if we are not doing specialize Sort3
}
//
// Type specific DirectComparer(s) to ensure optimal code-gen
//
internal struct SByteDirectComparer : IDirectComparer<sbyte>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GreaterThan(sbyte x, sbyte y) => x > y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThan(sbyte x, sbyte y) => x < y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThanEqual(sbyte x, sbyte y) => x <= y;
}
internal struct ByteDirectComparer : IDirectComparer<byte>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GreaterThan(byte x, byte y) => x > y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThan(byte x, byte y) => x < y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThanEqual(byte x, byte y) => x <= y;
}
internal struct Int16DirectComparer : IDirectComparer<short>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GreaterThan(short x, short y) => x > y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThan(short x, short y) => x < y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThanEqual(short x, short y) => x <= y;
}
internal struct UInt16DirectComparer : IDirectComparer<ushort>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GreaterThan(ushort x, ushort y) => x > y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThan(ushort x, ushort y) => x < y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThanEqual(ushort x, ushort y) => x <= y;
}
internal struct Int32DirectComparer : IDirectComparer<int>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GreaterThan(int x, int y) => x > y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThan(int x, int y) => x < y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThanEqual(int x, int y) => x <= y;
}
internal struct UInt32DirectComparer : IDirectComparer<uint>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GreaterThan(uint x, uint y) => x > y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThan(uint x, uint y) => x < y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThanEqual(uint x, uint y) => x <= y;
}
internal struct Int64DirectComparer : IDirectComparer<long>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GreaterThan(long x, long y) => x > y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThan(long x, long y) => x < y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThanEqual(long x, long y) => x <= y;
}
internal struct UInt64DirectComparer : IDirectComparer<ulong>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GreaterThan(ulong x, ulong y) => x > y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThan(ulong x, ulong y) => x < y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThanEqual(ulong x, ulong y) => x <= y;
}
internal struct SingleDirectComparer : IDirectComparer<float>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GreaterThan(float x, float y) => x > y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThan(float x, float y) => x < y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThanEqual(float x, float y) => x <= y;
}
internal struct DoubleDirectComparer : IDirectComparer<double>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GreaterThan(double x, double y) => x > y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThan(double x, double y) => x < y;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThanEqual(double x, double y) => x <= y;
}
// TODO: Revise whether this is needed
internal struct StringDirectComparer : IDirectComparer<string>
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool GreaterThan(string x, string y) => x.CompareTo(y) > 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThan(string x, string y) => x.CompareTo(y) < 0;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public bool LessThanEqual(string x, string y) => x.CompareTo(y) <= 0;
}

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