Improve throughput of {U}Int32/64.{Try}Parse for hex (dotnet/coreclr#18952)
authorStephen Toub <stoub@microsoft.com>
Tue, 17 Jul 2018 13:00:11 +0000 (06:00 -0700)
committerGitHub <noreply@github.com>
Tue, 17 Jul 2018 13:00:11 +0000 (06:00 -0700)
* Improve throughput of {U}Int32/64.{Try}Parse for hex

- Copies/modifies the recently added TryParse32/64IntegerStyle routines for hex in order to provide more optimized parsing.
- Since AllowHexSpecifier can't be used with any other styles (other than allowing leading/trailing whitespace), deletes existing code paths for parsing hex.
- As I was copying code around, I kept getting bitten by different names used for the same thing, so I also standardized some parameter names in various helper functions.

* Clean up

- Remove a bunch of unnecessary 'unsafe' keywords
- Rephrase some while(true) loops as do/while loops for consistency and to avoid some repetition
- Remove an unnecessary wrapper function

Commit migrated from https://github.com/dotnet/coreclr/commit/624f72d55a92e49aef3c3cd6e69150fa3b085fac

src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs

index 0fe2c08..c764f08 100644 (file)
@@ -5,6 +5,7 @@
 using System.Diagnostics;
 using System.Globalization;
 using System.Runtime.InteropServices;
+using Internal.Runtime.CompilerServices;
 
 namespace System
 {
@@ -30,129 +31,26 @@ namespace System
         private const int Int64Precision = 19;
         private const int UInt64Precision = 20;
 
-        private static bool HexNumberToInt32(ref NumberBuffer number, ref int value)
+        /// <summary>256-element map from an ASCII char to its hex value, e.g. arr['b'] == 11. 0xFF means it's not a hex digit.</summary>
+        private static readonly int[] s_charToHexLookup =
         {
-            uint passedValue = 0;
-            bool returnValue = HexNumberToUInt32(ref number, ref passedValue);
-            value = (int)passedValue;
-            return returnValue;
-        }
-
-        private static bool HexNumberToInt64(ref NumberBuffer number, ref long value)
-        {
-            ulong passedValue = 0;
-            bool returnValue = HexNumberToUInt64(ref number, ref passedValue);
-            value = (long)passedValue;
-            return returnValue;
-        }
-
-        private static unsafe bool HexNumberToUInt32(ref NumberBuffer number, ref uint value)
-        {
-            int i = number.scale;
-            if (i > UInt32Precision || i < number.precision)
-            {
-                return false;
-            }
-            char* p = number.digits;
-            Debug.Assert(p != null);
-
-            uint n = 0;
-            while (--i >= 0)
-            {
-                if (n > ((uint)0xFFFFFFFF / 16))
-                {
-                    return false;
-                }
-                n *= 16;
-                if (*p != '\0')
-                {
-                    uint newN = n;
-                    if (*p != '\0')
-                    {
-                        if (IsDigit(*p))
-                        {
-                            newN += (uint)(*p - '0');
-                        }
-                        else
-                        {
-                            if (*p >= 'A' && *p <= 'F')
-                            {
-                                newN += (uint)((*p - 'A') + 10);
-                            }
-                            else
-                            {
-                                Debug.Assert(*p >= 'a' && *p <= 'f');
-                                newN += (uint)((*p - 'a') + 10);
-                            }
-                        }
-                        p++;
-                    }
-
-                    // Detect an overflow here...
-                    if (newN < n)
-                    {
-                        return false;
-                    }
-                    n = newN;
-                }
-            }
-            value = n;
-            return true;
-        }
-
-        private static unsafe bool HexNumberToUInt64(ref NumberBuffer number, ref ulong value)
-        {
-            int i = number.scale;
-            if (i > UInt64Precision || i < number.precision)
-            {
-                return false;
-            }
-            char* p = number.digits;
-            Debug.Assert(p != null);
-
-            ulong n = 0;
-            while (--i >= 0)
-            {
-                if (n > (0xFFFFFFFFFFFFFFFF / 16))
-                {
-                    return false;
-                }
-                n *= 16;
-                if (*p != '\0')
-                {
-                    ulong newN = n;
-                    if (*p != '\0')
-                    {
-                        if (IsDigit(*p))
-                        {
-                            newN += (ulong)(*p - '0');
-                        }
-                        else
-                        {
-                            if (*p >= 'A' && *p <= 'F')
-                            {
-                                newN += (ulong)((*p - 'A') + 10);
-                            }
-                            else
-                            {
-                                Debug.Assert(*p >= 'a' && *p <= 'f');
-                                newN += (ulong)((*p - 'a') + 10);
-                            }
-                        }
-                        p++;
-                    }
-
-                    // Detect an overflow here...
-                    if (newN < n)
-                    {
-                        return false;
-                    }
-                    n = newN;
-                }
-            }
-            value = n;
-            return true;
-        }
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 15
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 31
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 47
+            0x0,  0x1,  0x2,  0x3,  0x4,  0x5,  0x6,  0x7,  0x8,  0x9,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 63
+            0xFF, 0xA,  0xB,  0xC,  0xD,  0xE,  0xF,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 79
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 95
+            0xFF, 0xa,  0xb,  0xc,  0xd,  0xe,  0xf,  0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 111
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 127
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 143
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 159
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 175
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 191
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 207
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 223
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 239
+            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF  // 255
+        };
 
         private static unsafe bool NumberToInt32(ref NumberBuffer number, ref int value)
         {
@@ -173,7 +71,7 @@ namespace System
                 n *= 10;
                 if (*p != '\0')
                 {
-                    n += (int)(*p++ - '0');
+                    n += (*p++ - '0');
                 }
             }
             if (number.sign)
@@ -214,7 +112,7 @@ namespace System
                 n *= 10;
                 if (*p != '\0')
                 {
-                    n += (int)(*p++ - '0');
+                    n += (*p++ - '0');
                 }
             }
             if (number.sign)
@@ -300,159 +198,146 @@ namespace System
             return true;
         }
 
-        internal static unsafe int ParseInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info)
+        internal static int ParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            int i = 0;
-
-            if ((style & ~NumberStyles.Integer) == 0)
+            if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
                 bool overflow = false;
-                if (!TryParseInt32IntegerStyle(s, style, info, out i, ref overflow))
+                if (!TryParseInt32IntegerStyle(value, styles, info, out int intResult, ref overflow))
                 {
-                    throw overflow ?
-                        new OverflowException(SR.Overflow_Int32) :
-                        (Exception)new FormatException(SR.Format_InvalidString);
+                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int32));
                 }
-                return i;
+                return intResult;
             }
 
