Optimize number parsing (dotnet/coreclr#21365)
authorPent Ploompuu <kaalikas@gmail.com>
Wed, 12 Dec 2018 14:47:16 +0000 (16:47 +0200)
committerJan Kotas <jkotas@microsoft.com>
Wed, 12 Dec 2018 14:47:16 +0000 (09:47 -0500)
Commit migrated from https://github.com/dotnet/coreclr/commit/4d7f7112efaf54a9634abf2f3a8fecc254301c85

15 files changed:
src/coreclr/src/System.Private.CoreLib/src/System/Enum.cs
src/libraries/System.Private.CoreLib/src/System/Byte.cs
src/libraries/System.Private.CoreLib/src/System/Decimal.DecCalc.cs
src/libraries/System.Private.CoreLib/src/System/Decimal.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs
src/libraries/System.Private.CoreLib/src/System/Guid.cs
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.Parsing.cs
src/libraries/System.Private.CoreLib/src/System/ParseNumbers.cs
src/libraries/System.Private.CoreLib/src/System/SByte.cs
src/libraries/System.Private.CoreLib/src/System/UInt16.cs
src/libraries/System.Private.CoreLib/src/System/UInt32.cs
src/libraries/System.Private.CoreLib/src/System/UInt64.cs

index 44a52ab..c8c1fa1 100644 (file)
@@ -402,32 +402,32 @@ namespace System
             switch (Type.GetTypeCode(rt))
             {
                 case TypeCode.SByte:
-                    parsed = TryParseInt32Enum(rt, value, valueSpan, sbyte.MinValue, sbyte.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_SByte), out intResult);
+                    parsed = TryParseInt32Enum(rt, value, valueSpan, sbyte.MinValue, sbyte.MaxValue, ignoreCase, throwOnFailure, TypeCode.SByte, out intResult);
                     result = parsed ? InternalBoxEnum(rt, intResult) : null;
                     return parsed;
 
                 case TypeCode.Int16:
-                    parsed = TryParseInt32Enum(rt, value, valueSpan, short.MinValue, short.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_Int16), out intResult);
+                    parsed = TryParseInt32Enum(rt, value, valueSpan, short.MinValue, short.MaxValue, ignoreCase, throwOnFailure, TypeCode.Int16, out intResult);
                     result = parsed ? InternalBoxEnum(rt, intResult) : null;
                     return parsed;
 
                 case TypeCode.Int32:
-                    parsed = TryParseInt32Enum(rt, value, valueSpan, int.MinValue, int.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_Int32), out intResult);
+                    parsed = TryParseInt32Enum(rt, value, valueSpan, int.MinValue, int.MaxValue, ignoreCase, throwOnFailure, TypeCode.Int32, out intResult);
                     result = parsed ? InternalBoxEnum(rt, intResult) : null;
                     return parsed;
 
                 case TypeCode.Byte:
-                    parsed = TryParseUInt32Enum(rt, value, valueSpan, byte.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_Byte), out uintResult);
+                    parsed = TryParseUInt32Enum(rt, value, valueSpan, byte.MaxValue, ignoreCase, throwOnFailure, TypeCode.Byte, out uintResult);
                     result = parsed ? InternalBoxEnum(rt, uintResult) : null;
                     return parsed;
 
                 case TypeCode.UInt16:
-                    parsed = TryParseUInt32Enum(rt, value, valueSpan, ushort.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_UInt16), out uintResult);
+                    parsed = TryParseUInt32Enum(rt, value, valueSpan, ushort.MaxValue, ignoreCase, throwOnFailure, TypeCode.UInt16, out uintResult);
                     result = parsed ? InternalBoxEnum(rt, uintResult) : null;
                     return parsed;
 
                 case TypeCode.UInt32:
-                    parsed = TryParseUInt32Enum(rt, value, valueSpan, uint.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_UInt32), out uintResult);
+                    parsed = TryParseUInt32Enum(rt, value, valueSpan, uint.MaxValue, ignoreCase, throwOnFailure, TypeCode.UInt32, out uintResult);
                     result = parsed ? InternalBoxEnum(rt, uintResult) : null;
                     return parsed;
 
@@ -482,36 +482,36 @@ namespace System
             switch (Type.GetTypeCode(typeof(TEnum)))
             {
                 case TypeCode.SByte:
-                    parsed = TryParseInt32Enum(rt, value, valueSpan, sbyte.MinValue, sbyte.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_SByte), out intResult);
+                    parsed = TryParseInt32Enum(rt, value, valueSpan, sbyte.MinValue, sbyte.MaxValue, ignoreCase, throwOnFailure, TypeCode.SByte, out intResult);
                     sbyte sbyteResult = (sbyte)intResult;
                     result = Unsafe.As<sbyte, TEnum>(ref sbyteResult);
                     return parsed;
 
                 case TypeCode.Int16:
-                    parsed = TryParseInt32Enum(rt, value, valueSpan, short.MinValue, short.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_Int16), out intResult);
+                    parsed = TryParseInt32Enum(rt, value, valueSpan, short.MinValue, short.MaxValue, ignoreCase, throwOnFailure, TypeCode.Int16, out intResult);
                     short shortResult = (short)intResult;
                     result = Unsafe.As<short, TEnum>(ref shortResult);
                     return parsed;
 
                 case TypeCode.Int32:
-                    parsed = TryParseInt32Enum(rt, value, valueSpan, int.MinValue, int.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_Int32), out intResult);
+                    parsed = TryParseInt32Enum(rt, value, valueSpan, int.MinValue, int.MaxValue, ignoreCase, throwOnFailure, TypeCode.Int32, out intResult);
                     result = Unsafe.As<int, TEnum>(ref intResult);
                     return parsed;
 
                 case TypeCode.Byte:
-                    parsed = TryParseUInt32Enum(rt, value, valueSpan, byte.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_Byte), out uintResult);
+                    parsed = TryParseUInt32Enum(rt, value, valueSpan, byte.MaxValue, ignoreCase, throwOnFailure, TypeCode.Byte, out uintResult);
                     byte byteResult = (byte)uintResult;
                     result = Unsafe.As<byte, TEnum>(ref byteResult);
                     return parsed;
 
                 case TypeCode.UInt16:
-                    parsed = TryParseUInt32Enum(rt, value, valueSpan, ushort.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_UInt16), out uintResult);
+                    parsed = TryParseUInt32Enum(rt, value, valueSpan, ushort.MaxValue, ignoreCase, throwOnFailure, TypeCode.UInt16, out uintResult);
                     ushort ushortResult = (ushort)uintResult;
                     result = Unsafe.As<ushort, TEnum>(ref ushortResult);
                     return parsed;
 
                 case TypeCode.UInt32:
-                    parsed = TryParseUInt32Enum(rt, value, valueSpan, uint.MaxValue, ignoreCase, throwOnFailure, nameof(SR.Overflow_UInt32), out uintResult);
+                    parsed = TryParseUInt32Enum(rt, value, valueSpan, uint.MaxValue, ignoreCase, throwOnFailure, TypeCode.UInt32, out uintResult);
                     result = Unsafe.As<uint, TEnum>(ref uintResult);
                     return parsed;
 
@@ -534,29 +534,33 @@ namespace System
 
         /// <summary>Tries to parse the value of an enum with known underlying types that fit in an Int32 (Int32, Int16, and SByte).</summary>
         private static bool TryParseInt32Enum(
-            RuntimeType enumType, string originalValueString, ReadOnlySpan<char> value, int minInclusive, int maxInclusive, bool ignoreCase, bool throwOnFailure, string overflowFailureResourceName, out int result)
+            RuntimeType enumType, string originalValueString, ReadOnlySpan<char> value, int minInclusive, int maxInclusive, bool ignoreCase, bool throwOnFailure, TypeCode type, out int result)
         {
             Debug.Assert(
                 enumType.GetEnumUnderlyingType() == typeof(sbyte) ||
                 enumType.GetEnumUnderlyingType() == typeof(short) ||
                 enumType.GetEnumUnderlyingType() == typeof(int));
 
-            bool failureIsOverflow = false;
-            if (StartsNumber(value[0]) && Number.TryParseInt32IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result, ref failureIsOverflow))
+            Number.ParsingStatus status = default;
+            if (StartsNumber(value[0]))
             {
-                if ((uint)(result - minInclusive) <= (uint)(maxInclusive - minInclusive))
+                status = Number.TryParseInt32IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result);
+                if (status == Number.ParsingStatus.OK)
                 {
-                    return true;
-                }
+                    if ((uint)(result - minInclusive) <= (uint)(maxInclusive - minInclusive))
+                    {
+                        return true;
+                    }
 
-                failureIsOverflow = true;
+                    status = Number.ParsingStatus.Overflow;
+                }
             }
 
-            if (failureIsOverflow)
+            if (status == Number.ParsingStatus.Overflow)
             {
                 if (throwOnFailure)
                 {
-                    throw new OverflowException(SR.GetResourceString(overflowFailureResourceName));
+                    Number.ThrowOverflowException(type);
                 }
             }
             else if (TryParseByName(enumType, originalValueString, value, ignoreCase, throwOnFailure, out ulong ulongResult))
