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)
sb.Append(c);
}
- return StringBuilderCache.GetStringAndRelease(sb);
+ return sb.ToString();
}
private static SafeTypeNameParserHandle? CreateTypeNameParser(string typeName, bool throwOnError)
<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" />
/// </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();
}
}
// 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)
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>
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();
}
}
}
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();
}
}
}
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();
}
}
}
// 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
{
result.Append(c);
}
while (en.MoveNext());
- return StringBuilderCache.GetStringAndRelease(result);
+ return result.ToString();
}
}
else
return firstString ?? string.Empty;
}
- StringBuilder result = StringBuilderCache.Acquire();
+ var result = new ValueStringBuilder(stackalloc char[256]);
result.Append(firstString);
}
while (en.MoveNext());
- return StringBuilderCache.GetStringAndRelease(result);
+ return result.ToString();
}
}
}
return firstValue ?? string.Empty;
}
- StringBuilder result = StringBuilderCache.Acquire();
+ var result = new ValueStringBuilder(stackalloc char[256]);
+
result.Append(firstValue);
do
}
while (en.MoveNext());
- return StringBuilderCache.GetStringAndRelease(result);
+ return result.ToString();
}
}
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)
}
// Null separator and values are handled by the StringBuilder
- StringBuilder result = StringBuilderCache.Acquire();
+ var result = new ValueStringBuilder(stackalloc char[256]);
+
result.Append(firstValue);
do
}
while (en.MoveNext());
- return StringBuilderCache.GetStringAndRelease(result);
+ return result.ToString();
}
}
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++)
}
}
- return StringBuilderCache.GetStringAndRelease(result);
+ return result.ToString();
}
private static unsafe string JoinCore<T>(char* separator, int separatorLength, IEnumerable<T> values)
return firstString ?? string.Empty;
}
- StringBuilder result = StringBuilderCache.Acquire();
+ var result = new ValueStringBuilder(stackalloc char[256]);
result.Append(firstString);
}
while (en.MoveNext());
- return StringBuilderCache.GetStringAndRelease(result);
+ return result.ToString();
}
}
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;
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);
// 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.
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] == '{')
break;
}
}
- // If it neither then treat the character as just text.
+ // If it's neither then treat the character as just text.
Append(ch);
}
--- /dev/null
+// 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);
+ }
+ }
+}
_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))
}
[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.
{
/// </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();
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)
{
}
serializedText.Append(Sep);
- return StringBuilderCache.GetStringAndRelease(serializedText);
+ return serializedText.ToString();
}
/// <summary>
/// "]" -> "\]"
/// "\" -> "\\"
/// </summary>
- private static void SerializeSubstitute(string text, StringBuilder serializedText)
+ private static void SerializeSubstitute(string text, ref ValueStringBuilder serializedText)
{
foreach (char c in text)
{
/// <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');
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++)
{
_state = State.StartOfToken;
}
- return StringBuilderCache.GetStringAndRelease(token);
+ return token.ToString();
case '\0':
// invalid character