-            NumberBuffer number = default;
-            StringToNumber(s, style, ref number, info, false);
-
-            if ((style & NumberStyles.AllowHexSpecifier) != 0)
+            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                if (!HexNumberToInt32(ref number, ref i))
+                bool overflow = false;
+                if (!TryParseUInt32HexNumberStyle(value, styles, info, out uint hexResult, ref overflow))
                 {
-                    throw new OverflowException(SR.Overflow_Int32);
+                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int32));
                 }
+                return (int)hexResult;
             }
-            else
+
+            NumberBuffer number = default;
+            int result = 0;
+            StringToNumber(value, styles, ref number, info, false);
+            if (!NumberToInt32(ref number, ref result))
             {
-                if (!NumberToInt32(ref number, ref i))
-                {
-                    throw new OverflowException(SR.Overflow_Int32);
-                }
+                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Int32));
             }
-            return i;
+            return result;
         }
 
-        internal static unsafe long ParseInt64(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
+        internal static long ParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            long i = 0;
-
-            if ((options & ~NumberStyles.Integer) == 0)
+            if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
                 bool overflow = false;
-                if (!TryParseInt64IntegerStyle(value, options, numfmt, out i, ref overflow))
+                if (!TryParseInt64IntegerStyle(value, styles, info, out long intResult, ref overflow))
                 {
-                    throw overflow ?
-                        new OverflowException(SR.Overflow_Int64) :
-                        (Exception)new FormatException(SR.Format_InvalidString);
+                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int64));
                 }
-                return i;
+                return intResult;
             }
 
-            NumberBuffer number = default;
-            StringToNumber(value, options, ref number, numfmt, false);
-
-            if ((options & NumberStyles.AllowHexSpecifier) != 0)
+            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                if (!HexNumberToInt64(ref number, ref i))
+                bool overflow = false;
+                if (!TryParseUInt64HexNumberStyle(value, styles, info, out ulong hexResult, ref overflow))
                 {
-                    throw new OverflowException(SR.Overflow_Int64);
+                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_Int64));
                 }
+                return (long)hexResult;
             }
-            else
+
+            NumberBuffer number = default;
+            long result = 0;
+            StringToNumber(value, styles, ref number, info, false);
+            if (!NumberToInt64(ref number, ref result))
             {
-                if (!NumberToInt64(ref number, ref i))
-                {
-                    throw new OverflowException(SR.Overflow_Int64);
-                }
+                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Int64));
             }
-            return i;
+            return result;
         }
 
-        internal static unsafe uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
+        internal static uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            uint i = 0;
+            uint result = 0;
 
-            if ((options & ~NumberStyles.Integer) == 0)
+            if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
                 bool overflow = false;
-                if (!TryParseUInt32IntegerStyle(value, options, numfmt, out i, ref overflow))
+                if (!TryParseUInt32IntegerStyle(value, styles, info, out result, ref overflow))
                 {
-                    throw overflow ?
-                        new OverflowException(SR.Overflow_UInt32) :
-                        (Exception)new FormatException(SR.Format_InvalidString);
+                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt32));
                 }
-                return i;
+                return result;
             }
 
-            NumberBuffer number = default;
-            StringToNumber(value, options, ref number, numfmt, false);
-
-            if ((options & NumberStyles.AllowHexSpecifier) != 0)
+            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                if (!HexNumberToUInt32(ref number, ref i))
+                bool overflow = false;
+                if (!TryParseUInt32HexNumberStyle(value, styles, info, out result, ref overflow))
                 {
-                    throw new OverflowException(SR.Overflow_UInt32);
+                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt32));
                 }
+                return result;
             }
-            else
+
+            NumberBuffer number = default;
+            StringToNumber(value, styles, ref number, info, false);
+            if (!NumberToUInt32(ref number, ref result))
             {
-                if (!NumberToUInt32(ref number, ref i))
-                {
-                    throw new OverflowException(SR.Overflow_UInt32);
-                }
+                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_UInt32));
             }
-
-            return i;
+            return result;
         }
 
-        internal static unsafe ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
+        internal static ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            ulong i = 0;
+            ulong result = 0;
 
-            if ((options & ~NumberStyles.Integer) == 0)
+            if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
                 bool overflow = false;
-                if (!TryParseUInt64IntegerStyle(value, options, numfmt, out i, ref overflow))
+                if (!TryParseUInt64IntegerStyle(value, styles, info, out result, ref overflow))
                 {
-                    throw overflow ?
-                        new OverflowException(SR.Overflow_UInt64) :
-                        (Exception)new FormatException(SR.Format_InvalidString);
+                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt64));
                 }
-                return i;
+                return result;
             }
 
-            NumberBuffer number = default;
-            StringToNumber(value, options, ref number, numfmt, false);
-            if ((options & NumberStyles.AllowHexSpecifier) != 0)
+            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                if (!HexNumberToUInt64(ref number, ref i))
+                bool overflow = false;
+                if (!TryParseUInt64HexNumberStyle(value, styles, info, out result, ref overflow))
                 {
-                    throw new OverflowException(SR.Overflow_UInt64);
+                    ThrowOverflowOrFormatException(overflow, nameof(SR.Overflow_UInt64));
                 }
+                return result;
             }
-            else
+
+            NumberBuffer number = default;
+            StringToNumber(value, styles, ref number, info, false);
+            if (!NumberToUInt64(ref number, ref result))
             {
-                if (!NumberToUInt64(ref number, ref i))
-                {
-                    throw new OverflowException(SR.Overflow_UInt64);
-                }
+                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_UInt64));
             }
-            return i;
+            return result;
         }
 