@@ -571,29 +575,33 @@ namespace System
         }
 
         /// <summary>Tries to parse the value of an enum with known underlying types that fit in a UInt32 (UInt32, UInt16, and Byte).</summary>
-        private static bool TryParseUInt32Enum(RuntimeType enumType, string originalValueString, ReadOnlySpan<char> value, uint maxInclusive, bool ignoreCase, bool throwOnFailure, string overflowFailureResourceName, out uint result)
+        private static bool TryParseUInt32Enum(RuntimeType enumType, string originalValueString, ReadOnlySpan<char> value, uint maxInclusive, bool ignoreCase, bool throwOnFailure, TypeCode type, out uint result)
         {
             Debug.Assert(
                 enumType.GetEnumUnderlyingType() == typeof(byte) ||
                 enumType.GetEnumUnderlyingType() == typeof(ushort) ||
                 enumType.GetEnumUnderlyingType() == typeof(uint));
 
-            bool failureIsOverflow = false;
-            if (StartsNumber(value[0]) && Number.TryParseUInt32IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result, ref failureIsOverflow))
+            Number.ParsingStatus status = default;
+            if (StartsNumber(value[0]))
             {
-                if (result <= maxInclusive)
+                status = Number.TryParseUInt32IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result);
+                if (status == Number.ParsingStatus.OK)
                 {
-                    return true;
-                }
+                    if (result <= maxInclusive)
+                    {
+                        return true;
+                    }
 
-                failureIsOverflow = true;
+                    status = Number.ParsingStatus.Overflow;
+                }
             }
 
-            if (failureIsOverflow)
+            if (status == Number.ParsingStatus.Overflow)
             {
                 if (throwOnFailure)
                 {
-                    throw new OverflowException(SR.GetResourceString(overflowFailureResourceName));
+                    Number.ThrowOverflowException(type);
                 }
             }
             else if (TryParseByName(enumType, originalValueString, value, ignoreCase, throwOnFailure, out ulong ulongResult))
@@ -612,17 +620,21 @@ namespace System
         {
             Debug.Assert(enumType.GetEnumUnderlyingType() == typeof(long));
 
-            bool failureIsOverflow = false;
-            if (StartsNumber(value[0]) && Number.TryParseInt64IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result, ref failureIsOverflow))
+            Number.ParsingStatus status = default;
+            if (StartsNumber(value[0]))
             {
-                return true;
+                status = Number.TryParseInt64IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result);
+                if (status == Number.ParsingStatus.OK)
+                {
+                    return true;
+                }
             }
 
-            if (failureIsOverflow)
+            if (status == Number.ParsingStatus.Overflow)
             {
                 if (throwOnFailure)
                 {
-                    throw new OverflowException(SR.Overflow_Int64);
+                    Number.ThrowOverflowException(TypeCode.Int64);
                 }
             }
             else if (TryParseByName(enumType, originalValueString, value, ignoreCase, throwOnFailure, out ulong ulongResult))
@@ -640,17 +652,21 @@ namespace System
         {
             Debug.Assert(enumType.GetEnumUnderlyingType() == typeof(ulong));
 
-            bool failureIsOverflow = false;
-            if (StartsNumber(value[0]) && Number.TryParseUInt64IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result, ref failureIsOverflow))
+            Number.ParsingStatus status = default;
+            if (StartsNumber(value[0]))
             {
-                return true;
+                status = Number.TryParseUInt64IntegerStyle(value, NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite, CultureInfo.InvariantCulture.NumberFormat, out result);
+                if (status == Number.ParsingStatus.OK)
+                {
+                    return true;
+                }
             }
 
-            if (failureIsOverflow)
+            if (status == Number.ParsingStatus.Overflow)
             {
                 if (throwOnFailure)
                 {
-                    throw new OverflowException(SR.Overflow_UInt64);
+                    Number.ThrowOverflowException(TypeCode.UInt64);
                 }
             }
             else if (TryParseByName(enumType, originalValueString, value, ignoreCase, throwOnFailure, out result))
index 4004cf1..697ad29 100644 (file)
@@ -107,17 +107,13 @@ namespace System
 
         private static byte Parse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info)
         {
-            int i = 0;
-            try
+            Number.ParsingStatus status = Number.TryParseUInt32(s, style, info, out uint i);
+            if (status != Number.ParsingStatus.OK)
             {
-                i = Number.ParseInt32(s, style, info);
-            }
-            catch (OverflowException e)
-            {
-                throw new OverflowException(SR.Overflow_Byte, e);
+                Number.ThrowOverflowOrFormatException(status, TypeCode.Byte);
             }
 
-            if (i < MinValue || i > MaxValue) throw new OverflowException(SR.Overflow_Byte);
+            if (i > MaxValue) Number.ThrowOverflowException(TypeCode.Byte);
             return (byte)i;
         }
 
@@ -158,14 +154,10 @@ namespace System
 
         private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out byte result)
         {
-            result = 0;
-            int i;
-            if (!Number.TryParseInt32(s, style, info, out i, out _))
-            {
-                return false;
-            }
-            if (i < MinValue || i > MaxValue)
+            if (Number.TryParseUInt32(s, style, info, out uint i) != Number.ParsingStatus.OK
+                || i > MaxValue)
             {
+                result = 0;
                 return false;
             }
             result = (byte)i;
index ee27231..774c5d9 100644 (file)
@@ -202,7 +202,7 @@ namespace System
                     high++;
 
                 if (high > uint.MaxValue)
-                    throw new OverflowException(SR.Overflow_Decimal);
+                    Number.ThrowOverflowException(TypeCode.Decimal);
                 result.Low64 = low;
                 result.High = (uint)high;
             }
@@ -687,7 +687,8 @@ PosRem:
                 return scale;
 
 ThrowOverflow:
-                throw new OverflowException(SR.Overflow_Decimal);
+                Number.ThrowOverflowException(TypeCode.Decimal);
+                return 0;
             }
 
             [MethodImpl(MethodImplOptions.AggressiveInlining)]
@@ -758,7 +759,7 @@ ThrowOverflow:
             private static int OverflowUnscale(ref Buf12 bufQuo, int scale, bool sticky)
             {
                 if (--scale < 0)
-                    throw new OverflowException(SR.Overflow_Decimal);
+                    Number.ThrowOverflowException(TypeCode.Decimal);
 
                 Debug.Assert(bufQuo.U2 == 0);
 
@@ -870,7 +871,7 @@ ThrowOverflow:
                 // positive if it isn't already.
                 //
                 if (curScale + scale < 0)
-                    throw new OverflowException(SR.Overflow_Decimal);
+                    Number.ThrowOverflowException(TypeCode.Decimal);
 
                 return curScale;
             }
@@ -1138,7 +1139,7 @@ AlignedScale:
                     // Divide the value by 10, dropping the scale factor.
                     //
                     if ((flags & ScaleMask) == 0)
-                        throw new OverflowException(SR.Overflow_Decimal);
+                        Number.ThrowOverflowException(TypeCode.Decimal);
                     flags -= 1 << ScaleShift;
 
                     const uint den = 10;
@@ -1578,7 +1579,7 @@ ReturnZero:
                     return; // result should be zeroed out
 
                 if (exp > 96)
-                    throw new OverflowException(SR.Overflow_Decimal);
+                    Number.ThrowOverflowException(TypeCode.Decimal);
 
                 uint flags = 0;
                 if (input < 0)
@@ -1745,7 +1746,7 @@ ReturnZero:
                     return; // result should be zeroed out
 
                 if (exp > 96)
-                    throw new OverflowException(SR.Overflow_Decimal);
+                    Number.ThrowOverflowException(TypeCode.Decimal);
 
                 uint flags = 0;
                 if (input < 0)
@@ -2213,7 +2214,7 @@ RoundUp:
                 }
 
 ThrowOverflow:
-                throw new OverflowException(SR.Overflow_Decimal);
+                Number.ThrowOverflowException(TypeCode.Decimal);
             }
 
             /// <summary>
index a82ca0d..8a9f0d4 100644 (file)
@@ -487,12 +487,12 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result, out _);
+            return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK;
         }
 
         public static bool TryParse(ReadOnlySpan<char> s, out decimal result)
         {
-            return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result, out _);
+            return Number.TryParseDecimal(s, NumberStyles.Number, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK;
         }
 
         public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out decimal result)
@@ -505,13 +505,13 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
+            return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
         }
 
         public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out decimal result)
         {
             NumberFormatInfo.ValidateParseStyleFloatingPoint(style);
-            return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
+            return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
         }
 
         // Returns a binary representation of a Decimal. The return value is an
@@ -646,11 +646,12 @@ namespace System
             {
                 temp = ToUInt32(value);
             }
-            catch (OverflowException e)
+            catch (OverflowException)
             {
-                throw new OverflowException(SR.Overflow_Byte, e);
+                Number.ThrowOverflowException(TypeCode.Byte);
+                throw;
             }
-            if (temp != (byte)temp) throw new OverflowException(SR.Overflow_Byte);
+            if (temp != (byte)temp) Number.ThrowOverflowException(TypeCode.Byte);
             return (byte)temp;
         }
 
