diff --git a/src/libraries/Common/src/System/Number.Parsing.Common.cs b/src/libraries/Common/src/System/Number.Parsing.Common.cs index e43cbe14c29226..382a9b24df9e2c 100644 --- a/src/libraries/Common/src/System/Number.Parsing.Common.cs +++ b/src/libraries/Common/src/System/Number.Parsing.Common.cs @@ -10,7 +10,7 @@ namespace System { internal static partial class Number { - private static unsafe bool TryParseNumber(scoped ref TChar* str, TChar* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info) + private static unsafe bool TryParseNumber(TChar* str, TChar* strEnd, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info, out int elementsConsumed) where TChar : unmanaged, IUtfChar { Debug.Assert(str != null); @@ -269,33 +269,37 @@ private static unsafe bool TryParseNumber(scoped ref TChar* str, TChar* s number.IsNegative = false; } } - str = p; - return true; + + int index = (int)(p - str); + int length = (int)(strEnd - str); + + if ((index == length) || ((styles & NumberStyles.AllowTrailingInvalidCharacters) != 0) || TrailingZeros(new ReadOnlySpan(str, length), index)) + { + // For AllowTrailingInvalidCharacters, we want to stop at the first invalid character + // including trailing nulls (zeros). Otherwise, for compatibility we still need to + // process any trailing nulls that exist and report them as having been consumed. + + elementsConsumed = index; + return true; + } } } - str = p; + + elementsConsumed = 0; return false; } - internal static unsafe bool TryStringToNumber(ReadOnlySpan value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info) + internal static unsafe bool TryStringToNumber(ReadOnlySpan value, NumberStyles styles, ref NumberBuffer number, NumberFormatInfo info, out int elementsConsumed) where TChar : unmanaged, IUtfChar { Debug.Assert(info != null); fixed (TChar* stringPointer = &MemoryMarshal.GetReference(value)) { - TChar* p = stringPointer; - - if (!TryParseNumber(ref p, p + value.Length, styles, ref number, info) - || ((int)(p - stringPointer) < value.Length && !TrailingZeros(value, (int)(p - stringPointer)))) - { - number.CheckConsistency(); - return false; - } + bool succeeded = TryParseNumber(stringPointer, stringPointer + value.Length, styles, ref number, info, out elementsConsumed); + number.CheckConsistency(); + return succeeded; } - - number.CheckConsistency(); - return true; } [MethodImpl(MethodImplOptions.NoInlining)] // rare slow path that shouldn't impact perf of the main use case diff --git a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx index f511ebeb21810d..d1c52f21d259cd 100644 --- a/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx +++ b/src/libraries/System.Private.CoreLib/src/Resources/Strings.resx @@ -437,7 +437,7 @@ Invalid handle. - With the AllowHexSpecifier or AllowBinarySpecifier bit set in the enum bit field, the only other valid bits that can be combined into the enum value must be AllowLeadingWhite and AllowTrailingWhite. + With the AllowHexSpecifier or AllowBinarySpecifier bit set in the enum bit field, the only other valid bits that can be combined into the enum value must be AllowLeadingWhite, AllowTrailingWhite, and AllowTrailingInvalidCharacters. The NeutralResourcesLanguageAttribute on the assembly "{0}" specifies an invalid culture name: "{1}". @@ -4441,4 +4441,4 @@ The directory '{0}' does not have the expected owner permissions: {1}. - + \ No newline at end of file diff --git a/src/libraries/System.Private.CoreLib/src/System/Byte.cs b/src/libraries/System.Private.CoreLib/src/System/Byte.cs index 1c933aaaced78b..457f1541b0d958 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Byte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Byte.cs @@ -130,13 +130,13 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I result = 0; return false; } - return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out byte result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } public override string ToString() @@ -1103,6 +1103,34 @@ static bool INumberBase.TryConvertToTruncating(byte value, [MaybeN } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out byte result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s is null) + { + result = 0; + charsConsumed = 0; + return false; + } + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out byte result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out byte result, out int bytesConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed) == Number.ParsingStatus.OK; + } + // // IParsable // @@ -1175,7 +1203,7 @@ public static byte Parse(ReadOnlySpan utf8Text, NumberStyles style = Numbe public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out byte result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Char.cs b/src/libraries/System.Private.CoreLib/src/System/Char.cs index e8ee0692a82d32..987f12b3e013bb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Char.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Char.cs @@ -1972,6 +1972,44 @@ static bool INumberBase.TryConvertToTruncating(char value, [MaybeN static bool INumberBase.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out char result) => TryParse(s, out result); + static bool INumberBase.TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out char result, out int charsConsumed) + { + if (TryParse(s, out result)) + { + charsConsumed = 1; + return true; + } + + charsConsumed = 0; + return false; + } + + static bool INumberBase.TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out char result, out int charsConsumed) + { + if (TryParse(s, out result)) + { + charsConsumed = 1; + return true; + } + + charsConsumed = 0; + return false; + } + + static bool INumberBase.TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out char result, out int bytesConsumed) + { + if (Rune.DecodeFromUtf8(utf8Text, out Rune rune, out bytesConsumed) != Buffers.OperationStatus.Done || + bytesConsumed != utf8Text.Length || + !rune.IsBmp) + { + result = '\0'; + return false; + } + + result = (char)rune.Value; + return true; + } + // // IParsable // diff --git a/src/libraries/System.Private.CoreLib/src/System/Decimal.cs b/src/libraries/System.Private.CoreLib/src/System/Decimal.cs index 4ae829ca83e174..ea0d6ad7e55c85 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Decimal.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Decimal.cs @@ -557,13 +557,13 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I result = 0; return false; } - return Number.TryParseDecimal(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseDecimal(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out decimal result) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); - return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } // Returns a binary representation of a Decimal. The return value is an @@ -1803,6 +1803,34 @@ private static bool TryConvertTo(decimal value, [MaybeNullWhen(false)] o } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out decimal result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + + if (s is null) + { + result = 0; + charsConsumed = 0; + return false; + } + return Number.TryParseDecimal(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out decimal result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.TryParseDecimal(s, style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out decimal result, out int bytesConsumed) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.TryParseDecimal(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed) == Number.ParsingStatus.OK; + } + // // IParsable // @@ -1842,7 +1870,7 @@ public static decimal Parse(ReadOnlySpan utf8Text, NumberStyles style = Nu public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out decimal result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseDecimal(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseDecimal(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Double.cs b/src/libraries/System.Private.CoreLib/src/System/Double.cs index 516f61d4e5b0f2..e3409960fbb16f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Double.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Double.cs @@ -433,13 +433,13 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I result = 0; return false; } - return Number.TryParseFloat(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseFloat(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _); } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out double result) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); - return Number.TryParseFloat(s, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseFloat(s, style, NumberFormatInfo.GetInstance(provider), out result, out _); } // @@ -1467,6 +1467,34 @@ private static bool TryConvertTo(double value, [MaybeNullWhen(false)] ou } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out double result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + + if (s is null) + { + result = 0; + charsConsumed = 0; + return false; + } + return Number.TryParseFloat(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed); + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out double result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.TryParseFloat(s, style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed); + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out double result, out int bytesConsumed) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.TryParseFloat(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed); + } + // // IParsable // @@ -2272,7 +2300,7 @@ public static double Parse(ReadOnlySpan utf8Text, NumberStyles style = Num public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out double result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseFloat(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseFloat(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _); } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Enum.cs b/src/libraries/System.Private.CoreLib/src/System/Enum.cs index 4e06f0e6da0ae4..30d597a9787fa2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Enum.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Enum.cs @@ -941,7 +941,7 @@ private static bool TryParseByValueOrName( NumberFormatInfo numberFormat = NumberFormatInfo.InvariantInfo; const NumberStyles NumberStyle = NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingWhite; - Number.ParsingStatus status = Number.TryParseBinaryIntegerStyle(value, NumberStyle, numberFormat, out result); + Number.ParsingStatus status = Number.TryParseBinaryIntegerStyle(value, NumberStyle, numberFormat, out result, out _); if (status == Number.ParsingStatus.OK) { return true; 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 7e41139b4bf6c5..a85e264f2999b9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberFormatInfo.cs @@ -798,21 +798,21 @@ public static NumberFormatInfo ReadOnly(NumberFormatInfo nfi) return info; } - // private const NumberStyles InvalidNumberStyles = unchecked((NumberStyles) 0xFFFFFC00); + // private const NumberStyles InvalidNumberStyles = unchecked((NumberStyles) 0xFFFFF000); private const NumberStyles InvalidNumberStyles = ~(NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite | NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingSign | NumberStyles.AllowParentheses | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands | NumberStyles.AllowExponent | NumberStyles.AllowCurrencySymbol | NumberStyles.AllowHexSpecifier - | NumberStyles.AllowBinarySpecifier); + | NumberStyles.AllowBinarySpecifier | NumberStyles.AllowTrailingInvalidCharacters); [MethodImpl(MethodImplOptions.AggressiveInlining)] internal static void ValidateParseStyleInteger(NumberStyles style) { - // Check for undefined flags or using AllowHexSpecifier/AllowBinarySpecifier each with anything other than AllowLeadingWhite/AllowTrailingWhite. + // Check for undefined flags or using AllowHexSpecifier/AllowBinarySpecifier each with anything other than AllowLeadingWhite/AllowTrailingWhite/AllowTrailingInvalidCharacters. if ((style & (InvalidNumberStyles | NumberStyles.AllowHexSpecifier | NumberStyles.AllowBinarySpecifier)) != 0 && - (style & ~NumberStyles.HexNumber) != 0 && - (style & ~NumberStyles.BinaryNumber) != 0) + (style & ~(NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters)) != 0 && + (style & ~(NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters)) != 0) { ThrowInvalid(style); diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberStyles.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberStyles.cs index 84a8afdf39a3a2..2dccdcb8305db7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberStyles.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/NumberStyles.cs @@ -60,6 +60,9 @@ public enum NumberStyles /// AllowBinarySpecifier = 0x00000400, + /// Indicates that the numeric string is allowed to contain otherwise invalid characters and that parsing should treat such characters as terminating the input. + AllowTrailingInvalidCharacters = 0x00000800, + Integer = AllowLeadingWhite | AllowTrailingWhite | AllowLeadingSign, HexNumber = AllowLeadingWhite | AllowTrailingWhite | AllowHexSpecifier, diff --git a/src/libraries/System.Private.CoreLib/src/System/Guid.cs b/src/libraries/System.Private.CoreLib/src/System/Guid.cs index 270da4ab7d0b09..79c7775cc819b6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Guid.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Guid.cs @@ -590,7 +590,7 @@ static bool TryCompatParsing(ReadOnlySpan guidString, ref GuidResult resu 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.TryParseBinaryIntegerHexNumberStyle(guidString.Slice(28, 8), NumberStyles.AllowHexSpecifier, out uintTmp) == Number.ParsingStatus.OK) // _h, _i, _j, _k + if (Number.TryParseBinaryIntegerHexOrBinaryNumberStyle>(guidString.Slice(28, 8), NumberStyles.AllowHexSpecifier, out uintTmp, out _) == 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/Half.cs b/src/libraries/System.Private.CoreLib/src/System/Half.cs index fc6dd2bd0c423a..40f1e7b0c46657 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Half.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Half.cs @@ -397,7 +397,7 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I result = Zero; return false; } - return Number.TryParseFloat(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseFloat(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _); } /// @@ -411,7 +411,7 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Half result) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); - return Number.TryParseFloat(s, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseFloat(s, style, NumberFormatInfo.GetInstance(provider), out result, out _); } private static bool AreZero(Half left, Half right) @@ -2164,6 +2164,34 @@ private static bool TryConvertTo(Half value, [MaybeNullWhen(false)] out } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out Half result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + + if (s is null) + { + result = Zero; + charsConsumed = 0; + return false; + } + return Number.TryParseFloat(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed); + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Half result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.TryParseFloat(s, style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed); + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out Half result, out int bytesConsumed) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.TryParseFloat(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed); + } + // // IParsable // @@ -2319,7 +2347,7 @@ public static Half Parse(ReadOnlySpan utf8Text, NumberStyles style = Numbe public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out Half result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseFloat(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseFloat(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _); } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Int128.cs b/src/libraries/System.Private.CoreLib/src/System/Int128.cs index bfc8e8c8bf0e25..ae7eb5d55cffb7 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int128.cs @@ -163,13 +163,13 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I result = 0; return false; } - return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Int128 result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } // @@ -1921,6 +1921,34 @@ static bool INumberBase.TryConvertToTruncating(Int128 value, [Ma } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out Int128 result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s is null) + { + result = 0; + charsConsumed = 0; + return false; + } + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Int128 result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out Int128 result, out int bytesConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed) == Number.ParsingStatus.OK; + } + // // IParsable // @@ -2116,7 +2144,7 @@ public static Int128 Parse(ReadOnlySpan utf8Text, NumberStyles style = Num public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out Int128 result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _) == 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 b1a90999ea5e9e..3588e09eba2c10 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int16.cs @@ -163,13 +163,13 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I result = 0; return false; } - return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out short result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } // @@ -1300,6 +1300,34 @@ static bool INumberBase.TryConvertToTruncating(short value, [Mayb } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out short result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s is null) + { + result = 0; + charsConsumed = 0; + return false; + } + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out short result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out short result, out int bytesConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed) == Number.ParsingStatus.OK; + } + // // IParsable // @@ -1379,7 +1407,7 @@ public static short Parse(ReadOnlySpan utf8Text, NumberStyles style = Numb public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out short result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _) == 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 7a0cce55bc8099..1b6cdd72dad89b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int32.cs @@ -179,13 +179,13 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I result = 0; return false; } - return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out int result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } // @@ -1366,6 +1366,34 @@ static bool INumberBase.TryConvertToTruncating(int value, [MaybeNul } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out int result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s is null) + { + result = 0; + charsConsumed = 0; + return false; + } + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out int result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out int result, out int bytesConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed) == Number.ParsingStatus.OK; + } + // // IParsable // @@ -1445,7 +1473,7 @@ public static int Parse(ReadOnlySpan utf8Text, NumberStyles style = Number public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out int result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _) == 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 aa07dbb7d0327b..93b6a263834572 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Int64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Int64.cs @@ -176,13 +176,13 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I result = 0; return false; } - return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out long result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } // @@ -1369,6 +1369,34 @@ static bool INumberBase.TryConvertToTruncating(long value, [MaybeN } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out long result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s is null) + { + result = 0; + charsConsumed = 0; + return false; + } + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out long result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out long result, out int bytesConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed) == Number.ParsingStatus.OK; + } + // // IParsable // @@ -1448,7 +1476,7 @@ public static long Parse(ReadOnlySpan utf8Text, NumberStyles style = Numbe public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out long result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs index 4abd1b935f0eb5..60e2e601727c6d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IntPtr.cs @@ -1376,6 +1376,27 @@ static bool INumberBase.TryConvertToTruncating(nint value, [MaybeN } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out nint result, out int charsConsumed) + { + Unsafe.SkipInit(out result); + return nint_t.TryParse(s, style, provider, out Unsafe.As(ref result), out charsConsumed); + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out nint result, out int charsConsumed) + { + Unsafe.SkipInit(out result); + return nint_t.TryParse(s, style, provider, out Unsafe.As(ref result), out charsConsumed); + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out nint result, out int bytesConsumed) + { + Unsafe.SkipInit(out result); + return nint_t.TryParse(utf8Text, style, provider, out Unsafe.As(ref result), out bytesConsumed); + } + // // IShiftOperators // 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 3748792c1784d3..72ff343953c3ba 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs @@ -211,7 +211,7 @@ internal static TInteger ParseBinaryInteger(ReadOnlySpan where TChar : unmanaged, IUtfChar where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { - ParsingStatus status = TryParseBinaryInteger(value, styles, info, out TInteger result); + ParsingStatus status = TryParseBinaryInteger(value, styles, info, out TInteger result, out _); if (status != ParsingStatus.OK) { @@ -221,37 +221,37 @@ internal static TInteger ParseBinaryInteger(ReadOnlySpan } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static ParsingStatus TryParseBinaryInteger(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out TInteger result) + internal static ParsingStatus TryParseBinaryInteger(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out TInteger result, out int elementsConsumed) where TChar : unmanaged, IUtfChar where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { 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); + return TryParseBinaryIntegerStyle(value, styles, info, out result, out elementsConsumed); } if ((styles & NumberStyles.AllowHexSpecifier) != 0) { - return TryParseBinaryIntegerHexNumberStyle(value, styles, out result); + return TryParseBinaryIntegerHexOrBinaryNumberStyle>(value, styles, out result, out elementsConsumed); } if ((styles & NumberStyles.AllowBinarySpecifier) != 0) { - return TryParseBinaryIntegerHexOrBinaryNumberStyle>(value, styles, out result); + return TryParseBinaryIntegerHexOrBinaryNumberStyle>(value, styles, out result, out elementsConsumed); } - return TryParseBinaryIntegerNumber(value, styles, info, out result); + return TryParseBinaryIntegerNumber(value, styles, info, out result, out elementsConsumed); } - private static ParsingStatus TryParseBinaryIntegerNumber(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out TInteger result) + private static ParsingStatus TryParseBinaryIntegerNumber(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out TInteger result, out int elementsConsumed) where TChar : unmanaged, IUtfChar where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { result = TInteger.Zero; NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, stackalloc byte[TInteger.MaxDigitCount + 1]); - if (!TryStringToNumber(value, styles, ref number, info)) + if (!TryStringToNumber(value, styles, ref number, info, out elementsConsumed)) { return ParsingStatus.Failed; } @@ -265,7 +265,7 @@ private static ParsingStatus TryParseBinaryIntegerNumber(ReadOn } /// Parses int limited to styles that make up NumberStyles.Integer. - internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out TInteger result) + internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out TInteger result, out int elementsConsumed) where TChar : unmanaged, IUtfChar where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { @@ -503,17 +503,36 @@ internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOn result = isNegative ? -answer : answer; } ParsingStatus status = ParsingStatus.OK; + elementsConsumed = index; Exit: return status; + InvalidExit: + // For AllowTrailingInvalidCharacters, we want to stop at the first invalid character + // including trailing nulls (zeros). Otherwise, for compatibility we still need to + // process any trailing nulls that exist and report them as having been consumed. + + if ((styles & NumberStyles.AllowTrailingInvalidCharacters) != 0) + { + goto DoneAtEndButPotentialOverflow; + } + + if (TrailingZeros(value, index)) + { + index = value.Length; + goto DoneAtEndButPotentialOverflow; + } + FalseExit: // parsing failed result = TInteger.Zero; + elementsConsumed = 0; status = ParsingStatus.Failed; goto Exit; OverflowExit: result = TInteger.Zero; + elementsConsumed = 0; status = ParsingStatus.Overflow; goto Exit; @@ -523,7 +542,7 @@ internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOn { if ((styles & NumberStyles.AllowTrailingWhite) == 0) { - goto FalseExit; + goto InvalidExit; } for (index++; index < value.Length; index++) @@ -538,23 +557,10 @@ internal static ParsingStatus TryParseBinaryIntegerStyle(ReadOn if ((uint)index >= (uint)value.Length) goto DoneAtEndButPotentialOverflow; } - - if (!TrailingZeros(value, index)) - { - goto FalseExit; - } - goto DoneAtEndButPotentialOverflow; - } - - /// Parses limited to styles that make up NumberStyles.HexNumber. - internal static ParsingStatus TryParseBinaryIntegerHexNumberStyle(ReadOnlySpan value, NumberStyles styles, out TInteger result) - where TChar : unmanaged, IUtfChar - where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo - { - return TryParseBinaryIntegerHexOrBinaryNumberStyle>(value, styles, out result); + goto InvalidExit; } - private interface IHexOrBinaryParser + internal interface IHexOrBinaryParser where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { static abstract NumberStyles AllowedStyles { get; } @@ -565,7 +571,7 @@ private interface IHexOrBinaryParser static abstract TInteger ShiftLeftForNextDigit(TInteger value); } - private readonly struct HexParser : IHexOrBinaryParser where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo + internal readonly struct HexParser : IHexOrBinaryParser where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo { public static NumberStyles AllowedStyles => NumberStyles.HexNumber; public static bool IsValidChar(uint ch) => HexConverter.IsHexChar((int)ch); @@ -585,7 +591,7 @@ private interface IHexOrBinaryParser public static TInteger ShiftLeftForNextDigit(TInteger value) => value << 1; } - private static ParsingStatus TryParseBinaryIntegerHexOrBinaryNumberStyle(ReadOnlySpan value, NumberStyles styles, out TInteger result) + internal static ParsingStatus TryParseBinaryIntegerHexOrBinaryNumberStyle(ReadOnlySpan value, NumberStyles styles, out TInteger result, out int elementsConsumed) where TChar : unmanaged, IUtfChar where TInteger : unmanaged, IBinaryIntegerParseAndFormatInfo where TParser : struct, IHexOrBinaryParser @@ -705,17 +711,36 @@ private static ParsingStatus TryParseBinaryIntegerHexOrBinaryNumberStyle(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info) where TChar : unmanaged, IUtfChar { - ParsingStatus status = TryParseDecimal(value, styles, info, out decimal result); + ParsingStatus status = TryParseDecimal(value, styles, info, out decimal result, out _); + if (status != ParsingStatus.OK) { if (status == ParsingStatus.Failed) @@ -891,21 +912,21 @@ internal static TFloat ParseFloat(ReadOnlySpan value, Numb where TChar : unmanaged, IUtfChar where TFloat : unmanaged, IBinaryFloatParseAndFormatInfo { - if (!TryParseFloat(value, styles, info, out TFloat result)) + if (!TryParseFloat(value, styles, info, out TFloat result, out _)) { ThrowFormatException(value); } return result; } - internal static ParsingStatus TryParseDecimal(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out decimal result) + internal static ParsingStatus TryParseDecimal(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out decimal result, out int elementsConsumed) where TChar : unmanaged, IUtfChar { NumberBuffer number = new NumberBuffer(NumberBufferKind.Decimal, stackalloc byte[DecimalNumberBufferLength]); result = 0; - if (!TryStringToNumber(value, styles, ref number, info)) + if (!TryStringToNumber(value, styles, ref number, info, out elementsConsumed)) { return ParsingStatus.Failed; } @@ -979,13 +1000,13 @@ internal static bool SpanEqualsOrdinalIgnoreCase(ReadOnlySpan span } } - internal static bool TryParseFloat(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out TFloat result) + internal static bool TryParseFloat(ReadOnlySpan value, NumberStyles styles, NumberFormatInfo info, out TFloat result, out int elementsConsumed) where TChar : unmanaged, IUtfChar where TFloat : unmanaged, IBinaryFloatParseAndFormatInfo { NumberBuffer number = new NumberBuffer(NumberBufferKind.FloatingPoint, stackalloc byte[TFloat.NumberBufferLength]); - if (!TryStringToNumber(value, styles, ref number, info)) + if (!TryStringToNumber(value, styles, ref number, info, out elementsConsumed)) { ReadOnlySpan valueTrim = SpanTrim(value); diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/BFloat16.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/BFloat16.cs index 545b9a62091637..d7d394f84f3018 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/BFloat16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/BFloat16.cs @@ -314,7 +314,7 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I result = Zero; return false; } - return Number.TryParseFloat(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseFloat(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _); } /// @@ -328,7 +328,7 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out BFloat16 result) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); - return Number.TryParseFloat(s, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseFloat(s, style, NumberFormatInfo.GetInstance(provider), out result, out _); } // Comparison @@ -1941,6 +1941,34 @@ private static bool TryConvertTo(BFloat16 value, [MaybeNullWhen(false)] } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out BFloat16 result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + + if (s is null) + { + result = Zero; + charsConsumed = 0; + return false; + } + return Number.TryParseFloat(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed); + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out BFloat16 result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.TryParseFloat(s, style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed); + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out BFloat16 result, out int bytesConsumed) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.TryParseFloat(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed); + } + // // IParsable // @@ -2096,7 +2124,7 @@ public static BFloat16 Parse(ReadOnlySpan utf8Text, NumberStyles style = D public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out BFloat16 result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseFloat(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseFloat(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _); } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Numerics/INumberBase.cs b/src/libraries/System.Private.CoreLib/src/System/Numerics/INumberBase.cs index e647b23c344a36..814d2b8c9d586b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Numerics/INumberBase.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Numerics/INumberBase.cs @@ -419,6 +419,30 @@ protected static abstract bool TryConvertToTruncating(TSelf value, [Mayb /// is not a supported value. static abstract bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out TSelf result); + /// Tries to parse a string into a value. + /// The string to parse. + /// A bitwise combination of number styles that can be present in . + /// An object that provides culture-specific formatting information about . + /// On return, contains the result of successfully parsing or an undefined value on failure. + /// On return, contains the number of characters consumed during parsing. + /// true if was successfully parsed; otherwise, false. + /// is not a supported value. + static virtual bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out TSelf result, out int charsConsumed) + { + // NumberStyles.AllowTrailingInvalidCharacters is new with the introduction of this DIM and so the expected behavior of TryParse + // is to throw, meaning that we will always consume "everything" or "nothing". Types which add support for the new flag will be + // expected to override this method and provide a correct implementation. + + if (TSelf.TryParse(s, style & ~NumberStyles.AllowTrailingInvalidCharacters, provider, out result)) + { + charsConsumed = s.Length; + return true; + } + + charsConsumed = 0; + return false; + } + /// Tries to parse a span of characters into a value. /// The span of characters to parse. /// A bitwise combination of number styles that can be present in . @@ -428,6 +452,30 @@ protected static abstract bool TryConvertToTruncating(TSelf value, [Mayb /// is not a supported value. static abstract bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out TSelf result); + /// Tries to parse a span of characters into a value. + /// The span of characters to parse. + /// A bitwise combination of number styles that can be present in . + /// An object that provides culture-specific formatting information about . + /// On return, contains the result of successfully parsing or an undefined value on failure. + /// On return, contains the number of characters consumed during parsing. + /// true if was successfully parsed; otherwise, false. + /// is not a supported value. + static virtual bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out TSelf result, out int charsConsumed) + { + // NumberStyles.AllowTrailingInvalidCharacters is new with the introduction of this DIM and so the expected behavior of TryParse + // is to throw, meaning that we will always consume "everything" or "nothing". Types which add support for the new flag will be + // expected to override this method and provide a correct implementation. + + if (TSelf.TryParse(s, style & ~NumberStyles.AllowTrailingInvalidCharacters, provider, out result)) + { + charsConsumed = s.Length; + return true; + } + + charsConsumed = 0; + return false; + } + /// Tries to parse a span of UTF-8 characters into a value. /// The span of UTF-8 characters to parse. /// A bitwise combination of number styles that can be present in . @@ -483,6 +531,30 @@ static virtual bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IF return succeeded; } + /// Tries to parse a span of UTF-8 characters into a value. + /// The span of UTF-8 characters to parse. + /// A bitwise combination of number styles that can be present in . + /// An object that provides culture-specific formatting information about . + /// On return, contains the result of successfully parsing or an undefined value on failure. + /// On return, contains the number of bytes consumed during parsing. + /// true if was successfully parsed; otherwise, false. + /// is not a supported value. + static virtual bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, [MaybeNullWhen(false)] out TSelf result, out int bytesConsumed) + { + // NumberStyles.AllowTrailingInvalidCharacters is new with the introduction of this DIM and so the expected behavior of TryParse + // is to throw, meaning that we will always consume "everything" or "nothing". Types which add support for the new flag will be + // expected to override this method and provide a correct implementation. + + if (TSelf.TryParse(utf8Text, style & ~NumberStyles.AllowTrailingInvalidCharacters, provider, out result)) + { + bytesConsumed = utf8Text.Length; + return true; + } + + bytesConsumed = 0; + return false; + } + bool IUtf8SpanFormattable.TryFormat(Span utf8Destination, out int bytesWritten, ReadOnlySpan format, IFormatProvider? provider) { char[]? utf16DestinationArray; diff --git a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs index d542d13b39f07a..a4bf18abe9ba9d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Runtime/InteropServices/NFloat.cs @@ -1786,6 +1786,27 @@ private static bool TryConvertTo(NFloat value, [MaybeNullWhen(false)] ou } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out NFloat result, out int charsConsumed) + { + Unsafe.SkipInit(out result); + return NativeType.TryParse(s, style, provider, out Unsafe.As(ref result), out charsConsumed); + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out NFloat result, out int charsConsumed) + { + Unsafe.SkipInit(out result); + return NativeType.TryParse(s, style, provider, out Unsafe.As(ref result), out charsConsumed); + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out NFloat result, out int bytesConsumed) + { + Unsafe.SkipInit(out result); + return NativeType.TryParse(utf8Text, style, provider, out Unsafe.As(ref result), out bytesConsumed); + } + // // IParsable // diff --git a/src/libraries/System.Private.CoreLib/src/System/SByte.cs b/src/libraries/System.Private.CoreLib/src/System/SByte.cs index 4ba9c4372de61b..e7af288c6af25d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/SByte.cs +++ b/src/libraries/System.Private.CoreLib/src/System/SByte.cs @@ -166,13 +166,13 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I result = 0; return false; } - return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out sbyte result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } // @@ -1263,6 +1263,34 @@ static bool INumberBase.TryConvertToTruncating(sbyte value, [Mayb } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out sbyte result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s is null) + { + result = 0; + charsConsumed = 0; + return false; + } + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out sbyte result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out sbyte result, out int bytesConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed) == Number.ParsingStatus.OK; + } + // // IParsable // @@ -1342,7 +1370,7 @@ public static sbyte Parse(ReadOnlySpan utf8Text, NumberStyles style = Numb public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out sbyte result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/Single.cs b/src/libraries/System.Private.CoreLib/src/System/Single.cs index a284cb69ed3a32..47b7b429679053 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Single.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Single.cs @@ -428,13 +428,13 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I result = 0; return false; } - return Number.TryParseFloat(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseFloat(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _); } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out float result) { NumberFormatInfo.ValidateParseStyleFloatingPoint(style); - return Number.TryParseFloat(s, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseFloat(s, style, NumberFormatInfo.GetInstance(provider), out result, out _); } // @@ -1486,6 +1486,34 @@ private static bool TryConvertTo(float value, [MaybeNullWhen(false)] out } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out float result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + + if (s is null) + { + result = 0; + charsConsumed = 0; + return false; + } + return Number.TryParseFloat(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed); + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out float result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.TryParseFloat(s, style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed); + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out float result, out int bytesConsumed) + { + NumberFormatInfo.ValidateParseStyleFloatingPoint(style); + return Number.TryParseFloat(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed); + } + // // IParsable // @@ -2188,7 +2216,7 @@ public static float Parse(ReadOnlySpan utf8Text, NumberStyles style = Numb public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out float result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseFloat(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result); + return Number.TryParseFloat(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _); } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs index 20ad904bc423a2..d3ba93687989ca 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt128.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt128.cs @@ -165,13 +165,13 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I result = 0; return false; } - return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out UInt128 result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } // @@ -1999,6 +1999,34 @@ static bool INumberBase.TryConvertToTruncating(UInt128 value, [ } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out UInt128 result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s is null) + { + result = 0; + charsConsumed = 0; + return false; + } + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out UInt128 result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out UInt128 result, out int bytesConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed) == Number.ParsingStatus.OK; + } + // // IParsable // @@ -2151,7 +2179,7 @@ public static UInt128 Parse(ReadOnlySpan utf8Text, NumberStyles style = Nu public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out UInt128 result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _) == 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 87d4f12a9a10d9..12f8b403fbfa2b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt16.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt16.cs @@ -158,13 +158,13 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I result = 0; return false; } - return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out ushort result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } // @@ -1122,6 +1122,34 @@ static bool INumberBase.TryConvertToTruncating(ushort value, [Ma } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out ushort result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s is null) + { + result = 0; + charsConsumed = 0; + return false; + } + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out ushort result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out ushort result, out int bytesConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed) == Number.ParsingStatus.OK; + } + // // IParsable // @@ -1194,7 +1222,7 @@ public static ushort Parse(ReadOnlySpan utf8Text, NumberStyles style = Num public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out ushort result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _) == 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 a774069fb560b2..ebf462834fdea1 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt32.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt32.cs @@ -174,13 +174,13 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I result = 0; return false; } - return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out uint result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } // @@ -1167,6 +1167,34 @@ static bool INumberBase.TryConvertToTruncating(uint value, [MaybeN } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out uint result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s is null) + { + result = 0; + charsConsumed = 0; + return false; + } + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out uint result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out uint result, out int bytesConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed) == Number.ParsingStatus.OK; + } + // // IParsable // @@ -1239,7 +1267,7 @@ public static uint Parse(ReadOnlySpan utf8Text, NumberStyles style = Numbe public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out uint result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _) == 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 56d9fc2cc2015a..e182eec9cedee2 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UInt64.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UInt64.cs @@ -173,13 +173,13 @@ public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, I result = 0; return false; } - return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out ulong result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } // @@ -1160,6 +1160,34 @@ static bool INumberBase.TryConvertToTruncating(ulong value, [Mayb } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out ulong result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + + if (s is null) + { + result = 0; + charsConsumed = 0; + return false; + } + return Number.TryParseBinaryInteger(s.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out ulong result, out int charsConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(s, style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out ulong result, out int bytesConsumed) + { + NumberFormatInfo.ValidateParseStyleInteger(style); + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed) == Number.ParsingStatus.OK; + } + // // IParsable // @@ -1232,7 +1260,7 @@ public static ulong Parse(ReadOnlySpan utf8Text, NumberStyles style = Numb public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out ulong result) { NumberFormatInfo.ValidateParseStyleInteger(style); - return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBinaryInteger(utf8Text, style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } /// diff --git a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs index 450160040fb4f7..6c5fb3f1637703 100644 --- a/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs +++ b/src/libraries/System.Private.CoreLib/src/System/UIntPtr.cs @@ -1176,6 +1176,27 @@ static bool INumberBase.TryConvertToTruncating(nuint value, [Mayb } } + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out nuint result, out int charsConsumed) + { + Unsafe.SkipInit(out result); + return nuint_t.TryParse(s, style, provider, out Unsafe.As(ref result), out charsConsumed); + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out nuint result, out int charsConsumed) + { + Unsafe.SkipInit(out result); + return nuint_t.TryParse(s, style, provider, out Unsafe.As(ref result), out charsConsumed); + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out nuint result, out int bytesConsumed) + { + Unsafe.SkipInit(out result); + return nuint_t.TryParse(utf8Text, style, provider, out Unsafe.As(ref result), out bytesConsumed); + } + // // IShiftOperators // diff --git a/src/libraries/System.Private.CoreLib/src/System/Version.cs b/src/libraries/System.Private.CoreLib/src/System/Version.cs index 502a51ff60d10e..7476d61b92167b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Version.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Version.cs @@ -430,7 +430,7 @@ static bool IUtf8SpanParsable.TryParse(ReadOnlySpan utf8Text, IFo private static bool TryParseComponent(ReadOnlySpan component, string componentName, bool throwOnFailure, ReadOnlySpan originalInput, out int parsedComponent) where TChar : unmanaged, IUtfChar { - Number.ParsingStatus parseStatus = Number.TryParseBinaryIntegerStyle(component, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out parsedComponent); + Number.ParsingStatus parseStatus = Number.TryParseBinaryIntegerStyle(component, NumberStyles.Integer, NumberFormatInfo.InvariantInfo, out parsedComponent, out _); if (parseStatus == Number.ParsingStatus.OK && parsedComponent >= 0) { diff --git a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs index d4923a544cc3a0..afeab7a6f197fa 100644 --- a/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs +++ b/src/libraries/System.Runtime.InteropServices/ref/System.Runtime.InteropServices.cs @@ -1573,6 +1573,9 @@ public static void Free(void* ptr) { } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Runtime.InteropServices.NFloat result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.Runtime.InteropServices.NFloat result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Runtime.InteropServices.NFloat result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Runtime.InteropServices.NFloat result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Runtime.InteropServices.NFloat result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Runtime.InteropServices.NFloat result, out int charsConsumed) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Parameter, Inherited=false)] public sealed partial class OptionalAttribute : System.Attribute diff --git a/src/libraries/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs b/src/libraries/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs index 9c3e42270020ca..53abb1a5b3272d 100644 --- a/src/libraries/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs +++ b/src/libraries/System.Runtime.Numerics/ref/System.Runtime.Numerics.cs @@ -247,6 +247,9 @@ namespace System.Numerics public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? value, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.BigInteger result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.Numerics.BigInteger result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? value, out System.Numerics.BigInteger result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.BigInteger result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.BigInteger result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.BigInteger result, out int charsConsumed) { throw null; } public bool TryWriteBytes(System.Span destination, out int bytesWritten, bool isUnsigned = false, bool isBigEndian = false) { throw null; } } public readonly partial struct Complex : System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumberBase, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators @@ -397,5 +400,8 @@ namespace System.Numerics public static bool TryParse(System.ReadOnlySpan s, System.IFormatProvider? provider, out System.Numerics.Complex result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Complex result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.Numerics.Complex result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Complex result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Complex result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.Complex result, out int charsConsumed) { throw null; } } } diff --git a/src/libraries/System.Runtime.Numerics/src/Resources/Strings.resx b/src/libraries/System.Runtime.Numerics/src/Resources/Strings.resx index b9493813fa2800..34b00699194c40 100644 --- a/src/libraries/System.Runtime.Numerics/src/Resources/Strings.resx +++ b/src/libraries/System.Runtime.Numerics/src/Resources/Strings.resx @@ -66,8 +66,8 @@ An undefined NumberStyles value is being used. - - With the AllowHexSpecifier bit set in the enum bit field, the only other valid bits that can be combined into the enum value must be a subset of those in HexNumber. + + With the AllowHexSpecifier or AllowBinarySpecifier bit set in the enum bit field, the only other valid bits that can be combined into the enum value must be AllowLeadingWhite, AllowTrailingWhite, and AllowTrailingInvalidCharacters. '{0}' cannot be greater than {1}. @@ -114,4 +114,4 @@ Negative values do not have an unsigned representation. - + \ No newline at end of file diff --git a/src/libraries/System.Runtime.Numerics/src/System/Number.BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Number.BigInteger.cs index 9596786fcf516c..8cc73b39cd1222 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Number.BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Number.BigInteger.cs @@ -16,12 +16,13 @@ namespace System { internal static partial class Number { + // private const NumberStyles InvalidNumberStyles = unchecked((NumberStyles) 0xFFFFF000); private const NumberStyles InvalidNumberStyles = ~(NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite | NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingSign | NumberStyles.AllowParentheses | NumberStyles.AllowDecimalPoint | NumberStyles.AllowThousands | NumberStyles.AllowExponent | NumberStyles.AllowCurrencySymbol | NumberStyles.AllowHexSpecifier - | NumberStyles.AllowBinarySpecifier); + | NumberStyles.AllowBinarySpecifier | NumberStyles.AllowTrailingInvalidCharacters); private static ReadOnlySpan UInt32PowersOfTen => [1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000]; @@ -37,25 +38,19 @@ private static Exception GetException(ParsingStatus status) internal static bool TryValidateParseStyleInteger(NumberStyles style, [NotNullWhen(false)] out ArgumentException? e) { - // Check for undefined flags - if ((style & InvalidNumberStyles) != 0) + // Check for undefined flags or using AllowHexSpecifier/AllowBinarySpecifier each with anything other than AllowLeadingWhite/AllowTrailingWhite/AllowTrailingInvalidCharacters. + if ((style & (InvalidNumberStyles | NumberStyles.AllowHexSpecifier | NumberStyles.AllowBinarySpecifier)) != 0 && + (style & ~(NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters)) != 0 && + (style & ~(NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters)) != 0) { - e = new ArgumentException(SR.Argument_InvalidNumberStyles, nameof(style)); + e = new ArgumentException((style & InvalidNumberStyles) != 0 ? SR.Argument_InvalidNumberStyles : SR.Argument_InvalidHexBinaryStyle, nameof(style)); return false; } - if ((style & NumberStyles.AllowHexSpecifier) != 0) - { // Check for hex number - if ((style & ~NumberStyles.HexNumber) != 0) - { - e = new ArgumentException(SR.Argument_InvalidHexStyle, nameof(style)); - return false; - } - } e = null; return true; } - internal static ParsingStatus TryParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info, out BigInteger result) + internal static ParsingStatus TryParseBigInteger(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info, out BigInteger result, out int elementsConsumed) where TChar : unmanaged, IUtfChar { if (!TryValidateParseStyleInteger(style, out ArgumentException? e)) @@ -65,18 +60,18 @@ internal static ParsingStatus TryParseBigInteger(ReadOnlySpan valu if ((style & NumberStyles.AllowHexSpecifier) != 0) { - return TryParseBigIntegerHexOrBinaryNumberStyle, TChar>(value, style, out result); + return TryParseBigIntegerHexOrBinaryNumberStyle, TChar>(value, style, out result, out elementsConsumed); } if ((style & NumberStyles.AllowBinarySpecifier) != 0) { - return TryParseBigIntegerHexOrBinaryNumberStyle, TChar>(value, style, out result); + return TryParseBigIntegerHexOrBinaryNumberStyle, TChar>(value, style, out result, out elementsConsumed); } - return TryParseBigIntegerNumber(value, style, info, out result); + return TryParseBigIntegerNumber(value, style, info, out result, out elementsConsumed); } - internal static unsafe ParsingStatus TryParseBigIntegerNumber(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info, out BigInteger result) + internal static unsafe ParsingStatus TryParseBigIntegerNumber(ReadOnlySpan value, NumberStyles style, NumberFormatInfo info, out BigInteger result, out int elementsConsumed) where TChar : unmanaged, IUtfChar { scoped Span buffer; @@ -85,6 +80,7 @@ internal static unsafe ParsingStatus TryParseBigIntegerNumber(ReadOnlySpa if (value.Length == 0) { result = default; + elementsConsumed = 0; return ParsingStatus.Failed; } @@ -103,7 +99,7 @@ internal static unsafe ParsingStatus TryParseBigIntegerNumber(ReadOnlySpa { NumberBuffer number = new NumberBuffer(NumberBufferKind.Integer, buffer); - if (!TryStringToNumber(value, style, ref number, info)) + if (!TryStringToNumber(value, style, ref number, info, out elementsConsumed)) { result = default; ret = ParsingStatus.Failed; @@ -130,43 +126,48 @@ internal static BigInteger ParseBigInteger(ReadOnlySpan value, Num throw e; } - ParsingStatus status = TryParseBigInteger(value, style, info, out BigInteger result); + ParsingStatus status = TryParseBigInteger(value, style, info, out BigInteger result, out _); + if (status != ParsingStatus.OK) { ThrowOverflowOrFormatException(status); } - return result; } - internal static ParsingStatus TryParseBigIntegerHexOrBinaryNumberStyle(ReadOnlySpan value, NumberStyles style, out BigInteger result) + internal static ParsingStatus TryParseBigIntegerHexOrBinaryNumberStyle(ReadOnlySpan value, NumberStyles style, out BigInteger result, out int elementsConsumed) where TParser : struct, IBigIntegerHexOrBinaryParser where TChar : unmanaged, IUtfChar { - int whiteIndex; + int index = 0; + int trailingWhiteLength = 0; // Skip past any whitespace at the beginning. if ((style & NumberStyles.AllowLeadingWhite) != 0) { - for (whiteIndex = 0; whiteIndex < value.Length; whiteIndex++) + for (index = 0; index < value.Length; index++) { - if (!IsWhite(TChar.CastToUInt32(value[whiteIndex]))) + if (!IsWhite(TChar.CastToUInt32(value[index]))) break; } - value = value[whiteIndex..]; + value = value[index..]; } // Skip past any whitespace at the end. if ((style & NumberStyles.AllowTrailingWhite) != 0) { + int whiteIndex; + for (whiteIndex = value.Length - 1; whiteIndex >= 0; whiteIndex--) { if (!IsWhite(TChar.CastToUInt32(value[whiteIndex]))) break; } + whiteIndex++; - value = value[..(whiteIndex + 1)]; + value = value[..whiteIndex]; + trailingWhiteLength = value.Length - whiteIndex; } if (value.IsEmpty) @@ -192,7 +193,9 @@ internal static ParsingStatus TryParseBigIntegerHexOrBinaryNumberStyle= 0) { // Small value that fits in Int32. @@ -241,16 +248,29 @@ internal static ParsingStatus TryParseBigIntegerHexOrBinaryNumberStyle BigInteger.MaxLength) { result = default; + elementsConsumed = 0; return ParsingStatus.Overflow; } uint[] bits = new uint[totalUIntCount]; Span wholeBlockDestination = bits.AsSpan(0, wholeBlockCount); - if (!TParser.TryParseWholeBlocks(value, wholeBlockDestination)) + if (!TParser.TryParseWholeBlocks(value, wholeBlockDestination, out int blocksWritten)) { - goto FailExit; + if ((style & NumberStyles.AllowTrailingInvalidCharacters) != 0) + { + uint[] actualBits = new uint[blocksWritten]; + wholeBlockDestination.Slice(0, blocksWritten).CopyTo(actualBits); + + bits = actualBits; + trailingWhiteLength = 0; + } + else + { + goto FailExit; + } } + index += (TParser.DigitsPerBlock * blocksWritten); bits[^1] = leading; @@ -268,6 +288,7 @@ internal static ParsingStatus TryParseBigIntegerHexOrBinaryNumberStyle BigInteger.MaxLength) { result = default; + elementsConsumed = 0; return ParsingStatus.Overflow; } @@ -276,6 +297,7 @@ internal static ParsingStatus TryParseBigIntegerHexOrBinaryNumberStyle input, out uint r static virtual bool TryParseSingleBlock(ReadOnlySpan input, out uint result) => TParser.TryParseUnalignedBlock(input, out result); - static virtual bool TryParseWholeBlocks(ReadOnlySpan input, Span destination) + static virtual bool TryParseWholeBlocks(ReadOnlySpan input, Span destination, out int blocksWritten) { Debug.Assert(destination.Length * TParser.DigitsPerBlock == input.Length); ref TChar lastWholeBlockStart = ref Unsafe.Add(ref MemoryMarshal.GetReference(input), input.Length - TParser.DigitsPerBlock); @@ -1421,10 +1445,12 @@ static virtual bool TryParseWholeBlocks(ReadOnlySpan input, Span de MemoryMarshal.CreateReadOnlySpan(ref Unsafe.Subtract(ref lastWholeBlockStart, i * TParser.DigitsPerBlock), TParser.DigitsPerBlock), out destination[i])) { + blocksWritten = i; return false; } } + blocksWritten = destination.Length; return true; } } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Number.Polyfill.cs b/src/libraries/System.Runtime.Numerics/src/System/Number.Polyfill.cs index 519cba467cf6ba..96585253cba2e9 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Number.Polyfill.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Number.Polyfill.cs @@ -84,7 +84,7 @@ internal static bool AllowHyphenDuringParsing(this NumberFormatInfo info) } [MethodImpl(MethodImplOptions.AggressiveInlining)] - internal static bool IsWhiteSpace(this ReadOnlySpan span) + internal static bool IsWhiteSpace(this ReadOnlySpan span, out int elementsConsumed) where TChar : unmanaged, IUtfChar { int elemsConsumed; @@ -93,15 +93,18 @@ internal static bool IsWhiteSpace(this ReadOnlySpan span) { if (DecodeFromUtfChar(span, out Rune rune, out elemsConsumed) != OperationStatus.Done) { + elementsConsumed = i; return false; } if (!Rune.IsWhiteSpace(rune)) { + elementsConsumed = i; return false; } } + elementsConsumed = span.Length; return true; } diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs index 2262614b9c4ee4..dc5cb74a178eca 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/BigInteger.cs @@ -668,33 +668,33 @@ public int Sign public static BigInteger Parse(string value) { - return Parse(value, NumberStyles.Integer); + return Parse(value, NumberStyles.Integer, provider: null); } public static BigInteger Parse(string value, NumberStyles style) { - return Parse(value, style, NumberFormatInfo.CurrentInfo); + return Parse(value, style, provider: null); } public static BigInteger Parse(string value, IFormatProvider? provider) { - return Parse(value, NumberStyles.Integer, NumberFormatInfo.GetInstance(provider)); + return Parse(value, NumberStyles.Integer, provider); } public static BigInteger Parse(string value, NumberStyles style, IFormatProvider? provider) { ArgumentNullException.ThrowIfNull(value); - return Parse(value.AsSpan(), style, NumberFormatInfo.GetInstance(provider)); + return Parse(value.AsSpan(), style, provider); } public static bool TryParse([NotNullWhen(true)] string? value, out BigInteger result) { - return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + return TryParse(value, NumberStyles.Integer, provider: null, out result); } public static bool TryParse([NotNullWhen(true)] string? value, NumberStyles style, IFormatProvider? provider, out BigInteger result) { - return TryParse(value.AsSpan(), style, NumberFormatInfo.GetInstance(provider), out result); + return TryParse(value.AsSpan(), style, provider, out result); } public static BigInteger Parse(ReadOnlySpan value, NumberStyles style = NumberStyles.Integer, IFormatProvider? provider = null) @@ -709,22 +709,40 @@ public static BigInteger Parse(ReadOnlySpan utf8Text, NumberStyles style = public static bool TryParse(ReadOnlySpan value, out BigInteger result) { - return TryParse(value, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + return TryParse(value, NumberStyles.Integer, provider: null, out result); } public static bool TryParse(ReadOnlySpan value, NumberStyles style, IFormatProvider? provider, out BigInteger result) { - return Number.TryParseBigInteger(MemoryMarshal.Cast(value), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBigInteger(MemoryMarshal.Cast(value), style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; } public static bool TryParse(ReadOnlySpan utf8Text, out BigInteger result) { - return TryParse(utf8Text, NumberStyles.Integer, NumberFormatInfo.CurrentInfo, out result); + return TryParse(utf8Text, NumberStyles.Integer, provider: null, out result); } public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out BigInteger result) { - return Number.TryParseBigInteger(MemoryMarshal.Cast(utf8Text), style, NumberFormatInfo.GetInstance(provider), out result) == Number.ParsingStatus.OK; + return Number.TryParseBigInteger(MemoryMarshal.Cast(utf8Text), style, NumberFormatInfo.GetInstance(provider), out result, out _) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out BigInteger result, out int charsConsumed) + { + return TryParse(s.AsSpan(), style, provider, out result, out charsConsumed); + } + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out BigInteger result, out int bytesConsumed) + { + return Number.TryParseBigInteger(MemoryMarshal.Cast(utf8Text), style, NumberFormatInfo.GetInstance(provider), out result, out bytesConsumed) == Number.ParsingStatus.OK; + } + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out BigInteger result, out int charsConsumed) + { + return Number.TryParseBigInteger(MemoryMarshal.Cast(s), style, NumberFormatInfo.GetInstance(provider), out result, out charsConsumed) == Number.ParsingStatus.OK; } public static int Compare(BigInteger left, BigInteger right) diff --git a/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs b/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs index bcbef6b29a4d5b..26b67349368a93 100644 --- a/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs +++ b/src/libraries/System.Runtime.Numerics/src/System/Numerics/Complex.cs @@ -2132,13 +2132,25 @@ static bool INumberBase.TryConvertToTruncating(Complex value, [ /// public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Complex result) - => TryParse(MemoryMarshal.Cast(s), style, provider, out result); + => TryParse(MemoryMarshal.Cast(s), style, provider, out result, out _); /// public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out Complex result) - => TryParse(MemoryMarshal.Cast(utf8Text), style, provider, out result); + => TryParse(MemoryMarshal.Cast(utf8Text), style, provider, out result, out _); - private static bool TryParse(ReadOnlySpan text, NumberStyles style, IFormatProvider? provider, out Complex result) + /// + public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out Complex result, out int charsConsumed) + => TryParse(s.AsSpan(), style, provider, out result, out charsConsumed); + + /// + public static bool TryParse(ReadOnlySpan utf8Text, NumberStyles style, IFormatProvider? provider, out Complex result, out int bytesConsumed) + => TryParse(MemoryMarshal.Cast(utf8Text), style, provider, out result, out bytesConsumed); + + /// + public static bool TryParse(ReadOnlySpan s, NumberStyles style, IFormatProvider? provider, out Complex result, out int charsConsumed) + => TryParse(MemoryMarshal.Cast(s), style, provider, out result, out charsConsumed); + + private static bool TryParse(ReadOnlySpan text, NumberStyles style, IFormatProvider? provider, out Complex result, out int elementsConsumed) where TChar : unmanaged, IUtfChar { ValidateParseStyleFloatingPoint(style); @@ -2153,15 +2165,17 @@ private static bool TryParse(ReadOnlySpan text, NumberStyles style // We also expect a to find an open bracket, a semicolon, and a closing bracket in that order result = default; + elementsConsumed = 0; return false; } - if ((openBracket != 0) && (((style & NumberStyles.AllowLeadingWhite) == 0) || !text.Slice(0, openBracket).IsWhiteSpace())) + if ((openBracket != 0) && (((style & NumberStyles.AllowLeadingWhite) == 0) || !text.Slice(0, openBracket).IsWhiteSpace(out _))) { // The opening bracket wasn't the first and we either didn't allow leading whitespace // or one of the leading characters wasn't whitespace at all. result = default; + elementsConsumed = 0; return false; } @@ -2172,6 +2186,7 @@ private static bool TryParse(ReadOnlySpan text, NumberStyles style : !double.TryParse(Unsafe.BitCast, ReadOnlySpan>(slice), style, provider, out real)) { result = default; + elementsConsumed = 0; return false; } @@ -2192,19 +2207,37 @@ private static bool TryParse(ReadOnlySpan text, NumberStyles style : !double.TryParse(Unsafe.BitCast, ReadOnlySpan>(slice), style, provider, out imaginary)) { result = default; + elementsConsumed = 0; return false; } - if ((closeBracket != (text.Length - 1)) && (((style & NumberStyles.AllowTrailingWhite) == 0) || !text.Slice(closeBracket).IsWhiteSpace())) + int trailingWhiteLength = 0; + + if (closeBracket != (text.Length - 1)) { - // The closing bracket wasn't the last and we either didn't allow trailing whitespace - // or one of the trailing characters wasn't whitespace at all. + bool isInvalid = true; - result = default; - return false; + if ((style & NumberStyles.AllowTrailingWhite) != 0) + { + if (text.Slice(closeBracket).IsWhiteSpace(out trailingWhiteLength)) + { + isInvalid = false; + } + } + + if (isInvalid && ((style & NumberStyles.AllowTrailingInvalidCharacters) == 0)) + { + // The closing bracket wasn't the last and we either didn't allow trailing whitespace + // or one of the trailing characters wasn't whitespace at all. + + result = default; + elementsConsumed = 0; + return false; + } } result = new Complex(real, imaginary); + elementsConsumed = closeBracket + trailingWhiteLength; return true; } diff --git a/src/libraries/System.Runtime.Numerics/tests/BigInteger/parse.cs b/src/libraries/System.Runtime.Numerics/tests/BigInteger/parse.cs index bdfde88395a326..586a331d69135f 100644 --- a/src/libraries/System.Runtime.Numerics/tests/BigInteger/parse.cs +++ b/src/libraries/System.Runtime.Numerics/tests/BigInteger/parse.cs @@ -1382,5 +1382,91 @@ public static void RegressionIssueRuntime94610(string text) parseTest.RegressionIssueRuntime94610(text); })); } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() + { + // Basic BigInteger parsing with trailing invalid characters + yield return new object[] { "123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, new BigInteger(123), 3 }; + yield return new object[] { "456789xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, new BigInteger(456789), 6 }; + yield return new object[] { "0abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, BigInteger.Zero, 1 }; + + // With leading whitespace + yield return new object[] { " 123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, new BigInteger(123), 5 }; + + // With signs + yield return new object[] { "+123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, new BigInteger(123), 4 }; + yield return new object[] { "-456xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, new BigInteger(-456), 4 }; + + // HexNumber with trailing invalid characters + yield return new object[] { "ABCDEFxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, BigInteger.Parse("ABCDEF", NumberStyles.HexNumber), 6 }; + yield return new object[] { "FFGHxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, new BigInteger(0xFF), 2 }; + + // BinaryNumber with trailing invalid characters + yield return new object[] { "101010abc", NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters, null, BigInteger.Parse("101010", NumberStyles.BinaryNumber), 6 }; + yield return new object[] { "1112", NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters, null, BigInteger.Parse("111", NumberStyles.BinaryNumber), 3 }; + + // Large numbers with trailing characters + yield return new object[] { "123456789012345678901234567890abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, BigInteger.Parse("123456789012345678901234567890"), 30 }; + + // Valid number without trailing characters + yield return new object[] { "123456", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, new BigInteger(123456), 6 }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_TestData))] + public static void Parse_AllowTrailingInvalidCharacters(string value, NumberStyles style, IFormatProvider provider, BigInteger expectedValue, int expectedCharsConsumed) + { + BigInteger result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.True(BigInteger.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.True(BigInteger.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test UTF-8 overload with bytesConsumed + byte[] utf8Bytes = Encoding.UTF8.GetBytes(value); + int bytesConsumed; + Assert.True(BigInteger.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out bytesConsumed)); + Assert.Equal(expectedValue, result); + // For ASCII characters, bytes consumed should equal chars consumed + if (value.All(c => c < 128)) + { + Assert.Equal(expectedCharsConsumed, bytesConsumed); + } + } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_Invalid_TestData() + { + // Empty string + yield return new object[] { "", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Only invalid characters (no valid number) + yield return new object[] { "abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + yield return new object[] { "xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_Invalid_TestData))] + public static void Parse_AllowTrailingInvalidCharacters_Invalid(string value, NumberStyles style, IFormatProvider provider) + { + BigInteger result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.False(BigInteger.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(BigInteger.Zero, result); + Assert.Equal(0, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.False(BigInteger.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(BigInteger.Zero, result); + Assert.Equal(0, charsConsumed); + } } } diff --git a/src/libraries/System.Runtime/ref/System.Runtime.cs b/src/libraries/System.Runtime/ref/System.Runtime.cs index 2d17261bf302f5..c3da18dda0c793 100644 --- a/src/libraries/System.Runtime/ref/System.Runtime.cs +++ b/src/libraries/System.Runtime/ref/System.Runtime.cs @@ -1070,6 +1070,9 @@ public static void SetByte(System.Array array, int index, byte value) { } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out byte result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out byte result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out byte result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out byte result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out byte result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out byte result, out int charsConsumed) { throw null; } } public partial class CannotUnloadAppDomainException : System.SystemException { @@ -1238,6 +1241,9 @@ public CannotUnloadAppDomainException(string? message, System.Exception? innerEx static bool System.Numerics.INumberBase.TryConvertToTruncating(char value, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TOther result) { throw null; } static bool System.Numerics.INumberBase.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out char result) { throw null; } static bool System.Numerics.INumberBase.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out char result) { throw null; } + static bool System.Numerics.INumberBase.TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out char result, out int charsConsumed) { throw null; } + static bool System.Numerics.INumberBase.TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out char result, out int bytesConsumed) { throw null; } + static bool System.Numerics.INumberBase.TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out char result, out int charsConsumed) { throw null; } static char System.Numerics.IShiftOperators.operator <<(char value, int shiftAmount) { throw null; } static char System.Numerics.IShiftOperators.operator >>(char value, int shiftAmount) { throw null; } static char System.Numerics.IShiftOperators.operator >>>(char value, int shiftAmount) { throw null; } @@ -2323,6 +2329,9 @@ void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Ser public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out decimal result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out decimal result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out decimal result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out decimal result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out decimal result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out decimal result, out int charsConsumed) { throw null; } } public abstract partial class Delegate : System.ICloneable, System.Runtime.Serialization.ISerializable { @@ -2588,6 +2597,9 @@ public DivideByZeroException(string? message, System.Exception? innerException) public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out double result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out double result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out double result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out double result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out double result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out double result, out int charsConsumed) { throw null; } } public partial class DuplicateWaitObjectException : System.ArgumentException { @@ -3452,6 +3464,9 @@ public enum GCNotificationStatus public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Half result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Half result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.Half result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Half result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Half result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Half result, out int charsConsumed) { throw null; } } public partial struct HashCode { @@ -3760,6 +3775,9 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Int128 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.Int128 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Int128 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Int128 result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Int128 result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Int128 result, out int charsConsumed) { throw null; } } public readonly partial struct Int16 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators { @@ -3900,6 +3918,9 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out short result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out short result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out short result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out short result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out short result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out short result, out int charsConsumed) { throw null; } } public readonly partial struct Int32 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators { @@ -4041,6 +4062,9 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out int result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out int result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out int result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out int result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out int result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out int result, out int charsConsumed) { throw null; } } public readonly partial struct Int64 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators { @@ -4182,6 +4206,9 @@ public InsufficientMemoryException(string? message, System.Exception? innerExcep public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out long result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out long result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out long result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out long result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out long result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out long result, out int charsConsumed) { throw null; } } public readonly partial struct IntPtr : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISignedNumber, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Runtime.Serialization.ISerializable { @@ -4330,6 +4357,9 @@ void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Ser public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out nint result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out nint result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out nint result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out nint result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out nint result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out nint result, out int charsConsumed) { throw null; } } public partial class InvalidCastException : System.SystemException { @@ -5342,6 +5372,9 @@ public void GetObjectData(System.Runtime.Serialization.SerializationInfo info, S public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out sbyte result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out sbyte result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out sbyte result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out sbyte result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out sbyte result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out sbyte result, out int charsConsumed) { throw null; } } [System.AttributeUsageAttribute(System.AttributeTargets.Class | System.AttributeTargets.Delegate | System.AttributeTargets.Enum | System.AttributeTargets.Struct, Inherited=false)] [System.ComponentModel.EditorBrowsableAttribute(System.ComponentModel.EditorBrowsableState.Never)] @@ -5553,6 +5586,9 @@ public SerializableAttribute() { } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out float result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out float result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out float result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out float result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out float result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out float result, out int charsConsumed) { throw null; } } [System.Runtime.InteropServices.Marshalling.NativeMarshallingAttribute(typeof(System.Runtime.InteropServices.Marshalling.SpanMarshaller<,>))] public readonly ref partial struct Span @@ -7049,6 +7085,9 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.UInt128 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.UInt128 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.UInt128 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.UInt128 result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.UInt128 result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.UInt128 result, out int charsConsumed) { throw null; } } [System.CLSCompliantAttribute(false)] public readonly partial struct UInt16 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber @@ -7189,6 +7228,9 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out ushort result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out ushort result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out ushort result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out ushort result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out ushort result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out ushort result, out int charsConsumed) { throw null; } } [System.CLSCompliantAttribute(false)] public readonly partial struct UInt32 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber @@ -7330,6 +7372,9 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out uint result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out uint result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out uint result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out uint result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out uint result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out uint result, out int charsConsumed) { throw null; } } [System.CLSCompliantAttribute(false)] public readonly partial struct UInt64 : System.IComparable, System.IComparable, System.IConvertible, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber @@ -7471,6 +7516,9 @@ public TypeUnloadedException(string? message, System.Exception? innerException) public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out ulong result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out ulong result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out ulong result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out ulong result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out ulong result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out ulong result, out int charsConsumed) { throw null; } } [System.CLSCompliantAttribute(false)] public readonly partial struct UIntPtr : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.IUtf8SpanFormattable, System.IUtf8SpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IBinaryInteger, System.Numerics.IBinaryNumber, System.Numerics.IBitwiseOperators, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IMinMaxValue, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumber, System.Numerics.INumberBase, System.Numerics.IShiftOperators, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators, System.Numerics.IUnsignedNumber, System.Runtime.Serialization.ISerializable @@ -7615,6 +7663,9 @@ void System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Ser public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out nuint result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out nuint result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out nuint result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out nuint result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out nuint result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out nuint result, out int charsConsumed) { throw null; } } public partial class UnauthorizedAccessException : System.SystemException { @@ -9803,6 +9854,7 @@ public enum NumberStyles HexNumber = 515, AllowBinarySpecifier = 1024, BinaryNumber = 1027, + AllowTrailingInvalidCharacters = 2048, } public partial class PersianCalendar : System.Globalization.Calendar { @@ -11491,6 +11543,9 @@ namespace System.Numerics public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.BFloat16 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.IFormatProvider? provider, out System.Numerics.BFloat16 result) { throw null; } public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, out System.Numerics.BFloat16 result) { throw null; } + public static bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.BFloat16 result, out int charsConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.BFloat16 result, out int bytesConsumed) { throw null; } + public static bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, out System.Numerics.BFloat16 result, out int charsConsumed) { throw null; } } public static partial class BitOperations { @@ -11823,6 +11878,9 @@ protected static abstract bool TryConvertToTruncating(TSelf value, [Syst static virtual bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out TSelf result) { throw null; } static abstract bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out TSelf result); static abstract bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhen(false)] out TSelf result); + static virtual bool TryParse([System.Diagnostics.CodeAnalysis.NotNullWhenAttribute(true)] string? s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TSelf result, out int charsConsumed) { throw null; } + static virtual bool TryParse(System.ReadOnlySpan utf8Text, System.Globalization.NumberStyles style, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TSelf result, out int bytesConsumed) { throw null; } + static virtual bool TryParse(System.ReadOnlySpan s, System.Globalization.NumberStyles style, System.IFormatProvider? provider, [System.Diagnostics.CodeAnalysis.MaybeNullWhenAttribute(false)] out TSelf result, out int charsConsumed) { throw null; } } public partial interface INumber : System.IComparable, System.IComparable, System.IEquatable, System.IFormattable, System.IParsable, System.ISpanFormattable, System.ISpanParsable, System.Numerics.IAdditionOperators, System.Numerics.IAdditiveIdentity, System.Numerics.IComparisonOperators, System.Numerics.IDecrementOperators, System.Numerics.IDivisionOperators, System.Numerics.IEqualityOperators, System.Numerics.IIncrementOperators, System.Numerics.IModulusOperators, System.Numerics.IMultiplicativeIdentity, System.Numerics.IMultiplyOperators, System.Numerics.INumberBase, System.Numerics.ISubtractionOperators, System.Numerics.IUnaryNegationOperators, System.Numerics.IUnaryPlusOperators where TSelf : System.Numerics.INumber? { diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.cs index d95fd2c3d8666f..f65f7d56b92459 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/ByteTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Text; using Xunit; @@ -429,5 +430,40 @@ public static void Parse_Utf8Span_InvalidUtf8() [MemberData(nameof(ToString_TestData))] public static void TryFormat(byte i, string format, IFormatProvider provider, string expected) => NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() + { + yield return new object[] { "123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (byte)123, 3 }; + yield return new object[] { "12xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (byte)12, 2 }; + yield return new object[] { "0abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (byte)0, 1 }; + yield return new object[] { "255abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (byte)255, 3 }; + yield return new object[] { "FFxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, (byte)0xFF, 2 }; + yield return new object[] { "ABGxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, (byte)0xAB, 2 }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_TestData))] + public static void Parse_AllowTrailingInvalidCharacters(string value, NumberStyles style, IFormatProvider provider, byte expectedValue, int expectedCharsConsumed) + { + byte result; + int charsConsumed; + + Assert.True(byte.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + Assert.True(byte.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + byte[] utf8Bytes = Encoding.UTF8.GetBytes(value); + int bytesConsumed; + Assert.True(byte.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out bytesConsumed)); + Assert.Equal(expectedValue, result); + if (value.All(c => c < 128)) + { + Assert.Equal(expectedCharsConsumed, bytesConsumed); + } + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DecimalTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DecimalTests.cs index 6363e20f02e313..39744f82f95a20 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DecimalTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DecimalTests.cs @@ -2444,5 +2444,93 @@ public static void TryFormat() } } } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() + { + // Basic decimal parsing with trailing invalid characters + yield return new object[] { "123.45abc", NumberStyles.Number | NumberStyles.AllowTrailingInvalidCharacters, null, 123.45m, 6 }; + yield return new object[] { "456.78xyz", NumberStyles.Number | NumberStyles.AllowTrailingInvalidCharacters, null, 456.78m, 6 }; + yield return new object[] { "0.123abc", NumberStyles.Number | NumberStyles.AllowTrailingInvalidCharacters, null, 0.123m, 5 }; + + // With leading whitespace + yield return new object[] { " 123.45abc", NumberStyles.Number | NumberStyles.AllowTrailingInvalidCharacters, null, 123.45m, 8 }; + + // With signs + yield return new object[] { "+123.45abc", NumberStyles.Number | NumberStyles.AllowTrailingInvalidCharacters, null, 123.45m, 7 }; + yield return new object[] { "-456.78xyz", NumberStyles.Number | NumberStyles.AllowTrailingInvalidCharacters, null, -456.78m, 7 }; + + // Integer without decimal point + yield return new object[] { "123abc", NumberStyles.Number | NumberStyles.AllowTrailingInvalidCharacters, null, 123m, 3 }; + + // With thousands separator + NumberFormatInfo customFormat = new NumberFormatInfo() { NumberGroupSeparator = "," }; + yield return new object[] { "1,234abc", NumberStyles.Number | NumberStyles.AllowTrailingInvalidCharacters, customFormat, 1234m, 5 }; + + // Max and min values with trailing characters + yield return new object[] { "79228162514264337593543950335abc", NumberStyles.Number | NumberStyles.AllowTrailingInvalidCharacters, null, 79228162514264337593543950335m, 29 }; + yield return new object[] { "-79228162514264337593543950335xyz", NumberStyles.Number | NumberStyles.AllowTrailingInvalidCharacters, null, -79228162514264337593543950335m, 30 }; + + // Valid number without trailing characters + yield return new object[] { "123.45", NumberStyles.Number | NumberStyles.AllowTrailingInvalidCharacters, null, 123.45m, 6 }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_TestData))] + public static void Parse_AllowTrailingInvalidCharacters(string value, NumberStyles style, IFormatProvider provider, decimal expectedValue, int expectedCharsConsumed) + { + decimal result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.True(decimal.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.True(decimal.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test UTF-8 overload with bytesConsumed + byte[] utf8Bytes = Encoding.UTF8.GetBytes(value); + int bytesConsumed; + Assert.True(decimal.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out bytesConsumed)); + Assert.Equal(expectedValue, result); + // For ASCII characters, bytes consumed should equal chars consumed + if (value.All(c => c < 128)) + { + Assert.Equal(expectedCharsConsumed, bytesConsumed); + } + } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_Invalid_TestData() + { + // Empty string + yield return new object[] { "", NumberStyles.Number | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Only invalid characters (no valid number) + yield return new object[] { "abc", NumberStyles.Number | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Overflow + yield return new object[] { "79228162514264337593543950336abc", NumberStyles.Number | NumberStyles.AllowTrailingInvalidCharacters, null }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_Invalid_TestData))] + public static void Parse_AllowTrailingInvalidCharacters_Invalid(string value, NumberStyles style, IFormatProvider provider) + { + decimal result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.False(decimal.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(0m, result); + Assert.Equal(0, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.False(decimal.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(0m, result); + Assert.Equal(0, charsConsumed); + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DoubleTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DoubleTests.cs index ac1bf4abf4e863..0a5690bfde68b8 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DoubleTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/DoubleTests.cs @@ -1536,5 +1536,120 @@ public static void RadiansToDegreesTest(double value, double expectedResult, dou AssertExtensions.Equal(-expectedResult, double.RadiansToDegrees(-value), allowedVariance); AssertExtensions.Equal(+expectedResult, double.RadiansToDegrees(+value), allowedVariance); } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() + { + // Basic floating point parsing with trailing invalid characters + yield return new object[] { "123.45abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 123.45, 6 }; + yield return new object[] { "456.78xyz", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 456.78, 6 }; + yield return new object[] { "0.123abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 0.123, 5 }; + + // With leading whitespace + yield return new object[] { " 123.45abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 123.45, 8 }; + + // With signs + yield return new object[] { "+123.45abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 123.45, 7 }; + yield return new object[] { "-456.78xyz", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, -456.78, 7 }; + + // With exponent + yield return new object[] { "1.23e10abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 1.23e10, 7 }; + yield return new object[] { "4.56E-5xyz", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 4.56E-5, 7 }; + yield return new object[] { "1E10abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 1E10, 4 }; + + // Integer without decimal point + yield return new object[] { "123abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 123.0, 3 }; + yield return new object[] { "456xyz", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 456.0, 3 }; + + // Special values + yield return new object[] { "Infinityabc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, double.PositiveInfinity, 8 }; + yield return new object[] { "-Infinityxyz", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, double.NegativeInfinity, 9 }; + yield return new object[] { "NaNabc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, double.NaN, 3 }; + + // Valid number without trailing characters + yield return new object[] { "123.45", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 123.45, 6 }; + + // Stop at invalid exponent + yield return new object[] { "123eabc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 123.0, 3 }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_TestData))] + public static void Parse_AllowTrailingInvalidCharacters(string value, NumberStyles style, IFormatProvider provider, double expectedValue, int expectedCharsConsumed) + { + double result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.True(double.TryParse(value, style, provider, out result, out charsConsumed)); + if (double.IsNaN(expectedValue)) + { + Assert.True(double.IsNaN(result)); + } + else + { + Assert.Equal(expectedValue, result); + } + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.True(double.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + if (double.IsNaN(expectedValue)) + { + Assert.True(double.IsNaN(result)); + } + else + { + Assert.Equal(expectedValue, result); + } + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test UTF-8 overload with bytesConsumed + byte[] utf8Bytes = Encoding.UTF8.GetBytes(value); + int bytesConsumed; + Assert.True(double.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out bytesConsumed)); + if (double.IsNaN(expectedValue)) + { + Assert.True(double.IsNaN(result)); + } + else + { + Assert.Equal(expectedValue, result); + } + // For ASCII characters, bytes consumed should equal chars consumed + if (value.All(c => c < 128)) + { + Assert.Equal(expectedCharsConsumed, bytesConsumed); + } + } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_Invalid_TestData() + { + // Empty string + yield return new object[] { "", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Only invalid characters (no valid number) + yield return new object[] { "abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Invalid start characters + yield return new object[] { ".abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_Invalid_TestData))] + public static void Parse_AllowTrailingInvalidCharacters_Invalid(string value, NumberStyles style, IFormatProvider provider) + { + double result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.False(double.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(0.0, result); + Assert.Equal(0, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.False(double.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(0.0, result); + Assert.Equal(0, charsConsumed); + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs index 62a8d831e9fccd..708bc76f14e677 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/HalfTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Dynamic; using System.Globalization; +using System.Linq; using System.Text; using Xunit; @@ -2269,5 +2270,108 @@ public static void ILogB_Subnormal() Half subnormal = BitConverter.UInt16BitsToHalf(0x0200); Assert.Equal(-15, Half.ILogB(subnormal)); } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() + { + // Basic Half parsing with trailing invalid characters + yield return new object[] { "123.45abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, (Half)123.45, 6 }; + yield return new object[] { "1.5xyz", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, (Half)1.5, 3 }; + yield return new object[] { "0.123abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, (Half)0.123, 5 }; + + // With leading whitespace + yield return new object[] { " 12.5abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, (Half)12.5, 6 }; + + // With signs + yield return new object[] { "+12.5abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, (Half)12.5, 5 }; + yield return new object[] { "-45.5xyz", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, (Half)(-45.5), 5 }; + + // With exponent + yield return new object[] { "1.5e2abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, (Half)1.5e2, 5 }; + + // Special values + yield return new object[] { "Infinityabc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, Half.PositiveInfinity, 8 }; + yield return new object[] { "-Infinityxyz", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, Half.NegativeInfinity, 9 }; + yield return new object[] { "NaNabc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, Half.NaN, 3 }; + + // Valid number without trailing characters + yield return new object[] { "123.45", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, (Half)123.45, 6 }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_TestData))] + public static void Parse_AllowTrailingInvalidCharacters(string value, NumberStyles style, IFormatProvider provider, Half expectedValue, int expectedCharsConsumed) + { + Half result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.True(Half.TryParse(value, style, provider, out result, out charsConsumed)); + if (Half.IsNaN(expectedValue)) + { + Assert.True(Half.IsNaN(result)); + } + else + { + Assert.Equal(expectedValue, result); + } + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.True(Half.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + if (Half.IsNaN(expectedValue)) + { + Assert.True(Half.IsNaN(result)); + } + else + { + Assert.Equal(expectedValue, result); + } + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test UTF-8 overload with bytesConsumed + byte[] utf8Bytes = Encoding.UTF8.GetBytes(value); + int bytesConsumed; + Assert.True(Half.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out bytesConsumed)); + if (Half.IsNaN(expectedValue)) + { + Assert.True(Half.IsNaN(result)); + } + else + { + Assert.Equal(expectedValue, result); + } + // For ASCII characters, bytes consumed should equal chars consumed + if (value.All(c => c < 128)) + { + Assert.Equal(expectedCharsConsumed, bytesConsumed); + } + } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_Invalid_TestData() + { + // Empty string + yield return new object[] { "", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Only invalid characters (no valid number) + yield return new object[] { "abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_Invalid_TestData))] + public static void Parse_AllowTrailingInvalidCharacters_Invalid(string value, NumberStyles style, IFormatProvider provider) + { + Half result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.False(Half.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal((Half)0.0, result); + Assert.Equal(0, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.False(Half.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal((Half)0.0, result); + Assert.Equal(0, charsConsumed); + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.cs index 4601ac9dad7965..45fb635fd859f3 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int16Tests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Text; using Xunit; @@ -454,5 +455,40 @@ public static void Parse_Utf8Span_InvalidUtf8() [MemberData(nameof(ToString_TestData))] public static void TryFormat(short i, string format, IFormatProvider provider, string expected) => NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() + { + yield return new object[] { "123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (short)123, 3 }; + yield return new object[] { "1234xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (short)1234, 4 }; + yield return new object[] { "-32768abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (short)-32768, 6 }; + yield return new object[] { "32767xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (short)32767, 5 }; + yield return new object[] { "ABCxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, (short)0xABC, 3 }; + yield return new object[] { "7FFFabc", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, (short)0x7FFF, 4 }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_TestData))] + public static void Parse_AllowTrailingInvalidCharacters(string value, NumberStyles style, IFormatProvider provider, short expectedValue, int expectedCharsConsumed) + { + short result; + int charsConsumed; + + Assert.True(short.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + Assert.True(short.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + byte[] utf8Bytes = Encoding.UTF8.GetBytes(value); + int bytesConsumed; + Assert.True(short.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out bytesConsumed)); + Assert.Equal(expectedValue, result); + if (value.All(c => c < 128)) + { + Assert.Equal(expectedCharsConsumed, bytesConsumed); + } + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.cs index d1b9c154aefc97..d481c3b05c5a5e 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int32Tests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Text; using Xunit; @@ -988,5 +989,162 @@ public static void TestParseLenientData() Assert.Equal(-9670, int.Parse("-9670", NumberStyles.Integer, nfi)); } } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() + { + // Basic integer parsing with trailing invalid characters + yield return new object[] { "123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123, 3 }; + yield return new object[] { "456xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 456, 3 }; + yield return new object[] { "789!", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 789, 3 }; + yield return new object[] { "0@#$", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 0, 1 }; + + // With leading whitespace + yield return new object[] { " 123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123, 5 }; + yield return new object[] { "\t456xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 456, 4 }; + + // With signs + yield return new object[] { "+123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123, 4 }; + yield return new object[] { "-456xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, -456, 4 }; + yield return new object[] { "+0xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 0, 2 }; + yield return new object[] { "-0xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 0, 2 }; + + // HexNumber with trailing invalid characters + yield return new object[] { "ABCxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0xABC, 3 }; + yield return new object[] { "FFGHxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0xFF, 2 }; + yield return new object[] { "123ZZZ", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0x123, 3 }; + yield return new object[] { "0GHI", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0x0, 1 }; + yield return new object[] { " ABCxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0xABC, 5 }; + + // BinaryNumber with trailing invalid characters + yield return new object[] { "101abc", NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0b101, 3 }; + yield return new object[] { "1112", NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0b111, 3 }; + yield return new object[] { "110xyz", NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0b110, 3 }; + yield return new object[] { "0abc", NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0b0, 1 }; + yield return new object[] { " 101abc", NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0b101, 5 }; + + // Number style with decimal point (should stop at decimal for integers) + yield return new object[] { "123.45", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123, 3 }; + yield return new object[] { "456.xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 456, 3 }; + + // With thousands separator in invalid position + NumberFormatInfo customFormat = new NumberFormatInfo() { NumberGroupSeparator = "," }; + yield return new object[] { "123,456xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, customFormat, 123, 3 }; + + // Currency style with trailing invalid characters + NumberFormatInfo currencyFormat = new NumberFormatInfo() { CurrencySymbol = "$", CurrencyGroupSeparator = "," }; + yield return new object[] { "$123abc", NumberStyles.Currency | NumberStyles.AllowTrailingInvalidCharacters, currencyFormat, 123, 4 }; + yield return new object[] { "$1,234xyz", NumberStyles.Currency | NumberStyles.AllowTrailingInvalidCharacters, currencyFormat, 1234, 6 }; + yield return new object[] { "($123)abc", NumberStyles.Currency | NumberStyles.AllowTrailingInvalidCharacters, currencyFormat, -123, 6 }; + + // Max and min values with trailing characters + yield return new object[] { "2147483647abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 2147483647, 10 }; + yield return new object[] { "-2147483648xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, -2147483648, 11 }; + + // Edge case: only invalid characters (should fail) + // Edge case: valid number without trailing characters (should still work) + yield return new object[] { "123", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123, 3 }; + yield return new object[] { "456", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 456, 3 }; + + // Multiple styles combined + yield return new object[] { " +123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123, 6 }; + yield return new object[] { " -456xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, -456, 6 }; + + // Trailing whitespace is not considered "invalid" in the traditional sense + // but should be handled when AllowTrailingWhite is not set + yield return new object[] { "123 ", NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingInvalidCharacters, null, 123, 3 }; + yield return new object[] { "123\t", NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingInvalidCharacters, null, 123, 3 }; + + // Stop at null character + yield return new object[] { "123\0abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123, 3 }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_TestData))] + public static void Parse_AllowTrailingInvalidCharacters(string value, NumberStyles style, IFormatProvider provider, int expectedValue, int expectedCharsConsumed) + { + int result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.True(int.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.True(int.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test UTF-8 overload with bytesConsumed + byte[] utf8Bytes = Encoding.UTF8.GetBytes(value); + int bytesConsumed; + Assert.True(int.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out bytesConsumed)); + Assert.Equal(expectedValue, result); + // For ASCII characters, bytes consumed should equal chars consumed + if (value.All(c => c < 128)) + { + Assert.Equal(expectedCharsConsumed, bytesConsumed); + } + } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_Invalid_TestData() + { + // Empty string + yield return new object[] { "", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Only whitespace + yield return new object[] { " ", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Only invalid characters (no valid number) + yield return new object[] { "abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + yield return new object[] { "xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + yield return new object[] { "!!!", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Invalid start characters + yield return new object[] { ".123", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + yield return new object[] { ",123", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Sign not allowed without AllowLeadingSign + yield return new object[] { "+123abc", NumberStyles.None | NumberStyles.AllowTrailingInvalidCharacters, null }; + yield return new object[] { "-123abc", NumberStyles.None | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Overflow + yield return new object[] { "2147483648abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + yield return new object[] { "-2147483649xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + yield return new object[] { "9999999999abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Invalid hex characters at start + yield return new object[] { "Gxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null }; + yield return new object[] { "Zabc", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Invalid binary characters at start + yield return new object[] { "2abc", NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters, null }; + yield return new object[] { "9xyz", NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters, null }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_Invalid_TestData))] + public static void Parse_AllowTrailingInvalidCharacters_Invalid(string value, NumberStyles style, IFormatProvider provider) + { + int result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.False(int.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(0, result); + Assert.Equal(0, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.False(int.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(0, result); + Assert.Equal(0, charsConsumed); + + // Test UTF-8 overload with bytesConsumed + byte[] utf8Bytes = Encoding.UTF8.GetBytes(value); + int bytesConsumed; + Assert.False(int.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out bytesConsumed)); + Assert.Equal(0, result); + Assert.Equal(0, bytesConsumed); + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.cs index 7e755afd985834..e49f0a880bac0b 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/Int64Tests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Numerics; using System.Text; using Xunit; @@ -489,5 +490,95 @@ public static void TestNegativeNumberParsingWithHyphen() CultureInfo ci = CultureInfo.GetCultureInfo("sv-SE"); Assert.Equal(-15868, long.Parse("-15868", NumberStyles.Number, ci)); } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() + { + // Basic integer parsing with trailing invalid characters + yield return new object[] { "123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123L, 3 }; + yield return new object[] { "456xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 456L, 3 }; + yield return new object[] { "789!", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 789L, 3 }; + + // With leading whitespace + yield return new object[] { " 123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123L, 5 }; + + // With signs + yield return new object[] { "+123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123L, 4 }; + yield return new object[] { "-456xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, -456L, 4 }; + + // HexNumber with trailing invalid characters + yield return new object[] { "ABCxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0xABCL, 3 }; + yield return new object[] { "FFGHxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0xFFL, 2 }; + + // BinaryNumber with trailing invalid characters + yield return new object[] { "101abc", NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0b101L, 3 }; + yield return new object[] { "1112", NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0b111L, 3 }; + + // Max and min values with trailing characters + yield return new object[] { "9223372036854775807abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 9223372036854775807L, 19 }; + yield return new object[] { "-9223372036854775808xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, -9223372036854775808L, 20 }; + + // Valid number without trailing characters + yield return new object[] { "123", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123L, 3 }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_TestData))] + public static void Parse_AllowTrailingInvalidCharacters(string value, NumberStyles style, IFormatProvider provider, long expectedValue, int expectedCharsConsumed) + { + long result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.True(long.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.True(long.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test UTF-8 overload with bytesConsumed + byte[] utf8Bytes = Encoding.UTF8.GetBytes(value); + int bytesConsumed; + Assert.True(long.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out bytesConsumed)); + Assert.Equal(expectedValue, result); + // For ASCII characters, bytes consumed should equal chars consumed + if (value.All(c => c < 128)) + { + Assert.Equal(expectedCharsConsumed, bytesConsumed); + } + } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_Invalid_TestData() + { + // Empty string + yield return new object[] { "", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Only invalid characters (no valid number) + yield return new object[] { "abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Overflow + yield return new object[] { "9223372036854775808abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + yield return new object[] { "-9223372036854775809xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_Invalid_TestData))] + public static void Parse_AllowTrailingInvalidCharacters_Invalid(string value, NumberStyles style, IFormatProvider provider) + { + long result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.False(long.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(0, result); + Assert.Equal(0, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.False(long.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(0, result); + Assert.Equal(0, charsConsumed); + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.cs index effc332bc5b93d..37863da770149e 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SByteTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Text; using Xunit; @@ -448,5 +449,40 @@ public static void Parse_Utf8Span_InvalidUtf8() [MemberData(nameof(ToString_TestData))] public static void TryFormat(sbyte i, string format, IFormatProvider provider, string expected) => NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() + { + yield return new object[] { "123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (sbyte)123, 3 }; + yield return new object[] { "12xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (sbyte)12, 2 }; + yield return new object[] { "-123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (sbyte)-123, 4 }; + yield return new object[] { "+127xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (sbyte)127, 4 }; + yield return new object[] { "-128abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (sbyte)-128, 4 }; + yield return new object[] { "7Fxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, (sbyte)0x7F, 2 }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_TestData))] + public static void Parse_AllowTrailingInvalidCharacters(string value, NumberStyles style, IFormatProvider provider, sbyte expectedValue, int expectedCharsConsumed) + { + sbyte result; + int charsConsumed; + + Assert.True(sbyte.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + Assert.True(sbyte.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + byte[] utf8Bytes = Encoding.UTF8.GetBytes(value); + int bytesConsumed; + Assert.True(sbyte.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out bytesConsumed)); + Assert.Equal(expectedValue, result); + if (value.All(c => c < 128)) + { + Assert.Equal(expectedCharsConsumed, bytesConsumed); + } + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SingleTests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SingleTests.cs index fc8f547f15ea45..60b2de97ead19d 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SingleTests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/SingleTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Text; using Xunit; @@ -1448,5 +1449,109 @@ public static void RadiansToDegreesTest(float value, float expectedResult, float AssertExtensions.Equal(-expectedResult, float.RadiansToDegrees(-value), allowedVariance); AssertExtensions.Equal(+expectedResult, float.RadiansToDegrees(+value), allowedVariance); } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() + { + // Basic floating point parsing with trailing invalid characters + yield return new object[] { "123.45abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 123.45f, 6 }; + yield return new object[] { "456.78xyz", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 456.78f, 6 }; + yield return new object[] { "0.123abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 0.123f, 5 }; + + // With leading whitespace + yield return new object[] { " 123.45abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 123.45f, 8 }; + + // With signs + yield return new object[] { "+123.45abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 123.45f, 7 }; + yield return new object[] { "-456.78xyz", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, -456.78f, 7 }; + + // With exponent + yield return new object[] { "1.23e10abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 1.23e10f, 7 }; + yield return new object[] { "4.56E-5xyz", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 4.56E-5f, 7 }; + + // Special values + yield return new object[] { "Infinityabc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, float.PositiveInfinity, 8 }; + yield return new object[] { "-Infinityxyz", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, float.NegativeInfinity, 9 }; + yield return new object[] { "NaNabc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, float.NaN, 3 }; + + // Valid number without trailing characters + yield return new object[] { "123.45", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null, 123.45f, 6 }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_TestData))] + public static void Parse_AllowTrailingInvalidCharacters(string value, NumberStyles style, IFormatProvider provider, float expectedValue, int expectedCharsConsumed) + { + float result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.True(float.TryParse(value, style, provider, out result, out charsConsumed)); + if (float.IsNaN(expectedValue)) + { + Assert.True(float.IsNaN(result)); + } + else + { + Assert.Equal(expectedValue, result); + } + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.True(float.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + if (float.IsNaN(expectedValue)) + { + Assert.True(float.IsNaN(result)); + } + else + { + Assert.Equal(expectedValue, result); + } + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test UTF-8 overload with bytesConsumed + byte[] utf8Bytes = Encoding.UTF8.GetBytes(value); + int bytesConsumed; + Assert.True(float.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out bytesConsumed)); + if (float.IsNaN(expectedValue)) + { + Assert.True(float.IsNaN(result)); + } + else + { + Assert.Equal(expectedValue, result); + } + // For ASCII characters, bytes consumed should equal chars consumed + if (value.All(c => c < 128)) + { + Assert.Equal(expectedCharsConsumed, bytesConsumed); + } + } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_Invalid_TestData() + { + // Empty string + yield return new object[] { "", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Only invalid characters (no valid number) + yield return new object[] { "abc", NumberStyles.Float | NumberStyles.AllowTrailingInvalidCharacters, null }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_Invalid_TestData))] + public static void Parse_AllowTrailingInvalidCharacters_Invalid(string value, NumberStyles style, IFormatProvider provider) + { + float result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.False(float.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(0.0f, result); + Assert.Equal(0, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.False(float.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(0.0f, result); + Assert.Equal(0, charsConsumed); + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.cs index bc5ffd25435887..bc0e21dbe5bdad 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt16Tests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Text; using Xunit; @@ -426,5 +427,39 @@ public static void Parse_Utf8Span_InvalidUtf8() [MemberData(nameof(ToString_TestData))] public static void TryFormat(ushort i, string format, IFormatProvider provider, string expected) => NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() + { + yield return new object[] { "123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (ushort)123, 3 }; + yield return new object[] { "1234xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (ushort)1234, 4 }; + yield return new object[] { "65535abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, (ushort)65535, 5 }; + yield return new object[] { "ABCxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, (ushort)0xABC, 3 }; + yield return new object[] { "FFFFabc", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, (ushort)0xFFFF, 4 }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_TestData))] + public static void Parse_AllowTrailingInvalidCharacters(string value, NumberStyles style, IFormatProvider provider, ushort expectedValue, int expectedCharsConsumed) + { + ushort result; + int charsConsumed; + + Assert.True(ushort.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + Assert.True(ushort.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + byte[] utf8Bytes = Encoding.UTF8.GetBytes(value); + int bytesConsumed; + Assert.True(ushort.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out bytesConsumed)); + Assert.Equal(expectedValue, result); + if (value.All(c => c < 128)) + { + Assert.Equal(expectedCharsConsumed, bytesConsumed); + } + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.cs index ade9fe7664bec6..d3f995c96b51bb 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt32Tests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Numerics; using System.Text; using Xunit; @@ -456,5 +457,89 @@ public static void Parse_Utf8Span_InvalidUtf8() [MemberData(nameof(ToString_TestData))] public static void TryFormat(uint i, string format, IFormatProvider provider, string expected) => NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() + { + // Basic unsigned integer parsing with trailing invalid characters + yield return new object[] { "123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123u, 3 }; + yield return new object[] { "456xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 456u, 3 }; + yield return new object[] { "0abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 0u, 1 }; + + // With leading whitespace + yield return new object[] { " 123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123u, 5 }; + + // HexNumber with trailing invalid characters + yield return new object[] { "ABCxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0xABCu, 3 }; + yield return new object[] { "FFGHxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0xFFu, 2 }; + + // BinaryNumber with trailing invalid characters + yield return new object[] { "101abc", NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0b101u, 3 }; + yield return new object[] { "1112", NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0b111u, 3 }; + + // Max value with trailing characters + yield return new object[] { "4294967295abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 4294967295u, 10 }; + + // Valid number without trailing characters + yield return new object[] { "123", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123u, 3 }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_TestData))] + public static void Parse_AllowTrailingInvalidCharacters(string value, NumberStyles style, IFormatProvider provider, uint expectedValue, int expectedCharsConsumed) + { + uint result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.True(uint.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.True(uint.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test UTF-8 overload with bytesConsumed + byte[] utf8Bytes = Encoding.UTF8.GetBytes(value); + int bytesConsumed; + Assert.True(uint.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out bytesConsumed)); + Assert.Equal(expectedValue, result); + // For ASCII characters, bytes consumed should equal chars consumed + if (value.All(c => c < 128)) + { + Assert.Equal(expectedCharsConsumed, bytesConsumed); + } + } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_Invalid_TestData() + { + // Empty string + yield return new object[] { "", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Only invalid characters (no valid number) + yield return new object[] { "abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Overflow + yield return new object[] { "4294967296abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_Invalid_TestData))] + public static void Parse_AllowTrailingInvalidCharacters_Invalid(string value, NumberStyles style, IFormatProvider provider) + { + uint result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.False(uint.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(0u, result); + Assert.Equal(0, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.False(uint.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(0u, result); + Assert.Equal(0, charsConsumed); + } } } diff --git a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.cs b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.cs index 2ef2bc24f5a3ef..37c0bbfd22aafb 100644 --- a/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.cs +++ b/src/libraries/System.Runtime/tests/System.Runtime.Tests/System/UInt64Tests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Globalization; +using System.Linq; using System.Numerics; using System.Text; using Xunit; @@ -468,5 +469,89 @@ public static void Parse_Utf8Span_InvalidUtf8() [MemberData(nameof(ToString_TestData))] public static void TryFormat(ulong i, string format, IFormatProvider provider, string expected) => NumberFormatTestHelper.TryFormatNumberTest(i, format, provider, expected); + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_TestData() + { + // Basic unsigned long parsing with trailing invalid characters + yield return new object[] { "123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123ul, 3 }; + yield return new object[] { "456xyz", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 456ul, 3 }; + yield return new object[] { "0abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 0ul, 1 }; + + // With leading whitespace + yield return new object[] { " 123abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123ul, 5 }; + + // HexNumber with trailing invalid characters + yield return new object[] { "ABCxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0xABCul, 3 }; + yield return new object[] { "FFGHxyz", NumberStyles.HexNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0xFFul, 2 }; + + // BinaryNumber with trailing invalid characters + yield return new object[] { "101abc", NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0b101ul, 3 }; + yield return new object[] { "1112", NumberStyles.BinaryNumber | NumberStyles.AllowTrailingInvalidCharacters, null, 0b111ul, 3 }; + + // Max value with trailing characters + yield return new object[] { "18446744073709551615abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 18446744073709551615ul, 20 }; + + // Valid number without trailing characters + yield return new object[] { "123", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null, 123ul, 3 }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_TestData))] + public static void Parse_AllowTrailingInvalidCharacters(string value, NumberStyles style, IFormatProvider provider, ulong expectedValue, int expectedCharsConsumed) + { + ulong result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.True(ulong.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.True(ulong.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(expectedValue, result); + Assert.Equal(expectedCharsConsumed, charsConsumed); + + // Test UTF-8 overload with bytesConsumed + byte[] utf8Bytes = Encoding.UTF8.GetBytes(value); + int bytesConsumed; + Assert.True(ulong.TryParse(utf8Bytes.AsSpan(), style, provider, out result, out bytesConsumed)); + Assert.Equal(expectedValue, result); + // For ASCII characters, bytes consumed should equal chars consumed + if (value.All(c => c < 128)) + { + Assert.Equal(expectedCharsConsumed, bytesConsumed); + } + } + + public static IEnumerable Parse_AllowTrailingInvalidCharacters_Invalid_TestData() + { + // Empty string + yield return new object[] { "", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Only invalid characters (no valid number) + yield return new object[] { "abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + + // Overflow + yield return new object[] { "18446744073709551616abc", NumberStyles.Integer | NumberStyles.AllowTrailingInvalidCharacters, null }; + } + + [Theory] + [MemberData(nameof(Parse_AllowTrailingInvalidCharacters_Invalid_TestData))] + public static void Parse_AllowTrailingInvalidCharacters_Invalid(string value, NumberStyles style, IFormatProvider provider) + { + ulong result; + int charsConsumed; + + // Test string overload with charsConsumed + Assert.False(ulong.TryParse(value, style, provider, out result, out charsConsumed)); + Assert.Equal(0ul, result); + Assert.Equal(0, charsConsumed); + + // Test ReadOnlySpan overload with charsConsumed + Assert.False(ulong.TryParse(value.AsSpan(), style, provider, out result, out charsConsumed)); + Assert.Equal(0ul, result); + Assert.Equal(0, charsConsumed); + } } }