Add MemoryExtensions.Count (#80662)
authorSandro Bollhalder <boli89@gmail.com>
Fri, 27 Jan 2023 20:24:05 +0000 (21:24 +0100)
committerGitHub <noreply@github.com>
Fri, 27 Jan 2023 20:24:05 +0000 (15:24 -0500)
* Add MemoryExtensions.Count

* add test cases

* fix implementation

* fix review comments part I

* more tests

* return 0 if value is Empty

* Address PR feedback

---------

Co-authored-by: Stephen Toub <stoub@microsoft.com>
src/libraries/System.Memory/System.Memory.sln
src/libraries/System.Memory/ref/System.Memory.cs
src/libraries/System.Memory/tests/ReadOnlySpan/Count.T.cs [new file with mode: 0644]
src/libraries/System.Memory/tests/ReadOnlySpan/Count.byte.cs [new file with mode: 0644]
src/libraries/System.Memory/tests/System.Memory.Tests.csproj
src/libraries/System.Memory/tests/TestHelpers.cs
src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs
src/libraries/System.Private.CoreLib/src/System/SpanHelpers.T.cs

index d66566f..1dd8168 100644 (file)
@@ -1,4 +1,8 @@
-Microsoft Visual Studio Solution File, Format Version 12.00
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio Version 17
+VisualStudioVersion = 17.6.33316.92
+MinimumVisualStudioVersion = 10.0.40219.1
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Private.CoreLib", "..\..\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj", "{7746BFD6-E6D6-4703-AA2D-43380B5DEA22}"
 EndProject
 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{6A54FACA-933E-4C1D-92AB-1A5506CFC212}"
@@ -29,17 +33,23 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "gen", "gen", "{2BEB1A89-DD2
 EndProject
 Global
        GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Checked|Any CPU = Checked|Any CPU
+               Checked|x64 = Checked|x64
+               Checked|x86 = Checked|x86
                Debug|Any CPU = Debug|Any CPU
                Debug|x64 = Debug|x64
                Debug|x86 = Debug|x86
                Release|Any CPU = Release|Any CPU
                Release|x64 = Release|x64
                Release|x86 = Release|x86
-               Checked|Any CPU = Checked|Any CPU
-               Checked|x64 = Checked|x64
-               Checked|x86 = Checked|x86
        EndGlobalSection
        GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Checked|Any CPU.ActiveCfg = Checked|x64
+               {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Checked|Any CPU.Build.0 = Checked|x64
+               {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Checked|x64.ActiveCfg = Checked|x64
+               {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Checked|x64.Build.0 = Checked|x64
+               {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Checked|x86.ActiveCfg = Checked|x86
+               {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Checked|x86.Build.0 = Checked|x86
                {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Debug|Any CPU.ActiveCfg = Debug|x64
                {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Debug|Any CPU.Build.0 = Debug|x64
                {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Debug|x64.ActiveCfg = Debug|x64
@@ -52,12 +62,9 @@ Global
                {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Release|x64.Build.0 = Release|x64
                {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Release|x86.ActiveCfg = Release|x86
                {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Release|x86.Build.0 = Release|x86
-               {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Checked|Any CPU.ActiveCfg = Checked|x64
-               {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Checked|Any CPU.Build.0 = Checked|x64
-               {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Checked|x64.ActiveCfg = Checked|x64
-               {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Checked|x64.Build.0 = Checked|x64
-               {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Checked|x86.ActiveCfg = Checked|x86
-               {7746BFD6-E6D6-4703-AA2D-43380B5DEA22}.Checked|x86.Build.0 = Checked|x86
+               {6A54FACA-933E-4C1D-92AB-1A5506CFC212}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+               {6A54FACA-933E-4C1D-92AB-1A5506CFC212}.Checked|x64.ActiveCfg = Debug|Any CPU
+               {6A54FACA-933E-4C1D-92AB-1A5506CFC212}.Checked|x86.ActiveCfg = Debug|Any CPU
                {6A54FACA-933E-4C1D-92AB-1A5506CFC212}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {6A54FACA-933E-4C1D-92AB-1A5506CFC212}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {6A54FACA-933E-4C1D-92AB-1A5506CFC212}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -70,9 +77,9 @@ Global
                {6A54FACA-933E-4C1D-92AB-1A5506CFC212}.Release|x64.Build.0 = Release|Any CPU
                {6A54FACA-933E-4C1D-92AB-1A5506CFC212}.Release|x86.ActiveCfg = Release|Any CPU
                {6A54FACA-933E-4C1D-92AB-1A5506CFC212}.Release|x86.Build.0 = Release|Any CPU
-               {6A54FACA-933E-4C1D-92AB-1A5506CFC212}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
-               {6A54FACA-933E-4C1D-92AB-1A5506CFC212}.Checked|x64.ActiveCfg = Debug|Any CPU
-               {6A54FACA-933E-4C1D-92AB-1A5506CFC212}.Checked|x86.ActiveCfg = Debug|Any CPU
+               {9112BAE3-344D-4DD0-ADC9-478D82B84584}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+               {9112BAE3-344D-4DD0-ADC9-478D82B84584}.Checked|x64.ActiveCfg = Debug|Any CPU
+               {9112BAE3-344D-4DD0-ADC9-478D82B84584}.Checked|x86.ActiveCfg = Debug|Any CPU
                {9112BAE3-344D-4DD0-ADC9-478D82B84584}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {9112BAE3-344D-4DD0-ADC9-478D82B84584}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {9112BAE3-344D-4DD0-ADC9-478D82B84584}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -85,9 +92,9 @@ Global
                {9112BAE3-344D-4DD0-ADC9-478D82B84584}.Release|x64.Build.0 = Release|Any CPU
                {9112BAE3-344D-4DD0-ADC9-478D82B84584}.Release|x86.ActiveCfg = Release|Any CPU
                {9112BAE3-344D-4DD0-ADC9-478D82B84584}.Release|x86.Build.0 = Release|Any CPU
-               {9112BAE3-344D-4DD0-ADC9-478D82B84584}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
-               {9112BAE3-344D-4DD0-ADC9-478D82B84584}.Checked|x64.ActiveCfg = Debug|Any CPU
-               {9112BAE3-344D-4DD0-ADC9-478D82B84584}.Checked|x86.ActiveCfg = Debug|Any CPU
+               {C9417154-D8DB-4FF9-9DD8-6B2ED351FC92}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+               {C9417154-D8DB-4FF9-9DD8-6B2ED351FC92}.Checked|x64.ActiveCfg = Debug|Any CPU
+               {C9417154-D8DB-4FF9-9DD8-6B2ED351FC92}.Checked|x86.ActiveCfg = Debug|Any CPU
                {C9417154-D8DB-4FF9-9DD8-6B2ED351FC92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {C9417154-D8DB-4FF9-9DD8-6B2ED351FC92}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {C9417154-D8DB-4FF9-9DD8-6B2ED351FC92}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -100,9 +107,9 @@ Global
                {C9417154-D8DB-4FF9-9DD8-6B2ED351FC92}.Release|x64.Build.0 = Release|Any CPU
                {C9417154-D8DB-4FF9-9DD8-6B2ED351FC92}.Release|x86.ActiveCfg = Release|Any CPU
                {C9417154-D8DB-4FF9-9DD8-6B2ED351FC92}.Release|x86.Build.0 = Release|Any CPU
-               {C9417154-D8DB-4FF9-9DD8-6B2ED351FC92}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
-               {C9417154-D8DB-4FF9-9DD8-6B2ED351FC92}.Checked|x64.ActiveCfg = Debug|Any CPU
-               {C9417154-D8DB-4FF9-9DD8-6B2ED351FC92}.Checked|x86.ActiveCfg = Debug|Any CPU
+               {C2BC6AE7-7E8B-4AA2-8E9F-5D4B9127B297}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+               {C2BC6AE7-7E8B-4AA2-8E9F-5D4B9127B297}.Checked|x64.ActiveCfg = Debug|Any CPU
+               {C2BC6AE7-7E8B-4AA2-8E9F-5D4B9127B297}.Checked|x86.ActiveCfg = Debug|Any CPU
                {C2BC6AE7-7E8B-4AA2-8E9F-5D4B9127B297}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {C2BC6AE7-7E8B-4AA2-8E9F-5D4B9127B297}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {C2BC6AE7-7E8B-4AA2-8E9F-5D4B9127B297}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -115,9 +122,9 @@ Global
                {C2BC6AE7-7E8B-4AA2-8E9F-5D4B9127B297}.Release|x64.Build.0 = Release|Any CPU
                {C2BC6AE7-7E8B-4AA2-8E9F-5D4B9127B297}.Release|x86.ActiveCfg = Release|Any CPU
                {C2BC6AE7-7E8B-4AA2-8E9F-5D4B9127B297}.Release|x86.Build.0 = Release|Any CPU
-               {C2BC6AE7-7E8B-4AA2-8E9F-5D4B9127B297}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
-               {C2BC6AE7-7E8B-4AA2-8E9F-5D4B9127B297}.Checked|x64.ActiveCfg = Debug|Any CPU
-               {C2BC6AE7-7E8B-4AA2-8E9F-5D4B9127B297}.Checked|x86.ActiveCfg = Debug|Any CPU
+               {EFF00253-633C-4D2F-86EE-F40C721F6A68}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+               {EFF00253-633C-4D2F-86EE-F40C721F6A68}.Checked|x64.ActiveCfg = Debug|Any CPU
+               {EFF00253-633C-4D2F-86EE-F40C721F6A68}.Checked|x86.ActiveCfg = Debug|Any CPU
                {EFF00253-633C-4D2F-86EE-F40C721F6A68}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {EFF00253-633C-4D2F-86EE-F40C721F6A68}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {EFF00253-633C-4D2F-86EE-F40C721F6A68}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -130,9 +137,9 @@ Global
                {EFF00253-633C-4D2F-86EE-F40C721F6A68}.Release|x64.Build.0 = Release|Any CPU
                {EFF00253-633C-4D2F-86EE-F40C721F6A68}.Release|x86.ActiveCfg = Release|Any CPU
                {EFF00253-633C-4D2F-86EE-F40C721F6A68}.Release|x86.Build.0 = Release|Any CPU
-               {EFF00253-633C-4D2F-86EE-F40C721F6A68}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
-               {EFF00253-633C-4D2F-86EE-F40C721F6A68}.Checked|x64.ActiveCfg = Debug|Any CPU
-               {EFF00253-633C-4D2F-86EE-F40C721F6A68}.Checked|x86.ActiveCfg = Debug|Any CPU
+               {D0EEF7E0-BD51-4C39-AF4F-DD583D01AEBE}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+               {D0EEF7E0-BD51-4C39-AF4F-DD583D01AEBE}.Checked|x64.ActiveCfg = Debug|Any CPU
+               {D0EEF7E0-BD51-4C39-AF4F-DD583D01AEBE}.Checked|x86.ActiveCfg = Debug|Any CPU
                {D0EEF7E0-BD51-4C39-AF4F-DD583D01AEBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {D0EEF7E0-BD51-4C39-AF4F-DD583D01AEBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {D0EEF7E0-BD51-4C39-AF4F-DD583D01AEBE}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -145,9 +152,9 @@ Global
                {D0EEF7E0-BD51-4C39-AF4F-DD583D01AEBE}.Release|x64.Build.0 = Release|Any CPU
                {D0EEF7E0-BD51-4C39-AF4F-DD583D01AEBE}.Release|x86.ActiveCfg = Release|Any CPU
                {D0EEF7E0-BD51-4C39-AF4F-DD583D01AEBE}.Release|x86.Build.0 = Release|Any CPU
-               {D0EEF7E0-BD51-4C39-AF4F-DD583D01AEBE}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
-               {D0EEF7E0-BD51-4C39-AF4F-DD583D01AEBE}.Checked|x64.ActiveCfg = Debug|Any CPU
-               {D0EEF7E0-BD51-4C39-AF4F-DD583D01AEBE}.Checked|x86.ActiveCfg = Debug|Any CPU
+               {84AD7BF6-D76C-4BEE-9879-5A23150DD3F7}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+               {84AD7BF6-D76C-4BEE-9879-5A23150DD3F7}.Checked|x64.ActiveCfg = Debug|Any CPU
+               {84AD7BF6-D76C-4BEE-9879-5A23150DD3F7}.Checked|x86.ActiveCfg = Debug|Any CPU
                {84AD7BF6-D76C-4BEE-9879-5A23150DD3F7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {84AD7BF6-D76C-4BEE-9879-5A23150DD3F7}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {84AD7BF6-D76C-4BEE-9879-5A23150DD3F7}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -160,9 +167,9 @@ Global
                {84AD7BF6-D76C-4BEE-9879-5A23150DD3F7}.Release|x64.Build.0 = Release|Any CPU
                {84AD7BF6-D76C-4BEE-9879-5A23150DD3F7}.Release|x86.ActiveCfg = Release|Any CPU
                {84AD7BF6-D76C-4BEE-9879-5A23150DD3F7}.Release|x86.Build.0 = Release|Any CPU
-               {84AD7BF6-D76C-4BEE-9879-5A23150DD3F7}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
-               {84AD7BF6-D76C-4BEE-9879-5A23150DD3F7}.Checked|x64.ActiveCfg = Debug|Any CPU
-               {84AD7BF6-D76C-4BEE-9879-5A23150DD3F7}.Checked|x86.ActiveCfg = Debug|Any CPU
+               {DA7CEED7-1A86-4221-B4AD-4307AB83A31F}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+               {DA7CEED7-1A86-4221-B4AD-4307AB83A31F}.Checked|x64.ActiveCfg = Debug|Any CPU
+               {DA7CEED7-1A86-4221-B4AD-4307AB83A31F}.Checked|x86.ActiveCfg = Debug|Any CPU
                {DA7CEED7-1A86-4221-B4AD-4307AB83A31F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {DA7CEED7-1A86-4221-B4AD-4307AB83A31F}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {DA7CEED7-1A86-4221-B4AD-4307AB83A31F}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -175,9 +182,9 @@ Global
                {DA7CEED7-1A86-4221-B4AD-4307AB83A31F}.Release|x64.Build.0 = Release|Any CPU
                {DA7CEED7-1A86-4221-B4AD-4307AB83A31F}.Release|x86.ActiveCfg = Release|Any CPU
                {DA7CEED7-1A86-4221-B4AD-4307AB83A31F}.Release|x86.Build.0 = Release|Any CPU
-               {DA7CEED7-1A86-4221-B4AD-4307AB83A31F}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
-               {DA7CEED7-1A86-4221-B4AD-4307AB83A31F}.Checked|x64.ActiveCfg = Debug|Any CPU
-               {DA7CEED7-1A86-4221-B4AD-4307AB83A31F}.Checked|x86.ActiveCfg = Debug|Any CPU
+               {B773D664-C00B-4DB3-823B-947576EE7A46}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
+               {B773D664-C00B-4DB3-823B-947576EE7A46}.Checked|x64.ActiveCfg = Debug|Any CPU
+               {B773D664-C00B-4DB3-823B-947576EE7A46}.Checked|x86.ActiveCfg = Debug|Any CPU
                {B773D664-C00B-4DB3-823B-947576EE7A46}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
                {B773D664-C00B-4DB3-823B-947576EE7A46}.Debug|Any CPU.Build.0 = Debug|Any CPU
                {B773D664-C00B-4DB3-823B-947576EE7A46}.Debug|x64.ActiveCfg = Debug|Any CPU
@@ -190,26 +197,26 @@ Global
                {B773D664-C00B-4DB3-823B-947576EE7A46}.Release|x64.Build.0 = Release|Any CPU
                {B773D664-C00B-4DB3-823B-947576EE7A46}.Release|x86.ActiveCfg = Release|Any CPU
                {B773D664-C00B-4DB3-823B-947576EE7A46}.Release|x86.Build.0 = Release|Any CPU
-               {B773D664-C00B-4DB3-823B-947576EE7A46}.Checked|Any CPU.ActiveCfg = Debug|Any CPU
-               {B773D664-C00B-4DB3-823B-947576EE7A46}.Checked|x64.ActiveCfg = Debug|Any CPU
-               {B773D664-C00B-4DB3-823B-947576EE7A46}.Checked|x86.ActiveCfg = Debug|Any CPU
        EndGlobalSection
        GlobalSection(SolutionProperties) = preSolution
                HideSolutionNode = FALSE
        EndGlobalSection
        GlobalSection(NestedProjects) = preSolution
                {7746BFD6-E6D6-4703-AA2D-43380B5DEA22} = {C352AC7D-959D-431F-AF83-2CA506B70D59}
-               {C9417154-D8DB-4FF9-9DD8-6B2ED351FC92} = {C352AC7D-959D-431F-AF83-2CA506B70D59}
                {6A54FACA-933E-4C1D-92AB-1A5506CFC212} = {FA259C32-B79B-4DE2-9677-055D5D25FA33}
-               {C2BC6AE7-7E8B-4AA2-8E9F-5D4B9127B297} = {FA259C32-B79B-4DE2-9677-055D5D25FA33}
                {9112BAE3-344D-4DD0-ADC9-478D82B84584} = {7212FBCF-E89D-4065-9DCE-D5F7E5D3EF1D}
-               {D0EEF7E0-BD51-4C39-AF4F-DD583D01AEBE} = {7212FBCF-E89D-4065-9DCE-D5F7E5D3EF1D}
-               {B773D664-C00B-4DB3-823B-947576EE7A46} = {7212FBCF-E89D-4065-9DCE-D5F7E5D3EF1D}
+               {C9417154-D8DB-4FF9-9DD8-6B2ED351FC92} = {C352AC7D-959D-431F-AF83-2CA506B70D59}
+               {C2BC6AE7-7E8B-4AA2-8E9F-5D4B9127B297} = {FA259C32-B79B-4DE2-9677-055D5D25FA33}
                {EFF00253-633C-4D2F-86EE-F40C721F6A68} = {2BEB1A89-DD2D-42BD-95DD-89860A0C9663}
+               {D0EEF7E0-BD51-4C39-AF4F-DD583D01AEBE} = {7212FBCF-E89D-4065-9DCE-D5F7E5D3EF1D}
                {84AD7BF6-D76C-4BEE-9879-5A23150DD3F7} = {2BEB1A89-DD2D-42BD-95DD-89860A0C9663}
                {DA7CEED7-1A86-4221-B4AD-4307AB83A31F} = {2BEB1A89-DD2D-42BD-95DD-89860A0C9663}
+               {B773D664-C00B-4DB3-823B-947576EE7A46} = {7212FBCF-E89D-4065-9DCE-D5F7E5D3EF1D}
        EndGlobalSection
        GlobalSection(ExtensibilityGlobals) = postSolution
                SolutionGuid = {65DB6A6B-0AAC-4BC2-A61A-641679771DC7}
        EndGlobalSection
+       GlobalSection(SharedMSBuildProjectFiles) = preSolution
+               ..\System.Private.CoreLib\src\System.Private.CoreLib.Shared.projitems*{7746bfd6-e6d6-4703-aa2d-43380b5dea22}*SharedItemsImports = 5
+       EndGlobalSection
 EndGlobal
index 6669b94..9f998a0 100644 (file)
@@ -234,6 +234,10 @@ namespace System
         public static bool Contains<T>(this System.Span<T> span, T value) where T : System.IEquatable<T>? { throw null; }
         public static void CopyTo<T>(this T[]? source, System.Memory<T> destination) { }
         public static void CopyTo<T>(this T[]? source, System.Span<T> destination) { }
+        public static int Count<T>(this System.Span<T> span, T value) where T : System.IEquatable<T>? { throw null; }
+        public static int Count<T>(this System.ReadOnlySpan<T> span, T value) where T : System.IEquatable<T>? { throw null; }
+        public static int Count<T>(this System.Span<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T>? { throw null; }
+        public static int Count<T>(this System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T>? { throw null; }
         public static bool EndsWith(this System.ReadOnlySpan<char> span, System.ReadOnlySpan<char> value, System.StringComparison comparisonType) { throw null; }
         public static bool EndsWith<T>(this System.ReadOnlySpan<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T>? { throw null; }
         public static bool EndsWith<T>(this System.Span<T> span, System.ReadOnlySpan<T> value) where T : System.IEquatable<T>? { throw null; }
diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/Count.T.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/Count.T.cs
new file mode 100644 (file)
index 0000000..6cc1ff7
--- /dev/null
@@ -0,0 +1,383 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Xunit;
+
+namespace System.SpanTests
+{
+    public static partial class ReadOnlySpanTests
+    {    
+        [Fact]
+        public static void ZeroLengthCount_Int()
+        {
+            Assert.Equal(0, ReadOnlySpan<int>.Empty.Count(0));
+        }
+        
+        [Fact]
+        public static void ZeroLengthCount_RosInt()
+        {
+            for (int i = 0; i <= 2; i++)
+            {
+                Assert.Equal(0, ReadOnlySpan<int>.Empty.Count(new int[i]));
+            }
+        }
+
+        [Fact]
+        public static void ZeroLengthNeedleCount_RosInt()
+        {
+            ReadOnlySpan<int> span = new ReadOnlySpan<int>(new int[] { 5, 5, 5, 5, 5 });
+
+            Assert.Equal(0, span.Count<int>(ReadOnlySpan<int>.Empty));
+        }
+
+        [Fact]
+        public static void TestCount_Int()
+        {
+            for (int length = 0; length < 32; length++)
+            {
+                int[] a = new int[length];
+                for (int i = 0; i < length; i++)
+                {
+                    a[i] = 10 * (i + 1);
+                }
+                ReadOnlySpan<int> span = new ReadOnlySpan<int>(a);
+
+                for (int targetIndex = 0; targetIndex < length; targetIndex++)
+                {
+                    Assert.Equal(1, span.Count(a[targetIndex]));
+                }
+            }
+        }
+
+        [Fact]
+        public static void TestCount_RosInt()
+        {
+            for (int length = 0; length < 32; length++)
+            {
+                int[] a = new int[length];
+                for (int i = 0; i < length; i++)
+                {
+                    a[i] = 10 * (i + 1);
+                }
+                ReadOnlySpan<int> span = new ReadOnlySpan<int>(a);
+
+                for (int targetIndex = 0; targetIndex < length - 1; targetIndex++)
+                {
+                    Assert.Equal(1, span.Count(new int[] { a[targetIndex], a[targetIndex + 1] }));
+                }
+            }
+        }
+
+        [Fact]
+        public static void TestMultipleCount_Int()
+        {
+            for (int length = 2; length < 32; length++)
+            {
+                int[] a = new int[length];
+                for (int i = 0; i < length; i++)
+                {
+                    a[i] = 10 * (i + 1);
+                }
+
+                a[^1] = a[^2] = 5555;
+
+                ReadOnlySpan<int> span = new ReadOnlySpan<int>(a);
+                Assert.Equal(2, span.Count(5555));
+            }
+        }
+        
+        [Fact]
+        public static void TestMultipleCount_RosInt()
+        {
+            for (int length = 4; length < 32; length++)
+            {
+                int[] a = new int[length];
+                for (int i = 0; i < length; i++)
+                {
+                    a[i] = 10 * (i + 1);
+                }
+                a[0] = a[1] = a[^1] = a[^2] = 5555;
+
+                ReadOnlySpan<int> span = new ReadOnlySpan<int>(a);
+                Assert.Equal(2, span.Count<int>(new int[] { 5555, 5555 }));
+            }
+        }
+
+        [Fact]
+        public static void OnNoMatchForCountMakeSureEveryElementIsCompared_TInt()
+        {
+            for (int length = 0; length < 100; length++)
+            {
+                TIntLog log = new TIntLog();
+
+                TInt[] a = new TInt[length];
+                for (int i = 0; i < length; i++)
+                {
+                    a[i] = new TInt(10 * (i + 1), log);
+                }
+                ReadOnlySpan<TInt> span = new ReadOnlySpan<TInt>(a);
+                Assert.Equal(0, span.Count(new TInt(9999, log)));
+
+                // Since we asked for a non-existent value, make sure each element of the array was compared once.
+                // (Strictly speaking, it would not be illegal for Count to compare an element more than once but
+                // that would be a non-optimal implementation and a red flag. So we'll stick with the stricter test.)
+                Assert.Equal(a.Length, log.Count);
+                foreach (TInt elem in a)
+                {
+                    int numCompares = log.CountCompares(elem.Value, 9999);
+                    Assert.True(numCompares == 1, $"Expected {numCompares} == 1 for element {elem.Value}.");
+                }
+            }
+        }
+        
+        [Fact]
+        public static void OnNoMatchForCountMakeSureEveryElementIsCompared_RosTInt()
+        {
+            for (int length = 0; length < 100; length++)
+            {
+                TIntLog log = new TIntLog();
+
+                TInt[] a = new TInt[length];
+                for (int i = 0; i < length; i++)
+                {
+                    a[i] = new TInt(10 * (i + 1), log);
+                }
+
+                ReadOnlySpan<TInt> span = new ReadOnlySpan<TInt>(a);
+                Assert.Equal(0, span.Count(new TInt[] { new TInt(9999, log), new TInt(10000, log) }));
+
+                // Since we asked for a non-existent value, make sure each element of the array was compared once.
+                // (Strictly speaking, it would not be illegal for Count to compare an element more than once but
+                // that would be a non-optimal implementation and a red flag. So we'll stick with the stricter test.)
+                if (length > 0)
+                {
+                    Assert.Equal(a.Length - 1, log.Count);
+                }
+                for (int i = 0; i < length - 1; i++)
+                {
+                    int numCompares = log.CountCompares(a[i].Value, 9999);
+                    Assert.True(numCompares == 1, $"Expected {numCompares} == 1 for element {a[i].Value}.");
+                    
+                    numCompares = log.CountCompares(a[i].Value, 10000);
+                    Assert.True(numCompares == 0, $"Expected {numCompares} == 0 for element {a[i].Value}.");
+                }
+            }
+        }
+
+        [Fact]
+        public static void MakeSureNoChecksForCountGoOutOfRange_TInt()
+        {
+            const int GuardValue = 77777;
+            const int GuardLength = 50;
+
+            void CheckForOutOfRangeAccess(int x, int y) =>
+                Assert.True(x != GuardValue && y != GuardValue, $"{x} or {y} == {GuardValue}");
+
+            for (int length = 0; length < 100; length++)
+            {
+                TInt[] a = new TInt[GuardLength + length + GuardLength];
+                for (int i = 0; i < a.Length; i++)
+                {
+                    a[i] = new TInt(GuardValue, CheckForOutOfRangeAccess);
+                }
+
+                for (int i = 0; i < length; i++)
+                {
+                    a[GuardLength + i] = new TInt(10 * (i + 1), CheckForOutOfRangeAccess);
+                }
+
+                ReadOnlySpan<TInt> span = new ReadOnlySpan<TInt>(a, GuardLength, length);
+                Assert.Equal(0, span.Count(new TInt(9999, CheckForOutOfRangeAccess)));
+            }
+        }
+
+        [Fact]
+        public static void MakeSureNoChecksForCountGoOutOfRange_RosTInt()
+        {
+            const int GuardValue = 77777;
+            const int GuardLength = 50;
+
+            void CheckForOutOfRangeAccess(int x, int y) =>
+                Assert.True(x != GuardValue && y != GuardValue, $"{x} or {y} == {GuardValue}");
+
+            for (int length = 0; length < 100; length++)
+            {
+                TInt[] a = new TInt[GuardLength + length + GuardLength];
+                for (int i = 0; i < a.Length; i++)
+                {
+                    a[i] = new TInt(GuardValue, CheckForOutOfRangeAccess);
+                }
+
+                for (int i = 0; i < length; i++)
+                {
+                    a[GuardLength + i] = new TInt(10 * (i + 1), CheckForOutOfRangeAccess);
+                }
+
+                ReadOnlySpan<TInt> span = new ReadOnlySpan<TInt>(a, GuardLength, length);
+                Assert.Equal(0, span.Count(new TInt[] { new TInt(9999, CheckForOutOfRangeAccess), new TInt(9999, CheckForOutOfRangeAccess) }));
+            }
+        }
+
+        [Fact]
+        public static void ZeroLengthCount_String()
+        {
+            Assert.Equal(0, ReadOnlySpan<string>.Empty.Count("a"));
+        }
+        
+        [Fact]
+        public static void ZeroLengthCount_RosString()
+        {
+            Assert.Equal(0, ReadOnlySpan<string>.Empty.Count(new[] { "a", "b" }));
+        }
+
+        [Fact]
+        public static void TestMatchCount_String()
+        {
+            for (int length = 0; length < 32; length++)
+            {
+                string[] a = new string[length];
+                for (int i = 0; i < length; i++)
+                {
+                    a[i] = (10 * (i + 1)).ToString();
+                }
+                ReadOnlySpan<string> span = new ReadOnlySpan<string>(a);
+
+                for (int targetIndex = 0; targetIndex < length; targetIndex++)
+                {
+                    Assert.Equal(1, span.Count(a[targetIndex]));
+                }
+            }
+        }
+
+        [Fact]
+        public static void TestMatchCount_RosString()
+        {
+            for (int length = 0; length < 32; length++)
+            {
+                string[] a = new string[length];
+                for (int i = 0; i < length; i++)
+                {
+                    a[i] = (10 * (i + 1)).ToString();
+                }
+                ReadOnlySpan<string> span = new ReadOnlySpan<string>(a);
+
+                for (int targetIndex = 0; targetIndex < length - 1; targetIndex++)
+                {
+                    Assert.Equal(1, span.Count(new string[] { a[targetIndex], a[targetIndex + 1] }));
+                }
+            }
+        }
+
+        [Fact]
+        public static void TestNoMatchCount_String()
+        {
+            var rnd = new Random(42);
+            for (int length = 0; length <= byte.MaxValue; length++)
+            {
+                string[] a = new string[length];
+                string target = rnd.Next(0, 256).ToString();
+                for (int i = 0; i < length; i++)
+                {
+                    string val = (i + 1).ToString();
+                    a[i] = val == target ? (target + 1) : val;
+                }
+
+                ReadOnlySpan<string> span = new ReadOnlySpan<string>(a);
+                Assert.Equal(0, span.Count(target));
+            }
+        }
+        
+        [Fact]
+        public static void TestNoMatchCount_RosString()
+        {
+            var rnd = new Random(42);
+            for (int length = 0; length <= byte.MaxValue; length++)
+            {
+                string[] a = new string[length];
+                ReadOnlySpan<string> target = new string[] { rnd.Next(0, 256).ToString(), "0" };
+                for (int i = 0; i < length; i++)
+                {
+                    string val = (i + 1).ToString();
+                    a[i] = val == target[0] ? (target[0] + 1) : val;
+                }
+
+                ReadOnlySpan<string> span = new ReadOnlySpan<string>(a);
+                Assert.Equal(0, span.Count(target));
+            }
+        }
+
+        [Fact]
+        public static void TestMultipleMatchCount_String()
+        {
+            for (int length = 2; length < 32; length++)
+            {
+                string[] a = new string[length];
+                for (int i = 0; i < length; i++)
+                {
+                    a[i] = (10 * (i + 1)).ToString();
+                }
+
+                a[^1] = a[^2] = "5555";
+
+                ReadOnlySpan<string> span = new ReadOnlySpan<string>(a);
+                Assert.Equal(2, span.Count("5555"));
+            }
+        }
+
+        [Fact]
+        public static void TestMultipleMatchCount_RosString()
+        {
+            for (int length = 4; length < 32; length++)
+            {
+                string[] a = new string[length];
+                for (int i = 0; i < length; i++)
+                {
+                    a[i] = (10 * (i + 1)).ToString();
+                }
+                
+                a[0] = a[1] = a[^1] = a[^2] = "5555";
+
+                ReadOnlySpan<string> span = new ReadOnlySpan<string>(a);
+                Assert.Equal(2, span.Count(new string[] { "5555", "5555" }));
+            }
+        }
+
+        [Fact]
+        public static void TestOrdinalStringCount_String()
+        {
+            ReadOnlySpan<string> span = new string[] { "ii", "II", "ìì", "ii" };
+            Assert.Equal(2, span.Count("ii"));
+        }
+
+        [Fact]
+        public static void TestOrdinalStringCount_RosString()
+        {
+            ReadOnlySpan<string> span = new string[] { "ii", "II", "ìì", "ii", "ìì" };
+            Assert.Equal(1, span.Count(new string[] { "ii", "II" }));
+        }
+        
+        [Fact]
+        public static void TestOverlapDoNotCount_RosChar()
+        {
+            ReadOnlySpan<char> span = new string('a', 10);
+            Assert.Equal(5, span.Count("aa"));
+        }
+
+        [Theory]
+        [MemberData(nameof(TestHelpers.CountNullData), MemberType = typeof(TestHelpers))]
+        public static void CountNull_String(string[] spanInput, int expected)
+        {
+            ReadOnlySpan<string> theStrings = spanInput;
+            Assert.Equal(expected, theStrings.Count((string)null));
+        }
+
+        [Theory]
+        [MemberData(nameof(TestHelpers.CountNullRosData), MemberType = typeof(TestHelpers))]
+        public static void CountNull_RosString(string[] spanInput, int expected)
+        {
+            ReadOnlySpan<string> theStrings = spanInput;
+            ReadOnlySpan<string> target = new string[] { null, "9" };
+            Assert.Equal(expected, theStrings.Count(target));
+        }
+    }
+}
diff --git a/src/libraries/System.Memory/tests/ReadOnlySpan/Count.byte.cs b/src/libraries/System.Memory/tests/ReadOnlySpan/Count.byte.cs
new file mode 100644 (file)
index 0000000..642a24c
--- /dev/null
@@ -0,0 +1,271 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Linq;
+using System.Numerics;
+using Xunit;
+
+namespace System.SpanTests
+{
+    public static partial class ReadOnlySpanTests
+    {
+        [Fact]
+        public static void ZeroLengthCount_Byte()
+        {
+            Assert.Equal(0, ReadOnlySpan<byte>.Empty.Count<byte>(0));
+        }
+        
+        [Fact]
+        public static void ZeroLengthCount_RosByte()
+        {
+            for (int i = 0; i <= 2; i++)
+            {
+                Assert.Equal(0, ReadOnlySpan<byte>.Empty.Count(new byte[i]));
+            }
+        }
+        
+        [Fact]
+        public static void ZeroLengthNeedleCount_RosByte()
+        {
+            var span = new ReadOnlySpan<byte>(new byte[] { 5, 5, 5, 5, 5 });
+
+            Assert.Equal(0, span.Count<byte>(ReadOnlySpan<byte>.Empty));
+        }
+        
+        [Fact]
+        public static void DefaultFilledCount_Byte()
+        {
+            foreach (int length in new int[] { 0, 1, 7, 8, 9, 15, 16, 17, 31, 32, 33, 255, 256 })
+            {
+                var span = new ReadOnlySpan<byte>(new byte[length]);
+                Assert.Equal(length, span.Count((byte)0));
+            }
+        }
+        
+        [Fact]
+        public static void DefaultFilledCount_RosByte()
+        {
+            foreach (int length in new int[] { 0, 1, 7, 8, 9, 15, 16, 17, 31, 32, 33, 255, 256 })
+            {
+                var span = new ReadOnlySpan<byte>(new byte[length]);
+                Assert.Equal(length / 2,  span.Count(new byte[2]));
+            }
+        }
+        
+        [Fact]
+        public static void TestCount_Byte()
+        {
+            foreach (int length in new int[] { 0, 1, 7, 8, 9, 15, 16, 17, 31, 32, 33, 255, 256 })
+            {
+                var span = new ReadOnlySpan<byte>(Enumerable.Range(1, length).Select(i => (byte)i).ToArray());
+
+                foreach (byte target in span)
+                {
+                    Assert.Equal(1, span.Count(target));
+                }
+            }
+        }
+        
+        [Fact]
+        public static void TestCount_RosByte()
+        {
+            foreach (int length in new int[] { 0, 1, 7, 8, 9, 15, 16, 17, 31, 32, 33, 255, 256 })
+            {
+                var span = new ReadOnlySpan<byte>(Enumerable.Range(1, length).Select(i => (byte)i).ToArray());
+
+                for (int targetIndex = 0; targetIndex < length - 1; targetIndex++)
+                {
+                    Assert.Equal(1, span.Count(new byte[] { span[targetIndex], span[targetIndex + 1] }));
+                }
+            }
+        }
+        
+        [Fact]
+        public static void TestSingleValueCount_Byte()
+        {
+            foreach (int length in new int[] { 0, 1, 7, 8, 9, 15, 16, 17, 31, 32, 33, 255, 256 })
+            {
+                var span = new ReadOnlySpan<byte>(Enumerable.Range(1, length).Select(i => (byte)i).ToArray());
+
+                foreach (byte value in span)
+                {
+                    Assert.Equal(1, span.Count(new byte[] { value }));
+                }
+            }
+        }
+
+        [Fact]
+        public static void TestNotCount_Byte()
+        {
+            var rnd = new Random(42);
+            int[] lengths = new int[] { 0, 1, 7, 8, 9, 15, 16, 17, 31, 32, 33, 255, 256 };
+            foreach (int length in lengths)
+            {
+                byte[] a = new byte[length];
+                byte target = (byte)rnd.Next(0, 256);
+                for (int i = 0; i < length; i++)
+                {
+                    byte val = (byte)(i + 1);
+                    a[i] = val == target ? (byte)(target + 1) : val;
+                }
+
+                var span = new ReadOnlySpan<byte>(a);
+                Assert.Equal(0, span.Count(target));
+            }
+        }
+
+        [Fact]
+        public static void TestNotCount_RosByte()
+        {
+            var rnd = new Random(42);
+            int[] lengths = new int[] { 0, 1, 7, 8, 9, 15, 16, 17, 31, 32, 33, 255, 256 };
+            foreach (int length in lengths)
+            {
+                byte[] a = new byte[length];
+                byte targetVal = (byte)rnd.Next(0, 256);
+                for (int i = 0; i < length; i++)
+                {
+                    byte val = (byte)(i + 1);
+                    a[i] = val == targetVal ? (byte)(targetVal + 1) : val;
+                }
+
+                var span = new ReadOnlySpan<byte>(a);
+                Assert.Equal(0, span.Count(new byte[] { targetVal, 0 }));
+            }
+        }
+
+        [Fact]
+        public static void TestAlignmentNotCount_Byte()
+        {
+            byte[] array = new byte[4 * Vector<byte>.Count];
+            for (var i = 0; i < Vector<byte>.Count; i++)
+            {
+                var span = new ReadOnlySpan<byte>(array, i, 3 * Vector<byte>.Count);
+                Assert.Equal(0, span.Count((byte)'1'));
+
+                span = new ReadOnlySpan<byte>(array, i, 3 * Vector<byte>.Count - 3);
+                Assert.Equal(0, span.Count((byte)'1'));
+            }
+        }
+
+        [Fact]
+        public static void TestAlignmentNotCount_RosByte()
+        {
+            byte[] array = new byte[4 * Vector<byte>.Count];
+            for (var i = 0; i < Vector<byte>.Count; i++)
+            {
+                var span = new ReadOnlySpan<byte>(array, i, 3 * Vector<byte>.Count);
+                ReadOnlySpan<byte> target = new byte[] { 1, 0 };
+                Assert.Equal(0, span.Count(target));
+
+                span = new ReadOnlySpan<byte>(array, i, 3 * Vector<byte>.Count - 3);
+                Assert.Equal(0, span.Count(target));
+            }
+        }
+
+        [Fact]
+        public static void TestAlignmentCount_Byte()
+        {
+            byte[] array = new byte[4 * Vector<byte>.Count];
+            Array.Fill(array, (byte)5);
+            for (var i = 0; i < Vector<byte>.Count; i++)
+            {
+                var span = new ReadOnlySpan<byte>(array, i, 3 * Vector<byte>.Count);
+                Assert.Equal(span.Length, span.Count<byte>(5));
+
+                span = new ReadOnlySpan<byte>(array, i, 3 * Vector<byte>.Count - 3);
+                Assert.Equal(span.Length, span.Count<byte>(5));
+            }
+        }
+        
+        [Fact]
+        public static void TestAlignmentCount_RosByte()
+        {
+            byte[] array = new byte[4 * Vector<byte>.Count];
+            Array.Fill(array, (byte)5);
+            for (var i = 0; i < Vector<byte>.Count; i++)
+            {
+                var span = new ReadOnlySpan<byte>(array, i, 3 * Vector<byte>.Count);
+                ReadOnlySpan<byte> target = new byte[] { 5, 5 };
+                Assert.Equal(span.Length / 2, span.Count<byte>(target));
+
+                span = new ReadOnlySpan<byte>(array, i, 3 * Vector<byte>.Count - 3);
+                Assert.Equal(span.Length / 2, span.Count<byte>(target));
+            }
+        }
+
+        [Fact]
+        public static void TestMultipleCount_Byte()
+        {
+            for (int length = 2; length <= byte.MaxValue; length++)
+            {
+                byte[] a = new byte[length];
+                for (int i = 0; i < length; i++)
+                {
+                    byte val = (byte)(i + 1);
+                    a[i] = val == 200 ? (byte)201 : val;
+                }
+
+                a[^1] = a[^2] = 200;
+
+                var span = new ReadOnlySpan<byte>(a);
+                Assert.Equal(2, span.Count<byte>(200));
+            }
+        }
+        
+        [Fact]
+        public static void TestMultipleCount_RosByte()
+        {
+            for (int length = 4; length <= byte.MaxValue; length++)
+            {
+                byte[] a = new byte[length];
+                for (int i = 0; i < length; i++)
+                {
+                    byte val = (byte)(i + 1);
+                    a[i] = val == 200 ? (byte)201 : val;
+                }
+                a[0] = a[1] = a[^1] = a[^2] = 200;
+
+                var span = new ReadOnlySpan<byte>(a);
+                Assert.Equal(2, span.Count<byte>(new byte[] { 200, 200 }));
+            }
+        }
+
+        [Fact]
+        public static void MakeSureNoCountChecksGoOutOfRange_Byte()
+        {
+            for (int length = 0; length <= byte.MaxValue; length++)
+            {
+                byte[] a = new byte[length + 2];
+                a[0] = a[^1] = 99;
+
+                var span = new ReadOnlySpan<byte>(a, 1, length);
+                Assert.Equal(0, span.Count<byte>(99));
+            }
+        }
+
+        [Fact]
+        public static void MakeSureNoCountChecksGoOutOfRange_RosByte()
+        {
+            for (int length = 0; length <= byte.MaxValue; length++)
+            {
+                byte[] a = new byte[length + 4];
+                a[0] = a[1] = a[^1] = a[^2] = 99;
+
+                var span = new ReadOnlySpan<byte>(a, 2, length);
+                Assert.Equal(0, span.Count<byte>(new byte[] { 99, 99 }));
+            }
+        }
+        
+        [Fact]
+        public static void TestOverlapDoNotCount_RosByte()
+        {
+            byte[] a = new byte[10];
+            Array.Fill<byte>(a, 6);
+
+
+            var span = new ReadOnlySpan<byte>(a);
+            Assert.Equal(5, span.Count(new byte[] { 6, 6 }));
+        }
+    }
+}
index 5179cd1..e35c06d 100644 (file)
     <Compile Include="ReadOnlySpan\AsSpan.cs" />
     <Compile Include="ReadOnlySpan\BinarySearch.cs" />
     <Compile Include="ReadOnlySpan\CopyTo.cs" />
+    <Compile Include="ReadOnlySpan\Count.byte.cs" />
+    <Compile Include="ReadOnlySpan\Count.T.cs" />
     <Compile Include="ReadOnlySpan\CtorArray.cs" />
     <Compile Include="ReadOnlySpan\CtorArrayIntInt.cs" />
     <Compile Include="ReadOnlySpan\CtorPointerInt.cs" />
index f209d92..95dbbe9 100644 (file)
@@ -403,7 +403,7 @@ namespace System
         /// <summary>Creates a <see cref="ReadOnlyMemory{T}"/> with the specified values in its backing field.</summary>
         public static ReadOnlyMemory<T> DangerousCreateReadOnlyMemory<T>(object obj, int offset, int length) =>
             DangerousCreateMemory<T>(obj, offset, length);
-
+        
         public static TheoryData<string[], bool> ContainsNullData => new TheoryData<string[], bool>()
         {
             { new string[] { "1", null, "2" }, true},
@@ -412,6 +412,24 @@ namespace System
             { new string[] { "1", null, null }, true},
             { new string[] { null, null, null }, true},
         };
+        
+        public static TheoryData<string[], int> CountNullData => new TheoryData<string[], int>()
+        {
+            { new string[] { "1", null, "2" }, 1},
+            { new string[] { "1", "3", "2" }, 0},
+            { null, 0},
+            { new string[] { "1", null, null }, 2},
+            { new string[] { null, null, null }, 3},
+        };
+
+        public static TheoryData<string[], int> CountNullRosData => new TheoryData<string[], int>()
+        {
+            { new string[] { "1", null, "9", "2" }, 1},
+            { new string[] { "1", "3", "9", "2" }, 0},
+            { null, 0},
+            { new string[] { "1", null, "9", null, "9"}, 2},
+            { new string[] { null, null, "9", null, "9", "9", null, "9"}, 3},
+        };
 
         public static TheoryData<string[], string[],  bool> SequenceEqualsNullData => new TheoryData<string[], string[], bool>()
         {
index 1c148d5..4baf212 100644 (file)
@@ -3564,6 +3564,97 @@ namespace System
             return (startInclusive, endExclusive);
         }
 
+        /// <summary>Counts the number of times the specified <paramref name="value"/> occurs in the <paramref name="span"/>.</summary>
+        /// <typeparam name="T">The element type of the span.</typeparam>
+        /// <param name="span">The span to search.</param>
+        /// <param name="value">The value for which to search.</param>
+        /// <returns>The number of times <paramref name="value"/> was found in the <paramref name="span"/>.</returns>
+        public static int Count<T>(this Span<T> span, T value) where T : IEquatable<T>? =>
+            Count((ReadOnlySpan<T>)span, value);
+
+        /// <summary>Counts the number of times the specified <paramref name="value"/> occurs in the <paramref name="span"/>.</summary>
+        /// <typeparam name="T">The element type of the span.</typeparam>
+        /// <param name="span">The span to search.</param>
+        /// <param name="value">The value for which to search.</param>
+        /// <returns>The number of times <paramref name="value"/> was found in the <paramref name="span"/>.</returns>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static int Count<T>(this ReadOnlySpan<T> span, T value) where T : IEquatable<T>?
+        {
+            if (RuntimeHelpers.IsBitwiseEquatable<T>())
+            {
+                if (Unsafe.SizeOf<T>() == sizeof(byte))
+                {
+                    return SpanHelpers.CountValueType(
+                        ref Unsafe.As<T, byte>(ref MemoryMarshal.GetReference(span)),
+                        Unsafe.As<T, byte>(ref value),
+                        span.Length);
+                }
+                else if (Unsafe.SizeOf<T>() == sizeof(short))
+                {
+                    return SpanHelpers.CountValueType(
+                        ref Unsafe.As<T, short>(ref MemoryMarshal.GetReference(span)),
+                        Unsafe.As<T, short>(ref value),
+                        span.Length);
+                }
+                else if (Unsafe.SizeOf<T>() == sizeof(int))
+                {
+                    return SpanHelpers.CountValueType(
+                        ref Unsafe.As<T, int>(ref MemoryMarshal.GetReference(span)),
+                        Unsafe.As<T, int>(ref value),
+                        span.Length);
+                }
+                else if (Unsafe.SizeOf<T>() == sizeof(long))
+                {
+                    return SpanHelpers.CountValueType(
+                        ref Unsafe.As<T, long>(ref MemoryMarshal.GetReference(span)),
+                        Unsafe.As<T, long>(ref value),
+                        span.Length);
+                }
+            }
+
+            return SpanHelpers.Count(
+                ref MemoryMarshal.GetReference(span),
+                value,
+                span.Length);
+        }
+
+        /// <summary>Counts the number of times the specified <paramref name="value"/> occurs in the <paramref name="span"/>.</summary>
+        /// <typeparam name="T">The element type of the span.</typeparam>
+        /// <param name="span">The span to search.</param>
+        /// <param name="value">The value for which to search.</param>
+        /// <returns>The number of times <paramref name="value"/> was found in the <paramref name="span"/>.</returns>
+        public static int Count<T>(this Span<T> span, ReadOnlySpan<T> value) where T : IEquatable<T>? =>
+            Count((ReadOnlySpan<T>)span, value);
+
+        /// <summary>Counts the number of times the specified <paramref name="value"/> occurs in the <paramref name="span"/>.</summary>
+        /// <typeparam name="T">The element type of the span.</typeparam>
+        /// <param name="span">The span to search.</param>
+        /// <param name="value">The value for which to search.</param>
+        /// <returns>The number of times <paramref name="value"/> was found in the <paramref name="span"/>.</returns>
+        public static int Count<T>(this ReadOnlySpan<T> span, ReadOnlySpan<T> value) where T : IEquatable<T>?
+        {
+            switch (value.Length)
+            {
+                case 0:
+                    return 0;
+
+                case 1:
+                    return Count(span, value[0]);
+
+                default:
+                    int count = 0;
+
+                    int pos;
+                    while ((pos = span.IndexOf(value)) >= 0)
+                    {
+                        span = span.Slice(pos + value.Length);
+                        count++;
+                    }
+
+                    return count;
+            }
+        }
+
         /// <summary>Writes the specified interpolated string to the character span.</summary>
         /// <param name="destination">The span to which the interpolated string should be formatted.</param>
         /// <param name="handler">The interpolated string.</param>
index e45c796..11c79ef 100644 (file)
@@ -5,6 +5,7 @@ using System.Collections.Generic;
 using System.Diagnostics;
 using System.Numerics;
 using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
 using System.Runtime.Intrinsics;
 
 #pragma warning disable IDE0060 // https://github.com/dotnet/roslyn-analyzers/issues/6228
@@ -3296,5 +3297,88 @@ namespace System
 
             return -1;
         }
+
+        public static int Count<T>(ref T current, T value, int length) where T : IEquatable<T>?
+        {
+            int count = 0;
+
+            ref T end = ref Unsafe.Add(ref current, length);
+            if (value is not null)
+            {
+                while (Unsafe.IsAddressLessThan(ref current, ref end))
+                {
+                    if (value.Equals(current))
+                    {
+                        count++;
+                    }
+
+                    current = ref Unsafe.Add(ref current, 1);
+                }
+            }
+            else
+            {
+                while (Unsafe.IsAddressLessThan(ref current, ref end))
+                {
+                    if (current is null)
+                    {
+                        count++;
+                    }
+
+                    current = ref Unsafe.Add(ref current, 1);
+                }
+            }
+
+            return count;
+        }
+
+        public static int CountValueType<T>(ref T current, T value, int length) where T : struct, IEquatable<T>?
+        {
+            int count = 0;
+
+            ref T end = ref Unsafe.Add(ref current, length);
+            if (Vector128.IsHardwareAccelerated && length >= Vector128<T>.Count)
+            {
+                if (Vector256.IsHardwareAccelerated && length >= Vector256<T>.Count)
+                {
+                    Vector256<T> targetVector = Vector256.Create(value);
+                    ref T oneVectorAwayFromEndMinus1 = ref Unsafe.Subtract(ref end, Vector256<T>.Count - 1);
+                    do
+                    {
+                        count += BitOperations.PopCount(Vector256.Equals(Vector256.LoadUnsafe(ref current), targetVector).ExtractMostSignificantBits());
+                        current = ref Unsafe.Add(ref current, Vector256<T>.Count);
+                    }
+                    while (Unsafe.IsAddressLessThan(ref current, ref oneVectorAwayFromEndMinus1));
+
+                    if (Unsafe.IsAddressLessThan(ref current, ref Unsafe.Subtract(ref end, Vector128<T>.Count - 1)))
+                    {
+                        count += BitOperations.PopCount(Vector128.Equals(Vector128.LoadUnsafe(ref current), Vector128.Create(value)).ExtractMostSignificantBits());
+                        current = ref Unsafe.Add(ref current, Vector128<T>.Count);
+                    }
+                }
+                else
+                {
+                    Vector128<T> targetVector = Vector128.Create(value);
+                    ref T oneVectorAwayFromEndMinus1 = ref Unsafe.Subtract(ref end, Vector128<T>.Count - 1);
+                    do
+                    {
+                        count += BitOperations.PopCount(Vector128.Equals(Vector128.LoadUnsafe(ref current), targetVector).ExtractMostSignificantBits());
+                        current = ref Unsafe.Add(ref current, Vector128<T>.Count);
+                    }
+                    while (Unsafe.IsAddressLessThan(ref current, ref oneVectorAwayFromEndMinus1));
+                }
+            }
+
+            while (Unsafe.IsAddressLessThan(ref current, ref end))
+            {
+                if (current.Equals(value))
+                {
+                    count++;
+                }
+
+                current = ref Unsafe.Add(ref current, 1);
+            }
+
+            return count;
+        }
     }
 }