@@ -666,11 +667,12 @@ namespace System
             {
                 temp = ToInt32(value);
             }
-            catch (OverflowException e)
+            catch (OverflowException)
             {
-                throw new OverflowException(SR.Overflow_SByte, e);
+                Number.ThrowOverflowException(TypeCode.SByte);
+                throw;
             }
-            if (temp != (sbyte)temp) throw new OverflowException(SR.Overflow_SByte);
+            if (temp != (sbyte)temp) Number.ThrowOverflowException(TypeCode.SByte);
             return (sbyte)temp;
         }
 
@@ -685,11 +687,12 @@ namespace System
             {
                 temp = ToInt32(value);
             }
-            catch (OverflowException e)
+            catch (OverflowException)
             {
-                throw new OverflowException(SR.Overflow_Int16, e);
+                Number.ThrowOverflowException(TypeCode.Int16);
+                throw;
             }
-            if (temp != (short)temp) throw new OverflowException(SR.Overflow_Int16);
+            if (temp != (short)temp) Number.ThrowOverflowException(TypeCode.Int16);
             return (short)temp;
         }
 
@@ -759,11 +762,12 @@ namespace System
             {
                 temp = ToUInt32(value);
             }
-            catch (OverflowException e)
+            catch (OverflowException)
             {
-                throw new OverflowException(SR.Overflow_UInt16, e);
+                Number.ThrowOverflowException(TypeCode.UInt16);
+                throw;
             }
-            if (temp != (ushort)temp) throw new OverflowException(SR.Overflow_UInt16);
+            if (temp != (ushort)temp) Number.ThrowOverflowException(TypeCode.UInt16);
             return (ushort)temp;
         }
 
index 7afe094..8fa6127 100644 (file)
@@ -2,8 +2,6 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-using System;
-using System.Text;
 
 namespace System.Globalization
 {
@@ -39,16 +37,11 @@ namespace System.Globalization
     // CurrencySymbol            "$"      String used as local monetary symbol.
     //
 
-    sealed public class NumberFormatInfo : IFormatProvider, ICloneable
+    public sealed class NumberFormatInfo : IFormatProvider, ICloneable
     {
         // invariantInfo is constant irrespective of your current culture.
         private static volatile NumberFormatInfo s_invariantInfo;
 
-        // READTHIS READTHIS READTHIS
-        // This class has an exact mapping onto a native structure defined in COMNumber.cpp
-        // DO NOT UPDATE THIS WITHOUT UPDATING THAT STRUCTURE. IF YOU ADD BOOL, ADD THEM AT THE END.
-        // ALSO MAKE SURE TO UPDATE mscorlib.h in the VM directory to check field offsets.
-        // READTHIS READTHIS READTHIS
         internal int[] numberGroupSizes = new int[] { 3 };
         internal int[] currencyGroupSizes = new int[] { 3 };
         internal int[] percentGroupSizes = new int[] { 3 };
@@ -83,10 +76,9 @@ namespace System.Globalization
 
         internal bool isReadOnly = false;
 
-        // Is this NumberFormatInfo for invariant culture?
-        internal bool m_isInvariant = false;
+        private bool _hasInvariantNumberSigns = true;
 
-        public NumberFormatInfo() : this(null)
+        public NumberFormatInfo()
         {
         }
 
@@ -171,6 +163,13 @@ namespace System.Globalization
             }
         }
 
+        internal bool HasInvariantNumberSigns => _hasInvariantNumberSigns;
+
+        private void UpdateHasInvariantNumberSigns()
+        {
+            _hasInvariantNumberSigns = positiveSign == "+" && negativeSign == "-";
+        }
+
         internal NumberFormatInfo(CultureData cultureData)
         {
             if (cultureData != null)
@@ -179,11 +178,7 @@ namespace System.Globalization
                 // don't need to verify their values (except for invalid parsing situations).
                 cultureData.GetNFIValues(this);
 
-                if (cultureData.IsInvariantCulture)
-                {
-                    // For invariant culture
-                    this.m_isInvariant = true;
-                }
+                UpdateHasInvariantNumberSigns();
             }
         }
 
@@ -208,9 +203,10 @@ namespace System.Globalization
                 {
                     // Lazy create the invariant info. This cannot be done in a .cctor because exceptions can
                     // be thrown out of a .cctor stack that will need this.
-                    NumberFormatInfo nfi = new NumberFormatInfo();
-                    nfi.m_isInvariant = true;
-                    s_invariantInfo = ReadOnly(nfi);
+                    s_invariantInfo = new NumberFormatInfo
+                    {
+                        isReadOnly = true
+                    };
                 }
                 return s_invariantInfo;
             }
@@ -568,6 +564,7 @@ namespace System.Globalization
                 }
                 VerifyWritable();
                 negativeSign = value;
+                UpdateHasInvariantNumberSigns();
             }
         }
 
@@ -669,6 +666,7 @@ namespace System.Globalization
                 }
                 VerifyWritable();
                 positiveSign = value;
+                UpdateHasInvariantNumberSigns();
             }
         }
 
@@ -803,15 +801,18 @@ namespace System.Globalization
 
         internal static void ValidateParseStyleInteger(NumberStyles style)
         {
-            // Check for undefined flags
-            if ((style & InvalidNumberStyles) != 0)
+            // Check for undefined flags or invalid hex number flags
+            if ((style & (InvalidNumberStyles | NumberStyles.AllowHexSpecifier)) != 0
+                && (style & ~NumberStyles.HexNumber) != 0)
             {
-                throw new ArgumentException(SR.Argument_InvalidNumberStyles, nameof(style));
-            }
-            if ((style & NumberStyles.AllowHexSpecifier) != 0)
-            { // Check for hex number
-                if ((style & ~NumberStyles.HexNumber) != 0)
+                throwInvalid(style);
+
+                void throwInvalid(NumberStyles value)
                 {
+                    if ((value & InvalidNumberStyles) != 0)
+                    {
+                        throw new ArgumentException(SR.Argument_InvalidNumberStyles, nameof(style));
+                    }
                     throw new ArgumentException(SR.Arg_InvalidHexStyle);
                 }
             }
@@ -819,14 +820,19 @@ namespace System.Globalization
 
         internal static void ValidateParseStyleFloatingPoint(NumberStyles style)
         {
-            // Check for undefined flags
-            if ((style & InvalidNumberStyles) != 0)
+            // Check for undefined flags or hex number
+            if ((style & (InvalidNumberStyles | NumberStyles.AllowHexSpecifier)) != 0)
             {
-                throw new ArgumentException(SR.Argument_InvalidNumberStyles, nameof(style));
-            }
-            if ((style & NumberStyles.AllowHexSpecifier) != 0)
-            { // Check for hex number
-                throw new ArgumentException(SR.Arg_HexStyleNotSupported);
+                throwInvalid(style);
+
+                void throwInvalid(NumberStyles value)
+                {
+                    if ((value & InvalidNumberStyles) != 0)
+                    {
+                        throw new ArgumentException(SR.Argument_InvalidNumberStyles, nameof(style));
+                    }
+                    throw new ArgumentException(SR.Arg_HexStyleNotSupported);
+                }
             }
         }
     } // NumberFormatInfo
index be95bff..b241818 100644 (file)
@@ -716,13 +716,13 @@ namespace System
             for (; i < guidString.Length && guidString[i] == '0'; i++);
 
             int processedDigits = 0;
