Use ValueStringBuilder instead of StringBuidler{Cache} in multiple places
authorStephen Toub <stoub@microsoft.com>
Sat, 26 Oct 2019 00:44:13 +0000 (20:44 -0400)
committerStephen Toub <stoub@microsoft.com>
Sat, 26 Oct 2019 10:47:52 +0000 (06:47 -0400)
Commit migrated from https://github.com/dotnet/coreclr/commit/29694529dcae65e75e7917126c56e88fc4596332

12 files changed:
src/coreclr/src/System.Private.CoreLib/src/System/TypeNameParser.cs
src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/Collections/Generic/KeyValuePair.cs
src/libraries/System.Private.CoreLib/src/System/IO/Path.cs
src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector128_1.cs
src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector256_1.cs
src/libraries/System.Private.CoreLib/src/System/Runtime/Intrinsics/Vector64_1.cs
src/libraries/System.Private.CoreLib/src/System/String.Manipulation.cs
src/libraries/System.Private.CoreLib/src/System/Text/StringBuilder.cs
src/libraries/System.Private.CoreLib/src/System/Text/ValueStringBuilder.AppendFormat.cs [new file with mode: 0644]
src/libraries/System.Private.CoreLib/src/System/Text/ValueStringBuilder.cs
src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.StringSerializer.cs

index 02af06b..d0dbe6b 100644 (file)
@@ -282,7 +282,7 @@ namespace System
             if (name.IndexOfAny(SPECIAL_CHARS) < 0)
                 return name;
 
-            StringBuilder sb = StringBuilderCache.Acquire();
+            var sb = new ValueStringBuilder(stackalloc char[64]);
             foreach (char c in name)
             {
                 if (Array.IndexOf<char>(SPECIAL_CHARS, c) >= 0)
@@ -291,7 +291,7 @@ namespace System
                 sb.Append(c);
             }
 
-            return StringBuilderCache.GetStringAndRelease(sb);
+            return sb.ToString();
         }
 
         private static SafeTypeNameParserHandle? CreateTypeNameParser(string typeName, bool throwOnError)
index 813998b..d3cc670 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)System\Text\UTF7Encoding.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Text\UTF8Encoding.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Text\ValueStringBuilder.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)System\Text\ValueStringBuilder.AppendFormat.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Text\Unicode\Utf16Utility.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Text\Unicode\Utf16Utility.Validation.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Text\Unicode\Utf8.cs" />
index f53b816..9e1526f 100644 (file)
@@ -21,24 +21,25 @@ namespace System.Collections.Generic
         /// </summary>
         internal static string PairToString(object? key, object? value)
         {
-            StringBuilder s = StringBuilderCache.Acquire();
+            var s = new ValueStringBuilder(stackalloc char[64]);
+
             s.Append('[');
 
             if (key != null)
             {
-                s.Append(key);
+                s.Append(key.ToString());
             }
 
             s.Append(", ");
 
             if (value != null)
             {
-                s.Append(value);
+                s.Append(value.ToString());
             }
 
             s.Append(']');
 
-            return StringBuilderCache.GetStringAndRelease(s);
+            return s.ToString();
         }
     }
 
index f41d239..500e98f 100644 (file)
@@ -859,7 +859,8 @@ namespace System.IO
             //  C:\Foo\Bar C:\Bar\Bar L3, S2 -> ..\..\Bar\Bar
             //  C:\Foo\Foo C:\Foo\Bar L7, S1 -> ..\Bar
 
-            StringBuilder sb = StringBuilderCache.Acquire(Math.Max(relativeTo.Length, path.Length));
+            var sb = new ValueStringBuilder(stackalloc char[260]);
+            sb.EnsureCapacity(Math.Max(relativeTo.Length, path.Length));
 
             // Add parent segments for segments past the common on the "from" path
             if (commonLength < relativeToLength)
@@ -894,10 +895,10 @@ namespace System.IO
                     sb.Append(DirectorySeparatorChar);
                 }
 
-                sb.Append(path, commonLength, differenceLength);
+                sb.Append(path.AsSpan(commonLength, differenceLength));
             }
 
-            return StringBuilderCache.GetStringAndRelease(sb);
+            return sb.ToString();
         }
 
         /// <summary>Returns a comparison that can be used to compare file and directory names for equality.</summary>
