private static bool TryFormatFloatingPoint <T>(T value, Span <byte> destination, out int bytesWritten, StandardFormat format) where T : IFormattable, ISpanFormattable { Span <char> formatText = stackalloc char[0]; if (!format.IsDefault) { formatText = stackalloc char[StandardFormat.FormatStringLength]; int formatTextLength = format.Format(formatText); formatText = formatText.Slice(0, formatTextLength); } // We first try to format into a stack-allocated buffer, and if it succeeds, we can avoid // all allocation. If that fails, we fall back to allocating strings. If it proves impactful, // that allocation (as well as roundtripping from byte to char and back to byte) could be avoided by // calling into a refactored Number.FormatSingle/Double directly. const int StackBufferLength = 128; // large enough to handle the majority cases Span <char> stackBuffer = stackalloc char[StackBufferLength]; ReadOnlySpan <char> utf16Text = stackalloc char[0]; // Try to format into the stack buffer. If we're successful, we can avoid all allocations. if (value.TryFormat(stackBuffer, out int formattedLength, formatText, CultureInfo.InvariantCulture)) { utf16Text = stackBuffer.Slice(0, formattedLength); }
private static bool TryFormatFloatingPoint <T>(T value, Span <byte> destination, out int bytesWritten, StandardFormat format) where T : IFormattable, ISpanFormattable { if (format.IsDefault) { format = 'G'; } switch (format.Symbol) { case 'g': case 'G': if (format.Precision != StandardFormat.NoPrecision) { throw new NotSupportedException(SR.Argument_GWithPrecisionNotSupported); } break; case 'f': case 'F': case 'e': case 'E': break; default: return(FormattingHelpers.TryFormatThrowFormatException(out bytesWritten)); } Span <char> formatText = stackalloc char[StandardFormat.FormatStringLength]; int formattedLength = format.Format(formatText); formatText = formatText.Slice(0, formattedLength); // We first try to format into a stack-allocated buffer, and if it succeeds, we can avoid // all allocation. If that fails, we fall back to allocating strings. If it proves impactful, // that allocation (as well as roundtripping from byte to char and back to byte) could be avoided by // calling into a refactored Number.FormatSingle/Double directly. const int StackBufferLength = 128; // large enough to handle the majority cases Span <char> stackBuffer = stackalloc char[StackBufferLength]; ReadOnlySpan <char> utf16Text = stackalloc char[0]; // Try to format into the stack buffer. If we're successful, we can avoid all allocations. if (value.TryFormat(stackBuffer, out formattedLength, formatText, CultureInfo.InvariantCulture)) { utf16Text = stackBuffer.Slice(0, formattedLength); } else { // The stack buffer wasn't large enough. If the destination buffer isn't at least as // big as the stack buffer, we know the whole operation will eventually fail, so we // can just fail now. if (destination.Length <= StackBufferLength) { bytesWritten = 0; return(false); } // Fall back to using a string format and allocating a string for the resulting formatted value. utf16Text = value.ToString(new string(formatText), CultureInfo.InvariantCulture); } // Copy the value to the destination, if it's large enough. if (utf16Text.Length > destination.Length) { bytesWritten = 0; return(false); } for (int i = 0; i < utf16Text.Length; i++) { Debug.Assert(utf16Text[i] < 128, "A culture-invariant ToString() of a floating point expected to produce ASCII characters only."); destination[i] = (byte)utf16Text[i]; } bytesWritten = utf16Text.Length; return(true); }