diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
index 60ef1fbf28cad2..79f75cc2706728 100644
--- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
+++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx
@@ -2608,6 +2608,9 @@
Handle is not pinned.
+
+ Formatted string contains characters not representable as valid UTF-8.
+
Hashtable insert failed. Load factor too high. The most common cause is multiple threads writing to the Hashtable simultaneously.
diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/INumberBase.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/INumberBase.cs
index 4d943b19338889..4368605183d2e9 100644
--- a/src/libraries/System.Private.CoreLib/src/System/Numerics/INumberBase.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/INumberBase.cs
@@ -27,6 +27,7 @@ public interface INumberBase
ISubtractionOperators,
IUnaryPlusOperators,
IUnaryNegationOperators,
+ IUtf8SpanFormattable,
IUtf8SpanParsable
where TSelf : INumberBase?
{
@@ -297,7 +298,7 @@ static virtual TSelf Parse(ReadOnlySpan utf8Text, NumberStyles style, IFor
if (textMaxCharCount < 256)
{
utf16TextArray = null;
- utf16Text = stackalloc char[512];
+ utf16Text = stackalloc char[256];
}
else
{
@@ -425,7 +426,7 @@ static virtual bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IF
if (textMaxCharCount < 256)
{
utf16TextArray = null;
- utf16Text = stackalloc char[512];
+ utf16Text = stackalloc char[256];
}
else
{
@@ -456,6 +457,60 @@ static virtual bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IF
return succeeded;
}
+ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider)
+ {
+ char[]? utf16DestinationArray;
+ scoped Span utf16Destination;
+ int destinationMaxCharCount = Encoding.UTF8.GetMaxCharCount(utf8Destination.Length);
+
+ if (destinationMaxCharCount < 256)
+ {
+ utf16DestinationArray = null;
+ utf16Destination = stackalloc char[256];
+ }
+ else
+ {
+ utf16DestinationArray = ArrayPool.Shared.Rent(destinationMaxCharCount);
+ utf16Destination = utf16DestinationArray.AsSpan(0, destinationMaxCharCount);
+ }
+
+ if (!TryFormat(utf16Destination, out int charsWritten, format, provider))
+ {
+ if (utf16DestinationArray != null)
+ {
+ // Return rented buffers if necessary
+ ArrayPool.Shared.Return(utf16DestinationArray);
+ }
+
+ bytesWritten = 0;
+ return false;
+ }
+
+ // Make sure we slice the buffer to just the characters written
+ utf16Destination = utf16Destination.Slice(0, charsWritten);
+
+ OperationStatus utf8DestinationStatus = Utf8.FromUtf16(utf16Destination, utf8Destination, out _, out bytesWritten, replaceInvalidSequences: false);
+
+ if (utf16DestinationArray != null)
+ {
+ // Return rented buffers if necessary
+ ArrayPool.Shared.Return(utf16DestinationArray);
+ }
+
+ if (utf8DestinationStatus == OperationStatus.Done)
+ {
+ return true;
+ }
+
+ if (utf8DestinationStatus != OperationStatus.DestinationTooSmall)
+ {
+ ThrowHelper.ThrowInvalidOperationException_InvalidUtf8();
+ }
+
+ bytesWritten = 0;
+ return false;
+ }
+
static TSelf IUtf8SpanParsable.Parse(ReadOnlySpan utf8Text, IFormatProvider? provider)
{
// Convert text using stackalloc for <= 256 characters and ArrayPool otherwise
diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs
index 71e2904b8dfbb1..2d4700576196c7 100644
--- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs
+++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs
@@ -529,6 +529,12 @@ internal static void ThrowArraySegmentCtorValidationFailedExceptions(Array? arra
throw GetArraySegmentCtorValidationFailedException(array, offset, count);
}
+ [DoesNotReturn]
+ internal static void ThrowInvalidOperationException_InvalidUtf8()
+ {
+ throw new InvalidOperationException(SR.InvalidOperation_InvalidUtf8);
+ }
+
[DoesNotReturn]
internal static void ThrowFormatException_BadFormatSpecifier()
{
diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs
index 08e6907f7e80ea..58438a47c8c8f2 100644
--- a/src/libraries/System.Runtime/ref/System.Runtime.cs
+++ b/src/libraries/System.Runtime/ref/System.Runtime.cs
@@ -10793,7 +10793,7 @@ public partial interface IMultiplyOperators where TSelf
static virtual TResult operator checked *(TSelf left, TOther right) { throw null; }
static abstract TResult operator *(TSelf left, TOther right);
}
- public partial interface INumberBase : System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.IUtf8SpanParsable where TSelf : System.Numerics.INumberBase?
+ public partial interface INumberBase : System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.IUtf8SpanFormattable, System.IUtf8SpanParsable where TSelf : System.Numerics.INumberBase?
{
static abstract TSelf One { get; }
static abstract int Radix { get; }
@@ -10835,6 +10835,7 @@ static virtual TSelf CreateTruncating(TOther value)
static virtual TSelf Parse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider) { throw null; }
static abstract TSelf Parse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider);
static abstract TSelf Parse(string s, System.Globalization.NumberStyles style, System.IFormatProvider? provider);
+ bool System.IUtf8SpanFormattable.TryFormat(System.Span utf8Destination, out int bytesWritten, System.ReadOnlySpan format, System.IFormatProvider? provider) { throw null; }
static TSelf System.IUtf8SpanParsable.Parse(System.ReadOnlySpan utf8Text, System.IFormatProvider? provider) { throw null; }
static bool System.IUtf8SpanParsable.TryParse(System.ReadOnlySpan utf8Text, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out TSelf result) { throw null; }
protected static abstract bool TryConvertFromChecked(TOther value, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out TSelf result)