index 8729965..46c9323 100644 (file)
@@ -173,20 +173,20 @@ namespace System.Runtime.Intrinsics
             ThrowHelper.ThrowForUnsupportedVectorBaseType<T>();
 
             int lastElement = Count - 1;
-            StringBuilder sb = StringBuilderCache.Acquire();
+            var sb = new ValueStringBuilder(stackalloc char[64]);
             CultureInfo invariant = CultureInfo.InvariantCulture;
 
             sb.Append('<');
             for (int i = 0; i < lastElement; i++)
             {
-                sb.Append(((IFormattable)this.GetElement(i)).ToString("G", invariant))
-                 .Append(',')
-                 .Append(' ');
+                sb.Append(((IFormattable)this.GetElement(i)).ToString("G", invariant));
+                sb.Append(',');
+                sb.Append(' ');
             }
-            sb.Append(((IFormattable)this.GetElement(lastElement)).ToString("G", invariant))
-             .Append('>');
+            sb.Append(((IFormattable)this.GetElement(lastElement)).ToString("G", invariant));
+            sb.Append('>');
 
-            return StringBuilderCache.GetStringAndRelease(sb);
+            return sb.ToString();
         }
     }
 }
index 0f5a0a6..09962fc 100644 (file)
@@ -174,20 +174,20 @@ namespace System.Runtime.Intrinsics
             ThrowHelper.ThrowForUnsupportedVectorBaseType<T>();
 
             int lastElement = Count - 1;
-            StringBuilder sb = StringBuilderCache.Acquire();
+            var sb = new ValueStringBuilder(stackalloc char[64]);
             CultureInfo invariant = CultureInfo.InvariantCulture;
 
             sb.Append('<');
             for (int i = 0; i < lastElement; i++)
             {
-                sb.Append(((IFormattable)this.GetElement(i)).ToString("G", invariant))
-                 .Append(',')
-                 .Append(' ');
+                sb.Append(((IFormattable)this.GetElement(i)).ToString("G", invariant));
+                sb.Append(',');
+                sb.Append(' ');
             }
-            sb.Append(((IFormattable)this.GetElement(lastElement)).ToString("G", invariant))
-             .Append('>');
+            sb.Append(((IFormattable)this.GetElement(lastElement)).ToString("G", invariant));
+            sb.Append('>');
 
-            return StringBuilderCache.GetStringAndRelease(sb);
+            return sb.ToString();
         }
     }
 }
index c1fdcf8..3c35b31 100644 (file)
@@ -127,20 +127,20 @@ namespace System.Runtime.Intrinsics
             ThrowHelper.ThrowForUnsupportedVectorBaseType<T>();
 
             int lastElement = Count - 1;
-            StringBuilder sb = StringBuilderCache.Acquire();
+            var sb = new ValueStringBuilder(stackalloc char[64]);
             CultureInfo invariant = CultureInfo.InvariantCulture;
 
             sb.Append('<');
             for (int i = 0; i < lastElement; i++)
             {
-                sb.Append(((IFormattable)this.GetElement(i)).ToString("G", invariant))
-                 .Append(',')
-                 .Append(' ');
+                sb.Append(((IFormattable)this.GetElement(i)).ToString("G", invariant));
+                sb.Append(',');
+                sb.Append(' ');
             }
-            sb.Append(((IFormattable)this.GetElement(lastElement)).ToString("G", invariant))
-             .Append('>');
+            sb.Append(((IFormattable)this.GetElement(lastElement)).ToString("G", invariant));
+            sb.Append('>');
 
-            return StringBuilderCache.GetStringAndRelease(sb);
+            return sb.ToString();
         }
     }
 }
index 48ce4a5..4d703da 100644 (file)
@@ -159,7 +159,7 @@ namespace System
 
                     // Create the StringBuilder, add the chars we've already enumerated,
                     // add the rest, and then get the resulting string.
-                    StringBuilder result = StringBuilderCache.Acquire();
+                    var result = new ValueStringBuilder(stackalloc char[256]);
                     result.Append(c); // first value
                     do
                     {
@@ -167,7 +167,7 @@ namespace System
                         result.Append(c);
                     }
                     while (en.MoveNext());
