Optimize integral ToString (without generics) (#32528)
authorts2do <tsdodo@gmail.com>
Wed, 19 Feb 2020 07:18:47 +0000 (01:18 -0600)
committerGitHub <noreply@github.com>
Wed, 19 Feb 2020 07:18:46 +0000 (23:18 -0800)
- Make the fast path for Number.FormatXX & Number.TryFormatXX inlineable
- Make parameterless ToString in SByte, Int16, Int32, and Int64 directly invoke the default formatting method

src/libraries/System.Private.CoreLib/src/System/Int16.cs
src/libraries/System.Private.CoreLib/src/System/Int32.cs
src/libraries/System.Private.CoreLib/src/System/Int64.cs
src/libraries/System.Private.CoreLib/src/System/Number.Formatting.cs
src/libraries/System.Private.CoreLib/src/System/SByte.cs

index ed556ee..88bd953 100644 (file)
@@ -69,12 +69,12 @@ namespace System
 
         public override string ToString()
         {
-            return Number.FormatInt32(m_value, null, null);
+            return Number.Int32ToDecStr(m_value);
         }
 
         public string ToString(IFormatProvider? provider)
         {
-            return Number.FormatInt32(m_value, null, provider);
+            return ToString(null, provider);
         }
 
         public string ToString(string? format)
@@ -84,23 +84,12 @@ namespace System
 
         public string ToString(string? format, IFormatProvider? provider)
         {
-            if (m_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x'))
-            {
-                uint temp = (uint)(m_value & 0x0000FFFF);
-                return Number.FormatUInt32(temp, format, provider);
-            }
-
-            return Number.FormatInt32(m_value, format, provider);
+            return Number.FormatInt32(m_value, 0x0000FFFF, format, provider);
         }
 
         public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
         {
-            if (m_value < 0 && format.Length > 0 && (format[0] == 'X' || format[0] == 'x'))
-            {
-                uint temp = (uint)(m_value & 0x0000FFFF);
-                return Number.TryFormatUInt32(temp, format, provider, destination, out charsWritten);
-            }
-            return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten);
+            return Number.TryFormatInt32(m_value, 0x0000FFFF, format, provider, destination, out charsWritten);
         }
 
         public static short Parse(string s)
index e1707f0..2e71773 100644 (file)
@@ -79,27 +79,27 @@ namespace System
 
         public override string ToString()
         {
-            return Number.FormatInt32(m_value, null, null);
+            return Number.Int32ToDecStr(m_value);
         }
 
         public string ToString(string? format)
         {
-            return Number.FormatInt32(m_value, format, null);
+            return ToString(format, null);
         }
 
         public string ToString(IFormatProvider? provider)
         {
-            return Number.FormatInt32(m_value, null, provider);
+            return ToString(null, provider);
         }
 
         public string ToString(string? format, IFormatProvider? provider)
         {
-            return Number.FormatInt32(m_value, format, provider);
+            return Number.FormatInt32(m_value, ~0, format, provider);
         }
 
         public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
         {
-            return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten);
+            return Number.TryFormatInt32(m_value, ~0, format, provider, destination, out charsWritten);
         }
 
         public static int Parse(string s)
index ae513d1..d4d22e0 100644 (file)
@@ -76,7 +76,7 @@ namespace System
 
         public override string ToString()
         {
-            return Number.FormatInt64(m_value, null, null);
+            return Number.Int64ToDecStr(m_value);
         }
 
         public string ToString(IFormatProvider? provider)
index ee9e67d..2317048 100644 (file)
@@ -685,143 +685,165 @@ namespace System
             return false;
         }
 
-        public static unsafe string FormatInt32(int value, ReadOnlySpan<char> format, IFormatProvider? provider)
+        private static char GetHexBase(char fmt)
         {
-            // Fast path for default format with a non-negative value
-            if (value >= 0 && format.Length == 0)
-            {
-                return UInt32ToDecStr((uint)value, digits: -1);
-            }
+            // 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.
+            return (char)(fmt - ('X' - 'A' + 10));
+        }
 
