diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/StandardFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/StandardFormat.cs index 91dca61b2415a3..9a738100d8b7b9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/StandardFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/StandardFormat.cs @@ -40,6 +40,9 @@ namespace System.Buffers /// public bool HasPrecision => _precision != NoPrecision; + /// Gets the precision if one was specified; otherwise, 0. + internal byte PrecisionOrZero => _precision != NoPrecision ? _precision : (byte)0; + /// /// true if the StandardFormat == default(StandardFormat) /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.cs b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.cs index 303383a097e33b..0a55ad664ab270 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Buffers/Text/Utf8Formatter/Utf8Formatter.Integer.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace System.Buffers.Text { /// @@ -30,7 +32,7 @@ public static partial class Utf8Formatter /// System.FormatException if the format is not valid for this data type. /// public static bool TryFormat(byte value, Span destination, out int bytesWritten, StandardFormat format = default) => - FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); + TryFormat((uint)value, destination, out bytesWritten, format); /// /// Formats an SByte as a UTF8 string. @@ -55,7 +57,7 @@ public static bool TryFormat(byte value, Span destination, out int bytesWr /// [CLSCompliant(false)] public static bool TryFormat(sbyte value, Span destination, out int bytesWritten, StandardFormat format = default) => - FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); + TryFormat(value, 0xFF, destination, out bytesWritten, format); /// /// Formats a Unt16 as a UTF8 string. @@ -80,7 +82,7 @@ public static bool TryFormat(sbyte value, Span destination, out int bytesW /// [CLSCompliant(false)] public static bool TryFormat(ushort value, Span destination, out int bytesWritten, StandardFormat format = default) => - FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); + TryFormat((uint)value, destination, out bytesWritten, format); /// /// Formats an Int16 as a UTF8 string. @@ -104,7 +106,7 @@ public static bool TryFormat(ushort value, Span destination, out int bytes /// System.FormatException if the format is not valid for this data type. /// public static bool TryFormat(short value, Span destination, out int bytesWritten, StandardFormat format = default) => - FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); + TryFormat(value, 0xFFFF, destination, out bytesWritten, format); /// /// Formats a UInt32 as a UTF8 string. @@ -127,9 +129,38 @@ public static bool TryFormat(short value, Span destination, out int bytesW /// /// System.FormatException if the format is not valid for this data type. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] - public static bool TryFormat(uint value, Span destination, out int bytesWritten, StandardFormat format = default) => - FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); + public static bool TryFormat(uint value, Span destination, out int bytesWritten, StandardFormat format = default) + { + if (format.IsDefault) + { + return Number.TryUInt32ToDecStr(value, destination, out bytesWritten); + } + + switch (format.Symbol | 0x20) + { + case 'd': + return Number.TryUInt32ToDecStr(value, format.PrecisionOrZero, destination, out bytesWritten); + + case 'x': + return Number.TryInt32ToHexStr((int)value, Number.GetHexBase(format.Symbol), format.PrecisionOrZero, destination, out bytesWritten); + + case 'n': + return FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); + + case 'g' or 'r': + if (format.HasPrecision) + { + ThrowGWithPrecisionNotSupported(); + } + goto case 'd'; + + default: + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + goto case 'd'; + } + } /// /// Formats an Int32 as a UTF8 string. @@ -153,7 +184,43 @@ public static bool TryFormat(uint value, Span destination, out int bytesWr /// System.FormatException if the format is not valid for this data type. /// public static bool TryFormat(int value, Span destination, out int bytesWritten, StandardFormat format = default) => - FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); + TryFormat(value, ~0, destination, out bytesWritten, format); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + private static bool TryFormat(int value, int hexMask, Span destination, out int bytesWritten, StandardFormat format = default) + { + if (format.IsDefault) + { + return value >= 0 ? + Number.TryUInt32ToDecStr((uint)value, destination, out bytesWritten) : + Number.TryNegativeInt32ToDecStr(value, format.PrecisionOrZero, "-"u8, destination, out bytesWritten); + } + + switch (format.Symbol | 0x20) + { + case 'd': + return value >= 0 ? + Number.TryUInt32ToDecStr((uint)value, format.PrecisionOrZero, destination, out bytesWritten) : + Number.TryNegativeInt32ToDecStr(value, format.PrecisionOrZero, "-"u8, destination, out bytesWritten); + + case 'x': + return Number.TryInt32ToHexStr(value & hexMask, Number.GetHexBase(format.Symbol), format.PrecisionOrZero, destination, out bytesWritten); + + case 'n': + return FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); + + case 'g' or 'r': + if (format.HasPrecision) + { + ThrowGWithPrecisionNotSupported(); + } + goto case 'd'; + + default: + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + goto case 'd'; + } + } /// /// Formats a UInt64 as a UTF8 string. @@ -176,9 +243,38 @@ public static bool TryFormat(int value, Span destination, out int bytesWri /// /// System.FormatException if the format is not valid for this data type. /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] [CLSCompliant(false)] - public static bool TryFormat(ulong value, Span destination, out int bytesWritten, StandardFormat format = default) => - FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); + public static bool TryFormat(ulong value, Span destination, out int bytesWritten, StandardFormat format = default) + { + if (format.IsDefault) + { + return Number.TryUInt64ToDecStr(value, destination, out bytesWritten); + } + + switch (format.Symbol | 0x20) + { + case 'd': + return Number.TryUInt64ToDecStr(value, format.PrecisionOrZero, destination, out bytesWritten); + + case 'x': + return Number.TryInt64ToHexStr((long)value, Number.GetHexBase(format.Symbol), format.PrecisionOrZero, destination, out bytesWritten); + + case 'n': + return FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); + + case 'g' or 'r': + if (format.HasPrecision) + { + ThrowGWithPrecisionNotSupported(); + } + goto case 'd'; + + default: + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + goto case 'd'; + } + } /// /// Formats an Int64 as a UTF8 string. @@ -201,7 +297,44 @@ public static bool TryFormat(ulong value, Span destination, out int bytesW /// /// System.FormatException if the format is not valid for this data type. /// - public static bool TryFormat(long value, Span destination, out int bytesWritten, StandardFormat format = default) => - FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool TryFormat(long value, Span destination, out int bytesWritten, StandardFormat format = default) + { + if (format.IsDefault) + { + return value >= 0 ? + Number.TryUInt64ToDecStr((ulong)value, destination, out bytesWritten) : + Number.TryNegativeInt64ToDecStr(value, format.PrecisionOrZero, "-"u8, destination, out bytesWritten); + } + + switch (format.Symbol | 0x20) + { + case 'd': + return value >= 0 ? + Number.TryUInt64ToDecStr((ulong)value, format.PrecisionOrZero, destination, out bytesWritten) : + Number.TryNegativeInt64ToDecStr(value, format.PrecisionOrZero, "-"u8, destination, out bytesWritten); + + case 'x': + return Number.TryInt64ToHexStr(value, Number.GetHexBase(format.Symbol), format.PrecisionOrZero, destination, out bytesWritten); + + case 'n': + return FormattingHelpers.TryFormat(value, destination, out bytesWritten, format); + + case 'g' or 'r': + if (format.HasPrecision) + { + ThrowGWithPrecisionNotSupported(); + } + goto case 'd'; + + default: + ThrowHelper.ThrowFormatException_BadFormatSpecifier(); + goto case 'd'; + } + } + + private static void ThrowGWithPrecisionNotSupported() => + // With a precision, 'G' can produce exponential format, even for integers. + throw new NotSupportedException(SR.Argument_GWithPrecisionNotSupported); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs index 0a4d1ceaf65845..ed689b5d9ec774 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs @@ -907,7 +907,7 @@ private static bool TryCopyTo(string source, Span destination, out } } - private static char GetHexBase(char fmt) + internal static char GetHexBase(char fmt) { // The fmt-(X-A+10) hack has the effect of dictating whether we produce uppercase or lowercase // hex numbers for a-f. 'X' as the fmt code produces uppercase. 'x' as the format code produces lowercase. @@ -1675,7 +1675,7 @@ private static unsafe string NegativeInt32ToDecStr(int value, int digits, string return result; } - private static unsafe bool TryNegativeInt32ToDecStr(int value, int digits, ReadOnlySpan sNegative, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar + internal static unsafe bool TryNegativeInt32ToDecStr(int value, int digits, ReadOnlySpan sNegative, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); Debug.Assert(value < 0); @@ -1724,7 +1724,7 @@ private static unsafe string Int32ToHexStr(int value, char hexBase, int digits) return result; } - private static unsafe bool TryInt32ToHexStr(int value, char hexBase, int digits, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar + internal static unsafe bool TryInt32ToHexStr(int value, char hexBase, int digits, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); @@ -1999,7 +1999,7 @@ private static unsafe string UInt32ToDecStr(uint value, int digits) return result; } - private static unsafe bool TryUInt32ToDecStr(uint value, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar + internal static unsafe bool TryUInt32ToDecStr(uint value, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); @@ -2019,7 +2019,7 @@ private static unsafe bool TryUInt32ToDecStr(uint value, Span dest return false; } - private static unsafe bool TryUInt32ToDecStr(uint value, int digits, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar + internal static unsafe bool TryUInt32ToDecStr(uint value, int digits, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); @@ -2108,7 +2108,7 @@ private static unsafe string NegativeInt64ToDecStr(long value, int digits, strin return result; } - private static unsafe bool TryNegativeInt64ToDecStr(long value, int digits, ReadOnlySpan sNegative, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar + internal static unsafe bool TryNegativeInt64ToDecStr(long value, int digits, ReadOnlySpan sNegative, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); Debug.Assert(value < 0); @@ -2157,7 +2157,7 @@ private static unsafe string Int64ToHexStr(long value, char hexBase, int digits) return result; } - private static unsafe bool TryInt64ToHexStr(long value, char hexBase, int digits, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar + internal static unsafe bool TryInt64ToHexStr(long value, char hexBase, int digits, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); @@ -2427,7 +2427,7 @@ internal static unsafe string UInt64ToDecStr(ulong value, int digits) return result; } - private static unsafe bool TryUInt64ToDecStr(ulong value, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar + internal static unsafe bool TryUInt64ToDecStr(ulong value, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { Debug.Assert(typeof(TChar) == typeof(char) || typeof(TChar) == typeof(byte)); @@ -2448,7 +2448,7 @@ private static unsafe bool TryUInt64ToDecStr(ulong value, Span des return false; } - private static unsafe bool TryUInt64ToDecStr(ulong value, int digits, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar + internal static unsafe bool TryUInt64ToDecStr(ulong value, int digits, Span destination, out int charsWritten) where TChar : unmanaged, IUtfChar { int countedDigits = FormattingHelpers.CountDigits(value); int bufferLength = Math.Max(digits, countedDigits);