-                    return StringBuilderCache.GetStringAndRelease(result);
+                    return result.ToString();
                 }
             }
             else
@@ -195,7 +195,7 @@ namespace System
                         return firstString ?? string.Empty;
                     }
 
-                    StringBuilder result = StringBuilderCache.Acquire();
+                    var result = new ValueStringBuilder(stackalloc char[256]);
 
                     result.Append(firstString);
 
@@ -210,7 +210,7 @@ namespace System
                     }
                     while (en.MoveNext());
 
-                    return StringBuilderCache.GetStringAndRelease(result);
+                    return result.ToString();
                 }
             }
         }
@@ -232,7 +232,8 @@ namespace System
                     return firstValue ?? string.Empty;
                 }
 
-                StringBuilder result = StringBuilderCache.Acquire();
+                var result = new ValueStringBuilder(stackalloc char[256]);
+
                 result.Append(firstValue);
 
                 do
@@ -241,7 +242,7 @@ namespace System
                 }
                 while (en.MoveNext());
 
-                return StringBuilderCache.GetStringAndRelease(result);
+                return result.ToString();
             }
         }
 
@@ -522,10 +523,10 @@ namespace System
             if (format == null)
                 throw new ArgumentNullException(nameof(format));
 
-            return StringBuilderCache.GetStringAndRelease(
-                StringBuilderCache
-                    .Acquire(format.Length + args.Length * 8)
-                    .AppendFormatHelper(provider, format, args));
+            var sb = new ValueStringBuilder(stackalloc char[256]);
+            sb.EnsureCapacity(format.Length + args.Length * 8);
+            sb.AppendFormatHelper(provider, format, args);
+            return sb.ToString();
         }
 
         public string Insert(int startIndex, string value)
@@ -646,7 +647,8 @@ namespace System
                 }
 
                 // Null separator and values are handled by the StringBuilder
-                StringBuilder result = StringBuilderCache.Acquire();
+                var result = new ValueStringBuilder(stackalloc char[256]);
+
                 result.Append(firstValue);
 
                 do
@@ -656,7 +658,7 @@ namespace System
                 }
                 while (en.MoveNext());
 
-                return StringBuilderCache.GetStringAndRelease(result);
+                return result.ToString();
             }
         }
 
@@ -691,7 +693,8 @@ namespace System
                 return firstString ?? string.Empty;
             }
 
-            StringBuilder result = StringBuilderCache.Acquire();
+            var result = new ValueStringBuilder(stackalloc char[256]);
+
             result.Append(firstString);
 
             for (int i = 1; i < values.Length; i++)
@@ -704,7 +707,7 @@ namespace System
                 }
             }
 
-            return StringBuilderCache.GetStringAndRelease(result);
+            return result.ToString();
         }
 
         private static unsafe string JoinCore<T>(char* separator, int separatorLength, IEnumerable<T> values)
@@ -739,7 +742,7 @@ namespace System
                     return firstString ?? string.Empty;
                 }
 
-                StringBuilder result = StringBuilderCache.Acquire();
+                var result = new ValueStringBuilder(stackalloc char[256]);
 
                 result.Append(firstString);
 
@@ -755,7 +758,7 @@ namespace System
                 }
                 while (en.MoveNext());
 
-                return StringBuilderCache.GetStringAndRelease(result);
+                return result.ToString();
             }
         }
 
@@ -1003,7 +1006,8 @@ namespace System
             newValue ??= string.Empty;
 
             CultureInfo referenceCulture = culture ?? CultureInfo.CurrentCulture;
-            StringBuilder result = StringBuilderCache.Acquire();
+            var result = new ValueStringBuilder(stackalloc char[256]);
+            result.EnsureCapacity(this.Length);
 
             int startIndex = 0;
             int index = 0;
@@ -1019,7 +1023,7 @@ namespace System
                 if (index >= 0)
                 {
                     // append the unmodified portion of string
-                    result.Append(this, startIndex, index - startIndex);
+                    result.Append(this.AsSpan(startIndex, index - startIndex));
 
                     // append the replacement
                     result.Append(newValue);
@@ -1032,16 +1036,16 @@ namespace System
                     // small optimization,
                     // if we have not done any replacements,
                     // we will return the original string
-                    StringBuilderCache.Release(result);
+                    result.Dispose();
                     return this;
                 }
                 else
                 {
-                    result.Append(this, startIndex, this.Length - startIndex);
+                    result.Append(this.AsSpan(startIndex, this.Length - startIndex));
                 }
             } while (index >= 0);
 
