diff --git a/src/libraries/Common/src/System/Text/ValueStringBuilder.cs b/src/libraries/Common/src/System/Text/ValueStringBuilder.cs index e5c8610cd60dc5..8fb266df99ae57 100644 --- a/src/libraries/Common/src/System/Text/ValueStringBuilder.cs +++ b/src/libraries/Common/src/System/Text/ValueStringBuilder.cs @@ -54,27 +54,23 @@ public void EnsureCapacity(int capacity) } /// - /// Get a pinnable reference to the builder. - /// Does not ensure there is a null char after - /// This overload is pattern matched in the C# 7.3+ compiler so you can omit - /// the explicit method call, and write eg "fixed (char* c = builder)" + /// Ensures that the builder is terminated with a NUL character. /// - public ref char GetPinnableReference() + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void NullTerminate() { - return ref MemoryMarshal.GetReference(_chars); + EnsureCapacity(_pos + 1); + _chars[_pos] = '\0'; } /// /// Get a pinnable reference to the builder. + /// Does not ensure there is a null char after + /// This overload is pattern matched in the C# 7.3+ compiler so you can omit + /// the explicit method call, and write eg "fixed (char* c = builder)" /// - /// Ensures that the builder has a null char after - public ref char GetPinnableReference(bool terminate) + public ref char GetPinnableReference() { - if (terminate) - { - EnsureCapacity(Length + 1); - _chars[Length] = '\0'; - } return ref MemoryMarshal.GetReference(_chars); } @@ -97,40 +93,10 @@ public override string ToString() /// Returns the underlying storage of the builder. public Span RawChars => _chars; - /// - /// Returns a span around the contents of the builder. - /// - /// Ensures that the builder has a null char after - public ReadOnlySpan AsSpan(bool terminate) - { - if (terminate) - { - EnsureCapacity(Length + 1); - _chars[Length] = '\0'; - } - return _chars.Slice(0, _pos); - } - public ReadOnlySpan AsSpan() => _chars.Slice(0, _pos); public ReadOnlySpan AsSpan(int start) => _chars.Slice(start, _pos - start); public ReadOnlySpan AsSpan(int start, int length) => _chars.Slice(start, length); - public bool TryCopyTo(Span destination, out int charsWritten) - { - if (_chars.Slice(0, _pos).TryCopyTo(destination)) - { - charsWritten = _pos; - Dispose(); - return true; - } - else - { - charsWritten = 0; - Dispose(); - return false; - } - } - public void Insert(int index, char value, int count) { if (_pos > _chars.Length - count) diff --git a/src/libraries/Common/tests/Common.Tests.csproj b/src/libraries/Common/tests/Common.Tests.csproj index dab6d98a4ebd7d..df0afcee4cd29a 100644 --- a/src/libraries/Common/tests/Common.Tests.csproj +++ b/src/libraries/Common/tests/Common.Tests.csproj @@ -159,12 +159,7 @@ - - - - - - + diff --git a/src/libraries/Common/tests/Tests/System/Text/ValueStringBuilderTests.cs b/src/libraries/Common/tests/Tests/System/Text/ValueStringBuilderTests.cs index 3aadbc26fed28e..7959d4298e6d00 100644 --- a/src/libraries/Common/tests/Tests/System/Text/ValueStringBuilderTests.cs +++ b/src/libraries/Common/tests/Tests/System/Text/ValueStringBuilderTests.cs @@ -178,46 +178,6 @@ public void ToString_ClearsBuilder_ThenReusable() Assert.Equal(0, vsb.Length); Assert.Equal(string.Empty, vsb.ToString()); - Assert.True(vsb.TryCopyTo(Span.Empty, out _)); - - const string Text2 = "another test"; - vsb.Append(Text2); - Assert.Equal(Text2.Length, vsb.Length); - Assert.Equal(Text2, vsb.ToString()); - } - - [Fact] - public void TryCopyTo_FailsWhenDestinationIsTooSmall_SucceedsWhenItsLargeEnough() - { - var vsb = new ValueStringBuilder(); - - const string Text = "expected text"; - vsb.Append(Text); - Assert.Equal(Text.Length, vsb.Length); - - Span dst = new char[Text.Length - 1]; - Assert.False(vsb.TryCopyTo(dst, out int charsWritten)); - Assert.Equal(0, charsWritten); - Assert.Equal(0, vsb.Length); - } - - [Fact] - public void TryCopyTo_ClearsBuilder_ThenReusable() - { - const string Text1 = "test"; - var vsb = new ValueStringBuilder(); - - vsb.Append(Text1); - Assert.Equal(Text1.Length, vsb.Length); - - Span dst = new char[Text1.Length]; - Assert.True(vsb.TryCopyTo(dst, out int charsWritten)); - Assert.Equal(Text1.Length, charsWritten); - Assert.Equal(Text1, new string(dst)); - - Assert.Equal(0, vsb.Length); - Assert.Equal(string.Empty, vsb.ToString()); - Assert.True(vsb.TryCopyTo(Span.Empty, out _)); const string Text2 = "another test"; vsb.Append(Text2); @@ -238,7 +198,6 @@ public void Dispose_ClearsBuilder_ThenReusable() Assert.Equal(0, vsb.Length); Assert.Equal(string.Empty, vsb.ToString()); - Assert.True(vsb.TryCopyTo(Span.Empty, out _)); const string Text2 = "another test"; vsb.Append(Text2); diff --git a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs index dc05e138a5aec6..cebd7469d43667 100644 --- a/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs +++ b/src/libraries/System.Diagnostics.Process/src/System/Diagnostics/Process.Windows.cs @@ -545,9 +545,10 @@ private unsafe bool StartWithCreateProcess(ProcessStartInfo startInfo) logonFlags = Interop.Advapi32.LogonFlags.LOGON_NETCREDENTIALS_ONLY; } + commandLine.NullTerminate(); fixed (char* passwordInClearTextPtr = startInfo.PasswordInClearText ?? string.Empty) fixed (char* environmentBlockPtr = environmentBlock) - fixed (char* commandLinePtr = &commandLine.GetPinnableReference(terminate: true)) + fixed (char* commandLinePtr = &commandLine.GetPinnableReference()) { IntPtr passwordPtr = (startInfo.Password != null) ? Marshal.SecureStringToGlobalAllocUnicode(startInfo.Password) : IntPtr.Zero; @@ -579,8 +580,9 @@ ref processInfo // pointer to PROCESS_INFORMATION } else { + commandLine.NullTerminate(); fixed (char* environmentBlockPtr = environmentBlock) - fixed (char* commandLinePtr = &commandLine.GetPinnableReference(terminate: true)) + fixed (char* commandLinePtr = &commandLine.GetPinnableReference()) { retVal = Interop.Kernel32.CreateProcess( null, // we don't need this since all the info is in commandLine diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/PathHelper.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/PathHelper.Windows.cs index ff479ce957f2c7..687478435ff06c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/PathHelper.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/PathHelper.Windows.cs @@ -51,7 +51,8 @@ internal static string Normalize(ref ValueStringBuilder path) var builder = new ValueStringBuilder(stackalloc char[PathInternal.MaxShortPath]); // Get the full path - GetFullPathName(path.AsSpan(terminate: true), ref builder); + path.NullTerminate(); + GetFullPathName(path.AsSpan(), ref builder); string result = builder.AsSpan().IndexOf('~') >= 0 ? TryExpandShortFileName(ref builder, originalPath: null) @@ -176,8 +177,9 @@ internal static string TryExpandShortFileName(ref ValueStringBuilder outputBuild while (!success) { + inputBuilder.NullTerminate(); uint result = Interop.Kernel32.GetLongPathNameW( - ref inputBuilder.GetPinnableReference(terminate: true), ref outputBuilder.GetPinnableReference(), (uint)outputBuilder.Capacity); + ref inputBuilder.GetPinnableReference(), ref outputBuilder.GetPinnableReference(), (uint)outputBuilder.Capacity); // Replace any temporary null we added if (inputBuilder[foundIndex] == '\0') inputBuilder[foundIndex] = '\\'; diff --git a/src/libraries/System.Runtime.Numerics/src/System/Number.BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Number.BigInteger.cs index 2ec19b8d824907..1bb961bce55f53 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Number.BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Number.BigInteger.cs @@ -620,7 +620,8 @@ static uint MultiplyAdd(Span bits, uint multiplier, uint addValue) if (targetSpan) { - spanSuccess = sb.TryCopyTo(destination, out charsWritten); + charsWritten = (spanSuccess = sb.AsSpan().TryCopyTo(destination)) ? sb.Length : 0; + sb.Dispose(); return null; } else