-        private static unsafe bool ParseNumber(ref char* str, char* strEnd, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
+        private static unsafe bool ParseNumber(ref char* str, char* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
         {
             Debug.Assert(str != null);
             Debug.Assert(strEnd != null);
             Debug.Assert(str <= strEnd);
+            Debug.Assert((styles & NumberStyles.AllowHexSpecifier) == 0);
 
             const int StateSign = 0x0001;
             const int StateParens = 0x0002;
@@ -468,20 +353,20 @@ namespace System
             string currSymbol = null;       // currency symbol from NumberFormatInfo.
 
             bool parsingCurrency = false;
-            if ((options & NumberStyles.AllowCurrencySymbol) != 0)
+            if ((styles & NumberStyles.AllowCurrencySymbol) != 0)
             {
-                currSymbol = numfmt.CurrencySymbol;
+                currSymbol = info.CurrencySymbol;
 
                 // The idea here is to match the currency separators and on failure match the number separators to keep the perf of VB's IsNumeric fast.
                 // The values of decSep are setup to use the correct relevant separator (currency in the if part and decimal in the else part).
-                decSep = numfmt.CurrencyDecimalSeparator;
-                groupSep = numfmt.CurrencyGroupSeparator;
+                decSep = info.CurrencyDecimalSeparator;
+                groupSep = info.CurrencyGroupSeparator;
                 parsingCurrency = true;
             }
             else
             {
-                decSep = numfmt.NumberDecimalSeparator;
-                groupSep = numfmt.NumberGroupSeparator;
+                decSep = info.NumberDecimalSeparator;
+                groupSep = info.NumberGroupSeparator;
             }
 
             int state = 0;
@@ -493,14 +378,14 @@ namespace System
             {
                 // Eat whitespace unless we've found a sign which isn't followed by a currency symbol.
                 // "-Kr 1231.47" is legal but "- 1231.47" is not.
-                if (!IsWhite(ch) || (options & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && numfmt.NumberNegativePattern != 2)))
+                if (!IsWhite(ch) || (styles & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && ((state & StateCurrency) == 0 && info.NumberNegativePattern != 2)))
                 {
-                    if ((((options & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, strEnd, numfmt.PositiveSign)) != null || ((next = MatchChars(p, strEnd, numfmt.NegativeSign)) != null && (number.sign = true))))
+                    if ((((styles & NumberStyles.AllowLeadingSign) != 0) && (state & StateSign) == 0) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || ((next = MatchChars(p, strEnd, info.NegativeSign)) != null && (number.sign = true))))
                     {
                         state |= StateSign;
                         p = next - 1;
                     }
-                    else if (ch == '(' && ((options & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
+                    else if (ch == '(' && ((styles & NumberStyles.AllowParentheses) != 0) && ((state & StateSign) == 0))
                     {
                         state |= StateSign | StateParens;
                         number.sign = true;
@@ -524,7 +409,7 @@ namespace System
             int digEnd = 0;
             while (true)
             {
-                if (IsDigit(ch) || (((options & NumberStyles.AllowHexSpecifier) != 0) && ((ch >= 'a' && ch <= 'f') || (ch >= 'A' && ch <= 'F'))))
+                if (IsDigit(ch))
                 {
                     state |= StateDigits;
 
@@ -549,12 +434,12 @@ namespace System
                         number.scale--;
                     }
                 }
-                else if (((options & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, numfmt.NumberDecimalSeparator)) != null))
+                else if (((styles & NumberStyles.AllowDecimalPoint) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, decSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberDecimalSeparator)) != null))
                 {
                     state |= StateDecimal;
                     p = next - 1;
                 }
-                else if (((options & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, numfmt.NumberGroupSeparator)) != null))
+                else if (((styles & NumberStyles.AllowThousands) != 0) && ((state & StateDigits) != 0) && ((state & StateDecimal) == 0) && ((next = MatchChars(p, strEnd, groupSep)) != null || ((parsingCurrency) && (state & StateCurrency) == 0) && (next = MatchChars(p, strEnd, info.NumberGroupSeparator)) != null))
                 {
                     p = next - 1;
                 }
@@ -570,15 +455,15 @@ namespace System
             number.digits[digEnd] = '\0';
             if ((state & StateDigits) != 0)
             {
-                if ((ch == 'E' || ch == 'e') && ((options & NumberStyles.AllowExponent) != 0))
+                if ((ch == 'E' || ch == 'e') && ((styles & NumberStyles.AllowExponent) != 0))
                 {
                     char* temp = p;
                     ch = ++p < strEnd ? *p : '\0';
-                    if ((next = MatchChars(p, strEnd, numfmt.positiveSign)) != null)
+                    if ((next = MatchChars(p, strEnd, info.positiveSign)) != null)
                     {
                         ch = (p = next) < strEnd ? *p : '\0';
                     }
-                    else if ((next = MatchChars(p, strEnd, numfmt.negativeSign)) != null)
+                    else if ((next = MatchChars(p, strEnd, info.negativeSign)) != null)
                     {
                         ch = (p = next) < strEnd ? *p : '\0';
                         negExp = true;
@@ -613,9 +498,9 @@ namespace System
                 }
                 while (true)
                 {
-                    if (!IsWhite(ch) || (options & NumberStyles.AllowTrailingWhite) == 0)
+                    if (!IsWhite(ch) || (styles & NumberStyles.AllowTrailingWhite) == 0)
                     {
-                        if (((options & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, strEnd, numfmt.PositiveSign)) != null || (((next = MatchChars(p, strEnd, numfmt.NegativeSign)) != null) && (number.sign = true))))
+                        if (((styles & NumberStyles.AllowTrailingSign) != 0 && ((state & StateSign) == 0)) && ((next = MatchChars(p, strEnd, info.PositiveSign)) != null || (((next = MatchChars(p, strEnd, info.NegativeSign)) != null) && (number.sign = true))))
                         {
                             state |= StateSign;
                             p = next - 1;
@@ -657,64 +542,52 @@ namespace System
             return false;
         }
 
-        internal static bool TryParseInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out int result)
+        internal static bool TryParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
         {
-            NumberBuffer number = default;
-            result = 0;
-
-            if ((style & ~NumberStyles.Integer) == 0)
+            if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
                 bool overflow = false;
-                return TryParseInt32IntegerStyle(s, style, info, out result, ref overflow);
+                return TryParseInt32IntegerStyle(value, styles, info, out result, ref overflow);
             }
 
-            if (!TryStringToNumber(s, style, ref number, info, false))
-            {
-                return false;
-            }
+            result = 0;
 
-            if ((style & NumberStyles.AllowHexSpecifier) != 0)
+            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                if (!HexNumberToInt32(ref number, ref result))
-                {
-                    return false;
-                }
-            }
-            else
-            {
-                if (!NumberToInt32(ref number, ref result))
-                {
-                    return false;
-                }
+                bool overflow = false;
+                return TryParseUInt32HexNumberStyle(value, styles, info, out Unsafe.As<int, uint>(ref result), ref overflow);
             }
-            return true;
+
+            NumberBuffer number = default;
+            return
+                TryStringToNumber(value, styles, ref number, info, false) &&
+                NumberToInt32(ref number, ref result);
         }
 
         /// <summary>Parses int limited to styles that make up NumberStyles.Integer.</summary>
-        private static bool TryParseInt32IntegerStyle(ReadOnlySpan<char> source, NumberStyles styles, NumberFormatInfo info, out int result, ref bool failureIsOverflow)
+        private static bool TryParseInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result, ref bool failureIsOverflow)
         {
             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
 
-            if ((uint)source.Length < 1) goto FalseExit;
+            if ((uint)value.Length < 1) goto FalseExit;
 
             bool overflow = false;
             int sign = 1;
             int index = 0;
-            int num = source[0];
+            int num = value[0];
 
             // Skip past any whitespace at the beginning.  
             if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
             {
-                index++;
-                while (true)
+                do
                 {
-                    if ((uint)index >= (uint)source.Length) goto FalseExit;
-                    num = source[index];
-                    if (!IsWhite(num)) break;
                     index++;
+                    if ((uint)index >= (uint)value.Length) goto FalseExit;
+                    num = value[index];
                 }
+                while (IsWhite(num));
             }
 
             // Parse leading sign.
@@ -728,32 +601,32 @@ namespace System
                     {
                         sign = -1;
                         index++;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
                     else if (num == '+')
                     {
                         index++;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
                 }
                 else
                 {
-                    source = source.Slice(index);
+                    value = value.Slice(index);
                     index = 0;
-                    if (!string.IsNullOrEmpty(positiveSign) && source.StartsWith(positiveSign))
+                    if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
                     {
                         index += positiveSign.Length;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
-                    else if (!string.IsNullOrEmpty(negativeSign) && source.StartsWith(negativeSign))
+                    else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
                     {
                         sign = -1;
                         index += negativeSign.Length;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
                 }
             }
@@ -768,8 +641,8 @@ namespace System
                     do
                     {
                         index++;
-                        if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                        num = value[index];
                     } while (num == '0');
                     if (!IsDigit(num)) goto HasTrailingChars;
                 }
@@ -779,16 +652,16 @@ namespace System
                 index++;
                 for (int i = 0; i < 8; i++) // next 8 digits can't overflow
                 {
-                    if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
-                    num = source[index];
+                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    num = value[index];
                     if (!IsDigit(num)) goto HasTrailingChars;
                     index++;
                     answer = 10 * answer + num - '0';
                 }
 
                 // Potential overflow now processing the 10th digit.
-                if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
-                num = source[index];
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                num = value[index];
                 if (!IsDigit(num)) goto HasTrailingChars;
                 index++;
                 if (answer > int.MaxValue / 10)
@@ -800,18 +673,18 @@ namespace System
                 {
                     overflow = true;
                 }
-                if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
 
                 // At this point, we're either overflowing or hitting a formatting error.
                 // Format errors take precedence for compatibility.
-                num = source[index];
+                num = value[index];
                 while (IsDigit(num))
                 {
                     overflow = true;
                     index++;
-                    if ((uint)index >= (uint)source.Length)
+                    if ((uint)index >= (uint)value.Length)
                         goto DoneAtEndButPotentialOverflow;
-                    num = source[index];
+                    num = value[index];
                 }
                 goto HasTrailingChars;
             }
@@ -834,43 +707,42 @@ namespace System
             if (IsWhite(num))
             {
                 if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
-                for (index++; index < source.Length; index++)
+                for (index++; index < value.Length; index++)
                 {
-                    if (!IsWhite(source[index])) break;
+                    if (!IsWhite(value[index])) break;
                 }
-                if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
             }
 
-            if (!TrailingZeros(source, index)) goto FalseExit;
+            if (!TrailingZeros(value, index)) goto FalseExit;
             
             goto DoneAtEndButPotentialOverflow;
         }
 
         /// <summary>Parses long inputs limited to styles that make up NumberStyles.Integer.</summary>
         private static bool TryParseInt64IntegerStyle(
-            ReadOnlySpan<char> source, NumberStyles styles, NumberFormatInfo info, out long result, ref bool failureIsOverflow)
+            ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result, ref bool failureIsOverflow)
         {
             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
 
-            if ((uint)source.Length < 1) goto FalseExit;
+            if ((uint)value.Length < 1) goto FalseExit;
 
             bool overflow = false;
             int sign = 1;
             int index = 0;
-            int num = source[0];
+            int num = value[0];
 
             // Skip past any whitespace at the beginning.  
             if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
             {
-                index++;
-                while (true)
+                do
                 {
-                    if ((uint)index >= (uint)source.Length) goto FalseExit;
-                    num = source[index];
-                    if (!IsWhite(num)) break;
                     index++;
+                    if ((uint)index >= (uint)value.Length) goto FalseExit;
+                    num = value[index];
                 }
+                while (IsWhite(num));
             }
 
             // Parse leading sign.
@@ -884,32 +756,32 @@ namespace System
                     {
                         sign = -1;
                         index++;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
                     else if (num == '+')
                     {
                         index++;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
                 }
                 else
                 {
-                    source = source.Slice(index);
+                    value = value.Slice(index);
                     index = 0;
-                    if (!string.IsNullOrEmpty(positiveSign) && source.StartsWith(positiveSign))
+                    if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
                     {
                         index += positiveSign.Length;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
-                    else if (!string.IsNullOrEmpty(negativeSign) && source.StartsWith(negativeSign))
+                    else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
                     {
                         sign = -1;
                         index += negativeSign.Length;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
                 }
             }
@@ -924,8 +796,8 @@ namespace System
                     do
                     {
                         index++;
-                        if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                        num = value[index];
                     } while (num == '0');
                     if (!IsDigit(num)) goto HasTrailingChars;
                 }
@@ -935,16 +807,16 @@ namespace System
                 index++;
                 for (int i = 0; i < 17; i++) // next 17 digits can't overflow
                 {
-                    if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
-                    num = source[index];
+                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    num = value[index];
                     if (!IsDigit(num)) goto HasTrailingChars;
                     index++;
                     answer = 10 * answer + num - '0';
                 }
 
                 // Potential overflow now processing the 19th digit.
-                if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
-                num = source[index];
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                num = value[index];
                 if (!IsDigit(num)) goto HasTrailingChars;
                 index++;
                 if (answer > long.MaxValue / 10)
@@ -956,17 +828,17 @@ namespace System
                 {
                     overflow = true;
                 }
-                if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
 
                 // At this point, we're either overflowing or hitting a formatting error.
                 // Format errors take precedence for compatibility.
-                num = source[index];
+                num = value[index];
                 while (IsDigit(num))
                 {
                     overflow = true;
                     index++;
-                    if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
-                    num = source[index];
+                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    num = value[index];
                 }
                 goto HasTrailingChars;
             }
@@ -989,111 +861,87 @@ namespace System
             if (IsWhite(num))
             {
                 if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
-                for (index++; index < source.Length; index++)
+                for (index++; index < value.Length; index++)
                 {
-                    if (!IsWhite(source[index])) break;
+                    if (!IsWhite(value[index])) break;
                 }
-                if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
             }
 
-            if (!TrailingZeros(source, index)) goto FalseExit;
+            if (!TrailingZeros(value, index)) goto FalseExit;
             
             goto DoneAtEndButPotentialOverflow;
         }
 
-        internal static unsafe bool TryParseInt64(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out long result)
+        internal static bool TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
         {
-            result = 0;
-
-            if ((style & ~NumberStyles.Integer) == 0)
+            if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
                 bool overflow = false;
-                return TryParseInt64IntegerStyle(s, style, info, out result, ref overflow);
+                return TryParseInt64IntegerStyle(value, styles, info, out result, ref overflow);
             }
 
-            NumberBuffer number = default;
-            if (!TryStringToNumber(s, style, ref number, info, false))
-            {
-                return false;
-            }
+            result = 0;
 
-            if ((style & NumberStyles.AllowHexSpecifier) != 0)
-            {
-                if (!HexNumberToInt64(ref number, ref result))
-                {
-                    return false;
-                }
-            }
-            else
+            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                if (!NumberToInt64(ref number, ref result))
-                {
-                    return false;
-                }
+                bool overflow = false;
+                return TryParseUInt64HexNumberStyle(value, styles, info, out Unsafe.As<long, ulong>(ref result), ref overflow);
             }
