summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPent Ploompuu <kaalikas@gmail.com>2018-12-12 16:47:16 +0200
committerJan Kotas <jkotas@microsoft.com>2018-12-12 09:47:16 -0500
commit4d7f7112efaf54a9634abf2f3a8fecc254301c85 (patch)
tree28e698dcb6edf1cdbfe3a160b882305c8534fdc2
parent60467e9aa8f39c7c93c90cdf007dc4ccb21f53a0 (diff)
downloadcoreclr-4d7f7112efaf54a9634abf2f3a8fecc254301c85.tar.gz
coreclr-4d7f7112efaf54a9634abf2f3a8fecc254301c85.tar.bz2
coreclr-4d7f7112efaf54a9634abf2f3a8fecc254301c85.zip
Optimize number parsing (#21365)
-rw-r--r--src/System.Private.CoreLib/shared/System/Byte.cs22
-rw-r--r--src/System.Private.CoreLib/shared/System/Decimal.DecCalc.cs17
-rw-r--r--src/System.Private.CoreLib/shared/System/Decimal.cs36
-rw-r--r--src/System.Private.CoreLib/shared/System/Globalization/NumberFormatInfo.cs72
-rw-r--r--src/System.Private.CoreLib/shared/System/Guid.cs4
-rw-r--r--src/System.Private.CoreLib/shared/System/Int16.cs51
-rw-r--r--src/System.Private.CoreLib/shared/System/Int32.cs8
-rw-r--r--src/System.Private.CoreLib/shared/System/Int64.cs8
-rw-r--r--src/System.Private.CoreLib/shared/System/Number.Parsing.cs541
-rw-r--r--src/System.Private.CoreLib/shared/System/ParseNumbers.cs29
-rw-r--r--src/System.Private.CoreLib/shared/System/SByte.cs47
-rw-r--r--src/System.Private.CoreLib/shared/System/UInt16.cs22
-rw-r--r--src/System.Private.CoreLib/shared/System/UInt32.cs8
-rw-r--r--src/System.Private.CoreLib/shared/System/UInt64.cs8
-rw-r--r--src/System.Private.CoreLib/src/System/Enum.cs96
15 files changed, 491 insertions, 478 deletions
diff --git a/src/System.Private.CoreLib/shared/System/Byte.cs b/src/System.Private.CoreLib/shared/System/Byte.cs
index 4004cf13a5..697ad29f27 100644
--- a/src/System.Private.CoreLib/shared/System/Byte.cs
+++ b/src/System.Private.CoreLib/shared/System/Byte.cs
@@ -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;
diff --git a/src/System.Private.CoreLib/shared/System/Decimal.DecCalc.cs b/src/System.Private.CoreLib/shared/System/Decimal.DecCalc.cs
index ee27231aac..774c5d9a52 100644
--- a/src/System.Private.CoreLib/shared/System/Decimal.DecCalc.cs
+++ b/src/System.Private.CoreLib/shared/System/Decimal.DecCalc.cs
@@ -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>
diff --git a/src/System.Private.CoreLib/shared/System/Decimal.cs b/src/System.Private.CoreLib/shared/System/Decimal.cs
index a82ca0dcca..8a9f0d4237 100644
--- a/src/System.Private.CoreLib/shared/System/Decimal.cs
+++ b/src/System.Private.CoreLib/shared/System/Decimal.cs
@@ -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;
}
diff --git a/src/System.Private.CoreLib/shared/System/Globalization/NumberFormatInfo.cs b/src/System.Private.CoreLib/shared/System/Globalization/NumberFormatInfo.cs
index 7afe094748..8fa6127da6 100644
--- a/src/System.Private.CoreLib/shared/System/Globalization/NumberFormatInfo.cs
+++ b/src/System.Private.CoreLib/shared/System/Globalization/NumberFormatInfo.cs
@@ -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
diff --git a/src/System.Private.CoreLib/shared/System/Guid.cs b/src/System.Private.CoreLib/shared/System/Guid.cs
index be95bff0b3..b241818410 100644
--- a/src/System.Private.CoreLib/shared/System/Guid.cs
+++ b/src/System.Private.CoreLib/shared/System/Guid.cs
@@ -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;
diff --git a/src/System.Private.CoreLib/shared/System/Int16.cs b/src/System.Private.CoreLib/shared/System/Int16.cs
index a88d477e4e..bb5a91b913 100644
--- a/src/System.Private.CoreLib/shared/System/Int16.cs
+++ b/src/System.Private.CoreLib/shared/System/Int16.cs
@@ -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;
diff --git a/src/System.Private.CoreLib/shared/System/Int32.cs b/src/System.Private.CoreLib/shared/System/Int32.cs
index 5aa97537a9..0688f89f80 100644
--- a/src/System.Private.CoreLib/shared/System/Int32.cs
+++ b/src/System.Private.CoreLib/shared/System/Int32.cs
@@ -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;
}
//
diff --git a/src/System.Private.CoreLib/shared/System/Int64.cs b/src/System.Private.CoreLib/shared/System/Int64.cs
index 78e8352d25..b7722fed46 100644
--- a/src/System.Private.CoreLib/shared/System/Int64.cs
+++ b/src/System.Private.CoreLib/shared/System/Int64.cs
@@ -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;
}
//
diff --git a/src/System.Private.CoreLib/shared/System/Number.Parsing.cs b/src/System.Private.CoreLib/shared/System/Number.Parsing.cs
index 1ef1879d67..0d3c4028b3 100644
--- a/src/System.Private.CoreLib/shared/System/Number.Parsing.cs
+++ b/src/System.Private.CoreLib/shared/System/Number.Parsing.cs
@@ -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)
diff --git a/src/System.Private.CoreLib/shared/System/ParseNumbers.cs b/src/System.Private.CoreLib/shared/System/ParseNumbers.cs
index dab4cb2190..0978186902 100644
--- a/src/System.Private.CoreLib/shared/System/ParseNumbers.cs
+++ b/src/System.Private.CoreLib/shared/System/ParseNumbers.cs
@@ -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)
{
diff --git a/src/System.Private.CoreLib/shared/System/SByte.cs b/src/System.Private.CoreLib/shared/System/SByte.cs
index d311484497..c3bc38624a 100644
--- a/src/System.Private.CoreLib/shared/System/SByte.cs
+++ b/src/System.Private.CoreLib/shared/System/SByte.cs
@@ -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;
diff --git a/src/System.Private.CoreLib/shared/System/UInt16.cs b/src/System.Private.CoreLib/shared/System/UInt16.cs
index 38c8b97b24..0cce4083f0 100644
--- a/src/System.Private.CoreLib/shared/System/UInt16.cs
+++ b/src/System.Private.CoreLib/shared/System/UInt16.cs
@@ -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;
diff --git a/src/System.Private.CoreLib/shared/System/UInt32.cs b/src/System.Private.CoreLib/shared/System/UInt32.cs
index 576b55f535..19faee1730 100644
--- a/src/System.Private.CoreLib/shared/System/UInt32.cs
+++ b/src/System.Private.CoreLib/shared/System/UInt32.cs
@@ -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;
}
//
diff --git a/src/System.Private.CoreLib/shared/System/UInt64.cs b/src/System.Private.CoreLib/shared/System/UInt64.cs
index 0abe8d4f9a..39fa643127 100644
--- a/src/System.Private.CoreLib/shared/System/UInt64.cs
+++ b/src/System.Private.CoreLib/shared/System/UInt64.cs
@@ -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;
}
//
diff --git a/src/System.Private.CoreLib/src/System/Enum.cs b/src/System.Private.CoreLib/src/System/Enum.cs
index 44a52abbf8..c8c1fa11fc 100644
--- a/src/System.Private.CoreLib/src/System/Enum.cs
+++ b/src/System.Private.CoreLib/src/System/Enum.cs
@@ -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))