/// includes the format items themselves, e.g. "{0}", and since it's rare to have double-digit
/// numbers of items, we bump the 8 up to 11 to account for the three extra characters in "{d}",
/// since the compiler-provided base length won't include the equivalent character count.
/// </remarks>
privateconstintGuessedLengthPerHole=11;
/// <summary>Minimum size array to rent from the pool.</summary>
/// <remarks>Same as stack-allocation size used today by string.Format.</remarks>
privateconstintMinimumArrayPoolLength=256;
/// <summary>Optional provider to pass to IFormattable.ToString or ISpanFormattable.TryFormat calls.</summary>
privatereadonlyIFormatProvider?_provider;
/// <summary>Array rented from the array pool and used to back <see cref="_chars"/>.</summary>
privatechar[]?_arrayToReturnToPool;
/// <summary>The span to write into.</summary>
privateSpan<char>_chars;
/// <summary>Position at which to write the next character.</summary>
privateint_pos;
/// <summary>Whether <see cref="_provider"/> provides an ICustomFormatter.</summary>
/// <remarks>
/// Custom formatters are very rare. We want to support them, but it's ok if we make them more expensive
/// in order to make them as pay-for-play as possible. So, we avoid adding another reference type field
/// to reduce the size of the handler and to reduce required zero'ing, by only storing whether the provider
/// provides a formatter, rather than actually storing the formatter. This in turn means, if there is a
/// formatter, we pay for the extra interface call on each AppendFormatted that needs it.
/// </remarks>
privatereadonlybool_hasCustomFormatter;
/// <summary>Creates a handler used to translate an interpolated string into a <see cref="string"/>.</summary>
/// <param name="literalLength">The number of constant characters outside of interpolation expressions in the interpolated string.</param>
/// <param name="formattedCount">The number of interpolation expressions in the interpolated string.</param>
/// <remarks>This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly.</remarks>
/// <summary>Creates a handler used to translate an interpolated string into a <see cref="string"/>.</summary>
/// <param name="literalLength">The number of constant characters outside of interpolation expressions in the interpolated string.</param>
/// <param name="formattedCount">The number of interpolation expressions in the interpolated string.</param>
/// <param name="provider">An object that supplies culture-specific formatting information.</param>
/// <remarks>This is intended to be called only by compiler-generated code. Arguments are not validated as they'd otherwise be for members intended to be used directly.</remarks>
// Check first for IFormattable, even though we'll prefer to use ISpanFormattable, as the latter
// requires the former. For value types, it won't matter as the type checks devolve into
// JIT-time constants. For reference types, they're more likely to implement IFormattable
// than they are to implement ISpanFormattable: if they don't implement either, we save an
// interface check over first checking for ISpanFormattable and then for IFormattable, and
// if it only implements IFormattable, we come out even: only if it implements both do we
// end up paying for an extra interface check.
string?s;
if(valueisIFormattable)
{
// If the value can format itself directly into our buffer, do so.
if(valueisISpanFormattable)
{
intcharsWritten;
while(!((ISpanFormattable)value).TryFormat(_chars[_pos..],outcharsWritten,format,_provider))// constrained call avoiding boxing for value types
{
Grow();
}
_pos+=charsWritten;
return;
}
s=((IFormattable)value).ToString(format,_provider);// constrained call avoiding boxing for value types
}
else
{
s=value?.ToString();
}
if(sisnotnull)
{
AppendStringDirect(s);
}
}
/// <summary>Writes the specified value to the handler.</summary>
/// <param name="value">The value to write.</param>
/// <param name="alignment">Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value.</param>
/// <summary>Writes the specified value to the handler.</summary>
/// <param name="value">The value to write.</param>
/// <param name="format">The format string.</param>
/// <param name="alignment">Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value.</param>
// Fast path for when the value fits in the current buffer
if(value.TryCopyTo(_chars[_pos..]))
{
_pos+=value.Length;
}
else
{
GrowThenCopySpan(value);
}
}
/// <summary>Writes the specified string of chars to the handler.</summary>
/// <param name="value">The span to write.</param>
/// <param name="alignment">Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value.</param>
/// <param name="format">The format string.</param>
/// <summary>Writes the specified value to the handler.</summary>
/// <param name="value">The value to write.</param>
/// <remarks>
/// Slow path to handle a custom formatter, potentially null value,
/// or a string that doesn't fit in the current buffer.
/// </remarks>
[MethodImpl(MethodImplOptions.NoInlining)]
privatevoidAppendFormattedSlow(string?value)
{
if(_hasCustomFormatter)
{
AppendCustomFormatter(value,format:null);
}
elseif(valueisnotnull)
{
EnsureCapacityForAdditionalChars(value.Length);
value.CopyTo(_chars[_pos..]);
_pos+=value.Length;
}
}
/// <summary>Writes the specified value to the handler.</summary>
/// <param name="value">The value to write.</param>
/// <param name="alignment">Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value.</param>
/// <param name="format">The format string.</param>
// Format is meaningless for strings and doesn't make sense for someone to specify. We have the overload
// simply to disambiguate between ROS<char> and object, just in case someone does specify a format, as
// string is implicitly convertible to both. Just delegate to the T-based implementation.
AppendFormatted<string?>(value,alignment,format);
#endregion
#regionAppendFormattedobject
/// <summary>Writes the specified value to the handler.</summary>
/// <param name="value">The value to write.</param>
/// <param name="alignment">Minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value.</param>
/// <param name="format">The format string.</param>
/// <summary>Handles adding any padding required for aligning a formatted value in an interpolation expression.</summary>
/// <param name="startingPos">The position at which the written value started.</param>
/// <param name="alignment">Non-zero minimum number of characters that should be written for this value. If the value is negative, it indicates left-aligned and the required minimum is the absolute value.</param>