-            return true;
-        }
 
-        internal static unsafe bool TryParseUInt32(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out uint result)
-        {
             NumberBuffer number = default;
-            result = 0;
+            return
+                TryStringToNumber(value, styles, ref number, info, false) &&
+                NumberToInt64(ref number, ref result);
+        }
 
-            if ((style & ~NumberStyles.Integer) == 0)
+        internal static bool TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
+        {
+            if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
                 bool overflow = false;
-                return TryParseUInt32IntegerStyle(s, style, info, out result, ref overflow);
+                return TryParseUInt32IntegerStyle(value, styles, info, out result, ref overflow);
             }
 
-            if (!TryStringToNumber(s, style, ref number, info, false))
+            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                return false;
+                bool overflow = false;
+                return TryParseUInt32HexNumberStyle(value, styles, info, out result, ref overflow);
             }
 
-            if ((style & NumberStyles.AllowHexSpecifier) != 0)
-            {
-                if (!HexNumberToUInt32(ref number, ref result))
-                {
-                    return false;
-                }
-            }
-            else
-            {
-                if (!NumberToUInt32(ref number, ref result))
-                {
-                    return false;
-                }
-            }
-            return true;
+            NumberBuffer number = default;
+            result = 0;
+            return
+                TryStringToNumber(value, styles, ref number, info, false) &&
+                NumberToUInt32(ref number, ref result);
         }
 
         /// <summary>Parses uint limited to styles that make up NumberStyles.Integer.</summary>
         private static bool TryParseUInt32IntegerStyle(
-            ReadOnlySpan<char> source, NumberStyles styles, NumberFormatInfo info, out uint result, ref bool failureIsOverflow)
+            ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result, ref bool failureIsOverflow)
         {
             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
 
-            if ((uint)source.Length < 1) goto FalseExit;
+            if ((uint)value.Length < 1) goto FalseExit;
 
             bool overflow = false;
             bool hasNegativeSign = false;
             int index = 0;
-            int num = source[0];
+            int num = value[0];
 
             // Skip past any whitespace at the beginning.  
             if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
             {
-                index++;
-                while (true)
+                do
                 {
-                    if ((uint)index >= (uint)source.Length) goto FalseExit;
-                    num = source[index];
-                    if (!IsWhite(num)) break;
                     index++;
+                    if ((uint)index >= (uint)value.Length) goto FalseExit;
+                    num = value[index];
                 }
+                while (IsWhite(num));
             }
 
             // Parse leading sign.
@@ -1106,33 +954,33 @@ namespace System
                     if (num == '+')
                     {
                         index++;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
                     else if (num == '-')
                     {
                         hasNegativeSign = true;
                         index++;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
                 }
                 else
                 {
-                    source = source.Slice(index);
+                    value = value.Slice(index);
                     index = 0;
-                    if (!string.IsNullOrEmpty(positiveSign) && source.StartsWith(positiveSign))
+                    if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
                     {
                         index += positiveSign.Length;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
-                    else if (!string.IsNullOrEmpty(negativeSign) && source.StartsWith(negativeSign))
+                    else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
                     {
                         hasNegativeSign = true;
                         index += negativeSign.Length;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
                 }
             }
@@ -1147,8 +995,8 @@ namespace System
                     do
                     {
                         index++;
-                        if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                        num = value[index];
                     } while (num == '0');
                     if (!IsDigit(num)) goto HasTrailingChars;
                 }
@@ -1158,16 +1006,16 @@ namespace System
                 index++;
                 for (int i = 0; i < 8; i++) // next 8 digits can't overflow
                 {
-                    if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
-                    num = source[index];
+                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    num = value[index];
                     if (!IsDigit(num)) goto HasTrailingChars;
                     index++;
                     answer = 10 * answer + num - '0';
                 }
 
                 // Potential overflow now processing the 10th digit.
-                if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
-                num = source[index];
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                num = value[index];
                 if (!IsDigit(num)) goto HasTrailingChars;
                 index++;
                 if ((uint)answer > uint.MaxValue / 10 || ((uint)answer == uint.MaxValue / 10 && num > '5'))
@@ -1175,17 +1023,17 @@ namespace System
                     overflow = true;
                 }
                 answer = answer * 10 + num - '0';
-                if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
 
                 // At this point, we're either overflowing or hitting a formatting error.
                 // Format errors take precedence for compatibility.
-                num = source[index];
+                num = value[index];
                 while (IsDigit(num))
                 {
                     overflow = true;
                     index++;
-                    if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
-                    num = source[index];
+                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    num = value[index];
                 }
                 goto HasTrailingChars;
             }