-            int[] charToHexLookup = Number.s_charToHexLookup;
+            ReadOnlySpan<byte> charToHexLookup = Number.CharToHexLookup;
             uint tmp = 0;
             for (; i < guidString.Length; i++)
             {
                 int numValue;
                 char c = guidString[i];
-                if (c >= charToHexLookup.Length || (numValue = charToHexLookup[c]) == 0xFF)
+                if (c >= (uint)charToHexLookup.Length || (numValue = charToHexLookup[c]) == 0xFF)
                 {
                     if (processedDigits > 8) overflow = true;
                     result = 0;
index a88d477..bb5a91b 100644 (file)
@@ -137,28 +137,18 @@ namespace System
 
         private static short Parse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info)
         {
-            int i = 0;
-            try
+            Number.ParsingStatus status = Number.TryParseInt32(s, style, info, out int i);
+            if (status != Number.ParsingStatus.OK)
             {
-                i = Number.ParseInt32(s, style, info);
-            }
-            catch (OverflowException e)
-            {
-                throw new OverflowException(SR.Overflow_Int16, e);
+                Number.ThrowOverflowOrFormatException(status, TypeCode.Int16);
             }
 
-            // We need this check here since we don't allow signs to specified in hex numbers. So we fixup the result
-            // for negative numbers
-            if ((style & NumberStyles.AllowHexSpecifier) != 0)
-            { // We are parsing a hexadecimal number
-                if ((i < 0) || (i > ushort.MaxValue))
-                {
-                    throw new OverflowException(SR.Overflow_Int16);
-                }
-                return (short)i;
+            // For hex number styles AllowHexSpecifier << 6 == 0x8000 and cancels out MinValue so the check is effectively: (uint)i > ushort.MaxValue
+            // For integer styles it's zero and the effective check is (uint)(i - MinValue) > ushort.MaxValue
+            if ((uint)(i - MinValue - ((int)(style & NumberStyles.AllowHexSpecifier) << 6)) > ushort.MaxValue)
+            {
+                Number.ThrowOverflowException(TypeCode.Int16);
             }
-
-            if (i < MinValue || i > MaxValue) throw new OverflowException(SR.Overflow_Int16);
             return (short)i;
         }
 
@@ -199,27 +189,12 @@ namespace System
 
         private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out short result)
         {
-            result = 0;
-            int i;
-            if (!Number.TryParseInt32(s, style, info, out i, out _))
-            {
-                return false;
-            }
-
-            // We need this check here since we don't allow signs to specified in hex numbers. So we fixup the result
-            // for negative numbers
-            if ((style & NumberStyles.AllowHexSpecifier) != 0)
-            { // We are parsing a hexadecimal number
-                if ((i < 0) || i > ushort.MaxValue)
-                {
-                    return false;
-                }
-                result = (short)i;
-                return true;
-            }
-
-            if (i < MinValue || i > MaxValue)
+            // For hex number styles AllowHexSpecifier << 6 == 0x8000 and cancels out MinValue so the check is effectively: (uint)i > ushort.MaxValue
+            // For integer styles it's zero and the effective check is (uint)(i - MinValue) > ushort.MaxValue
+            if (Number.TryParseInt32(s, style, info, out int i) != Number.ParsingStatus.OK
+                || (uint)(i - MinValue - ((int)(style & NumberStyles.AllowHexSpecifier) << 6)) > ushort.MaxValue)
             {
+                result = 0;
                 return false;
             }
             result = (short)i;
index 5aa9753..0688f89 100644 (file)
@@ -152,12 +152,12 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
+            return Number.TryParseInt32IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK;
         }
 
         public static bool TryParse(ReadOnlySpan<char> s, out int result)
         {
-            return Number.TryParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
+            return Number.TryParseInt32IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK;
         }
 
         // Parses an integer from a String in the given style. Returns false rather
@@ -173,13 +173,13 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
+            return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
         }
 
         public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out int result)
         {
             NumberFormatInfo.ValidateParseStyleInteger(style);
-            return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
+            return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
         }
 
         //
index 78e8352..b7722fe 100644 (file)
@@ -143,12 +143,12 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
+            return Number.TryParseInt64IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK;
         }
 
         public static bool TryParse(ReadOnlySpan<char> s, out long result)
         {
-            return Number.TryParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
+            return Number.TryParseInt64IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK;
         }
 
         public static bool TryParse(string s, NumberStyles style, IFormatProvider provider, out long result)
@@ -161,13 +161,13 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
+            return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
         }
 
         public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out long result)
         {
             NumberFormatInfo.ValidateParseStyleInteger(style);
-            return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
+            return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
         }
 
         //
index 1ef1879..0d3c402 100644 (file)
@@ -4,6 +4,7 @@
 
 using System.Diagnostics;
 using System.Globalization;
+using System.Runtime.CompilerServices;
 using System.Runtime.InteropServices;
 using Internal.Runtime.CompilerServices;
 
@@ -31,8 +32,8 @@ namespace System
         private const int Int64Precision = 19;
         private const int UInt64Precision = 20;
 
-        /// <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>
-        internal static readonly int[] s_charToHexLookup =
+        /// <summary>Map from an ASCII char to its hex value, e.g. arr['b'] == 11. 0xFF means it's not a hex digit.</summary>
+        internal static ReadOnlySpan<byte> CharToHexLookup => new byte[]
         {
             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
@@ -40,16 +41,7 @@ namespace System
             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
+            0xFF, 0xa,  0xb,  0xc,  0xd,  0xe,  0xf // 102
         };
 
         private static unsafe bool TryNumberToInt32(ref NumberBuffer number, ref int value)
@@ -208,9 +200,10 @@ namespace System
 
         internal static int ParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            if (!TryParseInt32(value, styles, info, out int result, out bool failureIsOverflow))
+            ParsingStatus status = TryParseInt32(value, styles, info, out int result);
+            if (status != ParsingStatus.OK)
             {
-                ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Int32));
+                ThrowOverflowOrFormatException(status, TypeCode.Int32);
             }
 
             return result;
@@ -218,9 +211,10 @@ namespace System
 
         internal static long ParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            if (!TryParseInt64(value, styles, info, out long result, out bool failureIsOverflow))
+            ParsingStatus status = TryParseInt64(value, styles, info, out long result);
+            if (status != ParsingStatus.OK)
             {
-                ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Int64));
+                ThrowOverflowOrFormatException(status, TypeCode.Int64);
             }
 
             return result;
@@ -228,9 +222,10 @@ namespace System
 
         internal static uint ParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            if (!TryParseUInt32(value, styles, info, out uint result, out bool failureIsOverflow))
+            ParsingStatus status = TryParseUInt32(value, styles, info, out uint result);
+            if (status != ParsingStatus.OK)
             {
-                ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_UInt32));
+                ThrowOverflowOrFormatException(status, TypeCode.UInt32);
             }
 
             return result;
@@ -238,9 +233,10 @@ namespace System
 
         internal static ulong ParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            if (!TryParseUInt64(value, styles, info, out ulong result, out bool failureIsOverflow))
+            ParsingStatus status = TryParseUInt64(value, styles, info, out ulong result);
+            if (status != ParsingStatus.OK)
             {
-                ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_UInt64));
+                ThrowOverflowOrFormatException(status, TypeCode.UInt64);
             }
 
             return result;
@@ -476,50 +472,51 @@ namespace System
             return false;
         }
 
-        internal static unsafe bool TryParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result, out bool failureIsOverflow)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static ParsingStatus TryParseInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
         {
-            result = 0;
-            failureIsOverflow = false;
-
             if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
-                return TryParseInt32IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
+                return TryParseInt32IntegerStyle(value, styles, info, out result);
             }
 
             if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                return TryParseUInt32HexNumberStyle(value, styles, out Unsafe.As<int, uint>(ref result), ref failureIsOverflow);
+                result = 0;
+                return TryParseUInt32HexNumberStyle(value, styles, out Unsafe.As<int, uint>(ref result));
             }
 
+            return TryParseInt32Number(value, styles, info, out result);
+        }
+
+        private static unsafe ParsingStatus TryParseInt32Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
+        {
+            result = 0;
             byte* pDigits = stackalloc byte[Int32NumberBufferLength];
             NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int32NumberBufferLength);
 
             if (!TryStringToNumber(value, styles, ref number, info))
             {
-                return false;
+                return ParsingStatus.Failed;
             }
 
             if (!TryNumberToInt32(ref number, ref result))
             {
-                failureIsOverflow = true;
-                return false;
+                return ParsingStatus.Overflow;
             }
 
-            return true;
+            return ParsingStatus.OK;
         }
 
         /// <summary>Parses int limited to styles that make up NumberStyles.Integer.</summary>
-        internal static bool TryParseInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result, ref bool failureIsOverflow)
+        internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out int result)
         {
             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
-            Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
 
-            if ((uint)value.Length < 1)
+            if (value.IsEmpty)
                 goto FalseExit;
 
-            bool overflow = false;
-            int sign = 1;
             int index = 0;
             int num = value[0];
 
@@ -537,11 +534,10 @@ namespace System
             }
 
             // Parse leading sign.
