From 6523116f1f8c4c41594cc484a482b972c5d8dba3 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 10 Apr 2025 17:05:34 +0300 Subject: [PATCH 1/2] Remove more unsafe from STJ --- .../Common/src/System/HexConverter.cs | 20 ++++++++++++++++--- .../src/System/Text/ValueStringBuilder.cs | 16 --------------- .../src/System/PercentEncodingHelper.cs | 4 ++-- .../src/System/UriHelper.cs | 2 +- 4 files changed, 20 insertions(+), 22 deletions(-) diff --git a/src/libraries/Common/src/System/HexConverter.cs b/src/libraries/Common/src/System/HexConverter.cs index 2ce0f59691fd0b..0fe533692bf2e6 100644 --- a/src/libraries/Common/src/System/HexConverter.cs +++ b/src/libraries/Common/src/System/HexConverter.cs @@ -178,7 +178,7 @@ public static void EncodeToUtf16(ReadOnlySpan bytes, Span chars, Cas } } - public static unsafe string ToString(ReadOnlySpan bytes, Casing casing = Casing.Upper) + public static string ToString(ReadOnlySpan bytes, Casing casing = Casing.Upper) { #if NETFRAMEWORK || NETSTANDARD2_0 Span result = bytes.Length > 16 ? @@ -192,12 +192,26 @@ public static unsafe string ToString(ReadOnlySpan bytes, Casing casing = C pos += 2; } return result.ToString(); +#elif NET9_0_OR_GREATER + SpanCasingPair args = new() { Bytes = bytes, Casing = casing }; + return string.Create(bytes.Length * 2, args, static (chars, args) => + EncodeToUtf16(args.Bytes, chars, args.Casing)); #else - return string.Create(bytes.Length * 2, (RosPtr: (IntPtr)(&bytes), casing), static (chars, args) => - EncodeToUtf16(*(ReadOnlySpan*)args.RosPtr, chars, args.casing)); + // .NET 8.0 path (doesn't support 'allow ref struct' feature) + unsafe + { + return string.Create(bytes.Length * 2, (RosPtr: (IntPtr)(&bytes), casing), static (chars, args) => + EncodeToUtf16(*(ReadOnlySpan*)args.RosPtr, chars, args.casing)); + } #endif } + private ref struct SpanCasingPair + { + public ReadOnlySpan Bytes { get; set; } + public Casing Casing { get; set; } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static char ToCharUpper(int value) { diff --git a/src/libraries/Common/src/System/Text/ValueStringBuilder.cs b/src/libraries/Common/src/System/Text/ValueStringBuilder.cs index 14aa77db3e57b0..e5c8610cd60dc5 100644 --- a/src/libraries/Common/src/System/Text/ValueStringBuilder.cs +++ b/src/libraries/Common/src/System/Text/ValueStringBuilder.cs @@ -235,22 +235,6 @@ public void Append(char c, int count) _pos += count; } - public unsafe void Append(char* value, int length) - { - int pos = _pos; - if (pos > _chars.Length - length) - { - Grow(length); - } - - Span dst = _chars.Slice(_pos, length); - for (int i = 0; i < dst.Length; i++) - { - dst[i] = *value++; - } - _pos += length; - } - public void Append(scoped ReadOnlySpan value) { int pos = _pos; diff --git a/src/libraries/System.Private.Uri/src/System/PercentEncodingHelper.cs b/src/libraries/System.Private.Uri/src/System/PercentEncodingHelper.cs index 67c43295fa581b..5b88088b59933b 100644 --- a/src/libraries/System.Private.Uri/src/System/PercentEncodingHelper.cs +++ b/src/libraries/System.Private.Uri/src/System/PercentEncodingHelper.cs @@ -93,7 +93,7 @@ public static unsafe int UnescapePercentEncodedUTF8Sequence(char* input, int len { if (charsToCopy != 0) { - dest.Append(input + totalCharsConsumed - charsToCopy, charsToCopy); + dest.Append(new ReadOnlySpan(input + totalCharsConsumed - charsToCopy, charsToCopy)); charsToCopy = 0; } @@ -167,7 +167,7 @@ public static unsafe int UnescapePercentEncodedUTF8Sequence(char* input, int len return totalCharsConsumed; bytesLeftInBuffer *= 3; - dest.Append(input + totalCharsConsumed - charsToCopy, charsToCopy + bytesLeftInBuffer); + dest.Append(new ReadOnlySpan(input + totalCharsConsumed - charsToCopy, charsToCopy + bytesLeftInBuffer)); return totalCharsConsumed + bytesLeftInBuffer; } } diff --git a/src/libraries/System.Private.Uri/src/System/UriHelper.cs b/src/libraries/System.Private.Uri/src/System/UriHelper.cs index 3b45b59c03debb..cfeb9f5da863ed 100644 --- a/src/libraries/System.Private.Uri/src/System/UriHelper.cs +++ b/src/libraries/System.Private.Uri/src/System/UriHelper.cs @@ -365,7 +365,7 @@ internal static unsafe void UnescapeString(char* pStr, int start, int end, ref V { if ((unescapeMode & UnescapeMode.EscapeUnescape) == UnescapeMode.CopyOnly) { - dest.Append(pStr + start, end - start); + dest.Append(new ReadOnlySpan(pStr + start, end - start)); return; } From fc01079fbc7c570edc0b0680258da399cb2c8a93 Mon Sep 17 00:00:00 2001 From: EgorBo Date: Thu, 10 Apr 2025 17:49:13 +0300 Subject: [PATCH 2/2] Fix build --- .../System/Text/ValueStringBuilderTests.cs | 21 +------------------ 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/src/libraries/Common/tests/Tests/System/Text/ValueStringBuilderTests.cs b/src/libraries/Common/tests/Tests/System/Text/ValueStringBuilderTests.cs index 728749ad34e861..3aadbc26fed28e 100644 --- a/src/libraries/Common/tests/Tests/System/Text/ValueStringBuilderTests.cs +++ b/src/libraries/Common/tests/Tests/System/Text/ValueStringBuilderTests.cs @@ -103,25 +103,6 @@ public void Append_CharInt_MatchesStringBuilder() Assert.Equal(sb.ToString(), vsb.ToString()); } - [Fact] - public unsafe void Append_PtrInt_MatchesStringBuilder() - { - var sb = new StringBuilder(); - var vsb = new ValueStringBuilder(); - for (int i = 1; i <= 100; i++) - { - string s = i.ToString(); - fixed (char* p = s) - { - sb.Append(p, s.Length); - vsb.Append(p, s.Length); - } - } - - Assert.Equal(sb.Length, vsb.Length); - Assert.Equal(sb.ToString(), vsb.ToString()); - } - [Fact] public void AppendSpan_DataAppendedCorrectly() { @@ -266,7 +247,7 @@ public void Dispose_ClearsBuilder_ThenReusable() } [Fact] - public unsafe void Indexer() + public void Indexer() { const string Text1 = "foobar"; var vsb = new ValueStringBuilder();