@@ -1208,77 +1056,170 @@ namespace System
             if (IsWhite(num))
             {
                 if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
-                for (index++; index < source.Length; index++)
+                for (index++; index < value.Length; index++)
                 {
-                    if (!IsWhite(source[index])) break;
+                    if (!IsWhite(value[index])) break;
                 }
-                if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
             }
 
-            if (!TrailingZeros(source, index)) goto FalseExit;
+            if (!TrailingZeros(value, index)) goto FalseExit;
             
             goto DoneAtEndButPotentialOverflow;
         }
 
-        internal static unsafe bool TryParseUInt64(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out ulong result)
+        /// <summary>Parses uint limited to styles that make up NumberStyles.HexNumber.</summary>
+        private static bool TryParseUInt32HexNumberStyle(
+            ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result, ref bool failureIsOverflow)
         {
-            NumberBuffer number = default;
-            result = 0;
+            Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
+            Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
 
-            if ((style & ~NumberStyles.Integer) == 0)
-            {
-                // Optimized path for the common case of anything that's allowed for integer style.
-                bool overflow = false;
-                return TryParseUInt64IntegerStyle(s, style, info, out result, ref overflow);
-            }
+            if ((uint)value.Length < 1) goto FalseExit;
 
-            if (!TryStringToNumber(s, style, ref number, info, false))
+            bool overflow = false;
+            int index = 0;
+            int num = value[0];
+            int numValue = 0;
+
+            // Skip past any whitespace at the beginning.  
+            if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
             {
-                return false;
+                do
+                {
+                    index++;
+                    if ((uint)index >= (uint)value.Length) goto FalseExit;
+                    num = value[index];
+                }
+                while (IsWhite(num));
             }
 
-            if ((style & NumberStyles.AllowHexSpecifier) != 0)
+            int answer = 0;
+            int[] charToHexLookup = s_charToHexLookup;
+
+            if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
             {
-                if (!HexNumberToUInt64(ref number, ref result))
+                // Skip past leading zeros.
+                if (num == '0')
                 {
-                    return false;
+                    do
+                    {
+                        index++;
+                        if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+                        num = value[index];
+                    } while (num == '0');
+                    if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF) goto HasTrailingChars;
                 }
+                // Parse up through 8 digits, as no overflow is possible
+                answer = charToHexLookup[num]; // first digit
+                index++;
+                for (int i = 0; i < 7; i++) // next 7 digits can't overflow
+                {
+                    if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+                    num = value[index];
+                    if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+                    index++;
+                    answer = 16 * answer + numValue;
+                }
+
+                // If there's another digit, it's an overflow.
+                if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+                num = value[index];
+                if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+                index++;
+                overflow = true;
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+
+                // At this point, we're either overflowing or hitting a formatting error.
+                // Format errors take precedence for compatibility. Read through any remaining digits.
+                num = value[index];
+                while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
+                {
+                    index++;
+                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    num = value[index];
+                }
+                goto HasTrailingChars;
             }
