From 85188641f92abb6caab1ce6dc79c6dbe1a322a5e Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 10 Apr 2023 10:01:35 -0700 Subject: [PATCH 01/11] Deduplicate TryNumberToInt* --- .../System.Private.CoreLib/src/System/Byte.cs | 19 +- .../System.Private.CoreLib/src/System/Char.cs | 19 +- .../src/System/Int128.cs | 19 +- .../src/System/Int16.cs | 19 +- .../src/System/Int32.cs | 19 +- .../src/System/Int64.cs | 19 +- .../src/System/Number.Parsing.cs | 238 ++++-------------- .../src/System/SByte.cs | 19 +- .../src/System/UInt128.cs | 19 +- .../src/System/UInt16.cs | 19 +- .../src/System/UInt32.cs | 19 +- .../src/System/UInt64.cs | 19 +- 12 files changed, 240 insertions(+), 207 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index 2a454153011960..81f9ec9c917864 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -22,7 +22,8 @@ public readonly struct Byte IEquatable, IBinaryInteger, IMinMaxValue, - IUnsignedNumber + IUnsignedNumber, + IBinaryIntegerParseAndFormatInfo { private readonly byte m_value; // Do not rename (binary serialization) @@ -1196,5 +1197,21 @@ static bool INumberBase.TryConvertToTruncating(byte value, [MaybeN /// static byte IUnaryPlusOperators.operator +(byte value) => (byte)(+value); + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; + + static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => true; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 3; // 255 + + static byte IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(byte left, byte positiveRight) => left > positiveRight; + + static byte IBinaryIntegerParseAndFormatInfo.MultiplyBy10(byte value) => (byte)(value * 10); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index 5678c119e0b96e..c86a8ec5c0b641 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -35,7 +35,8 @@ public readonly struct Char IBinaryInteger, IMinMaxValue, IUnsignedNumber, - IUtf8SpanFormattable + IUtf8SpanFormattable, + IBinaryIntegerParseAndFormatInfo { // // Member Variables @@ -2010,5 +2011,21 @@ static bool ISpanParsable.TryParse(ReadOnlySpan s, IFormatProvider? /// static char IUnaryPlusOperators.operator +(char value) => (char)(+value); + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; + + static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => true; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 5; // 65_535 + + static char IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => (char)(MaxValue / 10); + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(char left, char positiveRight) => left > positiveRight; + + static char IBinaryIntegerParseAndFormatInfo.MultiplyBy10(char value) => (char)(value * 10); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index cbfeeb9b8ba6e3..7d6032c812571f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -17,7 +17,8 @@ namespace System public readonly struct Int128 : IBinaryInteger, IMinMaxValue, - ISignedNumber + ISignedNumber, + IBinaryIntegerParseAndFormatInfo { internal const int Size = 16; @@ -2192,5 +2193,21 @@ static bool INumberBase.TryConvertToTruncating(Int128 value, [Ma /// public static Int128 operator +(Int128 value) => value; + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true; + + static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => false; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 39; // 170_141_183_460_469_231_731_687_303_715_884_105_727 + + static Int128 IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => new Int128(0x0CCC_CCCC_CCCC_CCCC, 0xCCCC_CCCC_CCCC_CCCC); + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(Int128 left, Int128 right) => (UInt128)(left) > (UInt128)(right); + + static Int128 IBinaryIntegerParseAndFormatInfo.MultiplyBy10(Int128 value) => value * 10; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index 67183c0b1b5f7e..f2654d064393ae 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -23,7 +23,8 @@ public readonly struct Int16 IEquatable, IBinaryInteger, IMinMaxValue, - ISignedNumber + ISignedNumber, + IBinaryIntegerParseAndFormatInfo { private readonly short m_value; // Do not rename (binary serialization) @@ -1414,5 +1415,21 @@ static bool INumberBase.TryConvertToTruncating(short value, [Mayb /// static short IUnaryPlusOperators.operator +(short value) => (short)(+value); + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true; + + static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => false; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 5; // 32_767 + + static short IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(short left, short right) => (ushort)(left) > (ushort)(right); + + static short IBinaryIntegerParseAndFormatInfo.MultiplyBy10(short value) => (short)(value * 10); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index 56dc9a7c8ab051..99d4347fe64a4d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -23,7 +23,8 @@ public readonly struct Int32 IEquatable, IBinaryInteger, IMinMaxValue, - ISignedNumber + ISignedNumber, + IBinaryIntegerParseAndFormatInfo { private readonly int m_value; // Do not rename (binary serialization) @@ -1433,5 +1434,21 @@ static bool INumberBase.TryConvertToTruncating(int value, [MaybeNul /// static int IUnaryPlusOperators.operator +(int value) => +value; + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true; + + static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => false; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 10; // 2_147_483_647 + + static int IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(int left, int right) => (uint)(left) > (uint)(right); + + static int IBinaryIntegerParseAndFormatInfo.MultiplyBy10(int value) => value * 10; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index de499239ebfd3f..895d5dc78eda12 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -23,7 +23,8 @@ public readonly struct Int64 IEquatable, IBinaryInteger, IMinMaxValue, - ISignedNumber + ISignedNumber, + IBinaryIntegerParseAndFormatInfo { private readonly long m_value; // Do not rename (binary serialization) @@ -1426,5 +1427,21 @@ static bool INumberBase.TryConvertToTruncating(long value, [MaybeN /// static long IUnaryPlusOperators.operator +(long value) => +value; + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true; + + static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => false; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 19; // 9_223_372_036_854_775_807 + + static long IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(long left, long right) => (ulong)(left) > (ulong)(right); + + static long IBinaryIntegerParseAndFormatInfo.MultiplyBy10(long value) => value * 10; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index b738f5058d08a9..7cb1b33063684c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Numerics; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -24,6 +25,22 @@ namespace System // specified. Note, however, that the Parse methods do not accept // NaNs or Infinities. + internal interface IBinaryIntegerParseAndFormatInfo : IBinaryInteger, IMinMaxValue + where TSelf : unmanaged, IBinaryIntegerParseAndFormatInfo + { + static abstract bool IsSigned { get; } + + static abstract bool IsUnsigned { get; } + + static abstract int MaxDigitCount { get; } + + static abstract TSelf MaxValueDiv10 { get; } + + static abstract bool IsGreaterThanAsUnsigned(TSelf left, TSelf right); + + static abstract TSelf MultiplyBy10(TSelf value); + } + internal static partial class Number { private const int Int32Precision = 10; @@ -45,233 +62,62 @@ internal static partial class Number private const int HalfMaxExponent = 5; private const int HalfMinExponent = -8; - private static unsafe bool TryNumberToInt32(ref NumberBuffer number, ref int value) + private static unsafe bool TryNumberBufferToBinaryInteger(ref NumberBuffer number, ref TInteger value) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { number.CheckConsistency(); int i = number.Scale; - if (i > Int32Precision || i < number.DigitsCount) - { - return false; - } - byte* p = number.GetDigitsPointer(); - Debug.Assert(p != null); - int n = 0; - while (--i >= 0) - { - if ((uint)n > (0x7FFFFFFF / 10)) - { - return false; - } - n *= 10; - if (*p != '\0') - { - n += (*p++ - '0'); - } - } - if (number.IsNegative) - { - n = -n; - if (n > 0) - { - return false; - } - } - else - { - if (n < 0) - { - return false; - } - } - value = n; - return true; - } - private static unsafe bool TryNumberToInt64(ref NumberBuffer number, ref long value) - { - number.CheckConsistency(); - - int i = number.Scale; - if (i > Int64Precision || i < number.DigitsCount) + if ((i > TInteger.MaxDigitCount) || (i < number.DigitsCount) || (TInteger.IsUnsigned && number.IsNegative)) { return false; } - byte* p = number.GetDigitsPointer(); - Debug.Assert(p != null); - long n = 0; - while (--i >= 0) - { - if ((ulong)n > (0x7FFFFFFFFFFFFFFF / 10)) - { - return false; - } - n *= 10; - if (*p != '\0') - { - n += (*p++ - '0'); - } - } - if (number.IsNegative) - { - n = -n; - if (n > 0) - { - return false; - } - } - else - { - if (n < 0) - { - return false; - } - } - value = n; - return true; - } - - private static unsafe bool TryNumberToInt128(ref NumberBuffer number, ref Int128 value) - { - number.CheckConsistency(); - int i = number.Scale; - if ((i > Int128Precision) || (i < number.DigitsCount)) - { - return false; - } byte* p = number.GetDigitsPointer(); + Debug.Assert(p != null); - Int128 n = 0; + TInteger n = TInteger.Zero; + while (--i >= 0) { - if ((UInt128)n > new UInt128(0x0CCCCCCCCCCCCCCC, 0xCCCCCCCCCCCCCCCC)) // Int128.MaxValue / 10 - { - return false; - } - n *= 10; - if (*p != '\0') - { - n += (*p++ - '0'); - } - } - if (number.IsNegative) - { - n = -n; - if (n > 0) + if (TInteger.IsGreaterThanAsUnsigned(n, TInteger.MaxValueDiv10)) { return false; } - } - else - { - if (n < 0) - { - return false; - } - } - value = n; - return true; - } - private static unsafe bool TryNumberToUInt32(ref NumberBuffer number, ref uint value) - { - number.CheckConsistency(); + n = TInteger.MultiplyBy10(n); - int i = number.Scale; - if (i > UInt32Precision || i < number.DigitsCount || number.IsNegative) - { - return false; - } - byte* p = number.GetDigitsPointer(); - Debug.Assert(p != null); - uint n = 0; - while (--i >= 0) - { - if (n > (0xFFFFFFFF / 10)) - { - return false; - } - n *= 10; if (*p != '\0') { - uint newN = n + (uint)(*p++ - '0'); - // Detect an overflow here... - if (newN < n) + TInteger newN = n + TInteger.CreateTruncating(*p++ - '0'); + + if (TInteger.IsUnsigned && (newN < n)) { return false; } + n = newN; } } - value = n; - return true; - } - - private static unsafe bool TryNumberToUInt64(ref NumberBuffer number, ref ulong value) - { - number.CheckConsistency(); - int i = number.Scale; - if (i > UInt64Precision || i < number.DigitsCount || number.IsNegative) - { - return false; - } - byte* p = number.GetDigitsPointer(); - Debug.Assert(p != null); - ulong n = 0; - while (--i >= 0) + if (TInteger.IsSigned) { - if (n > (0xFFFFFFFFFFFFFFFF / 10)) + if (number.IsNegative) { - return false; - } - n *= 10; - if (*p != '\0') - { - ulong newN = n + (ulong)(*p++ - '0'); - // Detect an overflow here... - if (newN < n) + n = -n; + + if (n > TInteger.Zero) { return false; } - n = newN; } - } - value = n; - return true; - } - - private static unsafe bool TryNumberToUInt128(ref NumberBuffer number, ref UInt128 value) - { - number.CheckConsistency(); - - int i = number.Scale; - if (i > UInt128Precision || (i < number.DigitsCount) || number.IsNegative) - { - return false; - } - byte* p = number.GetDigitsPointer(); - Debug.Assert(p != null); - UInt128 n = 0U; - while (--i >= 0) - { - if (n > new UInt128(0x1999999999999999, 0x9999999999999999)) // UInt128.MaxValue / 10 + else if (n < TInteger.Zero) { return false; } - n *= 10U; - if (*p != '\0') - { - UInt128 newN = n + (UInt128)(*p++ - '0'); - // Detect an overflow here... - if (newN < n) - { - return false; - } - n = newN; - } } + value = n; return true; } @@ -629,7 +475,7 @@ private static unsafe ParsingStatus TryParseInt32Number(ReadOnlySpan value return ParsingStatus.Failed; } - if (!TryNumberToInt32(ref number, ref result)) + if (!TryNumberBufferToBinaryInteger(ref number, ref result)) { return ParsingStatus.Overflow; } @@ -1023,7 +869,7 @@ private static unsafe ParsingStatus TryParseInt64Number(ReadOnlySpan value return ParsingStatus.Failed; } - if (!TryNumberToInt64(ref number, ref result)) + if (!TryNumberBufferToBinaryInteger(ref number, ref result)) { return ParsingStatus.Overflow; } @@ -1239,7 +1085,7 @@ private static unsafe ParsingStatus TryParseInt128Number(ReadOnlySpan valu return ParsingStatus.Failed; } - if (!TryNumberToInt128(ref number, ref result)) + if (!TryNumberBufferToBinaryInteger(ref number, ref result)) { return ParsingStatus.Overflow; } @@ -1274,7 +1120,7 @@ private static unsafe ParsingStatus TryParseUInt32Number(ReadOnlySpan valu return ParsingStatus.Failed; } - if (!TryNumberToUInt32(ref number, ref result)) + if (!TryNumberBufferToBinaryInteger(ref number, ref result)) { return ParsingStatus.Overflow; } @@ -1609,7 +1455,7 @@ private static unsafe ParsingStatus TryParseUInt64Number(ReadOnlySpan valu return ParsingStatus.Failed; } - if (!TryNumberToUInt64(ref number, ref result)) + if (!TryNumberBufferToBinaryInteger(ref number, ref result)) { return ParsingStatus.Overflow; } @@ -1944,7 +1790,7 @@ private static unsafe ParsingStatus TryParseUInt128Number(ReadOnlySpan val return ParsingStatus.Failed; } - if (!TryNumberToUInt128(ref number, ref result)) + if (!TryNumberBufferToBinaryInteger(ref number, ref result)) { return ParsingStatus.Overflow; } diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index ee9df96053b4cc..4967bc9570e25d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -23,7 +23,8 @@ public readonly struct SByte IEquatable, IBinaryInteger, IMinMaxValue, - ISignedNumber + ISignedNumber, + IBinaryIntegerParseAndFormatInfo { private readonly sbyte m_value; // Do not rename (binary serialization) @@ -1379,5 +1380,21 @@ static bool INumberBase.TryConvertToTruncating(sbyte value, [Mayb /// static sbyte IUnaryPlusOperators.operator +(sbyte value) => (sbyte)(+value); + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true; + + static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => false; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 3; // 127 + + static sbyte IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(sbyte left, sbyte right) => (byte)(left) > (byte)(right); + + static sbyte IBinaryIntegerParseAndFormatInfo.MultiplyBy10(sbyte value) => (sbyte)(value * 10); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index 0ae4d6bee34004..ea12e689796bd9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -18,7 +18,8 @@ namespace System public readonly struct UInt128 : IBinaryInteger, IMinMaxValue, - IUnsignedNumber + IUnsignedNumber, + IBinaryIntegerParseAndFormatInfo { internal const int Size = 16; @@ -2155,5 +2156,21 @@ static bool INumberBase.TryConvertToTruncating(UInt128 value, [ /// public static UInt128 operator +(UInt128 value) => value; + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; + + static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => true; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 39; // 340_282_366_920_938_463_463_374_607_431_768_211_455 + + static UInt128 IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => new UInt128(0x1999_9999_9999_9999, 0x9999_9999_9999_9999); + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(UInt128 left, UInt128 positiveRight) => left > positiveRight; + + static UInt128 IBinaryIntegerParseAndFormatInfo.MultiplyBy10(UInt128 value) => value * 10; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs index 025a6adddea1c9..986463d5459a1d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -23,7 +23,8 @@ public readonly struct UInt16 IEquatable, IBinaryInteger, IMinMaxValue, - IUnsignedNumber + IUnsignedNumber, + IBinaryIntegerParseAndFormatInfo { private readonly ushort m_value; // Do not rename (binary serialization) @@ -1222,5 +1223,21 @@ static bool INumberBase.TryConvertToTruncating(ushort value, [Ma /// static ushort IUnaryPlusOperators.operator +(ushort value) => (ushort)(+value); + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; + + static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => true; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 5; // 65_535 + + static ushort IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(ushort left, ushort positiveRight) => left > positiveRight; + + static ushort IBinaryIntegerParseAndFormatInfo.MultiplyBy10(ushort value) => (ushort)(value * 10); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index c84612fc8530af..fbf357e5f8a460 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -23,7 +23,8 @@ public readonly struct UInt32 IEquatable, IBinaryInteger, IMinMaxValue, - IUnsignedNumber + IUnsignedNumber, + IBinaryIntegerParseAndFormatInfo { private readonly uint m_value; // Do not rename (binary serialization) @@ -1237,5 +1238,21 @@ static bool INumberBase.TryConvertToTruncating(uint value, [MaybeN /// static uint IUnaryPlusOperators.operator +(uint value) => +value; + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; + + static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => true; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 10; // 4_294_967_295 + + static uint IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(uint left, uint positiveRight) => left > positiveRight; + + static uint IBinaryIntegerParseAndFormatInfo.MultiplyBy10(uint value) => value * 10; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index 91b569a71cf766..a838355dfd48cb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -23,7 +23,8 @@ public readonly struct UInt64 IEquatable, IBinaryInteger, IMinMaxValue, - IUnsignedNumber + IUnsignedNumber, + IBinaryIntegerParseAndFormatInfo { private readonly ulong m_value; // Do not rename (binary serialization) @@ -1230,5 +1231,21 @@ static bool INumberBase.TryConvertToTruncating(ulong value, [Mayb /// static ulong IUnaryPlusOperators.operator +(ulong value) => +value; + + // + // IBinaryIntegerParseAndFormatInfo + // + + static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; + + static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => true; + + static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 20; // 18_446_744_073_709_551_615 + + static ulong IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(ulong left, ulong positiveRight) => left > positiveRight; + + static ulong IBinaryIntegerParseAndFormatInfo.MultiplyBy10(ulong value) => value * 10; } } From f1c7772a8fe2cd9a14049f152f09f2cb47abb652 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 10 Apr 2023 10:49:52 -0700 Subject: [PATCH 02/11] Deduplicate TryParseInt*IntegerStyle parsing methods --- .../System.Private.CoreLib/src/System/Byte.cs | 9 +- .../System.Private.CoreLib/src/System/Char.cs | 14 +- .../System.Private.CoreLib/src/System/Enum.cs | 183 ++- .../src/System/Int128.cs | 11 +- .../src/System/Int16.cs | 7 +- .../src/System/Int32.cs | 7 +- .../src/System/Int64.cs | 7 +- .../src/System/Number.Parsing.cs | 1268 +++-------------- .../src/System/SByte.cs | 7 +- .../src/System/UInt128.cs | 13 +- .../src/System/UInt16.cs | 9 +- .../src/System/UInt32.cs | 9 +- .../src/System/UInt64.cs | 9 +- 13 files changed, 334 insertions(+), 1219 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index 81f9ec9c917864..801d538647db01 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -141,18 +141,17 @@ private static byte Parse(ReadOnlySpan s, NumberStyles style, NumberFormat public static bool TryParse([NotNullWhen(true)] string? s, out byte result) { - if (s == null) + if (s is null) { result = 0; return false; } - - return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, out byte result) { - return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out byte result) @@ -1210,7 +1209,7 @@ static bool INumberBase.TryConvertToTruncating(byte value, [MaybeN static byte IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; - static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(byte left, byte positiveRight) => left > positiveRight; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(byte left, byte right) => left > right; static byte IBinaryIntegerParseAndFormatInfo.MultiplyBy10(byte value) => (byte)(value * 10); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index c86a8ec5c0b641..62a37c941cef7c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -214,15 +214,12 @@ public static char Parse(string s) public static bool TryParse([NotNullWhen(true)] string? s, out char result) { - result = '\0'; - if (s == null) - { - return false; - } - if (s.Length != 1) + if ((s is null) || (s.Length != 1)) { + result = '\0'; return false; } + result = s[0]; return true; } @@ -1933,9 +1930,10 @@ static bool INumberBase.TryParse(ReadOnlySpan s, NumberStyles style, { if (s.Length != 1) { - result = default; + result = '\0'; return false; } + result = s[0]; return true; } @@ -2024,7 +2022,7 @@ static bool ISpanParsable.TryParse(ReadOnlySpan s, IFormatProvider? static char IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => (char)(MaxValue / 10); - static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(char left, char positiveRight) => left > positiveRight; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(char left, char right) => left > right; static char IBinaryIntegerParseAndFormatInfo.MultiplyBy10(char value) => (char)(value * 10); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Enum.cs b/src/libraries/System.Private.CoreLib/src/System/Enum.cs index 33a5beae402dee..ce8906e7cb22ff 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Enum.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Enum.cs @@ -726,41 +726,41 @@ private static unsafe bool TryParse(Type enumType, ReadOnlySpan value, boo switch (InternalGetCorElementType(rt)) { case CorElementType.ELEMENT_TYPE_I1: - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(sbyte*)&longScratch); + parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(sbyte*)&longScratch); longScratch = *(sbyte*)&longScratch; break; case CorElementType.ELEMENT_TYPE_U1: - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(byte*)&longScratch); + parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(byte*)&longScratch); longScratch = *(byte*)&longScratch; break; case CorElementType.ELEMENT_TYPE_I2: - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(short*)&longScratch); + parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(short*)&longScratch); longScratch = *(short*)&longScratch; break; case CorElementType.ELEMENT_TYPE_U2: - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(ushort*)&longScratch); + parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(ushort*)&longScratch); longScratch = *(ushort*)&longScratch; break; case CorElementType.ELEMENT_TYPE_I4: - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(int*)&longScratch); + parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(int*)&longScratch); longScratch = *(int*)&longScratch; break; case CorElementType.ELEMENT_TYPE_U4: - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(uint*)&longScratch); + parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(uint*)&longScratch); longScratch = *(uint*)&longScratch; break; case CorElementType.ELEMENT_TYPE_I8: - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out longScratch); + parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out longScratch); break; case CorElementType.ELEMENT_TYPE_U8: - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(ulong*)&longScratch); + parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(ulong*)&longScratch); break; default: @@ -781,35 +781,35 @@ static bool TryParseRareTypes(RuntimeType rt, ReadOnlySpan value, bool ign { case CorElementType.ELEMENT_TYPE_R4: { - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out float localResult); + parsed = TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out float localResult); result = BitConverter.SingleToInt32Bits(localResult); } break; case CorElementType.ELEMENT_TYPE_R8: { - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out double localResult); + parsed = TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out double localResult); result = BitConverter.DoubleToInt64Bits(localResult); } break; case CorElementType.ELEMENT_TYPE_I: { - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out nint localResult); + parsed = TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out nint localResult); result = localResult; } break; case CorElementType.ELEMENT_TYPE_U: { - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out nuint localResult); + parsed = TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out nuint localResult); result = (long)localResult; } break; case CorElementType.ELEMENT_TYPE_CHAR: { - parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out char localResult); + parsed = TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out char localResult); result = localResult; } break; @@ -892,30 +892,30 @@ private static bool TryParse(ReadOnlySpan value, bool ignoreCase, b RuntimeType rt = (RuntimeType)typeof(TEnum); Type underlyingType = typeof(TEnum).GetEnumUnderlyingType(); - if (underlyingType == typeof(sbyte)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(byte)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(short)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(ushort)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(int)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(uint)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(long)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(ulong)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(sbyte)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(byte)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(short)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(ushort)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(int)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(uint)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(long)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(ulong)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); #if RARE_ENUMS - if (underlyingType == typeof(nint)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(nuint)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(float)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(double)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(char)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(nint)) return TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(nuint)) return TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(float)) return TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(double)) return TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(char)) return TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); #endif throw CreateUnknownEnumTypeException(); } /// Core implementation for all {Try}Parse methods, both generic and non-generic, parsing either by value or by name. - private static unsafe bool TryParseByValueOrName( + private static unsafe bool TryParseBinaryIntegerByValueOrName( RuntimeType enumType, ReadOnlySpan value, bool ignoreCase, bool throwOnFailure, out TUnderlying result) - where TUnderlying : struct, INumber, IBitwiseOperators, IMinMaxValue - where TStorage : struct, INumber, IBitwiseOperators, IMinMaxValue + where TUnderlying : unmanaged, IBinaryIntegerParseAndFormatInfo + where TStorage : unmanaged, IBinaryIntegerParseAndFormatInfo { AssertValidGenerics(); @@ -942,89 +942,77 @@ private static unsafe bool TryParseByValueOrName( NumberFormatInfo numberFormat = CultureInfo.InvariantCulture.NumberFormat; const NumberStyles NumberStyle = NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite; - Number.ParsingStatus status; - if (typeof(TUnderlying) == typeof(int)) + Number.ParsingStatus status = Number.TryParseBinaryIntegerStyle(value, NumberStyle, numberFormat, out result); + if (status == Number.ParsingStatus.OK) { - Unsafe.SkipInit(out result); - status = Number.TryParseInt32IntegerStyle(value, NumberStyle, numberFormat, out Unsafe.As(ref result)); - if (status == Number.ParsingStatus.OK) - { - return true; - } + return true; } - else if (typeof(TUnderlying) == typeof(uint)) + + if (status != Number.ParsingStatus.Overflow) { Unsafe.SkipInit(out result); - status = Number.TryParseUInt32IntegerStyle(value, NumberStyle, numberFormat, out Unsafe.As(ref result)); - if (status == Number.ParsingStatus.OK) - { - return true; - } + return TryParseByName(enumType, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); } - else if (typeof(TUnderlying) == typeof(long)) + + if (throwOnFailure) { - Unsafe.SkipInit(out result); - status = Number.TryParseInt64IntegerStyle(value, NumberStyle, numberFormat, out Unsafe.As(ref result)); - if (status == Number.ParsingStatus.OK) + Number.ThrowOverflowException(Type.GetTypeCode(typeof(TUnderlying))); + } + } + + ParseFailure: + if (throwOnFailure) + { + ThrowInvalidEmptyParseArgument(); + } + + result = default; + return false; + } + + private static unsafe bool TryParseRareTypeByValueOrName( + RuntimeType enumType, ReadOnlySpan value, bool ignoreCase, bool throwOnFailure, out TUnderlying result) + where TUnderlying : struct, INumber, IBitwiseOperators, IMinMaxValue + where TStorage : struct, INumber, IBitwiseOperators, IMinMaxValue + { + AssertValidGenerics(); + + if (!value.IsEmpty) + { + char c = value[0]; + if (char.IsWhiteSpace(c)) + { + value = value.TrimStart(); + if (value.IsEmpty) { - return true; + goto ParseFailure; } + + c = value[0]; } - else if (typeof(TUnderlying) == typeof(ulong)) + + if (!char.IsAsciiDigit(c) && c != '-' && c != '+') { Unsafe.SkipInit(out result); - status = Number.TryParseUInt64IntegerStyle(value, NumberStyle, numberFormat, out Unsafe.As(ref result)); - if (status == Number.ParsingStatus.OK) - { - return true; - } + return TryParseByName(enumType, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); } - else if (typeof(TUnderlying) == typeof(byte) || typeof(TUnderlying) == typeof(ushort)) + +#if RARE_ENUMS + Number.ParsingStatus status; + Type underlyingType = GetUnderlyingType(enumType); + + try { - status = Number.TryParseUInt32IntegerStyle(value, NumberStyle, numberFormat, out uint uint32result); - if (status == Number.ParsingStatus.OK) - { - if (uint32result <= uint.CreateTruncating(TUnderlying.MaxValue)) - { - result = TUnderlying.CreateTruncating(uint32result); - return true; - } - status = Number.ParsingStatus.Overflow; - } + result = (TUnderlying)ToObject(enumType, Convert.ChangeType(value.ToString(), underlyingType, CultureInfo.InvariantCulture)!); + return true; } - else if (typeof(TUnderlying) == typeof(sbyte) || typeof(TUnderlying) == typeof(short)) + catch (FormatException) { - status = Number.TryParseInt32IntegerStyle(value, NumberStyle, numberFormat, out int int32result); - if (status == Number.ParsingStatus.OK) - { - if (int32result >= int.CreateTruncating(TUnderlying.MinValue) && int32result <= int.CreateTruncating(TUnderlying.MaxValue)) - { - result = TUnderlying.CreateTruncating(int32result); - return true; - } - status = Number.ParsingStatus.Overflow; - } + status = Number.ParsingStatus.Failed; // e.g. tlbimp enums that can have values of the form "3D" } - else + catch when (!throwOnFailure) { -#if RARE_ENUMS - Type underlyingType = GetUnderlyingType(enumType); - try - { - result = (TUnderlying)ToObject(enumType, Convert.ChangeType(value.ToString(), underlyingType, CultureInfo.InvariantCulture)!); - return true; - } - catch (FormatException) - { - status = Number.ParsingStatus.Failed; // e.g. tlbimp enums that can have values of the form "3D" - } - catch when (!throwOnFailure) - { - status = Number.ParsingStatus.Overflow; // fall through to returning failure - } -#else - throw CreateUnknownEnumTypeException(); -#endif + status = Number.ParsingStatus.Overflow; // fall through to returning failure } if (status != Number.ParsingStatus.Overflow) @@ -1037,6 +1025,9 @@ private static unsafe bool TryParseByValueOrName( { Number.ThrowOverflowException(Type.GetTypeCode(typeof(TUnderlying))); } +#else + throw CreateUnknownEnumTypeException(); +#endif } ParseFailure: @@ -1049,7 +1040,7 @@ private static unsafe bool TryParseByValueOrName( return false; } - /// Handles just the name parsing portion of . + /// Handles just the name parsing portion of . private static bool TryParseByName(RuntimeType enumType, ReadOnlySpan value, bool ignoreCase, bool throwOnFailure, out TStorage result) where TStorage : struct, INumber, IBitwiseOperators { diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index 7d6032c812571f..2db9025e6029f8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -153,20 +153,17 @@ public static Int128 Parse(ReadOnlySpan s, NumberStyles style = NumberStyl public static bool TryParse([NotNullWhen(true)] string? s, out Int128 result) { - if (s is not null) + if (s is null) { - return Number.TryParseInt128IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } - else - { - result = default; + result = 0; return false; } + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, out Int128 result) { - return Number.TryParseInt128IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out Int128 result) diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index f2654d064393ae..6b01e53a6c64fc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -170,18 +170,17 @@ private static short Parse(ReadOnlySpan s, NumberStyles style, NumberForma public static bool TryParse([NotNullWhen(true)] string? s, out short result) { - if (s == null) + if (s is null) { result = 0; return false; } - - return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, out short result) { - return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out short result) diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index 99d4347fe64a4d..c38e9835e0b9e6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -174,18 +174,17 @@ public static int Parse(ReadOnlySpan s, NumberStyles style = NumberStyles. // public static bool TryParse([NotNullWhen(true)] string? s, out int result) { - if (s == null) + if (s is null) { result = 0; return false; } - - return Number.TryParseInt32IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, out int result) { - return Number.TryParseInt32IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } // Parses an integer from a String in the given style. Returns false rather diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index 895d5dc78eda12..cfe27a9fce0486 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -164,18 +164,17 @@ public static long Parse(ReadOnlySpan s, NumberStyles style = NumberStyles public static bool TryParse([NotNullWhen(true)] string? s, out long result) { - if (s == null) + if (s is null) { result = 0; return false; } - - return Number.TryParseInt64IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, out long result) { - return Number.TryParseInt64IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out long result) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index 7cb1b33063684c..0a5e7ee9698444 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -453,7 +453,7 @@ internal static ParsingStatus TryParseInt32(ReadOnlySpan value, NumberStyl 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); + return TryParseBinaryIntegerStyle(value, styles, info, out result); } if ((styles & NumberStyles.AllowHexSpecifier) != 0) @@ -484,7 +484,8 @@ private static unsafe ParsingStatus TryParseInt32Number(ReadOnlySpan value } /// Parses int limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out int result) + internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out TInteger result) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format"); @@ -508,14 +509,14 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value } // Parse leading sign. - int sign = 1; + bool isNegative = false; if ((styles & NumberStyles.AllowLeadingSign) != 0) { if (info.HasInvariantNumberSigns) { if (num == '-') { - sign = -1; + isNegative = true; index++; if ((uint)index >= (uint)value.Length) goto FalseExit; @@ -531,7 +532,7 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value } else if (info.AllowHyphenDuringParsing && num == '-') { - sign = -1; + isNegative = true; index++; if ((uint)index >= (uint)value.Length) goto FalseExit; @@ -551,7 +552,7 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value } else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign)) { - sign = -1; + isNegative = true; index += negativeSign.Length; if ((uint)index >= (uint)value.Length) goto FalseExit; @@ -560,8 +561,8 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value } } - bool overflow = false; - int answer = 0; + bool overflow = TInteger.IsUnsigned && isNegative; + TInteger answer = TInteger.Zero; if (IsDigit(num)) { @@ -579,30 +580,58 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value goto HasTrailingChars; } - // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits. - answer = num - '0'; // first digit + // Parse most digits, up to the potential for overflow, which can't happen until after MaxDigitCount - 1 digits. + answer = TInteger.CreateTruncating(num - '0'); // first digit index++; - for (int i = 0; i < 8; i++) // next 8 digits can't overflow + for (int i = 0; i < TInteger.MaxDigitCount - 2; i++) // next MaxDigitCount - 2 digits can't overflow { if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; + { + if (TInteger.IsUnsigned) + goto DoneAtEndButPotentialOverflow; + else + goto DoneAtEnd; + } num = value[index]; if (!IsDigit(num)) + { + if (TInteger.IsUnsigned) + { + overflow = false; + } goto HasTrailingChars; + } index++; - answer = 10 * answer + num - '0'; + answer = TInteger.MultiplyBy10(answer); + answer += TInteger.CreateTruncating(num - '0'); } if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; + { + if (TInteger.IsUnsigned) + goto DoneAtEndButPotentialOverflow; + else + goto DoneAtEnd; + } num = value[index]; if (!IsDigit(num)) goto HasTrailingChars; index++; - // Potential overflow now processing the 10th digit. - overflow = answer > int.MaxValue / 10; - answer = answer * 10 + num - '0'; - overflow |= (uint)answer > int.MaxValue + (((uint)sign) >> 31); + // Potential overflow now processing the MaxDigitCount digit. + if (TInteger.IsUnsigned) + { + overflow |= (answer > TInteger.MaxValueDiv10) || (answer == TInteger.MaxValueDiv10) && (num > '5'); + } + else + { + overflow = answer > TInteger.MaxValueDiv10; + } + answer = TInteger.MultiplyBy10(answer); + answer += TInteger.CreateTruncating(num - '0'); + if (TInteger.IsSigned) + { + overflow |= TInteger.IsGreaterThanAsUnsigned(answer, TInteger.MaxValue + (isNegative ? TInteger.One : TInteger.Zero)); + } if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow; @@ -627,17 +656,25 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value goto OverflowExit; } DoneAtEnd: - result = answer * sign; + if (TInteger.IsUnsigned) + { + Debug.Assert(!isNegative); + result = answer; + } + else + { + result = isNegative ? -answer : answer; + } ParsingStatus status = ParsingStatus.OK; Exit: return status; FalseExit: // parsing failed - result = 0; + result = TInteger.Zero; status = ParsingStatus.Failed; goto Exit; OverflowExit: - result = 0; + result = TInteger.Zero; status = ParsingStatus.Overflow; goto Exit; @@ -662,10 +699,118 @@ internal static ParsingStatus TryParseInt32IntegerStyle(ReadOnlySpan value goto DoneAtEndButPotentialOverflow; } - /// Parses long inputs limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out long result) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ParsingStatus TryParseInt64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out long result) + { + if ((styles & ~NumberStyles.Integer) == 0) + { + // Optimized path for the common case of anything that's allowed for integer style. + return TryParseBinaryIntegerStyle(value, styles, info, out result); + } + + if ((styles & NumberStyles.AllowHexSpecifier) != 0) + { + result = 0; + return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As(ref result)); + } + + return TryParseInt64Number(value, styles, info, out result); + } + + private static unsafe ParsingStatus TryParseInt64Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out long result) { - Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format"); + result = 0; + NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[Int64NumberBufferLength]); + + if (!TryStringToNumber(value, styles, ref number, info)) + { + return ParsingStatus.Failed; + } + + if (!TryNumberBufferToBinaryInteger(ref number, ref result)) + { + return ParsingStatus.Overflow; + } + + return ParsingStatus.OK; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ParsingStatus TryParseInt128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Int128 result) + { + if ((styles & ~NumberStyles.Integer) == 0) + { + // Optimized path for the common case of anything that's allowed for integer style. + return TryParseBinaryIntegerStyle(value, styles, info, out result); + } + + if ((styles & NumberStyles.AllowHexSpecifier) != 0) + { + ParsingStatus status = TryParseUInt128HexNumberStyle(value, styles, out UInt128 unsignedResult); + result = new Int128(unsignedResult.Upper, unsignedResult.Lower); + return status; + } + + return TryParseInt128Number(value, styles, info, out result); + } + + private static unsafe ParsingStatus TryParseInt128Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Int128 result) + { + result = 0; + NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[Int128NumberBufferLength]); + + if (!TryStringToNumber(value, styles, ref number, info)) + { + return ParsingStatus.Failed; + } + + if (!TryNumberBufferToBinaryInteger(ref number, ref result)) + { + return ParsingStatus.Overflow; + } + + return ParsingStatus.OK; + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal static ParsingStatus TryParseUInt32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out uint result) + { + if ((styles & ~NumberStyles.Integer) == 0) + { + // Optimized path for the common case of anything that's allowed for integer style. + return TryParseBinaryIntegerStyle(value, styles, info, out result); + } + + if ((styles & NumberStyles.AllowHexSpecifier) != 0) + { + return TryParseUInt32HexNumberStyle(value, styles, out result); + } + + return TryParseUInt32Number(value, styles, info, out result); + } + + private static unsafe ParsingStatus TryParseUInt32Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out uint result) + { + result = 0; + NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[UInt32NumberBufferLength]); + + if (!TryStringToNumber(value, styles, ref number, info)) + { + return ParsingStatus.Failed; + } + + if (!TryNumberBufferToBinaryInteger(ref number, ref result)) + { + return ParsingStatus.Overflow; + } + + return ParsingStatus.OK; + } + + /// Parses uint limited to styles that make up NumberStyles.HexNumber. + internal static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan value, NumberStyles styles, out uint result) + { + Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format"); if (value.IsEmpty) goto FalseExit; @@ -686,63 +831,10 @@ internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan value while (IsWhite(num)); } - // Parse leading sign. - int sign = 1; - if ((styles & NumberStyles.AllowLeadingSign) != 0) - { - if (info.HasInvariantNumberSigns) - { - if (num == '-') - { - sign = -1; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (num == '+') - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - else if (info.AllowHyphenDuringParsing && num == '-') - { - sign = -1; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else - { - value = value.Slice(index); - index = 0; - string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign; - if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign)) - { - index += positiveSign.Length; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign)) - { - sign = -1; - index += negativeSign.Length; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - } - bool overflow = false; - long answer = 0; + uint answer = 0; - if (IsDigit(num)) + if (HexConverter.IsHexChar(num)) { // Skip past leading zeros. if (num == '0') @@ -754,48 +846,43 @@ internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan value goto DoneAtEnd; num = value[index]; } while (num == '0'); - if (!IsDigit(num)) + if (!HexConverter.IsHexChar(num)) goto HasTrailingChars; } - // Parse most digits, up to the potential for overflow, which can't happen until after 18 digits. - answer = num - '0'; // first digit + // Parse up through 8 digits, as no overflow is possible + answer = (uint)HexConverter.FromChar(num); // first digit index++; - for (int i = 0; i < 17; i++) // next 17 digits can't overflow + for (int i = 0; i < 7; i++) // next 7 digits can't overflow { if ((uint)index >= (uint)value.Length) goto DoneAtEnd; num = value[index]; - if (!IsDigit(num)) + + uint numValue = (uint)HexConverter.FromChar(num); + if (numValue == 0xFF) goto HasTrailingChars; index++; - answer = 10 * answer + num - '0'; + answer = 16 * answer + numValue; } + // If there's another digit, it's an overflow. if ((uint)index >= (uint)value.Length) goto DoneAtEnd; num = value[index]; - if (!IsDigit(num)) + if (!HexConverter.IsHexChar(num)) goto HasTrailingChars; - index++; - // Potential overflow now processing the 19th digit. - overflow = answer > long.MaxValue / 10; - answer = answer * 10 + num - '0'; - overflow |= (ulong)answer > (ulong)long.MaxValue + (((uint)sign) >> 31); - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; // At this point, we're either overflowing or hitting a formatting error. - // Format errors take precedence for compatibility. - num = value[index]; - while (IsDigit(num)) + // Format errors take precedence for compatibility. Read through any remaining digits. + do { - overflow = true; index++; if ((uint)index >= (uint)value.Length) goto OverflowExit; num = value[index]; - } + } while (HexConverter.IsHexChar(num)); + overflow = true; goto HasTrailingChars; } goto FalseExit; @@ -806,7 +893,7 @@ internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan value goto OverflowExit; } DoneAtEnd: - result = answer * sign; + result = answer; ParsingStatus status = ParsingStatus.OK; Exit: return status; @@ -842,27 +929,26 @@ internal static ParsingStatus TryParseInt64IntegerStyle(ReadOnlySpan value } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseInt64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out long result) + internal static ParsingStatus TryParseUInt64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out ulong result) { if ((styles & ~NumberStyles.Integer) == 0) { // Optimized path for the common case of anything that's allowed for integer style. - return TryParseInt64IntegerStyle(value, styles, info, out result); + return TryParseBinaryIntegerStyle(value, styles, info, out result); } if ((styles & NumberStyles.AllowHexSpecifier) != 0) { - result = 0; - return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As(ref result)); + return TryParseUInt64HexNumberStyle(value, styles, out result); } - return TryParseInt64Number(value, styles, info, out result); + return TryParseUInt64Number(value, styles, info, out result); } - private static unsafe ParsingStatus TryParseInt64Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out long result) + private static unsafe ParsingStatus TryParseUInt64Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out ulong result) { result = 0; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[Int64NumberBufferLength]); + NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[UInt64NumberBufferLength]); if (!TryStringToNumber(value, styles, ref number, info)) { @@ -877,10 +963,10 @@ private static unsafe ParsingStatus TryParseInt64Number(ReadOnlySpan value return ParsingStatus.OK; } - /// Parses Int128 inputs limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseInt128IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Int128 result) + /// Parses ulong limited to styles that make up NumberStyles.HexNumber. + private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan value, NumberStyles styles, out ulong result) { - Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format"); + Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format"); if (value.IsEmpty) goto FalseExit; @@ -901,784 +987,19 @@ internal static ParsingStatus TryParseInt128IntegerStyle(ReadOnlySpan valu while (IsWhite(num)); } - // Parse leading sign. - int sign = 1; - if ((styles & NumberStyles.AllowLeadingSign) != 0) + bool overflow = false; + ulong answer = 0; + + if (HexConverter.IsHexChar(num)) { - if (info.HasInvariantNumberSigns) + // Skip past leading zeros. + if (num == '0') { - if (num == '-') - { - sign = -1; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (num == '+') + do { index++; if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - else if (info.AllowHyphenDuringParsing && num == '-') - { - sign = -1; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else - { - value = value.Slice(index); - index = 0; - string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign; - if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign)) - { - index += positiveSign.Length; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign)) - { - sign = -1; - index += negativeSign.Length; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - } - - bool overflow = false; - Int128 answer = 0; - - if (IsDigit(num)) - { - // Skip past leading zeros. - if (num == '0') - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - } while (num == '0'); - if (!IsDigit(num)) - goto HasTrailingChars; - } - - // Parse most digits, up to the potential for overflow, which can't happen until after 18 digits. - answer = num - '0'; // first digit - index++; - for (int i = 0; i < 37; i++) // next 37 digits can't overflow - { - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - answer = 10 * answer + num - '0'; - } - - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - // Potential overflow now processing the 39th digit. - overflow = answer > new Int128(0x0CCCCCCCCCCCCCCC, 0xCCCCCCCCCCCCCCCC); // Int128.MaxValue / 10 - answer = answer * 10 + num - '0'; - overflow |= (UInt128)answer > new UInt128(0x7FFF_FFFF_FFFF_FFFF, 0xFFFF_FFFF_FFFF_FFFF) + (((uint)sign) >> 31); - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - - // At this point, we're either overflowing or hitting a formatting error. - // Format errors take precedence for compatibility. - num = value[index]; - while (IsDigit(num)) - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto OverflowExit; - num = value[index]; - } - goto HasTrailingChars; - } - goto FalseExit; - - DoneAtEndButPotentialOverflow: - if (overflow) - { - goto OverflowExit; - } - DoneAtEnd: - result = answer * sign; - 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. - if (IsWhite(num)) - { - if ((styles & NumberStyles.AllowTrailingWhite) == 0) - goto FalseExit; - for (index++; index < value.Length; index++) - { - if (!IsWhite(value[index])) - break; - } - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - } - - if (!TrailingZeros(value, index)) - goto FalseExit; - - goto DoneAtEndButPotentialOverflow; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseInt128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Int128 result) - { - if ((styles & ~NumberStyles.Integer) == 0) - { - // Optimized path for the common case of anything that's allowed for integer style. - return TryParseInt128IntegerStyle(value, styles, info, out result); - } - - if ((styles & NumberStyles.AllowHexSpecifier) != 0) - { - ParsingStatus status = TryParseUInt128HexNumberStyle(value, styles, out UInt128 unsignedResult); - result = new Int128(unsignedResult.Upper, unsignedResult.Lower); - return status; - } - - return TryParseInt128Number(value, styles, info, out result); - } - - private static unsafe ParsingStatus TryParseInt128Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Int128 result) - { - result = 0; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[Int128NumberBufferLength]); - - if (!TryStringToNumber(value, styles, ref number, info)) - { - return ParsingStatus.Failed; - } - - if (!TryNumberBufferToBinaryInteger(ref number, ref result)) - { - return ParsingStatus.Overflow; - } - - return ParsingStatus.OK; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseUInt32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out uint result) - { - if ((styles & ~NumberStyles.Integer) == 0) - { - // Optimized path for the common case of anything that's allowed for integer style. - return TryParseUInt32IntegerStyle(value, styles, info, out result); - } - - if ((styles & NumberStyles.AllowHexSpecifier) != 0) - { - return TryParseUInt32HexNumberStyle(value, styles, out result); - } - - return TryParseUInt32Number(value, styles, info, out result); - } - - private static unsafe ParsingStatus TryParseUInt32Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out uint result) - { - result = 0; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[UInt32NumberBufferLength]); - - if (!TryStringToNumber(value, styles, ref number, info)) - { - return ParsingStatus.Failed; - } - - if (!TryNumberBufferToBinaryInteger(ref number, ref result)) - { - return ParsingStatus.Overflow; - } - - return ParsingStatus.OK; - } - - /// Parses uint limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseUInt32IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out uint result) - { - Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format"); - - if (value.IsEmpty) - goto FalseExit; - - int index = 0; - int num = value[0]; - - // Skip past any whitespace at the beginning. - if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num)) - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - while (IsWhite(num)); - } - - // Parse leading sign. - bool overflow = false; - if ((styles & NumberStyles.AllowLeadingSign) != 0) - { - if (info.HasInvariantNumberSigns) - { - if (num == '+') - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (num == '-') - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - else if (info.AllowHyphenDuringParsing && num == '-') - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else - { - value = value.Slice(index); - index = 0; - string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign; - if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign)) - { - index += positiveSign.Length; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign)) - { - overflow = true; - index += negativeSign.Length; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - } - - int answer = 0; - - if (IsDigit(num)) - { - // Skip past leading zeros. - if (num == '0') - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - } while (num == '0'); - if (!IsDigit(num)) - goto HasTrailingCharsZero; - } - - // Parse most digits, up to the potential for overflow, which can't happen until after 9 digits. - answer = num - '0'; // first digit - index++; - for (int i = 0; i < 8; i++) // next 8 digits can't overflow - { - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - answer = 10 * answer + num - '0'; - } - - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - // 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; - - // At this point, we're either overflowing or hitting a formatting error. - // Format errors take precedence for compatibility. - num = value[index]; - while (IsDigit(num)) - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto OverflowExit; - num = value[index]; - } - goto HasTrailingChars; - } - goto FalseExit; - - DoneAtEndButPotentialOverflow: - if (overflow) - { - goto OverflowExit; - } - DoneAtEnd: - result = (uint)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; - - 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)) - { - if ((styles & NumberStyles.AllowTrailingWhite) == 0) - goto FalseExit; - for (index++; index < value.Length; index++) - { - if (!IsWhite(value[index])) - break; - } - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - } - - if (!TrailingZeros(value, index)) - goto FalseExit; - - goto DoneAtEndButPotentialOverflow; - } - - /// Parses uint limited to styles that make up NumberStyles.HexNumber. - internal static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan value, NumberStyles styles, out uint result) - { - Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format"); - - if (value.IsEmpty) - goto FalseExit; - - int index = 0; - int num = value[0]; - - // Skip past any whitespace at the beginning. - if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num)) - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - while (IsWhite(num)); - } - - bool overflow = false; - uint answer = 0; - - if (HexConverter.IsHexChar(num)) - { - // Skip past leading zeros. - if (num == '0') - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - } while (num == '0'); - if (!HexConverter.IsHexChar(num)) - goto HasTrailingChars; - } - - // Parse up through 8 digits, as no overflow is possible - answer = (uint)HexConverter.FromChar(num); // first digit - index++; - for (int i = 0; i < 7; i++) // next 7 digits can't overflow - { - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - - uint numValue = (uint)HexConverter.FromChar(num); - if (numValue == 0xFF) - goto HasTrailingChars; - index++; - answer = 16 * answer + numValue; - } - - // If there's another digit, it's an overflow. - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - if (!HexConverter.IsHexChar(num)) - goto HasTrailingChars; - - // At this point, we're either overflowing or hitting a formatting error. - // Format errors take precedence for compatibility. Read through any remaining digits. - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto OverflowExit; - num = value[index]; - } while (HexConverter.IsHexChar(num)); - overflow = true; - goto HasTrailingChars; - } - goto FalseExit; - - DoneAtEndButPotentialOverflow: - if (overflow) - { - goto OverflowExit; - } - DoneAtEnd: - 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. - if (IsWhite(num)) - { - if ((styles & NumberStyles.AllowTrailingWhite) == 0) - goto FalseExit; - for (index++; index < value.Length; index++) - { - if (!IsWhite(value[index])) - break; - } - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - } - - if (!TrailingZeros(value, index)) - goto FalseExit; - - goto DoneAtEndButPotentialOverflow; - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseUInt64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out ulong result) - { - if ((styles & ~NumberStyles.Integer) == 0) - { - // Optimized path for the common case of anything that's allowed for integer style. - return TryParseUInt64IntegerStyle(value, styles, info, out result); - } - - if ((styles & NumberStyles.AllowHexSpecifier) != 0) - { - return TryParseUInt64HexNumberStyle(value, styles, out result); - } - - return TryParseUInt64Number(value, styles, info, out result); - } - - private static unsafe ParsingStatus TryParseUInt64Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out ulong result) - { - result = 0; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[UInt64NumberBufferLength]); - - if (!TryStringToNumber(value, styles, ref number, info)) - { - return ParsingStatus.Failed; - } - - if (!TryNumberBufferToBinaryInteger(ref number, ref result)) - { - return ParsingStatus.Overflow; - } - - return ParsingStatus.OK; - } - - /// Parses ulong limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseUInt64IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out ulong result) - { - Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format"); - - if (value.IsEmpty) - goto FalseExit; - - int index = 0; - int num = value[0]; - - // Skip past any whitespace at the beginning. - if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num)) - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - while (IsWhite(num)); - } - - // Parse leading sign. - bool overflow = false; - if ((styles & NumberStyles.AllowLeadingSign) != 0) - { - if (info.HasInvariantNumberSigns) - { - if (num == '+') - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (num == '-') - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - else if (info.AllowHyphenDuringParsing && num == '-') - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else - { - value = value.Slice(index); - index = 0; - string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign; - if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign)) - { - index += positiveSign.Length; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign)) - { - overflow = true; - index += negativeSign.Length; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - } - - long answer = 0; - - if (IsDigit(num)) - { - // Skip past leading zeros. - if (num == '0') - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - } while (num == '0'); - if (!IsDigit(num)) - goto HasTrailingCharsZero; - } - - // Parse most digits, up to the potential for overflow, which can't happen until after 19 digits. - answer = num - '0'; // first digit - index++; - for (int i = 0; i < 18; i++) // next 18 digits can't overflow - { - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - answer = 10 * answer + num - '0'; - } - - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - // 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; - - // At this point, we're either overflowing or hitting a formatting error. - // Format errors take precedence for compatibility. - num = value[index]; - while (IsDigit(num)) - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto OverflowExit; - num = value[index]; - } - goto HasTrailingChars; - } - goto FalseExit; - - DoneAtEndButPotentialOverflow: - if (overflow) - { - goto OverflowExit; - } - DoneAtEnd: - result = (ulong)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; - - 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)) - { - if ((styles & NumberStyles.AllowTrailingWhite) == 0) - goto FalseExit; - for (index++; index < value.Length; index++) - { - if (!IsWhite(value[index])) - break; - } - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - } - - if (!TrailingZeros(value, index)) - goto FalseExit; - - goto DoneAtEndButPotentialOverflow; - } - - /// Parses ulong limited to styles that make up NumberStyles.HexNumber. - private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan value, NumberStyles styles, out ulong result) - { - Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format"); - - if (value.IsEmpty) - goto FalseExit; - - int index = 0; - int num = value[0]; - - // Skip past any whitespace at the beginning. - if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num)) - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - while (IsWhite(num)); - } - - bool overflow = false; - ulong answer = 0; - - if (HexConverter.IsHexChar(num)) - { - // Skip past leading zeros. - if (num == '0') - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; + goto DoneAtEnd; num = value[index]; } while (num == '0'); if (!HexConverter.IsHexChar(num)) @@ -1769,7 +1090,7 @@ internal static ParsingStatus TryParseUInt128(ReadOnlySpan value, NumberSt if ((styles & ~NumberStyles.Integer) == 0) { // Optimized path for the common case of anything that's allowed for integer style. - return TryParseUInt128IntegerStyle(value, styles, info, out result); + return TryParseBinaryIntegerStyle(value, styles, info, out result); } if ((styles & NumberStyles.AllowHexSpecifier) != 0) @@ -1798,187 +1119,6 @@ private static unsafe ParsingStatus TryParseUInt128Number(ReadOnlySpan val return ParsingStatus.OK; } - /// Parses UInt128 limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseUInt128IntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out UInt128 result) - { - Debug.Assert((styles & ~NumberStyles.Integer) == 0, "Only handles subsets of Integer format"); - - if (value.IsEmpty) - goto FalseExit; - - int index = 0; - int num = value[0]; - - // Skip past any whitespace at the beginning. - if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num)) - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - while (IsWhite(num)); - } - - // Parse leading sign. - bool overflow = false; - if ((styles & NumberStyles.AllowLeadingSign) != 0) - { - if (info.HasInvariantNumberSigns) - { - if (num == '+') - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (num == '-') - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - else if (info.AllowHyphenDuringParsing && num == '-') - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else - { - value = value.Slice(index); - index = 0; - string positiveSign = info.PositiveSign, negativeSign = info.NegativeSign; - if (!string.IsNullOrEmpty(positiveSign) && value.StartsWith(positiveSign)) - { - index += positiveSign.Length; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - else if (!string.IsNullOrEmpty(negativeSign) && value.StartsWith(negativeSign)) - { - overflow = true; - index += negativeSign.Length; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - } - } - - Int128 answer = 0; - - if (IsDigit(num)) - { - // Skip past leading zeros. - if (num == '0') - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - } while (num == '0'); - if (!IsDigit(num)) - goto HasTrailingCharsZero; - } - - // Parse most digits, up to the potential for overflow, which can't happen until after 38 digits. - answer = num - '0'; // first digit - index++; - for (int i = 0; i < 37; i++) // next 37 digits can't overflow - { - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - answer = 10 * answer + num - '0'; - } - - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - num = value[index]; - if (!IsDigit(num)) - goto HasTrailingChars; - index++; - // Potential overflow now processing the 39th digit. - UInt128 maxValueDiv10 = new UInt128(0x1999999999999999, 0x9999999999999999); // UInt128.MaxValue / 10 - overflow |= (UInt128)answer > maxValueDiv10; - overflow |= ((UInt128)answer == maxValueDiv10 && num > '5'); - answer = answer * 10 + num - '0'; - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - - // At this point, we're either overflowing or hitting a formatting error. - // Format errors take precedence for compatibility. - num = value[index]; - while (IsDigit(num)) - { - overflow = true; - index++; - if ((uint)index >= (uint)value.Length) - goto OverflowExit; - num = value[index]; - } - goto HasTrailingChars; - } - goto FalseExit; - - DoneAtEndButPotentialOverflow: - if (overflow) - { - goto OverflowExit; - } - DoneAtEnd: - result = (UInt128)answer; - ParsingStatus status = ParsingStatus.OK; - Exit: - return status; - - FalseExit: // parsing failed - result = 0U; - status = ParsingStatus.Failed; - goto Exit; - OverflowExit: - result = 0U; - 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)) - { - if ((styles & NumberStyles.AllowTrailingWhite) == 0) - goto FalseExit; - for (index++; index < value.Length; index++) - { - if (!IsWhite(value[index])) - break; - } - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - } - - if (!TrailingZeros(value, index)) - goto FalseExit; - - goto DoneAtEndButPotentialOverflow; - } - /// Parses UInt128 limited to styles that make up NumberStyles.HexNumber. private static ParsingStatus TryParseUInt128HexNumberStyle(ReadOnlySpan value, NumberStyles styles, out UInt128 result) { diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index 4967bc9570e25d..2591bffef18dc8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -177,18 +177,17 @@ private static sbyte Parse(ReadOnlySpan s, NumberStyles style, NumberForma public static bool TryParse([NotNullWhen(true)] string? s, out sbyte result) { - if (s == null) + if (s is null) { result = 0; return false; } - - return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, out sbyte result) { - return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out sbyte result) diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index ea12e689796bd9..16cb8d425dac42 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -154,20 +154,17 @@ public static UInt128 Parse(ReadOnlySpan s, NumberStyles style = NumberSty public static bool TryParse([NotNullWhen(true)] string? s, out UInt128 result) { - if (s is not null) + if (s is null) { - return Number.TryParseUInt128IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } - else - { - result = default; + result = 0; return false; } + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, out UInt128 result) { - return Number.TryParseUInt128IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out UInt128 result) @@ -2169,7 +2166,7 @@ static bool INumberBase.TryConvertToTruncating(UInt128 value, [ static UInt128 IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => new UInt128(0x1999_9999_9999_9999, 0x9999_9999_9999_9999); - static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(UInt128 left, UInt128 positiveRight) => left > positiveRight; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(UInt128 left, UInt128 right) => left > right; static UInt128 IBinaryIntegerParseAndFormatInfo.MultiplyBy10(UInt128 value) => value * 10; } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs index 986463d5459a1d..d6c59e0f201d1d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -160,18 +160,17 @@ private static ushort Parse(ReadOnlySpan s, NumberStyles style, NumberForm public static bool TryParse([NotNullWhen(true)] string? s, out ushort result) { - if (s == null) + if (s is null) { result = 0; return false; } - - return TryParse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, out ushort result) { - return TryParse(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out ushort result) @@ -1236,7 +1235,7 @@ static bool INumberBase.TryConvertToTruncating(ushort value, [Ma static ushort IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; - static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(ushort left, ushort positiveRight) => left > positiveRight; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(ushort left, ushort right) => left > right; static ushort IBinaryIntegerParseAndFormatInfo.MultiplyBy10(ushort value) => (ushort)(value * 10); } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index fbf357e5f8a460..694f49c40ab80c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -158,18 +158,17 @@ public static uint Parse(ReadOnlySpan s, NumberStyles style = NumberStyles public static bool TryParse([NotNullWhen(true)] string? s, out uint result) { - if (s == null) + if (s is null) { result = 0; return false; } - - return Number.TryParseUInt32IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, out uint result) { - return Number.TryParseUInt32IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out uint result) @@ -1251,7 +1250,7 @@ static bool INumberBase.TryConvertToTruncating(uint value, [MaybeN static uint IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; - static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(uint left, uint positiveRight) => left > positiveRight; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(uint left, uint right) => left > right; static uint IBinaryIntegerParseAndFormatInfo.MultiplyBy10(uint value) => value * 10; } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index a838355dfd48cb..61cdf219715946 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -157,18 +157,17 @@ public static ulong Parse(ReadOnlySpan s, NumberStyles style = NumberStyle public static bool TryParse([NotNullWhen(true)] string? s, out ulong result) { - if (s == null) + if (s is null) { result = 0; return false; } - - return Number.TryParseUInt64IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, out ulong result) { - return Number.TryParseUInt64IntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; } public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out ulong result) @@ -1244,7 +1243,7 @@ static bool INumberBase.TryConvertToTruncating(ulong value, [Mayb static ulong IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; - static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(ulong left, ulong positiveRight) => left > positiveRight; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(ulong left, ulong right) => left > right; static ulong IBinaryIntegerParseAndFormatInfo.MultiplyBy10(ulong value) => value * 10; } From b792be2640fc8836ccf00e43f156e06dd9e8369d Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 10 Apr 2023 11:02:59 -0700 Subject: [PATCH 03/11] Deduplicate TryParseInt*HexNumberStyle --- .../System.Private.CoreLib/src/System/Byte.cs | 4 + .../System.Private.CoreLib/src/System/Char.cs | 4 + .../System.Private.CoreLib/src/System/Guid.cs | 2 +- .../src/System/Int128.cs | 4 + .../src/System/Int16.cs | 4 + .../src/System/Int32.cs | 4 + .../src/System/Int64.cs | 4 + .../src/System/Number.Parsing.cs | 276 ++---------------- .../src/System/SByte.cs | 4 + .../src/System/UInt128.cs | 4 + .../src/System/UInt16.cs | 4 + .../src/System/UInt32.cs | 4 + .../src/System/UInt64.cs | 4 + 13 files changed, 65 insertions(+), 257 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index 801d538647db01..58d1a5f8e24b1a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -1207,10 +1207,14 @@ static bool INumberBase.TryConvertToTruncating(byte value, [MaybeN static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 3; // 255 + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 2; // 0xFF + static byte IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(byte left, byte right) => left > right; static byte IBinaryIntegerParseAndFormatInfo.MultiplyBy10(byte value) => (byte)(value * 10); + + static byte IBinaryIntegerParseAndFormatInfo.MultiplyBy16(byte value) => (byte)(value * 16); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index 62a37c941cef7c..57c43b94f976ae 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -2020,10 +2020,14 @@ static bool ISpanParsable.TryParse(ReadOnlySpan s, IFormatProvider? static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 5; // 65_535 + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 4; // 0xFFFF + static char IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => (char)(MaxValue / 10); static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(char left, char right) => left > right; static char IBinaryIntegerParseAndFormatInfo.MultiplyBy10(char value) => (char)(value * 10); + + static char IBinaryIntegerParseAndFormatInfo.MultiplyBy16(char value) => (char)(value * 16); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 4a1ede42b511ac..a9d1f77364aa7a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -490,7 +490,7 @@ static bool TryCompatParsing(ReadOnlySpan guidString, ref GuidResult resul result._fg = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness((ushort)uintTmp) : (ushort)uintTmp; // Unlike the other components, this one never allowed 0x or +, so we can parse it as straight hex. - if (Number.TryParseUInt32HexNumberStyle(guidString.Slice(28, 8), NumberStyles.AllowHexSpecifier, out uintTmp) == Number.ParsingStatus.OK) // _h, _i, _j, _k + if (Number.TryParseBinaryIntegerHexNumberStyle(guidString.Slice(28, 8), NumberStyles.AllowHexSpecifier, out uintTmp) == Number.ParsingStatus.OK) // _h, _i, _j, _k { result._hijk = BitConverter.IsLittleEndian ? BinaryPrimitives.ReverseEndianness(uintTmp) : uintTmp; return true; diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index 2db9025e6029f8..451f1a21a0a345 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -2201,10 +2201,14 @@ static bool INumberBase.TryConvertToTruncating(Int128 value, [Ma static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 39; // 170_141_183_460_469_231_731_687_303_715_884_105_727 + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 32; // 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF + static Int128 IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => new Int128(0x0CCC_CCCC_CCCC_CCCC, 0xCCCC_CCCC_CCCC_CCCC); static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(Int128 left, Int128 right) => (UInt128)(left) > (UInt128)(right); static Int128 IBinaryIntegerParseAndFormatInfo.MultiplyBy10(Int128 value) => value * 10; + + static Int128 IBinaryIntegerParseAndFormatInfo.MultiplyBy16(Int128 value) => value * 16; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index 6b01e53a6c64fc..fd23d3d3fb67e8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -1425,10 +1425,14 @@ static bool INumberBase.TryConvertToTruncating(short value, [Mayb static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 5; // 32_767 + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 4; // 0x7FFF + static short IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(short left, short right) => (ushort)(left) > (ushort)(right); static short IBinaryIntegerParseAndFormatInfo.MultiplyBy10(short value) => (short)(value * 10); + + static short IBinaryIntegerParseAndFormatInfo.MultiplyBy16(short value) => (short)(value * 16); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index c38e9835e0b9e6..5d6d9370ecfc09 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -1444,10 +1444,14 @@ static bool INumberBase.TryConvertToTruncating(int value, [MaybeNul static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 10; // 2_147_483_647 + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 8; // 0x7FFF_FFFF + static int IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(int left, int right) => (uint)(left) > (uint)(right); static int IBinaryIntegerParseAndFormatInfo.MultiplyBy10(int value) => value * 10; + + static int IBinaryIntegerParseAndFormatInfo.MultiplyBy16(int value) => value * 16; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index cfe27a9fce0486..8a268798755cf4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -1437,10 +1437,14 @@ static bool INumberBase.TryConvertToTruncating(long value, [MaybeN static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 19; // 9_223_372_036_854_775_807 + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 16; // 0x7FFF_FFFF_FFFF_FFFF + static long IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(long left, long right) => (ulong)(left) > (ulong)(right); static long IBinaryIntegerParseAndFormatInfo.MultiplyBy10(long value) => value * 10; + + static long IBinaryIntegerParseAndFormatInfo.MultiplyBy16(long value) => value * 16; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index 0a5e7ee9698444..e780f1a0e8cb2a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -34,11 +34,15 @@ internal interface IBinaryIntegerParseAndFormatInfo : IBinaryInteger value, NumberStyl if ((styles & NumberStyles.AllowHexSpecifier) != 0) { result = 0; - return TryParseUInt32HexNumberStyle(value, styles, out Unsafe.As(ref result)); + return TryParseBinaryIntegerHexNumberStyle(value, styles, out Unsafe.As(ref result)); } return TryParseInt32Number(value, styles, info, out result); @@ -711,7 +715,7 @@ internal static ParsingStatus TryParseInt64(ReadOnlySpan value, NumberStyl if ((styles & NumberStyles.AllowHexSpecifier) != 0) { result = 0; - return TryParseUInt64HexNumberStyle(value, styles, out Unsafe.As(ref result)); + return TryParseBinaryIntegerHexNumberStyle(value, styles, out Unsafe.As(ref result)); } return TryParseInt64Number(value, styles, info, out result); @@ -746,7 +750,7 @@ internal static ParsingStatus TryParseInt128(ReadOnlySpan value, NumberSty if ((styles & NumberStyles.AllowHexSpecifier) != 0) { - ParsingStatus status = TryParseUInt128HexNumberStyle(value, styles, out UInt128 unsignedResult); + ParsingStatus status = TryParseBinaryIntegerHexNumberStyle(value, styles, out UInt128 unsignedResult); result = new Int128(unsignedResult.Upper, unsignedResult.Lower); return status; } @@ -783,7 +787,7 @@ internal static ParsingStatus TryParseUInt32(ReadOnlySpan value, NumberSty if ((styles & NumberStyles.AllowHexSpecifier) != 0) { - return TryParseUInt32HexNumberStyle(value, styles, out result); + return TryParseBinaryIntegerHexNumberStyle(value, styles, out result); } return TryParseUInt32Number(value, styles, info, out result); @@ -808,7 +812,8 @@ private static unsafe ParsingStatus TryParseUInt32Number(ReadOnlySpan valu } /// Parses uint limited to styles that make up NumberStyles.HexNumber. - internal static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan value, NumberStyles styles, out uint result) + internal static ParsingStatus TryParseBinaryIntegerHexNumberStyle(ReadOnlySpan value, NumberStyles styles, out TInteger result) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format"); @@ -832,7 +837,7 @@ internal static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan va } bool overflow = false; - uint answer = 0; + TInteger answer = TInteger.Zero; if (HexConverter.IsHexChar(num)) { @@ -850,10 +855,10 @@ internal static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan va goto HasTrailingChars; } - // Parse up through 8 digits, as no overflow is possible - answer = (uint)HexConverter.FromChar(num); // first digit + // Parse up through MaxHexDigitCount digits, as no overflow is possible + answer = TInteger.CreateTruncating((uint)HexConverter.FromChar(num)); // first digit index++; - for (int i = 0; i < 7; i++) // next 7 digits can't overflow + for (int i = 0; i < TInteger.MaxHexDigitCount - 1; i++) // next MaxHexDigitCount - 1 digits can't overflow { if ((uint)index >= (uint)value.Length) goto DoneAtEnd; @@ -863,7 +868,8 @@ internal static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan va if (numValue == 0xFF) goto HasTrailingChars; index++; - answer = 16 * answer + numValue; + answer = TInteger.MultiplyBy16(answer); + answer += TInteger.CreateTruncating(numValue); } // If there's another digit, it's an overflow. @@ -899,11 +905,11 @@ internal static ParsingStatus TryParseUInt32HexNumberStyle(ReadOnlySpan va return status; FalseExit: // parsing failed - result = 0; + result = TInteger.Zero; status = ParsingStatus.Failed; goto Exit; OverflowExit: - result = 0; + result = TInteger.Zero; status = ParsingStatus.Overflow; goto Exit; @@ -939,7 +945,7 @@ internal static ParsingStatus TryParseUInt64(ReadOnlySpan value, NumberSty if ((styles & NumberStyles.AllowHexSpecifier) != 0) { - return TryParseUInt64HexNumberStyle(value, styles, out result); + return TryParseBinaryIntegerHexNumberStyle(value, styles, out result); } return TryParseUInt64Number(value, styles, info, out result); @@ -963,127 +969,6 @@ private static unsafe ParsingStatus TryParseUInt64Number(ReadOnlySpan valu return ParsingStatus.OK; } - /// Parses ulong limited to styles that make up NumberStyles.HexNumber. - private static ParsingStatus TryParseUInt64HexNumberStyle(ReadOnlySpan value, NumberStyles styles, out ulong result) - { - Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format"); - - if (value.IsEmpty) - goto FalseExit; - - int index = 0; - int num = value[0]; - - // Skip past any whitespace at the beginning. - if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num)) - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - while (IsWhite(num)); - } - - bool overflow = false; - ulong answer = 0; - - if (HexConverter.IsHexChar(num)) - { - // Skip past leading zeros. - if (num == '0') - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - } while (num == '0'); - if (!HexConverter.IsHexChar(num)) - goto HasTrailingChars; - } - - // Parse up through 16 digits, as no overflow is possible - answer = (uint)HexConverter.FromChar(num); // first digit - index++; - for (int i = 0; i < 15; i++) // next 15 digits can't overflow - { - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - - uint numValue = (uint)HexConverter.FromChar(num); - if (numValue == 0xFF) - goto HasTrailingChars; - index++; - answer = 16 * answer + numValue; - } - - // If there's another digit, it's an overflow. - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - if (!HexConverter.IsHexChar(num)) - goto HasTrailingChars; - - // At this point, we're either overflowing or hitting a formatting error. - // Format errors take precedence for compatibility. Read through any remaining digits. - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto OverflowExit; - num = value[index]; - } while (HexConverter.IsHexChar(num)); - overflow = true; - goto HasTrailingChars; - } - goto FalseExit; - - DoneAtEndButPotentialOverflow: - if (overflow) - { - goto OverflowExit; - } - DoneAtEnd: - 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. - if (IsWhite(num)) - { - if ((styles & NumberStyles.AllowTrailingWhite) == 0) - goto FalseExit; - for (index++; index < value.Length; index++) - { - if (!IsWhite(value[index])) - break; - } - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - } - - if (!TrailingZeros(value, index)) - goto FalseExit; - - goto DoneAtEndButPotentialOverflow; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static ParsingStatus TryParseUInt128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out UInt128 result) { @@ -1095,7 +980,7 @@ internal static ParsingStatus TryParseUInt128(ReadOnlySpan value, NumberSt if ((styles & NumberStyles.AllowHexSpecifier) != 0) { - return TryParseUInt128HexNumberStyle(value, styles, out result); + return TryParseBinaryIntegerHexNumberStyle(value, styles, out result); } return TryParseUInt128Number(value, styles, info, out result); @@ -1119,127 +1004,6 @@ private static unsafe ParsingStatus TryParseUInt128Number(ReadOnlySpan val return ParsingStatus.OK; } - /// Parses UInt128 limited to styles that make up NumberStyles.HexNumber. - private static ParsingStatus TryParseUInt128HexNumberStyle(ReadOnlySpan value, NumberStyles styles, out UInt128 result) - { - Debug.Assert((styles & ~NumberStyles.HexNumber) == 0, "Only handles subsets of HexNumber format"); - - if (value.IsEmpty) - goto FalseExit; - - int index = 0; - int num = value[0]; - - // Skip past any whitespace at the beginning. - if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num)) - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto FalseExit; - num = value[index]; - } - while (IsWhite(num)); - } - - bool overflow = false; - UInt128 answer = 0U; - - if (HexConverter.IsHexChar(num)) - { - // Skip past leading zeros. - if (num == '0') - { - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - } while (num == '0'); - if (!HexConverter.IsHexChar(num)) - goto HasTrailingChars; - } - - // Parse up through 32 digits, as no overflow is possible - answer = (uint)HexConverter.FromChar(num); // first digit - index++; - for (int i = 0; i < 31; i++) // next 31 digits can't overflow - { - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - - uint numValue = (uint)HexConverter.FromChar(num); - if (numValue == 0xFF) - goto HasTrailingChars; - index++; - answer = 16U * answer + numValue; - } - - // If there's another digit, it's an overflow. - if ((uint)index >= (uint)value.Length) - goto DoneAtEnd; - num = value[index]; - if (!HexConverter.IsHexChar(num)) - goto HasTrailingChars; - - // At this point, we're either overflowing or hitting a formatting error. - // Format errors take precedence for compatibility. Read through any remaining digits. - do - { - index++; - if ((uint)index >= (uint)value.Length) - goto OverflowExit; - num = value[index]; - } while (HexConverter.IsHexChar(num)); - overflow = true; - goto HasTrailingChars; - } - goto FalseExit; - - DoneAtEndButPotentialOverflow: - if (overflow) - { - goto OverflowExit; - } - DoneAtEnd: - result = answer; - ParsingStatus status = ParsingStatus.OK; - Exit: - return status; - - FalseExit: // parsing failed - result = 0U; - status = ParsingStatus.Failed; - goto Exit; - OverflowExit: - result = 0U; - 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. - if (IsWhite(num)) - { - if ((styles & NumberStyles.AllowTrailingWhite) == 0) - goto FalseExit; - for (index++; index < value.Length; index++) - { - if (!IsWhite(value[index])) - break; - } - if ((uint)index >= (uint)value.Length) - goto DoneAtEndButPotentialOverflow; - } - - if (!TrailingZeros(value, index)) - goto FalseExit; - - goto DoneAtEndButPotentialOverflow; - } - internal static decimal ParseDecimal(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) { ParsingStatus status = TryParseDecimal(value, styles, info, out decimal result); diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index 2591bffef18dc8..3f3ae6f83ce8ef 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -1390,10 +1390,14 @@ static bool INumberBase.TryConvertToTruncating(sbyte value, [Mayb static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 3; // 127 + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 2; // 0x7F + static sbyte IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(sbyte left, sbyte right) => (byte)(left) > (byte)(right); static sbyte IBinaryIntegerParseAndFormatInfo.MultiplyBy10(sbyte value) => (sbyte)(value * 10); + + static sbyte IBinaryIntegerParseAndFormatInfo.MultiplyBy16(sbyte value) => (sbyte)(value * 16); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index 16cb8d425dac42..7068755b8f0cab 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -2164,10 +2164,14 @@ static bool INumberBase.TryConvertToTruncating(UInt128 value, [ static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 39; // 340_282_366_920_938_463_463_374_607_431_768_211_455 + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 32; // 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF + static UInt128 IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => new UInt128(0x1999_9999_9999_9999, 0x9999_9999_9999_9999); static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(UInt128 left, UInt128 right) => left > right; static UInt128 IBinaryIntegerParseAndFormatInfo.MultiplyBy10(UInt128 value) => value * 10; + + static UInt128 IBinaryIntegerParseAndFormatInfo.MultiplyBy16(UInt128 value) => value * 16; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs index d6c59e0f201d1d..21f2032d6688be 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -1233,10 +1233,14 @@ static bool INumberBase.TryConvertToTruncating(ushort value, [Ma static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 5; // 65_535 + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 4; // 0xFFFF + static ushort IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(ushort left, ushort right) => left > right; static ushort IBinaryIntegerParseAndFormatInfo.MultiplyBy10(ushort value) => (ushort)(value * 10); + + static ushort IBinaryIntegerParseAndFormatInfo.MultiplyBy16(ushort value) => (ushort)(value * 16); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index 694f49c40ab80c..a850e7b339d373 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -1248,10 +1248,14 @@ static bool INumberBase.TryConvertToTruncating(uint value, [MaybeN static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 10; // 4_294_967_295 + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 8; // 0xFFFF_FFFF + static uint IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(uint left, uint right) => left > right; static uint IBinaryIntegerParseAndFormatInfo.MultiplyBy10(uint value) => value * 10; + + static uint IBinaryIntegerParseAndFormatInfo.MultiplyBy16(uint value) => value * 16; } } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index 61cdf219715946..24f0c3495d3711 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -1241,10 +1241,14 @@ static bool INumberBase.TryConvertToTruncating(ulong value, [Mayb static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 20; // 18_446_744_073_709_551_615 + static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 16; // 0xFFFF_FFFF_FFFF_FFFF + static ulong IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(ulong left, ulong right) => left > right; static ulong IBinaryIntegerParseAndFormatInfo.MultiplyBy10(ulong value) => value * 10; + + static ulong IBinaryIntegerParseAndFormatInfo.MultiplyBy16(ulong value) => value * 16; } } From 6d67dbbefbd56714c06ebe1ed407a3791e91e8c5 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 10 Apr 2023 11:06:05 -0700 Subject: [PATCH 04/11] Deduplicate TryParseInt*Number --- .../src/System/Number.Parsing.cs | 109 ++---------------- 1 file changed, 10 insertions(+), 99 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index e780f1a0e8cb2a..0b90364263087a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -466,13 +466,14 @@ internal static ParsingStatus TryParseInt32(ReadOnlySpan value, NumberStyl return TryParseBinaryIntegerHexNumberStyle(value, styles, out Unsafe.As(ref result)); } - return TryParseInt32Number(value, styles, info, out result); + return TryParseBinaryIntegerNumber(value, styles, info, out result); } - private static unsafe ParsingStatus TryParseInt32Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out int result) + private static unsafe ParsingStatus TryParseBinaryIntegerNumber(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out TInteger result) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { - result = 0; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[Int32NumberBufferLength]); + result = TInteger.Zero; + NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[TInteger.MaxDigitCount + 1]); if (!TryStringToNumber(value, styles, ref number, info)) { @@ -718,25 +719,7 @@ internal static ParsingStatus TryParseInt64(ReadOnlySpan value, NumberStyl return TryParseBinaryIntegerHexNumberStyle(value, styles, out Unsafe.As(ref result)); } - return TryParseInt64Number(value, styles, info, out result); - } - - private static unsafe ParsingStatus TryParseInt64Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out long result) - { - result = 0; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[Int64NumberBufferLength]); - - if (!TryStringToNumber(value, styles, ref number, info)) - { - return ParsingStatus.Failed; - } - - if (!TryNumberBufferToBinaryInteger(ref number, ref result)) - { - return ParsingStatus.Overflow; - } - - return ParsingStatus.OK; + return TryParseBinaryIntegerNumber(value, styles, info, out result); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -755,25 +738,7 @@ internal static ParsingStatus TryParseInt128(ReadOnlySpan value, NumberSty return status; } - return TryParseInt128Number(value, styles, info, out result); - } - - private static unsafe ParsingStatus TryParseInt128Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Int128 result) - { - result = 0; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[Int128NumberBufferLength]); - - if (!TryStringToNumber(value, styles, ref number, info)) - { - return ParsingStatus.Failed; - } - - if (!TryNumberBufferToBinaryInteger(ref number, ref result)) - { - return ParsingStatus.Overflow; - } - - return ParsingStatus.OK; + return TryParseBinaryIntegerNumber(value, styles, info, out result); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -790,25 +755,7 @@ internal static ParsingStatus TryParseUInt32(ReadOnlySpan value, NumberSty return TryParseBinaryIntegerHexNumberStyle(value, styles, out result); } - return TryParseUInt32Number(value, styles, info, out result); - } - - private static unsafe ParsingStatus TryParseUInt32Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out uint result) - { - result = 0; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[UInt32NumberBufferLength]); - - if (!TryStringToNumber(value, styles, ref number, info)) - { - return ParsingStatus.Failed; - } - - if (!TryNumberBufferToBinaryInteger(ref number, ref result)) - { - return ParsingStatus.Overflow; - } - - return ParsingStatus.OK; + return TryParseBinaryIntegerNumber(value, styles, info, out result); } /// Parses uint limited to styles that make up NumberStyles.HexNumber. @@ -948,25 +895,7 @@ internal static ParsingStatus TryParseUInt64(ReadOnlySpan value, NumberSty return TryParseBinaryIntegerHexNumberStyle(value, styles, out result); } - return TryParseUInt64Number(value, styles, info, out result); - } - - private static unsafe ParsingStatus TryParseUInt64Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out ulong result) - { - result = 0; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[UInt64NumberBufferLength]); - - if (!TryStringToNumber(value, styles, ref number, info)) - { - return ParsingStatus.Failed; - } - - if (!TryNumberBufferToBinaryInteger(ref number, ref result)) - { - return ParsingStatus.Overflow; - } - - return ParsingStatus.OK; + return TryParseBinaryIntegerNumber(value, styles, info, out result); } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -983,25 +912,7 @@ internal static ParsingStatus TryParseUInt128(ReadOnlySpan value, NumberSt return TryParseBinaryIntegerHexNumberStyle(value, styles, out result); } - return TryParseUInt128Number(value, styles, info, out result); - } - - private static unsafe ParsingStatus TryParseUInt128Number(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out UInt128 result) - { - result = 0U; - NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[UInt128NumberBufferLength]); - - if (!TryStringToNumber(value, styles, ref number, info)) - { - return ParsingStatus.Failed; - } - - if (!TryNumberBufferToBinaryInteger(ref number, ref result)) - { - return ParsingStatus.Overflow; - } - - return ParsingStatus.OK; + return TryParseBinaryIntegerNumber(value, styles, info, out result); } internal static decimal ParseDecimal(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) From 30f246b9a45724711650c56564959430772584ff Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 10 Apr 2023 11:20:59 -0700 Subject: [PATCH 05/11] Deduplicate TryParseInt* --- .../System.Private.CoreLib/src/System/Byte.cs | 21 +--- .../src/System/Int128.cs | 11 +- .../src/System/Int16.cs | 23 +--- .../src/System/Int32.cs | 7 +- .../src/System/Int64.cs | 7 +- .../src/System/Number.Parsing.cs | 106 ++---------------- .../src/System/SByte.cs | 23 +--- .../src/System/UInt128.cs | 11 +- .../src/System/UInt16.cs | 21 +--- .../src/System/UInt32.cs | 7 +- .../src/System/UInt64.cs | 7 +- 11 files changed, 45 insertions(+), 199 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index 58d1a5f8e24b1a..16fabdc5cc3ccb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -129,7 +129,7 @@ public static byte Parse(ReadOnlySpan s, NumberStyles style = NumberStyles private static byte Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) { - Number.ParsingStatus status = Number.TryParseUInt32(s, style, info, out uint i); + Number.ParsingStatus status = Number.TryParseBinaryInteger(s, style, info, out uint i); if (status != Number.ParsingStatus.OK) { Number.ThrowOverflowOrFormatException(status, s, TypeCode.Byte); @@ -158,31 +158,18 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out byte result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); - } - - private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out byte result) - { - if (Number.TryParseUInt32(s, style, info, out uint i) != Number.ParsingStatus.OK - || i > MaxValue) - { - result = 0; - return false; - } - result = (byte)i; - return true; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public override string ToString() diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index 451f1a21a0a345..157aee49c184a6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -170,21 +170,18 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s is not null) - { - return Number.TryParseInt128(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; - } - else + if (s is null) { - result = default; + result = 0; return false; } + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Int128 result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseInt128(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index fd23d3d3fb67e8..033fbb6cffbc50 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -153,7 +153,7 @@ public static short Parse(ReadOnlySpan s, NumberStyles style = NumberStyle private static short Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) { - Number.ParsingStatus status = Number.TryParseInt32(s, style, info, out int i); + Number.ParsingStatus status = Number.TryParseBinaryInteger(s, style, info, out int i); if (status != Number.ParsingStatus.OK) { Number.ThrowOverflowOrFormatException(status, s, TypeCode.Int16); @@ -187,33 +187,18 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out short result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); - } - - private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out short result) - { - // 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; - return true; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index 5d6d9370ecfc09..146921bdaee529 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -194,19 +194,18 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out int result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseInt32(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index 8a268798755cf4..4dbbf5990ef234 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -181,19 +181,18 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out long result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseInt64(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index 0b90364263087a..04ccfa952bd7bc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -128,7 +128,7 @@ private static unsafe bool TryNumberBufferToBinaryInteger(ref NumberBu internal static int ParseInt32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) { - ParsingStatus status = TryParseInt32(value, styles, info, out int result); + ParsingStatus status = TryParseBinaryInteger(value, styles, info, out int result); if (status != ParsingStatus.OK) { ThrowOverflowOrFormatException(status, value, TypeCode.Int32); @@ -139,7 +139,7 @@ internal static int ParseInt32(ReadOnlySpan value, NumberStyles styles, Nu internal static long ParseInt64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) { - ParsingStatus status = TryParseInt64(value, styles, info, out long result); + ParsingStatus status = TryParseBinaryInteger(value, styles, info, out long result); if (status != ParsingStatus.OK) { ThrowOverflowOrFormatException(status, value, TypeCode.Int64); @@ -150,7 +150,7 @@ internal static long ParseInt64(ReadOnlySpan value, NumberStyles styles, N internal static Int128 ParseInt128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) { - ParsingStatus status = TryParseInt128(value, styles, info, out Int128 result); + ParsingStatus status = TryParseBinaryInteger(value, styles, info, out Int128 result); if (status != ParsingStatus.OK) { ThrowOverflowOrFormatExceptionInt128(status); @@ -161,7 +161,7 @@ internal static Int128 ParseInt128(ReadOnlySpan value, NumberStyles styles internal static uint ParseUInt32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) { - ParsingStatus status = TryParseUInt32(value, styles, info, out uint result); + ParsingStatus status = TryParseBinaryInteger(value, styles, info, out uint result); if (status != ParsingStatus.OK) { ThrowOverflowOrFormatException(status, value, TypeCode.UInt32); @@ -172,7 +172,7 @@ internal static uint ParseUInt32(ReadOnlySpan value, NumberStyles styles, internal static ulong ParseUInt64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) { - ParsingStatus status = TryParseUInt64(value, styles, info, out ulong result); + ParsingStatus status = TryParseBinaryInteger(value, styles, info, out ulong result); if (status != ParsingStatus.OK) { ThrowOverflowOrFormatException(status, value, TypeCode.UInt64); @@ -183,7 +183,7 @@ internal static ulong ParseUInt64(ReadOnlySpan value, NumberStyles styles, internal static UInt128 ParseUInt128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) { - ParsingStatus status = TryParseUInt128(value, styles, info, out UInt128 result); + ParsingStatus status = TryParseBinaryInteger(value, styles, info, out UInt128 result); if (status != ParsingStatus.OK) { ThrowOverflowOrFormatExceptionUInt128(status); @@ -452,7 +452,8 @@ private static unsafe bool TryParseNumber(scoped ref char* str, char* strEnd, Nu } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseInt32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out int result) + internal static ParsingStatus TryParseBinaryInteger(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out TInteger result) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { if ((styles & ~NumberStyles.Integer) == 0) { @@ -462,8 +463,7 @@ internal static ParsingStatus TryParseInt32(ReadOnlySpan value, NumberStyl if ((styles & NumberStyles.AllowHexSpecifier) != 0) { - result = 0; - return TryParseBinaryIntegerHexNumberStyle(value, styles, out Unsafe.As(ref result)); + return TryParseBinaryIntegerHexNumberStyle(value, styles, out result); } return TryParseBinaryIntegerNumber(value, styles, info, out result); @@ -704,60 +704,6 @@ internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOnlySpan< goto DoneAtEndButPotentialOverflow; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseInt64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out long result) - { - if ((styles & ~NumberStyles.Integer) == 0) - { - // Optimized path for the common case of anything that's allowed for integer style. - return TryParseBinaryIntegerStyle(value, styles, info, out result); - } - - if ((styles & NumberStyles.AllowHexSpecifier) != 0) - { - result = 0; - return TryParseBinaryIntegerHexNumberStyle(value, styles, out Unsafe.As(ref result)); - } - - return TryParseBinaryIntegerNumber(value, styles, info, out result); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseInt128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out Int128 result) - { - if ((styles & ~NumberStyles.Integer) == 0) - { - // Optimized path for the common case of anything that's allowed for integer style. - return TryParseBinaryIntegerStyle(value, styles, info, out result); - } - - if ((styles & NumberStyles.AllowHexSpecifier) != 0) - { - ParsingStatus status = TryParseBinaryIntegerHexNumberStyle(value, styles, out UInt128 unsignedResult); - result = new Int128(unsignedResult.Upper, unsignedResult.Lower); - return status; - } - - return TryParseBinaryIntegerNumber(value, styles, info, out result); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseUInt32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out uint result) - { - if ((styles & ~NumberStyles.Integer) == 0) - { - // Optimized path for the common case of anything that's allowed for integer style. - return TryParseBinaryIntegerStyle(value, styles, info, out result); - } - - if ((styles & NumberStyles.AllowHexSpecifier) != 0) - { - return TryParseBinaryIntegerHexNumberStyle(value, styles, out result); - } - - return TryParseBinaryIntegerNumber(value, styles, info, out result); - } - /// Parses uint limited to styles that make up NumberStyles.HexNumber. internal static ParsingStatus TryParseBinaryIntegerHexNumberStyle(ReadOnlySpan value, NumberStyles styles, out TInteger result) where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo @@ -881,40 +827,6 @@ internal static ParsingStatus TryParseBinaryIntegerHexNumberStyle(Read goto DoneAtEndButPotentialOverflow; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseUInt64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out ulong result) - { - if ((styles & ~NumberStyles.Integer) == 0) - { - // Optimized path for the common case of anything that's allowed for integer style. - return TryParseBinaryIntegerStyle(value, styles, info, out result); - } - - if ((styles & NumberStyles.AllowHexSpecifier) != 0) - { - return TryParseBinaryIntegerHexNumberStyle(value, styles, out result); - } - - return TryParseBinaryIntegerNumber(value, styles, info, out result); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseUInt128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out UInt128 result) - { - if ((styles & ~NumberStyles.Integer) == 0) - { - // Optimized path for the common case of anything that's allowed for integer style. - return TryParseBinaryIntegerStyle(value, styles, info, out result); - } - - if ((styles & NumberStyles.AllowHexSpecifier) != 0) - { - return TryParseBinaryIntegerHexNumberStyle(value, styles, out result); - } - - return TryParseBinaryIntegerNumber(value, styles, info, out result); - } - internal static decimal ParseDecimal(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) { ParsingStatus status = TryParseDecimal(value, styles, info, out decimal result); diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index 3f3ae6f83ce8ef..305b41acfad059 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -160,7 +160,7 @@ public static sbyte Parse(ReadOnlySpan s, NumberStyles style = NumberStyle private static sbyte Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) { - Number.ParsingStatus status = Number.TryParseInt32(s, style, info, out int i); + Number.ParsingStatus status = Number.TryParseBinaryInteger(s, style, info, out int i); if (status != Number.ParsingStatus.OK) { Number.ThrowOverflowOrFormatException(status, s, TypeCode.SByte); @@ -194,33 +194,18 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out sbyte result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); - } - - private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out sbyte result) - { - // 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; - return true; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index 7068755b8f0cab..34711babf6ead2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -171,21 +171,18 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s is not null) - { - return Number.TryParseUInt128(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; - } - else + if (s is null) { - result = default; + result = 0; return false; } + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out UInt128 result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseUInt128(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs index 21f2032d6688be..46f9047ecdcfba 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -148,7 +148,7 @@ public static ushort Parse(ReadOnlySpan s, NumberStyles style = NumberStyl private static ushort Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) { - Number.ParsingStatus status = Number.TryParseUInt32(s, style, info, out uint i); + Number.ParsingStatus status = Number.TryParseBinaryInteger(s, style, info, out uint i); if (status != Number.ParsingStatus.OK) { Number.ThrowOverflowOrFormatException(status, s, TypeCode.UInt16); @@ -177,31 +177,18 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return TryParse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out ushort result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return TryParse(s, style, NumberFormatInfo.GetInstance(provider), out result); - } - - private static bool TryParse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info, out ushort result) - { - if (Number.TryParseUInt32(s, style, info, out uint i) != Number.ParsingStatus.OK - || i > MaxValue) - { - result = 0; - return false; - } - result = (ushort)i; - return true; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index a850e7b339d373..a90eceb20e1830 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -175,19 +175,18 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out uint result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseUInt32(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index 24f0c3495d3711..7ece43c3f64718 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -174,19 +174,18 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I { NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) + if (s is null) { result = 0; return false; } - - return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out ulong result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseUInt64(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } // From dc81f3bd37df4ce43d975b86c789885f3450525d Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 10 Apr 2023 11:37:21 -0700 Subject: [PATCH 06/11] Deduplicate ParseInt* --- .../src/System/Int128.cs | 10 +-- .../src/System/Int32.cs | 10 +-- .../src/System/Int64.cs | 10 +-- .../src/System/Number.Parsing.cs | 62 ++----------------- .../src/System/UInt128.cs | 10 +-- .../src/System/UInt32.cs | 10 +-- .../src/System/UInt64.cs | 10 +-- 7 files changed, 34 insertions(+), 88 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index 157aee49c184a6..2052114393ad34 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -122,33 +122,33 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta public static Int128 Parse(string s) { ArgumentNullException.ThrowIfNull(s); - return Number.ParseInt128(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static Int128 Parse(string s, NumberStyles style) { ArgumentNullException.ThrowIfNull(s); NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseInt128(s, style, NumberFormatInfo.CurrentInfo); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.CurrentInfo); } public static Int128 Parse(string s, IFormatProvider? provider) { ArgumentNullException.ThrowIfNull(s); - return Number.ParseInt128(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } public static Int128 Parse(string s, NumberStyles style, IFormatProvider? provider) { ArgumentNullException.ThrowIfNull(s); NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseInt128(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } public static Int128 Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseInt128(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } public static bool TryParse([NotNullWhen(true)] string? s, out Int128 result) diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index 146921bdaee529..653852c1475e88 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -132,14 +132,14 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta public static int Parse(string s) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static int Parse(string s, NumberStyles style) { NumberFormatInfo.ValidateParseStyleInteger(style); if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt32(s, style, NumberFormatInfo.CurrentInfo); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.CurrentInfo); } // Parses an integer from a String in the given style. If @@ -149,7 +149,7 @@ public static int Parse(string s, NumberStyles style) public static int Parse(string s, IFormatProvider? provider) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt32(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } // Parses an integer from a String in the given style. If @@ -160,13 +160,13 @@ public static int Parse(string s, NumberStyles style, IFormatProvider? provider) { NumberFormatInfo.ValidateParseStyleInteger(style); if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt32(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } public static int Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseInt32(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } // Parses an integer from a String. Returns false rather diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index 4dbbf5990ef234..6f0f03e902a5a3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -129,20 +129,20 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta public static long Parse(string s) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static long Parse(string s, NumberStyles style) { NumberFormatInfo.ValidateParseStyleInteger(style); if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt64(s, style, NumberFormatInfo.CurrentInfo); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.CurrentInfo); } public static long Parse(string s, IFormatProvider? provider) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt64(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } // Parses a long from a String in the given style. If @@ -153,13 +153,13 @@ public static long Parse(string s, NumberStyles style, IFormatProvider? provider { NumberFormatInfo.ValidateParseStyleInteger(style); if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseInt64(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } public static long Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseInt64(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } public static bool TryParse([NotNullWhen(true)] string? s, out long result) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index 04ccfa952bd7bc..99707953f0b54b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -126,69 +126,15 @@ private static unsafe bool TryNumberBufferToBinaryInteger(ref NumberBu return true; } - internal static int ParseInt32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) - { - ParsingStatus status = TryParseBinaryInteger(value, styles, info, out int result); - if (status != ParsingStatus.OK) - { - ThrowOverflowOrFormatException(status, value, TypeCode.Int32); - } - - return result; - } - - internal static long ParseInt64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) - { - ParsingStatus status = TryParseBinaryInteger(value, styles, info, out long result); - if (status != ParsingStatus.OK) - { - ThrowOverflowOrFormatException(status, value, TypeCode.Int64); - } - - return result; - } - - internal static Int128 ParseInt128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) - { - ParsingStatus status = TryParseBinaryInteger(value, styles, info, out Int128 result); - if (status != ParsingStatus.OK) - { - ThrowOverflowOrFormatExceptionInt128(status); - } - - return result; - } - - internal static uint ParseUInt32(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) - { - ParsingStatus status = TryParseBinaryInteger(value, styles, info, out uint result); - if (status != ParsingStatus.OK) - { - ThrowOverflowOrFormatException(status, value, TypeCode.UInt32); - } - - return result; - } - - internal static ulong ParseUInt64(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) + internal static TInteger ParseBinaryInteger(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { - ParsingStatus status = TryParseBinaryInteger(value, styles, info, out ulong result); - if (status != ParsingStatus.OK) - { - ThrowOverflowOrFormatException(status, value, TypeCode.UInt64); - } - - return result; - } + ParsingStatus status = TryParseBinaryInteger(value, styles, info, out TInteger result); - internal static UInt128 ParseUInt128(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) - { - ParsingStatus status = TryParseBinaryInteger(value, styles, info, out UInt128 result); if (status != ParsingStatus.OK) { - ThrowOverflowOrFormatExceptionUInt128(status); + ThrowOverflowOrFormatException(status, value, TypeCode.Int32); } - return result; } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index 34711babf6ead2..28d24d70193624 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -123,33 +123,33 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta public static UInt128 Parse(string s) { ArgumentNullException.ThrowIfNull(s); - return Number.ParseUInt128(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static UInt128 Parse(string s, NumberStyles style) { ArgumentNullException.ThrowIfNull(s); NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseUInt128(s, style, NumberFormatInfo.CurrentInfo); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.CurrentInfo); } public static UInt128 Parse(string s, IFormatProvider? provider) { ArgumentNullException.ThrowIfNull(s); - return Number.ParseUInt128(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } public static UInt128 Parse(string s, NumberStyles style, IFormatProvider? provider) { ArgumentNullException.ThrowIfNull(s); NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseUInt128(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } public static UInt128 Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseUInt128(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } public static bool TryParse([NotNullWhen(true)] string? s, out UInt128 result) diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index a90eceb20e1830..cedacb8ad59985 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -127,33 +127,33 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta public static uint Parse(string s) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static uint Parse(string s, NumberStyles style) { NumberFormatInfo.ValidateParseStyleInteger(style); if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt32(s, style, NumberFormatInfo.CurrentInfo); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.CurrentInfo); } public static uint Parse(string s, IFormatProvider? provider) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt32(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } public static uint Parse(string s, NumberStyles style, IFormatProvider? provider) { NumberFormatInfo.ValidateParseStyleInteger(style); if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt32(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } public static uint Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseUInt32(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } public static bool TryParse([NotNullWhen(true)] string? s, out uint result) diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index 7ece43c3f64718..24a8ecea9f2d31 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -126,33 +126,33 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta public static ulong Parse(string s) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); + return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); } public static ulong Parse(string s, NumberStyles style) { NumberFormatInfo.ValidateParseStyleInteger(style); if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt64(s, style, NumberFormatInfo.CurrentInfo); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.CurrentInfo); } public static ulong Parse(string s, IFormatProvider? provider) { if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt64(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); } public static ulong Parse(string s, NumberStyles style, IFormatProvider? provider) { NumberFormatInfo.ValidateParseStyleInteger(style); if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseUInt64(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } public static ulong Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseUInt64(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } public static bool TryParse([NotNullWhen(true)] string? s, out ulong result) From ad26bd75e4f8d93f44ae318872f0bfdd10cf1296 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 10 Apr 2023 12:15:15 -0700 Subject: [PATCH 07/11] Deduplicate some more binary integer parsing logic --- .../System.Private.CoreLib/src/System/Byte.cs | 60 +++-------------- .../System.Private.CoreLib/src/System/Char.cs | 64 ++++++------------ .../System/Globalization/NumberFormatInfo.cs | 3 + .../src/System/Int128.cs | 41 ++---------- .../src/System/Int16.cs | 62 +++-------------- .../src/System/Int32.cs | 57 +++------------- .../src/System/Int64.cs | 47 +++---------- .../src/System/SByte.cs | 66 +++---------------- .../src/System/ThrowHelper.cs | 6 ++ .../src/System/UInt128.cs | 41 ++---------- .../src/System/UInt16.cs | 57 +++------------- .../src/System/UInt32.cs | 43 +++--------- .../src/System/UInt64.cs | 43 +++--------- 13 files changed, 112 insertions(+), 478 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index 16fabdc5cc3ccb..f88b28b8084cb0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -92,78 +92,36 @@ public override int GetHashCode() return m_value; } - public static byte Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static byte Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static byte Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); - } + public static byte Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static byte Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static byte Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); - // Parses an unsigned byte from a String in the given style. If - // a NumberFormatInfo isn't specified, the current culture's - // NumberFormatInfo is assumed. public static byte Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + ArgumentNullException.ThrowIfNull(s); + return Parse(s.AsSpan(), style, provider); } public static byte Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Parse(s, style, NumberFormatInfo.GetInstance(provider)); - } - - private static byte Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) - { - Number.ParsingStatus status = Number.TryParseBinaryInteger(s, style, info, out uint i); - if (status != Number.ParsingStatus.OK) - { - Number.ThrowOverflowOrFormatException(status, s, TypeCode.Byte); - } - - if (i > MaxValue) Number.ThrowOverflowException(TypeCode.Byte); - return (byte)i; + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse([NotNullWhen(true)] string? s, out byte result) - { - if (s is null) - { - result = 0; - return false; - } - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse([NotNullWhen(true)] string? s, out byte result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - public static bool TryParse(ReadOnlySpan s, out byte result) - { - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out byte result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out byte result) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s is null) { result = 0; return false; } - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return TryParse(s.AsSpan(), style, provider, out result); } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out byte result) diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index 57c43b94f976ae..a8e808c000170e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -200,21 +200,32 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri public static char Parse(string s) { - if (s == null) - { - ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - } + ArgumentNullException.ThrowIfNull(s); + return Parse(s.AsSpan()); + } + internal static char Parse(ReadOnlySpan s) + { if (s.Length != 1) { - throw new FormatException(SR.Format_NeedSingleChar); + ThrowHelper.ThrowFormatException_NeedSingleChar(); } return s[0]; } public static bool TryParse([NotNullWhen(true)] string? s, out char result) { - if ((s is null) || (s.Length != 1)) + if (s is null) + { + result = '\0'; + return false; + } + return TryParse(s.AsSpan(), out result); + } + + internal static bool TryParse(ReadOnlySpan s, out char result) + { + if (s.Length != 1) { result = '\0'; return false; @@ -1505,14 +1516,7 @@ bool IBinaryInteger.TryWriteLittleEndian(Span destination, out int b static char INumberBase.Parse(string s, NumberStyles style, IFormatProvider? provider) => Parse(s); - static char INumberBase.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) - { - if (s.Length != 1) - { - throw new FormatException(SR.Format_NeedSingleChar); - } - return s[0]; - } + static char INumberBase.Parse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider) => Parse(s); /// [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -1926,17 +1930,7 @@ static bool INumberBase.TryConvertToTruncating(char value, [MaybeN static bool INumberBase.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out char result) => TryParse(s, out result); - static bool INumberBase.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out char result) - { - if (s.Length != 1) - { - result = '\0'; - return false; - } - - result = s[0]; - return true; - } + static bool INumberBase.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out char result) => TryParse(s, out result); // // IParsable @@ -1963,25 +1957,9 @@ static bool INumberBase.TryParse(ReadOnlySpan s, NumberStyles style, // ISpanParsable // - static char ISpanParsable.Parse(ReadOnlySpan s, IFormatProvider? provider) - { - if (s.Length != 1) - { - throw new FormatException(SR.Format_NeedSingleChar); - } - return s[0]; - } + static char ISpanParsable.Parse(ReadOnlySpan s, IFormatProvider? provider) => Parse(s); - static bool ISpanParsable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out char result) - { - if (s.Length != 1) - { - result = default; - return false; - } - result = s[0]; - return true; - } + static bool ISpanParsable.TryParse(ReadOnlySpan s, IFormatProvider? provider, out char result) => TryParse(s, out result); // // ISubtractionOperators diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs index 3d2c60fd01550b..da7ad538dea604 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs @@ -1,6 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Runtime.CompilerServices; + namespace System.Globalization { /// @@ -667,6 +669,7 @@ public static NumberFormatInfo ReadOnly(NumberFormatInfo nfi) | NumberStyles.AllowThousands | NumberStyles.AllowExponent | NumberStyles.AllowCurrencySymbol | NumberStyles.AllowHexSpecifier); + [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void ValidateParseStyleInteger(NumberStyles style) { // Check for undefined flags or invalid hex number flags diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index 2052114393ad34..7d8e8e5552fd95 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -119,30 +119,16 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatInt128(this, format, provider, destination, out charsWritten); } - public static Int128 Parse(string s) - { - ArgumentNullException.ThrowIfNull(s); - return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static Int128 Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static Int128 Parse(string s, NumberStyles style) - { - ArgumentNullException.ThrowIfNull(s); - NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseBinaryInteger(s, style, NumberFormatInfo.CurrentInfo); - } + public static Int128 Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static Int128 Parse(string s, IFormatProvider? provider) - { - ArgumentNullException.ThrowIfNull(s); - return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static Int128 Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); public static Int128 Parse(string s, NumberStyles style, IFormatProvider? provider) { ArgumentNullException.ThrowIfNull(s); - NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); + return Parse(s.AsSpan(), style, provider); } public static Int128 Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) @@ -151,31 +137,18 @@ public static Int128 Parse(ReadOnlySpan s, NumberStyles style = NumberStyl return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse([NotNullWhen(true)] string? s, out Int128 result) - { - if (s is null) - { - result = 0; - return false; - } - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse([NotNullWhen(true)] string? s, out Int128 result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - public static bool TryParse(ReadOnlySpan s, out Int128 result) - { - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out Int128 result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out Int128 result) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s is null) { result = 0; return false; } - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return TryParse(s.AsSpan(), style, provider, out result); } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Int128 result) diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index 033fbb6cffbc50..7760e4d3842593 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -119,80 +119,36 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatInt32(m_value, 0x0000FFFF, format, provider, destination, out charsWritten); } - public static short Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static short Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static short Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); - } + public static short Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static short Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static short Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); public static short Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + ArgumentNullException.ThrowIfNull(s); + return Parse(s.AsSpan(), style, provider); } public static short Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Parse(s, style, NumberFormatInfo.GetInstance(provider)); - } - - private static short Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) - { - Number.ParsingStatus status = Number.TryParseBinaryInteger(s, style, info, out int i); - if (status != Number.ParsingStatus.OK) - { - Number.ThrowOverflowOrFormatException(status, s, TypeCode.Int16); - } - - // 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); - } - return (short)i; + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse([NotNullWhen(true)] string? s, out short result) - { - if (s is null) - { - result = 0; - return false; - } - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse([NotNullWhen(true)] string? s, out short result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - public static bool TryParse(ReadOnlySpan s, out short result) - { - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out short result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out short result) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s is null) { result = 0; return false; } - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return TryParse(s.AsSpan(), style, provider, out result); } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out short result) diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index 653852c1475e88..0edb1186d295c3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -129,38 +129,16 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatInt32(m_value, ~0, format, provider, destination, out charsWritten); } - public static int Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static int Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static int Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, style, NumberFormatInfo.CurrentInfo); - } + public static int Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - // Parses an integer from a String in the given style. If - // a NumberFormatInfo isn't specified, the current culture's - // NumberFormatInfo is assumed. - // - public static int Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static int Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); - // Parses an integer from a String in the given style. If - // a NumberFormatInfo isn't specified, the current culture's - // NumberFormatInfo is assumed. - // public static int Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); + ArgumentNullException.ThrowIfNull(s); + return Parse(s.AsSpan(), style, provider); } public static int Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) @@ -169,37 +147,18 @@ public static int Parse(ReadOnlySpan s, NumberStyles style = NumberStyles. return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - // Parses an integer from a String. Returns false rather - // than throwing an exception if input is invalid. - // - public static bool TryParse([NotNullWhen(true)] string? s, out int result) - { - if (s is null) - { - result = 0; - return false; - } - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse([NotNullWhen(true)] string? s, out int result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - public static bool TryParse(ReadOnlySpan s, out int result) - { - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out int result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - // Parses an integer from a String in the given style. Returns false rather - // than throwing an exception if input is invalid. - // public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out int result) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s is null) { result = 0; return false; } - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return TryParse(s.AsSpan(), style, provider, out result); } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out int result) diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index 6f0f03e902a5a3..eff8751dda195f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -126,34 +126,16 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatInt64(m_value, format, provider, destination, out charsWritten); } - public static long Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static long Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static long Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, style, NumberFormatInfo.CurrentInfo); - } + public static long Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static long Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static long Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); - // Parses a long from a String in the given style. If - // a NumberFormatInfo isn't specified, the current culture's - // NumberFormatInfo is assumed. - // public static long Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); + ArgumentNullException.ThrowIfNull(s); + return Parse(s.AsSpan(), style, provider); } public static long Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) @@ -162,31 +144,18 @@ public static long Parse(ReadOnlySpan s, NumberStyles style = NumberStyles return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse([NotNullWhen(true)] string? s, out long result) - { - if (s is null) - { - result = 0; - return false; - } - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse([NotNullWhen(true)] string? s, out long result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - public static bool TryParse(ReadOnlySpan s, out long result) - { - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out long result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out long result) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s is null) { result = 0; return false; } - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return TryParse(s.AsSpan(), style, provider, out result); } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out long result) diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index 305b41acfad059..a8db3725ae6c09 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -122,84 +122,36 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatInt32(m_value, 0x000000FF, format, provider, destination, out charsWritten); } - public static sbyte Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static sbyte Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static sbyte Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); - } + public static sbyte Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static sbyte Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static sbyte Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); - // Parses a signed byte from a String in the given style. If - // a NumberFormatInfo isn't specified, the current culture's - // NumberFormatInfo is assumed. - // public static sbyte Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + ArgumentNullException.ThrowIfNull(s); + return Parse(s.AsSpan(), style, provider); } public static sbyte Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Parse(s, style, NumberFormatInfo.GetInstance(provider)); + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - private static sbyte Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) - { - Number.ParsingStatus status = Number.TryParseBinaryInteger(s, style, info, out int i); - if (status != Number.ParsingStatus.OK) - { - Number.ThrowOverflowOrFormatException(status, s, TypeCode.SByte); - } + public static bool TryParse([NotNullWhen(true)] string? s, out sbyte result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - // 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); - } - return (sbyte)i; - } - - public static bool TryParse([NotNullWhen(true)] string? s, out sbyte result) - { - if (s is null) - { - result = 0; - return false; - } - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } - - public static bool TryParse(ReadOnlySpan s, out sbyte result) - { - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out sbyte result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out sbyte result) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s is null) { result = 0; return false; } - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return TryParse(s.AsSpan(), style, provider, out result); } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out sbyte result) diff --git a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs index e8b9232e489018..142b94d84a5459 100644 --- a/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs +++ b/src/libraries/System.Private.CoreLib/src/System/ThrowHelper.cs @@ -521,6 +521,12 @@ internal static void ThrowFormatException_BadFormatSpecifier() throw new FormatException(SR.Argument_BadFormatSpecifier); } + [DoesNotReturn] + internal static void ThrowFormatException_NeedSingleChar() + { + throw new FormatException(SR.Format_NeedSingleChar); + } + [DoesNotReturn] internal static void ThrowArgumentOutOfRangeException_PrecisionTooLarge() { diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index 28d24d70193624..65afd1e4da10c1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -120,30 +120,16 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatUInt128(this, format, provider, destination, out charsWritten); } - public static UInt128 Parse(string s) - { - ArgumentNullException.ThrowIfNull(s); - return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static UInt128 Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static UInt128 Parse(string s, NumberStyles style) - { - ArgumentNullException.ThrowIfNull(s); - NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseBinaryInteger(s, style, NumberFormatInfo.CurrentInfo); - } + public static UInt128 Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static UInt128 Parse(string s, IFormatProvider? provider) - { - ArgumentNullException.ThrowIfNull(s); - return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static UInt128 Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); public static UInt128 Parse(string s, NumberStyles style, IFormatProvider? provider) { ArgumentNullException.ThrowIfNull(s); - NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); + return Parse(s.AsSpan(), style, provider); } public static UInt128 Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) @@ -152,31 +138,18 @@ public static UInt128 Parse(ReadOnlySpan s, NumberStyles style = NumberSty return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse([NotNullWhen(true)] string? s, out UInt128 result) - { - if (s is null) - { - result = 0; - return false; - } - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse([NotNullWhen(true)] string? s, out UInt128 result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - public static bool TryParse(ReadOnlySpan s, out UInt128 result) - { - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out UInt128 result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out UInt128 result) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s is null) { result = 0; return false; } - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return TryParse(s.AsSpan(), style, provider, out result); } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out UInt128 result) diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs index 46f9047ecdcfba..529a0a4847601b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -114,75 +114,36 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatUInt32(m_value, format, provider, destination, out charsWritten); } - public static ushort Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static ushort Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static ushort Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.CurrentInfo); - } + public static ushort Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static ushort Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static ushort Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); public static ushort Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Parse((ReadOnlySpan)s, style, NumberFormatInfo.GetInstance(provider)); + ArgumentNullException.ThrowIfNull(s); + return Parse(s.AsSpan(), style, provider); } public static ushort Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Parse(s, style, NumberFormatInfo.GetInstance(provider)); - } - - private static ushort Parse(ReadOnlySpan s, NumberStyles style, NumberFormatInfo info) - { - Number.ParsingStatus status = Number.TryParseBinaryInteger(s, style, info, out uint i); - if (status != Number.ParsingStatus.OK) - { - Number.ThrowOverflowOrFormatException(status, s, TypeCode.UInt16); - } - - if (i > MaxValue) Number.ThrowOverflowException(TypeCode.UInt16); - return (ushort)i; + return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse([NotNullWhen(true)] string? s, out ushort result) - { - if (s is null) - { - result = 0; - return false; - } - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse([NotNullWhen(true)] string? s, out ushort result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - public static bool TryParse(ReadOnlySpan s, out ushort result) - { - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out ushort result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out ushort result) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s is null) { result = 0; return false; } - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return TryParse(s.AsSpan(), style, provider, out result); } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out ushort result) diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index cedacb8ad59985..7d7352e760253c 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -124,30 +124,16 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatUInt32(m_value, format, provider, destination, out charsWritten); } - public static uint Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static uint Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static uint Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, style, NumberFormatInfo.CurrentInfo); - } + public static uint Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static uint Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static uint Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); public static uint Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); + ArgumentNullException.ThrowIfNull(s); + return Parse(s.AsSpan(), style, provider); } public static uint Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) @@ -156,31 +142,18 @@ public static uint Parse(ReadOnlySpan s, NumberStyles style = NumberStyles return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse([NotNullWhen(true)] string? s, out uint result) - { - if (s is null) - { - result = 0; - return false; - } - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse([NotNullWhen(true)] string? s, out uint result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - public static bool TryParse(ReadOnlySpan s, out uint result) - { - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out uint result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out uint result) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s is null) { result = 0; return false; } - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return TryParse(s.AsSpan(), style, provider, out result); } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out uint result) diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index 24a8ecea9f2d31..1c33f6ad59479b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -123,30 +123,16 @@ public bool TryFormat(Span destination, out int charsWritten, [StringSynta return Number.TryFormatUInt64(m_value, format, provider, destination, out charsWritten); } - public static ulong Parse(string s) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo); - } + public static ulong Parse(string s) => Parse(s, NumberStyles.Integer, provider: null); - public static ulong Parse(string s, NumberStyles style) - { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, style, NumberFormatInfo.CurrentInfo); - } + public static ulong Parse(string s, NumberStyles style) => Parse(s, style, provider: null); - public static ulong Parse(string s, IFormatProvider? provider) - { - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); - } + public static ulong Parse(string s, IFormatProvider? provider) => Parse(s, NumberStyles.Integer, provider); public static ulong Parse(string s, NumberStyles style, IFormatProvider? provider) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s == null) ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); - return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); + ArgumentNullException.ThrowIfNull(s); + return Parse(s.AsSpan(), style, provider); } public static ulong Parse(ReadOnlySpan s, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) @@ -155,31 +141,18 @@ public static ulong Parse(ReadOnlySpan s, NumberStyles style = NumberStyle return Number.ParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider)); } - public static bool TryParse([NotNullWhen(true)] string? s, out ulong result) - { - if (s is null) - { - result = 0; - return false; - } - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse([NotNullWhen(true)] string? s, out ulong result) => TryParse(s, NumberStyles.Integer, provider: null, out result); - public static bool TryParse(ReadOnlySpan s, out ulong result) - { - return Number.TryParseBinaryIntegerStyle(s, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result) == Number.ParsingStatus.OK; - } + public static bool TryParse(ReadOnlySpan s, out ulong result) => TryParse(s, NumberStyles.Integer, provider: null, out result); public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out ulong result) { - NumberFormatInfo.ValidateParseStyleInteger(style); - if (s is null) { result = 0; return false; } - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return TryParse(s.AsSpan(), style, provider, out result); } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out ulong result) From 23ace39c7397b8b7f5aad0efc89934bf087307b2 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 10 Apr 2023 13:54:40 -0700 Subject: [PATCH 08/11] Ensure the right overflow message is used for binary integer parsing --- .../System.Private.CoreLib/src/System/Byte.cs | 2 ++ .../System.Private.CoreLib/src/System/Char.cs | 2 ++ .../src/System/Int128.cs | 2 ++ .../src/System/Int16.cs | 2 ++ .../src/System/Int32.cs | 2 ++ .../src/System/Int64.cs | 2 ++ .../src/System/Number.Parsing.cs | 20 ++++++++++++++++++- .../src/System/SByte.cs | 2 ++ .../src/System/UInt128.cs | 2 ++ .../src/System/UInt16.cs | 2 ++ .../src/System/UInt32.cs | 2 ++ .../src/System/UInt64.cs | 2 ++ 12 files changed, 41 insertions(+), 1 deletion(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index f88b28b8084cb0..cff9b24652ad20 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -1156,6 +1156,8 @@ static bool INumberBase.TryConvertToTruncating(byte value, [MaybeN static byte IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_Byte; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(byte left, byte right) => left > right; static byte IBinaryIntegerParseAndFormatInfo.MultiplyBy10(byte value) => (byte)(value * 10); diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index a8e808c000170e..0b80bfabf26bb0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -2002,6 +2002,8 @@ static bool INumberBase.TryConvertToTruncating(char value, [MaybeN static char IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => (char)(MaxValue / 10); + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_Char; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(char left, char right) => left > right; static char IBinaryIntegerParseAndFormatInfo.MultiplyBy10(char value) => (char)(value * 10); diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index 7d8e8e5552fd95..4a4a9f60eb8f6b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -2175,6 +2175,8 @@ static bool INumberBase.TryConvertToTruncating(Int128 value, [Ma static Int128 IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => new Int128(0x0CCC_CCCC_CCCC_CCCC, 0xCCCC_CCCC_CCCC_CCCC); + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_Int128; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(Int128 left, Int128 right) => (UInt128)(left) > (UInt128)(right); static Int128 IBinaryIntegerParseAndFormatInfo.MultiplyBy10(Int128 value) => value * 10; diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index 7760e4d3842593..2921582233467a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -1370,6 +1370,8 @@ static bool INumberBase.TryConvertToTruncating(short value, [Mayb static short IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_Int16; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(short left, short right) => (ushort)(left) > (ushort)(right); static short IBinaryIntegerParseAndFormatInfo.MultiplyBy10(short value) => (short)(value * 10); diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index 0edb1186d295c3..91d26a72108c2e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -1406,6 +1406,8 @@ static bool INumberBase.TryConvertToTruncating(int value, [MaybeNul static int IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_Int32; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(int left, int right) => (uint)(left) > (uint)(right); static int IBinaryIntegerParseAndFormatInfo.MultiplyBy10(int value) => value * 10; diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index eff8751dda195f..931551f1dab779 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -1409,6 +1409,8 @@ static bool INumberBase.TryConvertToTruncating(long value, [MaybeN static long IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_Int64; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(long left, long right) => (ulong)(left) > (ulong)(right); static long IBinaryIntegerParseAndFormatInfo.MultiplyBy10(long value) => value * 10; diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index 99707953f0b54b..93f20df13ba6a4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -38,6 +38,8 @@ internal interface IBinaryIntegerParseAndFormatInfo : IBinaryInteger(ReadOnlySpan value, if (status != ParsingStatus.OK) { - ThrowOverflowOrFormatException(status, value, TypeCode.Int32); + ThrowOverflowOrFormatException(status, value); } return result; } @@ -1238,6 +1240,13 @@ internal enum ParsingStatus [DoesNotReturn] internal static void ThrowOverflowOrFormatException(ParsingStatus status, ReadOnlySpan value, TypeCode type = 0) => throw GetException(status, value, type); + [DoesNotReturn] + internal static void ThrowOverflowOrFormatException(ParsingStatus status, ReadOnlySpan value) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo + { + throw GetException(status, value); + } + [DoesNotReturn] internal static void ThrowOverflowException(TypeCode type) => throw GetOverflowException(type); @@ -1255,6 +1264,15 @@ private static Exception GetException(ParsingStatus status, ReadOnlySpan v return GetOverflowException(type); } + private static Exception GetException(ParsingStatus status, ReadOnlySpan value) + where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo + { + if (status == ParsingStatus.Failed) + return new FormatException(SR.Format(SR.Format_InvalidStringWithValue, value.ToString())); + + return new OverflowException(TInteger.OverflowMessage); + } + private static OverflowException GetOverflowException(TypeCode type) { string s; diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index a8db3725ae6c09..3a79d7ad08112a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -1331,6 +1331,8 @@ static bool INumberBase.TryConvertToTruncating(sbyte value, [Mayb static sbyte IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_SByte; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(sbyte left, sbyte right) => (byte)(left) > (byte)(right); static sbyte IBinaryIntegerParseAndFormatInfo.MultiplyBy10(sbyte value) => (sbyte)(value * 10); diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index 65afd1e4da10c1..1e9fe2f0ee9a25 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -2138,6 +2138,8 @@ static bool INumberBase.TryConvertToTruncating(UInt128 value, [ static UInt128 IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => new UInt128(0x1999_9999_9999_9999, 0x9999_9999_9999_9999); + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_UInt128; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(UInt128 left, UInt128 right) => left > right; static UInt128 IBinaryIntegerParseAndFormatInfo.MultiplyBy10(UInt128 value) => value * 10; diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs index 529a0a4847601b..efb465b1d62b44 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -1185,6 +1185,8 @@ static bool INumberBase.TryConvertToTruncating(ushort value, [Ma static ushort IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_UInt16; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(ushort left, ushort right) => left > right; static ushort IBinaryIntegerParseAndFormatInfo.MultiplyBy10(ushort value) => (ushort)(value * 10); diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index 7d7352e760253c..df608e7de83d13 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -1224,6 +1224,8 @@ static bool INumberBase.TryConvertToTruncating(uint value, [MaybeN static uint IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_UInt32; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(uint left, uint right) => left > right; static uint IBinaryIntegerParseAndFormatInfo.MultiplyBy10(uint value) => value * 10; diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index 1c33f6ad59479b..c7bfd4cea654f6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -1217,6 +1217,8 @@ static bool INumberBase.TryConvertToTruncating(ulong value, [Mayb static ulong IBinaryIntegerParseAndFormatInfo.MaxValueDiv10 => MaxValue / 10; + static string IBinaryIntegerParseAndFormatInfo.OverflowMessage => SR.Overflow_UInt64; + static bool IBinaryIntegerParseAndFormatInfo.IsGreaterThanAsUnsigned(ulong left, ulong right) => left > right; static ulong IBinaryIntegerParseAndFormatInfo.MultiplyBy10(ulong value) => value * 10; From 0a83ed39821da6cf883845c14826f4696c9d2801 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 10 Apr 2023 14:09:16 -0700 Subject: [PATCH 09/11] Ensure HasTrailingCharsZero handling is in the right spot --- .../src/System/Number.Parsing.cs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index 93f20df13ba6a4..6aed800c311082 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -529,8 +529,15 @@ internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOnlySpan< goto DoneAtEnd; num = value[index]; } while (num == '0'); + if (!IsDigit(num)) + { + if (TInteger.IsUnsigned) + { + overflow = false; + } goto HasTrailingChars; + } } // Parse most digits, up to the potential for overflow, which can't happen until after MaxDigitCount - 1 digits. @@ -547,13 +554,7 @@ internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOnlySpan< } num = value[index]; if (!IsDigit(num)) - { - if (TInteger.IsUnsigned) - { - overflow = false; - } goto HasTrailingChars; - } index++; answer = TInteger.MultiplyBy10(answer); answer += TInteger.CreateTruncating(num - '0'); @@ -611,7 +612,6 @@ internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOnlySpan< DoneAtEnd: if (TInteger.IsUnsigned) { - Debug.Assert(!isNegative); result = answer; } else From 59195d36800c693615541ef8cc05b3bd0e27ace1 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Tue, 11 Apr 2023 13:37:41 -0700 Subject: [PATCH 10/11] Resolving PR feedback --- .../System.Private.CoreLib/src/System/Byte.cs | 7 ++-- .../System.Private.CoreLib/src/System/Char.cs | 2 -- .../System.Private.CoreLib/src/System/Enum.cs | 34 +++++++++---------- .../src/System/Int128.cs | 6 ++-- .../src/System/Int16.cs | 6 ++-- .../src/System/Int32.cs | 6 ++-- .../src/System/Int64.cs | 6 ++-- .../src/System/Number.Parsing.cs | 20 +++++------ .../src/System/SByte.cs | 6 ++-- .../src/System/UInt128.cs | 6 ++-- .../src/System/UInt16.cs | 6 ++-- .../src/System/UInt32.cs | 6 ++-- .../src/System/UInt64.cs | 6 ++-- 13 files changed, 56 insertions(+), 61 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index cff9b24652ad20..f746ba493dafa7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System.Buffers.Binary; using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.Numerics; @@ -116,12 +115,14 @@ public static byte Parse(ReadOnlySpan s, NumberStyles style = NumberStyles public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out byte result) { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s is null) { result = 0; return false; } - return TryParse(s.AsSpan(), style, provider, out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out byte result) @@ -1148,8 +1149,6 @@ static bool INumberBase.TryConvertToTruncating(byte value, [MaybeN static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; - static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => true; - static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 3; // 255 static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 2; // 0xFF diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index 0b80bfabf26bb0..f016d0bf9b8445 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -1994,8 +1994,6 @@ static bool INumberBase.TryConvertToTruncating(char value, [MaybeN static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; - static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => true; - static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 5; // 65_535 static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 4; // 0xFFFF diff --git a/src/libraries/System.Private.CoreLib/src/System/Enum.cs b/src/libraries/System.Private.CoreLib/src/System/Enum.cs index ce8906e7cb22ff..bb7fbaa73fffa3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Enum.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Enum.cs @@ -726,41 +726,41 @@ private static unsafe bool TryParse(Type enumType, ReadOnlySpan value, boo switch (InternalGetCorElementType(rt)) { case CorElementType.ELEMENT_TYPE_I1: - parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(sbyte*)&longScratch); + parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(sbyte*)&longScratch); longScratch = *(sbyte*)&longScratch; break; case CorElementType.ELEMENT_TYPE_U1: - parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(byte*)&longScratch); + parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(byte*)&longScratch); longScratch = *(byte*)&longScratch; break; case CorElementType.ELEMENT_TYPE_I2: - parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(short*)&longScratch); + parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(short*)&longScratch); longScratch = *(short*)&longScratch; break; case CorElementType.ELEMENT_TYPE_U2: - parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(ushort*)&longScratch); + parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(ushort*)&longScratch); longScratch = *(ushort*)&longScratch; break; case CorElementType.ELEMENT_TYPE_I4: - parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(int*)&longScratch); + parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(int*)&longScratch); longScratch = *(int*)&longScratch; break; case CorElementType.ELEMENT_TYPE_U4: - parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(uint*)&longScratch); + parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(uint*)&longScratch); longScratch = *(uint*)&longScratch; break; case CorElementType.ELEMENT_TYPE_I8: - parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out longScratch); + parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out longScratch); break; case CorElementType.ELEMENT_TYPE_U8: - parsed = TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(ulong*)&longScratch); + parsed = TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out *(ulong*)&longScratch); break; default: @@ -892,14 +892,14 @@ private static bool TryParse(ReadOnlySpan value, bool ignoreCase, b RuntimeType rt = (RuntimeType)typeof(TEnum); Type underlyingType = typeof(TEnum).GetEnumUnderlyingType(); - if (underlyingType == typeof(sbyte)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(byte)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(short)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(ushort)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(int)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(uint)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(long)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); - if (underlyingType == typeof(ulong)) return TryParseBinaryIntegerByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(sbyte)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(byte)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(short)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(ushort)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(int)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(uint)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(long)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); + if (underlyingType == typeof(ulong)) return TryParseByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); #if RARE_ENUMS if (underlyingType == typeof(nint)) return TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); if (underlyingType == typeof(nuint)) return TryParseRareTypeByValueOrName(rt, value, ignoreCase, throwOnFailure, out Unsafe.As(ref result)); @@ -912,7 +912,7 @@ private static bool TryParse(ReadOnlySpan value, bool ignoreCase, b } /// Core implementation for all {Try}Parse methods, both generic and non-generic, parsing either by value or by name. - private static unsafe bool TryParseBinaryIntegerByValueOrName( + private static unsafe bool TryParseByValueOrName( RuntimeType enumType, ReadOnlySpan value, bool ignoreCase, bool throwOnFailure, out TUnderlying result) where TUnderlying : unmanaged, IBinaryIntegerParseAndFormatInfo where TStorage : unmanaged, IBinaryIntegerParseAndFormatInfo diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index 4a4a9f60eb8f6b..2b5f16727ce26b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -143,12 +143,14 @@ public static Int128 Parse(ReadOnlySpan s, NumberStyles style = NumberStyl public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out Int128 result) { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s is null) { result = 0; return false; } - return TryParse(s.AsSpan(), style, provider, out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Int128 result) @@ -2167,8 +2169,6 @@ static bool INumberBase.TryConvertToTruncating(Int128 value, [Ma static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true; - static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => false; - static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 39; // 170_141_183_460_469_231_731_687_303_715_884_105_727 static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 32; // 0x7FFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index 2921582233467a..b677eed0c99f39 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -143,12 +143,14 @@ public static short Parse(ReadOnlySpan s, NumberStyles style = NumberStyle public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out short result) { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s is null) { result = 0; return false; } - return TryParse(s.AsSpan(), style, provider, out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out short result) @@ -1362,8 +1364,6 @@ static bool INumberBase.TryConvertToTruncating(short value, [Mayb static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true; - static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => false; - static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 5; // 32_767 static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 4; // 0x7FFF diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index 91d26a72108c2e..6354eb5f8892a8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -153,12 +153,14 @@ public static int Parse(ReadOnlySpan s, NumberStyles style = NumberStyles. public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out int result) { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s is null) { result = 0; return false; } - return TryParse(s.AsSpan(), style, provider, out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out int result) @@ -1398,8 +1400,6 @@ static bool INumberBase.TryConvertToTruncating(int value, [MaybeNul static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true; - static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => false; - static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 10; // 2_147_483_647 static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 8; // 0x7FFF_FFFF diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index 931551f1dab779..3b4a696ca56250 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -150,12 +150,14 @@ public static long Parse(ReadOnlySpan s, NumberStyles style = NumberStyles public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out long result) { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s is null) { result = 0; return false; } - return TryParse(s.AsSpan(), style, provider, out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out long result) @@ -1401,8 +1403,6 @@ static bool INumberBase.TryConvertToTruncating(long value, [MaybeN static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true; - static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => false; - static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 19; // 9_223_372_036_854_775_807 static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 16; // 0x7FFF_FFFF_FFFF_FFFF diff --git a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs index 6aed800c311082..fd89703d9d96f5 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -26,12 +26,10 @@ namespace System // NaNs or Infinities. internal interface IBinaryIntegerParseAndFormatInfo : IBinaryInteger, IMinMaxValue - where TSelf : unmanaged, IBinaryIntegerParseAndFormatInfo + where TSelf : unmanaged, IBinaryIntegerParseAndFormatInfo { static abstract bool IsSigned { get; } - static abstract bool IsUnsigned { get; } - static abstract int MaxDigitCount { get; } static abstract int MaxHexDigitCount { get; } @@ -75,7 +73,7 @@ private static unsafe bool TryNumberBufferToBinaryInteger(ref NumberBu int i = number.Scale; - if ((i > TInteger.MaxDigitCount) || (i < number.DigitsCount) || (TInteger.IsUnsigned && number.IsNegative)) + if ((i > TInteger.MaxDigitCount) || (i < number.DigitsCount) || (!TInteger.IsSigned && number.IsNegative)) { return false; } @@ -98,7 +96,7 @@ private static unsafe bool TryNumberBufferToBinaryInteger(ref NumberBu { TInteger newN = n + TInteger.CreateTruncating(*p++ - '0'); - if (TInteger.IsUnsigned && (newN < n)) + if (!TInteger.IsSigned && (newN < n)) { return false; } @@ -514,7 +512,7 @@ internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOnlySpan< } } - bool overflow = TInteger.IsUnsigned && isNegative; + bool overflow = !TInteger.IsSigned && isNegative; TInteger answer = TInteger.Zero; if (IsDigit(num)) @@ -532,7 +530,7 @@ internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOnlySpan< if (!IsDigit(num)) { - if (TInteger.IsUnsigned) + if (!TInteger.IsSigned) { overflow = false; } @@ -547,7 +545,7 @@ internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOnlySpan< { if ((uint)index >= (uint)value.Length) { - if (TInteger.IsUnsigned) + if (!TInteger.IsSigned) goto DoneAtEndButPotentialOverflow; else goto DoneAtEnd; @@ -562,7 +560,7 @@ internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOnlySpan< if ((uint)index >= (uint)value.Length) { - if (TInteger.IsUnsigned) + if (!TInteger.IsSigned) goto DoneAtEndButPotentialOverflow; else goto DoneAtEnd; @@ -572,7 +570,7 @@ internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOnlySpan< goto HasTrailingChars; index++; // Potential overflow now processing the MaxDigitCount digit. - if (TInteger.IsUnsigned) + if (!TInteger.IsSigned) { overflow |= (answer > TInteger.MaxValueDiv10) || (answer == TInteger.MaxValueDiv10) && (num > '5'); } @@ -610,7 +608,7 @@ internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOnlySpan< goto OverflowExit; } DoneAtEnd: - if (TInteger.IsUnsigned) + if (!TInteger.IsSigned) { result = answer; } diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index 3a79d7ad08112a..5270decab700f3 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -146,12 +146,14 @@ public static sbyte Parse(ReadOnlySpan s, NumberStyles style = NumberStyle public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out sbyte result) { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s is null) { result = 0; return false; } - return TryParse(s.AsSpan(), style, provider, out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out sbyte result) @@ -1323,8 +1325,6 @@ static bool INumberBase.TryConvertToTruncating(sbyte value, [Mayb static bool IBinaryIntegerParseAndFormatInfo.IsSigned => true; - static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => false; - static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 3; // 127 static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 2; // 0x7F diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index 1e9fe2f0ee9a25..6a7251b7298999 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -144,12 +144,14 @@ public static UInt128 Parse(ReadOnlySpan s, NumberStyles style = NumberSty public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out UInt128 result) { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s is null) { result = 0; return false; } - return TryParse(s.AsSpan(), style, provider, out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out UInt128 result) @@ -2130,8 +2132,6 @@ static bool INumberBase.TryConvertToTruncating(UInt128 value, [ static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; - static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => true; - static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 39; // 340_282_366_920_938_463_463_374_607_431_768_211_455 static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 32; // 0xFFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF_FFFF diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs index efb465b1d62b44..23ded073b44610 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -138,12 +138,14 @@ public static ushort Parse(ReadOnlySpan s, NumberStyles style = NumberStyl public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out ushort result) { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s is null) { result = 0; return false; } - return TryParse(s.AsSpan(), style, provider, out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out ushort result) @@ -1177,8 +1179,6 @@ static bool INumberBase.TryConvertToTruncating(ushort value, [Ma static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; - static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => true; - static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 5; // 65_535 static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 4; // 0xFFFF diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index df608e7de83d13..1b16137f5d8f4a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -148,12 +148,14 @@ public static uint Parse(ReadOnlySpan s, NumberStyles style = NumberStyles public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out uint result) { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s is null) { result = 0; return false; } - return TryParse(s.AsSpan(), style, provider, out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out uint result) @@ -1216,8 +1218,6 @@ static bool INumberBase.TryConvertToTruncating(uint value, [MaybeN static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; - static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => true; - static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 10; // 4_294_967_295 static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 8; // 0xFFFF_FFFF diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index c7bfd4cea654f6..5f7cdb7121f0d0 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -147,12 +147,14 @@ public static ulong Parse(ReadOnlySpan s, NumberStyles style = NumberStyle public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out ulong result) { + NumberFormatInfo.ValidateParseStyleInteger(style); + if (s is null) { result = 0; return false; } - return TryParse(s.AsSpan(), style, provider, out result); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out ulong result) @@ -1209,8 +1211,6 @@ static bool INumberBase.TryConvertToTruncating(ulong value, [Mayb static bool IBinaryIntegerParseAndFormatInfo.IsSigned => false; - static bool IBinaryIntegerParseAndFormatInfo.IsUnsigned => true; - static int IBinaryIntegerParseAndFormatInfo.MaxDigitCount => 20; // 18_446_744_073_709_551_615 static int IBinaryIntegerParseAndFormatInfo.MaxHexDigitCount => 16; // 0xFFFF_FFFF_FFFF_FFFF From bdc5ba813cb89fd61a6000c0b1d1032bcbaa2108 Mon Sep 17 00:00:00 2001 From: Tanner Gooding Date: Mon, 17 Apr 2023 07:13:58 -0700 Subject: [PATCH 11/11] Revert using the public throw helpers --- src/libraries/System.Private.CoreLib/src/System/Byte.cs | 2 +- src/libraries/System.Private.CoreLib/src/System/Char.cs | 2 +- src/libraries/System.Private.CoreLib/src/System/Int128.cs | 2 +- src/libraries/System.Private.CoreLib/src/System/Int16.cs | 2 +- src/libraries/System.Private.CoreLib/src/System/Int32.cs | 2 +- src/libraries/System.Private.CoreLib/src/System/Int64.cs | 2 +- src/libraries/System.Private.CoreLib/src/System/SByte.cs | 2 +- src/libraries/System.Private.CoreLib/src/System/UInt128.cs | 2 +- src/libraries/System.Private.CoreLib/src/System/UInt16.cs | 2 +- src/libraries/System.Private.CoreLib/src/System/UInt32.cs | 2 +- src/libraries/System.Private.CoreLib/src/System/UInt64.cs | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index 8f9058acc17f4b..b34610f2a2f41a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -101,7 +101,7 @@ public override int GetHashCode() public static byte Parse(string s, NumberStyles style, IFormatProvider? provider) { - ArgumentNullException.ThrowIfNull(s); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } return Parse(s.AsSpan(), style, provider); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index c89c207e7cf3f7..b83d7d76f7ddbc 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -201,7 +201,7 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri public static char Parse(string s) { - ArgumentNullException.ThrowIfNull(s); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } return Parse(s.AsSpan()); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index 6fe7259e3560ec..3c253f434fa701 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -134,7 +134,7 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri public static Int128 Parse(string s, NumberStyles style, IFormatProvider? provider) { - ArgumentNullException.ThrowIfNull(s); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } return Parse(s.AsSpan(), style, provider); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int16.cs b/src/libraries/System.Private.CoreLib/src/System/Int16.cs index 86a62f6d439664..c6e775daf5c2e4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -134,7 +134,7 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri public static short Parse(string s, NumberStyles style, IFormatProvider? provider) { - ArgumentNullException.ThrowIfNull(s); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } return Parse(s.AsSpan(), style, provider); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int32.cs b/src/libraries/System.Private.CoreLib/src/System/Int32.cs index 566a1ee772a7ee..2349b48793d383 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -144,7 +144,7 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri public static int Parse(string s, NumberStyles style, IFormatProvider? provider) { - ArgumentNullException.ThrowIfNull(s); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } return Parse(s.AsSpan(), style, provider); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Int64.cs b/src/libraries/System.Private.CoreLib/src/System/Int64.cs index 7c5841656465a7..0fdbd57db4d2cd 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -141,7 +141,7 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri public static long Parse(string s, NumberStyles style, IFormatProvider? provider) { - ArgumentNullException.ThrowIfNull(s); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } return Parse(s.AsSpan(), style, provider); } diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index 9360638b7d518d..161c71556f0a05 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -137,7 +137,7 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri public static sbyte Parse(string s, NumberStyles style, IFormatProvider? provider) { - ArgumentNullException.ThrowIfNull(s); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } return Parse(s.AsSpan(), style, provider); } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index eab75c3efdf499..76a4f3ce579dc4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -135,7 +135,7 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri public static UInt128 Parse(string s, NumberStyles style, IFormatProvider? provider) { - ArgumentNullException.ThrowIfNull(s); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } return Parse(s.AsSpan(), style, provider); } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs index db71ae2b9cc9a3..4122d64b496cf7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -129,7 +129,7 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri public static ushort Parse(string s, NumberStyles style, IFormatProvider? provider) { - ArgumentNullException.ThrowIfNull(s); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } return Parse(s.AsSpan(), style, provider); } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs index 374e3b9b10b559..a8b9b3eb1ecda9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -139,7 +139,7 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri public static uint Parse(string s, NumberStyles style, IFormatProvider? provider) { - ArgumentNullException.ThrowIfNull(s); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } return Parse(s.AsSpan(), style, provider); } diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs index 25012034de4f93..847db94ff33d3e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -138,7 +138,7 @@ bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWri public static ulong Parse(string s, NumberStyles style, IFormatProvider? provider) { - ArgumentNullException.ThrowIfNull(s); + if (s is null) { ThrowHelper.ThrowArgumentNullException(ExceptionArgument.s); } return Parse(s.AsSpan(), style, provider); }