+            int sign = 1;
             if ((styles & NumberStyles.AllowLeadingSign) != 0)
             {
-                string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
-
-                if (positiveSign == "+" && negativeSign == "-")
+                if (info.HasInvariantNumberSigns)
                 {
                     if (num == '-')
                     {
@@ -563,6 +559,7 @@ namespace System
                 {
                     value = value.Slice(index);
                     index = 0;
+                    string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
                     if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
                     {
                         index += positiveSign.Length;
@@ -581,6 +578,7 @@ namespace System
                 }
             }
 
+            bool overflow = false;
             int answer = 0;
 
             if (IsDigit(num))
@@ -592,7 +590,7 @@ namespace System
                     {
                         index++;
                         if ((uint)index >= (uint)value.Length)
-                            goto DoneAtEndButPotentialOverflow;
+                            goto DoneAtEnd;
                         num = value[index];
                     } while (num == '0');
                     if (!IsDigit(num))
@@ -605,7 +603,7 @@ namespace System
                 for (int i = 0; i < 8; i++) // next 8 digits can't overflow
                 {
                     if ((uint)index >= (uint)value.Length)
-                        goto DoneAtEndButPotentialOverflow;
+                        goto DoneAtEnd;
                     num = value[index];
                     if (!IsDigit(num))
                         goto HasTrailingChars;
@@ -613,22 +611,16 @@ namespace System
                     answer = 10 * answer + num - '0';
                 }
 
-                // Potential overflow now processing the 10th digit.
                 if ((uint)index >= (uint)value.Length)
-                    goto DoneAtEndButPotentialOverflow;
+                    goto DoneAtEnd;
                 num = value[index];
                 if (!IsDigit(num))
                     goto HasTrailingChars;
                 index++;
-                if (answer > int.MaxValue / 10)
-                {
-                    overflow = true;
-                }
+                // Potential overflow now processing the 10th digit.
+                overflow = answer > int.MaxValue / 10;
                 answer = answer * 10 + num - '0';
-                if ((uint)answer > (uint)int.MaxValue + (-1 * sign + 1) / 2)
-                {
-                    overflow = true;
-                }
+                overflow |= (uint)answer > int.MaxValue + (((uint)sign) >> 31);
                 if ((uint)index >= (uint)value.Length)
                     goto DoneAtEndButPotentialOverflow;
 
@@ -640,24 +632,32 @@ namespace System
                     overflow = true;
                     index++;
                     if ((uint)index >= (uint)value.Length)
-                        goto DoneAtEndButPotentialOverflow;
+                        goto OverflowExit;
                     num = value[index];
                 }
                 goto HasTrailingChars;
             }
-
-        FalseExit: // parsing failed
-            result = 0;
-            return false;
+            goto FalseExit;
 
         DoneAtEndButPotentialOverflow:
             if (overflow)
             {
-                failureIsOverflow = true;
-                goto FalseExit;
+                goto OverflowExit;
             }
+        DoneAtEnd:
             result = answer * sign;
-            return true;
+            ParsingStatus status = ParsingStatus.OK;
+        Exit:
+            return status;
+
+        FalseExit: // parsing failed
+            result = 0;
+            status = ParsingStatus.Failed;
+            goto Exit;
+        OverflowExit:
+            result = 0;
+            status = ParsingStatus.Overflow;
+            goto Exit;
 
         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.
@@ -681,17 +681,13 @@ namespace System
         }
 
         /// <summary>Parses long inputs limited to styles that make up NumberStyles.Integer.</summary>
-        internal static bool TryParseInt64IntegerStyle(
-            ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result, ref bool failureIsOverflow)
+        internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
         {
             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
-            Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
 
-            if ((uint)value.Length < 1)
+            if (value.IsEmpty)
                 goto FalseExit;
 
-            bool overflow = false;
-            int sign = 1;
             int index = 0;
             int num = value[0];
 
@@ -709,11 +705,10 @@ namespace System
             }
 
             // Parse leading sign.
+            int sign = 1;
             if ((styles & NumberStyles.AllowLeadingSign) != 0)
             {
-                string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
-
-                if (positiveSign == "+" && negativeSign == "-")
+                if (info.HasInvariantNumberSigns)
                 {
                     if (num == '-')
                     {
@@ -735,6 +730,7 @@ namespace System
                 {
                     value = value.Slice(index);
                     index = 0;
+                    string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
                     if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
                     {
                         index += positiveSign.Length;
@@ -753,6 +749,7 @@ namespace System
                 }
             }
 
+            bool overflow = false;
             long answer = 0;
 
             if (IsDigit(num))
@@ -764,7 +761,7 @@ namespace System
                     {
                         index++;
                         if ((uint)index >= (uint)value.Length)
-                            goto DoneAtEndButPotentialOverflow;
+                            goto DoneAtEnd;
                         num = value[index];
                     } while (num == '0');
                     if (!IsDigit(num))
@@ -777,7 +774,7 @@ namespace System
                 for (int i = 0; i < 17; i++) // next 17 digits can't overflow
                 {
                     if ((uint)index >= (uint)value.Length)
-                        goto DoneAtEndButPotentialOverflow;
+                        goto DoneAtEnd;
                     num = value[index];
                     if (!IsDigit(num))
                         goto HasTrailingChars;
@@ -785,22 +782,16 @@ namespace System
                     answer = 10 * answer + num - '0';
                 }
 
-                // Potential overflow now processing the 19th digit.
                 if ((uint)index >= (uint)value.Length)
-                    goto DoneAtEndButPotentialOverflow;
+                    goto DoneAtEnd;
                 num = value[index];
                 if (!IsDigit(num))
                     goto HasTrailingChars;
                 index++;
-                if (answer > long.MaxValue / 10)
-                {
-                    overflow = true;
-                }
+                // Potential overflow now processing the 19th digit.
+                overflow = answer > long.MaxValue / 10;
                 answer = answer * 10 + num - '0';
-                if ((ulong)answer > (ulong)long.MaxValue + (ulong)((-1 * sign + 1) / 2)) // + sign => 0, - sign => 1
-                {
-                    overflow = true;
-                }
+                overflow |= (ulong)answer > (ulong)long.MaxValue + (((uint)sign) >> 31);
                 if ((uint)index >= (uint)value.Length)
                     goto DoneAtEndButPotentialOverflow;
 
@@ -812,24 +803,32 @@ namespace System
                     overflow = true;
                     index++;
                     if ((uint)index >= (uint)value.Length)
-                        goto DoneAtEndButPotentialOverflow;
+                        goto OverflowExit;
                     num = value[index];
                 }
                 goto HasTrailingChars;
             }
-
-        FalseExit: // parsing failed
-            result = 0;
-            return false;
+            goto FalseExit;
 
         DoneAtEndButPotentialOverflow:
             if (overflow)
             {
-                failureIsOverflow = true;
-                goto FalseExit;
+                goto OverflowExit;
             }
+        DoneAtEnd:
             result = answer * sign;
-            return true;
+            ParsingStatus status = ParsingStatus.OK;
+        Exit:
+            return status;
+
+        FalseExit: // parsing failed
+            result = 0;
+            status = ParsingStatus.Failed;
+            goto Exit;
+        OverflowExit:
+            result = 0;
+            status = ParsingStatus.Overflow;
+            goto Exit;
 
         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.
@@ -852,84 +851,87 @@ namespace System
             goto DoneAtEndButPotentialOverflow;
         }
 
-        internal static unsafe bool TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result, out bool failureIsOverflow)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static ParsingStatus TryParseInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
         {
-            result = 0;
-            failureIsOverflow = false;
-
             if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
-                return TryParseInt64IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
+                return TryParseInt64IntegerStyle(value, styles, info, out result);
             }
 
             if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As<long, ulong>(ref result), ref failureIsOverflow);
+                result = 0;
+                return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As<long, ulong>(ref result));
             }
 
+            return TryParseInt64Number(value, styles, info, out result);
+        }
+
+        private static unsafe ParsingStatus TryParseInt64Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out long result)
+        {
+            result = 0;
             byte* pDigits = stackalloc byte[Int64NumberBufferLength];
             NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, Int64NumberBufferLength);
 
             if (!TryStringToNumber(value, styles, ref number, info))
             {
-                return false;
+                return ParsingStatus.Failed;
             }
 
             if (!TryNumberToInt64(ref number, ref result))
             {
-                failureIsOverflow = true;
-                return false;
+                return ParsingStatus.Overflow;
             }
 
-            return true;
+            return ParsingStatus.OK;
         }
 
-        internal static unsafe bool TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result, out bool failureIsOverflow)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static ParsingStatus TryParseUInt32(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
         {
-            result = 0;
-            failureIsOverflow = false;
-
             if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
-                return TryParseUInt32IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
+                return TryParseUInt32IntegerStyle(value, styles, info, out result);
             }
 
             if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                return TryParseUInt32HexNumberStyle(value, styles, out result, ref failureIsOverflow);
+                return TryParseUInt32HexNumberStyle(value, styles, out result);
             }
 
+            return TryParseUInt32Number(value, styles, info, out result);
+        }
+
+        private static unsafe ParsingStatus TryParseUInt32Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
+        {
+            result = 0;
             byte* pDigits = stackalloc byte[UInt32NumberBufferLength];
             NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt32NumberBufferLength);
 
             if (!TryStringToNumber(value, styles, ref number, info))
             {
-                return false;
+                return ParsingStatus.Failed;
             }
 
             if (!TryNumberToUInt32(ref number, ref result))
             {
-                failureIsOverflow = true;
-                return false;
+                return ParsingStatus.Overflow;
             }
 
-            return true;
+            return ParsingStatus.OK;
         }
 
         /// <summary>Parses uint limited to styles that make up NumberStyles.Integer.</summary>
-        internal static bool TryParseUInt32IntegerStyle(
-            ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result, ref bool failureIsOverflow)
+        internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out uint result)
         {
             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
-            Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
 
-            if ((uint)value.Length < 1)
+            if (value.IsEmpty)
                 goto FalseExit;
 
-            bool overflow = false;
-            bool hasNegativeSign = false;
             int index = 0;
             int num = value[0];
 
@@ -947,11 +949,10 @@ namespace System
             }
 
             // Parse leading sign.