-            char fmt = ParseFormatSpecifier(format, out int digits);
-            char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
-            if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D')
+        public static string FormatInt32(int value, int hexMask, string? format, IFormatProvider? provider)
+        {
+            // Fast path for default format
+            if (string.IsNullOrEmpty(format))
             {
                 return value >= 0 ?
-                    UInt32ToDecStr((uint)value, digits) :
-                    NegativeInt32ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign);
+                    UInt32ToDecStr((uint)value, digits: -1) :
+                    NegativeInt32ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSign);
             }
-            else if (fmtUpper == 'X')
-            {
-                // 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.
-                return Int32ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits);
-            }
-            else
-            {
-                NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
-
-                byte* pDigits = stackalloc byte[Int32NumberBufferLength];
-                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
-
-                Int32ToNumber(value, ref number);
 
-                char* stackPtr = stackalloc char[CharStackBufferSize];
-                ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
+            return FormatInt32Slow(value, hexMask, format, provider);
 
-                if (fmt != 0)
+            static unsafe string FormatInt32Slow(int value, int hexMask, string? format, IFormatProvider? provider)
+            {
+                ReadOnlySpan<char> formatSpan = format;
+                char fmt = ParseFormatSpecifier(formatSpan, out int digits);
+                char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
+                if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D')
+                {
+                    return value >= 0 ?
+                        UInt32ToDecStr((uint)value, digits) :
+                        NegativeInt32ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign);
+                }
+                else if (fmtUpper == 'X')
                 {
-                    NumberToString(ref sb, ref number, fmt, digits, info);
+                    return Int32ToHexStr(value & hexMask, GetHexBase(fmt), digits);
                 }
                 else
                 {
-                    NumberToStringFormat(ref sb, ref number, format, info);
+                    NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
+
+                    byte* pDigits = stackalloc byte[Int32NumberBufferLength];
+                    NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
+
+                    Int32ToNumber(value, ref number);
+
+                    char* stackPtr = stackalloc char[CharStackBufferSize];
+                    ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
+
+                    if (fmt != 0)
+                    {
+                        NumberToString(ref sb, ref number, fmt, digits, info);
+                    }
+                    else
+                    {
+                        NumberToStringFormat(ref sb, ref number, formatSpan, info);
+                    }
+                    return sb.ToString();
                 }
-                return sb.ToString();
             }
         }
 
-        public static unsafe bool TryFormatInt32(int value, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
+        public static bool TryFormatInt32(int value, int hexMask, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
         {
-            // Fast path for default format with a non-negative value
-            if (value >= 0 && format.Length == 0)
-            {
-                return TryUInt32ToDecStr((uint)value, digits: -1, destination, out charsWritten);
-            }
-
-            char fmt = ParseFormatSpecifier(format, out int digits);
-            char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
-            if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D')
+            // Fast path for default format
+            if (format.Length == 0)
             {
                 return value >= 0 ?
-                    TryUInt32ToDecStr((uint)value, digits, destination, out charsWritten) :
-                    TryNegativeInt32ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten);
+                    TryUInt32ToDecStr((uint)value, digits: -1, destination, out charsWritten) :
+                    TryNegativeInt32ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten);
             }
-            else if (fmtUpper == 'X')
-            {
-                // 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.
-                return TryInt32ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten);
-            }
-            else
-            {
-                NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
-
-                byte* pDigits = stackalloc byte[Int32NumberBufferLength];
-                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
-
-                Int32ToNumber(value, ref number);
 
-                char* stackPtr = stackalloc char[CharStackBufferSize];
-                ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
+            return TryFormatInt32Slow(value, hexMask, format, provider, destination, out charsWritten);
 
-                if (fmt != 0)
+            static unsafe bool TryFormatInt32Slow(int value, int hexMask, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
+            {
+                char fmt = ParseFormatSpecifier(format, out int digits);
+                char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
+                if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D')
+                {
+                    return value >= 0 ?
+                        TryUInt32ToDecStr((uint)value, digits, destination, out charsWritten) :
+                        TryNegativeInt32ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten);
+                }
+                else if (fmtUpper == 'X')
                 {
-                    NumberToString(ref sb, ref number, fmt, digits, info);
+                    return TryInt32ToHexStr(value & hexMask, GetHexBase(fmt), digits, destination, out charsWritten);
                 }
                 else
                 {
-                    NumberToStringFormat(ref sb, ref number, format, info);
+                    NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
+
+                    byte* pDigits = stackalloc byte[Int32NumberBufferLength];
+                    NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
+
+                    Int32ToNumber(value, ref number);
+
+                    char* stackPtr = stackalloc char[CharStackBufferSize];
+                    ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
+
+                    if (fmt != 0)
+                    {
+                        NumberToString(ref sb, ref number, fmt, digits, info);
+                    }
+                    else
+                    {
+                        NumberToStringFormat(ref sb, ref number, format, info);
+                    }
+                    return sb.TryCopyTo(destination, out charsWritten);
                 }
-                return sb.TryCopyTo(destination, out charsWritten);
             }
         }
 
