From baff8f3f4089af04af431e7e395860bf6e106561 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Andr=C3=A9?= <2341261+manandre@users.noreply.github.com> Date: Sun, 17 May 2026 21:29:54 +0000 Subject: [PATCH 01/13] Add MemoryExtensions.Min/Max --- .../System.Memory/ref/System.Memory.cs | 4 + .../tests/ReadOnlySpan/MinMax.cs | 86 ++++++++++++ .../tests/System.Memory.Tests.csproj | 1 + .../src/System/MemoryExtensions.cs | 126 ++++++++++++++++++ 4 files changed, 217 insertions(+) create mode 100644 src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs diff --git a/src/libraries/System.Memory/ref/System.Memory.cs b/src/libraries/System.Memory/ref/System.Memory.cs index 7d53b7a8da7630..9642b313123609 100644 --- a/src/libraries/System.Memory/ref/System.Memory.cs +++ b/src/libraries/System.Memory/ref/System.Memory.cs @@ -230,6 +230,10 @@ public static partial class MemoryExtensions public static int BinarySearch(this System.Span span, T value, TComparer comparer) where TComparer : System.Collections.Generic.IComparer, allows ref struct { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] public static int BinarySearch(this System.Span span, TComparable comparable) where TComparable : System.IComparable, allows ref struct { throw null; } + public static T? Max(this System.ReadOnlySpan span) { throw null; } + public static T? Max(this System.ReadOnlySpan span, System.Collections.Generic.IComparer comparer) { throw null; } + public static T? Min(this System.ReadOnlySpan span) { throw null; } + public static T? Min(this System.ReadOnlySpan span, System.Collections.Generic.IComparer comparer) { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] public static int CommonPrefixLength(this System.Span span, System.ReadOnlySpan other) { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs new file mode 100644 index 00000000000000..c3cf844d904df3 --- /dev/null +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs @@ -0,0 +1,86 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System.Collections.Generic; +using Xunit; + +namespace System.SpanTests +{ + public static partial class ReadOnlySpanTests + { + [Fact] + public static void MinMax_Empty_NonNullableValueType_Throws() + { + ReadOnlySpan span = ReadOnlySpan.Empty; + + TestHelpers.AssertThrows(span, (_span) => _span.Min()); + TestHelpers.AssertThrows(span, (_span) => _span.Max()); + TestHelpers.AssertThrows(span, (_span) => _span.Min(Comparer.Default)); + TestHelpers.AssertThrows(span, (_span) => _span.Max(Comparer.Default)); + } + + [Fact] + public static void MinMax_Empty_ReferenceAndNullableValueType_ReturnsNull() + { + ReadOnlySpan strings = ReadOnlySpan.Empty; + ReadOnlySpan nullableInts = ReadOnlySpan.Empty; + + Assert.Null(strings.Min()); + Assert.Null(strings.Max()); + Assert.Null(nullableInts.Min()); + Assert.Null(nullableInts.Max()); + + Assert.Null(strings.Min(comparer: null!)); + Assert.Null(strings.Max(comparer: null!)); + Assert.Null(nullableInts.Min(comparer: null!)); + Assert.Null(nullableInts.Max(comparer: null!)); + } + + [Fact] + public static void MinMax_AllNull_ReturnsNull() + { + ReadOnlySpan strings = new string?[] { null, null, null }; + ReadOnlySpan nullableInts = new int?[] { null, null, null }; + + Assert.Null(strings.Min()); + Assert.Null(strings.Max()); + Assert.Null(nullableInts.Min()); + Assert.Null(nullableInts.Max()); + } + + [Fact] + public static void MinMax_NullNotFirst_NullIgnoredForComparison() + { + ReadOnlySpan strings = new string?[] { "charlie", null, "bravo", null, "delta" }; + ReadOnlySpan nullableInts = new int?[] { 4, null, -1, null, 7 }; + + Assert.Equal("bravo", strings.Min()); + Assert.Equal("delta", strings.Max()); + Assert.Equal(-1, nullableInts.Min()); + Assert.Equal(7, nullableInts.Max()); + } + + [Fact] + public static void MinMax_DefaultComparer_ProducesExpectedValues() + { + ReadOnlySpan ints = new int[] { 4, -1, 7, 2 }; + ReadOnlySpan strings = new string?[] { null, "charlie", "bravo", null, "delta" }; + + Assert.Equal(-1, ints.Min()); + Assert.Equal(7, ints.Max()); + + Assert.Equal("bravo", strings.Min()); + Assert.Equal("delta", strings.Max()); + } + + [Fact] + public static void MinMax_CustomComparer_IsUsed() + { + ReadOnlySpan ints = new int[] { 4, -1, 7, 2 }; + IComparer reverse = Comparer.Create((left, right) => right.CompareTo(left)); + + Assert.Equal(7, ints.Min(reverse)); + Assert.Equal(-1, ints.Max(reverse)); + } + } +} diff --git a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj index 5ef352b8d1f6d9..33e76aacc5e7c7 100644 --- a/src/libraries/System.Memory/tests/System.Memory.Tests.csproj +++ b/src/libraries/System.Memory/tests/System.Memory.Tests.csproj @@ -30,6 +30,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index 2ef440179364ef..4bbf319ab85b07 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -4312,6 +4312,132 @@ public static int BinarySearch( return BinarySearch(span, comparable); } + /// + /// Returns the minimum value in the span. + /// + /// The type of the elements in the span. + /// The span of values to determine the minimum value of. + /// The minimum value in the span. + /// is empty and is a non-nullable value type. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? Min(this ReadOnlySpan span) => + Min(span, comparer: null!); + + /// + /// Returns the minimum value in the span. + /// + /// The type of the elements in the span. + /// The span of values to determine the minimum value of. + /// The to compare values. + /// The minimum value in the span. + /// is empty and is a non-nullable value type. + public static T? Min(this ReadOnlySpan span, IComparer comparer) + { + if (span.IsEmpty) + { + if (default(T) is null) + { + return default; + } + + ThrowHelper.ThrowInvalidOperationException(); + } + + return comparer is null || comparer == Comparer.Default + ? MinCore(span, Comparer.Default) + : MinCore(span, comparer); + + static T? MinCore(ReadOnlySpan span, TComparer comparer) + where TComparer : IComparer + { + T? value = span[0]; + int i = 1; + + while (value is null) + { + if ((uint)i >= (uint)span.Length) + { + return value; + } + value = span[i++]; + } + + for (; (uint)i < (uint)span.Length; i++) + { + T next = span[i]; + if (next is not null && comparer.Compare(next, value) < 0) + { + value = next; + } + } + + return value; + } + } + + /// + /// Returns the maximum value in the span. + /// + /// The type of the elements in the span. + /// The span of values to determine the maximum value of. + /// The maximum value in the span. + /// is empty and is a non-nullable value type. + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T? Max(this ReadOnlySpan span) => + Max(span, comparer: null!); + + /// + /// Returns the maximum value in the span. + /// + /// The type of the elements in the span. + /// The span of values to determine the maximum value of. + /// The to compare values. + /// The maximum value in the span. + /// is empty and is a non-nullable value type. + public static T? Max(this ReadOnlySpan span, IComparer comparer) + { + if (span.IsEmpty) + { + if (default(T) is null) + { + return default; + } + + ThrowHelper.ThrowInvalidOperationException(); + } + + return comparer is null || comparer == Comparer.Default + ? MaxCore(span, Comparer.Default) + : MaxCore(span, comparer); + + static T? MaxCore(ReadOnlySpan span, TComparer comparer) + where TComparer : IComparer + { + T? value = span[0]; + int i = 1; + + while (value is null) + { + if ((uint)i >= (uint)span.Length) + { + return value; + } + value = span[i++]; + } + + for (; (uint)i < (uint)span.Length; i++) + { + T next = span[i]; + if (next is not null && comparer.Compare(next, value) > 0) + { + value = next; + } + } + + return value; + } + } + /// /// Sorts the elements in the entire using the implementation /// of each element of the From 6ef9e87a09e7cf1615aea441f3fb12449c81b3f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Andr=C3=A9?= <2341261+manandre@users.noreply.github.com> Date: Mon, 18 May 2026 21:05:25 +0000 Subject: [PATCH 02/13] Add specific exception message when span is empty --- .../System.Private.CoreLib/src/Resources/Strings.resx | 3 +++ .../System.Private.CoreLib/src/System/MemoryExtensions.cs | 4 ++-- .../System.Private.CoreLib/src/System/ThrowHelper.cs | 3 +++ 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index ac51112ce4dbd1..fbb481923d085a 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -4486,4 +4486,7 @@ The number styles AllowHexSpecifier and AllowBinarySpecifier are not supported on the decimal type. + + Sequence contains no elements + diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index 4bbf319ab85b07..c6c519f7f92c35 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -4340,7 +4340,7 @@ public static int BinarySearch( return default; } - ThrowHelper.ThrowInvalidOperationException(); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoElements); } return comparer is null || comparer == Comparer.Default @@ -4403,7 +4403,7 @@ public static int BinarySearch( return default; } - ThrowHelper.ThrowInvalidOperationException(); + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoElements); } return comparer is null || comparer == Comparer.Default diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index cf08add006be22..79b1e6e9aba57a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -1252,6 +1252,8 @@ private static string GetResourceString(ExceptionResource resource) return SR.ConcurrentDictionary_ItemKeyIsNull; case ExceptionResource.ConcurrentDictionary_TypeOfValueIncorrect: return SR.ConcurrentDictionary_TypeOfValueIncorrect; + case ExceptionResource.InvalidOperation_NoElements: + return SR.InvalidOperation_NoElements; default: Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum."); return ""; @@ -1453,5 +1455,6 @@ internal enum ExceptionResource InvalidOperation_IncompatibleComparer, ConcurrentDictionary_ItemKeyIsNull, ConcurrentDictionary_TypeOfValueIncorrect, + InvalidOperation_NoElements, } } From a1023cc29ba653118f12eecfa03a25085854151b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Andr=C3=A9?= <2341261+manandre@users.noreply.github.com> Date: Mon, 18 May 2026 22:00:45 +0000 Subject: [PATCH 03/13] Reject null comparer --- .../tests/ReadOnlySpan/MinMax.cs | 14 ++-- .../src/System/MemoryExtensions.cs | 82 ++++++++----------- 2 files changed, 45 insertions(+), 51 deletions(-) diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs index c3cf844d904df3..9f71130e5e72e6 100644 --- a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs @@ -19,6 +19,15 @@ public static void MinMax_Empty_NonNullableValueType_Throws() TestHelpers.AssertThrows(span, (_span) => _span.Max(Comparer.Default)); } + [Fact] + public static void MinMax_NullComparer_ThrowsArgumentNullException() + { + ReadOnlySpan span = new int[] { 4, -1, 7, 2 }; + + TestHelpers.AssertThrows(span, (_span) => _span.Min(comparer: null!)); + TestHelpers.AssertThrows(span, (_span) => _span.Max(comparer: null!)); + } + [Fact] public static void MinMax_Empty_ReferenceAndNullableValueType_ReturnsNull() { @@ -29,11 +38,6 @@ public static void MinMax_Empty_ReferenceAndNullableValueType_ReturnsNull() Assert.Null(strings.Max()); Assert.Null(nullableInts.Min()); Assert.Null(nullableInts.Max()); - - Assert.Null(strings.Min(comparer: null!)); - Assert.Null(strings.Max(comparer: null!)); - Assert.Null(nullableInts.Min(comparer: null!)); - Assert.Null(nullableInts.Max(comparer: null!)); } [Fact] diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index c6c519f7f92c35..dc7c5ec0a40c3a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -4321,7 +4321,7 @@ public static int BinarySearch( /// is empty and is a non-nullable value type. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T? Min(this ReadOnlySpan span) => - Min(span, comparer: null!); + Min(span, Comparer.Default); /// /// Returns the minimum value in the span. @@ -4333,6 +4333,9 @@ public static int BinarySearch( /// is empty and is a non-nullable value type. public static T? Min(this ReadOnlySpan span, IComparer comparer) { + if (comparer is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparer); + if (span.IsEmpty) { if (default(T) is null) @@ -4343,36 +4346,28 @@ public static int BinarySearch( ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoElements); } - return comparer is null || comparer == Comparer.Default - ? MinCore(span, Comparer.Default) - : MinCore(span, comparer); + T? value = span[0]; + int i = 1; - static T? MinCore(ReadOnlySpan span, TComparer comparer) - where TComparer : IComparer + while (value is null) { - T? value = span[0]; - int i = 1; - - while (value is null) + if ((uint)i >= (uint)span.Length) { - if ((uint)i >= (uint)span.Length) - { - return value; - } - value = span[i++]; + return value; } + value = span[i++]; + } - for (; (uint)i < (uint)span.Length; i++) + for (; (uint)i < (uint)span.Length; i++) + { + T next = span[i]; + if (next is not null && comparer.Compare(next, value) < 0) { - T next = span[i]; - if (next is not null && comparer.Compare(next, value) < 0) - { - value = next; - } + value = next; } - - return value; } + + return value; } /// @@ -4384,7 +4379,7 @@ public static int BinarySearch( /// is empty and is a non-nullable value type. [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T? Max(this ReadOnlySpan span) => - Max(span, comparer: null!); + Max(span, Comparer.Default); /// /// Returns the maximum value in the span. @@ -4396,6 +4391,9 @@ public static int BinarySearch( /// is empty and is a non-nullable value type. public static T? Max(this ReadOnlySpan span, IComparer comparer) { + if (comparer is null) + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparer); + if (span.IsEmpty) { if (default(T) is null) @@ -4406,36 +4404,28 @@ public static int BinarySearch( ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoElements); } - return comparer is null || comparer == Comparer.Default - ? MaxCore(span, Comparer.Default) - : MaxCore(span, comparer); + T? value = span[0]; + int i = 1; - static T? MaxCore(ReadOnlySpan span, TComparer comparer) - where TComparer : IComparer + while (value is null) { - T? value = span[0]; - int i = 1; - - while (value is null) + if ((uint)i >= (uint)span.Length) { - if ((uint)i >= (uint)span.Length) - { - return value; - } - value = span[i++]; + return value; } + value = span[i++]; + } - for (; (uint)i < (uint)span.Length; i++) + for (; (uint)i < (uint)span.Length; i++) + { + T next = span[i]; + if (next is not null && comparer.Compare(next, value) > 0) { - T next = span[i]; - if (next is not null && comparer.Compare(next, value) > 0) - { - value = next; - } + value = next; } - - return value; } + + return value; } /// From d214de8ff838e339dd934d3b998c36a27e3ae4c4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Andr=C3=A9?= <2341261+manandre@users.noreply.github.com> Date: Mon, 18 May 2026 22:43:21 +0000 Subject: [PATCH 04/13] Update documentation --- .../cache.staging.json | 1 + .../src/System/MemoryExtensions.cs | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 dotnet/runfile-discovery/runtime-b718cb77f445fb81923cf2b7a7bdea136c0ca526608d71b926e38c068cf2279d/cache.staging.json diff --git a/dotnet/runfile-discovery/runtime-b718cb77f445fb81923cf2b7a7bdea136c0ca526608d71b926e38c068cf2279d/cache.staging.json b/dotnet/runfile-discovery/runtime-b718cb77f445fb81923cf2b7a7bdea136c0ca526608d71b926e38c068cf2279d/cache.staging.json new file mode 100644 index 00000000000000..55b311ff64d2ac --- /dev/null +++ b/dotnet/runfile-discovery/runtime-b718cb77f445fb81923cf2b7a7bdea136c0ca526608d71b926e38c068cf2279d/cache.staging.json @@ -0,0 +1 @@ +{"WorkspacePath":"/workspaces/runtime","LastWalkTimeUtc":"0001-01-01T00:00:00+00:00","FileBasedAppFullPaths":[],"DirectoriesContainingCsproj":["/workspaces/runtime/eng/common/internal","/workspaces/runtime/src/coreclr/debug/ee/amd64/gen_amd64InstrDecode","/workspaces/runtime/src/coreclr/ilasm/GrammarExtractor","/workspaces/runtime/src/coreclr/inc/CrstTypeTool","/workspaces/runtime/src/coreclr/inc/genheaders","/workspaces/runtime/src/coreclr/nativeaot/System.Private.CoreLib/src","/workspaces/runtime/src/coreclr/nativeaot/System.Private.Reflection.Execution/src","/workspaces/runtime/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src","/workspaces/runtime/src/coreclr/nativeaot/System.Private.TypeLoader/src","/workspaces/runtime/src/coreclr/nativeaot/Test.CoreLib/src","/workspaces/runtime/src/coreclr/scripts/emitUnitTests","/workspaces/runtime/src/coreclr/System.Private.CoreLib","/workspaces/runtime/src/coreclr/tools/aot/crossgen2","/workspaces/runtime/src/coreclr/tools/aot/DependencyGraphViewer","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.Build.Tasks","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.Compiler","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.Compiler.Tests","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.Diagnostics","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.MetadataTransform","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.ReadyToRun","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.RyuJit","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.Trimming.Tests","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.TypeSystem","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests","/workspaces/runtime/src/coreclr/tools/aot/WhyDgml","/workspaces/runtime/src/coreclr/tools/AssemblyChecker","/workspaces/runtime/src/coreclr/tools/cdac-build-tool","/workspaces/runtime/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator","/workspaces/runtime/src/coreclr/tools/Common/JitInterface/ThunkGenerator","/workspaces/runtime/src/coreclr/tools/dotnet-pgo","/workspaces/runtime/src/coreclr/tools/GCLogParser","/workspaces/runtime/src/coreclr/tools/ILTrim","/workspaces/runtime/src/coreclr/tools/ILTrim.Core","/workspaces/runtime/src/coreclr/tools/ILTrim.Tests","/workspaces/runtime/src/coreclr/tools/ILVerification","/workspaces/runtime/src/coreclr/tools/ILVerification.Tests","/workspaces/runtime/src/coreclr/tools/ILVerify","/workspaces/runtime/src/coreclr/tools/PdbChecker","/workspaces/runtime/src/coreclr/tools/r2rdump","/workspaces/runtime/src/coreclr/tools/r2rtest","/workspaces/runtime/src/coreclr/tools/runincontext","/workspaces/runtime/src/coreclr/tools/SuperFileCheck","/workspaces/runtime/src/coreclr/tools/tieringtest","/workspaces/runtime/src/installer/managed/Microsoft.NET.HostModel","/workspaces/runtime/src/installer/tests/AppHost.Bundle.Tests","/workspaces/runtime/src/installer/tests/Assets/Projects/AppWithCustomEntryPoints","/workspaces/runtime/src/installer/tests/Assets/Projects/AppWithSubDirs","/workspaces/runtime/src/installer/tests/Assets/Projects/AppWithUnknownLanguageResource","/workspaces/runtime/src/installer/tests/Assets/Projects/AppWithWait","/workspaces/runtime/src/installer/tests/Assets/Projects/ComLibrary","/workspaces/runtime/src/installer/tests/Assets/Projects/ComLibraryConflictingGuid","/workspaces/runtime/src/installer/tests/Assets/Projects/ComLibraryMissingGuid","/workspaces/runtime/src/installer/tests/Assets/Projects/Component","/workspaces/runtime/src/installer/tests/Assets/Projects/HammerServicing","/workspaces/runtime/src/installer/tests/Assets/Projects/HelloWorld","/workspaces/runtime/src/installer/tests/Assets/Projects/HostApiInvokerApp","/workspaces/runtime/src/installer/tests/Assets/Projects/LocalizedApp","/workspaces/runtime/src/installer/tests/Assets/Projects/ManagedHost","/workspaces/runtime/src/installer/tests/Assets/Projects/ServicedLibrary","/workspaces/runtime/src/installer/tests/Assets/Projects/SharedLibrary","/workspaces/runtime/src/installer/tests/Assets/Projects/SingleFileApiTests","/workspaces/runtime/src/installer/tests/Assets/Projects/StartupHook","/workspaces/runtime/src/installer/tests/Assets/Projects/StartupHookWithAssemblyResolver","/workspaces/runtime/src/installer/tests/Assets/Projects/WindowsSpecific","/workspaces/runtime/src/installer/tests/HostActivation.Tests","/workspaces/runtime/src/installer/tests/Microsoft.DotNet.CoreSetup.Packaging.Tests","/workspaces/runtime/src/installer/tests/Microsoft.NET.HostModel.Tests","/workspaces/runtime/src/installer/tests/TestUtils","/workspaces/runtime/src/libraries","/workspaces/runtime/src/mono/browser/debugger/BrowserDebugHost","/workspaces/runtime/src/mono/browser/debugger/BrowserDebugProxy","/workspaces/runtime/src/mono/sample/Android","/workspaces/runtime/src/mono/sample/HelloWorld","/workspaces/runtime/src/mono/sample/iOS","/workspaces/runtime/src/mono/sample/iOS-NativeAOT","/workspaces/runtime/src/mono/sample/mbr/apple","/workspaces/runtime/src/mono/sample/mbr/browser","/workspaces/runtime/src/mono/sample/mbr/console","/workspaces/runtime/src/mono/sample/mbr/DeltaHelper","/workspaces/runtime/src/mono/sample/wasi/console","/workspaces/runtime/src/mono/sample/wasi/http-p2","/workspaces/runtime/src/mono/sample/wasi/jco","/workspaces/runtime/src/mono/sample/wasi/native","/workspaces/runtime/src/mono/sample/wasi/sockets-p2","/workspaces/runtime/src/mono/sample/wasm/blazor-frame","/workspaces/runtime/src/mono/sample/wasm/browser","/workspaces/runtime/src/mono/sample/wasm/browser-advanced","/workspaces/runtime/src/mono/sample/wasm/browser-bench","/workspaces/runtime/src/mono/sample/wasm/browser-eventpipe","/workspaces/runtime/src/mono/sample/wasm/browser-frame","/workspaces/runtime/src/mono/sample/wasm/browser-logprofile","/workspaces/runtime/src/mono/sample/wasm/browser-profile","/workspaces/runtime/src/mono/sample/wasm/browser-shutdown","/workspaces/runtime/src/mono/sample/wasm/browser-threads","/workspaces/runtime/src/mono/sample/wasm/console-node","/workspaces/runtime/src/mono/sample/wasm/console-v8","/workspaces/runtime/src/mono/sample/wasm/simple-raytracer","/workspaces/runtime/src/mono/sample/wasm/simple-server","/workspaces/runtime/src/mono/System.Private.CoreLib","/workspaces/runtime/src/mono/tests/HwIntrinsics","/workspaces/runtime/src/mono/tools/jitdiff","/workspaces/runtime/src/mono/wasi/Wasi.Build.Tests","/workspaces/runtime/src/mono/wasm/host","/workspaces/runtime/src/mono/wasm/symbolicator","/workspaces/runtime/src/mono/wasm/templates","/workspaces/runtime/src/mono/wasm/testassets/BlazorBasicTestApp/App","/workspaces/runtime/src/mono/wasm/testassets/BlazorBasicTestApp/RazorClassLibrary","/workspaces/runtime/src/mono/wasm/testassets/BlazorMultiClientHosted/Client1","/workspaces/runtime/src/mono/wasm/testassets/BlazorMultiClientHosted/Client2","/workspaces/runtime/src/mono/wasm/testassets/BlazorMultiClientHosted/Server","/workspaces/runtime/src/mono/wasm/testassets/BlazorWebWasm/BlazorWebWasm","/workspaces/runtime/src/mono/wasm/testassets/BlazorWebWasm/BlazorWebWasm.Client","/workspaces/runtime/src/mono/wasm/testassets/LibraryMode","/workspaces/runtime/src/mono/wasm/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources","/workspaces/runtime/src/mono/wasm/testassets/WasmBasicTestApp/App","/workspaces/runtime/src/mono/wasm/testassets/WasmBasicTestApp/Json","/workspaces/runtime/src/mono/wasm/testassets/WasmBasicTestApp/LazyLibrary","/workspaces/runtime/src/mono/wasm/testassets/WasmBasicTestApp/Library","/workspaces/runtime/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary","/workspaces/runtime/src/mono/wasm/testassets/WasmOnAspNetCore/AspNetCoreServer","/workspaces/runtime/src/mono/wasm/testassets/WasmOnAspNetCore/BlazorClient","/workspaces/runtime/src/mono/wasm/testassets/WasmOnAspNetCore/Shared","/workspaces/runtime/src/mono/wasm/testassets/WasmOnAspNetCore/WasmBrowserClient","/workspaces/runtime/src/mono/wasm/Wasm.Build.Tests","/workspaces/runtime/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader","/workspaces/runtime/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions","/workspaces/runtime/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts","/workspaces/runtime/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy","/workspaces/runtime/src/native/managed/cdac/mscordaccore_universal","/workspaces/runtime/src/native/managed/cdac/scripts","/workspaces/runtime/src/native/managed/cdac/tests","/workspaces/runtime/src/native/minipal/UnicodeDataGenerator","/workspaces/runtime/src/samples/LibraryImportGeneratorSample","/workspaces/runtime/src/tasks/AndroidAppBuilder","/workspaces/runtime/src/tasks/AotCompilerTask","/workspaces/runtime/src/tasks/AppleAppBuilder","/workspaces/runtime/src/tasks/AssemblyStripper","/workspaces/runtime/src/tasks/Crossgen2Tasks","/workspaces/runtime/src/tasks/HelixTestTasks","/workspaces/runtime/src/tasks/installer.tasks","/workspaces/runtime/src/tasks/LibraryBuilder","/workspaces/runtime/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks","/workspaces/runtime/src/tasks/Microsoft.NET.WebAssembly.Webcil","/workspaces/runtime/src/tasks/MobileBuildTasks","/workspaces/runtime/src/tasks/MonoTargetsTasks","/workspaces/runtime/src/tasks/WasmAppBuilder","/workspaces/runtime/src/tasks/WasmBuildTasks","/workspaces/runtime/src/tasks/WorkloadBuildTasks","/workspaces/runtime/src/tests/async","/workspaces/runtime/src/tests/baseservices","/workspaces/runtime/src/tests/Common/CoreCLRTestLibrary","/workspaces/runtime/src/tests/Common/external","/workspaces/runtime/src/tests/Common/GenerateHWIntrinsicTests","/workspaces/runtime/src/tests/Common/ILTestRunner","/workspaces/runtime/src/tests/Common/test_dependencies","/workspaces/runtime/src/tests/Common/XHarnessRunnerLibrary","/workspaces/runtime/src/tests/Common/xunit/assert.xunit","/workspaces/runtime/src/tests/Common/XUnitLogChecker","/workspaces/runtime/src/tests/Common/XUnitWrapperGenerator","/workspaces/runtime/src/tests/Common/XUnitWrapperLibrary","/workspaces/runtime/src/tests/CoreMangLib","/workspaces/runtime/src/tests/Exceptions","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/AOT","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/AOT_PROFILED","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/AOT_System.IO.Stream","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/CrashChaining","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/Interpreter","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/InvariantCultureOnlyMode","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/JIT","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode_AOT_LLVM","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/NativeAOT","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/PInvoke","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/RuntimeConfig","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/StartupHook","/workspaces/runtime/src/tests/FunctionalTests/iOS/Device/AOT","/workspaces/runtime/src/tests/FunctionalTests/iOS/Device/ExportManagedSymbols","/workspaces/runtime/src/tests/FunctionalTests/iOS/Device/LibraryMode","/workspaces/runtime/src/tests/FunctionalTests/iOS/Device/ParallelForEachAsync","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/AOT","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/AOT-LLVM","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/CoreCLR","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/Interpreter","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/InvariantCultureOnlyMode","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/LibraryMode","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/PInvoke","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/StartupHook","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/XmlFormatWriterGeneratorAOT","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/XmlSerializer_Deserialize","/workspaces/runtime/src/tests/FunctionalTests/TestAssets/StartupHookForFunctionalTest","/workspaces/runtime/src/tests/FunctionalTests/tvOS/Device/AOT","/workspaces/runtime/src/tests/FunctionalTests/tvOS/Simulator/AOT","/workspaces/runtime/src/tests/FunctionalTests/tvOS/Simulator/Interpreter","/workspaces/runtime/src/tests/FunctionalTests/tvOS/Simulator/PInvoke","/workspaces/runtime/src/tests/FunctionalTests/WebAssembly/Browser/HotReload","/workspaces/runtime/src/tests/FunctionalTests/WebAssembly/Browser/RuntimeConfig","/workspaces/runtime/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook","/workspaces/runtime/src/tests/GC","/workspaces/runtime/src/tests/ilasm","/workspaces/runtime/src/tests/Interop","/workspaces/runtime/src/tests/JIT","/workspaces/runtime/src/tests/Loader","/workspaces/runtime/src/tests/managed","/workspaces/runtime/src/tests/nativeaot","/workspaces/runtime/src/tests/profiler","/workspaces/runtime/src/tests/readytorun","/workspaces/runtime/src/tests/reflection","/workspaces/runtime/src/tests/Regressions","/workspaces/runtime/src/tests/tracing","/workspaces/runtime/src/tools/hotreload-delta-gen/Microsoft.DotNet.HotReload.Utils.Generator","/workspaces/runtime/src/tools/hotreload-delta-gen/Microsoft.DotNet.HotReload.Utils.Generator.BuildTool","/workspaces/runtime/src/tools/hotreload-delta-gen/Microsoft.DotNet.HotReload.Utils.Generator.Data","/workspaces/runtime/src/tools/hotreload-delta-gen/Microsoft.DotNet.HotReload.Utils.Generator.Frontend","/workspaces/runtime/src/tools/hotreload-delta-gen/Microsoft.DotNet.HotReload.Utils.Generator.Tasks","/workspaces/runtime/src/tools/ilasm/src/ilasm","/workspaces/runtime/src/tools/ilasm/src/ILAssembler","/workspaces/runtime/src/tools/ilasm/tests/ILAssembler.Tests","/workspaces/runtime/src/tools/illink/src/ILLink.CodeFix","/workspaces/runtime/src/tools/illink/src/ILLink.RoslynAnalyzer","/workspaces/runtime/src/tools/illink/src/ILLink.Tasks","/workspaces/runtime/src/tools/illink/src/linker","/workspaces/runtime/src/tools/illink/src/tlens","/workspaces/runtime/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests","/workspaces/runtime/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests.Generator","/workspaces/runtime/src/tools/illink/test/ILLink.Tasks.Tests","/workspaces/runtime/src/tools/illink/test/Mono.Linker.Tests","/workspaces/runtime/src/tools/illink/test/Mono.Linker.Tests.Cases","/workspaces/runtime/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations","/workspaces/runtime/src/tools/StressLogAnalyzer/src","/workspaces/runtime/src/workloads"]} \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index dc7c5ec0a40c3a..4d03c7d1a2eead 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -4319,6 +4319,11 @@ public static int BinarySearch( /// The span of values to determine the minimum value of. /// The minimum value in the span. /// is empty and is a non-nullable value type. + /// + /// If type implements , the method uses that implementation to compare values. Otherwise, if type implements , that implementation is used to compare values. + /// If is a reference type and the span sequence is empty, this method returns . + /// Null values are ignored when determining the minimum value. If the span contains at least one non-null value, the minimum of those values is returned. If the span does not contain any non-null values, is returned. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T? Min(this ReadOnlySpan span) => Min(span, Comparer.Default); @@ -4330,7 +4335,12 @@ public static int BinarySearch( /// The span of values to determine the minimum value of. /// The to compare values. /// The minimum value in the span. + /// is . /// is empty and is a non-nullable value type. + /// + /// If is a reference type and the span sequence is empty, this method returns . + /// Null values are ignored when determining the minimum value. If the span contains at least one non-null value, the minimum of those values is returned. If the span does not contain any non-null values, is returned. + /// public static T? Min(this ReadOnlySpan span, IComparer comparer) { if (comparer is null) @@ -4377,6 +4387,11 @@ public static int BinarySearch( /// The span of values to determine the maximum value of. /// The maximum value in the span. /// is empty and is a non-nullable value type. + /// + /// If type implements , the method uses that implementation to compare values. Otherwise, if type implements , that implementation is used to compare values. + /// If is a reference type and the span sequence is empty, this method returns . + /// Null values are ignored when determining the maximum value. If the span contains at least one non-null value, the maximum of those values is returned. If the span does not contain any non-null values, is returned. + /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T? Max(this ReadOnlySpan span) => Max(span, Comparer.Default); @@ -4388,7 +4403,12 @@ public static int BinarySearch( /// The span of values to determine the maximum value of. /// The to compare values. /// The maximum value in the span. + /// is . /// is empty and is a non-nullable value type. + /// + /// If is a reference type and the span sequence is empty, this method returns . + /// Null values are ignored when determining the maximum value. If the span contains at least one non-null value, the maximum of those values is returned. If the span does not contain any non-null values, is returned. + /// public static T? Max(this ReadOnlySpan span, IComparer comparer) { if (comparer is null) From f5107bdef0f6b5977ba6483750b4d8402c968de5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Andr=C3=A9?= <2341261+manandre@users.noreply.github.com> Date: Mon, 18 May 2026 22:54:50 +0000 Subject: [PATCH 05/13] Remove cache file --- .../cache.staging.json | 1 - 1 file changed, 1 deletion(-) delete mode 100644 dotnet/runfile-discovery/runtime-b718cb77f445fb81923cf2b7a7bdea136c0ca526608d71b926e38c068cf2279d/cache.staging.json diff --git a/dotnet/runfile-discovery/runtime-b718cb77f445fb81923cf2b7a7bdea136c0ca526608d71b926e38c068cf2279d/cache.staging.json b/dotnet/runfile-discovery/runtime-b718cb77f445fb81923cf2b7a7bdea136c0ca526608d71b926e38c068cf2279d/cache.staging.json deleted file mode 100644 index 55b311ff64d2ac..00000000000000 --- a/dotnet/runfile-discovery/runtime-b718cb77f445fb81923cf2b7a7bdea136c0ca526608d71b926e38c068cf2279d/cache.staging.json +++ /dev/null @@ -1 +0,0 @@ -{"WorkspacePath":"/workspaces/runtime","LastWalkTimeUtc":"0001-01-01T00:00:00+00:00","FileBasedAppFullPaths":[],"DirectoriesContainingCsproj":["/workspaces/runtime/eng/common/internal","/workspaces/runtime/src/coreclr/debug/ee/amd64/gen_amd64InstrDecode","/workspaces/runtime/src/coreclr/ilasm/GrammarExtractor","/workspaces/runtime/src/coreclr/inc/CrstTypeTool","/workspaces/runtime/src/coreclr/inc/genheaders","/workspaces/runtime/src/coreclr/nativeaot/System.Private.CoreLib/src","/workspaces/runtime/src/coreclr/nativeaot/System.Private.Reflection.Execution/src","/workspaces/runtime/src/coreclr/nativeaot/System.Private.StackTraceMetadata/src","/workspaces/runtime/src/coreclr/nativeaot/System.Private.TypeLoader/src","/workspaces/runtime/src/coreclr/nativeaot/Test.CoreLib/src","/workspaces/runtime/src/coreclr/scripts/emitUnitTests","/workspaces/runtime/src/coreclr/System.Private.CoreLib","/workspaces/runtime/src/coreclr/tools/aot/crossgen2","/workspaces/runtime/src/coreclr/tools/aot/DependencyGraphViewer","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.Build.Tasks","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.Compiler","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.Compiler.Tests","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.DependencyAnalysisFramework","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.Diagnostics","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.MetadataTransform","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.ReadyToRun","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.ReadyToRun.Tests","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.Reflection.ReadyToRun","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.RyuJit","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.Trimming.Tests","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.TypeSystem","/workspaces/runtime/src/coreclr/tools/aot/ILCompiler.TypeSystem.Tests","/workspaces/runtime/src/coreclr/tools/aot/WhyDgml","/workspaces/runtime/src/coreclr/tools/AssemblyChecker","/workspaces/runtime/src/coreclr/tools/cdac-build-tool","/workspaces/runtime/src/coreclr/tools/Common/Internal/Metadata/NativeFormat/Generator","/workspaces/runtime/src/coreclr/tools/Common/JitInterface/ThunkGenerator","/workspaces/runtime/src/coreclr/tools/dotnet-pgo","/workspaces/runtime/src/coreclr/tools/GCLogParser","/workspaces/runtime/src/coreclr/tools/ILTrim","/workspaces/runtime/src/coreclr/tools/ILTrim.Core","/workspaces/runtime/src/coreclr/tools/ILTrim.Tests","/workspaces/runtime/src/coreclr/tools/ILVerification","/workspaces/runtime/src/coreclr/tools/ILVerification.Tests","/workspaces/runtime/src/coreclr/tools/ILVerify","/workspaces/runtime/src/coreclr/tools/PdbChecker","/workspaces/runtime/src/coreclr/tools/r2rdump","/workspaces/runtime/src/coreclr/tools/r2rtest","/workspaces/runtime/src/coreclr/tools/runincontext","/workspaces/runtime/src/coreclr/tools/SuperFileCheck","/workspaces/runtime/src/coreclr/tools/tieringtest","/workspaces/runtime/src/installer/managed/Microsoft.NET.HostModel","/workspaces/runtime/src/installer/tests/AppHost.Bundle.Tests","/workspaces/runtime/src/installer/tests/Assets/Projects/AppWithCustomEntryPoints","/workspaces/runtime/src/installer/tests/Assets/Projects/AppWithSubDirs","/workspaces/runtime/src/installer/tests/Assets/Projects/AppWithUnknownLanguageResource","/workspaces/runtime/src/installer/tests/Assets/Projects/AppWithWait","/workspaces/runtime/src/installer/tests/Assets/Projects/ComLibrary","/workspaces/runtime/src/installer/tests/Assets/Projects/ComLibraryConflictingGuid","/workspaces/runtime/src/installer/tests/Assets/Projects/ComLibraryMissingGuid","/workspaces/runtime/src/installer/tests/Assets/Projects/Component","/workspaces/runtime/src/installer/tests/Assets/Projects/HammerServicing","/workspaces/runtime/src/installer/tests/Assets/Projects/HelloWorld","/workspaces/runtime/src/installer/tests/Assets/Projects/HostApiInvokerApp","/workspaces/runtime/src/installer/tests/Assets/Projects/LocalizedApp","/workspaces/runtime/src/installer/tests/Assets/Projects/ManagedHost","/workspaces/runtime/src/installer/tests/Assets/Projects/ServicedLibrary","/workspaces/runtime/src/installer/tests/Assets/Projects/SharedLibrary","/workspaces/runtime/src/installer/tests/Assets/Projects/SingleFileApiTests","/workspaces/runtime/src/installer/tests/Assets/Projects/StartupHook","/workspaces/runtime/src/installer/tests/Assets/Projects/StartupHookWithAssemblyResolver","/workspaces/runtime/src/installer/tests/Assets/Projects/WindowsSpecific","/workspaces/runtime/src/installer/tests/HostActivation.Tests","/workspaces/runtime/src/installer/tests/Microsoft.DotNet.CoreSetup.Packaging.Tests","/workspaces/runtime/src/installer/tests/Microsoft.NET.HostModel.Tests","/workspaces/runtime/src/installer/tests/TestUtils","/workspaces/runtime/src/libraries","/workspaces/runtime/src/mono/browser/debugger/BrowserDebugHost","/workspaces/runtime/src/mono/browser/debugger/BrowserDebugProxy","/workspaces/runtime/src/mono/sample/Android","/workspaces/runtime/src/mono/sample/HelloWorld","/workspaces/runtime/src/mono/sample/iOS","/workspaces/runtime/src/mono/sample/iOS-NativeAOT","/workspaces/runtime/src/mono/sample/mbr/apple","/workspaces/runtime/src/mono/sample/mbr/browser","/workspaces/runtime/src/mono/sample/mbr/console","/workspaces/runtime/src/mono/sample/mbr/DeltaHelper","/workspaces/runtime/src/mono/sample/wasi/console","/workspaces/runtime/src/mono/sample/wasi/http-p2","/workspaces/runtime/src/mono/sample/wasi/jco","/workspaces/runtime/src/mono/sample/wasi/native","/workspaces/runtime/src/mono/sample/wasi/sockets-p2","/workspaces/runtime/src/mono/sample/wasm/blazor-frame","/workspaces/runtime/src/mono/sample/wasm/browser","/workspaces/runtime/src/mono/sample/wasm/browser-advanced","/workspaces/runtime/src/mono/sample/wasm/browser-bench","/workspaces/runtime/src/mono/sample/wasm/browser-eventpipe","/workspaces/runtime/src/mono/sample/wasm/browser-frame","/workspaces/runtime/src/mono/sample/wasm/browser-logprofile","/workspaces/runtime/src/mono/sample/wasm/browser-profile","/workspaces/runtime/src/mono/sample/wasm/browser-shutdown","/workspaces/runtime/src/mono/sample/wasm/browser-threads","/workspaces/runtime/src/mono/sample/wasm/console-node","/workspaces/runtime/src/mono/sample/wasm/console-v8","/workspaces/runtime/src/mono/sample/wasm/simple-raytracer","/workspaces/runtime/src/mono/sample/wasm/simple-server","/workspaces/runtime/src/mono/System.Private.CoreLib","/workspaces/runtime/src/mono/tests/HwIntrinsics","/workspaces/runtime/src/mono/tools/jitdiff","/workspaces/runtime/src/mono/wasi/Wasi.Build.Tests","/workspaces/runtime/src/mono/wasm/host","/workspaces/runtime/src/mono/wasm/symbolicator","/workspaces/runtime/src/mono/wasm/templates","/workspaces/runtime/src/mono/wasm/testassets/BlazorBasicTestApp/App","/workspaces/runtime/src/mono/wasm/testassets/BlazorBasicTestApp/RazorClassLibrary","/workspaces/runtime/src/mono/wasm/testassets/BlazorMultiClientHosted/Client1","/workspaces/runtime/src/mono/wasm/testassets/BlazorMultiClientHosted/Client2","/workspaces/runtime/src/mono/wasm/testassets/BlazorMultiClientHosted/Server","/workspaces/runtime/src/mono/wasm/testassets/BlazorWebWasm/BlazorWebWasm","/workspaces/runtime/src/mono/wasm/testassets/BlazorWebWasm/BlazorWebWasm.Client","/workspaces/runtime/src/mono/wasm/testassets/LibraryMode","/workspaces/runtime/src/mono/wasm/testassets/SatelliteAssemblyFromProjectRef/LibraryWithResources","/workspaces/runtime/src/mono/wasm/testassets/WasmBasicTestApp/App","/workspaces/runtime/src/mono/wasm/testassets/WasmBasicTestApp/Json","/workspaces/runtime/src/mono/wasm/testassets/WasmBasicTestApp/LazyLibrary","/workspaces/runtime/src/mono/wasm/testassets/WasmBasicTestApp/Library","/workspaces/runtime/src/mono/wasm/testassets/WasmBasicTestApp/ResourceLibrary","/workspaces/runtime/src/mono/wasm/testassets/WasmOnAspNetCore/AspNetCoreServer","/workspaces/runtime/src/mono/wasm/testassets/WasmOnAspNetCore/BlazorClient","/workspaces/runtime/src/mono/wasm/testassets/WasmOnAspNetCore/Shared","/workspaces/runtime/src/mono/wasm/testassets/WasmOnAspNetCore/WasmBrowserClient","/workspaces/runtime/src/mono/wasm/Wasm.Build.Tests","/workspaces/runtime/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader","/workspaces/runtime/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Abstractions","/workspaces/runtime/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Contracts","/workspaces/runtime/src/native/managed/cdac/Microsoft.Diagnostics.DataContractReader.Legacy","/workspaces/runtime/src/native/managed/cdac/mscordaccore_universal","/workspaces/runtime/src/native/managed/cdac/scripts","/workspaces/runtime/src/native/managed/cdac/tests","/workspaces/runtime/src/native/minipal/UnicodeDataGenerator","/workspaces/runtime/src/samples/LibraryImportGeneratorSample","/workspaces/runtime/src/tasks/AndroidAppBuilder","/workspaces/runtime/src/tasks/AotCompilerTask","/workspaces/runtime/src/tasks/AppleAppBuilder","/workspaces/runtime/src/tasks/AssemblyStripper","/workspaces/runtime/src/tasks/Crossgen2Tasks","/workspaces/runtime/src/tasks/HelixTestTasks","/workspaces/runtime/src/tasks/installer.tasks","/workspaces/runtime/src/tasks/LibraryBuilder","/workspaces/runtime/src/tasks/Microsoft.NET.Sdk.WebAssembly.Pack.Tasks","/workspaces/runtime/src/tasks/Microsoft.NET.WebAssembly.Webcil","/workspaces/runtime/src/tasks/MobileBuildTasks","/workspaces/runtime/src/tasks/MonoTargetsTasks","/workspaces/runtime/src/tasks/WasmAppBuilder","/workspaces/runtime/src/tasks/WasmBuildTasks","/workspaces/runtime/src/tasks/WorkloadBuildTasks","/workspaces/runtime/src/tests/async","/workspaces/runtime/src/tests/baseservices","/workspaces/runtime/src/tests/Common/CoreCLRTestLibrary","/workspaces/runtime/src/tests/Common/external","/workspaces/runtime/src/tests/Common/GenerateHWIntrinsicTests","/workspaces/runtime/src/tests/Common/ILTestRunner","/workspaces/runtime/src/tests/Common/test_dependencies","/workspaces/runtime/src/tests/Common/XHarnessRunnerLibrary","/workspaces/runtime/src/tests/Common/xunit/assert.xunit","/workspaces/runtime/src/tests/Common/XUnitLogChecker","/workspaces/runtime/src/tests/Common/XUnitWrapperGenerator","/workspaces/runtime/src/tests/Common/XUnitWrapperLibrary","/workspaces/runtime/src/tests/CoreMangLib","/workspaces/runtime/src/tests/Exceptions","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/AOT","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/AOT_LLVM","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/AOT_PROFILED","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/AOT_System.IO.Stream","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/CrashChaining","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/Interpreter","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/InvariantCultureOnlyMode","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/JIT","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/LibraryMode_AOT_LLVM","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/NativeAOT","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/PInvoke","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/RuntimeConfig","/workspaces/runtime/src/tests/FunctionalTests/Android/Device_Emulator/StartupHook","/workspaces/runtime/src/tests/FunctionalTests/iOS/Device/AOT","/workspaces/runtime/src/tests/FunctionalTests/iOS/Device/ExportManagedSymbols","/workspaces/runtime/src/tests/FunctionalTests/iOS/Device/LibraryMode","/workspaces/runtime/src/tests/FunctionalTests/iOS/Device/ParallelForEachAsync","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/AOT","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/AOT-LLVM","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/CoreCLR","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/Interpreter","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/InvariantCultureOnlyMode","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/LambdaCompilerAOT","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/LibraryMode","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/PInvoke","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/StartupHook","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/XmlFormatWriterGeneratorAOT","/workspaces/runtime/src/tests/FunctionalTests/iOS/Simulator/XmlSerializer_Deserialize","/workspaces/runtime/src/tests/FunctionalTests/TestAssets/StartupHookForFunctionalTest","/workspaces/runtime/src/tests/FunctionalTests/tvOS/Device/AOT","/workspaces/runtime/src/tests/FunctionalTests/tvOS/Simulator/AOT","/workspaces/runtime/src/tests/FunctionalTests/tvOS/Simulator/Interpreter","/workspaces/runtime/src/tests/FunctionalTests/tvOS/Simulator/PInvoke","/workspaces/runtime/src/tests/FunctionalTests/WebAssembly/Browser/HotReload","/workspaces/runtime/src/tests/FunctionalTests/WebAssembly/Browser/RuntimeConfig","/workspaces/runtime/src/tests/FunctionalTests/WebAssembly/Browser/StartupHook","/workspaces/runtime/src/tests/GC","/workspaces/runtime/src/tests/ilasm","/workspaces/runtime/src/tests/Interop","/workspaces/runtime/src/tests/JIT","/workspaces/runtime/src/tests/Loader","/workspaces/runtime/src/tests/managed","/workspaces/runtime/src/tests/nativeaot","/workspaces/runtime/src/tests/profiler","/workspaces/runtime/src/tests/readytorun","/workspaces/runtime/src/tests/reflection","/workspaces/runtime/src/tests/Regressions","/workspaces/runtime/src/tests/tracing","/workspaces/runtime/src/tools/hotreload-delta-gen/Microsoft.DotNet.HotReload.Utils.Generator","/workspaces/runtime/src/tools/hotreload-delta-gen/Microsoft.DotNet.HotReload.Utils.Generator.BuildTool","/workspaces/runtime/src/tools/hotreload-delta-gen/Microsoft.DotNet.HotReload.Utils.Generator.Data","/workspaces/runtime/src/tools/hotreload-delta-gen/Microsoft.DotNet.HotReload.Utils.Generator.Frontend","/workspaces/runtime/src/tools/hotreload-delta-gen/Microsoft.DotNet.HotReload.Utils.Generator.Tasks","/workspaces/runtime/src/tools/ilasm/src/ilasm","/workspaces/runtime/src/tools/ilasm/src/ILAssembler","/workspaces/runtime/src/tools/ilasm/tests/ILAssembler.Tests","/workspaces/runtime/src/tools/illink/src/ILLink.CodeFix","/workspaces/runtime/src/tools/illink/src/ILLink.RoslynAnalyzer","/workspaces/runtime/src/tools/illink/src/ILLink.Tasks","/workspaces/runtime/src/tools/illink/src/linker","/workspaces/runtime/src/tools/illink/src/tlens","/workspaces/runtime/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests","/workspaces/runtime/src/tools/illink/test/ILLink.RoslynAnalyzer.Tests.Generator","/workspaces/runtime/src/tools/illink/test/ILLink.Tasks.Tests","/workspaces/runtime/src/tools/illink/test/Mono.Linker.Tests","/workspaces/runtime/src/tools/illink/test/Mono.Linker.Tests.Cases","/workspaces/runtime/src/tools/illink/test/Mono.Linker.Tests.Cases.Expectations","/workspaces/runtime/src/tools/StressLogAnalyzer/src","/workspaces/runtime/src/workloads"]} \ No newline at end of file From 93c617aa769ec6d95551ecacb7e584e7bb9e1c1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emmanuel=20Andr=C3=A9?= <2341261+manandre@users.noreply.github.com> Date: Mon, 18 May 2026 23:01:14 +0000 Subject: [PATCH 06/13] Apply Copilot suggestions --- src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs | 4 +++- .../System.Private.CoreLib/src/Resources/Strings.resx | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs index 9f71130e5e72e6..107e0736cb6e88 100644 --- a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs @@ -23,9 +23,11 @@ public static void MinMax_Empty_NonNullableValueType_Throws() public static void MinMax_NullComparer_ThrowsArgumentNullException() { ReadOnlySpan span = new int[] { 4, -1, 7, 2 }; - + ReadOnlySpan emptySpan = ReadOnlySpan.Empty; TestHelpers.AssertThrows(span, (_span) => _span.Min(comparer: null!)); TestHelpers.AssertThrows(span, (_span) => _span.Max(comparer: null!)); + TestHelpers.AssertThrows(emptySpan, (_span) => _span.Min(comparer: null!)); + TestHelpers.AssertThrows(emptySpan, (_span) => _span.Max(comparer: null!)); } [Fact] diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index fbb481923d085a..38ae696ec23048 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -4487,6 +4487,6 @@ The number styles AllowHexSpecifier and AllowBinarySpecifier are not supported on the decimal type. - Sequence contains no elements + Sequence contains no elements. From b4fb8271113a3446b25fd755bd8c14a6fed04b74 Mon Sep 17 00:00:00 2001 From: Emmanuel ANDRE <2341261+manandre@users.noreply.github.com> Date: Tue, 19 May 2026 22:02:03 +0200 Subject: [PATCH 07/13] Align implementation on enumerable logic --- .../System.Memory/ref/System.Memory.cs | 4 +- .../tests/ReadOnlySpan/MinMax.cs | 16 +- .../src/System/MemoryExtensions.cs | 138 ++++++++++++------ 3 files changed, 99 insertions(+), 59 deletions(-) diff --git a/src/libraries/System.Memory/ref/System.Memory.cs b/src/libraries/System.Memory/ref/System.Memory.cs index 9642b313123609..fd7082f4a6764e 100644 --- a/src/libraries/System.Memory/ref/System.Memory.cs +++ b/src/libraries/System.Memory/ref/System.Memory.cs @@ -231,9 +231,9 @@ public static partial class MemoryExtensions [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] public static int BinarySearch(this System.Span span, TComparable comparable) where TComparable : System.IComparable, allows ref struct { throw null; } public static T? Max(this System.ReadOnlySpan span) { throw null; } - public static T? Max(this System.ReadOnlySpan span, System.Collections.Generic.IComparer comparer) { throw null; } + public static T? Max(this System.ReadOnlySpan span, System.Collections.Generic.IComparer? comparer) { throw null; } public static T? Min(this System.ReadOnlySpan span) { throw null; } - public static T? Min(this System.ReadOnlySpan span, System.Collections.Generic.IComparer comparer) { throw null; } + public static T? Min(this System.ReadOnlySpan span, System.Collections.Generic.IComparer? comparer) { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] public static int CommonPrefixLength(this System.Span span, System.ReadOnlySpan other) { throw null; } [System.Runtime.CompilerServices.OverloadResolutionPriorityAttribute(-1)] diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs index 107e0736cb6e88..6d923aa833857c 100644 --- a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs @@ -19,17 +19,6 @@ public static void MinMax_Empty_NonNullableValueType_Throws() TestHelpers.AssertThrows(span, (_span) => _span.Max(Comparer.Default)); } - [Fact] - public static void MinMax_NullComparer_ThrowsArgumentNullException() - { - ReadOnlySpan span = new int[] { 4, -1, 7, 2 }; - ReadOnlySpan emptySpan = ReadOnlySpan.Empty; - TestHelpers.AssertThrows(span, (_span) => _span.Min(comparer: null!)); - TestHelpers.AssertThrows(span, (_span) => _span.Max(comparer: null!)); - TestHelpers.AssertThrows(emptySpan, (_span) => _span.Min(comparer: null!)); - TestHelpers.AssertThrows(emptySpan, (_span) => _span.Max(comparer: null!)); - } - [Fact] public static void MinMax_Empty_ReferenceAndNullableValueType_ReturnsNull() { @@ -40,6 +29,11 @@ public static void MinMax_Empty_ReferenceAndNullableValueType_ReturnsNull() Assert.Null(strings.Max()); Assert.Null(nullableInts.Min()); Assert.Null(nullableInts.Max()); + + Assert.Null(strings.Min(comparer: null)); + Assert.Null(strings.Max(comparer: null)); + Assert.Null(nullableInts.Min(comparer: null)); + Assert.Null(nullableInts.Max(comparer: null)); } [Fact] diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index 4d03c7d1a2eead..7bfe06fec40303 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -4326,7 +4326,7 @@ public static int BinarySearch( /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T? Min(this ReadOnlySpan span) => - Min(span, Comparer.Default); + Min(span, comparer: null); /// /// Returns the minimum value in the span. @@ -4335,45 +4335,68 @@ public static int BinarySearch( /// The span of values to determine the minimum value of. /// The to compare values. /// The minimum value in the span. - /// is . /// is empty and is a non-nullable value type. /// /// If is a reference type and the span sequence is empty, this method returns . /// Null values are ignored when determining the minimum value. If the span contains at least one non-null value, the minimum of those values is returned. If the span does not contain any non-null values, is returned. /// - public static T? Min(this ReadOnlySpan span, IComparer comparer) + public static T? Min(this ReadOnlySpan span, IComparer? comparer) { - if (comparer is null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparer); + comparer ??= Comparer.Default; + + T? value = default; + int i = 0; - if (span.IsEmpty) + if (value is null) { - if (default(T) is null) + do { - return default; + if (i >= span.Length) + { + return value; + } + value = span[i++]; } + while (value is null); - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoElements); + while (i < span.Length) + { + T next = span[i++]; + if (next is not null && comparer.Compare(next, value) < 0) + { + value = next; + } + } } - - T? value = span[0]; - int i = 1; - - while (value is null) + else { - if ((uint)i >= (uint)span.Length) + if (i >= span.Length) { - return value; + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoElements); } - value = span[i++]; - } - for (; (uint)i < (uint)span.Length; i++) - { - T next = span[i]; - if (next is not null && comparer.Compare(next, value) < 0) + value = span[i]; + if (comparer == Comparer.Default) { - value = next; + while (i < span.Length) + { + T next = span[i++]; + if (Comparer.Default.Compare(next, value) < 0) + { + value = next; + } + } + } + else + { + while (i < span.Length) + { + T next = span[i++]; + if (comparer.Compare(next, value) < 0) + { + value = next; + } + } } } @@ -4394,7 +4417,7 @@ public static int BinarySearch( /// [MethodImpl(MethodImplOptions.AggressiveInlining)] public static T? Max(this ReadOnlySpan span) => - Max(span, Comparer.Default); + Max(span, comparer: null); /// /// Returns the maximum value in the span. @@ -4403,45 +4426,68 @@ public static int BinarySearch( /// The span of values to determine the maximum value of. /// The to compare values. /// The maximum value in the span. - /// is . /// is empty and is a non-nullable value type. /// /// If is a reference type and the span sequence is empty, this method returns . /// Null values are ignored when determining the maximum value. If the span contains at least one non-null value, the maximum of those values is returned. If the span does not contain any non-null values, is returned. /// - public static T? Max(this ReadOnlySpan span, IComparer comparer) + public static T? Max(this ReadOnlySpan span, IComparer? comparer) { - if (comparer is null) - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.comparer); + comparer ??= Comparer.Default; + + T? value = default; + int i = 0; - if (span.IsEmpty) + if (value is null) { - if (default(T) is null) + do { - return default; + if (i >= span.Length) + { + return value; + } + value = span[i++]; } + while (value is null); - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoElements); + while (i < span.Length) + { + T next = span[i++]; + if (next is not null && comparer.Compare(next, value) > 0) + { + value = next; + } + } } - - T? value = span[0]; - int i = 1; - - while (value is null) + else { - if ((uint)i >= (uint)span.Length) + if (i >= span.Length) { - return value; + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoElements); } - value = span[i++]; - } - for (; (uint)i < (uint)span.Length; i++) - { - T next = span[i]; - if (next is not null && comparer.Compare(next, value) > 0) + value = span[i]; + if (comparer == Comparer.Default) { - value = next; + while (i < span.Length) + { + T next = span[i++]; + if (Comparer.Default.Compare(next, value) > 0) + { + value = next; + } + } + } + else + { + while (i < span.Length) + { + T next = span[i++]; + if (comparer.Compare(next, value) > 0) + { + value = next; + } + } } } From 8d325970e35aa01186a7d19092641479b05e2b76 Mon Sep 17 00:00:00 2001 From: Emmanuel ANDRE <2341261+manandre@users.noreply.github.com> Date: Tue, 19 May 2026 23:11:00 +0200 Subject: [PATCH 08/13] Add fast paths --- .../src/System/MemoryExtensions.cs | 182 ++++++++++++++++++ 1 file changed, 182 insertions(+) diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index 7bfe06fec40303..503875ee85c8ae 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -7,6 +7,7 @@ using System.ComponentModel; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using System.Runtime.Intrinsics; @@ -4344,6 +4345,20 @@ public static int BinarySearch( { comparer ??= Comparer.Default; + if (typeof(T) == typeof(byte) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(sbyte) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(ushort) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(short) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(char) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(uint) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(int) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(ulong) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(long) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(nuint) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(nint) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(Int128) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(UInt128) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + T? value = default; int i = 0; @@ -4435,6 +4450,20 @@ public static int BinarySearch( { comparer ??= Comparer.Default; + if (typeof(T) == typeof(byte) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(sbyte) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(ushort) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(short) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(char) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(uint) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(int) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(ulong) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(long) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(nuint) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(nint) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(Int128) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + if (typeof(T) == typeof(UInt128) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + T? value = default; int i = 0; @@ -4494,6 +4523,159 @@ public static int BinarySearch( return value; } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ReadOnlySpan Cast(ReadOnlySpan span) + where TTo : struct + => MemoryMarshal.CreateReadOnlySpan( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length); + + private interface IMinMaxCalc where T : struct, IBinaryInteger + { + public static abstract bool Compare(T left, T right); + public static abstract Vector128 Compare(Vector128 left, Vector128 right); + public static abstract Vector256 Compare(Vector256 left, Vector256 right); + public static abstract Vector512 Compare(Vector512 left, Vector512 right); + } + + private readonly struct MinCalc : IMinMaxCalc where T : struct, IBinaryInteger + { + public static bool Compare(T left, T right) => left < right; + public static Vector128 Compare(Vector128 left, Vector128 right) => Vector128.Min(left, right); + public static Vector256 Compare(Vector256 left, Vector256 right) => Vector256.Min(left, right); + public static Vector512 Compare(Vector512 left, Vector512 right) => Vector512.Min(left, right); + } + + private readonly struct MaxCalc : IMinMaxCalc where T : struct, IBinaryInteger + { + public static bool Compare(T left, T right) => left > right; + public static Vector128 Compare(Vector128 left, Vector128 right) => Vector128.Max(left, right); + public static Vector256 Compare(Vector256 left, Vector256 right) => Vector256.Max(left, right); + public static Vector512 Compare(Vector512 left, Vector512 right) => Vector512.Max(left, right); + } + + private static T MinMaxInteger(this ReadOnlySpan span) + where T : struct, IBinaryInteger + where TMinMax : IMinMaxCalc + { + T value; + + if (span.IsEmpty) + { + ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoElements); + } + + if (!Vector128.IsHardwareAccelerated || !Vector128.IsSupported || span.Length < Vector128.Count) + { + value = span[0]; + for (int i = 1; i < span.Length; i++) + { + if (TMinMax.Compare(span[i], value)) + { + value = span[i]; + } + } + return value; + } + + // All vectorized paths reduce to 128-bit, so we can use that as our accumulator + // regardless of the maximum supported vector size. + Vector128 best128; + + if (!Vector256.IsHardwareAccelerated || span.Length < Vector256.Count) + { + ReadOnlySpan data = span; + Vector128 best = Vector128.Create(data); + data = data.Slice(Vector128.Count); + + while (data.Length > Vector128.Count) + { + best = TMinMax.Compare(best, Vector128.Create(data)); + data = data.Slice(Vector128.Count); + } + best128 = TMinMax.Compare(best, Vector128.Create(span.Slice(span.Length - Vector128.Count))); + } + else if (!Vector512.IsHardwareAccelerated || span.Length < Vector512.Count) + { + ReadOnlySpan data = span; + Vector256 best = Vector256.Create(data); + data = data.Slice(Vector256.Count); + + while (data.Length > Vector256.Count) + { + best = TMinMax.Compare(best, Vector256.Create(data)); + data = data.Slice(Vector256.Count); + } + best = TMinMax.Compare(best, Vector256.Create(span.Slice(span.Length - Vector256.Count))); + + // Reduce to 128-bit + best128 = TMinMax.Compare(best.GetLower(), best.GetUpper()); + } + else + { + ReadOnlySpan data = span; + Vector512 best = Vector512.Create(data); + data = data.Slice(Vector512.Count); + + while (data.Length > Vector512.Count) + { + best = TMinMax.Compare(best, Vector512.Create(data)); + data = data.Slice(Vector512.Count); + } + best = TMinMax.Compare(best, Vector512.Create(span.Slice(span.Length - Vector512.Count))); + + // Reduce to 128-bit + Vector256 best256 = TMinMax.Compare(best.GetLower(), best.GetUpper()); + best128 = TMinMax.Compare(best256.GetLower(), best256.GetUpper()); + } + + // Reduce to single value + value = HorizontalMinMax(best128); + + return value; + } + + /// Reduces a to a single element using . + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static T HorizontalMinMax(Vector128 x) + where T : struct, IBinaryInteger + where TMinMax : IMinMaxCalc + { + // Perform log2(Vector128.Count) reductions, each combining the vector with a shuffled + // copy of itself so that lane 0 ends up holding the min/max of all original lanes. + if (Vector128.Count == 16) + { + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsByte(), + Vector128.Create((byte)8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7)).As()); + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsByte(), + Vector128.Create((byte)4, 5, 6, 7, 0, 1, 2, 3, 8, 9, 10, 11, 12, 13, 14, 15)).As()); + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsByte(), + Vector128.Create((byte)2, 3, 0, 1, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)).As()); + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsByte(), + Vector128.Create((byte)1, 0, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15)).As()); + } + else if (Vector128.Count == 8) + { + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsInt16(), + Vector128.Create(4, 5, 6, 7, 0, 1, 2, 3)).As()); + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsInt16(), + Vector128.Create(2, 3, 0, 1, 4, 5, 6, 7)).As()); + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsInt16(), + Vector128.Create(1, 0, 2, 3, 4, 5, 6, 7)).As()); + } + else if (Vector128.Count == 4) + { + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsInt32(), Vector128.Create(2, 3, 0, 1)).As()); + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsInt32(), Vector128.Create(1, 0, 3, 2)).As()); + } + else + { + Debug.Assert(Vector128.Count == 2); + x = TMinMax.Compare(x, Vector128.Shuffle(x.AsInt64(), Vector128.Create(1, 0)).As()); + } + return x.ToScalar(); + } + /// /// Sorts the elements in the entire using the implementation /// of each element of the From 69566d1fef3177be0006da024d6a0da4db281fd0 Mon Sep 17 00:00:00 2001 From: Emmanuel ANDRE <2341261+manandre@users.noreply.github.com> Date: Tue, 19 May 2026 23:40:14 +0200 Subject: [PATCH 09/13] Add tests for integer types --- .../tests/ReadOnlySpan/MinMax.cs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs index 6d923aa833857c..a4e3c739bb885b 100644 --- a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs @@ -82,5 +82,40 @@ public static void MinMax_CustomComparer_IsUsed() Assert.Equal(7, ints.Min(reverse)); Assert.Equal(-1, ints.Max(reverse)); } + + [Fact] + public static void MinMax_IntegerTypes_DefaultAndNullComparer_ProduceExpectedValues() + { + AssertMinMaxValues(new byte[] { 12, 3, 255, 7 }, (byte)3, (byte)255); + AssertMinMaxValues(new sbyte[] { 12, -9, 100, 7 }, (sbyte)-9, (sbyte)100); + AssertMinMaxValues(new ushort[] { 12, 3, 65535, 7 }, (ushort)3, ushort.MaxValue); + AssertMinMaxValues(new short[] { 12, -9, 100, 7 }, (short)-9, (short)100); + AssertMinMaxValues(new char[] { 'x', 'b', 'm', 'z' }, 'b', 'z'); + AssertMinMaxValues(new uint[] { 12u, 3u, 400u, 7u }, 3u, 400u); + AssertMinMaxValues(new int[] { 12, -9, 400, 7 }, -9, 400); + AssertMinMaxValues(new ulong[] { 12ul, 3ul, 400ul, 7ul }, 3ul, 400ul); + AssertMinMaxValues(new long[] { 12L, -9L, 400L, 7L }, -9L, 400L); + AssertMinMaxValues(new nuint[] { (nuint)12, (nuint)3, (nuint)400, (nuint)7 }, (nuint)3, (nuint)400); + AssertMinMaxValues(new nint[] { (nint)12, (nint)(-9), (nint)400, (nint)7 }, (nint)(-9), (nint)400); + AssertMinMaxValues(new Int128[] { (Int128)12, (Int128)(-9), (Int128)400, (Int128)7 }, (Int128)(-9), (Int128)400); + AssertMinMaxValues(new UInt128[] { (UInt128)12, (UInt128)3, (UInt128)400, (UInt128)7 }, (UInt128)3, (UInt128)400); + } + + private static void AssertMinMaxValues(T[] values, T expectedMin, T expectedMax) + where T : IComparable + { + ReadOnlySpan span = values; + IComparer comparer = Comparer.Create(static (left, right) => left.CompareTo(right)); + IComparer reverseComparer = Comparer.Create(static (left, right) => right.CompareTo(left)); + + Assert.Equal(expectedMin, span.Min()); + Assert.Equal(expectedMax, span.Max()); + Assert.Equal(expectedMin, span.Min(comparer: null)); + Assert.Equal(expectedMax, span.Max(comparer: null)); + Assert.Equal(expectedMin, span.Min(comparer)); + Assert.Equal(expectedMax, span.Max(comparer)); + Assert.Equal(expectedMax, span.Min(reverseComparer)); + Assert.Equal(expectedMin, span.Max(reverseComparer)); + } } } From c2c8dfa1579c30733b01cd12506bd27f3550192b Mon Sep 17 00:00:00 2001 From: Emmanuel ANDRE <2341261+manandre@users.noreply.github.com> Date: Wed, 20 May 2026 00:07:31 +0200 Subject: [PATCH 10/13] Add tests for NaN case --- .../tests/ReadOnlySpan/MinMax.cs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs index a4e3c739bb885b..018bce399a513c 100644 --- a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs @@ -101,6 +101,36 @@ public static void MinMax_IntegerTypes_DefaultAndNullComparer_ProduceExpectedVal AssertMinMaxValues(new UInt128[] { (UInt128)12, (UInt128)3, (UInt128)400, (UInt128)7 }, (UInt128)3, (UInt128)400); } + [Theory] + [InlineData(float.NaN, 1f, 2f)] + [InlineData(1f, float.NaN, 2f)] + [InlineData(1f, 2f, float.NaN)] + public static void MinMax_Float_NaN_UsesExpectedOrdering(float first, float second, float third) + { + float[] values = [first, second, third]; + ReadOnlySpan span = values; + + Assert.True(float.IsNaN(span.Min())); + Assert.True(float.IsNaN(span.Min(comparer: null))); + Assert.Equal(2f, span.Max()); + Assert.Equal(2f, span.Max(comparer: null)); + } + + [Theory] + [InlineData(double.NaN, 1d, 2d)] + [InlineData(1d, double.NaN, 2d)] + [InlineData(1d, 2d, double.NaN)] + public static void MinMax_Double_NaN_UsesExpectedOrdering(double first, double second, double third) + { + double[] values = [first, second, third]; + ReadOnlySpan span = values; + + Assert.True(double.IsNaN(span.Min())); + Assert.True(double.IsNaN(span.Min(comparer: null))); + Assert.Equal(2d, span.Max()); + Assert.Equal(2d, span.Max(comparer: null)); + } + private static void AssertMinMaxValues(T[] values, T expectedMin, T expectedMax) where T : IComparable { From 55b1cf7e752f0010036a5e5ce0997a653665bd3c Mon Sep 17 00:00:00 2001 From: Emmanuel ANDRE <2341261+manandre@users.noreply.github.com> Date: Wed, 20 May 2026 00:21:19 +0200 Subject: [PATCH 11/13] Small fixes --- src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs | 8 ++++---- .../System.Private.CoreLib/src/System/MemoryExtensions.cs | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs index 018bce399a513c..7bc0df9f097aed 100644 --- a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs @@ -13,10 +13,10 @@ public static void MinMax_Empty_NonNullableValueType_Throws() { ReadOnlySpan span = ReadOnlySpan.Empty; - TestHelpers.AssertThrows(span, (_span) => _span.Min()); - TestHelpers.AssertThrows(span, (_span) => _span.Max()); - TestHelpers.AssertThrows(span, (_span) => _span.Min(Comparer.Default)); - TestHelpers.AssertThrows(span, (_span) => _span.Max(Comparer.Default)); + TestHelpers.AssertThrows(span, _ => _.Min()); + TestHelpers.AssertThrows(span, _ => _.Max()); + TestHelpers.AssertThrows(span, _ => _.Min(Comparer.Default)); + TestHelpers.AssertThrows(span, _ => _.Max(Comparer.Default)); } [Fact] diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index 503875ee85c8ae..c9744f29634863 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -4390,7 +4390,7 @@ public static int BinarySearch( ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoElements); } - value = span[i]; + value = span[i++]; if (comparer == Comparer.Default) { while (i < span.Length) @@ -4495,7 +4495,7 @@ public static int BinarySearch( ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoElements); } - value = span[i]; + value = span[i++]; if (comparer == Comparer.Default) { while (i < span.Length) From 0acc3391ce1c927ff5f85b38750d56be95cce821 Mon Sep 17 00:00:00 2001 From: Emmanuel ANDRE <2341261+manandre@users.noreply.github.com> Date: Wed, 20 May 2026 00:35:33 +0200 Subject: [PATCH 12/13] Reduce code duplication --- .../src/System/MemoryExtensions.cs | 100 ++++++------------ 1 file changed, 33 insertions(+), 67 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index c9744f29634863..ece809a274ca5b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -4359,63 +4359,7 @@ public static int BinarySearch( if (typeof(T) == typeof(Int128) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); if (typeof(T) == typeof(UInt128) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); - T? value = default; - int i = 0; - - if (value is null) - { - do - { - if (i >= span.Length) - { - return value; - } - value = span[i++]; - } - while (value is null); - - while (i < span.Length) - { - T next = span[i++]; - if (next is not null && comparer.Compare(next, value) < 0) - { - value = next; - } - } - } - else - { - if (i >= span.Length) - { - ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_NoElements); - } - - value = span[i++]; - if (comparer == Comparer.Default) - { - while (i < span.Length) - { - T next = span[i++]; - if (Comparer.Default.Compare(next, value) < 0) - { - value = next; - } - } - } - else - { - while (i < span.Length) - { - T next = span[i++]; - if (comparer.Compare(next, value) < 0) - { - value = next; - } - } - } - } - - return value; + return MinMax(span, comparer); } /// @@ -4464,6 +4408,34 @@ public static int BinarySearch( if (typeof(T) == typeof(Int128) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); if (typeof(T) == typeof(UInt128) && comparer == Comparer.Default) return (T)(object)MinMaxInteger>(Cast(span)); + return MinMax(span, comparer); + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static ReadOnlySpan Cast(ReadOnlySpan span) + where TTo : struct + => MemoryMarshal.CreateReadOnlySpan( + ref Unsafe.As(ref MemoryMarshal.GetReference(span)), + span.Length); + + private interface IMinMaxDirection + { + static abstract bool CompareResult(int comparison); + } + + private readonly struct MinDirection : IMinMaxDirection + { + public static bool CompareResult(int comparison) => comparison < 0; + } + + private readonly struct MaxDirection : IMinMaxDirection + { + public static bool CompareResult(int comparison) => comparison > 0; + } + + private static T? MinMax(this ReadOnlySpan span, IComparer comparer) + where TDirection : struct, IMinMaxDirection + { T? value = default; int i = 0; @@ -4475,6 +4447,7 @@ public static int BinarySearch( { return value; } + value = span[i++]; } while (value is null); @@ -4482,7 +4455,7 @@ public static int BinarySearch( while (i < span.Length) { T next = span[i++]; - if (next is not null && comparer.Compare(next, value) > 0) + if (next is not null && TDirection.CompareResult(comparer.Compare(next, value))) { value = next; } @@ -4501,7 +4474,7 @@ public static int BinarySearch( while (i < span.Length) { T next = span[i++]; - if (Comparer.Default.Compare(next, value) > 0) + if (TDirection.CompareResult(Comparer.Default.Compare(next, value))) { value = next; } @@ -4512,7 +4485,7 @@ public static int BinarySearch( while (i < span.Length) { T next = span[i++]; - if (comparer.Compare(next, value) > 0) + if (TDirection.CompareResult(comparer.Compare(next, value))) { value = next; } @@ -4523,13 +4496,6 @@ public static int BinarySearch( return value; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static ReadOnlySpan Cast(ReadOnlySpan span) - where TTo : struct - => MemoryMarshal.CreateReadOnlySpan( - ref Unsafe.As(ref MemoryMarshal.GetReference(span)), - span.Length); - private interface IMinMaxCalc where T : struct, IBinaryInteger { public static abstract bool Compare(T left, T right); From 9c71813ca538a2981a16b9582daae7305fced0a5 Mon Sep 17 00:00:00 2001 From: Emmanuel ANDRE <2341261+manandre@users.noreply.github.com> Date: Wed, 20 May 2026 00:40:57 +0200 Subject: [PATCH 13/13] Add test --- src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs index 7bc0df9f097aed..bae15102aed304 100644 --- a/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs +++ b/src/libraries/System.Memory/tests/ReadOnlySpan/MinMax.cs @@ -12,11 +12,14 @@ public static partial class ReadOnlySpanTests public static void MinMax_Empty_NonNullableValueType_Throws() { ReadOnlySpan span = ReadOnlySpan.Empty; + IComparer nonDefaultComparer = Comparer.Create((x, y) => x.CompareTo(y)); TestHelpers.AssertThrows(span, _ => _.Min()); TestHelpers.AssertThrows(span, _ => _.Max()); TestHelpers.AssertThrows(span, _ => _.Min(Comparer.Default)); TestHelpers.AssertThrows(span, _ => _.Max(Comparer.Default)); + TestHelpers.AssertThrows(span, _ => _.Min(nonDefaultComparer)); + TestHelpers.AssertThrows(span, _ => _.Max(nonDefaultComparer)); } [Fact]