+            bool overflow = false;
             if ((styles & NumberStyles.AllowLeadingSign) != 0)
             {
-                string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
-
-                if (positiveSign == "+" && negativeSign == "-")
+                if (info.HasInvariantNumberSigns)
                 {
                     if (num == '+')
                     {
@@ -962,7 +963,7 @@ namespace System
                     }
                     else if (num == '-')
                     {
-                        hasNegativeSign = true;
+                        overflow = true;
                         index++;
                         if ((uint)index >= (uint)value.Length)
                             goto FalseExit;
@@ -973,6 +974,7 @@ namespace System
                 {
                     value = value.Slice(index);
                     index = 0;
+                    string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
                     if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
                     {
                         index += positiveSign.Length;
@@ -982,7 +984,7 @@ namespace System
                     }
                     else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
                     {
-                        hasNegativeSign = true;
+                        overflow = true;
                         index += negativeSign.Length;
                         if ((uint)index >= (uint)value.Length)
                             goto FalseExit;
@@ -1002,11 +1004,11 @@ namespace System
                     {
                         index++;
                         if ((uint)index >= (uint)value.Length)
-                            goto DoneAtEndButPotentialOverflow;
+                            goto DoneAtEnd;
                         num = value[index];
                     } while (num == '0');
                     if (!IsDigit(num))
-                        goto HasTrailingChars;
+                        goto HasTrailingCharsZero;
                 }
 
                 // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits.
@@ -1023,17 +1025,14 @@ namespace System
                     answer = 10 * answer + num - '0';
                 }
 
-                // Potential overflow now processing the 10th digit.
                 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'))
-                {
-                    overflow = true;
-                }
+                // Potential overflow now processing the 10th digit.
+                overflow |= (uint)answer > uint.MaxValue / 10 || ((uint)answer == uint.MaxValue / 10 && num > '5');
                 answer = answer * 10 + num - '0';
                 if ((uint)index >= (uint)value.Length)
                     goto DoneAtEndButPotentialOverflow;
@@ -1046,25 +1045,35 @@ namespace System
                     overflow = true;
                     index++;
                     if ((uint)index >= (uint)value.Length)
-                        goto DoneAtEndButPotentialOverflow;
+                        goto OverflowExit;
                     num = value[index];
                 }
                 goto HasTrailingChars;
             }
-
-        FalseExit: // parsing failed
-            result = 0;
-            return false;
+            goto FalseExit;
 
         DoneAtEndButPotentialOverflow:
-            if (overflow || (hasNegativeSign && answer != 0))
+            if (overflow)
             {
-                failureIsOverflow = true;
-                goto FalseExit;
+                goto OverflowExit;
             }
+        DoneAtEnd:
             result = (uint)answer;
-            return true;
+            ParsingStatus status = ParsingStatus.OK;
+        Exit:
+            return status;
+
+        FalseExit: // parsing failed
+            result = 0;
+            status = ParsingStatus.Failed;
+            goto Exit;
+        OverflowExit:
+            result = 0;
+            status = ParsingStatus.Overflow;
+            goto Exit;
 
+        HasTrailingCharsZero:
+            overflow = false;
         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))
@@ -1087,19 +1096,16 @@ namespace System
         }
 
         /// <summary>Parses uint limited to styles that make up NumberStyles.HexNumber.</summary>
-        private static bool TryParseUInt32HexNumberStyle(
-            ReadOnlySpan<char> value, NumberStyles styles, out uint result, ref bool failureIsOverflow)
+        private static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan<char> value, NumberStyles styles, out uint result)
         {
             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)
+            if (value.IsEmpty)
                 goto FalseExit;
 
-            bool overflow = false;
             int index = 0;
             int num = value[0];
-            int numValue = 0;
+            uint numValue;
 
             // Skip past any whitespace at the beginning.  
             if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
@@ -1114,8 +1120,9 @@ namespace System
                 while (IsWhite(num));
             }
 
-            int answer = 0;
-            int[] charToHexLookup = s_charToHexLookup;
+            bool overflow = false;
+            uint answer = 0;
+            ReadOnlySpan<byte> charToHexLookup = CharToHexLookup;
 
             if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
             {
@@ -1153,37 +1160,40 @@ namespace System
                 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)
+                do
                 {
                     index++;
                     if ((uint)index >= (uint)value.Length)
-                        goto DoneAtEndButPotentialOverflow;
+                        goto OverflowExit;
                     num = value[index];
-                }
+                } while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF);
+                overflow = true;
                 goto HasTrailingChars;
             }
-
-        FalseExit: // parsing failed
-            result = 0;
-            return false;
+            goto FalseExit;
 
         DoneAtEndButPotentialOverflow:
             if (overflow)
             {
-                failureIsOverflow = true;
-                goto FalseExit;
+                goto OverflowExit;
             }
         DoneAtEnd:
-            result = (uint)answer;
-            return true;
+            result = answer;
+            ParsingStatus status = ParsingStatus.OK;
+        Exit:
+            return status;
+
+        FalseExit: // parsing failed
+            result = 0;
+            status = ParsingStatus.Failed;
+            goto Exit;
+        OverflowExit:
+            result = 0;
+            status = ParsingStatus.Overflow;
+            goto Exit;
 
         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.
@@ -1206,51 +1216,50 @@ namespace System
             goto DoneAtEndButPotentialOverflow;
         }
 
-        internal static unsafe bool TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result, out bool failureIsOverflow)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        internal static ParsingStatus TryParseUInt64(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
         {
-            result = 0;
-            failureIsOverflow = false;
-
             if ((styles & ~NumberStyles.Integer) == 0)
             {
                 // Optimized path for the common case of anything that's allowed for integer style.
-                return TryParseUInt64IntegerStyle(value, styles, info, out result, ref failureIsOverflow);
+                return TryParseUInt64IntegerStyle(value, styles, info, out result);
             }
 
             if ((styles & NumberStyles.AllowHexSpecifier) != 0)
             {
-                return TryParseUInt64HexNumberStyle(value, styles, out result, ref failureIsOverflow);
+                return TryParseUInt64HexNumberStyle(value, styles, out result);
             }
 
+            return TryParseUInt64Number(value, styles, info, out result);
+        }
+
+        private static unsafe ParsingStatus TryParseUInt64Number(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
+        {
+            result = 0;
             byte* pDigits = stackalloc byte[UInt64NumberBufferLength];
             NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, pDigits, UInt64NumberBufferLength);
 
             if (!TryStringToNumber(value, styles, ref number, info))
             {
-                return false;
+                return ParsingStatus.Failed;
             }
 
             if (!TryNumberToUInt64(ref number, ref result))
             {
-                failureIsOverflow = true;
-                return false;
+                return ParsingStatus.Overflow;
             }
 
-            return true;
+            return ParsingStatus.OK;
         }
 
         /// <summary>Parses ulong limited to styles that make up NumberStyles.Integer.</summary>
-        internal static bool TryParseUInt64IntegerStyle(
-            ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result, ref bool failureIsOverflow)
+        internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out ulong result)
         {
             Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format");
-            Debug.Assert(!failureIsOverflow, $"failureIsOverflow should have been initialized to false");
 
-            if ((uint)value.Length < 1)
+            if (value.IsEmpty)
                 goto FalseExit;
 
-            bool overflow = false;
-            bool hasNegativeSign = false;
             int index = 0;
             int num = value[0];
 
@@ -1268,11 +1277,10 @@ namespace System
             }
 
             // Parse leading sign.
+            bool overflow = false;
             if ((styles & NumberStyles.AllowLeadingSign) != 0)
             {
-                string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
-
-                if (positiveSign == "+" && negativeSign == "-")
+                if (info.HasInvariantNumberSigns)
                 {
                     if (num == '+')
                     {
@@ -1283,7 +1291,7 @@ namespace System
                     }
                     else if (num == '-')
                     {
-                        hasNegativeSign = true;
+                        overflow = true;
                         index++;
                         if ((uint)index >= (uint)value.Length)
                             goto FalseExit;
@@ -1294,6 +1302,7 @@ namespace System
                 {
                     value = value.Slice(index);
                     index = 0;
+                    string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign;
                     if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign))
                     {
                         index += positiveSign.Length;
@@ -1303,7 +1312,7 @@ namespace System
                     }
                     else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign))
                     {
-                        hasNegativeSign = true;
+                        overflow = true;
                         index += negativeSign.Length;
                         if ((uint)index >= (uint)value.Length)
                             goto FalseExit;
@@ -1323,11 +1332,11 @@ namespace System
                     {
                         index++;
                         if ((uint)index >= (uint)value.Length)
-                            goto DoneAtEndButPotentialOverflow;
+                            goto DoneAtEnd;
                         num = value[index];
                     } while (num == '0');
                     if (!IsDigit(num))
-                        goto HasTrailingChars;
+                        goto HasTrailingCharsZero;
                 }
 
                 // Parse most digits, up to the potential for overflow, which can't happen until after 19 digits.
@@ -1344,17 +1353,14 @@ namespace System
                     answer = 10 * answer + num - '0';
                 }
 
-                // Potential overflow now processing the 20th digit.
                 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'))
-                {
-                    overflow = true;
-                }
+                // Potential overflow now processing the 20th digit.
+                overflow |= (ulong)answer > ulong.MaxValue / 10 || ((ulong)answer == ulong.MaxValue / 10 && num > '5');
                 answer = answer * 10 + num - '0';
                 if ((uint)index >= (uint)value.Length)
                     goto DoneAtEndButPotentialOverflow;
@@ -1367,25 +1373,35 @@ namespace System
                     overflow = true;
                     index++;
                     if ((uint)index >= (uint)value.Length)
