Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit 401e931

Browse files
authored
Avoid intermediate strings in some StringBuilder formatting (#22111)
* Avoid intermediate strings in some StringBuilder formatting Several appends to string builders are first creating strings and then appending those strings, and they're not using the existing Append(primitive) overloads because they want to customize the format string or provider as part of appending the value. This fixes a few of those cases, using another internal AppendSpanFormattable overload on StringBuilder. * Address PR feedback
1 parent a5c5feb commit 401e931

File tree

4 files changed

+30
-16
lines changed

4 files changed

+30
-16
lines changed

src/System.Private.CoreLib/shared/System/Globalization/DateTimeFormat.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -509,7 +509,7 @@ private static StringBuilder FormatCustomized(
509509
fraction = fraction / (long)Math.Pow(10, 7 - tokenLen);
510510
if (ch == 'f')
511511
{
512-
result.Append(((int)fraction).ToString(fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture));
512+
result.AppendSpanFormattable((int)fraction, fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture);
513513
}
514514
else
515515
{
@@ -528,7 +528,7 @@ private static StringBuilder FormatCustomized(
528528
}
529529
if (effectiveDigits > 0)
530530
{
531-
result.Append(((int)fraction).ToString(fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture));
531+
result.AppendSpanFormattable((int)fraction, fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture);
532532
}
533533
else
534534
{
@@ -679,10 +679,13 @@ private static StringBuilder FormatCustomized(
679679
{
680680
FormatDigits(result, year % 100, tokenLen);
681681
}
682+
else if (tokenLen <= 16) // FormatDigits has an implicit 16-digit limit
683+
{
684+
FormatDigits(result, year, tokenLen, overrideLengthLimit: true);
685+
}
682686
else
683687
{
684-
string fmtPattern = "D" + tokenLen.ToString();
685-
result.Append(year.ToString(fmtPattern, CultureInfo.InvariantCulture));
688+
result.Append(year.ToString("D" + tokenLen.ToString(), CultureInfo.InvariantCulture));
686689
}
687690
}
688691
bTimeOnly = false;

src/System.Private.CoreLib/shared/System/Globalization/TimeSpanFormat.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,7 @@ private static StringBuilder FormatCustomized(TimeSpan value, ReadOnlySpan<char>
400400

401401
tmp = fraction;
402402
tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen);
403-
result.Append((tmp).ToString(DateTimeFormat.fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture));
403+
result.AppendSpanFormattable(tmp, DateTimeFormat.fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture);
404404
break;
405405
case 'F':
406406
//
@@ -429,7 +429,7 @@ private static StringBuilder FormatCustomized(TimeSpan value, ReadOnlySpan<char>
429429
}
430430
if (effectiveDigits > 0)
431431
{
432-
result.Append((tmp).ToString(DateTimeFormat.fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture));
432+
result.AppendSpanFormattable(tmp, DateTimeFormat.fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture);
433433
}
434434
break;
435435
case 'd':

src/System.Private.CoreLib/shared/System/Text/StringBuilder.cs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1179,6 +1179,17 @@ private StringBuilder AppendSpanFormattable<T>(T value) where T : ISpanFormattab
11791179
return Append(value.ToString());
11801180
}
11811181

1182+
internal StringBuilder AppendSpanFormattable<T>(T value, string format, IFormatProvider provider) where T : ISpanFormattable, IFormattable
1183+
{
1184+
if (value.TryFormat(RemainingCurrentChunk, out int charsWritten, format, provider))
1185+
{
1186+
m_ChunkLength += charsWritten;
1187+
return this;
1188+
}
1189+
1190+
return Append(value.ToString(format, provider));
1191+
}
1192+
11821193
public StringBuilder Append(object value) => (value == null) ? this : Append(value.ToString());
11831194

11841195
public StringBuilder Append(char[] value)

src/System.Private.CoreLib/shared/System/TimeZoneInfo.StringSerializer.cs

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ public static string GetSerializedString(TimeZoneInfo zone)
4949
//
5050
SerializeSubstitute(zone.Id, serializedText);
5151
serializedText.Append(Sep);
52-
serializedText.Append(zone.BaseUtcOffset.TotalMinutes.ToString(CultureInfo.InvariantCulture));
52+
serializedText.AppendSpanFormattable(zone.BaseUtcOffset.TotalMinutes, format: default, CultureInfo.InvariantCulture);
5353
serializedText.Append(Sep);
5454
SerializeSubstitute(zone.DisplayName, serializedText);
5555
serializedText.Append(Sep);
@@ -62,11 +62,11 @@ public static string GetSerializedString(TimeZoneInfo zone)
6262
foreach (AdjustmentRule rule in rules)
6363
{
6464
serializedText.Append(Lhs);
65-
serializedText.Append(rule.DateStart.ToString(DateTimeFormat, DateTimeFormatInfo.InvariantInfo));
65+
serializedText.AppendSpanFormattable(rule.DateStart, DateTimeFormat, DateTimeFormatInfo.InvariantInfo);
6666
serializedText.Append(Sep);
67-
serializedText.Append(rule.DateEnd.ToString(DateTimeFormat, DateTimeFormatInfo.InvariantInfo));
67+
serializedText.AppendSpanFormattable(rule.DateEnd, DateTimeFormat, DateTimeFormatInfo.InvariantInfo);
6868
serializedText.Append(Sep);
69-
serializedText.Append(rule.DaylightDelta.TotalMinutes.ToString(CultureInfo.InvariantCulture));
69+
serializedText.AppendSpanFormattable(rule.DaylightDelta.TotalMinutes, format: default, CultureInfo.InvariantCulture);
7070
serializedText.Append(Sep);
7171
// serialize the TransitionTime's
7272
SerializeTransitionTime(rule.DaylightTransitionStart, serializedText);
@@ -76,7 +76,7 @@ public static string GetSerializedString(TimeZoneInfo zone)
7676
if (rule.BaseUtcOffsetDelta != TimeSpan.Zero)
7777
{
7878
// Serialize it only when BaseUtcOffsetDelta has a value to reduce the impact of adding rule.BaseUtcOffsetDelta
79-
serializedText.Append(rule.BaseUtcOffsetDelta.TotalMinutes.ToString(CultureInfo.InvariantCulture));
79+
serializedText.AppendSpanFormattable(rule.BaseUtcOffsetDelta.TotalMinutes, format: default, CultureInfo.InvariantCulture);
8080
serializedText.Append(Sep);
8181
}
8282
if (rule.NoDaylightTransitions)
@@ -155,20 +155,20 @@ private static void SerializeTransitionTime(TransitionTime time, StringBuilder s
155155
serializedText.Append(Lhs);
156156
serializedText.Append(time.IsFixedDateRule ? '1' : '0');
157157
serializedText.Append(Sep);
158-
serializedText.Append(time.TimeOfDay.ToString(TimeOfDayFormat, DateTimeFormatInfo.InvariantInfo));
158+
serializedText.AppendSpanFormattable(time.TimeOfDay, TimeOfDayFormat, DateTimeFormatInfo.InvariantInfo);
159159
serializedText.Append(Sep);
160-
serializedText.Append(time.Month.ToString(CultureInfo.InvariantCulture));
160+
serializedText.AppendSpanFormattable(time.Month, format: default, CultureInfo.InvariantCulture);
161161
serializedText.Append(Sep);
162162
if (time.IsFixedDateRule)
163163
{
164-
serializedText.Append(time.Day.ToString(CultureInfo.InvariantCulture));
164+
serializedText.AppendSpanFormattable(time.Day, format: default, CultureInfo.InvariantCulture);
165165
serializedText.Append(Sep);
166166
}
167167
else
168168
{
169-
serializedText.Append(time.Week.ToString(CultureInfo.InvariantCulture));
169+
serializedText.AppendSpanFormattable(time.Week, format: default, CultureInfo.InvariantCulture);
170170
serializedText.Append(Sep);
171-
serializedText.Append(((int)time.DayOfWeek).ToString(CultureInfo.InvariantCulture));
171+
serializedText.AppendSpanFormattable((int)time.DayOfWeek, format: default, CultureInfo.InvariantCulture);
172172
serializedText.Append(Sep);
173173
}
174174
serializedText.Append(Rhs);

0 commit comments

Comments
 (0)