-            return StringBuilderCache.GetStringAndRelease(result);
+            return result.ToString();
         }
 
         // Replaces all instances of oldChar with newChar.
index f9c5fb4..849aca2 100644 (file)
@@ -1579,8 +1579,8 @@ namespace System.Text
                             FormatError();
                         }
                     }
-                    // Is it a opening brace?
-                    if (ch == '{')
+                    // Is it an opening brace?
+                    else if (ch == '{')
                     {
                         // Check next character (if there is one) to see if it is escaped. eg {{
                         if (pos < len && format[pos] == '{')
@@ -1594,7 +1594,7 @@ namespace System.Text
                             break;
                         }
                     }
-                    // If it neither then treat the character as just text.
+                    // If it's neither then treat the character as just text.
                     Append(ch);
                 }
 
diff --git a/src/libraries/System.Private.CoreLib/src/System/Text/ValueStringBuilder.AppendFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Text/ValueStringBuilder.AppendFormat.cs
new file mode 100644 (file)
index 0000000..b54bf77
--- /dev/null
@@ -0,0 +1,355 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+namespace System.Text
+{
+    internal ref partial struct ValueStringBuilder
+    {
+        public void AppendFormat(string format, object? arg0) => AppendFormatHelper(null, format, new ParamsArray(arg0));
+
+        public void AppendFormat(string format, object? arg0, object? arg1) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1));
+
+        public void AppendFormat(string format, object? arg0, object? arg1, object? arg2) => AppendFormatHelper(null, format, new ParamsArray(arg0, arg1, arg2));
+
+        public void AppendFormat(string format, params object?[] args)
+        {
+            if (args == null)
+            {
+                // To preserve the original exception behavior, throw an exception about format if both
+                // args and format are null. The actual null check for format is in AppendFormatHelper.
+                string paramName = (format == null) ? nameof(format) : nameof(args);
+                throw new ArgumentNullException(paramName);
+            }
+
+            AppendFormatHelper(null, format, new ParamsArray(args));
+        }
+
+        public void AppendFormat(IFormatProvider? provider, string format, object? arg0) => AppendFormatHelper(provider, format, new ParamsArray(arg0));
+
+        public void AppendFormat(IFormatProvider? provider, string format, object? arg0, object? arg1) => AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1));
+
+        public void AppendFormat(IFormatProvider? provider, string format, object? arg0, object? arg1, object? arg2) => AppendFormatHelper(provider, format, new ParamsArray(arg0, arg1, arg2));
+
+        public void AppendFormat(IFormatProvider? provider, string format, params object?[] args)
+        {
+            if (args == null)
+            {
+                // To preserve the original exception behavior, throw an exception about format if both
+                // args and format are null. The actual null check for format is in AppendFormatHelper.
+                string paramName = (format == null) ? nameof(format) : nameof(args);
+                throw new ArgumentNullException(paramName);
+            }
+
+            AppendFormatHelper(provider, format, new ParamsArray(args));
+        }
+
+        private void AppendSpanFormattable<T>(T value) where T : ISpanFormattable
+        {
+            if (value.TryFormat(_chars.Slice(_pos), out int charsWritten, format: default, provider: null))
+            {
+                _pos += charsWritten;
+            }
+            else
+            {
+                string? s = value.ToString();
+                if (s != null)
+                {
+                    Append(s);
+                }
+            }
+        }
+
+        internal void AppendSpanFormattable<T>(T value, string? format, IFormatProvider? provider) where T : ISpanFormattable, IFormattable
+        {
+            if (value.TryFormat(_chars.Slice(_pos), out int charsWritten, format, provider))
+            {
+                _pos += charsWritten;
+            }
+            else
+            {
+                Append(value.ToString(format, provider));
+            }
+        }
+
+        // Copied from StringBuilder, can't be done via generic extension
+        // as ValueStringBuilder is a ref struct and cannot be used in a generic.
+        internal void AppendFormatHelper(IFormatProvider? provider, string format, ParamsArray args)
+        {
+            // Undocumented exclusive limits on the range for Argument Hole Index and Argument Hole Alignment.
+            const int IndexLimit = 1000000; // Note:            0 <= ArgIndex < IndexLimit
+            const int WidthLimit = 1000000; // Note:  -WidthLimit <  ArgAlign < WidthLimit
+
+            if (format == null)
+            {
+                throw new ArgumentNullException(nameof(format));
+            }
+
+            int pos = 0;
+            int len = format.Length;
+            char ch = '\0';
+            ICustomFormatter? cf = (ICustomFormatter?)provider?.GetFormat(typeof(ICustomFormatter));
+
+            while (true)
+            {
+                while (pos < len)
+                {
+                    ch = format[pos];
+
+                    pos++;
+                    // Is it a closing brace?
+                    if (ch == '}')
+                    {
+                        // Check next character (if there is one) to see if it is escaped. eg }}
+                        if (pos < len && format[pos] == '}')
+                        {
+                            pos++;
+                        }
+                        else
+                        {
+                            // Otherwise treat it as an error (Mismatched closing brace)
+                            ThrowFormatError();
+                        }
+                    }
+                    // Is it a opening brace?
+                    else if (ch == '{')
+                    {
+                        // Check next character (if there is one) to see if it is escaped. eg {{
+                        if (pos < len && format[pos] == '{')
+                        {
+                            pos++;
+                        }
+                        else
+                        {
+                            // Otherwise treat it as the opening brace of an Argument Hole.
+                            pos--;
+                            break;
+                        }
+                    }
+                    // If it's neither then treat the character as just text.
+                    Append(ch);
+                }
+
+                //
+                // Start of parsing of Argument Hole.
+                // Argument Hole ::= { Index (, WS* Alignment WS*)? (: Formatting)? }
+                //
+                if (pos == len)
+                {
+                    break;
+                }
+
+                //
+                //  Start of parsing required Index parameter.
+                //  Index ::= ('0'-'9')+ WS*
+                //
+                pos++;
+                // If reached end of text then error (Unexpected end of text)
+                // or character is not a digit then error (Unexpected Character)
+                if (pos == len || (ch = format[pos]) < '0' || ch > '9') ThrowFormatError();
+                int index = 0;
+                do
+                {
+                    index = index * 10 + ch - '0';
+                    pos++;
+                    // If reached end of text then error (Unexpected end of text)
+                    if (pos == len)
+                    {
+                        ThrowFormatError();
+                    }
+                    ch = format[pos];
+                    // so long as character is digit and value of the index is less than 1000000 ( index limit )
+                }
+                while (ch >= '0' && ch <= '9' && index < IndexLimit);
+
+                // If value of index is not within the range of the arguments passed in then error (Index out of range)
+                if (index >= args.Length)
+                {
+                    throw new FormatException(SR.Format_IndexOutOfRange);
+                }
+
+                // Consume optional whitespace.
+                while (pos < len && (ch = format[pos]) == ' ') pos++;
+                // End of parsing index parameter.
+
+                //
+                //  Start of parsing of optional Alignment
+                //  Alignment ::= comma WS* minus? ('0'-'9')+ WS*
+                //
+                bool leftJustify = false;
+                int width = 0;
+                // Is the character a comma, which indicates the start of alignment parameter.
+                if (ch == ',')
+                {
+                    pos++;
+
+                    // Consume Optional whitespace
+                    while (pos < len && format[pos] == ' ') pos++;
+
+                    // If reached the end of the text then error (Unexpected end of text)
+                    if (pos == len)
+                    {
+                        ThrowFormatError();
+                    }
+
+                    // Is there a minus sign?
+                    ch = format[pos];
+                    if (ch == '-')
+                    {
+                        // Yes, then alignment is left justified.
+                        leftJustify = true;
+                        pos++;
+                        // If reached end of text then error (Unexpected end of text)
+                        if (pos == len)
+                        {
+                            ThrowFormatError();
+                        }
+                        ch = format[pos];
+                    }
+
+                    // If current character is not a digit then error (Unexpected character)
+                    if (ch < '0' || ch > '9')
+                    {
+                        ThrowFormatError();
+                    }
+                    // Parse alignment digits.
+                    do
+                    {
+                        width = width * 10 + ch - '0';
+                        pos++;
+                        // If reached end of text then error. (Unexpected end of text)
+                        if (pos == len)
+                        {
+                            ThrowFormatError();
+                        }
+                        ch = format[pos];
+                        // So long a current character is a digit and the value of width is less than 100000 ( width limit )
+                    }
+                    while (ch >= '0' && ch <= '9' && width < WidthLimit);
+                    // end of parsing Argument Alignment
+                }
+
+                // Consume optional whitespace
+                while (pos < len && (ch = format[pos]) == ' ') pos++;
+
+                //
+                // Start of parsing of optional formatting parameter.
+                //
+                object? arg = args[index];
+
+                ReadOnlySpan<char> itemFormatSpan = default; // used if itemFormat is null
+                // Is current character a colon? which indicates start of formatting parameter.
+                if (ch == ':')
+                {
+                    pos++;
+                    int startPos = pos;
+
+                    while (true)
+                    {
+                        // If reached end of text then error. (Unexpected end of text)
+                        if (pos == len)
+                        {
+                            ThrowFormatError();
+                        }
+                        ch = format[pos];
+
+                        if (ch == '}')
+                        {
+                            // Argument hole closed
+                            break;
+                        }
+                        else if (ch == '{')
+                        {
+                            // Braces inside the argument hole are not supported
+                            ThrowFormatError();
+                        }
+
+                        pos++;
+                    }
+
+                    if (pos > startPos)
+                    {
+                        itemFormatSpan = format.AsSpan(startPos, pos - startPos);
+                    }
+                }
+                else if (ch != '}')
+                {
+                    // Unexpected character
+                    ThrowFormatError();
+                }
+
+                // Construct the output for this arg hole.
+                pos++;
+                string? s = null;
+                string? itemFormat = null;
+
+                if (cf != null)
+                {
+                    if (itemFormatSpan.Length != 0)
+                    {
+                        itemFormat = new string(itemFormatSpan);
+                    }
+                    s = cf.Format(itemFormat, arg, provider);
+                }
+
+                if (s == null)
+                {
+                    // If arg is ISpanFormattable and the beginning doesn't need padding,
+                    // try formatting it into the remaining current chunk.
+                    if (arg is ISpanFormattable spanFormattableArg &&
+                        (leftJustify || width == 0) &&
+                        spanFormattableArg.TryFormat(_chars.Slice(_pos), out int charsWritten, itemFormatSpan, provider))
+                    {
+                        _pos += charsWritten;
+
+                        // Pad the end, if needed.
+                        int padding = width - charsWritten;
+                        if (leftJustify && padding > 0)
+                        {
+                            Append(' ', padding);
+                        }
+
+                        // Continue to parse other characters.
+                        continue;
+                    }
+
+                    // Otherwise, fallback to trying IFormattable or calling ToString.
+                    if (arg is IFormattable formattableArg)
+                    {
+                        if (itemFormatSpan.Length != 0)
+                        {
+                            itemFormat ??= new string(itemFormatSpan);
+                        }
+                        s = formattableArg.ToString(itemFormat, provider);
+                    }
+                    else if (arg != null)
+                    {
+                        s = arg.ToString();
+                    }
+                }
+                // Append it to the final output of the Format String.
+                if (s == null)
+                {
+                    s = string.Empty;
+                }
+                int pad = width - s.Length;
+                if (!leftJustify && pad > 0)
+                {
+                    Append(' ', pad);
+                }
+
+                Append(s);
+                if (leftJustify && pad > 0)
+                {
+                    Append(' ', pad);
+                }
+                // Continue to parse other characters.
+            }
+        }
+
+        private static void ThrowFormatError()
+        {
+            throw new FormatException(SR.Format_InvalidString);
+        }
+    }
+}
index 617a90d..b62140b 100644 (file)
@@ -140,8 +140,13 @@ namespace System.Text
             _pos += count;
         }
 