-                        goto DoneAtEndButPotentialOverflow;
+                        goto OverflowExit;
                     num = value[index];
                 }
                 goto HasTrailingChars;
             }
-
-        FalseExit: // parsing failed
-            result = 0;
-            return false;
+            goto FalseExit;
 
         DoneAtEndButPotentialOverflow:
-            if (overflow || (hasNegativeSign && answer != 0))
+            if (overflow)
             {
-                failureIsOverflow = true;
-                goto FalseExit;
+                goto OverflowExit;
             }
+        DoneAtEnd:
             result = (ulong)answer;
-            return true;
+            ParsingStatus status = ParsingStatus.OK;
+        Exit:
+            return status;
 
+        FalseExit: // parsing failed
+            result = 0;
+            status = ParsingStatus.Failed;
+            goto Exit;
+        OverflowExit:
+            result = 0;
+            status = ParsingStatus.Overflow;
+            goto Exit;
+
+        HasTrailingCharsZero:
+            overflow = false;
         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))
@@ -1408,19 +1424,16 @@ namespace System
         }
 
         /// <summary>Parses ulong limited to styles that make up NumberStyles.HexNumber.</summary>
-        private static bool TryParseUInt64HexNumberStyle(
-            ReadOnlySpan<char> value, NumberStyles styles, out ulong result, ref bool failureIsOverflow)
+        private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan<char> value, NumberStyles styles, out ulong result)
         {
             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)
+            if (value.IsEmpty)
                 goto FalseExit;
 
-            bool overflow = false;
             int index = 0;
             int num = value[0];
-            int numValue = 0;
+            uint numValue;
 
             // Skip past any whitespace at the beginning.  
             if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
@@ -1435,8 +1448,9 @@ namespace System
                 while (IsWhite(num));
             }
 
-            long answer = 0;
-            int[] charToHexLookup = s_charToHexLookup;
+            bool overflow = false;
+            ulong answer = 0;
+            ReadOnlySpan<byte> charToHexLookup = CharToHexLookup;
 
             if ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF)
             {
@@ -1474,37 +1488,40 @@ namespace System
                 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)
+                do
                 {
                     index++;
                     if ((uint)index >= (uint)value.Length)
-                        goto DoneAtEndButPotentialOverflow;
+                        goto OverflowExit;
                     num = value[index];
-                }
+                } while ((uint)num < (uint)charToHexLookup.Length && charToHexLookup[num] != 0xFF);
+                overflow = true;
                 goto HasTrailingChars;
             }
-
-        FalseExit: // parsing failed
-            result = 0;
-            return false;
+            goto FalseExit;
 
         DoneAtEndButPotentialOverflow:
             if (overflow)
             {
-                failureIsOverflow = true;
-                goto FalseExit;
+                goto OverflowExit;
             }
         DoneAtEnd:
-            result = (ulong)answer;
-            return true;
+            result = answer;
+            ParsingStatus status = ParsingStatus.OK;
+        Exit:
+            return status;
+
+        FalseExit: // parsing failed
+            result = 0;
+            status = ParsingStatus.Failed;
+            goto Exit;
+        OverflowExit:
+            result = 0;
+            status = ParsingStatus.Overflow;
+            goto Exit;
 
         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.
@@ -1529,9 +1546,10 @@ namespace System
 
         internal static decimal ParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info)
         {
-            if (!TryParseDecimal(value, styles, info, out decimal result, out bool failureIsOverflow))
+            ParsingStatus status = TryParseDecimal(value, styles, info, out decimal result);
+            if (status != ParsingStatus.OK)
             {
-                ThrowOverflowOrFormatException(failureIsOverflow, nameof(SR.Overflow_Decimal));
+                ThrowOverflowOrFormatException(status, TypeCode.Decimal);
             }
 
             return result;
@@ -1646,7 +1664,7 @@ namespace System
         {
             if (!TryParseDouble(value, styles, info, out double result))
             {
-                ThrowOverflowOrFormatException(overflow: false, overflowResourceKey: null);
+                ThrowOverflowOrFormatException(ParsingStatus.Failed);
             }
 
             return result;
@@ -1656,32 +1674,30 @@ namespace System
         {
             if (!TryParseSingle(value, styles, info, out float result))
             {
-                ThrowOverflowOrFormatException(overflow: false, overflowResourceKey: null);
+                ThrowOverflowOrFormatException(ParsingStatus.Failed);
             }
 
             return result;
         }
 
-        internal static unsafe bool TryParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out decimal result, out bool failureIsOverflow)
+        internal static unsafe ParsingStatus TryParseDecimal(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out decimal result)
         {
             byte* pDigits = stackalloc byte[DecimalNumberBufferLength];
             NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, pDigits, DecimalNumberBufferLength);
 
             result = 0;
-            failureIsOverflow = false;
 
             if (!TryStringToNumber(value, styles, ref number, info))
             {
-                return false;
+                return ParsingStatus.Failed;
             }
 
             if (!TryNumberToDecimal(ref number, ref result))
             {
-                failureIsOverflow = true;
-                return false;
+                return ParsingStatus.Overflow;
             }
 
-            return true;
+            return ParsingStatus.OK;
         }
 
         internal static unsafe bool TryParseDouble(ReadOnlySpan<char> value, NumberStyles styles, NumberFormatInfo info, out double result)
@@ -1813,14 +1829,6 @@ namespace System
             return true;
         }
 
