@@ -366,13 +366,28 @@ private static unsafe void DecimalToNumber(decimal value, ref NumberBuffer numbe
366366
367367 public static string FormatDouble ( double value , string format , NumberFormatInfo info )
368368 {
369- ValueStringBuilder sb ;
370- unsafe
371- {
372- char * stackPtr = stackalloc char [ CharStackBufferSize ] ;
373- sb = new ValueStringBuilder ( new Span < char > ( stackPtr , CharStackBufferSize ) ) ;
374- }
369+ Span < char > stackBuffer = stackalloc char [ CharStackBufferSize ] ;
370+ var sb = new ValueStringBuilder ( stackBuffer ) ;
371+ return FormatDouble ( ref sb , value , format , info ) ?? sb . ToString ( ) ;
372+ }
373+
374+ public static bool TryFormatDouble ( double value , ReadOnlySpan < char > format , NumberFormatInfo info , Span < char > destination , out int charsWritten )
375+ {
376+ Span < char > stackBuffer = stackalloc char [ CharStackBufferSize ] ;
377+ var sb = new ValueStringBuilder ( stackBuffer ) ;
378+ string s = FormatDouble ( ref sb , value , format , info ) ;
379+ return s != null ?
380+ TryCopyTo ( s , destination , out charsWritten ) :
381+ sb . TryCopyTo ( destination , out charsWritten ) ;
382+ }
375383
384+ /// <summary>Formats the specified value according to the specified format and info.</summary>
385+ /// <returns>
386+ /// Non-null if an existing string can be returned, in which case the builder will be unmodified.
387+ /// Null if no existing string was returned, in which case the formatted output is in the builder.
388+ /// </returns>
389+ private static string FormatDouble ( ref ValueStringBuilder sb , double value , ReadOnlySpan < char > format , NumberFormatInfo info )
390+ {
376391 char fmt = ParseFormatSpecifier ( format , out int digits ) ;
377392 int precision = DoublePrecision ;
378393 NumberBuffer number = default ;
@@ -405,7 +420,7 @@ public static string FormatDouble(double value, string format, NumberFormatInfo
405420 NumberToString ( ref sb , ref number , 'G' , 17 , info , isDecimal : false ) ;
406421 }
407422
408- return sb . ToString ( ) ;
423+ return null ;
409424 }
410425
411426 case 'E' :
@@ -446,18 +461,33 @@ public static string FormatDouble(double value, string format, NumberFormatInfo
446461 NumberToStringFormat ( ref sb , ref number , format , info ) ;
447462 }
448463
449- return sb . ToString ( ) ;
464+ return null ;
450465 }
451466
452467 public static string FormatSingle ( float value , string format , NumberFormatInfo info )
453468 {
454- ValueStringBuilder sb ;
455- unsafe
456- {
457- char * stackPtr = stackalloc char [ CharStackBufferSize ] ;
458- sb = new ValueStringBuilder ( new Span < char > ( stackPtr , CharStackBufferSize ) ) ;
459- }
469+ Span < char > stackBuffer = stackalloc char [ CharStackBufferSize ] ;
470+ var sb = new ValueStringBuilder ( stackBuffer ) ;
471+ return FormatSingle ( ref sb , value , format , info ) ?? sb . ToString ( ) ;
472+ }
473+
474+ public static bool TryFormatSingle ( float value , ReadOnlySpan < char > format , NumberFormatInfo info , Span < char > destination , out int charsWritten )
475+ {
476+ Span < char > stackBuffer = stackalloc char [ CharStackBufferSize ] ;
477+ var sb = new ValueStringBuilder ( stackBuffer ) ;
478+ string s = FormatSingle ( ref sb , value , format , info ) ;
479+ return s != null ?
480+ TryCopyTo ( s , destination , out charsWritten ) :
481+ sb . TryCopyTo ( destination , out charsWritten ) ;
482+ }
460483
484+ /// <summary>Formats the specified value according to the specified format and info.</summary>
485+ /// <returns>
486+ /// Non-null if an existing string can be returned, in which case the builder will be unmodified.
487+ /// Null if no existing string was returned, in which case the formatted output is in the builder.
488+ /// </returns>
489+ private static string FormatSingle ( ref ValueStringBuilder sb , float value , ReadOnlySpan < char > format , NumberFormatInfo info )
490+ {
461491 char fmt = ParseFormatSpecifier ( format , out int digits ) ;
462492 int precision = FloatPrecision ;
463493 NumberBuffer number = default ;
@@ -489,8 +519,7 @@ public static string FormatSingle(float value, string format, NumberFormatInfo i
489519 DoubleToNumber ( value , 9 , ref number ) ;
490520 NumberToString ( ref sb , ref number , 'G' , 9 , info , isDecimal : false ) ;
491521 }
492-
493- return sb . ToString ( ) ;
522+ return null ;
494523 }
495524
496525 case 'E' :
@@ -530,8 +559,21 @@ public static string FormatSingle(float value, string format, NumberFormatInfo i
530559 {
531560 NumberToStringFormat ( ref sb , ref number , format , info ) ;
532561 }
562+ return null ;
563+ }
533564
534- return sb . ToString ( ) ;
565+ private static bool TryCopyTo ( string source , Span < char > destination , out int charsWritten )
566+ {
567+ Debug . Assert ( source != null ) ;
568+
569+ if ( source . AsReadOnlySpan ( ) . TryCopyTo ( destination ) )
570+ {
571+ charsWritten = source . Length ;
572+ return true ;
573+ }
574+
575+ charsWritten = 0 ;
576+ return false ;
535577 }
536578
537579 public static string FormatInt32 ( int value , ReadOnlySpan < char > format , NumberFormatInfo info )
0 commit comments