-            else
+        FalseExit: // parsing failed
+            result = 0;
+            return false;
+
+        DoneAtEndButPotentialOverflow:
+            if (overflow)
+            {
+                failureIsOverflow = true;
+                goto FalseExit;
+            }
+        DoneAtEnd:
+            result = (uint)answer;
+            return true;
+
+        HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
+            // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
+            if (IsWhite(num))
             {
-                if (!NumberToUInt64(ref number, ref result))
+                if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
+                for (index++; index < value.Length; index++)
                 {
-                    return false;
+                    if (!IsWhite(value[index])) break;
                 }
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
             }
-            return true;
+
+            if (!TrailingZeros(value, index)) goto FalseExit;
+            
+            goto DoneAtEndButPotentialOverflow;
+        }
+        
+        internal static bool TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
+        {
+            if ((styles & ~NumberStyles.Integer) == 0)
+            {
+                // Optimized path for the common case of anything that's allowed for integer style.
+                bool overflow = false;
+                return TryParseUInt64IntegerStyle(value, styles, info, out result, ref overflow);
+            }
+
+            if ((styles & NumberStyles.AllowHexSpecifier) != 0)
+            {
+                bool overflow = false;
+                return TryParseUInt64HexNumberStyle(value, styles, info, out result, ref overflow);
+            }
+
+            NumberBuffer number = default;
+            result = 0;
+            return
+                TryStringToNumber(value, styles, ref number, info, false) &&
+                NumberToUInt64(ref number, ref result);
         }
 
         /// <summary>Parses ulong limited to styles that make up NumberStyles.Integer.</summary>
         private static bool TryParseUInt64IntegerStyle(
-            ReadOnlySpan<char> source, NumberStyles styles, NumberFormatInfo info, out ulong result, ref bool failureIsOverflow)
+            ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result, ref bool failureIsOverflow)
         {
             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
             Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
 
-            if ((uint)source.Length < 1) goto FalseExit;
+            if ((uint)value.Length < 1) goto FalseExit;
 
             bool overflow = false;
             bool hasNegativeSign = false;
             int index = 0;
-            int num = source[0];
+            int num = value[0];
 
             // Skip past any whitespace at the beginning.  
             if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
             {
-                index++;
-                while (true)
+                do
                 {
-                    if ((uint)index >= (uint)source.Length) goto FalseExit;
-                    num = source[index];
-                    if (!IsWhite(num)) break;
                     index++;
+                    if ((uint)index >= (uint)value.Length) goto FalseExit;
+                    num = value[index];
                 }
+                while (IsWhite(num));
             }
 
             // Parse leading sign.
@@ -1291,33 +1232,33 @@ namespace System
                     if (num == '+')
                     {
                         index++;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
                     else if (num == '-')
                     {
                         hasNegativeSign = true;
                         index++;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
                 }
                 else
                 {
-                    source = source.Slice(index);
+                    value = value.Slice(index);
                     index = 0;
-                    if (!string.IsNullOrEmpty(positiveSign) && source.StartsWith(positiveSign))
+                    if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
                     {
                         index += positiveSign.Length;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
-                    else if (!string.IsNullOrEmpty(negativeSign) && source.StartsWith(negativeSign))
+                    else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
                     {
                         hasNegativeSign = true;
                         index += negativeSign.Length;
-                        if ((uint)index >= (uint)source.Length) goto FalseExit;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto FalseExit;
+                        num = value[index];
                     }
                 }
             }
@@ -1332,8 +1273,8 @@ namespace System
                     do
                     {
                         index++;
-                        if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
-                        num = source[index];
+                        if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                        num = value[index];
                     } while (num == '0');
                     if (!IsDigit(num)) goto HasTrailingChars;
                 }
@@ -1343,16 +1284,16 @@ namespace System
                 index++;
                 for (int i = 0; i < 18; i++) // next 18 digits can't overflow
                 {
-                    if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
-                    num = source[index];
+                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    num = value[index];
                     if (!IsDigit(num)) goto HasTrailingChars;
                     index++;
                     answer = 10 * answer + num - '0';
                 }
 
                 // Potential overflow now processing the 20th digit.
-                if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
-                num = source[index];
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                num = value[index];
                 if (!IsDigit(num)) goto HasTrailingChars;
                 index++;
                 if ((ulong)answer > ulong.MaxValue / 10 || ((ulong)answer == ulong.MaxValue / 10 && num > '5'))
@@ -1360,17 +1301,17 @@ namespace System
                     overflow = true;
                 }
                 answer = answer * 10 + num - '0';
-                if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
 
                 // At this point, we're either overflowing or hitting a formatting error.
                 // Format errors take precedence for compatibility.