-        public void Insert(int index, string s)
+        public void Insert(int index, string? s)
         {
+            if (s == null)
+            {
+                return;
+            }
+
             int count = s.Length;
 
             if (_pos > (_chars.Length - count))
@@ -171,8 +176,13 @@ namespace System.Text
         }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
-        public void Append(string s)
+        public void Append(string? s)
         {
+            if (s == null)
+            {
+                return;
+            }
+
             int pos = _pos;
             if (s.Length == 1 && (uint)pos < (uint)_chars.Length) // very common case, e.g. appending strings from NumberFormatInfo like separators, percent symbols, etc.
             {
index e5471e4..feea5de 100644 (file)
@@ -42,20 +42,20 @@ namespace System
             /// </summary>
             public static string GetSerializedString(TimeZoneInfo zone)
             {
-                StringBuilder serializedText = StringBuilderCache.Acquire();
+                var serializedText = new ValueStringBuilder(stackalloc char[InitialCapacityForString]);
 
                 //
                 // <_id>;<_baseUtcOffset>;<_displayName>;<_standardDisplayName>;<_daylightDispayName>
                 //
-                SerializeSubstitute(zone.Id, serializedText);
+                SerializeSubstitute(zone.Id, ref serializedText);
                 serializedText.Append(Sep);
                 serializedText.AppendSpanFormattable(zone.BaseUtcOffset.TotalMinutes, format: default, CultureInfo.InvariantCulture);
                 serializedText.Append(Sep);
-                SerializeSubstitute(zone.DisplayName, serializedText);
+                SerializeSubstitute(zone.DisplayName, ref serializedText);
                 serializedText.Append(Sep);
-                SerializeSubstitute(zone.StandardName, serializedText);
+                SerializeSubstitute(zone.StandardName, ref serializedText);
                 serializedText.Append(Sep);
-                SerializeSubstitute(zone.DaylightName, serializedText);
+                SerializeSubstitute(zone.DaylightName, ref serializedText);
                 serializedText.Append(Sep);
 
                 AdjustmentRule[] rules = zone.GetAdjustmentRules();
@@ -69,9 +69,9 @@ namespace System
                     serializedText.AppendSpanFormattable(rule.DaylightDelta.TotalMinutes, format: default, CultureInfo.InvariantCulture);
                     serializedText.Append(Sep);
                     // serialize the TransitionTime's
-                    SerializeTransitionTime(rule.DaylightTransitionStart, serializedText);
+                    SerializeTransitionTime(rule.DaylightTransitionStart, ref serializedText);
                     serializedText.Append(Sep);
-                    SerializeTransitionTime(rule.DaylightTransitionEnd, serializedText);
+                    SerializeTransitionTime(rule.DaylightTransitionEnd, ref serializedText);
                     serializedText.Append(Sep);
                     if (rule.BaseUtcOffsetDelta != TimeSpan.Zero)
                     {
@@ -89,7 +89,7 @@ namespace System
                 }
                 serializedText.Append(Sep);
 
-                return StringBuilderCache.GetStringAndRelease(serializedText);
+                return serializedText.ToString();
             }
 
             /// <summary>
@@ -135,7 +135,7 @@ namespace System
             /// "]" -> "\]"
             /// "\" -> "\\"
             /// </summary>
-            private static void SerializeSubstitute(string text, StringBuilder serializedText)
+            private static void SerializeSubstitute(string text, ref ValueStringBuilder serializedText)
             {
                 foreach (char c in text)
                 {
@@ -150,7 +150,7 @@ namespace System
             /// <summary>
             /// Helper method to serialize a TimeZoneInfo.TransitionTime object.
             /// </summary>
-            private static void SerializeTransitionTime(TransitionTime time, StringBuilder serializedText)
+            private static void SerializeTransitionTime(TransitionTime time, ref ValueStringBuilder serializedText)
             {
                 serializedText.Append(Lhs);
                 serializedText.Append(time.IsFixedDateRule ? '1' : '0');
@@ -265,7 +265,7 @@ namespace System
                     throw new SerializationException(SR.Serialization_InvalidData);
                 }
                 State tokenState = State.NotEscaped;
-                StringBuilder token = StringBuilderCache.Acquire(InitialCapacityForString);
+                var token = new ValueStringBuilder(stackalloc char[InitialCapacityForString]);
 
                 // walk the serialized text, building up the token as we go...
                 for (int i = _currentTokenStartIndex; i < _serializedText.Length; i++)
@@ -302,7 +302,7 @@ namespace System
                                 {
                                     _state = State.StartOfToken;
                                 }
-                                return StringBuilderCache.GetStringAndRelease(token);
+                                return token.ToString();
 
                             case '\0':
                                 // invalid character