Browse Source

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

Signed-off-by: dotnet-bot <[email protected]>
Stephen Toub 7 years ago
parent
commit
bce478353a

+ 7 - 4
netcore/System.Private.CoreLib/shared/System/Globalization/DateTimeFormat.cs

@@ -509,7 +509,7 @@ namespace System
                             fraction = fraction / (long)Math.Pow(10, 7 - tokenLen);
                             if (ch == 'f')
                             {
-                                result.Append(((int)fraction).ToString(fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture));
+                                result.AppendSpanFormattable((int)fraction, fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture);
                             }
                             else
                             {
@@ -528,7 +528,7 @@ namespace System
                                 }
                                 if (effectiveDigits > 0)
                                 {
-                                    result.Append(((int)fraction).ToString(fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture));
+                                    result.AppendSpanFormattable((int)fraction, fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture);
                                 }
                                 else
                                 {
@@ -679,10 +679,13 @@ namespace System
                             {
                                 FormatDigits(result, year % 100, tokenLen);
                             }
+                            else if (tokenLen <= 16) // FormatDigits has an implicit 16-digit limit
+                            {
+                                FormatDigits(result, year, tokenLen, overrideLengthLimit: true);
+                            }
                             else
                             {
-                                string fmtPattern = "D" + tokenLen.ToString();
-                                result.Append(year.ToString(fmtPattern, CultureInfo.InvariantCulture));
+                                result.Append(year.ToString("D" + tokenLen.ToString(), CultureInfo.InvariantCulture));
                             }
                         }
                         bTimeOnly = false;

+ 2 - 2
netcore/System.Private.CoreLib/shared/System/Globalization/TimeSpanFormat.cs

@@ -400,7 +400,7 @@ namespace System.Globalization
 
                         tmp = fraction;
                         tmp /= TimeSpanParse.Pow10(DateTimeFormat.MaxSecondsFractionDigits - tokenLen);
-                        result.Append((tmp).ToString(DateTimeFormat.fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture));
+                        result.AppendSpanFormattable(tmp, DateTimeFormat.fixedNumberFormats[tokenLen - 1], CultureInfo.InvariantCulture);
                         break;
                     case 'F':
                         //
@@ -429,7 +429,7 @@ namespace System.Globalization
                         }
                         if (effectiveDigits > 0)
                         {
-                            result.Append((tmp).ToString(DateTimeFormat.fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture));
+                            result.AppendSpanFormattable(tmp, DateTimeFormat.fixedNumberFormats[effectiveDigits - 1], CultureInfo.InvariantCulture);
                         }
                         break;
                     case 'd':

+ 11 - 0
netcore/System.Private.CoreLib/shared/System/Text/StringBuilder.cs

@@ -1179,6 +1179,17 @@ namespace System.Text
             return Append(value.ToString());
         }
 
+        internal StringBuilder AppendSpanFormattable<T>(T value, string format, IFormatProvider provider) where T : ISpanFormattable, IFormattable
+        {
+            if (value.TryFormat(RemainingCurrentChunk, out int charsWritten, format, provider))
+            {
+                m_ChunkLength += charsWritten;
+                return this;
+            }
+
+            return Append(value.ToString(format, provider));
+        }
+
         public StringBuilder Append(object value) => (value == null) ? this : Append(value.ToString());
 
         public StringBuilder Append(char[] value)

+ 10 - 10
netcore/System.Private.CoreLib/shared/System/TimeZoneInfo.StringSerializer.cs

@@ -49,7 +49,7 @@ namespace System
                 //
                 SerializeSubstitute(zone.Id, serializedText);
                 serializedText.Append(Sep);
-                serializedText.Append(zone.BaseUtcOffset.TotalMinutes.ToString(CultureInfo.InvariantCulture));
+                serializedText.AppendSpanFormattable(zone.BaseUtcOffset.TotalMinutes, format: default, CultureInfo.InvariantCulture);
                 serializedText.Append(Sep);
                 SerializeSubstitute(zone.DisplayName, serializedText);
                 serializedText.Append(Sep);
@@ -62,11 +62,11 @@ namespace System
                 foreach (AdjustmentRule rule in rules)
                 {
                     serializedText.Append(Lhs);
-                    serializedText.Append(rule.DateStart.ToString(DateTimeFormat, DateTimeFormatInfo.InvariantInfo));
+                    serializedText.AppendSpanFormattable(rule.DateStart, DateTimeFormat, DateTimeFormatInfo.InvariantInfo);
                     serializedText.Append(Sep);
-                    serializedText.Append(rule.DateEnd.ToString(DateTimeFormat, DateTimeFormatInfo.InvariantInfo));
+                    serializedText.AppendSpanFormattable(rule.DateEnd, DateTimeFormat, DateTimeFormatInfo.InvariantInfo);
                     serializedText.Append(Sep);
-                    serializedText.Append(rule.DaylightDelta.TotalMinutes.ToString(CultureInfo.InvariantCulture));
+                    serializedText.AppendSpanFormattable(rule.DaylightDelta.TotalMinutes, format: default, CultureInfo.InvariantCulture);
                     serializedText.Append(Sep);
                     // serialize the TransitionTime's
                     SerializeTransitionTime(rule.DaylightTransitionStart, serializedText);
@@ -76,7 +76,7 @@ namespace System
                     if (rule.BaseUtcOffsetDelta != TimeSpan.Zero)
                     {
                         // Serialize it only when BaseUtcOffsetDelta has a value to reduce the impact of adding rule.BaseUtcOffsetDelta
-                        serializedText.Append(rule.BaseUtcOffsetDelta.TotalMinutes.ToString(CultureInfo.InvariantCulture));
+                        serializedText.AppendSpanFormattable(rule.BaseUtcOffsetDelta.TotalMinutes, format: default, CultureInfo.InvariantCulture);
                         serializedText.Append(Sep);
                     }
                     if (rule.NoDaylightTransitions)
@@ -155,20 +155,20 @@ namespace System
                 serializedText.Append(Lhs);
                 serializedText.Append(time.IsFixedDateRule ? '1' : '0');
                 serializedText.Append(Sep);
-                serializedText.Append(time.TimeOfDay.ToString(TimeOfDayFormat, DateTimeFormatInfo.InvariantInfo));
+                serializedText.AppendSpanFormattable(time.TimeOfDay, TimeOfDayFormat, DateTimeFormatInfo.InvariantInfo);
                 serializedText.Append(Sep);
-                serializedText.Append(time.Month.ToString(CultureInfo.InvariantCulture));
+                serializedText.AppendSpanFormattable(time.Month, format: default, CultureInfo.InvariantCulture);
                 serializedText.Append(Sep);
                 if (time.IsFixedDateRule)
                 {
-                    serializedText.Append(time.Day.ToString(CultureInfo.InvariantCulture));
+                    serializedText.AppendSpanFormattable(time.Day, format: default, CultureInfo.InvariantCulture);
                     serializedText.Append(Sep);
                 }
                 else
                 {
-                    serializedText.Append(time.Week.ToString(CultureInfo.InvariantCulture));
+                    serializedText.AppendSpanFormattable(time.Week, format: default, CultureInfo.InvariantCulture);
                     serializedText.Append(Sep);
-                    serializedText.Append(((int)time.DayOfWeek).ToString(CultureInfo.InvariantCulture));
+                    serializedText.AppendSpanFormattable((int)time.DayOfWeek, format: default, CultureInfo.InvariantCulture);
                     serializedText.Append(Sep);
                 }
                 serializedText.Append(Rhs);