From 56790235de27adc35bca609ecfb6546382160f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20Cant=C3=BA?= Date: Mon, 22 Apr 2024 21:58:44 -0500 Subject: [PATCH] Reapply params ReadOnlySpan overloads without params keyword (#101308) * Reapply "Add params ReadOnlySpan overloads (#100898)" (#101123) This reverts commit 3e569f5696ddf6c93e56eeaaf1703bb64e561f1b. * Comment-out params keyword * Remove /*params*/ from ref --- .../Common/tests/Tests/System/StringTests.cs | 201 +++++++++++++--- .../Collections/Immutable/ImmutableArray.cs | 2 +- .../Immutable/ImmutableArray_1.Builder.cs | 4 +- .../Collections/Immutable/ImmutableArray_1.cs | 4 +- .../Collections/Immutable/ImmutableHashSet.cs | 4 +- .../Collections/Immutable/ImmutableList.cs | 2 +- .../Collections/Immutable/ImmutableQueue.cs | 2 +- .../Immutable/ImmutableSortedSet.cs | 4 +- .../Collections/Immutable/ImmutableStack.cs | 2 +- .../tests/ImmutableHashSetTest.cs | 4 +- .../tests/ImmutableListTest.cs | 2 +- .../tests/ImmutableQueueTest.cs | 2 +- .../tests/ImmutableSortedSetTest.cs | 4 +- .../tests/ImmutableStackTest.cs | 2 +- .../src/System.Collections.NonGeneric.csproj | 1 + .../src/System.Collections.Specialized.csproj | 1 + ...stem.ComponentModel.EventBasedAsync.csproj | 1 + .../System.ComponentModel.Primitives.csproj | 1 + .../System.Console/ref/System.Console.cs | 2 + .../System.Console/src/System/Console.cs | 22 ++ .../System.Console/tests/ReadAndWrite.cs | 30 ++- ...em.Diagnostics.DiagnosticSourceActivity.cs | 8 +- .../src/System/Diagnostics/Metrics/Counter.cs | 2 +- .../System/Diagnostics/Metrics/Histogram.cs | 2 +- .../System/Diagnostics/Metrics/Measurement.cs | 2 +- .../src/System/Diagnostics/Metrics/TagList.cs | 2 +- .../Diagnostics/Metrics/UpDownCounter.cs | 2 +- .../tests/MetricsTests.cs | 113 +++++++-- .../tests/TagListTests.cs | 9 +- .../System.IO.FileSystem.AccessControl.csproj | 2 +- .../src/System.IO.IsolatedStorage.csproj | 1 + .../src/System.IO.Pipes.AccessControl.csproj | 1 + .../src/System.Linq.Expressions.csproj | 1 + .../src/System.Linq.Parallel.csproj | 1 + .../src/System.Linq.Queryable.csproj | 1 + .../src/System.Net.Http.Json.csproj | 2 + .../src/System.ObjectModel.csproj | 1 + .../CodeDom/Compiler/IndentedTextWriter.cs | 23 ++ .../Generic/CollectionExtensions.cs | 4 +- .../src/System/Delegate.cs | 28 ++- .../src/System/IO/Path.cs | 20 +- .../src/System/IO/StreamWriter.cs | 34 +++ .../IO/TextWriter.CreateBroadcasting.cs | 16 ++ .../src/System/IO/TextWriter.cs | 28 +++ .../src/System/MemoryExtensions.cs | 2 +- .../src/System/String.Manipulation.cs | 217 ++++++++++++++++-- .../src/System/Text/StringBuilder.cs | 155 ++++++++++--- .../Threading/CancellationTokenSource.cs | 12 +- .../src/System/Threading/Tasks/Task.cs | 152 +++++++++--- .../System.Reflection.DispatchProxy.csproj | 1 + .../src/System.Resources.Writer.csproj | 1 + ...em.Runtime.Serialization.Primitives.csproj | 1 + .../System.Runtime/ref/System.Runtime.cs | 34 +++ ...namic.context.method.regmethod.regclass.cs | 2 + ...amic.overloadResolution.Operators.basic.cs | 4 + .../System.IO.Tests/IndentedTextWriter.cs | 27 ++- .../Stream/Stream.NullTests.cs | 6 +- .../StreamWriter/StreamWriter.cs | 16 +- .../TextWriter/TextWriterTests.cs | 41 +++- .../System/IO/PathTests_Combine.cs | 5 + .../System/IO/PathTests_Join.cs | 6 + .../System/MulticastDelegateTests.cs | 30 ++- .../System/String.SplitTests.cs | 2 + .../System/Text/CompositeFormatTests.cs | 39 ++-- .../System/Text/StringBuilderTests.cs | 56 ++++- .../CancellationTokenTests.cs | 12 +- .../MethodCoverage.cs | 62 +++++ .../Task/TaskRtTests_Core.cs | 5 - .../src/System.Security.Claims.csproj | 1 + .../System.Text.Json/ref/System.Text.Json.cs | 3 + .../src/System/Text/Json/Nodes/JsonArray.cs | 44 +++- .../Metadata/JsonTypeInfoResolver.cs | 19 ++ .../JsonNode/JsonArrayTests.cs | 21 +- .../JsonTypeInfoResolverTests.cs | 72 +++--- .../src/System.Threading.Channels.csproj | 1 + .../System.Threading.Tasks.Dataflow.csproj | 1 + .../System.Threading.Tasks.Parallel.csproj | 1 + 77 files changed, 1388 insertions(+), 263 deletions(-) diff --git a/src/libraries/Common/tests/Tests/System/StringTests.cs b/src/libraries/Common/tests/Tests/System/StringTests.cs index f21dbe3baad6a6..8e465475a5200e 100644 --- a/src/libraries/Common/tests/Tests/System/StringTests.cs +++ b/src/libraries/Common/tests/Tests/System/StringTests.cs @@ -314,6 +314,7 @@ void Validate(string result) } Validate(string.Concat(values)); + Validate(string.Concat((ReadOnlySpan)values)); Validate(string.Concat((IEnumerable)values)); Validate(string.Concat((IEnumerable)values)); // Call the generic IEnumerable-based overload } @@ -418,6 +419,7 @@ public static void Concat_Objects(object[] values, string expected) Assert.Equal(expected, string.Concat(values[0], values[1], values[2], values[3])); } Assert.Equal(expected, string.Concat(values)); + Assert.Equal(expected, string.Concat((ReadOnlySpan)values)); Assert.Equal(expected, string.Concat((IEnumerable)values)); } @@ -2730,9 +2732,11 @@ public static IEnumerable Format_Valid_TestData() public static void Format_Valid(IFormatProvider provider, string format, object[] values, string expected) { Assert.Equal(expected, string.Format(provider, format, values)); + Assert.Equal(expected, string.Format(provider, format, (ReadOnlySpan)values)); if (provider is null) { Assert.Equal(expected, string.Format(format, values)); + Assert.Equal(expected, string.Format(format, (ReadOnlySpan)values)); } switch (values.Length) @@ -3967,6 +3971,7 @@ public static void Join_StringArray(string separator, string[] values, int start if (startIndex + count == values.Length && count != 0) { Assert.Equal(expected, string.Join(separator, values)); + Assert.Equal(expected, string.Join(separator, (ReadOnlySpan)values)); var iEnumerableStringOptimized = new List(values); Assert.Equal(expected, string.Join(separator, iEnumerableStringOptimized)); @@ -3984,6 +3989,44 @@ public static void Join_StringArray(string separator, string[] values, int start { var arrayOfObjects = (object[])values; Assert.Equal(expected, string.Join(separator, arrayOfObjects)); + Assert.Equal(expected, string.Join(separator, (ReadOnlySpan)arrayOfObjects)); + } + } + Assert.Equal(expected, string.Join(separator, values, startIndex, count)); + } + + [Theory] + [InlineData('$', new string[] { }, 0, 0, "")] + [InlineData('$', new string[] { null }, 0, 1, "")] + [InlineData('$', new string[] { null, "Bar", null }, 0, 3, "$Bar$")] + [InlineData('$', new string[] { "", "", "" }, 0, 3, "$$")] + [InlineData('$', new string[] { "Foo", "Bar", "Baz" }, 0, 3, "Foo$Bar$Baz")] + [InlineData('$', new string[] { "Foo", "Bar", "Baz" }, 3, 0, "")] + [InlineData('$', new string[] { "Foo", "Bar", "Baz" }, 1, 1, "Bar")] + public static void Join_CharSeparator_StringArray(char separator, string[] values, int startIndex, int count, string expected) + { + if (startIndex + count == values.Length && count != 0) + { + Assert.Equal(expected, string.Join(separator, values)); + Assert.Equal(expected, string.Join(separator, (ReadOnlySpan)values)); + + var iEnumerableStringOptimized = new List(values); + Assert.Equal(expected, string.Join(separator, iEnumerableStringOptimized)); + Assert.Equal(expected, string.Join(separator, iEnumerableStringOptimized)); // Call the generic IEnumerable-based overload + + var iEnumerableStringNotOptimized = new Queue(values); + Assert.Equal(expected, string.Join(separator, iEnumerableStringNotOptimized)); + Assert.Equal(expected, string.Join(separator, iEnumerableStringNotOptimized)); + + var iEnumerableObject = new List(values); + Assert.Equal(expected, string.Join(separator, iEnumerableObject)); + + // Bug/Documented behavior: Join(string, object[]) returns "" when the first item in the array is null + if (values.Length == 0 || values[0] != null) + { + var arrayOfObjects = (object[])values; + Assert.Equal(expected, string.Join(separator, arrayOfObjects)); + Assert.Equal(expected, string.Join(separator, (ReadOnlySpan)arrayOfObjects)); } } Assert.Equal(expected, string.Join(separator, values, startIndex, count)); @@ -4038,6 +4081,31 @@ public static void Join_ObjectArray(string separator, object[] values, string ex { Assert.Equal(expected, string.Join(separator, values)); Assert.Equal(expected, string.Join(separator, (IEnumerable)values)); + Assert.Equal(expected, string.Join(separator, (ReadOnlySpan)values)); + } + + public static IEnumerable Join_CharSeparator_ObjectArray_TestData() + { + yield return new object[] { '$', new object[] { }, "" }; + yield return new object[] { '$', new object[] { new ObjectWithNullToString() }, "" }; + yield return new object[] { '$', new object[] { "Foo" }, "Foo" }; + yield return new object[] { '$', new object[] { "Foo", "Bar", "Baz" }, "Foo$Bar$Baz" }; + yield return new object[] { '$', new object[] { "Foo", null, "Baz" }, "Foo$$Baz" }; + + // Test join when first value is null + yield return new object[] { '$', new object[] { null, "Bar", "Baz" }, "$Bar$Baz" }; + + // Join should ignore objects that have a null ToString() value + yield return new object[] { "|", new object[] { new ObjectWithNullToString(), "Foo", new ObjectWithNullToString(), "Bar", new ObjectWithNullToString() }, "|Foo||Bar|" }; + } + + [Theory] + [MemberData(nameof(Join_CharSeparator_ObjectArray_TestData))] + public static void Join_CharSeparator_ObjectArray(char separator, object[] values, string expected) + { + Assert.Equal(expected, string.Join(separator, values)); + Assert.Equal(expected, string.Join(separator, (IEnumerable)values)); + Assert.Equal(expected, string.Join(separator, (ReadOnlySpan)values)); } [Fact] @@ -6098,6 +6166,7 @@ public static void Trim(string s, char[] trimChars, string expected) } Assert.Equal(expected, s.Trim(trimChars)); + Assert.Equal(expected, s.Trim((ReadOnlySpan)trimChars)); Assert.Equal(expected, s.AsSpan().Trim(trimChars).ToString()); } @@ -6128,6 +6197,7 @@ public static void TrimEnd(string s, char[] trimChars, string expected) } Assert.Equal(expected, s.TrimEnd(trimChars)); + Assert.Equal(expected, s.TrimEnd((ReadOnlySpan)trimChars)); Assert.Equal(expected, s.AsSpan().TrimEnd(trimChars).ToString()); } @@ -6158,6 +6228,7 @@ public static void TrimStart(string s, char[] trimChars, string expected) } Assert.Equal(expected, s.TrimStart(trimChars)); + Assert.Equal(expected, s.TrimStart((ReadOnlySpan)trimChars)); Assert.Equal(expected, s.AsSpan().TrimStart(trimChars).ToString()); } @@ -6388,18 +6459,27 @@ public static void ZeroLengthTrimCharacters() Assert.True(s1.SequenceEqual(s1.Trim(trimCharsString))); Assert.True(s1.SequenceEqual(s1.TrimStart(trimCharsString))); Assert.True(s1.SequenceEqual(s1.TrimEnd(trimCharsString))); + Assert.True(s1.SequenceEqual(s1.Trim((ReadOnlySpan)trimCharsString))); + Assert.True(s1.SequenceEqual(s1.TrimStart((ReadOnlySpan)trimCharsString))); + Assert.True(s1.SequenceEqual(s1.TrimEnd((ReadOnlySpan)trimCharsString))); char[] chars = { 'a', 'b', 'c', 'd', 'e' }; trimCharsString = chars; Assert.True(s1.SequenceEqual(s1.Trim(trimCharsString))); Assert.True(s1.SequenceEqual(s1.TrimStart(trimCharsString))); Assert.True(s1.SequenceEqual(s1.TrimEnd(trimCharsString))); + Assert.True(s1.SequenceEqual(s1.Trim((ReadOnlySpan)trimCharsString))); + Assert.True(s1.SequenceEqual(s1.TrimStart((ReadOnlySpan)trimCharsString))); + Assert.True(s1.SequenceEqual(s1.TrimEnd((ReadOnlySpan)trimCharsString))); string emptyString = string.Empty; char[] trimCharsArrayFromString = "abcde".ToCharArray(); Assert.True(emptyString.SequenceEqual(emptyString.Trim(trimCharsArrayFromString))); Assert.True(emptyString.SequenceEqual(emptyString.TrimStart(trimCharsArrayFromString))); Assert.True(emptyString.SequenceEqual(emptyString.TrimEnd(trimCharsArrayFromString))); + Assert.True(emptyString.SequenceEqual(emptyString.Trim((ReadOnlySpan)trimCharsArrayFromString))); + Assert.True(emptyString.SequenceEqual(emptyString.TrimStart((ReadOnlySpan)trimCharsArrayFromString))); + Assert.True(emptyString.SequenceEqual(emptyString.TrimEnd((ReadOnlySpan)trimCharsArrayFromString))); ReadOnlySpan span = s1.AsSpan(); ReadOnlySpan trimChars = trimCharsString.AsSpan(); @@ -6435,6 +6515,9 @@ public static void NoTrimCharacters() Assert.True(s1.SequenceEqual(s1.Trim(trimCharsString))); Assert.True(s1.SequenceEqual(s1.TrimStart(trimCharsString))); Assert.True(s1.SequenceEqual(s1.TrimEnd(trimCharsString))); + Assert.True(s1.SequenceEqual(s1.Trim((ReadOnlySpan)trimCharsString))); + Assert.True(s1.SequenceEqual(s1.TrimStart((ReadOnlySpan)trimCharsString))); + Assert.True(s1.SequenceEqual(s1.TrimEnd((ReadOnlySpan)trimCharsString))); ReadOnlySpan span = s1.AsSpan(); Assert.True(span.SequenceEqual(span.Trim(trimChars))); @@ -6454,6 +6537,9 @@ public static void NoTrimCharacters() Assert.True(s2.SequenceEqual(s2.Trim(chars))); Assert.True(s2.SequenceEqual(s2.TrimStart(chars))); Assert.True(s2.SequenceEqual(s2.TrimEnd(chars))); + Assert.True(s2.SequenceEqual(s2.Trim((ReadOnlySpan)chars))); + Assert.True(s2.SequenceEqual(s2.TrimStart((ReadOnlySpan)chars))); + Assert.True(s2.SequenceEqual(s2.TrimEnd((ReadOnlySpan)chars))); ReadOnlySpan span = s2.AsSpan(); Assert.True(span.SequenceEqual(span.Trim(chars))); @@ -6466,6 +6552,9 @@ public static void NoTrimCharacters() Assert.True(s3.SequenceEqual(s3.Trim(trimCharsFromString))); Assert.True(s3.SequenceEqual(s3.TrimStart(trimCharsFromString))); Assert.True(s3.SequenceEqual(s3.TrimEnd(trimCharsFromString))); + Assert.True(s3.SequenceEqual(s3.Trim((ReadOnlySpan)trimCharsFromString))); + Assert.True(s3.SequenceEqual(s3.TrimStart((ReadOnlySpan)trimCharsFromString))); + Assert.True(s3.SequenceEqual(s3.TrimEnd((ReadOnlySpan)trimCharsFromString))); ReadOnlySpan stringSpan = s3.AsSpan(); ReadOnlySpan trimCharsFromStringSpan = trimCharsFromString.AsSpan(); @@ -6490,6 +6579,9 @@ public static void OnlyTrimCharacters() Assert.True(string.Empty.SequenceEqual(s1.Trim(chars)), "G: " + length); Assert.True(string.Empty.SequenceEqual(s1.TrimStart(chars)), "H: " + length); Assert.True(string.Empty.SequenceEqual(s1.TrimEnd(chars)), "I: " + length); + Assert.True(string.Empty.SequenceEqual(s1.Trim((ReadOnlySpan)chars)), "G: " + length); + Assert.True(string.Empty.SequenceEqual(s1.TrimStart((ReadOnlySpan)chars)), "H: " + length); + Assert.True(string.Empty.SequenceEqual(s1.TrimEnd((ReadOnlySpan)chars)), "I: " + length); ReadOnlySpan span = s1.AsSpan(); Assert.True(ReadOnlySpan.Empty.SequenceEqual(span.Trim(chars)), "G: " + length); @@ -6502,6 +6594,9 @@ public static void OnlyTrimCharacters() Assert.True(string.Empty.SequenceEqual(s2.Trim(trimCharsString)), "J"); Assert.True(string.Empty.SequenceEqual(s2.TrimStart(trimCharsString)), "K"); Assert.True(string.Empty.SequenceEqual(s2.TrimEnd(trimCharsString)), "L"); + Assert.True(string.Empty.SequenceEqual(s2.Trim((ReadOnlySpan)trimCharsString)), "J"); + Assert.True(string.Empty.SequenceEqual(s2.TrimStart((ReadOnlySpan)trimCharsString)), "K"); + Assert.True(string.Empty.SequenceEqual(s2.TrimEnd((ReadOnlySpan)trimCharsString)), "L"); ReadOnlySpan stringSpan = s2.AsSpan(); ReadOnlySpan trimChars = trimCharsString.AsSpan(); @@ -6526,6 +6621,9 @@ public static void TrimCharactersAtStart() Assert.True(s1.Substring(1).SequenceEqual(s1.Trim(chars)), "A: " + length); Assert.True(s1.Substring(1).SequenceEqual(s1.TrimStart(chars)), "B: " + length); Assert.True(s1.SequenceEqual(s1.TrimEnd(chars)), "C: " + length); + Assert.True(s1.Substring(1).SequenceEqual(s1.Trim((ReadOnlySpan)chars)), "A: " + length); + Assert.True(s1.Substring(1).SequenceEqual(s1.TrimStart((ReadOnlySpan)chars)), "B: " + length); + Assert.True(s1.SequenceEqual(s1.TrimEnd((ReadOnlySpan)chars)), "C: " + length); ReadOnlySpan span = s1.AsSpan(); Assert.True(span.Slice(1).SequenceEqual(span.Trim(chars)), "A: " + length); @@ -6538,6 +6636,9 @@ public static void TrimCharactersAtStart() Assert.True(s2.Substring(3).SequenceEqual(s2.Trim(trimCharsString)), "D"); Assert.True(s2.Substring(3).SequenceEqual(s2.TrimStart(trimCharsString)), "E"); Assert.True(s2.SequenceEqual(s2.TrimEnd(trimCharsString)), "F"); + Assert.True(s2.Substring(3).SequenceEqual(s2.Trim((ReadOnlySpan)trimCharsString)), "D"); + Assert.True(s2.Substring(3).SequenceEqual(s2.TrimStart((ReadOnlySpan)trimCharsString)), "E"); + Assert.True(s2.SequenceEqual(s2.TrimEnd((ReadOnlySpan)trimCharsString)), "F"); ReadOnlySpan stringSpan = s2.AsSpan(); ReadOnlySpan trimChars = trimCharsString.AsSpan(); @@ -6563,6 +6664,9 @@ public static void TrimCharactersAtEnd() Assert.True(s1.Substring(0, length - 1).SequenceEqual(s1.Trim(chars))); Assert.True(s1.SequenceEqual(s1.TrimStart(chars))); Assert.True(s1.Substring(0, length - 1).SequenceEqual(s1.TrimEnd(chars))); + Assert.True(s1.Substring(0, length - 1).SequenceEqual(s1.Trim((ReadOnlySpan)chars))); + Assert.True(s1.SequenceEqual(s1.TrimStart((ReadOnlySpan)chars))); + Assert.True(s1.Substring(0, length - 1).SequenceEqual(s1.TrimEnd((ReadOnlySpan)chars))); ReadOnlySpan span = new ReadOnlySpan(a); Assert.True(span.Slice(0, length - 1).SequenceEqual(span.Trim(chars))); @@ -6600,6 +6704,9 @@ public static void TrimCharactersAtStartAndEnd() Assert.True(s1.Substring(1, length - 2).SequenceEqual(s1.Trim(chars))); Assert.True(s1.Substring(1).SequenceEqual(s1.TrimStart(chars))); Assert.True(s1.Substring(0, length - 1).SequenceEqual(s1.TrimEnd(chars))); + Assert.True(s1.Substring(1, length - 2).SequenceEqual(s1.Trim((ReadOnlySpan)chars))); + Assert.True(s1.Substring(1).SequenceEqual(s1.TrimStart((ReadOnlySpan)chars))); + Assert.True(s1.Substring(0, length - 1).SequenceEqual(s1.TrimEnd((ReadOnlySpan)chars))); ReadOnlySpan span = s1.AsSpan(); Assert.True(span.Slice(1, length - 2).SequenceEqual(span.Trim(chars))); @@ -6608,13 +6715,15 @@ public static void TrimCharactersAtStartAndEnd() } string s2 = "ccedafffffbdaa"; - char[] trimCharsString = "abcde".ToCharArray(); - Assert.True(s2.Substring(5, 5).SequenceEqual(s2.Trim(trimCharsString))); - Assert.True(s2.Substring(5).SequenceEqual(s2.TrimStart(trimCharsString))); - Assert.True(s2.Substring(0, 10).SequenceEqual(s2.TrimEnd(trimCharsString))); + Assert.True(s2.Substring(5, 5).SequenceEqual(s2.Trim(chars))); + Assert.True(s2.Substring(5).SequenceEqual(s2.TrimStart(chars))); + Assert.True(s2.Substring(0, 10).SequenceEqual(s2.TrimEnd(chars))); + Assert.True(s2.Substring(5, 5).SequenceEqual(s2.Trim((ReadOnlySpan)chars))); + Assert.True(s2.Substring(5).SequenceEqual(s2.TrimStart((ReadOnlySpan)chars))); + Assert.True(s2.Substring(0, 10).SequenceEqual(s2.TrimEnd((ReadOnlySpan)chars))); ReadOnlySpan stringSpan = s2.AsSpan(); - ReadOnlySpan trimChars = trimCharsString.AsSpan(); + ReadOnlySpan trimChars = chars.AsSpan(); Assert.True(stringSpan.Slice(5, 5).SequenceEqual(stringSpan.Trim(trimChars))); Assert.True(stringSpan.Slice(5).SequenceEqual(stringSpan.TrimStart(trimChars))); Assert.True(stringSpan.Slice(0, 10).SequenceEqual(stringSpan.TrimEnd(trimChars))); @@ -6637,6 +6746,9 @@ public static void TrimCharactersInMiddle() Assert.True(s1.SequenceEqual(s1.Trim(chars))); Assert.True(s1.SequenceEqual(s1.TrimStart(chars))); Assert.True(s1.SequenceEqual(s1.TrimEnd(chars))); + Assert.True(s1.SequenceEqual(s1.Trim((ReadOnlySpan)chars))); + Assert.True(s1.SequenceEqual(s1.TrimStart((ReadOnlySpan)chars))); + Assert.True(s1.SequenceEqual(s1.TrimEnd((ReadOnlySpan)chars))); ReadOnlySpan span = s1.AsSpan(); Assert.True(span.SequenceEqual(span.Trim(chars))); @@ -6645,13 +6757,15 @@ public static void TrimCharactersInMiddle() } string s2 = "fabbacddeeddef"; - char[] trimCharsString = "abcde".ToCharArray(); - Assert.True(s2.SequenceEqual(s2.Trim(trimCharsString))); - Assert.True(s2.SequenceEqual(s2.TrimStart(trimCharsString))); - Assert.True(s2.SequenceEqual(s2.TrimEnd(trimCharsString))); + Assert.True(s2.SequenceEqual(s2.Trim(chars))); + Assert.True(s2.SequenceEqual(s2.TrimStart(chars))); + Assert.True(s2.SequenceEqual(s2.TrimEnd(chars))); + Assert.True(s2.SequenceEqual(s2.Trim((ReadOnlySpan)chars))); + Assert.True(s2.SequenceEqual(s2.TrimStart((ReadOnlySpan)chars))); + Assert.True(s2.SequenceEqual(s2.TrimEnd((ReadOnlySpan)chars))); ReadOnlySpan stringSpan = s2.AsSpan(); - ReadOnlySpan trimChars = trimCharsString.AsSpan(); + ReadOnlySpan trimChars = chars.AsSpan(); Assert.True(stringSpan.SequenceEqual(stringSpan.Trim(trimChars))); Assert.True(stringSpan.SequenceEqual(stringSpan.TrimStart(trimChars))); Assert.True(stringSpan.SequenceEqual(stringSpan.TrimEnd(trimChars))); @@ -6684,6 +6798,19 @@ public static void TrimCharactersMultipleTimes() Assert.True(trimStartResultString.SequenceEqual(trimStartResultString.TrimStart(chars))); Assert.True(trimEndResultString.SequenceEqual(trimEndResultString.TrimEnd(chars))); + s1 = new string(a); + trimResultString = s1.Trim((ReadOnlySpan)chars); + trimStartResultString = s1.TrimStart((ReadOnlySpan)chars); + trimEndResultString = s1.TrimEnd((ReadOnlySpan)chars); + Assert.True(s1.Substring(1, length - 2).SequenceEqual(trimResultString)); + Assert.True(s1.Substring(1).SequenceEqual(trimStartResultString)); + Assert.True(s1.Substring(0, length - 1).SequenceEqual(trimEndResultString)); + + // 2nd attempt should do nothing + Assert.True(trimResultString.SequenceEqual(trimResultString.Trim((ReadOnlySpan)chars))); + Assert.True(trimStartResultString.SequenceEqual(trimStartResultString.TrimStart((ReadOnlySpan)chars))); + Assert.True(trimEndResultString.SequenceEqual(trimEndResultString.TrimEnd((ReadOnlySpan)chars))); + ReadOnlySpan span = s1.AsSpan(); ReadOnlySpan trimResult = span.Trim(chars); ReadOnlySpan trimStartResult = span.TrimStart(chars); @@ -6699,21 +6826,33 @@ public static void TrimCharactersMultipleTimes() } string s2 = "ccedafffffbdaa"; - char[] trimCharsString = "abcde".ToCharArray(); - string trimStringResultString = s2.Trim(trimCharsString); - string trimStartStringResultString = s2.TrimStart(trimCharsString); - string trimEndStringResultString = s2.TrimEnd(trimCharsString); + string trimStringResultString = s2.Trim(chars); + string trimStartStringResultString = s2.TrimStart(chars); + string trimEndStringResultString = s2.TrimEnd(chars); + Assert.True(s2.Substring(5, 5).SequenceEqual(trimStringResultString)); + Assert.True(s2.Substring(5).SequenceEqual(trimStartStringResultString)); + Assert.True(s2.Substring(0, 10).SequenceEqual(trimEndStringResultString)); + + // 2nd attempt should do nothing + Assert.True(trimStringResultString.SequenceEqual(trimStringResultString.Trim(chars))); + Assert.True(trimStartStringResultString.SequenceEqual(trimStartStringResultString.TrimStart(chars))); + Assert.True(trimEndStringResultString.SequenceEqual(trimEndStringResultString.TrimEnd(chars))); + + s2 = "ccedafffffbdaa"; + trimStringResultString = s2.Trim((ReadOnlySpan)chars); + trimStartStringResultString = s2.TrimStart((ReadOnlySpan)chars); + trimEndStringResultString = s2.TrimEnd((ReadOnlySpan)chars); Assert.True(s2.Substring(5, 5).SequenceEqual(trimStringResultString)); Assert.True(s2.Substring(5).SequenceEqual(trimStartStringResultString)); Assert.True(s2.Substring(0, 10).SequenceEqual(trimEndStringResultString)); // 2nd attempt should do nothing - Assert.True(trimStringResultString.SequenceEqual(trimStringResultString.Trim(trimCharsString))); - Assert.True(trimStartStringResultString.SequenceEqual(trimStartStringResultString.TrimStart(trimCharsString))); - Assert.True(trimEndStringResultString.SequenceEqual(trimEndStringResultString.TrimEnd(trimCharsString))); + Assert.True(trimStringResultString.SequenceEqual(trimStringResultString.Trim((ReadOnlySpan)chars))); + Assert.True(trimStartStringResultString.SequenceEqual(trimStartStringResultString.TrimStart((ReadOnlySpan)chars))); + Assert.True(trimEndStringResultString.SequenceEqual(trimEndStringResultString.TrimEnd((ReadOnlySpan)chars))); ReadOnlySpan stringSpan = s2.AsSpan(); - ReadOnlySpan trimChars = trimCharsString.AsSpan(); + ReadOnlySpan trimChars = chars.AsSpan(); ReadOnlySpan trimStringResult = stringSpan.Trim(trimChars); ReadOnlySpan trimStartStringResult = stringSpan.TrimStart(trimChars); @@ -6739,27 +6878,31 @@ public static void MakeSureNoTrimCharactersChecksGoOutOfRange() first[length - 1] = 'f'; string s1 = new string(first, 1, length - 2); Assert.Equal(s1.ToArray().Length, s1.Trim(chars).ToArray().Length); - Assert.True(s1.SequenceEqual(s1.Trim(chars)), "A : " + s1.Length); - Assert.True(s1.SequenceEqual(s1.TrimStart(chars)), "B :" + s1.Length); - Assert.True(s1.SequenceEqual(s1.TrimEnd(chars))); + Assert.True(s1.SequenceEqual(s1.Trim(chars)), "A: " + s1.Length); + Assert.True(s1.SequenceEqual(s1.TrimStart(chars)), "B: " + s1.Length); + Assert.True(s1.SequenceEqual(s1.TrimEnd(chars)), "C: " + s1.Length); ReadOnlySpan span = s1.AsSpan(); Assert.Equal(span.ToArray().Length, span.Trim(chars).ToArray().Length); - Assert.True(span.SequenceEqual(span.Trim(chars)), "A : " + span.Length); - Assert.True(span.SequenceEqual(span.TrimStart(chars)), "B :" + span.Length); - Assert.True(span.SequenceEqual(span.TrimEnd(chars))); + Assert.True(span.SequenceEqual(span.Trim(chars)), "A: " + span.Length); + Assert.True(span.SequenceEqual(span.TrimStart(chars)), "B: " + span.Length); + Assert.True(span.SequenceEqual(span.TrimEnd(chars)), "C: " + s1.Length); } string testString = "afghijklmnopqrstfe"; string s2 = testString.Substring(1, testString.Length - 2); - char[] trimCharsString = "abcde".ToCharArray(); - Assert.True(s2.SequenceEqual(s2.Trim(trimCharsString))); - Assert.True(s2.SequenceEqual(s2.TrimStart(trimCharsString))); - Assert.True(s2.SequenceEqual(s2.TrimEnd(trimCharsString))); + Assert.True(s2.SequenceEqual(s2.Trim(chars))); + Assert.True(s2.SequenceEqual(s2.TrimStart(chars))); + Assert.True(s2.SequenceEqual(s2.TrimEnd(chars))); + + s2 = testString.Substring(1, testString.Length - 2); + Assert.True(s2.SequenceEqual(s2.Trim((ReadOnlySpan)chars))); + Assert.True(s2.SequenceEqual(s2.TrimStart((ReadOnlySpan)chars))); + Assert.True(s2.SequenceEqual(s2.TrimEnd((ReadOnlySpan)chars))); ReadOnlySpan stringSpan = s2.AsSpan(); - ReadOnlySpan trimChars = trimCharsString.AsSpan(); + ReadOnlySpan trimChars = chars.AsSpan(); Assert.True(stringSpan.SequenceEqual(stringSpan.Trim(trimChars))); Assert.True(stringSpan.SequenceEqual(stringSpan.TrimStart(trimChars))); Assert.True(stringSpan.SequenceEqual(stringSpan.TrimEnd(trimChars))); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray.cs index f2743d3f16f8c9..aafe4316eb7503 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray.cs @@ -87,7 +87,7 @@ public static ImmutableArray Create(T item1, T item2, T item3, T item4) /// The type of element stored in the array. /// The elements to store in the array. /// An immutable array containing the specified items. - public static ImmutableArray Create(ReadOnlySpan items) + public static ImmutableArray Create(/*params*/ ReadOnlySpan items) { if (items.IsEmpty) { diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs index 8af78d72a063e0..060492a40bd06e 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.Builder.cs @@ -430,7 +430,7 @@ public void AddRange(ImmutableArray items, int length) /// Adds the specified items to the end of the array. /// /// The items to add at the end of the array. - public void AddRange(ReadOnlySpan items) + public void AddRange(/*params*/ ReadOnlySpan items) { int offset = this.Count; this.Count += items.Length; @@ -443,7 +443,7 @@ public void AddRange(ReadOnlySpan items) /// /// The type that derives from the type of item already in the array. /// The items to add at the end of the array. - public void AddRange(ReadOnlySpan items) where TDerived : T + public void AddRange(/*params*/ ReadOnlySpan items) where TDerived : T { int offset = this.Count; this.Count += items.Length; diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.cs index 2d203d015b828a..04e05477e0ff7c 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableArray_1.cs @@ -880,7 +880,7 @@ public IEnumerable OfType() /// /// The values to add. /// A new list with the elements added. - public ImmutableArray AddRange(ReadOnlySpan items) + public ImmutableArray AddRange(/*params*/ ReadOnlySpan items) { ImmutableArray self = this; return self.InsertRange(self.Length, items); @@ -949,7 +949,7 @@ public ImmutableArray InsertRange(int index, T[] items) /// The index at which to insert the value. /// The elements to insert. /// The new immutable collection. - public ImmutableArray InsertRange(int index, ReadOnlySpan items) + public ImmutableArray InsertRange(int index, /*params*/ ReadOnlySpan items) { ImmutableArray self = this; self.ThrowNullRefIfNotInitialized(); diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet.cs index 9630bb33b5c0fe..13742785d7d81e 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableHashSet.cs @@ -98,7 +98,7 @@ public static ImmutableHashSet Create(params T[] items) /// The type of items stored by the collection. /// The items to prepopulate. /// The new immutable collection. - public static ImmutableHashSet Create(ReadOnlySpan items) + public static ImmutableHashSet Create(/*params*/ ReadOnlySpan items) { return ImmutableHashSet.Empty.Union(items); } @@ -124,7 +124,7 @@ public static ImmutableHashSet Create(IEqualityComparer? equalityCompar /// The equality comparer. /// The items to prepopulate. /// The new immutable collection. - public static ImmutableHashSet Create(IEqualityComparer? equalityComparer, ReadOnlySpan items) + public static ImmutableHashSet Create(IEqualityComparer? equalityComparer, /*params*/ ReadOnlySpan items) { return ImmutableHashSet.Empty.WithComparer(equalityComparer).Union(items); } diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList.cs index f3004e3fd0199f..977686b880cc8c 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableList.cs @@ -52,7 +52,7 @@ public static ImmutableList Create(params T[] items) /// The type of items stored by the collection. /// A span that contains the items to prepopulate the list with. /// A new immutable list that contains the specified items. - public static ImmutableList Create(ReadOnlySpan items) => ImmutableList.Empty.AddRange(items); + public static ImmutableList Create(/*params*/ ReadOnlySpan items) => ImmutableList.Empty.AddRange(items); /// /// Creates a new immutable list builder. diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableQueue.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableQueue.cs index 122bae100534f0..d2f5d142bbf869 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableQueue.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableQueue.cs @@ -83,7 +83,7 @@ public static ImmutableQueue Create(params T[] items) /// The type of items in the immutable queue. /// A span that contains the items to prepopulate the queue with. /// A new immutable queue that contains the specified items. - public static ImmutableQueue Create(ReadOnlySpan items) + public static ImmutableQueue Create(/*params*/ ReadOnlySpan items) { if (items.IsEmpty) { diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet.cs index 567997ed0472aa..ae5493ff083213 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableSortedSet.cs @@ -97,7 +97,7 @@ public static ImmutableSortedSet Create(params T[] items) /// The type of items in the immutable set. /// A span that contains the items to prepopulate the set with. /// A new immutable set that contains the specified items. - public static ImmutableSortedSet Create(ReadOnlySpan items) + public static ImmutableSortedSet Create(/*params*/ ReadOnlySpan items) { return ImmutableSortedSet.Empty.Union(items); } @@ -123,7 +123,7 @@ public static ImmutableSortedSet Create(IComparer? comparer, params T[] /// The comparer. /// The items to prepopulate. /// The new immutable collection. - public static ImmutableSortedSet Create(IComparer? comparer, ReadOnlySpan items) + public static ImmutableSortedSet Create(IComparer? comparer, /*params*/ ReadOnlySpan items) { return ImmutableSortedSet.Empty.WithComparer(comparer).Union(items); } diff --git a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableStack.cs b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableStack.cs index 4e11bb76888077..f964f55c79908c 100644 --- a/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableStack.cs +++ b/src/libraries/System.Collections.Immutable/src/System/Collections/Immutable/ImmutableStack.cs @@ -70,7 +70,7 @@ public static ImmutableStack Create(params T[] items) /// The type of items in the immutable stack. /// A span that contains the items to prepopulate the stack with. /// A new immutable stack that contains the specified items. - public static ImmutableStack Create(ReadOnlySpan items) + public static ImmutableStack Create(/*params*/ ReadOnlySpan items) { ImmutableStack stack = ImmutableStack.Empty; foreach (T item in items) diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableHashSetTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableHashSetTest.cs index 0aab9405d55930..d6a01aee099a26 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableHashSetTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableHashSetTest.cs @@ -118,7 +118,7 @@ public void Create() Assert.Equal(1, set.Count); Assert.Same(comparer, set.KeyComparer); - set = ImmutableHashSet.Create("a", "b"); + set = ImmutableHashSet.Create(new[] { "a", "b" }); Assert.Equal(2, set.Count); Assert.Same(EqualityComparer.Default, set.KeyComparer); @@ -126,7 +126,7 @@ public void Create() Assert.Equal(2, set.Count); Assert.Same(EqualityComparer.Default, set.KeyComparer); - set = ImmutableHashSet.Create(comparer, "a", "b"); + set = ImmutableHashSet.Create(comparer, new[] { "a", "b" }); Assert.Equal(2, set.Count); Assert.Same(comparer, set.KeyComparer); diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableListTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableListTest.cs index ec569e77699464..7223c1305e9c62 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableListTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableListTest.cs @@ -584,7 +584,7 @@ public void Create() list = ImmutableList.Create("a"); Assert.Equal(1, list.Count); - list = ImmutableList.Create("a", "b"); + list = ImmutableList.Create(new[] { "a", "b" }); Assert.Equal(2, list.Count); list = ImmutableList.Create((ReadOnlySpan)new[] { "a", "b" }); diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableQueueTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableQueueTest.cs index c03621929a5990..4aef1b5577a345 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableQueueTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableQueueTest.cs @@ -213,7 +213,7 @@ public void Create() Assert.False(queue.IsEmpty); Assert.Equal(new[] { 1 }, queue); - queue = ImmutableQueue.Create(1, 2); + queue = ImmutableQueue.Create(new int[] { 1, 2 }); Assert.False(queue.IsEmpty); Assert.Equal(new[] { 1, 2 }, queue); diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedSetTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedSetTest.cs index eecff21ffb34b7..f16d59dd63ae1b 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableSortedSetTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableSortedSetTest.cs @@ -289,7 +289,7 @@ public void Create() Assert.Equal(1, set.Count); Assert.Same(comparer, set.KeyComparer); - set = ImmutableSortedSet.Create("a", "b"); + set = ImmutableSortedSet.Create(new [] { "a", "b" }); Assert.Equal(2, set.Count); Assert.Same(Comparer.Default, set.KeyComparer); @@ -297,7 +297,7 @@ public void Create() Assert.Equal(2, set.Count); Assert.Same(Comparer.Default, set.KeyComparer); - set = ImmutableSortedSet.Create(comparer, "a", "b"); + set = ImmutableSortedSet.Create(comparer, new[] { "a", "b" }); Assert.Equal(2, set.Count); Assert.Same(comparer, set.KeyComparer); diff --git a/src/libraries/System.Collections.Immutable/tests/ImmutableStackTest.cs b/src/libraries/System.Collections.Immutable/tests/ImmutableStackTest.cs index 26336209e12047..4abd031ed43b2d 100644 --- a/src/libraries/System.Collections.Immutable/tests/ImmutableStackTest.cs +++ b/src/libraries/System.Collections.Immutable/tests/ImmutableStackTest.cs @@ -241,7 +241,7 @@ public void Create() Assert.False(stack.IsEmpty); Assert.Equal(new[] { 1 }, stack); - stack = ImmutableStack.Create(1, 2); + stack = ImmutableStack.Create(new[] { 1, 2 }); Assert.False(stack.IsEmpty); Assert.Equal(new[] { 2, 1 }, stack); diff --git a/src/libraries/System.Collections.NonGeneric/src/System.Collections.NonGeneric.csproj b/src/libraries/System.Collections.NonGeneric/src/System.Collections.NonGeneric.csproj index 770e5fc0fc1816..1bc456cd13ba3a 100644 --- a/src/libraries/System.Collections.NonGeneric/src/System.Collections.NonGeneric.csproj +++ b/src/libraries/System.Collections.NonGeneric/src/System.Collections.NonGeneric.csproj @@ -21,6 +21,7 @@ + diff --git a/src/libraries/System.Collections.Specialized/src/System.Collections.Specialized.csproj b/src/libraries/System.Collections.Specialized/src/System.Collections.Specialized.csproj index 21e6fc760efae5..4301d885411c68 100644 --- a/src/libraries/System.Collections.Specialized/src/System.Collections.Specialized.csproj +++ b/src/libraries/System.Collections.Specialized/src/System.Collections.Specialized.csproj @@ -23,6 +23,7 @@ + diff --git a/src/libraries/System.ComponentModel.EventBasedAsync/src/System.ComponentModel.EventBasedAsync.csproj b/src/libraries/System.ComponentModel.EventBasedAsync/src/System.ComponentModel.EventBasedAsync.csproj index 0d413f6fd370ee..a5d9913733b441 100644 --- a/src/libraries/System.ComponentModel.EventBasedAsync/src/System.ComponentModel.EventBasedAsync.csproj +++ b/src/libraries/System.ComponentModel.EventBasedAsync/src/System.ComponentModel.EventBasedAsync.csproj @@ -18,6 +18,7 @@ + diff --git a/src/libraries/System.ComponentModel.Primitives/src/System.ComponentModel.Primitives.csproj b/src/libraries/System.ComponentModel.Primitives/src/System.ComponentModel.Primitives.csproj index 5fcff901309503..2938d1e641264e 100644 --- a/src/libraries/System.ComponentModel.Primitives/src/System.ComponentModel.Primitives.csproj +++ b/src/libraries/System.ComponentModel.Primitives/src/System.ComponentModel.Primitives.csproj @@ -47,6 +47,7 @@ + diff --git a/src/libraries/System.Console/ref/System.Console.cs b/src/libraries/System.Console/ref/System.Console.cs index 273290de2a89f2..7c5ca19cd01ad6 100644 --- a/src/libraries/System.Console/ref/System.Console.cs +++ b/src/libraries/System.Console/ref/System.Console.cs @@ -175,6 +175,7 @@ public static void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute( public static void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1) { } public static void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1, object? arg2) { } public static void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, params object?[]? arg) { } + public static void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, System.ReadOnlySpan arg) { } [System.CLSCompliantAttribute(false)] public static void Write(uint value) { } [System.CLSCompliantAttribute(false)] @@ -195,6 +196,7 @@ public static void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttrib public static void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1) { } public static void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1, object? arg2) { } public static void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, params object?[]? arg) { } + public static void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, System.ReadOnlySpan arg) { } [System.CLSCompliantAttribute(false)] public static void WriteLine(uint value) { } [System.CLSCompliantAttribute(false)] diff --git a/src/libraries/System.Console/src/System/Console.cs b/src/libraries/System.Console/src/System/Console.cs index 63736b59741706..5e0e387b8299fb 100644 --- a/src/libraries/System.Console/src/System/Console.cs +++ b/src/libraries/System.Console/src/System/Console.cs @@ -865,6 +865,17 @@ public static void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat Out.WriteLine(format, arg); } + /// + /// Writes the text representation of the specified span of objects, followed by the current line terminator, to the standard output stream using the specified format information. + /// + /// A composite format string. + /// A span of objects to write using format. + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan arg) + { + Out.WriteLine(format, arg); + } + [MethodImplAttribute(MethodImplOptions.NoInlining)] public static void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) { @@ -892,6 +903,17 @@ public static void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] s Out.Write(format, arg); } + /// + /// Writes the text representation of the specified span of objects to the standard output stream using the specified format information. + /// + /// A composite format string. + /// A span of objects to write using format. + [MethodImplAttribute(MethodImplOptions.NoInlining)] + public static void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan arg) + { + Out.Write(format, arg); + } + [MethodImplAttribute(MethodImplOptions.NoInlining)] public static void Write(bool value) { diff --git a/src/libraries/System.Console/tests/ReadAndWrite.cs b/src/libraries/System.Console/tests/ReadAndWrite.cs index 5fbf425fc84855..d60fd6aa34cc04 100644 --- a/src/libraries/System.Console/tests/ReadAndWrite.cs +++ b/src/libraries/System.Console/tests/ReadAndWrite.cs @@ -84,10 +84,14 @@ private static void WriteCore() Console.Write("{0}", null, null); Console.Write("{0} {1} {2}", 32, "Hello", (uint)50); Console.Write("{0}", null, null, null); - Console.Write("{0} {1} {2} {3}", 32, "Hello", (uint)50, (ulong)5); - Console.Write("{0}", null, null, null, null); - Console.Write("{0} {1} {2} {3} {4}", 32, "Hello", (uint)50, (ulong)5, 'a'); - Console.Write("{0}", null, null, null, null, null); + Console.Write("{0} {1} {2} {3}", new object[] { 32, "Hello", (uint)50, (ulong)5 }); + Console.Write("{0} {1} {2} {3}", new object[] { 32, "Hello", (uint)50, (ulong)5 }.AsSpan()); + Console.Write("{0}", new object[] { null, null, null, null }); + Console.Write("{0}", new object[] { null, null, null, null }.AsSpan()); + Console.Write("{0} {1} {2} {3} {4}", new object[] { 32, "Hello", (uint)50, (ulong)5, 'a' }); + Console.Write("{0} {1} {2} {3} {4}", new object[] { 32, "Hello", (uint)50, (ulong)5, 'a' }.AsSpan()); + Console.Write("{0}", new object[] { null, null, null, null, null }); + Console.Write("{0}", new object[] { null, null, null, null, null }.AsSpan()); Console.Write(true); Console.Write('a'); Console.Write(new char[] { 'a', 'b', 'c', 'd', }); @@ -120,10 +124,14 @@ private static void WriteLineCore() Console.WriteLine("{0}", null, null); Console.WriteLine("{0} {1} {2}", 32, "Hello", (uint)50); Console.WriteLine("{0}", null, null, null); - Console.WriteLine("{0} {1} {2} {3}", 32, "Hello", (uint)50, (ulong)5); - Console.WriteLine("{0}", null, null, null, null); - Console.WriteLine("{0} {1} {2} {3} {4}", 32, "Hello", (uint)50, (ulong)5, 'a'); - Console.WriteLine("{0}", null, null, null, null, null); + Console.WriteLine("{0} {1} {2} {3}", new object[] { 32, "Hello", (uint)50, (ulong)5 }); + Console.WriteLine("{0} {1} {2} {3}", new object[] { 32, "Hello", (uint)50, (ulong)5 }.AsSpan()); + Console.WriteLine("{0}", new object[] { null, null, null, null }); + Console.WriteLine("{0}", new object[] { null, null, null, null }.AsSpan()); + Console.WriteLine("{0} {1} {2} {3} {4}", new object[] { 32, "Hello", (uint)50, (ulong)5, 'a' }); + Console.WriteLine("{0} {1} {2} {3} {4}", new object[] { 32, "Hello", (uint)50, (ulong)5, 'a' }.AsSpan()); + Console.WriteLine("{0}", new object[] { null, null, null, null, null }); + Console.WriteLine("{0}", new object[] { null, null, null, null, null }.AsSpan()); Console.WriteLine(true); Console.WriteLine('a'); Console.WriteLine(new char[] { 'a', 'b', 'c', 'd', }); @@ -158,8 +166,10 @@ public static async Task OutWriteAndWriteLineOverloads() writer.Write("{0}", 32); writer.Write("{0} {1}", 32, "Hello"); writer.Write("{0} {1} {2}", 32, "Hello", (uint)50); - writer.Write("{0} {1} {2} {3}", 32, "Hello", (uint)50, (ulong)5); - writer.Write("{0} {1} {2} {3} {4}", 32, "Hello", (uint)50, (ulong)5, 'a'); + writer.Write("{0} {1} {2} {3}", new object[] { 32, "Hello", (uint)50, (ulong)5 }); + writer.Write("{0} {1} {2} {3}", new object[] { 32, "Hello", (uint)50, (ulong)5 }.AsSpan()); + writer.Write("{0} {1} {2} {3} {4}", new object[] { 32, "Hello", (uint)50, (ulong)5, 'a' }); + writer.Write("{0} {1} {2} {3} {4}", new object[] { 32, "Hello", (uint)50, (ulong)5, 'a' }.AsSpan()); writer.Write(true); writer.Write('a'); writer.Write(new char[] { 'a', 'b', 'c', 'd', }); diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs index 7cb90d245dcf93..382499070cd1d9 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/ref/System.Diagnostics.DiagnosticSourceActivity.cs @@ -336,7 +336,7 @@ public sealed class Counter : Instrument where T : struct public void Add(T delta, System.Collections.Generic.KeyValuePair tag) { throw null; } public void Add(T delta, System.Collections.Generic.KeyValuePair tag1, System.Collections.Generic.KeyValuePair tag2) { throw null; } public void Add(T delta, System.Collections.Generic.KeyValuePair tag1, System.Collections.Generic.KeyValuePair tag2, System.Collections.Generic.KeyValuePair tag3) { throw null; } - public void Add(T delta, ReadOnlySpan> tags) { throw null; } + public void Add(T delta, System.ReadOnlySpan> tags) { throw null; } public void Add(T delta, params System.Collections.Generic.KeyValuePair[] tags) { throw null; } public void Add(T delta, in TagList tagList) { throw null; } internal Counter(Meter meter, string name, string? unit, string? description) : @@ -348,7 +348,7 @@ public sealed class UpDownCounter : Instrument where T : struct public void Add(T delta, System.Collections.Generic.KeyValuePair tag) { throw null; } public void Add(T delta, System.Collections.Generic.KeyValuePair tag1, System.Collections.Generic.KeyValuePair tag2) { throw null; } public void Add(T delta, System.Collections.Generic.KeyValuePair tag1, System.Collections.Generic.KeyValuePair tag2, System.Collections.Generic.KeyValuePair tag3) { throw null; } - public void Add(T delta, ReadOnlySpan> tags) { throw null; } + public void Add(T delta, System.ReadOnlySpan> tags) { throw null; } public void Add(T delta, params System.Collections.Generic.KeyValuePair[] tags) { throw null; } public void Add(T delta, in TagList tagList) { throw null; } internal UpDownCounter(Meter meter, string name, string? unit, string? description) : @@ -362,7 +362,7 @@ public sealed class Histogram : Instrument where T : struct public void Record(T value, System.Collections.Generic.KeyValuePair tag1, System.Collections.Generic.KeyValuePair tag2) { throw null; } public void Record(T value, System.Collections.Generic.KeyValuePair tag1, System.Collections.Generic.KeyValuePair tag2, System.Collections.Generic.KeyValuePair tag3) { throw null; } public void Record(T value, in TagList tagList) { throw null; } - public void Record(T value, ReadOnlySpan> tags) { throw null; } + public void Record(T value, System.ReadOnlySpan> tags) { throw null; } public void Record(T value, params System.Collections.Generic.KeyValuePair[] tags) { throw null; } } public interface IMeterFactory : System.IDisposable @@ -398,7 +398,7 @@ public abstract class Instrument : Instrument where T : struct public Measurement(T value) { throw null; } public Measurement(T value, System.Collections.Generic.IEnumerable>? tags) { throw null; } public Measurement(T value, params System.Collections.Generic.KeyValuePair[]? tags) { throw null; } - public Measurement(T value, ReadOnlySpan> tags) { throw null; } + public Measurement(T value, System.ReadOnlySpan> tags) { throw null; } public ReadOnlySpan> Tags { get { throw null; } } public T Value { get { throw null; } } } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Counter.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Counter.cs index 1d311d62196391..9f15ff37c70104 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Counter.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Counter.cs @@ -59,7 +59,7 @@ internal Counter(Meter meter, string name, string? unit, string? description, IE /// /// The increment measurement. /// A span of key-value pair tags associated with the measurement. - public void Add(T delta, ReadOnlySpan> tags) => RecordMeasurement(delta, tags); + public void Add(T delta, /*params*/ ReadOnlySpan> tags) => RecordMeasurement(delta, tags); /// /// Record the increment value of the measurement. diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Histogram.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Histogram.cs index ed3431edfce8be..91f5f40da6e80e 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Histogram.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Histogram.cs @@ -59,7 +59,7 @@ internal Histogram(Meter meter, string name, string? unit, string? description, /// /// The measurement value. /// A span of key-value pair tags associated with the measurement. - public void Record(T value, ReadOnlySpan> tags) => RecordMeasurement(value, tags); + public void Record(T value, /*params*/ ReadOnlySpan> tags) => RecordMeasurement(value, tags); /// /// Record a measurement value. diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Measurement.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Measurement.cs index 49db3c86f8dbf1..f678ef66014ff0 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Measurement.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/Measurement.cs @@ -59,7 +59,7 @@ public Measurement(T value, params KeyValuePair[]? tags) /// /// The measurement value. /// The measurement associated tags list. - public Measurement(T value, ReadOnlySpan> tags) + public Measurement(T value, /*params*/ ReadOnlySpan> tags) { _tags = tags.ToArray(); Value = value; diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/TagList.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/TagList.cs index 7646bcc2d33a46..138da8875aeed6 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/TagList.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/TagList.cs @@ -43,7 +43,7 @@ public struct TagList : IList>, IReadOnlyList. /// /// A span of tags to initialize the list with. - public TagList(ReadOnlySpan> tagList) : this() + public TagList(/*params*/ ReadOnlySpan> tagList) : this() { _tagsCount = tagList.Length; switch (_tagsCount) diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/UpDownCounter.cs b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/UpDownCounter.cs index 79d0ed29d8f417..4a093865c5511d 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/UpDownCounter.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/src/System/Diagnostics/Metrics/UpDownCounter.cs @@ -58,7 +58,7 @@ internal UpDownCounter(Meter meter, string name, string? unit, string? descripti /// /// The amount to be added which can be positive, negative or zero. /// A span of key-value pair tags associated with the measurement. - public void Add(T delta, ReadOnlySpan> tags) => RecordMeasurement(delta, tags); + public void Add(T delta, /*params*/ ReadOnlySpan> tags) => RecordMeasurement(delta, tags); /// /// Record the delta value of the measurement. The delta can be positive, negative or zero. diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricsTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricsTests.cs index 4a3cd242bb8be0..b292e55f1bcf20 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricsTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/MetricsTests.cs @@ -4,6 +4,7 @@ using Microsoft.DotNet.RemoteExecutor; using System.Collections.Generic; using System.Diagnostics.Metrics; +using System.Diagnostics.Tests; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -13,6 +14,29 @@ namespace System.Diagnostics.Metrics.Tests { public class MetricsTests { + [Fact] + public void MeasurementConstructionTest() + { + for (int i = 0; i < 30; i++) + { + TagListTests.CreateTagList(i, out TagList tags); + TagListTests.ValidateTags(in tags, i); + KeyValuePair[] tagsArray = tags.ToArray(); + + var measurement = new Measurement(i, tags); + Assert.Equal(i, measurement.Value); + TagListTests.ValidateTags(new TagList(measurement.Tags), tagsArray); + + measurement = new Measurement(i, tagsArray); + Assert.Equal(i, measurement.Value); + TagListTests.ValidateTags(new TagList(measurement.Tags), tagsArray); + + measurement = new Measurement(i, tagsArray.AsSpan()); + Assert.Equal(i, measurement.Value); + TagListTests.ValidateTags(new TagList(measurement.Tags), tagsArray); + } + } + [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] public void MeterConstructionTest() { @@ -238,76 +262,115 @@ public void ThrowingExceptionsFromObservableInstrumentCallbacks() }).Dispose(); } - [ConditionalFact(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] - public void InstrumentMeasurementTest() + [ConditionalTheory(typeof(RemoteExecutor), nameof(RemoteExecutor.IsSupported))] + [InlineData(false)] + [InlineData(true)] + public void InstrumentMeasurementTest(bool useSpan) { - RemoteExecutor.Invoke(() => { + RemoteExecutor.Invoke((string useSpanStr) => { Meter meter = new Meter("InstrumentMeasurementTest"); + bool useSpan = bool.Parse(useSpanStr); Counter counter = meter.CreateCounter("byteCounter"); - InstrumentMeasurementAggregationValidation(counter, (value, tags) => { counter.Add(value, tags); } ); + InstrumentMeasurementAggregationValidation(counter, (value, tags) => { AddToCounter(counter, value, tags, useSpan); } ); UpDownCounter upDownCounter = meter.CreateUpDownCounter("byteUpDownCounter"); - InstrumentMeasurementAggregationValidation(upDownCounter, (value, tags) => { upDownCounter.Add(value, tags); }); + InstrumentMeasurementAggregationValidation(upDownCounter, (value, tags) => { AddToUpDownCounter(upDownCounter, value, tags, useSpan); }); Counter counter1 = meter.CreateCounter("shortCounter"); - InstrumentMeasurementAggregationValidation(counter1, (value, tags) => { counter1.Add(value, tags); } ); + InstrumentMeasurementAggregationValidation(counter1, (value, tags) => { AddToCounter(counter1, value, tags, useSpan); } ); UpDownCounter upDownCounter1 = meter.CreateUpDownCounter("shortUpDownCounter"); - InstrumentMeasurementAggregationValidation(upDownCounter1, (value, tags) => { upDownCounter1.Add(value, tags); }, true); + InstrumentMeasurementAggregationValidation(upDownCounter1, (value, tags) => { AddToUpDownCounter(upDownCounter1, value, tags, useSpan); }, true); Counter counter2 = meter.CreateCounter("intCounter"); - InstrumentMeasurementAggregationValidation(counter2, (value, tags) => { counter2.Add(value, tags); } ); + InstrumentMeasurementAggregationValidation(counter2, (value, tags) => { AddToCounter(counter2, value, tags, useSpan); } ); UpDownCounter upDownCounter2 = meter.CreateUpDownCounter("intUpDownCounter"); - InstrumentMeasurementAggregationValidation(upDownCounter2, (value, tags) => { upDownCounter2.Add(value, tags); }, true); + InstrumentMeasurementAggregationValidation(upDownCounter2, (value, tags) => { AddToUpDownCounter(upDownCounter2, value, tags, useSpan); }, true); Counter counter3 = meter.CreateCounter("longCounter"); - InstrumentMeasurementAggregationValidation(counter3, (value, tags) => { counter3.Add(value, tags); } ); + InstrumentMeasurementAggregationValidation(counter3, (value, tags) => { AddToCounter(counter3, value, tags, useSpan); } ); UpDownCounter upDownCounter3 = meter.CreateUpDownCounter("longUpDownCounter"); - InstrumentMeasurementAggregationValidation(upDownCounter3, (value, tags) => { upDownCounter3.Add(value, tags); }, true); + InstrumentMeasurementAggregationValidation(upDownCounter3, (value, tags) => { AddToUpDownCounter(upDownCounter3, value, tags, useSpan); }, true); Counter counter4 = meter.CreateCounter("floatCounter"); - InstrumentMeasurementAggregationValidation(counter4, (value, tags) => { counter4.Add(value, tags); } ); + InstrumentMeasurementAggregationValidation(counter4, (value, tags) => { AddToCounter(counter4, value, tags, useSpan); } ); UpDownCounter upDownCounter4 = meter.CreateUpDownCounter("floatUpDownCounter"); - InstrumentMeasurementAggregationValidation(upDownCounter4, (value, tags) => { upDownCounter4.Add(value, tags); }, true); + InstrumentMeasurementAggregationValidation(upDownCounter4, (value, tags) => { AddToUpDownCounter(upDownCounter4, value, tags, useSpan); }, true); Counter counter5 = meter.CreateCounter("doubleCounter"); - InstrumentMeasurementAggregationValidation(counter5, (value, tags) => { counter5.Add(value, tags); } ); + InstrumentMeasurementAggregationValidation(counter5, (value, tags) => { AddToCounter(counter5, value, tags, useSpan); } ); UpDownCounter upDownCounter5 = meter.CreateUpDownCounter("doubleUpDownCounter"); - InstrumentMeasurementAggregationValidation(upDownCounter5, (value, tags) => { upDownCounter5.Add(value, tags); }, true); + InstrumentMeasurementAggregationValidation(upDownCounter5, (value, tags) => { AddToUpDownCounter(upDownCounter5, value, tags, useSpan); }, true); Counter counter6 = meter.CreateCounter("decimalCounter"); - InstrumentMeasurementAggregationValidation(counter6, (value, tags) => { counter6.Add(value, tags); } ); + InstrumentMeasurementAggregationValidation(counter6, (value, tags) => { AddToCounter(counter6, value, tags, useSpan); } ); UpDownCounter upDownCounter6 = meter.CreateUpDownCounter("decimalUpDownCounter"); - InstrumentMeasurementAggregationValidation(upDownCounter6, (value, tags) => { upDownCounter6.Add(value, tags); }, true); + InstrumentMeasurementAggregationValidation(upDownCounter6, (value, tags) => { AddToUpDownCounter(upDownCounter6, value, tags, useSpan); }, true); Histogram histogram = meter.CreateHistogram("byteHistogram"); - InstrumentMeasurementAggregationValidation(histogram, (value, tags) => { histogram.Record(value, tags); } ); + InstrumentMeasurementAggregationValidation(histogram, (value, tags) => { Record(histogram, value, tags, useSpan); } ); Histogram histogram1 = meter.CreateHistogram("shortHistogram"); - InstrumentMeasurementAggregationValidation(histogram1, (value, tags) => { histogram1.Record(value, tags); } ); + InstrumentMeasurementAggregationValidation(histogram1, (value, tags) => { Record(histogram1, value, tags, useSpan); } ); Histogram histogram2 = meter.CreateHistogram("intHistogram"); - InstrumentMeasurementAggregationValidation(histogram2, (value, tags) => { histogram2.Record(value, tags); } ); + InstrumentMeasurementAggregationValidation(histogram2, (value, tags) => { Record(histogram2, value, tags, useSpan); } ); Histogram histogram3 = meter.CreateHistogram("longHistogram"); - InstrumentMeasurementAggregationValidation(histogram3, (value, tags) => { histogram3.Record(value, tags); } ); + InstrumentMeasurementAggregationValidation(histogram3, (value, tags) => { Record(histogram3, value, tags, useSpan); } ); Histogram histogram4 = meter.CreateHistogram("floatHistogram"); - InstrumentMeasurementAggregationValidation(histogram4, (value, tags) => { histogram4.Record(value, tags); } ); + InstrumentMeasurementAggregationValidation(histogram4, (value, tags) => { Record(histogram4, value, tags, useSpan); } ); Histogram histogram5 = meter.CreateHistogram("doubleHistogram"); - InstrumentMeasurementAggregationValidation(histogram5, (value, tags) => { histogram5.Record(value, tags); } ); + InstrumentMeasurementAggregationValidation(histogram5, (value, tags) => { Record(histogram5, value, tags, useSpan); } ); Histogram histogram6 = meter.CreateHistogram("decimalHistogram"); - InstrumentMeasurementAggregationValidation(histogram6, (value, tags) => { histogram6.Record(value, tags); } ); + InstrumentMeasurementAggregationValidation(histogram6, (value, tags) => { Record(histogram6, value, tags, useSpan); } ); - }).Dispose(); + }, useSpan.ToString()).Dispose(); + + void AddToCounter(Counter counter, T delta, KeyValuePair[] tags, bool useSpan) where T : struct + { + if (useSpan) + { + counter.Add(delta, (ReadOnlySpan>)tags); + } + else + { + counter.Add(delta, tags); + } + } + + void AddToUpDownCounter(UpDownCounter upDownCounter, T delta, KeyValuePair[] tags, bool useSpan) where T : struct + { + if (useSpan) + { + upDownCounter.Add(delta, (ReadOnlySpan>)tags); + } + else + { + upDownCounter.Add(delta, tags); + } + } + + void Record(Histogram histogram, T value, KeyValuePair[] tags, bool useSpan) where T : struct + { + if (useSpan) + { + histogram.Record(value, (ReadOnlySpan>)tags); + } + else + { + histogram.Record(value, tags); + } + } } diff --git a/src/libraries/System.Diagnostics.DiagnosticSource/tests/TagListTests.cs b/src/libraries/System.Diagnostics.DiagnosticSource/tests/TagListTests.cs index 9f35cb9d47c027..59edc168186a65 100644 --- a/src/libraries/System.Diagnostics.DiagnosticSource/tests/TagListTests.cs +++ b/src/libraries/System.Diagnostics.DiagnosticSource/tests/TagListTests.cs @@ -3,7 +3,6 @@ using Xunit; using System.Collections.Generic; -using System.Diagnostics; namespace System.Diagnostics.Tests { @@ -21,7 +20,7 @@ public void TestConstruction() KeyValuePair[] array = new KeyValuePair[tagList.Count]; tagList.CopyTo(array); TagList list = new TagList(array.AsSpan()); - ValidateTags(in tagList, i); + ValidateTags(in list, i); } } @@ -303,7 +302,7 @@ public void TestNegativeCases() Assert.Throws(() => list.RemoveAt(2)); } - private void ValidateTags(in TagList tagList, KeyValuePair[] array) + internal static void ValidateTags(in TagList tagList, KeyValuePair[] array) { Assert.True(tagList.Count <= array.Length); for (int i = 0; i < tagList.Count; i++) @@ -313,7 +312,7 @@ private void ValidateTags(in TagList tagList, KeyValuePair[] ar } } - private void ValidateTags(in TagList tagList, int tagsCount) + internal static void ValidateTags(in TagList tagList, int tagsCount) { Assert.Equal(tagsCount, tagList.Count); for (int i = 0; i < tagList.Count; i++) @@ -323,7 +322,7 @@ private void ValidateTags(in TagList tagList, int tagsCount) } } - private void CreateTagList(int tagsCount, out TagList tagList) + internal static void CreateTagList(int tagsCount, out TagList tagList) { tagList = new TagList(); for (int i = 0; i < tagsCount; i++) diff --git a/src/libraries/System.IO.FileSystem.AccessControl/src/System.IO.FileSystem.AccessControl.csproj b/src/libraries/System.IO.FileSystem.AccessControl/src/System.IO.FileSystem.AccessControl.csproj index e120f89518cc3f..03cbd816a2ff71 100644 --- a/src/libraries/System.IO.FileSystem.AccessControl/src/System.IO.FileSystem.AccessControl.csproj +++ b/src/libraries/System.IO.FileSystem.AccessControl/src/System.IO.FileSystem.AccessControl.csproj @@ -95,7 +95,7 @@ - + diff --git a/src/libraries/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj b/src/libraries/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj index 3b224099af7611..a11a0d7077cc65 100644 --- a/src/libraries/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj +++ b/src/libraries/System.IO.IsolatedStorage/src/System.IO.IsolatedStorage.csproj @@ -46,6 +46,7 @@ + diff --git a/src/libraries/System.IO.Pipes.AccessControl/src/System.IO.Pipes.AccessControl.csproj b/src/libraries/System.IO.Pipes.AccessControl/src/System.IO.Pipes.AccessControl.csproj index fcd66e7adf9a64..6e48d964b2744e 100644 --- a/src/libraries/System.IO.Pipes.AccessControl/src/System.IO.Pipes.AccessControl.csproj +++ b/src/libraries/System.IO.Pipes.AccessControl/src/System.IO.Pipes.AccessControl.csproj @@ -19,6 +19,7 @@ Condition="'$(TargetPlatformIdentifier)' == 'windows'" /> + diff --git a/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj b/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj index 9213e4c6c944fe..66a070211ddca4 100644 --- a/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj +++ b/src/libraries/System.Linq.Expressions/src/System.Linq.Expressions.csproj @@ -219,6 +219,7 @@ + diff --git a/src/libraries/System.Linq.Parallel/src/System.Linq.Parallel.csproj b/src/libraries/System.Linq.Parallel/src/System.Linq.Parallel.csproj index 3d7f38289ac0aa..15ff9887505a59 100644 --- a/src/libraries/System.Linq.Parallel/src/System.Linq.Parallel.csproj +++ b/src/libraries/System.Linq.Parallel/src/System.Linq.Parallel.csproj @@ -157,6 +157,7 @@ + diff --git a/src/libraries/System.Linq.Queryable/src/System.Linq.Queryable.csproj b/src/libraries/System.Linq.Queryable/src/System.Linq.Queryable.csproj index 8298f938086124..343028ced1ad2a 100644 --- a/src/libraries/System.Linq.Queryable/src/System.Linq.Queryable.csproj +++ b/src/libraries/System.Linq.Queryable/src/System.Linq.Queryable.csproj @@ -19,6 +19,7 @@ + diff --git a/src/libraries/System.Net.Http.Json/src/System.Net.Http.Json.csproj b/src/libraries/System.Net.Http.Json/src/System.Net.Http.Json.csproj index 4f8e58e36abfbb..ef01da51d137d3 100644 --- a/src/libraries/System.Net.Http.Json/src/System.Net.Http.Json.csproj +++ b/src/libraries/System.Net.Http.Json/src/System.Net.Http.Json.csproj @@ -53,6 +53,7 @@ System.Net.Http.Json.JsonContent + @@ -65,6 +66,7 @@ System.Net.Http.Json.JsonContent + diff --git a/src/libraries/System.ObjectModel/src/System.ObjectModel.csproj b/src/libraries/System.ObjectModel/src/System.ObjectModel.csproj index 1a8f23c29c492b..0ef93fda9ecd4b 100644 --- a/src/libraries/System.ObjectModel/src/System.ObjectModel.csproj +++ b/src/libraries/System.ObjectModel/src/System.ObjectModel.csproj @@ -33,6 +33,7 @@ + diff --git a/src/libraries/System.Private.CoreLib/src/System/CodeDom/Compiler/IndentedTextWriter.cs b/src/libraries/System.Private.CoreLib/src/System/CodeDom/Compiler/IndentedTextWriter.cs index 22ee77a8cc6e64..43608b97f78ff1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/CodeDom/Compiler/IndentedTextWriter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/CodeDom/Compiler/IndentedTextWriter.cs @@ -174,6 +174,17 @@ public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] _writer.Write(format, arg); } + /// + /// Writes out a formatted string, using the same semantics as specified. + /// + /// The formatting string to use. + /// The argument span to output. + public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan arg) + { + OutputTabs(); + _writer.Write(format, arg); + } + /// /// Asynchronously writes the specified to the underlying , inserting /// tabs at the start of every line. @@ -352,6 +363,18 @@ public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeForm _tabsPending = true; } + /// + /// Writes out a formatted string, followed by a line terminator, using the same semantics as specified. + /// + /// The formatting string to use. + /// The argument span to output. + public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan arg) + { + OutputTabs(); + _writer.WriteLine(format, arg); + _tabsPending = true; + } + [CLSCompliant(false)] public override void WriteLine(uint value) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/CollectionExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/CollectionExtensions.cs index 068536049a65f9..6c7949eed7bf74 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/CollectionExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Collections/Generic/CollectionExtensions.cs @@ -82,7 +82,7 @@ public static ReadOnlyDictionary AsReadOnly(this IDi /// The list to which the elements should be added. /// The span whose elements should be added to the end of the . /// The is null. - public static void AddRange(this List list, ReadOnlySpan source) + public static void AddRange(this List list, /*params*/ ReadOnlySpan source) { if (list is null) { @@ -109,7 +109,7 @@ public static void AddRange(this List list, ReadOnlySpan source) /// The span whose elements should be added to the . /// The is null. /// is less than 0 or greater than 's . - public static void InsertRange(this List list, int index, ReadOnlySpan source) + public static void InsertRange(this List list, int index, /*params*/ ReadOnlySpan source) { if (list is null) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Delegate.cs b/src/libraries/System.Private.CoreLib/src/System/Delegate.cs index d24059770d5f88..cc8a66e341b529 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Delegate.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Delegate.cs @@ -23,14 +23,30 @@ public abstract partial class Delegate : ICloneable, ISerializable return a.CombineImpl(b); } - public static Delegate? Combine(params Delegate?[]? delegates) + public static Delegate? Combine(params Delegate?[]? delegates) => + Combine((ReadOnlySpan)delegates); + + /// + /// Concatenates the invocation lists of an span of delegates. + /// + /// The span of delegates to combine. + /// + /// A new delegate with an invocation list that concatenates the invocation lists of the delegates in the span. + /// Returns if is , + /// if contains zero elements, or if every entry in is . + /// + public static Delegate? Combine(/*params*/ ReadOnlySpan delegates) { - if (delegates == null || delegates.Length == 0) - return null; + Delegate? d = null; - Delegate? d = delegates[0]; - for (int i = 1; i < delegates.Length; i++) - d = Combine(d, delegates[i]); + if (!delegates.IsEmpty) + { + d = delegates[0]; + for (int i = 1; i < delegates.Length; i++) + { + d = Combine(d, delegates[i]); + } + } return d; } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs index 9d1dced98c8c0b..e6cc976336bbfd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Path.cs @@ -371,7 +371,16 @@ public static string Combine(string path1, string path2, string path3, string pa public static string Combine(params string[] paths) { ArgumentNullException.ThrowIfNull(paths); + return Combine((ReadOnlySpan)paths); + } + /// + /// Combines a span of strings into a path. + /// + /// A span of parts of the path. + /// The combined paths. + public static string Combine(/*params*/ ReadOnlySpan paths) + { int maxSize = 0; int firstComponent = 0; @@ -520,8 +529,17 @@ public static string Join(string? path1, string? path2, string? path3, string? p public static string Join(params string?[] paths) { ArgumentNullException.ThrowIfNull(paths); + return Join((ReadOnlySpan)paths); + } - if (paths.Length == 0) + /// + /// Concatenates a span of paths into a single path. + /// + /// A span of paths. + /// The concatenated path. + public static string Join(/*params*/ ReadOnlySpan paths) + { + if (paths.IsEmpty) { return string.Empty; } diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs index d11c1313ba84d4..947c803d6d2747 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/StreamWriter.cs @@ -568,6 +568,23 @@ public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] } } + /// + /// Writes a formatted string to the stream, using the same semantics as . + /// + /// A composite format string. + /// An object span that contains zero or more objects to format and write. + public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan arg) + { + if (GetType() == typeof(StreamWriter)) + { + WriteFormatHelper(format, arg, appendNewLine: false); + } + else + { + base.Write(format, arg); + } + } + public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) { if (GetType() == typeof(StreamWriter)) @@ -619,6 +636,23 @@ public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeForm } } + /// + /// Writes out a formatted string and a new line to the stream, using the same semantics as . + /// + /// A composite format string. + /// An object span that contains zero or more objects to format and write. + public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan arg) + { + if (GetType() == typeof(StreamWriter)) + { + WriteFormatHelper(format, arg, appendNewLine: true); + } + else + { + base.WriteLine(format, arg); + } + } + public override Task WriteAsync(char value) { // If we have been inherited into a subclass, the following implementation could be incorrect diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.CreateBroadcasting.cs b/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.CreateBroadcasting.cs index 41fdf7eb56f964..82c9d6edd619d5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.CreateBroadcasting.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.CreateBroadcasting.cs @@ -268,6 +268,14 @@ public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] } } + public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan arg) + { + foreach (TextWriter writer in _writers) + { + writer.Write(format, arg); + } + } + public override void WriteLine() { foreach (TextWriter writer in _writers) @@ -428,6 +436,14 @@ public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeForm } } + public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan arg) + { + foreach (TextWriter writer in _writers) + { + writer.WriteLine(format, arg); + } + } + public override async Task WriteAsync(char value) { foreach (TextWriter writer in _writers) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs b/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs index 289ec5fac7a5e8..27a73516d4be96 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/TextWriter.cs @@ -317,6 +317,16 @@ public virtual void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] Write(string.Format(FormatProvider, format, arg)); } + /// + /// Writes a formatted string to the text stream, using the same semantics as . + /// + /// A composite format string. + /// An object span that contains zero or more objects to format and write. + public virtual void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan arg) + { + Write(string.Format(FormatProvider, format, arg)); + } + // Writes a line terminator to the text stream. The default line terminator // is Environment.NewLine, but this value can be changed by setting the NewLine property. // @@ -514,6 +524,16 @@ public virtual void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeForma WriteLine(string.Format(FormatProvider, format, arg)); } + /// + /// Writes out a formatted string and a new line to the text stream, using the same semantics as . + /// + /// A composite format string. + /// An object span that contains zero or more objects to format and write. + public virtual void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan arg) + { + WriteLine(string.Format(FormatProvider, format, arg)); + } + #region Task based Async APIs public virtual Task WriteAsync(char value) => Task.Factory.StartNew(static state => @@ -700,6 +720,7 @@ public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) { } public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) { } public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params object?[] arg) { } + public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan arg) { } public override Task WriteAsync(char value) => Task.CompletedTask; public override Task WriteAsync(string? value) => Task.CompletedTask; public override Task WriteAsync(StringBuilder? value, CancellationToken cancellationToken = default) => Task.CompletedTask; @@ -725,6 +746,7 @@ public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeForm public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) { } public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) { } public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params object?[] arg) { } + public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan arg) { } public override Task WriteLineAsync(char value) => Task.CompletedTask; public override Task WriteLineAsync(string? value) => Task.CompletedTask; public override Task WriteLineAsync(StringBuilder? value, CancellationToken cancellationToken = default) => Task.CompletedTask; @@ -833,6 +855,9 @@ protected override void Dispose(bool disposing) [MethodImpl(MethodImplOptions.Synchronized)] public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object?[] arg) => _out.Write(format, arg); + [MethodImpl(MethodImplOptions.Synchronized)] + public override void Write([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, ReadOnlySpan arg) => _out.Write(format, arg); + [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine() => _out.WriteLine(); @@ -893,6 +918,9 @@ protected override void Dispose(bool disposing) [MethodImpl(MethodImplOptions.Synchronized)] public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object?[] arg) => _out.WriteLine(format, arg); + [MethodImpl(MethodImplOptions.Synchronized)] + public override void WriteLine([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, ReadOnlySpan arg) => _out.WriteLine(format, arg); + // // On SyncTextWriter all APIs should run synchronously, even the async ones. // diff --git a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs index 50a0d5b19d5acd..d3c9e28cb0ccda 100644 --- a/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs +++ b/src/libraries/System.Private.CoreLib/src/System/MemoryExtensions.cs @@ -4173,7 +4173,7 @@ public static bool TryWrite(this Span destination, IFormatProvider? provid /// if the entire interpolated string could be formatted successfully; otherwise, . /// is null. /// The index of a format item is greater than or equal to the number of supplied arguments. - public static bool TryWrite(this Span destination, IFormatProvider? provider, CompositeFormat format, out int charsWritten, ReadOnlySpan args) + public static bool TryWrite(this Span destination, IFormatProvider? provider, CompositeFormat format, out int charsWritten, /*params*/ ReadOnlySpan args) { ArgumentNullException.ThrowIfNull(format); format.ValidateNumberOfArgs(args.Length); diff --git a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs index 7f4f642526d451..596e743ebe215d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs +++ b/src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs @@ -62,10 +62,19 @@ public static string Concat(object? arg0, object? arg1, object? arg2) => public static string Concat(params object?[] args) { ArgumentNullException.ThrowIfNull(args); + return Concat((ReadOnlySpan)args); + } + /// + /// Concatenates the string representations of the elements in a specified span of objects. + /// + /// A span of objects that contains the elements to concatenate. + /// The concatenated string representations of the values of the elements in . + public static string Concat(/*params*/ ReadOnlySpan args) + { if (args.Length <= 1) { - return args.Length == 0 ? + return args.IsEmpty ? Empty : args[0]?.ToString() ?? Empty; } @@ -355,10 +364,19 @@ internal static string Concat(ReadOnlySpan str0, ReadOnlySpan str1, public static string Concat(params string?[] values) { ArgumentNullException.ThrowIfNull(values); + return Concat((ReadOnlySpan)values); + } + /// + /// Concatenates the elements of a specified span of . + /// + /// A span of instances. + /// The concatenated elements of . + public static string Concat(/*params*/ ReadOnlySpan values) + { if (values.Length <= 1) { - return values.Length == 0 ? + return values.IsEmpty ? Empty : values[0] ?? Empty; } @@ -416,7 +434,7 @@ public static string Concat(params string?[] values) // something changed concurrently to mutate the input array: fall back to // doing the concatenation again, but this time with a defensive copy. This // fall back should be extremely rare. - return copiedLength == totalLength ? result : Concat((string?[])values.Clone()); + return copiedLength == totalLength ? result : Concat((ReadOnlySpan)values.ToArray()); } public static string Format([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) @@ -445,6 +463,17 @@ public static string Format([StringSyntax(StringSyntaxAttribute.CompositeFormat) ArgumentNullException.Throw(format is null ? nameof(format) : nameof(args)); } + return FormatHelper(null, format, (ReadOnlySpan)args); + } + + /// + /// Replaces the format item in a specified string with the string representation of a corresponding object in a specified span. + /// + /// A composite format string. + /// An object span that contains zero or more objects to format. + /// A copy of in which the format items have been replaced by the string representation of the corresponding objects in . + public static string Format([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan args) + { return FormatHelper(null, format, args); } @@ -474,6 +503,19 @@ public static string Format(IFormatProvider? provider, [StringSyntax(StringSynta ArgumentNullException.Throw(format is null ? nameof(format) : nameof(args)); } + return FormatHelper(provider, format, (ReadOnlySpan)args); + } + + /// + /// Replaces the format items in a string with the string representations of corresponding objects in a specified span. + /// A parameter supplies culture-specific formatting information. + /// + /// An object that supplies culture-specific formatting information. + /// A composite format string. + /// An object span that contains zero or more objects to format. + /// A copy of in which the format items have been replaced by the string representation of the corresponding objects in . + public static string Format(IFormatProvider? provider, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan args) + { return FormatHelper(provider, format, args); } @@ -575,7 +617,7 @@ public static string Format(IFormatProvider? provider, CompositeFormat format, p /// The formatted string. /// is null. /// The index of a format item is greater than or equal to the number of supplied arguments. - public static string Format(IFormatProvider? provider, CompositeFormat format, ReadOnlySpan args) + public static string Format(IFormatProvider? provider, CompositeFormat format, /*params*/ ReadOnlySpan args) { ArgumentNullException.ThrowIfNull(format); format.ValidateNumberOfArgs(args.Length); @@ -669,6 +711,21 @@ public static string Join(char separator, params string?[] value) return JoinCore(new ReadOnlySpan(in separator), new ReadOnlySpan(value)); } + /// + /// Concatenates a span of strings, using the specified separator between each member. + /// + /// The character to use as a separator. is included in the returned string only if has more than one element. + /// A span that contains the elements to concatenate. + /// + /// A string that consists of the elements of delimited by the string. + /// -or- + /// if has zero elements. + /// + public static string Join(char separator, /*params*/ ReadOnlySpan value) + { + return JoinCore(new ReadOnlySpan(in separator), value); + } + public static string Join(string? separator, params string?[] value) { if (value == null) @@ -679,6 +736,21 @@ public static string Join(string? separator, params string?[] value) return JoinCore(separator.AsSpan(), new ReadOnlySpan(value)); } + /// + /// Concatenates a span of strings, using the specified separator between each member. + /// + /// The string to use as a separator. is included in the returned string only if has more than one element. + /// A span that contains the elements to concatenate. + /// + /// A string that consists of the elements of delimited by the string. + /// -or- + /// if has zero elements. + /// + public static string Join(string? separator, /*params*/ ReadOnlySpan value) + { + return JoinCore(separator.AsSpan(), value); + } + public static string Join(char separator, string?[] value, int startIndex, int count) => JoinCore(new ReadOnlySpan(in separator), value, startIndex, count); @@ -743,20 +815,55 @@ public static string Join(string? separator, IEnumerable values) } } - public static string Join(char separator, params object?[] values) => - JoinCore(new ReadOnlySpan(in separator), values); + public static string Join(char separator, params object?[] values) + { + if (values == null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } - public static string Join(string? separator, params object?[] values) => - JoinCore(separator.AsSpan(), values); + return JoinCore(new ReadOnlySpan(in separator), (ReadOnlySpan)values); + } - private static string JoinCore(ReadOnlySpan separator, object?[] values) + /// + /// Concatenates the string representations of a span of objects, using the specified separator between each member. + /// + /// The character to use as a separator. is included in the returned string only if value has more than one element. + /// A span of objects whose string representations will be concatenated. + /// + /// A string that consists of the elements of delimited by the character. + /// -or- + /// if has zero elements. + /// + public static string Join(char separator, /*params*/ ReadOnlySpan values) => + JoinCore(new ReadOnlySpan(in separator), values); + + public static string Join(string? separator, params object?[] values) { if (values == null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); } - if (values.Length == 0) + return JoinCore(separator.AsSpan(), (ReadOnlySpan)values); + } + + /// + /// Concatenates the string representations of a span of objects, using the specified separator between each member. + /// + /// The string to use as a separator. is included in the returned string only if has more than one element. + /// A span of objects whose string representations will be concatenated. + /// + /// A string that consists of the elements of delimited by the string. + /// -or- + /// if has zero elements. + /// + public static string Join(string? separator, /*params*/ ReadOnlySpan values) => + JoinCore(separator.AsSpan(), values); + + private static string JoinCore(ReadOnlySpan separator, ReadOnlySpan values) + { + if (values.IsEmpty) { return Empty; } @@ -793,11 +900,16 @@ public static string Join(string? separator, IEnumerable values) => private static string JoinCore(ReadOnlySpan separator, IEnumerable values) { + if (values is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } + if (typeof(T) == typeof(string)) { - if (values is List valuesList) + if (values.GetType() == typeof(List)) // avoid accidentally bypassing a derived type's reimplementation of IEnumerable { - return JoinCore(separator, CollectionsMarshal.AsSpan(valuesList)); + return JoinCore(separator, CollectionsMarshal.AsSpan(Unsafe.As>(values))); } if (values is string?[] valuesArray) @@ -806,11 +918,6 @@ private static string JoinCore(ReadOnlySpan separator, IEnumerable v } } - if (values == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); - } - using (IEnumerator e = values.GetEnumerator()) { if (!e.MoveNext()) @@ -1548,6 +1655,16 @@ public string[] Split(params char[]? separator) return SplitInternal(separator, int.MaxValue, StringSplitOptions.None); } + /// + /// Splits a string into substrings based on specified delimiting characters. + /// + /// A span of delimiting characters, or an empty span that contains no delimiters. + /// An array whose elements contain the substrings from this instance that are delimited by one or more characters in . + public string[] Split(/*params*/ ReadOnlySpan separator) + { + return SplitInternal(separator, int.MaxValue, StringSplitOptions.None); + } + // Creates an array of strings by splitting this string at each // occurrence of a separator. The separator is searched for, and if found, // the substring preceding the occurrence is stored as the first element in @@ -2241,6 +2358,28 @@ public unsafe string Trim(params char[]? trimChars) } } + /// + /// Removes all leading and trailing occurrences of a set of characters specified in a span from the current string. + /// + /// A span of Unicode characters to remove. + /// + /// The string that remains after all occurrences of the characters in the parameter are removed from the start and end of the current string. + /// If is empty, white-space characters are removed instead. + /// If no characters can be trimmed from the current instance, the method returns the current instance unchanged. + /// + public unsafe string Trim(/*params*/ ReadOnlySpan trimChars) + { + if (trimChars.IsEmpty) + { + return TrimWhiteSpaceHelper(TrimType.Both); + } + + fixed (char* pTrimChars = &MemoryMarshal.GetReference(trimChars)) + { + return TrimHelper(pTrimChars, trimChars.Length, TrimType.Both); + } + } + // Removes a set of characters from the beginning of this string. public string TrimStart() => TrimWhiteSpaceHelper(TrimType.Head); @@ -2260,6 +2399,28 @@ public unsafe string TrimStart(params char[]? trimChars) } } + /// + /// Removes all the leading occurrences of a set of characters specified in a span from the current string. + /// + /// A span of Unicode characters to remove. + /// + /// The string that remains after all occurrences of characters in the parameter are removed from the start of the current string. + /// If is empty, white-space characters are removed instead. + /// If no characters can be trimmed from the current instance, the method returns the current instance unchanged. + /// + public unsafe string TrimStart(/*params*/ ReadOnlySpan trimChars) + { + if (trimChars.IsEmpty) + { + return TrimWhiteSpaceHelper(TrimType.Head); + } + + fixed (char* pTrimChars = &MemoryMarshal.GetReference(trimChars)) + { + return TrimHelper(pTrimChars, trimChars.Length, TrimType.Head); + } + } + // Removes a set of characters from the end of this string. public string TrimEnd() => TrimWhiteSpaceHelper(TrimType.Tail); @@ -2279,6 +2440,28 @@ public unsafe string TrimEnd(params char[]? trimChars) } } + /// + /// Removes all the trailing occurrences of a set of characters specified in a span from the current string. + /// + /// A span of Unicode characters to remove. + /// + /// The string that remains after all occurrences of characters in the parameter are removed from the end of the current string. + /// If is empty, white-space characters are removed instead. + /// If no characters can be trimmed from the current instance, the method returns the current instance unchanged. + /// + public unsafe string TrimEnd(/*params*/ ReadOnlySpan trimChars) + { + if (trimChars.IsEmpty) + { + return TrimWhiteSpaceHelper(TrimType.Tail); + } + + fixed (char* pTrimChars = &trimChars[0]) + { + return TrimHelper(pTrimChars, trimChars.Length, TrimType.Tail); + } + } + private string TrimWhiteSpaceHelper(TrimType trimType) { // end will point to the first non-trimmed character on the right. diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs index 6f7b0abfa9b91f..e7bee231dbf45d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs @@ -1123,6 +1123,24 @@ public StringBuilder Append(ReadOnlySpan value) #region AppendJoin public StringBuilder AppendJoin(string? separator, params object?[] values) + { + if (values is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } + + separator ??= string.Empty; + return AppendJoinCore(ref separator.GetRawStringData(), separator.Length, values); + } + + /// + /// Concatenates the string representations of the elements in the provided span of objects, using the specified separator between each member, + /// then appends the result to the current instance of the string builder. + /// + /// The string to use as a separator. is included in the joined strings only if has more than one element. + /// A span that contains the strings to concatenate and append to the current instance of the string builder. + /// A reference to this instance after the append operation has completed. + public StringBuilder AppendJoin(string? separator, /*params*/ ReadOnlySpan values) { separator ??= string.Empty; return AppendJoinCore(ref separator.GetRawStringData(), separator.Length, values); @@ -1130,11 +1148,34 @@ public StringBuilder AppendJoin(string? separator, params object?[] values) public StringBuilder AppendJoin(string? separator, IEnumerable values) { + if (values is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } + separator ??= string.Empty; return AppendJoinCore(ref separator.GetRawStringData(), separator.Length, values); } public StringBuilder AppendJoin(string? separator, params string?[] values) + { + if (values is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } + + separator ??= string.Empty; + return AppendJoinCore(ref separator.GetRawStringData(), separator.Length, values); + } + + /// + /// Concatenates the strings of the provided span, using the specified separator between each string, + /// then appends the result to the current instance of the string builder. + /// + /// The string to use as a separator. is included in the joined strings only if has more than one element. + /// A span that contains the strings to concatenate and append to the current instance of the string builder. + /// A reference to this instance after the append operation has completed. + public StringBuilder AppendJoin(string? separator, /*params*/ ReadOnlySpan values) { separator ??= string.Empty; return AppendJoinCore(ref separator.GetRawStringData(), separator.Length, values); @@ -1142,30 +1183,60 @@ public StringBuilder AppendJoin(string? separator, params string?[] values) public StringBuilder AppendJoin(char separator, params object?[] values) { - return AppendJoinCore(ref separator, 1, values); + if (values is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } + + return AppendJoinCore(ref separator, 1, (ReadOnlySpan)values); } + /// + /// Concatenates the string representations of the elements in the provided span of objects, using the specified char separator between each member, + /// then appends the result to the current instance of the string builder. + /// + /// The character to use as a separator. is included in the joined strings only if has more than one element. + /// A span that contains the strings to concatenate and append to the current instance of the string builder. + /// A reference to this instance after the append operation has completed. + public StringBuilder AppendJoin(char separator, /*params*/ ReadOnlySpan values) => + AppendJoinCore(ref separator, 1, values); + public StringBuilder AppendJoin(char separator, IEnumerable values) { + if (values is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } + return AppendJoinCore(ref separator, 1, values); } public StringBuilder AppendJoin(char separator, params string?[] values) { - return AppendJoinCore(ref separator, 1, values); + if (values is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); + } + + return AppendJoinCore(ref separator, 1, (ReadOnlySpan)values); } + /// + /// Concatenates the strings of the provided span, using the specified char separator between each string, + /// then appends the result to the current instance of the string builder. + /// + /// The character to use as a separator. is included in the joined strings only if has more than one element. + /// A span that contains the strings to concatenate and append to the current instance of the string builder. + /// A reference to this instance after the append operation has completed. + public StringBuilder AppendJoin(char separator, /*params*/ ReadOnlySpan values) => + AppendJoinCore(ref separator, 1, values); + private StringBuilder AppendJoinCore(ref char separator, int separatorLength, IEnumerable values) { + Debug.Assert(values != null); Debug.Assert(!Unsafe.IsNullRef(ref separator)); Debug.Assert(separatorLength >= 0); - if (values == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); - } - - Debug.Assert(values != null); using (IEnumerator en = values.GetEnumerator()) { if (!en.MoveNext()) @@ -1192,15 +1263,9 @@ private StringBuilder AppendJoinCore(ref char separator, int separatorLength, return this; } - private StringBuilder AppendJoinCore(ref char separator, int separatorLength, T[] values) + private StringBuilder AppendJoinCore(ref char separator, int separatorLength, ReadOnlySpan values) { - if (values == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.values); - } - - Debug.Assert(values != null); - if (values.Length == 0) + if (values.IsEmpty) { return this; } @@ -1360,19 +1425,19 @@ private StringBuilder InsertSpanFormattable(int index, T value) where T : ISp public StringBuilder AppendFormat([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) { - return AppendFormatHelper(null, format, new ReadOnlySpan(in arg0)); + return AppendFormat(null, format, new ReadOnlySpan(in arg0)); } public StringBuilder AppendFormat([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) { TwoObjects two = new TwoObjects(arg0, arg1); - return AppendFormatHelper(null, format, two); + return AppendFormat(null, format, (ReadOnlySpan)two); } public StringBuilder AppendFormat([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) { ThreeObjects three = new ThreeObjects(arg0, arg1, arg2); - return AppendFormatHelper(null, format, three); + return AppendFormat(null, format, (ReadOnlySpan)three); } public StringBuilder AppendFormat([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params object?[] args) @@ -1380,28 +1445,47 @@ public StringBuilder AppendFormat([StringSyntax(StringSyntaxAttribute.CompositeF if (args is null) { // To preserve the original exception behavior, throw an exception about format if both - // args and format are null. The actual null check for format is in AppendFormatHelper. + // args and format are null. The actual null check for format is in AppendFormat(..., span). ArgumentNullException.Throw(format is null ? nameof(format) : nameof(args)); } - return AppendFormatHelper(null, format, args); + return AppendFormat(null, format, args); + } + + /// + /// Appends the string returned by processing a composite format string, which contains zero or more format items, to this instance. + /// Each format item is replaced by the string representation of a corresponding argument in a parameter span. + /// + /// A composite format string. + /// A span of objects to format. + /// A reference to this instance after the append operation has completed. + /// is null. + /// The length of the expanded string would exceed . + /// + /// is invalid. + /// -or- + /// The index of a format item is less than 0 (zero), or greater than or equal to the length of the span. + /// + public StringBuilder AppendFormat([StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan args) + { + return AppendFormat(null, format, args); } public StringBuilder AppendFormat(IFormatProvider? provider, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0) { - return AppendFormatHelper(provider, format, new ReadOnlySpan(in arg0)); + return AppendFormat(provider, format, new ReadOnlySpan(in arg0)); } public StringBuilder AppendFormat(IFormatProvider? provider, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1) { TwoObjects two = new TwoObjects(arg0, arg1); - return AppendFormatHelper(provider, format, two); + return AppendFormat(provider, format, (ReadOnlySpan)two); } public StringBuilder AppendFormat(IFormatProvider? provider, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, object? arg0, object? arg1, object? arg2) { ThreeObjects three = new ThreeObjects(arg0, arg1, arg2); - return AppendFormatHelper(provider, format, three); + return AppendFormat(provider, format, (ReadOnlySpan)three); } public StringBuilder AppendFormat(IFormatProvider? provider, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, params object?[] args) @@ -1409,14 +1493,29 @@ public StringBuilder AppendFormat(IFormatProvider? provider, [StringSyntax(Strin if (args is null) { // To preserve the original exception behavior, throw an exception about format if both - // args and format are null. The actual null check for format is in AppendFormatHelper. + // args and format are null. The actual null check for format is in AppendFormat(..., span). ArgumentNullException.Throw(format is null ? nameof(format) : nameof(args)); } - return AppendFormatHelper(provider, format, args); + return AppendFormat(provider, format, (ReadOnlySpan)args); } - internal StringBuilder AppendFormatHelper(IFormatProvider? provider, string format, ReadOnlySpan args) + /// + /// Appends the string returned by processing a composite format string, which contains zero or more format items, to this instance. + /// Each format item is replaced by the string representation of a corresponding argument in a parameter span using a specified format provider. + /// + /// An object that supplies culture-specific formatting information. + /// A composite format string. + /// A span of objects to format. + /// A reference to this instance after the append operation has completed. + /// is null. + /// The length of the expanded string would exceed . + /// + /// is invalid. + /// -or- + /// The index of a format item is less than 0 (zero), or greater than or equal to the length of the span. + /// + public StringBuilder AppendFormat(IFormatProvider? provider, [StringSyntax(StringSyntaxAttribute.CompositeFormat)] string format, /*params*/ ReadOnlySpan args) { ArgumentNullException.ThrowIfNull(format); @@ -1776,7 +1875,7 @@ public StringBuilder AppendFormat(IFormatProvider? provider, CompositeFormat for /// A reference to this instance after the append operation has completed. /// is null. /// The index of a format item is greater than or equal to the number of supplied arguments. - public StringBuilder AppendFormat(IFormatProvider? provider, CompositeFormat format, ReadOnlySpan args) + public StringBuilder AppendFormat(IFormatProvider? provider, CompositeFormat format, /*params*/ ReadOnlySpan args) { ArgumentNullException.ThrowIfNull(format); format.ValidateNumberOfArgs(args.Length); diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs index ace2ed4b943d93..092b9467232157 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/CancellationTokenSource.cs @@ -868,7 +868,17 @@ public static CancellationTokenSource CreateLinkedTokenSource(CancellationToken public static CancellationTokenSource CreateLinkedTokenSource(params CancellationToken[] tokens) { ArgumentNullException.ThrowIfNull(tokens); + return CreateLinkedTokenSource((ReadOnlySpan)tokens); + } + /// + /// Creates a that will be in the canceled state + /// when any of the source tokens are in the canceled state. + /// + /// The CancellationToken instances to observe. + /// A that is linked to the source tokens. + public static CancellationTokenSource CreateLinkedTokenSource(/*params*/ ReadOnlySpan tokens) + { return tokens.Length switch { 0 => throw new ArgumentException(SR.CancellationToken_CreateLinkedToken_TokensIsEmpty), @@ -935,7 +945,7 @@ private sealed class LinkedNCancellationTokenSource : CancellationTokenSource }; private CancellationTokenRegistration[]? _linkingRegistrations; - internal LinkedNCancellationTokenSource(CancellationToken[] tokens) + internal LinkedNCancellationTokenSource(ReadOnlySpan tokens) { _linkingRegistrations = new CancellationTokenRegistration[tokens.Length]; diff --git a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs index fa03b43aff87cc..ab532be3e3da4e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/Task.cs @@ -4690,6 +4690,26 @@ public static void WaitAll(params Task[] tasks) Debug.Assert(waitResult, "expected wait to succeed"); } + /// + /// Waits for all of the provided objects to complete execution. + /// + /// + /// An array of instances on which to wait. + /// + /// + /// The argument contains a null element. + /// + /// + /// At least one of the instances was canceled -or- an exception was thrown during + /// the execution of at least one of the instances. + /// + [UnsupportedOSPlatform("browser")] + public static void WaitAll(/*params*/ ReadOnlySpan tasks) + { + bool waitResult = WaitAllCore(tasks, Timeout.Infinite, default); + Debug.Assert(waitResult, "expected wait to succeed"); + } + /// /// Waits for all of the provided objects to complete execution. /// @@ -4724,7 +4744,7 @@ public static void WaitAll(params Task[] tasks) public static bool WaitAll(Task[] tasks, TimeSpan timeout) { long totalMilliseconds = (long)timeout.TotalMilliseconds; - if (totalMilliseconds < -1 || totalMilliseconds > int.MaxValue) + if (totalMilliseconds is < -1 or > int.MaxValue) { ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.timeout); } @@ -5953,7 +5973,7 @@ public static Task WhenAll(params Task[] tasks) /// /// /// The array contained a null task. - internal static Task WhenAll(ReadOnlySpan tasks) => // TODO https://github.com/dotnet/runtime/issues/77873: Make this public. + public static Task WhenAll(/*params*/ ReadOnlySpan tasks) => tasks.Length != 0 ? new WhenAllPromise(tasks) : CompletedTask; /// A Task that gets completed when all of its constituent tasks complete. @@ -6172,7 +6192,13 @@ public static Task WhenAll(IEnumerable> tasks) // Skip a List allocation/copy if tasks is a collection if (tasks is ICollection> taskCollection) { - taskArray = new Task[taskCollection.Count]; + int count = taskCollection.Count; + if (count == 0) + { + return new Task(false, Array.Empty(), TaskCreationOptions.None, default); + } + + taskArray = new Task[count]; taskCollection.CopyTo(taskArray, 0); foreach (Task task in taskArray) { @@ -6181,20 +6207,30 @@ public static Task WhenAll(IEnumerable> tasks) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks); } } - return InternalWhenAll(taskArray); + + return new WhenAllPromise(taskArray); } // Do some argument checking and convert tasks into a List (later an array) - if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks); + if (tasks is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks); + } + List> taskList = new List>(); foreach (Task task in tasks) { - if (task == null) ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks); + if (task is null) + { + ThrowHelper.ThrowArgumentException(ExceptionResource.Task_MultiTaskContinuation_NullTask, ExceptionArgument.tasks); + } + taskList.Add(task); } - // Delegate the rest to InternalWhenAll(). - return InternalWhenAll(taskList.ToArray()); + return taskList.Count == 0 ? + new Task(false, Array.Empty(), TaskCreationOptions.None, default) : + new WhenAllPromise(taskList.ToArray()); } /// @@ -6229,13 +6265,49 @@ public static Task WhenAll(IEnumerable> tasks) /// public static Task WhenAll(params Task[] tasks) { - // Do some argument checking and make a defensive copy of the tasks array - if (tasks == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks); + if (tasks is null) + { + ThrowHelper.ThrowArgumentNullException(ExceptionArgument.tasks); + } - int taskCount = tasks.Length; - if (taskCount == 0) return InternalWhenAll(tasks); // small optimization in the case of an empty task array + return WhenAll((ReadOnlySpan>)tasks); + } + + /// + /// Creates a task that will complete when all of the supplied tasks have completed. + /// + /// The tasks to wait on for completion. + /// A task that represents the completion of all of the supplied tasks. + /// + /// + /// If any of the supplied tasks completes in a faulted state, the returned task will also complete in a Faulted state, + /// where its exceptions will contain the aggregation of the set of unwrapped exceptions from each of the supplied tasks. + /// + /// + /// If none of the supplied tasks faulted but at least one of them was canceled, the returned task will end in the Canceled state. + /// + /// + /// If none of the tasks faulted and none of the tasks were canceled, the resulting task will end in the RanToCompletion state. + /// The Result of the returned task will be set to an array containing all of the results of the + /// supplied tasks in the same order as they were provided (e.g. if the input tasks array contained t1, t2, t3, the output + /// task's Result will return an TResult[] where arr[0] == t1.Result, arr[1] == t2.Result, and arr[2] == t3.Result). + /// + /// + /// If the supplied array/enumerable contains no tasks, the returned task will immediately transition to a RanToCompletion + /// state before it's returned to the caller. The returned TResult[] will be an array of 0 elements. + /// + /// + /// + /// The array contained a null task. + /// + public static Task WhenAll(/*params*/ ReadOnlySpan> tasks) + { + if (tasks.IsEmpty) + { + return new Task(false, Array.Empty(), TaskCreationOptions.None, default); + } - Task[] tasksCopy = (Task[])tasks.Clone(); + Task[] tasksCopy = tasks.ToArray(); foreach (Task task in tasksCopy) { if (task is null) @@ -6244,17 +6316,7 @@ public static Task WhenAll(params Task[] tasks) } } - // Delegate the rest to InternalWhenAll() - return InternalWhenAll(tasksCopy); - } - - // Some common logic to support WhenAll methods - private static Task InternalWhenAll(Task[] tasks) - { - Debug.Assert(tasks != null, "Expected a non-null tasks array"); - return (tasks.Length == 0) ? // take shortcut if there are no tasks upon which to wait - new Task(false, Array.Empty(), TaskCreationOptions.None, default) : - new WhenAllPromise(tasks); + return new WhenAllPromise(tasksCopy); } // A Task that gets completed when all of its constituent tasks complete. @@ -6394,7 +6456,7 @@ public static Task WhenAny(params Task[] tasks) { ArgumentNullException.ThrowIfNull(tasks); - return WhenAny((ReadOnlySpan)tasks); + return WhenAnyCore((ReadOnlySpan)tasks); } /// @@ -6409,7 +6471,22 @@ public static Task WhenAny(params Task[] tasks) /// /// The array contained a null task, or was empty. /// - private static Task WhenAny(ReadOnlySpan tasks) where TTask : Task + public static Task WhenAny(/*params*/ ReadOnlySpan tasks) => + WhenAnyCore(tasks); + + /// + /// Creates a task that will complete when any of the supplied tasks have completed. + /// + /// The tasks to wait on for completion. + /// A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed. + /// + /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state + /// with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state. + /// + /// + /// The array contained a null task, or was empty. + /// + private static Task WhenAnyCore(ReadOnlySpan tasks) where TTask : Task { if (tasks.Length == 2) { @@ -6589,13 +6666,13 @@ private static Task WhenAny(IEnumerable tasks) where TTask { // Take a more efficient path if tasks is actually a list or an array. Arrays are a bit less common, // since if argument was strongly-typed as an array, it would have bound to the array-based overload. - if (tasks is List tasksAsList) + if (tasks.GetType() == typeof(List)) { - return WhenAny((ReadOnlySpan)CollectionsMarshal.AsSpan(tasksAsList)); + return WhenAnyCore((ReadOnlySpan)CollectionsMarshal.AsSpan(Unsafe.As>(tasks))); } if (tasks is TTask[] tasksAsArray) { - return WhenAny((ReadOnlySpan)tasksAsArray); + return WhenAnyCore((ReadOnlySpan)tasksAsArray); } int count = tasksAsCollection.Count; @@ -6663,9 +6740,24 @@ public static Task> WhenAny(params Task[] tasks) { ArgumentNullException.ThrowIfNull(tasks); - return WhenAny((ReadOnlySpan>)tasks); + return WhenAnyCore((ReadOnlySpan>)tasks); } + /// + /// Creates a task that will complete when any of the supplied tasks have completed. + /// + /// The tasks to wait on for completion. + /// A task that represents the completion of one of the supplied tasks. The return Task's Result is the task that completed. + /// + /// The returned task will complete when any of the supplied tasks has completed. The returned task will always end in the RanToCompletion state + /// with its Result set to the first task to complete. This is true even if the first task to complete ended in the Canceled or Faulted state. + /// + /// + /// The array contained a null task, or was empty. + /// + public static Task> WhenAny(/*params*/ ReadOnlySpan> tasks) => + WhenAnyCore(tasks); + /// Creates a task that will complete when either of the supplied tasks have completed. /// The first task to wait on for completion. /// The second task to wait on for completion. diff --git a/src/libraries/System.Reflection.DispatchProxy/src/System.Reflection.DispatchProxy.csproj b/src/libraries/System.Reflection.DispatchProxy/src/System.Reflection.DispatchProxy.csproj index 2d021999f5869c..17c279ab521382 100644 --- a/src/libraries/System.Reflection.DispatchProxy/src/System.Reflection.DispatchProxy.csproj +++ b/src/libraries/System.Reflection.DispatchProxy/src/System.Reflection.DispatchProxy.csproj @@ -16,6 +16,7 @@ + diff --git a/src/libraries/System.Resources.Writer/src/System.Resources.Writer.csproj b/src/libraries/System.Resources.Writer/src/System.Resources.Writer.csproj index bbe21a704c7f81..b2e3aeb636444c 100644 --- a/src/libraries/System.Resources.Writer/src/System.Resources.Writer.csproj +++ b/src/libraries/System.Resources.Writer/src/System.Resources.Writer.csproj @@ -20,6 +20,7 @@ + diff --git a/src/libraries/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj b/src/libraries/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj index 08ff96e95af3e1..ca2ed33befc50b 100644 --- a/src/libraries/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj +++ b/src/libraries/System.Runtime.Serialization.Primitives/src/System.Runtime.Serialization.Primitives.csproj @@ -22,6 +22,7 @@ + diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 1080983cb9bb2c..14575b820c33ab 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -2163,6 +2163,7 @@ protected Delegate([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAt [return: System.Diagnostics.CodeAnalysis.NotNullIfNotNullAttribute("b")] public static System.Delegate? Combine(System.Delegate? a, System.Delegate? b) { throw null; } public static System.Delegate? Combine(params System.Delegate?[]? delegates) { throw null; } + public static System.Delegate? Combine(System.ReadOnlySpan delegates) { throw null; } protected virtual System.Delegate CombineImpl(System.Delegate? d) { throw null; } public static System.Delegate CreateDelegate(System.Type type, object? firstArgument, System.Reflection.MethodInfo method) { throw null; } public static System.Delegate? CreateDelegate(System.Type type, object? firstArgument, System.Reflection.MethodInfo method, bool throwOnBindFailure) { throw null; } @@ -5242,6 +5243,7 @@ public unsafe String(sbyte* value, int startIndex, int length, System.Text.Encod public static string Concat(object? arg0, object? arg1) { throw null; } public static string Concat(object? arg0, object? arg1, object? arg2) { throw null; } public static string Concat(params object?[] args) { throw null; } + public static string Concat(System.ReadOnlySpan args) { throw null; } public static string Concat(System.ReadOnlySpan str0, System.ReadOnlySpan str1) { throw null; } public static string Concat(System.ReadOnlySpan str0, System.ReadOnlySpan str1, System.ReadOnlySpan str2) { throw null; } public static string Concat(System.ReadOnlySpan str0, System.ReadOnlySpan str1, System.ReadOnlySpan str2, System.ReadOnlySpan str3) { throw null; } @@ -5249,6 +5251,7 @@ public unsafe String(sbyte* value, int startIndex, int length, System.Text.Encod public static string Concat(string? str0, string? str1, string? str2) { throw null; } public static string Concat(string? str0, string? str1, string? str2, string? str3) { throw null; } public static string Concat(params string?[] values) { throw null; } + public static string Concat(System.ReadOnlySpan values) { throw null; } public static string Concat(System.Collections.Generic.IEnumerable values) { throw null; } public bool Contains(char value) { throw null; } public bool Contains(char value, System.StringComparison comparisonType) { throw null; } @@ -5276,12 +5279,14 @@ public void CopyTo(System.Span destination) { } public static string Format(System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1) { throw null; } public static string Format(System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1, object? arg2) { throw null; } public static string Format(System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, params object?[] args) { throw null; } + public static string Format(System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, System.ReadOnlySpan args) { throw null; } public static string Format(System.IFormatProvider? provider, System.Text.CompositeFormat format, params object?[] args) { throw null; } public static string Format(System.IFormatProvider? provider, System.Text.CompositeFormat format, System.ReadOnlySpan args) { throw null; } public static string Format([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0) { throw null; } public static string Format([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1) { throw null; } public static string Format([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1, object? arg2) { throw null; } public static string Format([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, params object?[] args) { throw null; } + public static string Format([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, System.ReadOnlySpan args) { throw null; } public static string Format(System.IFormatProvider? provider, System.Text.CompositeFormat format, TArg0 arg0) { throw null; } public static string Format(System.IFormatProvider? provider, System.Text.CompositeFormat format, TArg0 arg0, TArg1 arg1) { throw null; } public static string Format(System.IFormatProvider? provider, System.Text.CompositeFormat format, TArg0 arg0, TArg1 arg1, TArg2 arg2) { throw null; } @@ -5314,11 +5319,15 @@ public void CopyTo(System.Span destination) { } public static bool IsNullOrEmpty([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(false)] string? value) { throw null; } public static bool IsNullOrWhiteSpace([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(false)] string? value) { throw null; } public static string Join(char separator, params object?[] values) { throw null; } + public static string Join(char separator, System.ReadOnlySpan values) { throw null; } public static string Join(char separator, params string?[] value) { throw null; } + public static string Join(char separator, System.ReadOnlySpan value) { throw null; } public static string Join(char separator, string?[] value, int startIndex, int count) { throw null; } public static string Join(string? separator, System.Collections.Generic.IEnumerable values) { throw null; } public static string Join(string? separator, params object?[] values) { throw null; } + public static string Join(string? separator, System.ReadOnlySpan values) { throw null; } public static string Join(string? separator, params string?[] value) { throw null; } + public static string Join(string? separator, System.ReadOnlySpan value) { throw null; } public static string Join(string? separator, string?[] value, int startIndex, int count) { throw null; } public static string Join(char separator, System.Collections.Generic.IEnumerable values) { throw null; } public static string Join(string? separator, System.Collections.Generic.IEnumerable values) { throw null; } @@ -5354,6 +5363,7 @@ public void CopyTo(System.Span destination) { } public string[] Split(char separator, int count, System.StringSplitOptions options = System.StringSplitOptions.None) { throw null; } public string[] Split(char separator, System.StringSplitOptions options = System.StringSplitOptions.None) { throw null; } public string[] Split(params char[]? separator) { throw null; } + public string[] Split(System.ReadOnlySpan separator) { throw null; } public string[] Split(char[]? separator, int count) { throw null; } public string[] Split(char[]? separator, int count, System.StringSplitOptions options) { throw null; } public string[] Split(char[]? separator, System.StringSplitOptions options) { throw null; } @@ -5401,12 +5411,15 @@ public void CopyTo(System.Span destination) { } public string Trim() { throw null; } public string Trim(char trimChar) { throw null; } public string Trim(params char[]? trimChars) { throw null; } + public string Trim(System.ReadOnlySpan trimChars) { throw null; } public string TrimEnd() { throw null; } public string TrimEnd(char trimChar) { throw null; } public string TrimEnd(params char[]? trimChars) { throw null; } + public string TrimEnd(System.ReadOnlySpan trimChars) { throw null; } public string TrimStart() { throw null; } public string TrimStart(char trimChar) { throw null; } public string TrimStart(params char[]? trimChars) { throw null; } + public string TrimStart(System.ReadOnlySpan trimChars) { throw null; } public bool TryCopyTo(System.Span destination) { throw null; } } public abstract partial class StringComparer : System.Collections.Generic.IComparer, System.Collections.Generic.IEqualityComparer, System.Collections.IComparer, System.Collections.IEqualityComparer @@ -7527,6 +7540,7 @@ public override void Write(string? s) { } public override void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0) { } public override void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1) { } public override void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, params object?[] arg) { } + public override void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, System.ReadOnlySpan arg) { } public override System.Threading.Tasks.Task WriteAsync(char value) { throw null; } public override System.Threading.Tasks.Task WriteAsync(char[] buffer, int index, int count) { throw null; } public override System.Threading.Tasks.Task WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -7546,6 +7560,7 @@ public override void WriteLine(string? s) { } public override void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0) { } public override void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1) { } public override void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, params object?[] arg) { } + public override void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, System.ReadOnlySpan arg) { } [System.CLSCompliantAttribute(false)] public override void WriteLine(uint value) { } public override System.Threading.Tasks.Task WriteLineAsync() { throw null; } @@ -10112,6 +10127,7 @@ public static partial class Path public static string Combine(string path1, string path2, string path3) { throw null; } public static string Combine(string path1, string path2, string path3, string path4) { throw null; } public static string Combine(params string[] paths) { throw null; } + public static string Combine(System.ReadOnlySpan paths) { throw null; } public static bool EndsInDirectorySeparator(System.ReadOnlySpan path) { throw null; } public static bool EndsInDirectorySeparator([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? path) { throw null; } public static bool Exists([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? path) { throw null; } @@ -10149,6 +10165,7 @@ public static partial class Path public static string Join(string? path1, string? path2, string? path3) { throw null; } public static string Join(string? path1, string? path2, string? path3, string? path4) { throw null; } public static string Join(params string?[] paths) { throw null; } + public static string Join(System.ReadOnlySpan paths) { throw null; } public static System.ReadOnlySpan TrimEndingDirectorySeparator(System.ReadOnlySpan path) { throw null; } public static string TrimEndingDirectorySeparator(string path) { throw null; } public static bool TryJoin(System.ReadOnlySpan path1, System.ReadOnlySpan path2, System.ReadOnlySpan path3, System.Span destination, out int charsWritten) { throw null; } @@ -10315,6 +10332,7 @@ public override void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribut public override void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1) { } public override void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1, object? arg2) { } public override void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, params object?[] arg) { } + public override void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, System.ReadOnlySpan arg) { } public override System.Threading.Tasks.Task WriteAsync(char value) { throw null; } public override System.Threading.Tasks.Task WriteAsync(char[] buffer, int index, int count) { throw null; } public override System.Threading.Tasks.Task WriteAsync(System.ReadOnlyMemory buffer, System.Threading.CancellationToken cancellationToken = default(System.Threading.CancellationToken)) { throw null; } @@ -10325,6 +10343,7 @@ public override void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttr public override void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1) { } public override void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1, object? arg2) { } public override void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, params object?[] arg) { } + public override void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, System.ReadOnlySpan arg) { } public override System.Threading.Tasks.Task WriteLineAsync() { throw null; } public override System.Threading.Tasks.Task WriteLineAsync(char value) { throw null; } public override System.Threading.Tasks.Task WriteLineAsync(char[] buffer, int index, int count) { throw null; } @@ -10442,6 +10461,7 @@ public virtual void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute public virtual void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1) { } public virtual void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1, object? arg2) { } public virtual void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, params object?[] arg) { } + public virtual void Write([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, System.ReadOnlySpan arg) { } public virtual void Write(System.Text.StringBuilder? value) { } [System.CLSCompliantAttribute(false)] public virtual void Write(uint value) { } @@ -10470,6 +10490,7 @@ public virtual void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttri public virtual void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1) { } public virtual void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1, object? arg2) { } public virtual void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, params object?[] arg) { } + public virtual void WriteLine([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, System.ReadOnlySpan arg) { } public virtual void WriteLine(System.Text.StringBuilder? value) { } [System.CLSCompliantAttribute(false)] public virtual void WriteLine(uint value) { } @@ -14915,19 +14936,25 @@ public StringBuilder(string? value, int startIndex, int length, int capacity) { public System.Text.StringBuilder AppendFormat(System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1) { throw null; } public System.Text.StringBuilder AppendFormat(System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1, object? arg2) { throw null; } public System.Text.StringBuilder AppendFormat(System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, params object?[] args) { throw null; } + public System.Text.StringBuilder AppendFormat(System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, System.ReadOnlySpan args) { throw null; } public System.Text.StringBuilder AppendFormat(System.IFormatProvider? provider, System.Text.CompositeFormat format, params object?[] args) { throw null; } public System.Text.StringBuilder AppendFormat(System.IFormatProvider? provider, System.Text.CompositeFormat format, System.ReadOnlySpan args) { throw null; } public System.Text.StringBuilder AppendFormat([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0) { throw null; } public System.Text.StringBuilder AppendFormat([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1) { throw null; } public System.Text.StringBuilder AppendFormat([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, object? arg0, object? arg1, object? arg2) { throw null; } public System.Text.StringBuilder AppendFormat([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, params object?[] args) { throw null; } + public System.Text.StringBuilder AppendFormat([System.Diagnostics.CodeAnalysis.StringSyntaxAttribute("CompositeFormat")] string format, System.ReadOnlySpan args) { throw null; } public System.Text.StringBuilder AppendFormat(System.IFormatProvider? provider, System.Text.CompositeFormat format, TArg0 arg0) { throw null; } public System.Text.StringBuilder AppendFormat(System.IFormatProvider? provider, System.Text.CompositeFormat format, TArg0 arg0, TArg1 arg1) { throw null; } public System.Text.StringBuilder AppendFormat(System.IFormatProvider? provider, System.Text.CompositeFormat format, TArg0 arg0, TArg1 arg1, TArg2 arg2) { throw null; } public System.Text.StringBuilder AppendJoin(char separator, params object?[] values) { throw null; } public System.Text.StringBuilder AppendJoin(char separator, params string?[] values) { throw null; } + public System.Text.StringBuilder AppendJoin(char separator, System.ReadOnlySpan values) { throw null; } + public System.Text.StringBuilder AppendJoin(char separator, System.ReadOnlySpan values) { throw null; } public System.Text.StringBuilder AppendJoin(string? separator, params object?[] values) { throw null; } public System.Text.StringBuilder AppendJoin(string? separator, params string?[] values) { throw null; } + public System.Text.StringBuilder AppendJoin(string? separator, System.ReadOnlySpan values) { throw null; } + public System.Text.StringBuilder AppendJoin(string? separator, System.ReadOnlySpan values) { throw null; } public System.Text.StringBuilder AppendJoin(char separator, System.Collections.Generic.IEnumerable values) { throw null; } public System.Text.StringBuilder AppendJoin(string? separator, System.Collections.Generic.IEnumerable values) { throw null; } public System.Text.StringBuilder AppendLine() { throw null; } @@ -15103,6 +15130,7 @@ public void CancelAfter(System.TimeSpan delay) { } public System.Threading.Tasks.Task CancelAsync() { throw null; } public static System.Threading.CancellationTokenSource CreateLinkedTokenSource(System.Threading.CancellationToken token) { throw null; } public static System.Threading.CancellationTokenSource CreateLinkedTokenSource(System.Threading.CancellationToken token1, System.Threading.CancellationToken token2) { throw null; } + public static System.Threading.CancellationTokenSource CreateLinkedTokenSource(System.ReadOnlySpan tokens) { throw null; } public static System.Threading.CancellationTokenSource CreateLinkedTokenSource(params System.Threading.CancellationToken[] tokens) { throw null; } public void Dispose() { } protected virtual void Dispose(bool disposing) { } @@ -15309,6 +15337,8 @@ public void Wait(System.Threading.CancellationToken cancellationToken) { } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public static void WaitAll(System.Collections.Generic.IEnumerable tasks, System.Threading.CancellationToken cancellationToken = default) { } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] + public static void WaitAll(System.ReadOnlySpan tasks) { } + [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public static void WaitAll(params System.Threading.Tasks.Task[] tasks) { } [System.Runtime.Versioning.UnsupportedOSPlatformAttribute("browser")] public static bool WaitAll(System.Threading.Tasks.Task[] tasks, int millisecondsTimeout) { throw null; } @@ -15329,14 +15359,18 @@ public static void WaitAll(System.Threading.Tasks.Task[] tasks, System.Threading public System.Threading.Tasks.Task WaitAsync(System.TimeSpan timeout, System.TimeProvider timeProvider) { throw null; } public System.Threading.Tasks.Task WaitAsync(System.TimeSpan timeout, System.TimeProvider timeProvider, System.Threading.CancellationToken cancellationToken) { throw null; } public static System.Threading.Tasks.Task WhenAll(System.Collections.Generic.IEnumerable tasks) { throw null; } + public static System.Threading.Tasks.Task WhenAll(System.ReadOnlySpan tasks) { throw null; } public static System.Threading.Tasks.Task WhenAll(params System.Threading.Tasks.Task[] tasks) { throw null; } public static System.Threading.Tasks.Task WhenAll(System.Collections.Generic.IEnumerable> tasks) { throw null; } + public static System.Threading.Tasks.Task WhenAll(System.ReadOnlySpan> tasks) { throw null; } public static System.Threading.Tasks.Task WhenAll(params System.Threading.Tasks.Task[] tasks) { throw null; } public static System.Threading.Tasks.Task WhenAny(System.Collections.Generic.IEnumerable tasks) { throw null; } public static System.Threading.Tasks.Task WhenAny(System.Threading.Tasks.Task task1, System.Threading.Tasks.Task task2) { throw null; } + public static System.Threading.Tasks.Task WhenAny(System.ReadOnlySpan tasks) { throw null; } public static System.Threading.Tasks.Task WhenAny(params System.Threading.Tasks.Task[] tasks) { throw null; } public static System.Threading.Tasks.Task> WhenAny(System.Collections.Generic.IEnumerable> tasks) { throw null; } public static System.Threading.Tasks.Task> WhenAny(System.Threading.Tasks.Task task1, System.Threading.Tasks.Task task2) { throw null; } + public static System.Threading.Tasks.Task> WhenAny(System.ReadOnlySpan> tasks) { throw null; } public static System.Threading.Tasks.Task> WhenAny(params System.Threading.Tasks.Task[] tasks) { throw null; } public static System.Collections.Generic.IAsyncEnumerable WhenEach(System.Collections.Generic.IEnumerable tasks) { throw null; } public static System.Collections.Generic.IAsyncEnumerable WhenEach(params System.Threading.Tasks.Task[] tasks) { throw null; } diff --git a/src/libraries/System.Runtime/tests/System.Dynamic.Runtime.Tests/Dynamic.Context/Conformance.dynamic.context.method.regmethod.regclass.cs b/src/libraries/System.Runtime/tests/System.Dynamic.Runtime.Tests/Dynamic.Context/Conformance.dynamic.context.method.regmethod.regclass.cs index 4ab40934105281..6cf3503569751f 100644 --- a/src/libraries/System.Runtime/tests/System.Dynamic.Runtime.Tests/Dynamic.Context/Conformance.dynamic.context.method.regmethod.regclass.cs +++ b/src/libraries/System.Runtime/tests/System.Dynamic.Runtime.Tests/Dynamic.Context/Conformance.dynamic.context.method.regmethod.regclass.cs @@ -714,7 +714,9 @@ public static void CalledFrom_UsingExpression() { using (StreamWriter sw = new StreamWriter(sm)) { +#pragma warning disable CS9220 // One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. sw.WriteLine(mc.Method_ReturnString('a')); +#pragma warning restore CS9220 sw.Flush(); sm.Position = 0; using (StreamReader sr = new StreamReader(sm, (bool)mc.Method_ReturnBool())) diff --git a/src/libraries/System.Runtime/tests/System.Dynamic.Runtime.Tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Operators.basic.cs b/src/libraries/System.Runtime/tests/System.Dynamic.Runtime.Tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Operators.basic.cs index 1230648a1f443b..5d5933aaf4eea2 100644 --- a/src/libraries/System.Runtime/tests/System.Dynamic.Runtime.Tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Operators.basic.cs +++ b/src/libraries/System.Runtime/tests/System.Dynamic.Runtime.Tests/Dynamic.OverloadResolution/Conformance.dynamic.overloadResolution.Operators.basic.cs @@ -1169,7 +1169,9 @@ public static void ExecPositiveTest(dynamic dobj, Type exp_type, T exp_underv } else { +#pragma warning disable CS9220 // One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. System.Console.WriteLine("Got invalid result when testing {0}: {1}[{2}]", tip, dr, dr.GetType()); +#pragma warning restore CS9220 } } catch (Exception ex) @@ -1186,7 +1188,9 @@ public static void ExecNegativeTestWithBadOps(dynamic dobj, string[] exp_msg, st try { dynamic dr = test(dobj); +#pragma warning disable CS9220 // One or more overloads of method having non-array params collection parameter might be applicable only in expanded form which is not supported during dynamic dispatch. System.Console.WriteLine("Got invalid result when testing {0}: {1}[{2}]", tip, dr, dr.GetType()); +#pragma warning restore CS9220 } catch (Microsoft.CSharp.RuntimeBinder.RuntimeBinderException ex) { diff --git a/src/libraries/System.Runtime/tests/System.IO.Tests/IndentedTextWriter.cs b/src/libraries/System.Runtime/tests/System.IO.Tests/IndentedTextWriter.cs index 0de5d90f77bbdb..84ebd066786a22 100644 --- a/src/libraries/System.Runtime/tests/System.IO.Tests/IndentedTextWriter.cs +++ b/src/libraries/System.Runtime/tests/System.IO.Tests/IndentedTextWriter.cs @@ -104,6 +104,12 @@ public override void Write(string format, params object[] arg) LastCalledMethod = nameof(Write); } + public override void Write(string format, /*params*/ ReadOnlySpan arg) + { + base.Write(format, arg); + LastCalledMethod = nameof(Write); + } + public override void Write(string value) { base.Write(value); @@ -212,6 +218,12 @@ public override void WriteLine(string format, params object[] arg) LastCalledMethod = nameof(WriteLine); } + public override void WriteLine(string format, /*params*/ ReadOnlySpan arg) + { + base.WriteLine(format, arg); + LastCalledMethod = nameof(WriteLine); + } + public override void WriteLine(string value) { base.WriteLine(value); @@ -416,7 +428,8 @@ public static async Task Writes_ProducesExpectedOutput(string newline) itw.Write("{0}", 14); itw.Write("{0} {1}", 15, 16); itw.Write("{0} {1} {2}", 15, 16, 17); - itw.Write("{0} {1} {2} {3}", 15, 16, 17, 18); + itw.Write("{0} {1} {2} {3}", new object[] { 15, 16, 17, 18 }); + itw.Write("{0} {1} {2} {3}", (ReadOnlySpan)new object[] { 15, 16, 17, 18 }); itw.WriteLine(true); itw.WriteLine('a'); @@ -434,7 +447,8 @@ public static async Task Writes_ProducesExpectedOutput(string newline) itw.WriteLine("{0}", 14); itw.WriteLine("{0} {1}", 15, 16); itw.WriteLine("{0} {1} {2}", 15, 16, 17); - itw.WriteLine("{0} {1} {2} {3}", 15, 16, 17, 18); + itw.WriteLine("{0} {1} {2} {3}", new object[] { 15, 16, 17, 18 }); + itw.WriteLine("{0} {1} {2} {3}", (ReadOnlySpan)new object[] { 15, 16, 17, 18 }); await itw.WriteAsync('a'); await itw.WriteAsync(new char[] { 'b', 'c' }); @@ -450,7 +464,7 @@ public static async Task Writes_ProducesExpectedOutput(string newline) Assert.Equal( "t" + newline + - "tTrueabcde45.66.789101112131415 1615 16 1715 16 17 18True" + newline + + "tTrueabcde45.66.789101112131415 1615 16 1715 16 17 1815 16 17 18True" + newline + "ta" + newline + "tbc" + newline + "tde" + newline + @@ -467,6 +481,7 @@ public static async Task Writes_ProducesExpectedOutput(string newline) "t15 16" + newline + "t15 16 17" + newline + "t15 16 17 18" + newline + + "t15 16 17 18" + newline + "tabcde1a" + newline + "tbc" + newline + "tde" + newline + @@ -499,7 +514,8 @@ object[] CreateParameters(Action callWrite, string expected) yield return CreateParameters(x => x.Write("Hello {0} World", "Digital"), "Hello Digital World"); yield return CreateParameters(x => x.Write("Hello {0} World{1}", "Digital", "!!"), "Hello Digital World!!"); yield return CreateParameters(x => x.Write("Hello {0} {1} World{2}", "Dot", "NET", "!!"), "Hello Dot NET World!!"); - yield return CreateParameters(x => x.Write("Hello {0} {1} {2} World{3}", "Digital", "Dot", "NET", "!!"), "Hello Digital Dot NET World!!"); + yield return CreateParameters(x => x.Write("Hello {0} {1} {2} World{3}", new object[] { "Digital", "Dot", "NET", "!!" }), "Hello Digital Dot NET World!!"); + yield return CreateParameters(x => x.Write("Hello {0} {1} {2} World{3}", (ReadOnlySpan)new object[] { "Digital", "Dot", "NET", "!!" }), "Hello Digital Dot NET World!!"); yield return CreateParameters(x => x.Write("Hello World".ToCharArray(), 6, 5), "World"); } } @@ -530,7 +546,8 @@ object[] CreateParameters(Action callWriteLine, string expec yield return CreateParameters(x => x.WriteLine("Hello {0} {1} World", "Dot", "NET"), $"Hello Dot NET World{NewLine}"); yield return CreateParameters(x => x.WriteLine("Hello {0} {1} World{2}", "Dot", "NET", "!!"), $"Hello Dot NET World!!{NewLine}"); yield return CreateParameters(x => x.WriteLine("Hello World".ToCharArray(), 6, 5), $"World{NewLine}"); - yield return CreateParameters(x => x.WriteLine("Hello {0} {1} {2} World{3}", "Digital", "Dot", "NET", "!!"), $"Hello Digital Dot NET World!!{NewLine}"); + yield return CreateParameters(x => x.WriteLine("Hello {0} {1} {2} World{3}", new object[] { "Digital", "Dot", "NET", "!!" }), $"Hello Digital Dot NET World!!{NewLine}"); + yield return CreateParameters(x => x.WriteLine("Hello {0} {1} {2} World{3}", (ReadOnlySpan)new object[] { "Digital", "Dot", "NET", "!!" }), $"Hello Digital Dot NET World!!{NewLine}"); } } diff --git a/src/libraries/System.Runtime/tests/System.IO.Tests/Stream/Stream.NullTests.cs b/src/libraries/System.Runtime/tests/System.IO.Tests/Stream/Stream.NullTests.cs index 86ebfba5efbefb..3714ff3b2020dc 100644 --- a/src/libraries/System.Runtime/tests/System.IO.Tests/Stream/Stream.NullTests.cs +++ b/src/libraries/System.Runtime/tests/System.IO.Tests/Stream/Stream.NullTests.cs @@ -234,7 +234,8 @@ public static void TextNullTextWriter(TextWriter output) output.Write(" {0} ", "Friday"); output.Write(" {0}{1} ", "Saturday", "Sunday"); output.Write(" {0} {1} {2}", TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(2), TimeSpan.FromDays(3)); - output.Write(" {0} {1} {2} {3}", (Int128)4, (UInt128)5, (nint)6, (nuint)7); + output.Write(" {0} {1} {2} {3}", new object[] { (Int128)4, (UInt128)5, (nint)6, (nuint)7 }); + output.Write(" {0} {1} {2} {3} {4}", (ReadOnlySpan)new object[] { (Int128)4, (UInt128)5, (nint)6, (nuint)7, "8" }); output.WriteLine(); output.WriteLine(true); output.WriteLine('a'); @@ -256,7 +257,8 @@ public static void TextNullTextWriter(TextWriter output) output.WriteLine(" {0} ", "Friday"); output.WriteLine(" {0}{1} ", "Saturday", "Sunday"); output.WriteLine(" {0} {1} {2}", TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(2), TimeSpan.FromDays(3)); - output.WriteLine(" {0} {1} {2} {3}", (Int128)4, (UInt128)5, (nint)6, (nuint)7); + output.WriteLine(" {0} {1} {2} {3}", new object[] { (Int128)4, (UInt128)5, (nint)6, (nuint)7 }); + output.WriteLine(" {0} {1} {2} {3} {4}", (ReadOnlySpan)new object[] { (Int128)4, (UInt128)5, (nint)6, (nuint)7, "8" }); Assert.True(output.WriteAsync('a').IsCompletedSuccessfully); Assert.True(output.WriteAsync((char[])null).IsCompletedSuccessfully); Assert.True(output.WriteAsync(new char[] { 'b', 'c' }).IsCompletedSuccessfully); diff --git a/src/libraries/System.Runtime/tests/System.IO.Tests/StreamWriter/StreamWriter.cs b/src/libraries/System.Runtime/tests/System.IO.Tests/StreamWriter/StreamWriter.cs index 0d34fe6730f0ac..69edb13a8a1fce 100644 --- a/src/libraries/System.Runtime/tests/System.IO.Tests/StreamWriter/StreamWriter.cs +++ b/src/libraries/System.Runtime/tests/System.IO.Tests/StreamWriter/StreamWriter.cs @@ -58,20 +58,28 @@ public void FormatOverloadsCallWrite() Assert.Equal(2, writer.WriteCalls); writer.Write("{0}{1}{2}", "Zero", "One", "Two"); Assert.Equal(3, writer.WriteCalls); - writer.Write("{0}{1}{2}{3}", "Zero", "One", "Two", "Three"); + writer.Write("{0}{1}{2}{3}", new object[] { "Zero", "One", "Two", "Three" }); Assert.Equal(4, writer.WriteCalls); - writer.Write("{0}{1}{2}{3}{4}", "Zero", "One", "Two", "Three", "Four"); + writer.Write("{0}{1}{2}{3}", (ReadOnlySpan)new object[] { "Zero", "One", "Two", "Three" }); Assert.Equal(5, writer.WriteCalls); + writer.Write("{0}{1}{2}{3}{4}", new object[] { "Zero", "One", "Two", "Three", "Four" }); + Assert.Equal(6, writer.WriteCalls); + writer.Write("{0}{1}{2}{3}{4}", (ReadOnlySpan)new object[] { "Zero", "One", "Two", "Three", "Four" }); + Assert.Equal(7, writer.WriteCalls); writer.WriteLine("{0}", "Zero"); Assert.Equal(1, writer.WriteLineCalls); writer.WriteLine("{0}{1}", "Zero", "One"); Assert.Equal(2, writer.WriteLineCalls); writer.WriteLine("{0}{1}{2}", "Zero", "One", "Two"); Assert.Equal(3, writer.WriteLineCalls); - writer.WriteLine("{0}{1}{2}{3}", "Zero", "One", "Two", "Three"); + writer.WriteLine("{0}{1}{2}{3}", new object[] { "Zero", "One", "Two", "Three" }); Assert.Equal(4, writer.WriteLineCalls); - writer.WriteLine("{0}{1}{2}{3}{4}", "Zero", "One", "Two", "Three", "Four"); + writer.WriteLine("{0}{1}{2}{3}", (ReadOnlySpan)new object[] { "Zero", "One", "Two", "Three" }); Assert.Equal(5, writer.WriteLineCalls); + writer.WriteLine("{0}{1}{2}{3}{4}", new object[] { "Zero", "One", "Two", "Three", "Four" }); + Assert.Equal(6, writer.WriteLineCalls); + writer.WriteLine("{0}{1}{2}{3}{4}", (ReadOnlySpan)new object[] { "Zero", "One", "Two", "Three", "Four" }); + Assert.Equal(7, writer.WriteLineCalls); } } } diff --git a/src/libraries/System.Runtime/tests/System.IO.Tests/TextWriter/TextWriterTests.cs b/src/libraries/System.Runtime/tests/System.IO.Tests/TextWriter/TextWriterTests.cs index de3ed9530a1ee2..2299dc86f567db 100644 --- a/src/libraries/System.Runtime/tests/System.IO.Tests/TextWriter/TextWriterTests.cs +++ b/src/libraries/System.Runtime/tests/System.IO.Tests/TextWriter/TextWriterTests.cs @@ -226,15 +226,24 @@ public void WriteStringThreeObjectsTest() } [Fact] - public void WriteStringMultipleObjectsTest() + public void WriteStringMultipleObjectsArrayTest() { using (CharArrayTextWriter tw = NewTextWriter) { - tw.Write(TestDataProvider.FormatStringMultipleObjects, TestDataProvider.MultipleObjects); + tw.Write(TestDataProvider.FormatStringMultipleObjects, (object[])TestDataProvider.MultipleObjects); Assert.Equal(string.Format(TestDataProvider.FormatStringMultipleObjects, TestDataProvider.MultipleObjects), tw.Text); } } + [Fact] + public void WriteStringMultipleObjectsSpanTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.Write(TestDataProvider.FormatStringMultipleObjects, (ReadOnlySpan)TestDataProvider.MultipleObjects); + Assert.Equal(string.Format(TestDataProvider.FormatStringMultipleObjects, TestDataProvider.MultipleObjects), tw.Text); + } + } #endregion #region WriteLine Overloads @@ -454,11 +463,21 @@ public void WriteLineStringThreeObjectsTest() } [Fact] - public void WriteLineStringMultipleObjectsTest() + public void WriteLineStringMultipleObjectsArrayTest() { using (CharArrayTextWriter tw = NewTextWriter) { - tw.WriteLine(TestDataProvider.FormatStringMultipleObjects, TestDataProvider.MultipleObjects); + tw.WriteLine(TestDataProvider.FormatStringMultipleObjects, (object[])TestDataProvider.MultipleObjects); + Assert.Equal(string.Format(TestDataProvider.FormatStringMultipleObjects + tw.NewLine, TestDataProvider.MultipleObjects), tw.Text); + } + } + + [Fact] + public void WriteLineStringMultipleObjectsSpanTest() + { + using (CharArrayTextWriter tw = NewTextWriter) + { + tw.WriteLine(TestDataProvider.FormatStringMultipleObjects, (ReadOnlySpan)TestDataProvider.MultipleObjects); Assert.Equal(string.Format(TestDataProvider.FormatStringMultipleObjects + tw.NewLine, TestDataProvider.MultipleObjects), tw.Text); } } @@ -772,8 +791,11 @@ public async Task CreateBroadcasting_DelegatesToAllWriters() oracle.Write(" {0} {1} {2}", TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(2), TimeSpan.FromDays(3)); broadcasting.Write(" {0} {1} {2}", TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(2), TimeSpan.FromDays(3)); - oracle.Write(" {0} {1} {2} {3}", (Int128)4, (UInt128)5, (nint)6, (nuint)7); - broadcasting.Write(" {0} {1} {2} {3}", (Int128)4, (UInt128)5, (nint)6, (nuint)7); + oracle.Write(" {0} {1} {2} {3}", new object[] { (Int128)4, (UInt128)5, (nint)6, (nuint)7 }); + broadcasting.Write(" {0} {1} {2} {3}", new object[] { (Int128)4, (UInt128)5, (nint)6, (nuint)7 }); + + oracle.Write(" {0} {1} {2} {3} {4}", (ReadOnlySpan)new object[] { (Int128)4, (UInt128)5, (nint)6, (nuint)7, "8" }); + broadcasting.Write(" {0} {1} {2} {3} {4}", (ReadOnlySpan)new object[] { (Int128)4, (UInt128)5, (nint)6, (nuint)7, "8" }); oracle.WriteLine(); broadcasting.WriteLine(); @@ -835,8 +857,11 @@ public async Task CreateBroadcasting_DelegatesToAllWriters() oracle.WriteLine(" {0} {1} {2}", TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(2), TimeSpan.FromDays(3)); broadcasting.WriteLine(" {0} {1} {2}", TimeSpan.FromSeconds(1), TimeSpan.FromMinutes(2), TimeSpan.FromDays(3)); - oracle.WriteLine(" {0} {1} {2} {3}", (Int128)4, (UInt128)5, (nint)6, (nuint)7); - broadcasting.WriteLine(" {0} {1} {2} {3}", (Int128)4, (UInt128)5, (nint)6, (nuint)7); + oracle.WriteLine(" {0} {1} {2} {3}", new object[] { (Int128)4, (UInt128)5, (nint)6, (nuint)7 }); + broadcasting.WriteLine(" {0} {1} {2} {3}", new object[] { (Int128)4, (UInt128)5, (nint)6, (nuint)7 }); + + oracle.WriteLine(" {0} {1} {2} {3} {4}", (ReadOnlySpan)new object[] { (Int128)4, (UInt128)5, (nint)6, (nuint)7, "8" }); + broadcasting.WriteLine(" {0} {1} {2} {3} {4}", (ReadOnlySpan)new object[] { (Int128)4, (UInt128)5, (nint)6, (nuint)7, "8" }); await oracle.WriteAsync('a'); await broadcasting.WriteAsync('a'); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/IO/PathTests_Combine.cs b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/IO/PathTests_Combine.cs index 033168ce649a17..9493d5aed543ab 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/IO/PathTests_Combine.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/IO/PathTests_Combine.cs @@ -98,6 +98,7 @@ public static void Combine(string[] paths) // Combine(string[]) Assert.Equal(expected, Path.Combine(paths)); + Assert.Equal(expected, Path.Combine((ReadOnlySpan)paths)); // Verify special cases switch (paths.Length) @@ -166,6 +167,10 @@ public static void ContainsInvalidCharWithRooted_Windows_core() private static void VerifyException(string[] paths) where T : Exception { Assert.Throws(() => Path.Combine(paths)); + if (paths != null) + { + Assert.Throws(() => Path.Combine((ReadOnlySpan)paths)); + } //verify passed as elements case if (paths != null) diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/IO/PathTests_Join.cs b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/IO/PathTests_Join.cs index 0984a9145385ac..f8b61049251fee 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/IO/PathTests_Join.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Extensions.Tests/System/IO/PathTests_Join.cs @@ -200,36 +200,42 @@ public void JoinStringArray_ThrowsArgumentNullException() public void JoinStringArray_ZeroLengthArray() { Assert.Equal(string.Empty, Path.Join(new string[0])); + Assert.Equal(string.Empty, Path.Join((ReadOnlySpan)new string[0])); } [Theory, MemberData(nameof(TestData_JoinOnePath))] public void JoinStringArray_1(string path1, string expected) { Assert.Equal(expected, Path.Join(new string[] { path1 })); + Assert.Equal(expected, Path.Join((ReadOnlySpan)new string[] { path1 })); } [Theory, MemberData(nameof(TestData_JoinTwoPaths))] public void JoinStringArray_2(string path1, string path2, string expected) { Assert.Equal(expected, Path.Join(new string[] { path1, path2 })); + Assert.Equal(expected, Path.Join((ReadOnlySpan)new string[] { path1, path2 })); } [Theory, MemberData(nameof(TestData_JoinThreePaths))] public void JoinStringArray_3(string path1, string path2, string path3, string expected) { Assert.Equal(expected, Path.Join(new string[] { path1, path2, path3 })); + Assert.Equal(expected, Path.Join((ReadOnlySpan)new string[] { path1, path2, path3 })); } [Theory, MemberData(nameof(TestData_JoinFourPaths))] public void JoinStringArray_4(string path1, string path2, string path3, string path4, string expected) { Assert.Equal(expected, Path.Join(new string[] { path1, path2, path3, path4 })); + Assert.Equal(expected, Path.Join((ReadOnlySpan)new string[] { path1, path2, path3, path4 })); } [Theory, MemberData(nameof(TestData_JoinFourPaths))] public void JoinStringArray_8(string path1, string path2, string path3, string path4, string fourJoined) { Assert.Equal(Path.Join(fourJoined, fourJoined), Path.Join(new string[] { path1, path2, path3, path4, path1, path2, path3, path4 })); + Assert.Equal(Path.Join(fourJoined, fourJoined), Path.Join((ReadOnlySpan)new string[] { path1, path2, path3, path4, path1, path2, path3, path4 })); } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/MulticastDelegateTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/MulticastDelegateTests.cs index 9994fa02f10fac..ab798d6442beb0 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/MulticastDelegateTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/MulticastDelegateTests.cs @@ -105,7 +105,19 @@ public static void Combine() D nothing = (D)(Delegate.Combine()); Assert.Null(nothing); - D one = (D)(Delegate.Combine(a)); + nothing = (D)(Delegate.Combine(null)); + Assert.Null(nothing); + + nothing = (D)(Delegate.Combine(ReadOnlySpan.Empty)); + Assert.Null(nothing); + + D one = (D)(Delegate.Combine(new[] { a })); + t1.Clear(); + one(5); + Assert.Equal("A5", t1.S); + CheckInvokeList(new D[] { a }, one, t1); + + one = (D)(Delegate.Combine((ReadOnlySpan)new[] { a })); t1.Clear(); one(5); Assert.Equal("A5", t1.S); @@ -117,13 +129,25 @@ public static void Combine() Assert.Equal("A5B5", t1.S); CheckInvokeList(new D[] { a, b }, ab, t1); - D abc = (D)(Delegate.Combine(a, b, c)); + D abc = (D)(Delegate.Combine(new[] { a, b, c })); t1.Clear(); abc(5); Assert.Equal("A5B5C5", t1.S); CheckInvokeList(new D[] { a, b, c }, abc, t1); - D abcdabc = (D)(Delegate.Combine(abc, d, abc)); + abc = (D)(Delegate.Combine((ReadOnlySpan)new[] { a, b, c })); + t1.Clear(); + abc(5); + Assert.Equal("A5B5C5", t1.S); + CheckInvokeList(new D[] { a, b, c }, abc, t1); + + D abcdabc = (D)(Delegate.Combine(new[] { abc, d, abc })); + t1.Clear(); + abcdabc(9); + Assert.Equal("A9B9C9D9A9B9C9", t1.S); + CheckInvokeList(new D[] { a, b, c, d, a, b, c }, abcdabc, t1); + + abcdabc = (D)(Delegate.Combine((ReadOnlySpan)new[] { abc, d, abc })); t1.Clear(); abcdabc(9); Assert.Equal("A9B9C9D9A9B9C9", t1.S); diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/String.SplitTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/String.SplitTests.cs index b12bd2cb74fc48..037e7a98a95878 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/String.SplitTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/String.SplitTests.cs @@ -141,6 +141,7 @@ public static void SplitNoMatchSingleResult() Assert.Equal(expected, Value.Split(',', Options)); Assert.Equal(expected, Value.Split(',', Count, Options)); Assert.Equal(expected, Value.Split(new[] { ',' })); + Assert.Equal(expected, Value.Split((ReadOnlySpan)new[] { ',' })); Assert.Equal(expected, Value.Split(new[] { ',' }, Options)); Assert.Equal(expected, Value.Split(new[] { ',' }, Count)); Assert.Equal(expected, Value.Split(new[] { ',' }, Count, Options)); @@ -516,6 +517,7 @@ public static void SplitCharSeparator(string value, char separator, int count, S { Assert.Equal(expected, value.Split(separator)); Assert.Equal(expected, value.Split(new[] { separator })); + Assert.Equal(expected, value.Split((ReadOnlySpan)new[] { separator })); Assert.Equal(expected, value.Split(separator.ToString())); } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/CompositeFormatTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/CompositeFormatTests.cs index bdfde206c850f4..64c0caf8f8c396 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/CompositeFormatTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/CompositeFormatTests.cs @@ -68,12 +68,15 @@ public static void MinimumArgumentCount_MatchesExpectedValue(string format, int Assert.Equal(expected, cf.MinimumArgumentCount); - string s = string.Format(null, cf, Enumerable.Repeat((object)"arg", expected).ToArray()); - Assert.NotNull(s); + object[] args = Enumerable.Repeat((object)"arg", expected).ToArray(); + Assert.NotNull(string.Format(null, cf, args)); + Assert.NotNull(string.Format(null, cf, (ReadOnlySpan)args)); if (expected != 0) { - Assert.Throws(() => string.Format(null, cf, Enumerable.Repeat((object)"arg", expected - 1).ToArray())); + args = Enumerable.Repeat((object)"arg", expected - 1).ToArray(); + Assert.Throws(() => string.Format(null, cf, args)); + Assert.Throws(() => string.Format(null, cf, (ReadOnlySpan)args)); } } @@ -86,21 +89,20 @@ public static void StringFormat_Valid(IFormatProvider provider, string format, o Assert.Same(format, cf.Format); Assert.Equal(expected, string.Format(provider, cf, values)); - - Assert.Equal(expected, string.Format(provider, cf, (ReadOnlySpan)values)); + Assert.Equal(expected, string.Format(provider, cf, (ReadOnlySpan)values)); switch (values.Length) { case 1: - Assert.Equal(expected, string.Format(provider, cf, values[0])); + Assert.Equal(expected, string.Format(provider, cf, arg0: values[0])); break; case 2: - Assert.Equal(expected, string.Format(provider, cf, values[0], values[1])); + Assert.Equal(expected, string.Format(provider, cf, arg0: values[0], arg1: values[1])); break; case 3: - Assert.Equal(expected, string.Format(provider, cf, values[0], values[1], values[2])); + Assert.Equal(expected, string.Format(provider, cf, arg0: values[0], arg1: values[1], arg2: values[2])); break; } } @@ -188,6 +190,8 @@ public static void MemoryExtensionsTryWrite_Valid(IFormatProvider provider, stri dest = new char[expected.Length - 1]; Assert.False(dest.AsSpan().TryWrite(provider, cf, out charsWritten, values)); Assert.Equal(0, charsWritten); + Assert.False(dest.AsSpan().TryWrite(provider, cf, out charsWritten, (ReadOnlySpan)values)); + Assert.Equal(0, charsWritten); } } @@ -209,16 +213,17 @@ public static void StringFormat_Invalid_FormatExceptionFromArgs(IFormatProvider Assert.NotNull(cf); Assert.Throws(() => string.Format(provider, cf, args)); + Assert.Throws(() => string.Format(provider, cf, (ReadOnlySpan)args)); switch (args.Length) { case 1: - Assert.Throws(() => string.Format(provider, cf, args[0])); + Assert.Throws(() => string.Format(provider, cf, arg0: args[0])); break; case 2: - Assert.Throws(() => string.Format(provider, cf, args[0], args[1])); + Assert.Throws(() => string.Format(provider, cf, arg0: args[0], arg1: args[1])); break; case 3: - Assert.Throws(() => string.Format(provider, cf, args[0], args[1], args[2])); + Assert.Throws(() => string.Format(provider, cf, arg0: args[0], arg1: args[1], arg2: args[2])); break; } } @@ -233,16 +238,17 @@ public static void StringBuilderAppendFormat_Invalid_FormatExceptionFromArgs(IFo var sb = new StringBuilder(); Assert.Throws(() => sb.AppendFormat(provider, cf, args)); + Assert.Throws(() => sb.AppendFormat(provider, cf, (ReadOnlySpan)args)); switch (args.Length) { case 1: - Assert.Throws(() => sb.AppendFormat(provider, cf, args[0])); + Assert.Throws(() => sb.AppendFormat(provider, cf, arg0: args[0])); break; case 2: - Assert.Throws(() => sb.AppendFormat(provider, cf, args[0], args[1])); + Assert.Throws(() => sb.AppendFormat(provider, cf, arg0: args[0], arg1: args[1])); break; case 3: - Assert.Throws(() => sb.AppendFormat(provider, cf, args[0], args[1], args[2])); + Assert.Throws(() => sb.AppendFormat(provider, cf, arg0: args[0], arg1: args[1], arg2: args[2])); break; } } @@ -257,16 +263,17 @@ public static void MemoryExtensionsTryWrite_Invalid_FormatExceptionFromArgs(IFor char[] dest = new char[1024]; Assert.Throws(() => new Span(dest).TryWrite(provider, cf, out _, args)); + Assert.Throws(() => new Span(dest).TryWrite(provider, cf, out _, (ReadOnlySpan)args)); switch (args.Length) { case 1: Assert.Throws(() => new Span(dest).TryWrite(provider, cf, out _, args[0])); break; case 2: - Assert.Throws(() => new Span(dest).TryWrite(provider, cf, out _, args[0], args[1])); + Assert.Throws(() => new Span(dest).TryWrite(provider, cf, out _, arg0: args[0], arg1: args[1])); break; case 3: - Assert.Throws(() => new Span(dest).TryWrite(provider, cf, out _, args[0], args[1], args[2])); + Assert.Throws(() => new Span(dest).TryWrite(provider, cf, out _, arg0: args[0], arg1: args[1], arg2: args[2])); break; } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/StringBuilderTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/StringBuilderTests.cs index 2d1f569dd103be..5671c6d3614e57 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/StringBuilderTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Text/StringBuilderTests.cs @@ -842,6 +842,19 @@ public static void AppendFormat(string original, IFormatProvider provider, strin builder = new StringBuilder(original); builder.AppendFormat(provider, format, values); Assert.Equal(expected, builder.ToString()); + + // Use AppendFormat(string, ReadOnlySpan) or AppendFormat(IFormatProvider, string, ReadOnlySpan) + if (provider == null) + { + // Use AppendFormat(string, ReadOnlySpan) + builder = new StringBuilder(original); + builder.AppendFormat(format, (ReadOnlySpan)values); + Assert.Equal(expected, builder.ToString()); + } + // Use AppendFormat(IFormatProvider, string, ReadOnlySpan) + builder = new StringBuilder(original); + builder.AppendFormat(provider, format, (ReadOnlySpan)values); + Assert.Equal(expected, builder.ToString()); } [Fact] @@ -855,17 +868,21 @@ public static void AppendFormat_Invalid() var obj2 = new object(); var obj3 = new object(); var obj4 = new object(); + var objArray = new object[] { obj1, obj2, obj3, obj4 }; AssertExtensions.Throws("format", () => builder.AppendFormat(null, obj1)); // Format is null AssertExtensions.Throws("format", () => builder.AppendFormat(null, obj1, obj2, obj3)); // Format is null - AssertExtensions.Throws("format", () => builder.AppendFormat(null, obj1, obj2, obj3)); // Format is null AssertExtensions.Throws("format", () => builder.AppendFormat(null, obj1, obj2, obj3, obj4)); // Format is null + AssertExtensions.Throws("format", () => builder.AppendFormat(null, objArray)); // Format is null + AssertExtensions.Throws("format", () => builder.AppendFormat(null, (ReadOnlySpan)objArray)); // Format is null AssertExtensions.Throws("args", () => builder.AppendFormat("", null)); // Args is null AssertExtensions.Throws("format", () => builder.AppendFormat(null, (object[])null)); // Both format and args are null AssertExtensions.Throws("format", () => builder.AppendFormat(formatter, (string)null, obj1)); // Format is null AssertExtensions.Throws("format", () => builder.AppendFormat(formatter, (string)null, obj1, obj2)); // Format is null AssertExtensions.Throws("format", () => builder.AppendFormat(formatter, (string)null, obj1, obj2, obj3)); // Format is null AssertExtensions.Throws("format", () => builder.AppendFormat(formatter, (string)null, obj1, obj2, obj3, obj4)); // Format is null + AssertExtensions.Throws("format", () => builder.AppendFormat(formatter, (string)null, objArray)); // Format is null + AssertExtensions.Throws("format", () => builder.AppendFormat(formatter, (string)null, (ReadOnlySpan)objArray)); // Format is null AssertExtensions.Throws("args", () => builder.AppendFormat(formatter, "", null)); // Args is null AssertExtensions.Throws("format", () => builder.AppendFormat(formatter, (string)null, null)); // Both format and args are null @@ -873,19 +890,27 @@ public static void AppendFormat_Invalid() Assert.Throws(() => builder.AppendFormat("{-1}", obj1, obj2)); // Format has value < 0 Assert.Throws(() => builder.AppendFormat("{-1}", obj1, obj2, obj3)); // Format has value < 0 Assert.Throws(() => builder.AppendFormat("{-1}", obj1, obj2, obj3, obj4)); // Format has value < 0 + Assert.Throws(() => builder.AppendFormat("{-1}", objArray)); // Format has value < 0 + Assert.Throws(() => builder.AppendFormat("{-1}", (ReadOnlySpan)objArray)); // Format has value < 0 Assert.Throws(() => builder.AppendFormat(formatter, "{-1}", obj1)); // Format has value < 0 Assert.Throws(() => builder.AppendFormat(formatter, "{-1}", obj1, obj2)); // Format has value < 0 Assert.Throws(() => builder.AppendFormat(formatter, "{-1}", obj1, obj2, obj3)); // Format has value < 0 Assert.Throws(() => builder.AppendFormat(formatter, "{-1}", obj1, obj2, obj3, obj4)); // Format has value < 0 + Assert.Throws(() => builder.AppendFormat(formatter, "{-1}", objArray)); // Format has value < 0 + Assert.Throws(() => builder.AppendFormat(formatter, "{-1}", (ReadOnlySpan)objArray)); // Format has value < 0 Assert.Throws(() => builder.AppendFormat("{1}", obj1)); // Format has value >= 1 Assert.Throws(() => builder.AppendFormat("{2}", obj1, obj2)); // Format has value >= 2 Assert.Throws(() => builder.AppendFormat("{3}", obj1, obj2, obj3)); // Format has value >= 3 Assert.Throws(() => builder.AppendFormat("{4}", obj1, obj2, obj3, obj4)); // Format has value >= 4 + Assert.Throws(() => builder.AppendFormat("{4}", objArray)); // Format has value >= 4 + Assert.Throws(() => builder.AppendFormat("{4}", (ReadOnlySpan)objArray)); // Format has value >= 4 Assert.Throws(() => builder.AppendFormat(formatter, "{1}", obj1)); // Format has value >= 1 Assert.Throws(() => builder.AppendFormat(formatter, "{2}", obj1, obj2)); // Format has value >= 2 Assert.Throws(() => builder.AppendFormat(formatter, "{3}", obj1, obj2, obj3)); // Format has value >= 3 Assert.Throws(() => builder.AppendFormat(formatter, "{4}", obj1, obj2, obj3, obj4)); // Format has value >= 4 + Assert.Throws(() => builder.AppendFormat(formatter, "{4}", objArray)); // Format has value >= 4 + Assert.Throws(() => builder.AppendFormat(formatter, "{4}", (ReadOnlySpan)objArray)); // Format has value >= 4 Assert.Throws(() => builder.AppendFormat("{", "")); // Format has unescaped { Assert.Throws(() => builder.AppendFormat("{a", "")); // Format has unescaped { @@ -900,7 +925,9 @@ public static void AppendFormat_Invalid() Assert.Throws(() => builder.AppendFormat("{0 ", "")); // Format with index and spaces is not closed Assert.Throws(() => builder.AppendFormat("{1000000", new string[10])); // Format index is too long + Assert.Throws(() => builder.AppendFormat("{1000000", (ReadOnlySpan)new string[10])); // Format index is too long Assert.Throws(() => builder.AppendFormat("{10000000}", new string[10])); // Format index is too long + Assert.Throws(() => builder.AppendFormat("{10000000}", (ReadOnlySpan)new string[10])); // Format index is too long Assert.Throws(() => builder.AppendFormat("{0,", "")); // Format with comma is not closed Assert.Throws(() => builder.AppendFormat("{0, ", "")); // Format with comma and spaces is not closed @@ -910,13 +937,19 @@ public static void AppendFormat_Invalid() Assert.Throws(() => builder.AppendFormat("{0,-a", "")); // Format has invalid character after minus sign Assert.Throws(() => builder.AppendFormat("{0,1000000", new string[10])); // Format length is too long + Assert.Throws(() => builder.AppendFormat("{0,1000000", (ReadOnlySpan)new string[10])); // Format length is too long Assert.Throws(() => builder.AppendFormat("{0,10000000}", new string[10])); // Format length is too long + Assert.Throws(() => builder.AppendFormat("{0,10000000}", (ReadOnlySpan)new string[10])); // Format length is too long Assert.Throws(() => builder.AppendFormat("{0:", new string[10])); // Format with colon is not closed + Assert.Throws(() => builder.AppendFormat("{0:", (ReadOnlySpan)new string[10])); // Format with colon is not closed Assert.Throws(() => builder.AppendFormat("{0: ", new string[10])); // Format with colon and spaces is not closed + Assert.Throws(() => builder.AppendFormat("{0: ", (ReadOnlySpan)new string[10])); // Format with colon and spaces is not closed Assert.Throws(() => builder.AppendFormat("{0:{", new string[10])); // Format with custom format contains unescaped { + Assert.Throws(() => builder.AppendFormat("{0:{", (ReadOnlySpan)new string[10])); // Format with custom format contains unescaped { Assert.Throws(() => builder.AppendFormat("{0:{}", new string[10])); // Format with custom format contains unescaped { + Assert.Throws(() => builder.AppendFormat("{0:{}", (ReadOnlySpan)new string[10])); // Format with custom format contains unescaped { Assert.Throws(() => builder.AppendFormat("{0}", new TooManyCharsWrittenSpanFormattable())); // ISpanFormattable that returns more characters than it actually wrote } @@ -1775,11 +1808,15 @@ public static void AppendJoin_TestValues(object[] values, string expected) var enumerable = values.Select(_ => _); Assert.Equal(expected, new StringBuilder().AppendJoin('|', values).ToString()); + Assert.Equal(expected, new StringBuilder().AppendJoin('|', (ReadOnlySpan)values).ToString()); Assert.Equal(expected, new StringBuilder().AppendJoin('|', enumerable).ToString()); Assert.Equal(expected, new StringBuilder().AppendJoin('|', stringValues).ToString()); + Assert.Equal(expected, new StringBuilder().AppendJoin('|', (ReadOnlySpan)stringValues).ToString()); Assert.Equal(expected, new StringBuilder().AppendJoin("|", values).ToString()); + Assert.Equal(expected, new StringBuilder().AppendJoin("|", (ReadOnlySpan)values).ToString()); Assert.Equal(expected, new StringBuilder().AppendJoin("|", enumerable).ToString()); Assert.Equal(expected, new StringBuilder().AppendJoin("|", stringValues).ToString()); + Assert.Equal(expected, new StringBuilder().AppendJoin("|", (ReadOnlySpan)stringValues).ToString()); } [Fact] @@ -1801,9 +1838,14 @@ private sealed class NullToStringObject [InlineData(", ", "1, 2, 3")] public static void AppendJoin_TestStringSeparators(string separator, string expected) { - Assert.Equal(expected, new StringBuilder().AppendJoin(separator, new object[] { 1, 2, 3 }).ToString()); + var values = new object[] { 1, 2, 3 }; + var stringValues = new string[] { "1", "2", "3" }; + + Assert.Equal(expected, new StringBuilder().AppendJoin(separator, values).ToString()); + Assert.Equal(expected, new StringBuilder().AppendJoin(separator, (ReadOnlySpan)values).ToString()); Assert.Equal(expected, new StringBuilder().AppendJoin(separator, Enumerable.Range(1, 3)).ToString()); - Assert.Equal(expected, new StringBuilder().AppendJoin(separator, new string[] { "1", "2", "3" }).ToString()); + Assert.Equal(expected, new StringBuilder().AppendJoin(separator, stringValues).ToString()); + Assert.Equal(expected, new StringBuilder().AppendJoin(separator, (ReadOnlySpan)stringValues).ToString()); } @@ -1825,12 +1867,16 @@ public static void AppendJoin_NoValues_NoSpareCapacity_DoesNotThrow(string separ if (separator?.Length == 1) { CreateBuilderWithNoSpareCapacity().AppendJoin(separator[0], values); + CreateBuilderWithNoSpareCapacity().AppendJoin(separator[0], (ReadOnlySpan)values); CreateBuilderWithNoSpareCapacity().AppendJoin(separator[0], enumerable); CreateBuilderWithNoSpareCapacity().AppendJoin(separator[0], stringValues); + CreateBuilderWithNoSpareCapacity().AppendJoin(separator[0], (ReadOnlySpan)stringValues); } CreateBuilderWithNoSpareCapacity().AppendJoin(separator, values); + CreateBuilderWithNoSpareCapacity().AppendJoin(separator, (ReadOnlySpan)values); CreateBuilderWithNoSpareCapacity().AppendJoin(separator, enumerable); CreateBuilderWithNoSpareCapacity().AppendJoin(separator, stringValues); + CreateBuilderWithNoSpareCapacity().AppendJoin(separator, (ReadOnlySpan)stringValues); } [Theory] @@ -1849,12 +1895,16 @@ public static void AppendJoin_NoSpareCapacity_ThrowsArgumentOutOfRangeException( if (separator?.Length == 1) { AssertExtensions.Throws(s_noCapacityParamName, () => CreateBuilderWithNoSpareCapacity().AppendJoin(separator[0], values)); + AssertExtensions.Throws(s_noCapacityParamName, () => CreateBuilderWithNoSpareCapacity().AppendJoin(separator[0], (ReadOnlySpan)values)); AssertExtensions.Throws(s_noCapacityParamName, () => CreateBuilderWithNoSpareCapacity().AppendJoin(separator[0], enumerable)); AssertExtensions.Throws(s_noCapacityParamName, () => CreateBuilderWithNoSpareCapacity().AppendJoin(separator[0], stringValues)); + AssertExtensions.Throws(s_noCapacityParamName, () => CreateBuilderWithNoSpareCapacity().AppendJoin(separator[0], (ReadOnlySpan)stringValues)); } AssertExtensions.Throws(s_noCapacityParamName, () => CreateBuilderWithNoSpareCapacity().AppendJoin(separator, values)); + AssertExtensions.Throws(s_noCapacityParamName, () => CreateBuilderWithNoSpareCapacity().AppendJoin(separator, (ReadOnlySpan)values)); AssertExtensions.Throws(s_noCapacityParamName, () => CreateBuilderWithNoSpareCapacity().AppendJoin(separator, enumerable)); AssertExtensions.Throws(s_noCapacityParamName, () => CreateBuilderWithNoSpareCapacity().AppendJoin(separator, stringValues)); + AssertExtensions.Throws(s_noCapacityParamName, () => CreateBuilderWithNoSpareCapacity().AppendJoin(separator, (ReadOnlySpan)stringValues)); } [Theory] diff --git a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/CancellationTokenTests.cs b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/CancellationTokenTests.cs index ba75f617bf0b5d..bc66b33cd9b68a 100644 --- a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/CancellationTokenTests.cs +++ b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/CancellationTokenTests.cs @@ -207,6 +207,7 @@ public static void TokenSourceDispose_Negative() //shouldn't throw CancellationTokenSource.CreateLinkedTokenSource(new[] { token, token }); + CancellationTokenSource.CreateLinkedTokenSource((ReadOnlySpan)new[] { token, token }); } /// @@ -431,15 +432,20 @@ public static void CreateLinkedTokenSource_Simple_TwoToken() "CreateLinkedToken_Simple_TwoToken: The combined token should now be signalled"); } - [Fact] - public static void CreateLinkedTokenSource_Simple_MultiToken() + [Theory] + [InlineData(false)] + [InlineData(true)] + public static void CreateLinkedTokenSource_Simple_MultiToken(bool useSpan) { CancellationTokenSource signal1 = new CancellationTokenSource(); CancellationTokenSource signal2 = new CancellationTokenSource(); CancellationTokenSource signal3 = new CancellationTokenSource(); //Neither token is signalled. - CancellationTokenSource combined = CancellationTokenSource.CreateLinkedTokenSource(new[] { signal1.Token, signal2.Token, signal3.Token }); + CancellationTokenSource combined = useSpan ? + CancellationTokenSource.CreateLinkedTokenSource((ReadOnlySpan)new[] { signal1.Token, signal2.Token, signal3.Token }) : + CancellationTokenSource.CreateLinkedTokenSource(new[] { signal1.Token, signal2.Token, signal3.Token }); + Assert.False(combined.IsCancellationRequested, "CreateLinkedToken_Simple_MultiToken: The combined token should start unsignalled"); diff --git a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/MethodCoverage.cs b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/MethodCoverage.cs index 2634e8340ef9ec..01af9fb525c035 100644 --- a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/MethodCoverage.cs +++ b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/MethodCoverage.cs @@ -188,14 +188,30 @@ public static void Task_WhenAny_TwoTasks_InvalidArgs_Throws() AssertExtensions.Throws("task2", () => Task.WhenAny(Task.FromResult(2), null)); } + [Fact] + public static void Task_WhenAny_NullTaskElement_Throws() + { + AssertExtensions.Throws("tasks", () => { Task.WhenAny(new Task[] { null }); }); + AssertExtensions.Throws("tasks", () => { Task.WhenAny((ReadOnlySpan)new Task[] { null }); }); + AssertExtensions.Throws("tasks", () => { Task.WhenAny(NullElementIterator()); }); + + AssertExtensions.Throws("tasks", () => { Task.WhenAny(new Task[] { null }); }); + AssertExtensions.Throws("tasks", () => { Task.WhenAny((ReadOnlySpan>)new Task[] { null }); }); + AssertExtensions.Throws("tasks", () => { Task.WhenAny(NullElementIterator>()); }); + + static IEnumerable NullElementIterator() where T : Task { yield return null; } + } + [Fact] public static void Task_WhenAny_NoTasks_Throws() { AssertExtensions.Throws("tasks", () => { Task.WhenAny(new Task[0]); }); + AssertExtensions.Throws("tasks", () => { Task.WhenAny(ReadOnlySpan.Empty); }); AssertExtensions.Throws("tasks", () => { Task.WhenAny(new List()); }); AssertExtensions.Throws("tasks", () => { Task.WhenAny(EmptyIterator()); }); AssertExtensions.Throws("tasks", () => { Task.WhenAny(new Task[0]); }); + AssertExtensions.Throws("tasks", () => { Task.WhenAny(ReadOnlySpan>.Empty); }); AssertExtensions.Throws("tasks", () => { Task.WhenAny(new List>()); }); AssertExtensions.Throws("tasks", () => { Task.WhenAny(EmptyIterator>()); }); @@ -353,6 +369,36 @@ public static void FromAsync() mre2.WaitOne(); } + [Fact] + public static void Task_WaitAll_NullArgument_Throws() + { + AssertExtensions.Throws("tasks", () => { Task.WaitAll((Task[])null); }); + AssertExtensions.Throws("tasks", () => { Task.WaitAll((Task[])null, CancellationToken.None); }); + AssertExtensions.Throws("tasks", () => { Task.WaitAll((Task[])null, 30_000); }); + AssertExtensions.Throws("tasks", () => { Task.WaitAll((Task[])null, TimeSpan.FromSeconds(30)); }); + AssertExtensions.Throws("tasks", () => { Task.WaitAll((Task[])null, 30_000, CancellationToken.None); }); + } + + [Fact] + public static void Task_WaitAll_NullTaskElement_Throws() + { + Task[] nullElement = [null]; + AssertExtensions.Throws("tasks", () => { Task.WaitAll(nullElement); }); + AssertExtensions.Throws("tasks", () => { Task.WaitAll((ReadOnlySpan)nullElement); }); + AssertExtensions.Throws("tasks", () => { Task.WaitAll(nullElement, CancellationToken.None); }); + AssertExtensions.Throws("tasks", () => { Task.WaitAll(nullElement, 30_000); }); + AssertExtensions.Throws("tasks", () => { Task.WaitAll(nullElement, TimeSpan.FromSeconds(30)); }); + AssertExtensions.Throws("tasks", () => { Task.WaitAll(nullElement, 30_000, CancellationToken.None); }); + } + + [Fact] + public static void Task_WaitAll_InvalidArgument_Throws() + { + Assert.Throws(() => Task.WaitAll([Task.Factory.StartNew(() => { })], -2)); + Assert.Throws(() => Task.WaitAll([Task.Factory.StartNew(() => { })], -2, CancellationToken.None)); + Assert.Throws(() => Task.WaitAll([Task.Factory.StartNew(() => { })], TimeSpan.FromMilliseconds(-2))); + } + [Fact] public static void Task_WhenAll_NullArgument_Throws() { @@ -363,14 +409,30 @@ public static void Task_WhenAll_NullArgument_Throws() AssertExtensions.Throws("tasks", () => { Task.WhenAll((IEnumerable>)null); }); } + [Fact] + public static void Task_WhenAll_NullTaskElement_Throws() + { + AssertExtensions.Throws("tasks", () => { Task.WhenAll(new Task[] { null }); }); + AssertExtensions.Throws("tasks", () => { Task.WhenAll((ReadOnlySpan)new Task[] { null }); }); + AssertExtensions.Throws("tasks", () => { Task.WhenAll(NullElementIterator()); }); + + AssertExtensions.Throws("tasks", () => { Task.WhenAll(new Task[] { null }); }); + AssertExtensions.Throws("tasks", () => { Task.WhenAll((ReadOnlySpan>)new Task[] { null }); }); + AssertExtensions.Throws("tasks", () => { Task.WhenAll(NullElementIterator>()); }); + + static IEnumerable NullElementIterator() where T : Task { yield return null; } + } + [Fact] public static void Task_WhenAll_NoTasks_IsCompletedSuccessfully() { Assert.True(Task.WhenAll(new Task[0]).IsCompletedSuccessfully); + Assert.True(Task.WhenAll(ReadOnlySpan.Empty).IsCompletedSuccessfully); Assert.True(Task.WhenAll(new List()).IsCompletedSuccessfully); Assert.True(Task.WhenAll(EmptyIterator()).IsCompletedSuccessfully); AssertIsCompletedWithEmptyResult(Task.WhenAll(new Task[0])); + AssertIsCompletedWithEmptyResult(Task.WhenAll(ReadOnlySpan>.Empty)); AssertIsCompletedWithEmptyResult(Task.WhenAll(new List>())); AssertIsCompletedWithEmptyResult(Task.WhenAll(EmptyIterator>())); diff --git a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/Task/TaskRtTests_Core.cs b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/Task/TaskRtTests_Core.cs index 088c45ddc116fc..1225ca6433a291 100644 --- a/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/Task/TaskRtTests_Core.cs +++ b/src/libraries/System.Runtime/tests/System.Threading.Tasks.Tests/Task/TaskRtTests_Core.cs @@ -1338,11 +1338,6 @@ public static void RunTaskWaitAnyTests_WithCancellationTokenTests() [OuterLoop] public static void RunTaskWaitAllTests() { - Assert.Throws(() => Task.WaitAll((Task[])null)); - AssertExtensions.Throws("tasks", () => Task.WaitAll(new Task[] { null })); - Assert.Throws(() => Task.WaitAll(new Task[] { Task.Factory.StartNew(() => { }) }, -2)); - Assert.Throws(() => Task.WaitAll(new Task[] { Task.Factory.StartNew(() => { }) }, TimeSpan.FromMilliseconds(-2))); - ThreadPoolHelpers.EnsureMinThreadsAtLeast(10); RunTaskWaitAllTest(false, 1); RunTaskWaitAllTest(false, 10); diff --git a/src/libraries/System.Security.Claims/src/System.Security.Claims.csproj b/src/libraries/System.Security.Claims/src/System.Security.Claims.csproj index e1e3b9838c4c25..4376906dbbc4bf 100644 --- a/src/libraries/System.Security.Claims/src/System.Security.Claims.csproj +++ b/src/libraries/System.Security.Claims/src/System.Security.Claims.csproj @@ -18,6 +18,7 @@ + diff --git a/src/libraries/System.Text.Json/ref/System.Text.Json.cs b/src/libraries/System.Text.Json/ref/System.Text.Json.cs index 0ff5bdc90a66ee..e16277de475290 100644 --- a/src/libraries/System.Text.Json/ref/System.Text.Json.cs +++ b/src/libraries/System.Text.Json/ref/System.Text.Json.cs @@ -659,7 +659,9 @@ namespace System.Text.Json.Nodes public sealed partial class JsonArray : System.Text.Json.Nodes.JsonNode, System.Collections.Generic.ICollection, System.Collections.Generic.IEnumerable, System.Collections.Generic.IList, System.Collections.IEnumerable { public JsonArray(System.Text.Json.Nodes.JsonNodeOptions? options = default(System.Text.Json.Nodes.JsonNodeOptions?)) { } + public JsonArray(System.Text.Json.Nodes.JsonNodeOptions options, System.ReadOnlySpan items) { } public JsonArray(System.Text.Json.Nodes.JsonNodeOptions options, params System.Text.Json.Nodes.JsonNode?[] items) { } + public JsonArray(System.ReadOnlySpan items) { } public JsonArray(params System.Text.Json.Nodes.JsonNode?[] items) { } public int Count { get { throw null; } } bool System.Collections.Generic.ICollection.IsReadOnly { get { throw null; } } @@ -1352,6 +1354,7 @@ public enum JsonTypeInfoKind } public static partial class JsonTypeInfoResolver { + public static System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver Combine(System.ReadOnlySpan resolvers) { throw null; } public static System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver Combine(params System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver?[] resolvers) { throw null; } public static System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver WithAddedModifier(this System.Text.Json.Serialization.Metadata.IJsonTypeInfoResolver resolver, System.Action modifier) { throw null; } } diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonArray.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonArray.cs index f8429dad3ffeef..600249db627b2b 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonArray.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Nodes/JsonArray.cs @@ -39,6 +39,16 @@ public JsonArray(JsonNodeOptions options, params JsonNode?[] items) : base(optio InitializeFromArray(items); } + /// + /// Initializes a new instance of the class that contains items from the specified params span. + /// + /// Options to control the behavior. + /// The items to add to the new . + public JsonArray(JsonNodeOptions options, /*params*/ ReadOnlySpan items) : base(options) + { + InitializeFromSpan(items); + } + /// /// Initializes a new instance of the class that contains items from the specified array. /// @@ -48,6 +58,15 @@ public JsonArray(params JsonNode?[] items) : base() InitializeFromArray(items); } + /// + /// Initializes a new instance of the class that contains items from the specified span. + /// + /// The items to add to the new . + public JsonArray(/*params*/ ReadOnlySpan items) : base() + { + InitializeFromSpan(items); + } + internal override JsonValueKind GetValueKindCore() => JsonValueKind.Array; internal override JsonNode DeepCloneCore() @@ -129,9 +148,30 @@ private void InitializeFromArray(JsonNode?[] items) { var list = new List(items); - for (int i = 0; i < items.Length; i++) + for (int i = 0; i < list.Count; i++) + { + list[i]?.AssignParent(this); + } + + _list = list; + } + + private void InitializeFromSpan(ReadOnlySpan items) + { + List list = new(items.Length); + +#if NET8_0_OR_GREATER + list.AddRange(items); +#else + foreach (JsonNode? item in items) + { + list.Add(item); + } +#endif + + for (int i = 0; i < list.Count; i++) { - items[i]?.AssignParent(this); + list[i]?.AssignParent(this); } _list = list; diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoResolver.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoResolver.cs index 216329609f3b90..9bd63188cbe525 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoResolver.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/Metadata/JsonTypeInfoResolver.cs @@ -30,6 +30,25 @@ public static IJsonTypeInfoResolver Combine(params IJsonTypeInfoResolver?[] reso ThrowHelper.ThrowArgumentNullException(nameof(resolvers)); } + return Combine((ReadOnlySpan)resolvers); + } + + /// + /// Combines multiple sources into one. + /// + /// Sequence of contract resolvers to be queried for metadata. + /// A combining results from . + /// + /// The combined resolver will query each of in the specified order, + /// returning the first result that is non-null. If all return null, + /// then the combined resolver will also return . + /// + /// Can be used to combine multiple sources, + /// which typically define contract metadata for small subsets of types. + /// It can also be used to fall back to wherever necessary. + /// + public static IJsonTypeInfoResolver Combine(/*params*/ ReadOnlySpan resolvers) + { var resolverChain = new JsonTypeInfoResolverChain(); foreach (IJsonTypeInfoResolver? resolver in resolvers) { diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs index ef51703958151c..3b8a7b8c4b6b4e 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/JsonNode/JsonArrayTests.cs @@ -12,6 +12,25 @@ namespace System.Text.Json.Nodes.Tests { public static class JsonArrayTests { + [Fact] + public static void ParamsContructors() + { + JsonArray expectedArray = [41, 42, 43]; + JsonNodeOptions options = new JsonNodeOptions { PropertyNameCaseInsensitive = true }; + + Verify(new JsonArray(new JsonNode[] { 41, 42, 43 }), null); + Verify(new JsonArray((ReadOnlySpan)new JsonNode[] { 41, 42, 43 }), null); + Verify(new JsonArray(options, new JsonNode[] { 41, 42, 43 }), options); + Verify(new JsonArray(options, (ReadOnlySpan)new JsonNode[] { 41, 42, 43 }), options); + + void Verify(JsonArray jsonArray, JsonNodeOptions? expectedOptions) + { + Assert.Equal(expectedArray.Count, jsonArray.Count); + JsonNodeTests.AssertDeepEqual(expectedArray, jsonArray); + Assert.Equal(expectedOptions, jsonArray.Options); + } + } + [Fact] public static void FromElement() { @@ -34,7 +53,7 @@ public static void FromElement() public static void FromElement_WrongNodeTypeThrows(string json) { using (JsonDocument document = JsonDocument.Parse(json)) - Assert.Throws(() => JsonArray.Create(document.RootElement)); + Assert.Throws(() => JsonArray.Create(document.RootElement)); } [Fact] diff --git a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonTypeInfoResolverTests.cs b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonTypeInfoResolverTests.cs index 9136555b0fd9c9..17e1a73c970a00 100644 --- a/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonTypeInfoResolverTests.cs +++ b/src/libraries/System.Text.Json/tests/System.Text.Json.Tests/Serialization/MetadataTests/JsonTypeInfoResolverTests.cs @@ -7,32 +7,46 @@ namespace System.Text.Json.Serialization.Tests { - public static partial class JsonTypeInfoResolverTests + public class JsonTypeInfoResolverCombineArrayTests : JsonTypeInfoResolverCombineTests { + public override IJsonTypeInfoResolver Combine(params IJsonTypeInfoResolver?[] resolvers) => + JsonTypeInfoResolver.Combine(resolvers); + [Fact] - public static void CombineNullArgument() + public void CombineNullArgument() { IJsonTypeInfoResolver[] resolvers = null; - Assert.Throws(() => JsonTypeInfoResolver.Combine(resolvers)); + Assert.Throws(() => Combine(resolvers)); } + } + + public class JsonTypeInfoResolverCombineSpanTests : JsonTypeInfoResolverCombineTests + { + public override IJsonTypeInfoResolver Combine(params IJsonTypeInfoResolver?[] resolvers) => + JsonTypeInfoResolver.Combine((ReadOnlySpan)resolvers); + } + + public abstract class JsonTypeInfoResolverCombineTests + { + public abstract IJsonTypeInfoResolver Combine(params IJsonTypeInfoResolver?[] resolvers); [Fact] - public static void Combine_ShouldFlattenResolvers() + public void Combine_ShouldFlattenResolvers() { DefaultJsonTypeInfoResolver nonNullResolver1 = new(); DefaultJsonTypeInfoResolver nonNullResolver2 = new(); DefaultJsonTypeInfoResolver nonNullResolver3 = new(); - ValidateCombinations(Array.Empty(), JsonTypeInfoResolver.Combine()); - ValidateCombinations(Array.Empty(), JsonTypeInfoResolver.Combine(new IJsonTypeInfoResolver[] { null })); - ValidateCombinations(Array.Empty(), JsonTypeInfoResolver.Combine(null, null)); - ValidateCombinations(new[] { nonNullResolver1 }, JsonTypeInfoResolver.Combine(nonNullResolver1, null)); - ValidateCombinations(new[] { nonNullResolver1, nonNullResolver2 }, JsonTypeInfoResolver.Combine(nonNullResolver1, nonNullResolver2, null)); - ValidateCombinations(new[] { nonNullResolver1, nonNullResolver2 }, JsonTypeInfoResolver.Combine(nonNullResolver1, null, nonNullResolver2)); - ValidateCombinations(new[] { nonNullResolver1, nonNullResolver2, nonNullResolver3 }, JsonTypeInfoResolver.Combine(JsonTypeInfoResolver.Combine(JsonTypeInfoResolver.Combine(nonNullResolver1), nonNullResolver2), nonNullResolver3)); - ValidateCombinations(new[] { nonNullResolver1, nonNullResolver2, nonNullResolver3 }, JsonTypeInfoResolver.Combine(JsonTypeInfoResolver.Combine(nonNullResolver1, null, nonNullResolver2), nonNullResolver3)); + ValidateCombinations(Array.Empty(), Combine()); + ValidateCombinations(Array.Empty(), Combine(new IJsonTypeInfoResolver[] { null })); + ValidateCombinations(Array.Empty(), Combine(null, null)); + ValidateCombinations(new[] { nonNullResolver1 }, Combine(nonNullResolver1, null)); + ValidateCombinations(new[] { nonNullResolver1, nonNullResolver2 }, Combine(nonNullResolver1, nonNullResolver2, null)); + ValidateCombinations(new[] { nonNullResolver1, nonNullResolver2 }, Combine(nonNullResolver1, null, nonNullResolver2)); + ValidateCombinations(new[] { nonNullResolver1, nonNullResolver2, nonNullResolver3 }, Combine(Combine(Combine(nonNullResolver1), nonNullResolver2), nonNullResolver3)); + ValidateCombinations(new[] { nonNullResolver1, nonNullResolver2, nonNullResolver3 }, Combine(Combine(nonNullResolver1, null, nonNullResolver2), nonNullResolver3)); - static void ValidateCombinations(IJsonTypeInfoResolver[] expectedResolvers, IJsonTypeInfoResolver combinedResolver) + void ValidateCombinations(IJsonTypeInfoResolver[] expectedResolvers, IJsonTypeInfoResolver combinedResolver) { if (expectedResolvers.Length == 1) { @@ -46,9 +60,9 @@ static void ValidateCombinations(IJsonTypeInfoResolver[] expectedResolvers, IJso } [Fact] - public static void CombiningZeroResolversProducesValidResolver() + public void CombiningZeroResolversProducesValidResolver() { - IJsonTypeInfoResolver resolver = JsonTypeInfoResolver.Combine(); + IJsonTypeInfoResolver resolver = Combine(); Assert.NotNull(resolver); // calling twice to make sure we get the same answer @@ -57,7 +71,7 @@ public static void CombiningZeroResolversProducesValidResolver() } [Fact] - public static void CombiningSingleResolverProducesSameAnswersAsInputResolver() + public void CombiningSingleResolverProducesSameAnswersAsInputResolver() { JsonSerializerOptions options = new(); JsonTypeInfo t1 = JsonTypeInfo.CreateJsonTypeInfo(typeof(int), options); @@ -74,7 +88,7 @@ public static void CombiningSingleResolverProducesSameAnswersAsInputResolver() return null; }); - IJsonTypeInfoResolver combined = JsonTypeInfoResolver.Combine(resolver); + IJsonTypeInfoResolver combined = Combine(resolver); Assert.Same(t1, combined.GetTypeInfo(typeof(int), options)); Assert.Same(t2, combined.GetTypeInfo(typeof(uint), options)); @@ -84,7 +98,7 @@ public static void CombiningSingleResolverProducesSameAnswersAsInputResolver() } [Fact] - public static void CombiningUsesAndRespectsAllResolversInOrder() + public void CombiningUsesAndRespectsAllResolversInOrder() { JsonSerializerOptions options = new(); JsonTypeInfo t1 = JsonTypeInfo.CreateJsonTypeInfo(typeof(int), options); @@ -121,7 +135,7 @@ public static void CombiningUsesAndRespectsAllResolversInOrder() return null; }); - IJsonTypeInfoResolver combined = JsonTypeInfoResolver.Combine(r1, r2, r3); + IJsonTypeInfoResolver combined = Combine(r1, r2, r3); resolverId = 1; Assert.Same(t1, combined.GetTypeInfo(typeof(int), options)); @@ -144,7 +158,7 @@ public static void CombiningUsesAndRespectsAllResolversInOrder() Assert.Equal(4, resolverId); } - private static IList GetAndValidateCombinedResolvers(IJsonTypeInfoResolver resolver) + private IList GetAndValidateCombinedResolvers(IJsonTypeInfoResolver resolver) { var list = (IList)resolver; @@ -155,8 +169,12 @@ private static IList GetAndValidateCombinedResolvers(IJso return list; } + } + + public class JsonTypeInfoResolverTests + { [Fact] - public static void WithAddedModifier_CallsModifierOnResolvedMetadata() + public void WithAddedModifier_CallsModifierOnResolvedMetadata() { int modifierInvocationCount = 0; JsonSerializerOptions options = new(); @@ -175,11 +193,11 @@ public static void WithAddedModifier_CallsModifierOnResolvedMetadata() } [Fact] - public static void WithAddedModifier_DoesNotCallModifierOnUnResolvedMetadata() + public void WithAddedModifier_DoesNotCallModifierOnUnResolvedMetadata() { int modifierInvocationCount = 0; JsonSerializerOptions options = new(); - TestResolver resolver = new((_,_) => null); + TestResolver resolver = new((_, _) => null); IJsonTypeInfoResolver resolverWithModifier = resolver.WithAddedModifier(_ => modifierInvocationCount++); @@ -191,7 +209,7 @@ public static void WithAddedModifier_DoesNotCallModifierOnUnResolvedMetadata() } [Fact] - public static void WithAddedModifier_CanChainMultipleModifiers() + public void WithAddedModifier_CanChainMultipleModifiers() { int modifier1InvocationCount = 0; int modifier2InvocationCount = 0; @@ -208,7 +226,7 @@ public static void WithAddedModifier_CanChainMultipleModifiers() } [Fact] - public static void WithAddedModifier_ChainingDoesNotMutateIntermediateResolvers() + public void WithAddedModifier_ChainingDoesNotMutateIntermediateResolvers() { int modifier1InvocationCount = 0; int modifier2InvocationCount = 0; @@ -233,7 +251,7 @@ public static void WithAddedModifier_ChainingDoesNotMutateIntermediateResolvers( } [Fact] - public static void WithAddedModifier_ThrowsOnNullArguments() + public void WithAddedModifier_ThrowsOnNullArguments() { TestResolver resolver = new(JsonTypeInfo.CreateJsonTypeInfo); @@ -242,7 +260,7 @@ public static void WithAddedModifier_ThrowsOnNullArguments() } [Fact] - public static void NullResolver_ReturnsObjectMetadata() + public void NullResolver_ReturnsObjectMetadata() { var options = new JsonSerializerOptions(); var resolver = new NullResolver(); diff --git a/src/libraries/System.Threading.Channels/src/System.Threading.Channels.csproj b/src/libraries/System.Threading.Channels/src/System.Threading.Channels.csproj index 21e101ecd6d94e..c058b0b216b17a 100644 --- a/src/libraries/System.Threading.Channels/src/System.Threading.Channels.csproj +++ b/src/libraries/System.Threading.Channels/src/System.Threading.Channels.csproj @@ -50,6 +50,7 @@ System.Threading.Channel<T> + diff --git a/src/libraries/System.Threading.Tasks.Dataflow/src/System.Threading.Tasks.Dataflow.csproj b/src/libraries/System.Threading.Tasks.Dataflow/src/System.Threading.Tasks.Dataflow.csproj index 2a1eaf2804e355..21e1af10705020 100644 --- a/src/libraries/System.Threading.Tasks.Dataflow/src/System.Threading.Tasks.Dataflow.csproj +++ b/src/libraries/System.Threading.Tasks.Dataflow/src/System.Threading.Tasks.Dataflow.csproj @@ -80,6 +80,7 @@ System.Threading.Tasks.Dataflow.WriteOnceBlock<T> + diff --git a/src/libraries/System.Threading.Tasks.Parallel/src/System.Threading.Tasks.Parallel.csproj b/src/libraries/System.Threading.Tasks.Parallel/src/System.Threading.Tasks.Parallel.csproj index 3278b9f2c5e19d..32d0f7c8cf6a3f 100644 --- a/src/libraries/System.Threading.Tasks.Parallel/src/System.Threading.Tasks.Parallel.csproj +++ b/src/libraries/System.Threading.Tasks.Parallel/src/System.Threading.Tasks.Parallel.csproj @@ -24,6 +24,7 @@ +