-                num = source[index];
+                num = value[index];
                 while (IsDigit(num))
                 {
                     overflow = true;
                     index++;
-                    if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
-                    num = source[index];
+                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    num = value[index];
                 }
                 goto HasTrailingChars;
             }
@@ -1393,110 +1334,216 @@ namespace System
             if (IsWhite(num))
             {
                 if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
-                for (index++; index < source.Length; index++)
+                for (index++; index < value.Length; index++)
                 {
-                    if (!IsWhite(source[index])) break;
+                    if (!IsWhite(value[index])) break;
                 }
-                if ((uint)index >= (uint)source.Length) goto DoneAtEndButPotentialOverflow;
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
             }
 
-            if (!TrailingZeros(source, index)) goto FalseExit;
+            if (!TrailingZeros(value, index)) goto FalseExit;
             
             goto DoneAtEndButPotentialOverflow;
         }
 
-        internal static unsafe decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
+        /// <summary>Parses ulong limited to styles that make up NumberStyles.HexNumber.</summary>
+        private static bool TryParseUInt64HexNumberStyle(
+            ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result, ref bool failureIsOverflow)
+        {
+            Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format");
+            Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
+
+            if ((uint)value.Length < 1) goto FalseExit;
+
+            bool overflow = false;
+            int index = 0;
+            int num = value[0];
+            int numValue = 0;
+
+            // Skip past any whitespace at the beginning.  
+            if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
+            {
+                do
+                {
+                    index++;
+                    if ((uint)index >= (uint)value.Length) goto FalseExit;
+                    num = value[index];
+                }
+                while (IsWhite(num));
+            }
+
+            long answer = 0;
+            int[] charToHexLookup = s_charToHexLookup;
+
+            if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
+            {
+                // Skip past leading zeros.
+                if (num == '0')
+                {
+                    do
+                    {
+                        index++;
+                        if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+                        num = value[index];
+                    } while (num == '0');
+                    if ((uint)num >= (uint)charToHexLookup.Length || charToHexLookup[num] == 0xFF) goto HasTrailingChars;
+                }
+                // Parse up through 16 digits, as no overflow is possible
+                answer = charToHexLookup[num]; // first digit
+                index++;
+                for (int i = 0; i < 15; i++) // next 15 digits can't overflow
+                {
+                    if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+                    num = value[index];
+                    if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+                    index++;
+                    answer = 16 * answer + numValue;
+                }
+
+                // If there's another digit, it's an overflow.
+                if ((uint)index >= (uint)value.Length) goto DoneAtEnd;
+                num = value[index];
+                if ((uint)num >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[num]) == 0xFF) goto HasTrailingChars;
+                index++;
+                overflow = true;
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+
+                // At this point, we're either overflowing or hitting a formatting error.
+                // Format errors take precedence for compatibility. Read through any remaining digits.
+                num = value[index];
+                while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
+                {
+                    index++;
+                    if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+                    num = value[index];
+                }
+                goto HasTrailingChars;
+            }
+        FalseExit: // parsing failed
+            result = 0;
+            return false;
+
+        DoneAtEndButPotentialOverflow:
+            if (overflow)
+            {
+                failureIsOverflow = true;
+                goto FalseExit;
+            }
+        DoneAtEnd:
+            result = (ulong)answer;
+            return true;
+
+        HasTrailingChars: // we've successfully parsed, but there are still remaining characters in the span
+            // Skip past trailing whitespace, then past trailing zeros, and if anything else remains, fail.
+            if (IsWhite(num))
+            {
+                if ((styles & NumberStyles.AllowTrailingWhite) == 0) goto FalseExit;
+                for (index++; index < value.Length; index++)
+                {
+                    if (!IsWhite(value[index])) break;
+                }
+                if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow;
+            }
+
+            if (!TrailingZeros(value, index)) goto FalseExit;
+            
+            goto DoneAtEndButPotentialOverflow;
+        }
+
+        internal static decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
             NumberBuffer number = default;
             decimal result = 0;
 
-            StringToNumber(value, options, ref number, numfmt, true);
+            StringToNumber(value, styles, ref number, info, true);
 
             if (!NumberBufferToDecimal(ref number, ref result))
             {
-                throw new OverflowException(SR.Overflow_Decimal);
+                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Decimal));
             }
             return result;
         }
 
-        internal static unsafe double ParseDouble(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
+        internal static double ParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
             NumberBuffer number = default;
             double d = 0;
 
-            if (!TryStringToNumber(value, options, ref number, numfmt, false))
+            if (!TryStringToNumber(value, styles, ref number, info, false))
             {
                 //If we failed TryStringToNumber, it may be from one of our special strings.
                 //Check the three with which we're concerned and rethrow if it's not one of
                 //those strings.
                 ReadOnlySpan<char> sTrim = value.Trim();
-                if (sTrim.EqualsOrdinal(numfmt.PositiveInfinitySymbol))
+                if (sTrim.EqualsOrdinal(info.PositiveInfinitySymbol))
                 {
                     return double.PositiveInfinity;
                 }
-                if (sTrim.EqualsOrdinal(numfmt.NegativeInfinitySymbol))
+                if (sTrim.EqualsOrdinal(info.NegativeInfinitySymbol))
                 {
                     return double.NegativeInfinity;
                 }
-                if (sTrim.EqualsOrdinal(numfmt.NaNSymbol))
+                if (sTrim.EqualsOrdinal(info.NaNSymbol))
                 {
                     return double.NaN;
                 }
-                throw new FormatException(SR.Format_InvalidString);
+                ThrowOverflowOrFormatException(overflow: false, null);
             }
 
             if (!NumberBufferToDouble(ref number, ref d))
             {
-                throw new OverflowException(SR.Overflow_Double);
+                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Double));
             }
 
             return d;
         }
 
-        internal static unsafe float ParseSingle(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt)
+        internal static float ParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
             NumberBuffer number = default;
             double d = 0;
 
-            if (!TryStringToNumber(value, options, ref number, numfmt, false))
+            if (!TryStringToNumber(value, styles, ref number, info, false))
             {
                 //If we failed TryStringToNumber, it may be from one of our special strings.
                 //Check the three with which we're concerned and rethrow if it's not one of
                 //those strings.
                 ReadOnlySpan<char> sTrim = value.Trim();
-                if (sTrim.EqualsOrdinal(numfmt.PositiveInfinitySymbol))
+                if (sTrim.EqualsOrdinal(info.PositiveInfinitySymbol))
                 {
                     return float.PositiveInfinity;
                 }
-                if (sTrim.EqualsOrdinal(numfmt.NegativeInfinitySymbol))
+                if (sTrim.EqualsOrdinal(info.NegativeInfinitySymbol))
                 {
                     return float.NegativeInfinity;
                 }
-                if (sTrim.EqualsOrdinal(numfmt.NaNSymbol))
+                if (sTrim.EqualsOrdinal(info.NaNSymbol))
                 {
                     return float.NaN;
                 }
-                throw new FormatException(SR.Format_InvalidString);
+                ThrowOverflowOrFormatException(overflow: false, null);
             }
 
             if (!NumberBufferToDouble(ref number, ref d))
             {
-                throw new OverflowException(SR.Overflow_Single);
+                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Single));
             }
             float castSingle = (float)d;
             if (float.IsInfinity(castSingle))
             {
-                throw new OverflowException(SR.Overflow_Single);
+                ThrowOverflowOrFormatException(overflow: true, nameof(SR.Overflow_Single));
             }
             return castSingle;
         }
 