-        private static unsafe void StringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
-        {
-            if (!TryStringToNumber(value, styles, ref number, info))
-            {
-                ThrowOverflowOrFormatException(overflow: false, overflowResourceKey: null);
-            }
-        }
-
         internal static unsafe bool TryStringToNumber(ReadOnlySpan<char> value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info)
         {
             Debug.Assert(info != null);
@@ -1828,7 +1836,7 @@ namespace System
             {
                 char* p = stringPointer;
                 if (!TryParseNumber(ref p, p + value.Length, styles, ref number, info)
-                    || (p - stringPointer < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
+                    || ((int)(p - stringPointer) < value.Length && !TrailingZeros(value, (int)(p - stringPointer))))
                 {
                     number.CheckConsistency();
                     return false;
@@ -1842,7 +1850,7 @@ namespace System
         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 < value.Length; i++)
+            for (int i = index; (uint)i < (uint)value.Length; i++)
             {
                 if (value[i] != '\0')
                 {
@@ -1882,15 +1890,60 @@ namespace System
             return null;
         }
 
-        private static bool IsWhite(int ch) => ch == 0x20 || ((uint)(ch - 0x09) <= (0x0D - 0x09));
+        // Ternary op is a workaround for https://github.com/dotnet/coreclr/issues/914
+        private static bool IsWhite(int ch) => ch == 0x20 || (uint)(ch - 0x09) <= (0x0D - 0x09) ? true : false;
 
         private static bool IsDigit(int ch) => ((uint)ch - '0') <= 9;
 
-        private static void ThrowOverflowOrFormatException(bool overflow, string overflowResourceKey)
+        internal enum ParsingStatus
+        {
+            OK,
+            Failed,
+            Overflow
+        }
+
+        internal static void ThrowOverflowOrFormatException(ParsingStatus status, TypeCode type = 0) => throw GetException(status, type);
+
+        internal static void ThrowOverflowException(TypeCode type) => throw GetException(ParsingStatus.Overflow, type);
+
+        private static Exception GetException(ParsingStatus status, TypeCode type)
         {
-            throw overflow ?
-               new OverflowException(SR.GetResourceString(overflowResourceKey)) :
-               (Exception)new FormatException(SR.Format_InvalidString);
+            if (status == ParsingStatus.Failed)
+                return new FormatException(SR.Format_InvalidString);
+
+            string s;
+            switch (type)
+            {
+                case TypeCode.SByte:
+                    s = SR.Overflow_SByte;
+                    break;
+                case TypeCode.Byte:
+                    s = SR.Overflow_Byte;
+                    break;
+                case TypeCode.Int16:
+                    s = SR.Overflow_Int16;
+                    break;
+                case TypeCode.UInt16:
+                    s = SR.Overflow_UInt16;
+                    break;
+                case TypeCode.Int32:
+                    s = SR.Overflow_Int32;
+                    break;
+                case TypeCode.UInt32:
+                    s = SR.Overflow_UInt32;
+                    break;
+                case TypeCode.Int64:
+                    s = SR.Overflow_Int64;
+                    break;
+                case TypeCode.UInt64:
+                    s = SR.Overflow_UInt64;
+                    break;
+                default:
+                    Debug.Assert(type == TypeCode.Decimal);
+                    s = SR.Overflow_Decimal;
+                    break;
+            }
+            return new OverflowException(s);
         }
 
         internal static double NumberToDouble(ref NumberBuffer number)
index dab4cb2..0978186 100644 (file)
@@ -105,7 +105,7 @@ namespace System
 
             // Return the value properly signed.
             if ((ulong)result == 0x8000000000000000 && sign == 1 && r == 10 && ((flags & TreatAsUnsigned) == 0))
-                throw new OverflowException(SR.Overflow_Int64);
+                Number.ThrowOverflowException(TypeCode.Int64);
 
             if (r == 10)
             {
@@ -196,16 +196,16 @@ namespace System
             if ((flags & TreatAsI1) != 0)
             {
                 if ((uint)result > 0xFF)
-                    throw new OverflowException(SR.Overflow_SByte);
+                    Number.ThrowOverflowException(TypeCode.SByte);
             }
             else if ((flags & TreatAsI2) != 0)
             {
                 if ((uint)result > 0xFFFF)
-                    throw new OverflowException(SR.Overflow_Int16);
+                    Number.ThrowOverflowException(TypeCode.Int16);
             }
             else if ((uint)result == 0x80000000 && sign == 1 && r == 10 && ((flags & TreatAsUnsigned) == 0))
             {
-                throw new OverflowException(SR.Overflow_Int32);
+                Number.ThrowOverflowException(TypeCode.Int32);
             }
 
             if (r == 10)
@@ -527,7 +527,7 @@ namespace System
                     // Check for overflows - this is sufficient & correct.
                     if (result > maxVal || ((long)result) < 0)
                     {
-                        ThrowOverflowInt64Exception();
+                        Number.ThrowOverflowException(TypeCode.Int64);
                     }
 
                     result = result * (ulong)radix + (ulong)value;
@@ -536,7 +536,7 @@ namespace System
 
                 if ((long)result < 0 && result != 0x8000000000000000)
                 {
-                    ThrowOverflowInt64Exception();
+                    Number.ThrowOverflowException(TypeCode.Int64);
                 }
             }
             else
@@ -554,14 +554,14 @@ namespace System
                     // Check for overflows - this is sufficient & correct.
                     if (result > maxVal)
                     {
-                        ThrowOverflowUInt64Exception();
+                        Number.ThrowOverflowException(TypeCode.UInt64);
                     }
 
                     ulong temp = result * (ulong)radix + (ulong)value;
 
                     if (temp < result) // this means overflow as well
                     {
-                        ThrowOverflowUInt64Exception();
+                        Number.ThrowOverflowException(TypeCode.UInt64);
                     }
 
                     result = temp;
@@ -588,14 +588,14 @@ namespace System
                     // Check for overflows - this is sufficient & correct.
                     if (result > maxVal || (int)result < 0)
                     {
-                        ThrowOverflowInt32Exception();
+                        Number.ThrowOverflowException(TypeCode.Int32);
                     }
                     result = result * (uint)radix + (uint)value;
                     i++;
                 }
                 if ((int)result < 0 && result != 0x80000000)
                 {
-                    ThrowOverflowInt32Exception();
+                    Number.ThrowOverflowException(TypeCode.Int32);
                 }
             }
             else
@@ -613,14 +613,14 @@ namespace System
                     // Check for overflows - this is sufficient & correct.
                     if (result > maxVal)
                     {
-                        throw new OverflowException(SR.Overflow_UInt32);
+                        Number.ThrowOverflowException(TypeCode.UInt32);
                     }
 
                     uint temp = result * (uint)radix + (uint)value;
 
                     if (temp < result) // this means overflow as well
                     {
-                        ThrowOverflowUInt32Exception();
+                        Number.ThrowOverflowException(TypeCode.UInt32);
                     }
 
                     result = temp;
@@ -631,11 +631,6 @@ namespace System
             return (int)result;
         }
 
-        private static void ThrowOverflowInt32Exception() => throw new OverflowException(SR.Overflow_Int32);
-        private static void ThrowOverflowInt64Exception() => throw new OverflowException(SR.Overflow_Int64);
-        private static void ThrowOverflowUInt32Exception() => throw new OverflowException(SR.Overflow_UInt32);
-        private static void ThrowOverflowUInt64Exception() => throw new OverflowException(SR.Overflow_UInt64);
-
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private static bool IsDigit(char c, int radix, out int result)
         {
index d311484..c3bc386 100644 (file)
@@ -155,26 +155,18 @@ namespace System
 
         private static sbyte Parse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info)
         {
-            int i = 0;
-            try
+            Number.ParsingStatus status = Number.TryParseInt32(s, style, info, out int i);
+            if (status != Number.ParsingStatus.OK)
             {
-                i = Number.ParseInt32(s, style, info);
-            }
-            catch (OverflowException e)
-            {
-                throw new OverflowException(SR.Overflow_SByte, e);
+                Number.ThrowOverflowOrFormatException(status, TypeCode.SByte);
             }
 
-            if ((style & NumberStyles.AllowHexSpecifier) != 0)
-            { // We are parsing a hexadecimal number
-                if ((i < 0) || i > byte.MaxValue)
-                {
-                    throw new OverflowException(SR.Overflow_SByte);
-                }
-                return (sbyte)i;
+            // For hex number styles AllowHexSpecifier >> 2 == 0x80 and cancels out MinValue so the check is effectively: (uint)i > byte.MaxValue
+            // For integer styles it's zero and the effective check is (uint)(i - MinValue) > byte.MaxValue
+            if ((uint)(i - MinValue - ((int)(style & NumberStyles.AllowHexSpecifier) >> 2)) > byte.MaxValue)
+            {
+                Number.ThrowOverflowException(TypeCode.SByte);
             }
-
-            if (i < MinValue || i > MaxValue) throw new OverflowException(SR.Overflow_SByte);
             return (sbyte)i;
         }
 
@@ -219,25 +211,12 @@ namespace System
 
         private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out sbyte result)
         {
-            result = 0;
-            int i;
-            if (!Number.TryParseInt32(s, style, info, out i, out _))
-            {
-                return false;
-            }
-
-            if ((style & NumberStyles.AllowHexSpecifier) != 0)
-            { // We are parsing a hexadecimal number
-                if ((i < 0) || i > byte.MaxValue)
-                {
-                    return false;
-                }
-                result = (sbyte)i;
-                return true;
-            }
-
-            if (i < MinValue || i > MaxValue)
+            // For hex number styles AllowHexSpecifier >> 2 == 0x80 and cancels out MinValue so the check is effectively: (uint)i > byte.MaxValue
+            // For integer styles it's zero and the effective check is (uint)(i - MinValue) > byte.MaxValue
+            if (Number.TryParseInt32(s, style, info, out int i) != Number.ParsingStatus.OK
+                || (uint)(i - MinValue - ((int)(style & NumberStyles.AllowHexSpecifier) >> 2)) > byte.MaxValue)
             {
+                result = 0;
                 return false;
             }
             result = (sbyte)i;
index 38c8b97..0cce408 100644 (file)
@@ -133,17 +133,13 @@ namespace System
 
         private static ushort Parse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info)
         {
-            uint i = 0;
-            try
+            Number.ParsingStatus status = Number.TryParseUInt32(s, style, info, out uint i);
+            if (status != Number.ParsingStatus.OK)
             {
-                i = Number.ParseUInt32(s, style, info);
-            }
-            catch (OverflowException e)
-            {
-                throw new OverflowException(SR.Overflow_UInt16, e);
+                Number.ThrowOverflowOrFormatException(status, TypeCode.UInt16);
             }
 
-            if (i > MaxValue) throw new OverflowException(SR.Overflow_UInt16);
+            if (i > MaxValue) Number.ThrowOverflowException(TypeCode.UInt16);
             return (ushort)i;
         }
 
@@ -188,14 +184,10 @@ namespace System
 
         private static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, NumberFormatInfo info, out ushort result)
         {
-            result = 0;
-            uint i;
-            if (!Number.TryParseUInt32(s, style, info, out i, out _))
-            {
-                return false;
-            }
-            if (i > MaxValue)
+            if (Number.TryParseUInt32(s, style, info, out uint i) != Number.ParsingStatus.OK
+                || i > MaxValue)
             {
+                result = 0;
                 return false;
             }
             result = (ushort)i;
index 576b55f..19faee1 100644 (file)
@@ -148,13 +148,13 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
+            return Number.TryParseUInt32IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK;
         }
 
         [CLSCompliant(false)]
         public static bool TryParse(ReadOnlySpan<char> s, out uint result)
         {
-            return Number.TryParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
+            return Number.TryParseUInt32IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK;
         }
 
         [CLSCompliant(false)]
@@ -168,14 +168,14 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
+            return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
         }
 
         [CLSCompliant(false)]
         public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out uint result)
         {
             NumberFormatInfo.ValidateParseStyleInteger(style);
-            return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
+            return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
         }
 
         //
index 0abe8d4..39fa643 100644 (file)
@@ -145,13 +145,13 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
+            return Number.TryParseUInt64IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK;
         }
 
         [CLSCompliant(false)]
         public static bool TryParse(ReadOnlySpan<char> s, out ulong result)
         {
-            return Number.TryParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result, out _);
+            return Number.TryParseUInt64IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK;
         }
 
         [CLSCompliant(false)]
@@ -165,14 +165,14 @@ namespace System
                 return false;
             }
 
-            return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
+            return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
         }
 
         [CLSCompliant(false)]
         public static bool TryParse(ReadOnlySpan<char> s, NumberStyles style, IFormatProvider provider, out ulong result)
         {
             NumberFormatInfo.ValidateParseStyleInteger(style);
-            return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result, out _);
+            return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK;
         }
 
         //