From d02aad0e9be00b648b3ca82d3f04b49cb9be5cdd Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Tue, 25 May 2021 20:47:23 -0400 Subject: [PATCH] Make String.{Try}CopyTo public --- .../Common/src/System/Net/SocketAddress.cs | 2 +- .../src/System/Text/ValueStringBuilder.cs | 10 +++--- .../Common/tests/Tests/System/StringTests.cs | 36 +++++++++++++++++++ .../Diagnostics/XmlWriterTraceListener.cs | 4 +-- .../src/System/String.cs | 7 ++-- .../Xml/Serialization/CodeIdentifier.cs | 4 +-- .../System.Runtime/ref/System.Runtime.cs | 2 ++ .../Security/Cryptography/PemEncoding.cs | 4 +-- .../Pal.Unix/OpenSslX509ChainProcessor.cs | 4 +-- .../JsonCamelCaseNamingPolicy.cs | 6 +++- .../Text/RegularExpressions/RegexCharClass.cs | 4 +-- .../System/Web/Util/Utf16StringValidator.cs | 2 +- 12 files changed, 61 insertions(+), 24 deletions(-) diff --git a/src/libraries/Common/src/System/Net/SocketAddress.cs b/src/libraries/Common/src/System/Net/SocketAddress.cs index b5c8f955a70dc8..4575029d1de272 100644 --- a/src/libraries/Common/src/System/Net/SocketAddress.cs +++ b/src/libraries/Common/src/System/Net/SocketAddress.cs @@ -251,7 +251,7 @@ public override string ToString() stackalloc char[256] : new char[maxLength]; - familyString.AsSpan().CopyTo(result); + familyString.CopyTo(result); int length = familyString.Length; result[length++] = ':'; diff --git a/src/libraries/Common/src/System/Text/ValueStringBuilder.cs b/src/libraries/Common/src/System/Text/ValueStringBuilder.cs index fbba96711347e1..f2cf107c5418a5 100644 --- a/src/libraries/Common/src/System/Text/ValueStringBuilder.cs +++ b/src/libraries/Common/src/System/Text/ValueStringBuilder.cs @@ -158,10 +158,9 @@ public void Insert(int index, string? s) int remaining = _pos - index; _chars.Slice(index, remaining).CopyTo(_chars.Slice(index + count)); -#if SYSTEM_PRIVATE_CORELIB s -#else - s.AsSpan() +#if !NET6_0_OR_GREATER + .AsSpan() #endif .CopyTo(_chars.Slice(index)); _pos += count; @@ -210,10 +209,9 @@ private void AppendSlow(string s) Grow(s.Length); } -#if SYSTEM_PRIVATE_CORELIB s -#else - s.AsSpan() +#if !NET6_0_OR_GREATER + .AsSpan() #endif .CopyTo(_chars.Slice(pos)); _pos += s.Length; diff --git a/src/libraries/Common/tests/Tests/System/StringTests.cs b/src/libraries/Common/tests/Tests/System/StringTests.cs index b3ab3defdb967d..91ff8a5dcd3c94 100644 --- a/src/libraries/Common/tests/Tests/System/StringTests.cs +++ b/src/libraries/Common/tests/Tests/System/StringTests.cs @@ -482,6 +482,42 @@ public static void CopyTo_Invalid() AssertExtensions.Throws("sourceIndex", () => s.CopyTo(0, dst, 0, 6)); } + [Theory] + [InlineData("", 0)] + [InlineData("", 1)] + [InlineData("a", 1)] + [InlineData("a", 0)] + [InlineData("a", 2)] + [InlineData("abc", 2)] + [InlineData("abc", 3)] + [InlineData("abc", 4)] + [InlineData("Hello world", 20)] + public static void CopyTo_Span(string s, int destinationLength) + { + char[] destination = new char[destinationLength]; + + if (s.Length > destinationLength) + { + AssertExtensions.Throws("destination", () => s.CopyTo(destination)); + Assert.All(destination, c => Assert.Equal(0, c)); + + Assert.False(s.TryCopyTo(destination)); + Assert.All(destination, c => Assert.Equal(0, c)); + } + else + { + s.CopyTo(destination); + Assert.Equal(s, new Span(destination, 0, s.Length).ToString()); + Assert.All(destination.AsSpan(s.Length).ToArray(), c => Assert.Equal(0, c)); + + Array.Clear(destination, 0, destination.Length); + + Assert.True(s.TryCopyTo(destination)); + Assert.Equal(s, new Span(destination, 0, s.Length).ToString()); + Assert.All(destination.AsSpan(s.Length).ToArray(), c => Assert.Equal(0, c)); + } + } + public static IEnumerable Compare_TestData() { // CurrentCulture diff --git a/src/libraries/System.Diagnostics.TextWriterTraceListener/src/System/Diagnostics/XmlWriterTraceListener.cs b/src/libraries/System.Diagnostics.TextWriterTraceListener/src/System/Diagnostics/XmlWriterTraceListener.cs index a4820d96289f9a..70275389908575 100644 --- a/src/libraries/System.Diagnostics.TextWriterTraceListener/src/System/Diagnostics/XmlWriterTraceListener.cs +++ b/src/libraries/System.Diagnostics.TextWriterTraceListener/src/System/Diagnostics/XmlWriterTraceListener.cs @@ -71,14 +71,14 @@ public override void Fail(string? message, string? detailMessage) TraceEvent(null, SR.TraceAsTraceSource, TraceEventType.Error, 0, string.Create(length, (message, detailMessage), (dst, v) => { - ReadOnlySpan prefix = v.message; + string prefix = v.message; prefix.CopyTo(dst); if (v.detailMessage != null) { dst[prefix.Length] = ' '; - ReadOnlySpan detail = v.detailMessage; + string detail = v.detailMessage; detail.CopyTo(dst.Slice(prefix.Length + 1, detail.Length)); } })); diff --git a/src/libraries/System.Private.CoreLib/src/System/String.cs b/src/libraries/System.Private.CoreLib/src/System/String.cs index ea51bafb0e76dc..25ac8df2c582e1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/String.cs +++ b/src/libraries/System.Private.CoreLib/src/System/String.cs @@ -461,14 +461,11 @@ public unsafe void CopyTo(int sourceIndex, char[] destination, int destinationIn elementCount: (uint)count); } - // TODO: https://github.com/dotnet/runtime/issues/51061 - // Make these {Try}CopyTo methods public and use throughout dotnet/runtime. - /// Copies the contents of this string into the destination span. /// The span into which to copy this string's contents. /// The destination span is shorter than the source string. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal void CopyTo(Span destination) + public void CopyTo(Span destination) { if ((uint)Length <= (uint)destination.Length) { @@ -484,7 +481,7 @@ internal void CopyTo(Span destination) /// The span into which to copy this string's contents. /// true if the data was copied; false if the destination was too short to fit the contents of the string. [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal bool TryCopyTo(Span destination) + public bool TryCopyTo(Span destination) { bool retVal = false; if ((uint)Length <= (uint)destination.Length) diff --git a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifier.cs b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifier.cs index 1938972d79eac0..6de8502c2d76b5 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifier.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/Serialization/CodeIdentifier.cs @@ -35,7 +35,7 @@ public static string MakePascal(string identifier) { return string.Create(identifier.Length, identifier, static (buffer, identifier) => { - identifier.AsSpan().CopyTo(buffer); + identifier.CopyTo(buffer); buffer[0] = char.ToUpperInvariant(buffer[0]); // convert only first char to uppercase; leave all else as-is }); } @@ -59,7 +59,7 @@ public static string MakeCamel(string identifier) { return string.Create(identifier.Length, identifier, static (buffer, identifier) => { - identifier.AsSpan().CopyTo(buffer); + identifier.CopyTo(buffer); buffer[0] = char.ToLowerInvariant(buffer[0]); // convert only first char to lowercase; leave all else as-is }); } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 6a832718a37ee8..9b82d81e5fdcf6 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -3632,6 +3632,7 @@ public unsafe String(sbyte* value, int startIndex, int length, System.Text.Encod [System.ObsoleteAttribute("This API should not be used to create mutable strings. See https://go.microsoft.com/fwlink/?linkid=2084035 for alternatives.")] public static System.String Copy(System.String str) { throw null; } public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) { } + public void CopyTo(System.Span destination) { } public static System.String Create(int length, TState state, System.Buffers.SpanAction action) { throw null; } public bool EndsWith(char value) { throw null; } public bool EndsWith(System.String value) { throw null; } @@ -3767,6 +3768,7 @@ public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, in public System.String TrimStart() { throw null; } public System.String TrimStart(char trimChar) { throw null; } public System.String TrimStart(params char[]? 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 { diff --git a/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/PemEncoding.cs b/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/PemEncoding.cs index e4ae7fe472446d..005ef1cec09074 100644 --- a/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/PemEncoding.cs +++ b/src/libraries/System.Security.Cryptography.Encoding/src/System/Security/Cryptography/PemEncoding.cs @@ -168,9 +168,9 @@ static ReadOnlySpan WritePostEB(ReadOnlySpan label, Span desti { int size = PostEBPrefix.Length + label.Length + Ending.Length; Debug.Assert(destination.Length >= size); - PostEBPrefix.AsSpan().CopyTo(destination); + PostEBPrefix.CopyTo(destination); label.CopyTo(destination.Slice(PostEBPrefix.Length)); - Ending.AsSpan().CopyTo(destination.Slice(PostEBPrefix.Length + label.Length)); + Ending.CopyTo(destination.Slice(PostEBPrefix.Length + label.Length)); return destination.Slice(0, size); } } diff --git a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs index 6148b022a42cc8..7ec3e6f8f28ba4 100644 --- a/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs +++ b/src/libraries/System.Security.Cryptography.X509Certificates/src/Internal/Cryptography/Pal.Unix/OpenSslX509ChainProcessor.cs @@ -740,7 +740,7 @@ private static string UrlPathAppend(string baseUri, ReadOnlyMemory resourc (baseUri, resource), (buf, st) => { - st.baseUri.AsSpan().CopyTo(buf); + st.baseUri.CopyTo(buf); st.resource.Span.CopyTo(buf.Slice(st.baseUri.Length)); }); } @@ -750,7 +750,7 @@ private static string UrlPathAppend(string baseUri, ReadOnlyMemory resourc (baseUri, resource), (buf, st) => { - st.baseUri.AsSpan().CopyTo(buf); + st.baseUri.CopyTo(buf); buf[st.baseUri.Length] = '/'; st.resource.Span.CopyTo(buf.Slice(st.baseUri.Length + 1)); }); diff --git a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonCamelCaseNamingPolicy.cs b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonCamelCaseNamingPolicy.cs index 978ae04d568a74..decf0cce12d033 100644 --- a/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonCamelCaseNamingPolicy.cs +++ b/src/libraries/System.Text.Json/src/System/Text/Json/Serialization/JsonCamelCaseNamingPolicy.cs @@ -15,7 +15,11 @@ public override string ConvertName(string name) #if BUILDING_INBOX_LIBRARY return string.Create(name.Length, name, (chars, name) => { - name.AsSpan().CopyTo(chars); + name +#if !NET6_0_OR_GREATER + .AsSpan() +#endif + .CopyTo(chars); FixCasing(chars); }); #else diff --git a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs index 219fed81e301c3..f0565bbffe51c7 100644 --- a/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs +++ b/src/libraries/System.Text.RegularExpressions/src/System/Text/RegularExpressions/RegexCharClass.cs @@ -576,11 +576,11 @@ public static string ConvertOldStringsToClass(string set, string category) span[FlagsIndex] = '\0'; span[SetLengthIndex] = (char)state.set.Length; span[CategoryLengthIndex] = (char)state.category.Length; - state.set.AsSpan().CopyTo(span.Slice(SetStartIndex)); + state.set.CopyTo(span.Slice(SetStartIndex)); index = SetStartIndex + state.set.Length; } - state.category.AsSpan().CopyTo(span.Slice(index)); + state.category.CopyTo(span.Slice(index)); }); } diff --git a/src/libraries/System.Web.HttpUtility/src/System/Web/Util/Utf16StringValidator.cs b/src/libraries/System.Web.HttpUtility/src/System/Web/Util/Utf16StringValidator.cs index e20a42ea4cbe72..a9882aa14767ce 100644 --- a/src/libraries/System.Web.HttpUtility/src/System/Web/Util/Utf16StringValidator.cs +++ b/src/libraries/System.Web.HttpUtility/src/System/Web/Util/Utf16StringValidator.cs @@ -34,7 +34,7 @@ internal static string ValidateString(string input) // slow case: surrogates exist, so we need to validate them return string.Create(input.Length, (input, idxOfFirstSurrogate), (chars, state) => { - state.input.AsSpan().CopyTo(chars); + state.input.CopyTo(chars); for (int i = state.idxOfFirstSurrogate; i < chars.Length; i++) { char thisChar = chars[i];