-        internal static unsafe bool TryParseDecimal(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out decimal result)
+        internal static bool TryParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out decimal result)
         {
             NumberBuffer number = default;
             result = 0;
 
-            if (!TryStringToNumber(value, options, ref number, numfmt, true))
+            if (!TryStringToNumber(value, styles, ref number, info, true))
             {
                 return false;
             }
@@ -1508,13 +1555,12 @@ namespace System
             return true;
         }
 
-        internal static unsafe bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out double result)
+        internal static bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out double result)
         {
             NumberBuffer number = default;
             result = 0;
 
-
-            if (!TryStringToNumber(value, options, ref number, numfmt, false))
+            if (!TryStringToNumber(value, styles, ref number, info, false))
             {
                 return false;
             }
@@ -1525,13 +1571,13 @@ namespace System
             return true;
         }
 
-        internal static unsafe bool TryParseSingle(ReadOnlySpan<char> value, NumberStyles options, NumberFormatInfo numfmt, out float result)
+        internal static bool TryParseSingle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out float result)
         {
             NumberBuffer number = default;
             result = 0;
             double d = 0;
 
-            if (!TryStringToNumber(value, options, ref number, numfmt, false))
+            if (!TryStringToNumber(value, styles, ref number, info, false))
             {
                 return false;
             }
@@ -1549,28 +1595,28 @@ namespace System
             return true;
         }
 
-        private static unsafe void StringToNumber(ReadOnlySpan<char> str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
+        private static unsafe void StringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
         {
             Debug.Assert(info != null);
-            fixed (char* stringPointer = &MemoryMarshal.GetReference(str))
+            fixed (char* stringPointer = &MemoryMarshal.GetReference(value))
             {
                 char* p = stringPointer;
-                if (!ParseNumber(ref p, p + str.Length, options, ref number, info, parseDecimal)
-                    || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer))))
+                if (!ParseNumber(ref p, p + value.Length, styles, ref number, info, parseDecimal)
+                    || (p - stringPointer < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
                 {
-                    throw new FormatException(SR.Format_InvalidString);
+                    ThrowOverflowOrFormatException(overflow: false, null);
                 }
             }
         }
 
-        internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> str, NumberStyles options, ref NumberBuffer number, NumberFormatInfo numfmt, bool parseDecimal)
+        internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info, bool parseDecimal)
         {
-            Debug.Assert(numfmt != null);
-            fixed (char* stringPointer = &MemoryMarshal.GetReference(str))
+            Debug.Assert(info != null);
+            fixed (char* stringPointer = &MemoryMarshal.GetReference(value))
             {
                 char* p = stringPointer;
-                if (!ParseNumber(ref p, p + str.Length, options, ref number, numfmt, parseDecimal)
-                    || (p - stringPointer < str.Length && !TrailingZeros(str, (int)(p - stringPointer))))
+                if (!ParseNumber(ref p, p + value.Length, styles, ref number, info, parseDecimal)
+                    || (p - stringPointer < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
                 {
                     return false;
                 }
@@ -1579,12 +1625,12 @@ namespace System
             return true;
         }
 
-        private static bool TrailingZeros(ReadOnlySpan<char> s, int index)
+        private static bool TrailingZeros(ReadOnlySpan<char> value, int index)
         {
             // For compatibility, we need to allow trailing zeros at the end of a number string
-            for (int i = index; i < s.Length; i++)
+            for (int i = index; i < value.Length; i++)
             {
-                if (s[i] != '\0')
+                if (value[i] != '\0')
                 {
                     return false;
                 }
@@ -1593,36 +1639,29 @@ namespace System
             return true;
         }
 
-        private static unsafe char* MatchChars(char* p, char* pEnd, string str)
+        private static unsafe char* MatchChars(char* p, char* pEnd, string value)
         {
-            fixed (char* stringPointer = str)
-            {
-                return MatchChars(p, pEnd, stringPointer);
-            }
-        }
-
-        private static unsafe char* MatchChars(char* p, char* pEnd, char* str)
-        {
-            Debug.Assert(p != null && pEnd != null && p <= pEnd && str != null);
-
-            if (*str == '\0')
-            {
-                return null;
-            }
-            
-            // We only hurt the failure case
-            // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a
-            // space character we use 0x20 space character instead to mean the same.
-            while (true)
+            Debug.Assert(p != null && pEnd != null && p <= pEnd && value != null);
+            fixed (char* stringPointer = value)
             {
-                char cp = p < pEnd ? *p : '\0';
-                if (cp != *str && !(*str == '\u00a0' && cp == '\u0020'))
+                char* str = stringPointer;
+                if (*str != '\0')
                 {
-                    break;
+                    // We only hurt the failure case
+                    // This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 as a
+                    // space character we use 0x20 space character instead to mean the same.
+                    while (true)
+                    {
+                        char cp = p < pEnd ? *p : '\0';
+                        if (cp != *str && !(*str == '\u00a0' && cp == '\u0020'))
+                        {
+                            break;
+                        }
+                        p++;
+                        str++;
+                        if (*str == '\0') return p;
+                    }
                 }
-                p++;
-                str++;
-                if (*str == '\0') return p;
             }
 
             return null;
@@ -1632,10 +1671,17 @@ namespace System
 
         private static bool IsDigit(int ch) => ((uint)ch - '0') <= 9;
 
+        private static void ThrowOverflowOrFormatException(bool overflow, string overflowResourceKey)
+        {
+            throw overflow ?
+               new OverflowException(SR.GetResourceString(overflowResourceKey)) :
+               (Exception)new FormatException(SR.Format_InvalidString);
+        }
+
         private static bool NumberBufferToDouble(ref NumberBuffer number, ref double value)
         {
             double d = NumberToDouble(ref number);
-            if (!Double.IsFinite(d))
+            if (!double.IsFinite(d))
             {
                 value = default;
                 return false;