From c3092f6e2a3ab8c9fc02c1361aabc8295cc6bbe9 Mon Sep 17 00:00:00 2001 From: xtqqczze <45661989+xtqqczze@users.noreply.github.com> Date: Fri, 20 Dec 2024 00:19:00 +0000 Subject: [PATCH] Refactor use of `FastAllocateString` --- .../src/System/BitConverter.cs | 13 +++-- .../src/System/Convert.cs | 4 +- .../System.Private.CoreLib/src/System/Enum.cs | 4 +- .../src/System/Exception.cs | 3 +- .../System/Globalization/DateTimeFormat.cs | 49 ++++++++++--------- .../src/System/Globalization/IcuLocaleData.cs | 5 +- .../Globalization/InvariantModeCasing.cs | 14 +++--- .../src/System/Globalization/TextInfo.cs | 6 +-- .../System.Private.CoreLib/src/System/Guid.cs | 8 +-- .../Strings/StringSearchValues.cs | 11 +++-- .../src/System/String.cs | 11 ++++- .../src/System/Text/StringBuilder.cs | 4 +- 12 files changed, 67 insertions(+), 65 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/BitConverter.cs b/src/libraries/System.Private.CoreLib/src/System/BitConverter.cs index 24069608615fb..22ca54dd788eb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/BitConverter.cs +++ b/src/libraries/System.Private.CoreLib/src/System/BitConverter.cs @@ -798,21 +798,20 @@ public static string ToString(byte[] value, int startIndex, int length) // (int.MaxValue / 3) == 715,827,882 Bytes == 699 MB ArgumentOutOfRangeException.ThrowIfGreaterThan(length, int.MaxValue / 3); - string result = string.FastAllocateString(length * 3 - 1); + string result = string.AllocateInternal(length * 3 - 1, out Span resultSpan); - var dst = new Span(ref result.GetRawStringData(), result.Length); var src = new ReadOnlySpan(value, startIndex, length); int i = 0; int j = 0; byte b = src[i++]; - dst[j++] = HexConverter.ToCharUpper(b >> 4); - dst[j++] = HexConverter.ToCharUpper(b); + resultSpan[j++] = HexConverter.ToCharUpper(b >> 4); + resultSpan[j++] = HexConverter.ToCharUpper(b); while (i < src.Length) { b = src[i++]; - dst[j++] = '-'; - dst[j++] = HexConverter.ToCharUpper(b >> 4); - dst[j++] = HexConverter.ToCharUpper(b); + resultSpan[j++] = '-'; + resultSpan[j++] = HexConverter.ToCharUpper(b >> 4); + resultSpan[j++] = HexConverter.ToCharUpper(b); } return result; diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 62e4cd8412ab9..7933965d6ee96 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -2355,11 +2355,11 @@ public static string ToBase64String(ReadOnlySpan bytes, Base64FormattingOp bool insertLineBreaks = (options == Base64FormattingOptions.InsertLineBreaks); int outputLength = ToBase64_CalculateAndValidateOutputLength(bytes.Length, insertLineBreaks); - string result = string.FastAllocateString(outputLength); + string result = string.AllocateInternal(outputLength, out Span resultSpan); if (Vector128.IsHardwareAccelerated && !insertLineBreaks && bytes.Length >= Base64VectorizationLengthThreshold) { - ToBase64CharsLargeNoLineBreaks(bytes, new Span(ref result.GetRawStringData(), result.Length), result.Length); + ToBase64CharsLargeNoLineBreaks(bytes, resultSpan, result.Length); } else { diff --git a/src/libraries/System.Private.CoreLib/src/System/Enum.cs b/src/libraries/System.Private.CoreLib/src/System/Enum.cs index f559948df33b1..77a8dc851984a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Enum.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Enum.cs @@ -1945,8 +1945,8 @@ private static bool TryFormatPrimitiveNonDefault(RuntimeT foundItems = foundItems.Slice(0, foundItemsCount); int length = GetMultipleEnumsFlagsFormatResultLength(resultLength, foundItemsCount); - result = string.FastAllocateString(length); - WriteMultipleFoundFlagsNames(names, foundItems, new Span(ref result.GetRawStringData(), result.Length)); + result = string.AllocateInternal(length, out Span resultSpan); + WriteMultipleFoundFlagsNames(names, foundItems, resultSpan); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Exception.cs b/src/libraries/System.Private.CoreLib/src/System/Exception.cs index 2e59e4b792ad3..ef957fa1793bc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Exception.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Exception.cs @@ -148,8 +148,7 @@ public override string ToString() } // Create the string - string result = string.FastAllocateString(length); - Span resultSpan = new Span(ref result.GetRawStringData(), result.Length); + string result = string.AllocateInternal(length, out Span resultSpan); // Fill it in Write(className, ref resultSpan); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs index a7ca7828e6e49..c0d16533dbfaa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs @@ -919,6 +919,8 @@ internal static string Format(DateTime dateTime, string? format, IFormatProvider internal static string Format(DateTime dateTime, string? format, IFormatProvider? provider, TimeSpan offset) { + string result; + Span resultSpan; DateTimeFormatInfo dtfi; if (string.IsNullOrEmpty(format)) @@ -929,17 +931,17 @@ internal static string Format(DateTime dateTime, string? format, IFormatProvider { if (IsTimeOnlySpecialCase(dateTime, dtfi)) { - string str = string.FastAllocateString(FormatSLength); - TryFormatS(dateTime, new Span(ref str.GetRawStringData(), str.Length), out int charsWritten); + result = string.AllocateInternal(FormatSLength, out resultSpan); + TryFormatS(dateTime, resultSpan, out int charsWritten); Debug.Assert(charsWritten == FormatSLength); - return str; + return result; } else if (ReferenceEquals(dtfi, DateTimeFormatInfo.InvariantInfo)) { - string str = string.FastAllocateString(FormatInvariantGMinLength); - TryFormatInvariantG(dateTime, offset, new Span(ref str.GetRawStringData(), str.Length), out int charsWritten); + result = string.AllocateInternal(FormatInvariantGMinLength, out resultSpan); + TryFormatInvariantG(dateTime, offset, resultSpan, out int charsWritten); Debug.Assert(charsWritten == FormatInvariantGMinLength); - return str; + return result; } else { @@ -955,10 +957,10 @@ internal static string Format(DateTime dateTime, string? format, IFormatProvider } else if (ReferenceEquals(dtfi, DateTimeFormatInfo.InvariantInfo)) { - string str = string.FastAllocateString(FormatInvariantGMaxLength); - TryFormatInvariantG(dateTime, offset, new Span(ref str.GetRawStringData(), str.Length), out int charsWritten); + result = string.AllocateInternal(FormatInvariantGMaxLength, out resultSpan); + TryFormatInvariantG(dateTime, offset, resultSpan, out int charsWritten); Debug.Assert(charsWritten == FormatInvariantGMaxLength); - return str; + return result; } else { @@ -969,7 +971,6 @@ internal static string Format(DateTime dateTime, string? format, IFormatProvider else if (format.Length == 1) { int charsWritten; - string str; switch (format[0]) { // Round trip format @@ -981,24 +982,24 @@ internal static string Format(DateTime dateTime, string? format, IFormatProvider // RFC1123 format case 'r' or 'R': - str = string.FastAllocateString(FormatRLength); - TryFormatR(dateTime, offset, new Span(ref str.GetRawStringData(), str.Length), out charsWritten); - Debug.Assert(charsWritten == str.Length); - return str; + result = string.AllocateInternal(FormatRLength, out resultSpan); + TryFormatR(dateTime, offset, resultSpan, out charsWritten); + Debug.Assert(charsWritten == result.Length); + return result; // Sortable format case 's': - str = string.FastAllocateString(FormatSLength); - TryFormatS(dateTime, new Span(ref str.GetRawStringData(), str.Length), out charsWritten); - Debug.Assert(charsWritten == str.Length); - return str; + result = string.AllocateInternal(FormatSLength, out resultSpan); + TryFormatS(dateTime, resultSpan, out charsWritten); + Debug.Assert(charsWritten == result.Length); + return result; // Universal time in sortable format case 'u': - str = string.FastAllocateString(FormatuLength); - TryFormatu(dateTime, offset, new Span(ref str.GetRawStringData(), str.Length), out charsWritten); - Debug.Assert(charsWritten == str.Length); - return str; + result = string.AllocateInternal(FormatuLength, out resultSpan); + TryFormatu(dateTime, offset, resultSpan, out charsWritten); + Debug.Assert(charsWritten == result.Length); + return result; // Universal time in culture dependent format case 'U': @@ -1021,9 +1022,9 @@ internal static string Format(DateTime dateTime, string? format, IFormatProvider var vlb = new ValueListBuilder(stackalloc char[256]); FormatCustomized(dateTime, format, dtfi, offset, ref vlb); - string resultString = vlb.AsSpan().ToString(); + result = vlb.AsSpan().ToString(); vlb.Dispose(); - return resultString; + return result; } internal static bool TryFormat(DateTime dateTime, Span destination, out int charsWritten, ReadOnlySpan format, IFormatProvider? provider) where TChar : unmanaged, IUtfChar => diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/IcuLocaleData.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/IcuLocaleData.cs index 424e4bab73619..f0e705f641c5e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/IcuLocaleData.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/IcuLocaleData.cs @@ -3860,11 +3860,10 @@ private static ReadOnlySpan GetCultureName(int localeNameIndice) private static string GetString(ReadOnlySpan buffer) { - string result = string.FastAllocateString(buffer.Length); - var s = new Span(ref result.GetRawStringData(), buffer.Length); + string result = string.AllocateInternal(buffer.Length, out Span resultSpan); for (int i = 0; i < buffer.Length; i++) { - s[i] = (char)buffer[i]; + resultSpan[i] = (char)buffer[i]; } return result; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/InvariantModeCasing.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/InvariantModeCasing.cs index 5c6dfadf073bd..44c8762cc5f6d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/InvariantModeCasing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/InvariantModeCasing.cs @@ -53,11 +53,10 @@ internal static string ToLower(string s) return s; } - string result = string.FastAllocateString(s.Length); - var destination = new Span(ref result.GetRawStringData(), result.Length); + string result = string.AllocateInternal(s.Length, out Span resultSpan); ReadOnlySpan src = s; - src.Slice(0, i).CopyTo(destination); - ToLower(src.Slice(i), destination.Slice(i)); + src.Slice(0, i).CopyTo(resultSpan); + ToLower(src.Slice(i), resultSpan.Slice(i)); return result; } @@ -99,11 +98,10 @@ internal static string ToUpper(string s) return s; } - string result = string.FastAllocateString(s.Length); - var destination = new Span(ref result.GetRawStringData(), result.Length); + string result = string.AllocateInternal(s.Length, out Span resultSpan); ReadOnlySpan src = s; - src.Slice(0, i).CopyTo(destination); - ToUpper(src.Slice(i), destination.Slice(i)); + src.Slice(0, i).CopyTo(resultSpan); + ToUpper(src.Slice(i), resultSpan.Slice(i)); return result; } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs index 5b6f26637dcac..03c1571aa4788 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs @@ -324,10 +324,9 @@ private unsafe string ChangeCaseCommon(string source) where TConver // This will necessarily allocate a new string, but let's try to stay within the managed (non-localization tables) // conversion code path if we can. - string result = string.FastAllocateString(source.Length); // changing case uses simple folding: doesn't change UTF-16 code unit count + string result = string.AllocateInternal(source.Length, out Span resultSpan); // changing case uses simple folding: doesn't change UTF-16 code unit count // copy existing known-good data into the result - Span resultSpan = new Span(ref result.GetRawStringData(), result.Length); source.AsSpan(0, (int)currIdx).CopyTo(resultSpan); // and re-run the fast span-based logic over the remainder of the data @@ -341,12 +340,11 @@ private unsafe string ChangeCaseCommon(string source) where TConver // We reached non-ASCII data *or* the requested culture doesn't map ASCII data the same way as the invariant culture. // In either case we need to fall back to the localization tables. - string result = string.FastAllocateString(source.Length); // changing case uses simple folding: doesn't change UTF-16 code unit count + string result = string.AllocateInternal(source.Length, out Span resultSpan); // changing case uses simple folding: doesn't change UTF-16 code unit count if (currIdx > 0) { // copy existing known-good data into the result - Span resultSpan = new Span(ref result.GetRawStringData(), result.Length); source.AsSpan(0, (int)currIdx).CopyTo(resultSpan); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index e4bea80bf69bf..9534d6bb1902f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -1176,12 +1176,12 @@ public string ToString([StringSyntax(StringSyntaxAttribute.GuidFormat)] string? }; } - string guidString = string.FastAllocateString(guidSize); + string result = string.AllocateInternal(guidSize, out Span resultSpan); - bool result = TryFormatCore(new Span(ref guidString.GetRawStringData(), guidString.Length), out int bytesWritten, format); - Debug.Assert(result && bytesWritten == guidString.Length, "Formatting guid should have succeeded."); + bool success = TryFormatCore(resultSpan, out int bytesWritten, format); + Debug.Assert(success && bytesWritten == result.Length, "Formatting guid should have succeeded."); - return guidString; + return result; } public bool TryFormat(Span destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.GuidFormat)] ReadOnlySpan format = default) => diff --git a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/StringSearchValues.cs b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/StringSearchValues.cs index e2ae3c61b0445..393758d80c6f1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/StringSearchValues.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SearchValues/Strings/StringSearchValues.cs @@ -84,15 +84,16 @@ public static SearchValues Create(ReadOnlySpan values, bool igno static string NormalizeIfNeeded(string value, bool ignoreCase) { + string result = value; + if (ignoreCase && value.AsSpan().ContainsAnyExcept(s_allAsciiExceptLowercase)) { - string upperCase = string.FastAllocateString(value.Length); - int charsWritten = Ordinal.ToUpperOrdinal(value, new Span(ref upperCase.GetRawStringData(), upperCase.Length)); - Debug.Assert(charsWritten == upperCase.Length); - value = upperCase; + result = string.AllocateInternal(value.Length, out Span resultSpan); + int charsWritten = Ordinal.ToUpperOrdinal(value, resultSpan); + Debug.Assert(charsWritten == result.Length); } - return value; + return result; } static Span RemoveUnreachableValues(Span values, HashSet unreachableValues) diff --git a/src/libraries/System.Private.CoreLib/src/System/String.cs b/src/libraries/System.Private.CoreLib/src/System/String.cs index daa4618fed3cc..94ead33f9bac0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/String.cs +++ b/src/libraries/System.Private.CoreLib/src/System/String.cs @@ -340,8 +340,8 @@ public static string Create(int length, TState state, SpanAction(ref result.GetRawStringData(), length), state); + string result = AllocateInternal(length, out Span resultSpan); + action(resultSpan, state); return result; } @@ -527,6 +527,13 @@ public static bool IsNullOrWhiteSpace([NotNullWhen(false)] string? value) internal ref char GetRawStringData() => ref _firstChar; internal ref ushort GetRawStringDataAsUInt16() => ref Unsafe.As(ref _firstChar); + internal static string AllocateInternal(int length, out Span resultSpan) + { + string result = FastAllocateString(length); + resultSpan = new Span(ref result._firstChar, result.Length); + return result; + } + // Helper for encodings so they can talk to our buffer directly // stringLength must be the exact size we'll expect internal static unsafe string CreateStringFromEncoding( 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 11b76d7b19568..76b97e9ce4922 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs @@ -368,8 +368,8 @@ public string ToString(int startIndex, int length) } AssertInvariants(); - string result = string.FastAllocateString(length); - CopyTo(startIndex, new Span(ref result.GetRawStringData(), result.Length), result.Length); + string result = string.AllocateInternal(length, out Span resultSpan); + CopyTo(startIndex, resultSpan, resultSpan.Length); return result; }