-        public static unsafe string FormatUInt32(uint value, ReadOnlySpan<char> format, IFormatProvider? provider)
+        public static string FormatUInt32(uint value, string? format, IFormatProvider? provider)
         {
             // Fast path for default format
-            if (format.Length == 0)
+            if (string.IsNullOrEmpty(format))
             {
                 return UInt32ToDecStr(value, digits: -1);
             }
 
-            char fmt = ParseFormatSpecifier(format, out int digits);
-            char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
-            if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D')
-            {
-                return UInt32ToDecStr(value, digits);
-            }
-            else if (fmtUpper == 'X')
-            {
-                // 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.
-                return Int32ToHexStr((int)value, (char)(fmt - ('X' - 'A' + 10)), digits);
-            }
-            else
-            {
-                NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
-
-                byte* pDigits = stackalloc byte[UInt32NumberBufferLength];
-                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
+            return FormatUInt32Slow(value, format, provider);
 
-                UInt32ToNumber(value, ref number);
-
-                char* stackPtr = stackalloc char[CharStackBufferSize];
-                ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
-
-                if (fmt != 0)
+            static unsafe string FormatUInt32Slow(uint value, string? format, IFormatProvider? provider)
+            {
+                ReadOnlySpan<char> formatSpan = format;
+                char fmt = ParseFormatSpecifier(formatSpan, out int digits);
+                char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
+                if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D')
                 {
-                    NumberToString(ref sb, ref number, fmt, digits, info);
+                    return UInt32ToDecStr(value, digits);
+                }
+                else if (fmtUpper == 'X')
+                {
+                    return Int32ToHexStr((int)value, GetHexBase(fmt), digits);
                 }
                 else
                 {
-                    NumberToStringFormat(ref sb, ref number, format, info);
+                    NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
+
+                    byte* pDigits = stackalloc byte[UInt32NumberBufferLength];
+                    NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
+
+                    UInt32ToNumber(value, ref number);
+
+                    char* stackPtr = stackalloc char[CharStackBufferSize];
+                    ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
+
+                    if (fmt != 0)
+                    {
+                        NumberToString(ref sb, ref number, fmt, digits, info);
+                    }
+                    else
+                    {
+                        NumberToStringFormat(ref sb, ref number, formatSpan, info);
+                    }
+                    return sb.ToString();
                 }
-                return sb.ToString();
             }
         }
 
-        public static unsafe bool TryFormatUInt32(uint value, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
+        public static bool TryFormatUInt32(uint value, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
         {
             // Fast path for default format
             if (format.Length == 0)
@@ -829,182 +851,197 @@ namespace System
                 return TryUInt32ToDecStr(value, digits: -1, destination, out charsWritten);
             }
 
-            char fmt = ParseFormatSpecifier(format, out int digits);
-            char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
-            if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D')
-            {
-                return TryUInt32ToDecStr(value, digits, destination, out charsWritten);
-            }
-            else if (fmtUpper == 'X')
-            {
-                // 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.
-                return TryInt32ToHexStr((int)value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten);
-            }
-            else
-            {
-                NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
-
-                byte* pDigits = stackalloc byte[UInt32NumberBufferLength];
-                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
-
-                UInt32ToNumber(value, ref number);
-
-                char* stackPtr = stackalloc char[CharStackBufferSize];
-                ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
+            return TryFormatUInt32Slow(value, format, provider, destination, out charsWritten);
 
-                if (fmt != 0)
+            static unsafe bool TryFormatUInt32Slow(uint value, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
+            {
+                char fmt = ParseFormatSpecifier(format, out int digits);
+                char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
+                if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D')
+                {
+                    return TryUInt32ToDecStr(value, digits, destination, out charsWritten);
+                }
+                else if (fmtUpper == 'X')
                 {
-                    NumberToString(ref sb, ref number, fmt, digits, info);
+                    return TryInt32ToHexStr((int)value, GetHexBase(fmt), digits, destination, out charsWritten);
                 }
                 else
                 {
-                    NumberToStringFormat(ref sb, ref number, format, info);
+                    NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
+
+                    byte* pDigits = stackalloc byte[UInt32NumberBufferLength];
+                    NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
+
+                    UInt32ToNumber(value, ref number);
+
+                    char* stackPtr = stackalloc char[CharStackBufferSize];
+                    ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
+
+                    if (fmt != 0)
+                    {
+                        NumberToString(ref sb, ref number, fmt, digits, info);
+                    }
+                    else
+                    {
+                        NumberToStringFormat(ref sb, ref number, format, info);
+                    }
+                    return sb.TryCopyTo(destination, out charsWritten);
                 }
-                return sb.TryCopyTo(destination, out charsWritten);
             }
         }
 
-        public static unsafe string FormatInt64(long value, ReadOnlySpan<char> format, IFormatProvider? provider)
+        public static string FormatInt64(long value, string? format, IFormatProvider? provider)
         {
-            // Fast path for default format with a non-negative value
-            if (value >= 0 && format.Length == 0)
-            {
-                return UInt64ToDecStr((ulong)value, digits: -1);
-            }
-
-            char fmt = ParseFormatSpecifier(format, out int digits);
-            char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
-            if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D')
+            // Fast path for default format
+            if (string.IsNullOrEmpty(format))
             {
                 return value >= 0 ?
-                    UInt64ToDecStr((ulong)value, digits) :
-                    NegativeInt64ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign);
-            }
-            else if (fmtUpper == 'X')
-            {
-                // 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.
-                return Int64ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits);
+                    UInt64ToDecStr((ulong)value, digits: -1) :
+                    NegativeInt64ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSign);
             }
-            else
-            {
-                NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
 
-                byte* pDigits = stackalloc byte[Int64NumberBufferLength];
-                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
+            return FormatInt64Slow(value, format, provider);
 
-                Int64ToNumber(value, ref number);
-
-                char* stackPtr = stackalloc char[CharStackBufferSize];
-                ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
-
-                if (fmt != 0)
+            static unsafe string FormatInt64Slow(long value, string? format, IFormatProvider? provider)
+            {
+                ReadOnlySpan<char> formatSpan = format;
+                char fmt = ParseFormatSpecifier(formatSpan, out int digits);
+                char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
+                if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D')
                 {
-                    NumberToString(ref sb, ref number, fmt, digits, info);
+                    return value >= 0 ?
+                        UInt64ToDecStr((ulong)value, digits) :
+                        NegativeInt64ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign);
+                }
+                else if (fmtUpper == 'X')
+                {
+                    return Int64ToHexStr(value, GetHexBase(fmt), digits);
                 }
                 else
                 {
-                    NumberToStringFormat(ref sb, ref number, format, info);
+                    NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
+
+                    byte* pDigits = stackalloc byte[Int64NumberBufferLength];
+                    NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
+
+                    Int64ToNumber(value, ref number);
+
+                    char* stackPtr = stackalloc char[CharStackBufferSize];
+                    ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
+
+                    if (fmt != 0)
+                    {
+                        NumberToString(ref sb, ref number, fmt, digits, info);
+                    }
+                    else
+                    {
+                        NumberToStringFormat(ref sb, ref number, formatSpan, info);
+                    }
+                    return sb.ToString();
                 }
-                return sb.ToString();
             }
         }
 
-        public static unsafe bool TryFormatInt64(long value, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
+        public static bool TryFormatInt64(long value, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
         {
-            // Fast path for default format with a non-negative value
-            if (value >= 0 && format.Length == 0)
-            {
-                return TryUInt64ToDecStr((ulong)value, digits: -1, destination, out charsWritten);
-            }
-
-            char fmt = ParseFormatSpecifier(format, out int digits);
-            char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
-            if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D')
+            // Fast path for default format
+            if (format.Length == 0)
             {
                 return value >= 0 ?
-                    TryUInt64ToDecStr((ulong)value, digits, destination, out charsWritten) :
-                    TryNegativeInt64ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten);
-            }
-            else if (fmtUpper == 'X')
-            {
-                // 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.
-                return TryInt64ToHexStr(value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten);
+                    TryUInt64ToDecStr((ulong)value, digits: -1, destination, out charsWritten) :
+                    TryNegativeInt64ToDecStr(value, digits: -1, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten);
             }
-            else
-            {
-                NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
 
-                byte* pDigits = stackalloc byte[Int64NumberBufferLength];
-                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
+            return TryFormatInt64Slow(value, format, provider, destination, out charsWritten);
 
-                Int64ToNumber(value, ref number);
-
-                char* stackPtr = stackalloc char[CharStackBufferSize];
-                ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
-
-                if (fmt != 0)
+            static unsafe bool TryFormatInt64Slow(long value, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
+            {
+                char fmt = ParseFormatSpecifier(format, out int digits);
+                char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
+                if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D')
                 {
-                    NumberToString(ref sb, ref number, fmt, digits, info);
+                    return value >= 0 ?
+                        TryUInt64ToDecStr((ulong)value, digits, destination, out charsWritten) :
+                        TryNegativeInt64ToDecStr(value, digits, NumberFormatInfo.GetInstance(provider).NegativeSign, destination, out charsWritten);
+                }
+                else if (fmtUpper == 'X')
+                {
+                    return TryInt64ToHexStr(value, GetHexBase(fmt), digits, destination, out charsWritten);
                 }
                 else
                 {
-                    NumberToStringFormat(ref sb, ref number, format, info);
+                    NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
+
+                    byte* pDigits = stackalloc byte[Int64NumberBufferLength];
+                    NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
+
+                    Int64ToNumber(value, ref number);
+
+                    char* stackPtr = stackalloc char[CharStackBufferSize];
+                    ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
+
+                    if (fmt != 0)
+                    {
+                        NumberToString(ref sb, ref number, fmt, digits, info);
+                    }
+                    else
+                    {
+                        NumberToStringFormat(ref sb, ref number, format, info);
+                    }
+                    return sb.TryCopyTo(destination, out charsWritten);
                 }
-                return sb.TryCopyTo(destination, out charsWritten);
             }
         }
 
-        public static unsafe string FormatUInt64(ulong value, ReadOnlySpan<char> format, IFormatProvider? provider)
+        public static string FormatUInt64(ulong value, string? format, IFormatProvider? provider)
         {
             // Fast path for default format
-            if (format.Length == 0)
+            if (string.IsNullOrEmpty(format))
             {
                 return UInt64ToDecStr(value, digits: -1);
             }
 
-            char fmt = ParseFormatSpecifier(format, out int digits);
-            char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
-            if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D')
-            {
-                return UInt64ToDecStr(value, digits);
-            }
-            else if (fmtUpper == 'X')
-            {
-                // 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.
-                return Int64ToHexStr((long)value, (char)(fmt - ('X' - 'A' + 10)), digits);
-            }
-            else
-            {
-                NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
-
-                byte* pDigits = stackalloc byte[UInt64NumberBufferLength];
-                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
-
-                UInt64ToNumber(value, ref number);
+            return FormatUInt64Slow(value, format, provider);
 
-                char* stackPtr = stackalloc char[CharStackBufferSize];
-                ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
-
-                if (fmt != 0)
+            static unsafe string FormatUInt64Slow(ulong value, string? format, IFormatProvider? provider)
+            {
+                ReadOnlySpan<char> formatSpan = format;
+                char fmt = ParseFormatSpecifier(formatSpan, out int digits);
+                char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
+                if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D')
+                {
+                    return UInt64ToDecStr(value, digits);
+                }
+                else if (fmtUpper == 'X')
                 {
-                    NumberToString(ref sb, ref number, fmt, digits, info);
+                    return Int64ToHexStr((long)value, GetHexBase(fmt), digits);
                 }
                 else
                 {
-                    NumberToStringFormat(ref sb, ref number, format, info);
+                    NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
+
+                    byte* pDigits = stackalloc byte[UInt64NumberBufferLength];
+                    NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
+
+                    UInt64ToNumber(value, ref number);
+
+                    char* stackPtr = stackalloc char[CharStackBufferSize];
+                    ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
+
+                    if (fmt != 0)
+                    {
+                        NumberToString(ref sb, ref number, fmt, digits, info);
+                    }
+                    else
+                    {
+                        NumberToStringFormat(ref sb, ref number, formatSpan, info);
+                    }
+                    return sb.ToString();
                 }
-                return sb.ToString();
             }
         }
 
-        public static unsafe bool TryFormatUInt64(ulong value, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
+        public static bool TryFormatUInt64(ulong value, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
         {
             // Fast path for default format
             if (format.Length == 0)
@@ -1012,40 +1049,42 @@ namespace System
                 return TryUInt64ToDecStr(value, digits: -1, destination, out charsWritten);
             }
 
-            char fmt = ParseFormatSpecifier(format, out int digits);
-            char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
-            if ((fmtUpper == 'G' && digits < 1) || fmtUpper == 'D')
-            {
-                return TryUInt64ToDecStr(value, digits, destination, out charsWritten);
-            }
-            else if (fmtUpper == 'X')
-            {
-                // 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.
-                return TryInt64ToHexStr((long)value, (char)(fmt - ('X' - 'A' + 10)), digits, destination, out charsWritten);
-            }
-            else
-            {
-                NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
-
-                byte* pDigits = stackalloc byte[UInt64NumberBufferLength];
-                NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
+            return TryFormatUInt64Slow(value, format, provider, destination, out charsWritten);
 
-                UInt64ToNumber(value, ref number);
-
-                char* stackPtr = stackalloc char[CharStackBufferSize];
-                ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
-
-                if (fmt != 0)
+            static unsafe bool TryFormatUInt64Slow(ulong value, ReadOnlySpan<char> format, IFormatProvider? provider, Span<char> destination, out int charsWritten)
+            {
+                char fmt = ParseFormatSpecifier(format, out int digits);
+                char fmtUpper = (char)(fmt & 0xFFDF); // ensure fmt is upper-cased for purposes of comparison
+                if (fmtUpper == 'G' ? digits < 1 : fmtUpper == 'D')
                 {
-                    NumberToString(ref sb, ref number, fmt, digits, info);
+                    return TryUInt64ToDecStr(value, digits, destination, out charsWritten);
+                }
+                else if (fmtUpper == 'X')
+                {
+                    return TryInt64ToHexStr((long)value, GetHexBase(fmt), digits, destination, out charsWritten);
                 }
                 else
                 {
-                    NumberToStringFormat(ref sb, ref number, format, info);
+                    NumberFormatInfo info = NumberFormatInfo.GetInstance(provider);
+
+                    byte* pDigits = stackalloc byte[UInt64NumberBufferLength];
+                    NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
+
+                    UInt64ToNumber(value, ref number);
+
+                    char* stackPtr = stackalloc char[CharStackBufferSize];
+                    ValueStringBuilder sb = new ValueStringBuilder(new Span<char>(stackPtr, CharStackBufferSize));
+
+                    if (fmt != 0)
+                    {
+                        NumberToString(ref sb, ref number, fmt, digits, info);
+                    }
+                    else
+                    {
+                        NumberToStringFormat(ref sb, ref number, format, info);
+                    }
+                    return sb.TryCopyTo(destination, out charsWritten);
                 }
-                return sb.TryCopyTo(destination, out charsWritten);
             }
         }
 
@@ -1080,6 +1119,13 @@ namespace System
             number.CheckConsistency();
         }
 
+        public static string Int32ToDecStr(int value)
+        {
+            return value >= 0 ?
+                UInt32ToDecStr((uint)value, -1) :
+                NegativeInt32ToDecStr(value, -1, NumberFormatInfo.CurrentInfo.NegativeSign);
+        }
+
         private static unsafe string NegativeInt32ToDecStr(int value, int digits, string sNegative)
         {
             Debug.Assert(value < 0);
@@ -1313,6 +1359,13 @@ namespace System
             number.CheckConsistency();
         }
 
+        public static string Int64ToDecStr(long value)
+        {
+            return value >= 0 ?
+                UInt64ToDecStr((ulong)value, -1) :
+                NegativeInt64ToDecStr(value, -1, NumberFormatInfo.CurrentInfo.NegativeSign);
+        }
+
         private static unsafe string NegativeInt64ToDecStr(long input, int digits, string sNegative)
         {
             Debug.Assert(input < 0);
index 0d6d5e6..5618457 100644 (file)
@@ -74,37 +74,27 @@ namespace System
         // Provides a string representation of a byte.
         public override string ToString()
         {
-            return Number.FormatInt32(m_value, null, null);
+            return Number.Int32ToDecStr(m_value);
         }
 
-        public string ToString(IFormatProvider? provider)
+        public string ToString(string? format)
         {
-            return Number.FormatInt32(m_value, null, provider);
+            return ToString(format, null);
         }
 
-        public string ToString(string? format)
+        public string ToString(IFormatProvider? provider)
         {
-            return ToString(format, null);
+            return ToString(null, provider);
         }
 
         public string ToString(string? format, IFormatProvider? provider)
         {
-            if (m_value < 0 && format != null && format.Length > 0 && (format[0] == 'X' || format[0] == 'x'))
-            {
-                uint temp = (uint)(m_value & 0x000000FF);
-                return Number.FormatUInt32(temp, format, provider);
-            }
-            return Number.FormatInt32(m_value, format, provider);
+            return Number.FormatInt32(m_value, 0x000000FF, format, provider);
         }
 
         public bool TryFormat(Span<char> destination, out int charsWritten, ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
         {
-            if (m_value < 0 && format.Length > 0 && (format[0] == 'X' || format[0] == 'x'))
-            {
-                uint temp = (uint)(m_value & 0x000000FF);
-                return Number.TryFormatUInt32(temp, format, provider, destination, out charsWritten);
-            }
-            return Number.TryFormatInt32(m_value, format, provider, destination, out charsWritten);
+            return Number.TryFormatInt32(m_value, 0x000000FF, format, provider, destination, out charsWritten);
         }
 
         [CLSCompliant(false)]