Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/libraries/Common/src/System/Number.Parsing.Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -61,9 +61,9 @@ private static bool TryParseNumber<TChar>(ReadOnlySpan<TChar> value, NumberStyle

while (true)
{
// Eat whitespace unless we've found a sign which isn't followed by a currency symbol.
// "-Kr 1231.47" is legal but "- 1231.47" is not.
if (!IsWhite(ch) || (styles & NumberStyles.AllowLeadingWhite) == 0 || ((state & StateSign) != 0 && (state & StateCurrency) == 0 && info.NumberNegativePattern != 2))
// Eat leading whitespace. This mirrors the trailing loop below so that whitespace is
// allowed after a leading sign or opening parenthesis (e.g. "- 1231.47" and "( 1)").
if (!IsWhite(ch) || (styles & NumberStyles.AllowLeadingWhite) == 0)
{
int nextIndex;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ public enum NumberStyles
None = 0x00000000,

/// <summary>
/// Bit flag indicating that leading whitespace is allowed. Character values
/// 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, and 0x0020 are considered to be
/// whitespace.
/// Indicates that whitespace is allowed before the number, including after a permitted
/// leading sign or opening parenthesis. Character values 0x0009, 0x000A, 0x000B, 0x000C,
/// 0x000D, and 0x0020 are considered to be whitespace.
/// </summary>
AllowLeadingWhite = 0x00000001,

Expand Down
15 changes: 15 additions & 0 deletions src/libraries/System.Private.CoreLib/src/System/Number.Parsing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -529,6 +529,12 @@ internal static ParsingStatus TryParseBinaryIntegerStyle<TChar, TInteger>(ReadOn
}
goto HasTrailingChars;
}

// Leading whitespace was already consumed, so whitespace here can only follow a sign.
if ((styles & NumberStyles.AllowLeadingWhite) != 0 && IsWhite(num))
{
return TryParseBinaryIntegerNumber(value, styles, info, out result, out elementsConsumed);
}
goto FalseExit;

DoneAtEndButPotentialOverflow:
Expand Down Expand Up @@ -1252,6 +1258,15 @@ internal static bool TryParseHexFloatingPoint<TChar, TFloat>(ReadOnlySpan<TChar>
}
}

// Skip whitespace between the sign and the "0x" prefix, mirroring TryParseNumber.
if ((styles & NumberStyles.AllowLeadingWhite) != 0)
{
while (index < value.Length && IsWhite(TChar.CastToUInt32(value[index])))
{
index++;
}
}

if (index >= value.Length)
{
elementsConsumed = 0;
Expand Down
14 changes: 14 additions & 0 deletions src/libraries/System.Runtime.Numerics/tests/BigInteger/parse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,20 @@ public static void Parse_EmptySubspan_Fails()
Assert.Equal(0, result);
}

[Fact]
public static void Parse_WhitespaceAfterLeadingSign()
{
NumberStyles style = NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite | NumberStyles.AllowLeadingSign;
CultureInfo inv = CultureInfo.InvariantCulture;

Assert.Equal(new BigInteger(-123), BigInteger.Parse("- 123", style, inv));
Assert.Equal(new BigInteger(123), BigInteger.Parse("+ 123", style, inv));
Assert.Equal(new BigInteger(-123), BigInteger.Parse(" - 123 ", style, inv));

// Without AllowLeadingWhite the interior whitespace must still be rejected.
Assert.Throws<FormatException>(() => BigInteger.Parse("- 123", NumberStyles.AllowLeadingSign, inv));
}

[Fact]
public static void ParseUtf8_EmptySubspan_Fails()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -864,6 +864,10 @@ public static IEnumerable<object[]> Parse_Valid_TestData()
yield return new object[] { "0", defaultStyle, null, 0m };
yield return new object[] { "123", defaultStyle, null, 123m };
yield return new object[] { " 123 ", defaultStyle, null, 123m };

// Whitespace between a leading sign and the digits (mirrors trailing sign + AllowTrailingWhite)
yield return new object[] { "- 123", NumberStyles.AllowLeadingWhite | NumberStyles.AllowLeadingSign, invariantFormat, -123m };
yield return new object[] { " - 123 ", NumberStyles.Number, invariantFormat, -123m };
yield return new object[] { (567.89m).ToString(), defaultStyle, null, 567.89m };
yield return new object[] { (-567.89m).ToString(), defaultStyle, null, -567.89m };
yield return new object[] { "0.6666666666666666666666666666500000000000000000000000000000000000000000000000000000000000000", defaultStyle, invariantFormat, 0.6666666666666666666666666666m };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@ public static IEnumerable<object[]> Parse_Valid_TestData()
yield return new object[] { "0", defaultStyle, null, 0.0 };
yield return new object[] { "123", defaultStyle, null, 123.0 };
yield return new object[] { " 123 ", defaultStyle, null, 123.0 };

// Whitespace between a leading sign and the digits (mirrors trailing sign + AllowTrailingWhite)
yield return new object[] { "- 123", NumberStyles.AllowLeadingWhite | NumberStyles.AllowLeadingSign, invariantFormat, -123.0 };
yield return new object[] { " - 123 ", NumberStyles.Float, invariantFormat, -123.0 };
yield return new object[] { (567.89).ToString(), defaultStyle, null, 567.89 };
yield return new object[] { (-567.89).ToString(), defaultStyle, null, -567.89 };
yield return new object[] { "1E23", defaultStyle, null, 1E23 };
Expand Down Expand Up @@ -468,6 +472,11 @@ public static IEnumerable<object[]> Parse_Valid_TestData()
yield return new object[] { "0x1p0", NumberStyles.HexFloat, invariantFormat, 1.0 };
yield return new object[] { "0x100p0", NumberStyles.HexFloat, invariantFormat, 256.0 };

// Whitespace between a leading sign and the "0x" prefix (AllowLeadingWhite)
yield return new object[] { "- 0x1p0", NumberStyles.HexFloat, invariantFormat, -1.0 };
yield return new object[] { " - 0x1p0 ", NumberStyles.HexFloat, invariantFormat, -1.0 };
yield return new object[] { "+ 0x1p0", NumberStyles.HexFloat, invariantFormat, 1.0 };

// Large significand (many hex digits)
yield return new object[] { "0xFFFFFFFFFFFFFFp0", NumberStyles.HexFloat, invariantFormat, (double)0xFFFFFFFFFFFFFF };
yield return new object[] { "0x1.0000000000000p0", NumberStyles.HexFloat, invariantFormat, 1.0 };
Expand Down Expand Up @@ -2017,6 +2026,7 @@ public static IEnumerable<object[]> TryParsePartial_TestData()
// With signs
yield return new object[] { "+123.45abc", NumberStyles.Float, CultureInfo.InvariantCulture, 123.45, 7 };
yield return new object[] { "-456.78xyz", NumberStyles.Float, CultureInfo.InvariantCulture, -456.78, 7 };
yield return new object[] { " - 123.5xyz", NumberStyles.Float, CultureInfo.InvariantCulture, -123.5, 10 };

// With exponent
yield return new object[] { "1.23e10abc", NumberStyles.Float, CultureInfo.InvariantCulture, 1.23e10, 7 };
Expand Down Expand Up @@ -2046,6 +2056,7 @@ public static IEnumerable<object[]> TryParsePartial_TestData()
yield return new object[] { "0x1.8p0xyz", NumberStyles.HexFloat, CultureInfo.InvariantCulture, 1.5, 7 };
yield return new object[] { "0x1.0p10!!", NumberStyles.HexFloat, CultureInfo.InvariantCulture, 1024.0, 8 };
yield return new object[] { "0x1.8p0 x", NumberStyles.HexFloat, CultureInfo.InvariantCulture, 1.5, 9 };
yield return new object[] { "- 0x1.8p0xyz", NumberStyles.HexFloat, CultureInfo.InvariantCulture, -1.5, 9 };

// Hex-float special values (Infinity/NaN) with trailing invalid characters
yield return new object[] { "Infinityxyz", NumberStyles.HexFloat, CultureInfo.InvariantCulture, double.PositiveInfinity, 8 };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,20 @@ public static IEnumerable<object[]> Parse_Valid_TestData()
yield return new object[] { " 0 ", NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, null, 0 };
yield return new object[] { " 000000000 ", NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite, null, 0 };

// Whitespace between a leading sign and the digits (mirrors trailing sign + AllowTrailingWhite)
yield return new object[] { "- 123", NumberStyles.AllowLeadingWhite | NumberStyles.AllowLeadingSign, null, -123 };
yield return new object[] { "+ 123", NumberStyles.AllowLeadingWhite | NumberStyles.AllowLeadingSign, null, 123 };
yield return new object[] { " - 123 ", NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite | NumberStyles.AllowLeadingSign, null, -123 };
yield return new object[] { "- 123", NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite | NumberStyles.AllowLeadingSign | NumberStyles.AllowTrailingSign, null, -123 };
yield return new object[] { "- 123", NumberStyles.Integer, CultureInfo.InvariantCulture, -123 };
yield return new object[] { "- 123", NumberStyles.Number, CultureInfo.InvariantCulture, -123 };

NumberFormatInfo customSigns = new NumberFormatInfo() { NegativeSign = "~", PositiveSign = "++" };
yield return new object[] { "~ 123", NumberStyles.Integer, customSigns, -123 };
yield return new object[] { "++ 123", NumberStyles.Integer, customSigns, 123 };
yield return new object[] { "~ 123", NumberStyles.Number, customSigns, -123 };
yield return new object[] { "- 123", NumberStyles.Number, new NumberFormatInfo() { NumberNegativePattern = 2 }, -123 };

// AllowThousands
NumberFormatInfo thousandsFormat = new NumberFormatInfo() { NumberGroupSeparator = "|" };
yield return new object[] { "1000", NumberStyles.AllowThousands, thousandsFormat, 1000 };
Expand All @@ -375,6 +389,10 @@ public static IEnumerable<object[]> Parse_Valid_TestData()
yield return new object[] { "123", NumberStyles.AllowParentheses, null, 123 };
yield return new object[] { "(123)", NumberStyles.AllowParentheses, null, -123 };

// Whitespace between an opening parenthesis and the digits (AllowLeadingWhite)
yield return new object[] { "( 123)", NumberStyles.AllowLeadingWhite | NumberStyles.AllowParentheses, null, -123 };
yield return new object[] { "( 123 )", NumberStyles.AllowLeadingWhite | NumberStyles.AllowTrailingWhite | NumberStyles.AllowParentheses, null, -123 };

// AllowDecimalPoint
NumberFormatInfo decimalFormat = new NumberFormatInfo() { NumberDecimalSeparator = "|" };
yield return new object[] { "67|", NumberStyles.AllowDecimalPoint, decimalFormat, 67 };
Expand Down Expand Up @@ -525,6 +543,8 @@ public static IEnumerable<object[]> Parse_Invalid_TestData()
yield return new object[] { "-+123", NumberStyles.AllowLeadingSign, null, typeof(FormatException) };
yield return new object[] { "- 123", NumberStyles.AllowLeadingSign, null, typeof(FormatException) };
yield return new object[] { "+ 123", NumberStyles.AllowLeadingSign, null, typeof(FormatException) };
yield return new object[] { "- ", NumberStyles.Integer, null, typeof(FormatException) };
yield return new object[] { "( 123)", NumberStyles.AllowParentheses, null, typeof(FormatException) };

// AllowTrailingSign
yield return new object[] { "123-+", NumberStyles.AllowTrailingSign, null, typeof(FormatException) };
Expand Down Expand Up @@ -1115,6 +1135,8 @@ public static IEnumerable<object[]> TryParsePartial_TestData()
yield return new object[] { "-456xyz", NumberStyles.Integer, null, -456, 4 };
yield return new object[] { "+0xyz", NumberStyles.Integer, null, 0, 2 };
yield return new object[] { "-0xyz", NumberStyles.Integer, null, 0, 2 };
yield return new object[] { " - 123xyz", NumberStyles.Integer, CultureInfo.InvariantCulture, -123, 8 };
yield return new object[] { "- 123xyz", NumberStyles.Number, CultureInfo.InvariantCulture, -123, 5 };

// HexNumber with trailing invalid characters
yield return new object[] { "ABCxyz", NumberStyles.HexNumber, null, 0xABC, 3 };
Expand Down Expand Up @@ -1172,6 +1194,7 @@ public static IEnumerable<object[]> TryParsePartial_TestData()
yield return new object[] { " +123abc", NumberStyles.Integer, nonInvariantSignFormat, 123, 6 };
yield return new object[] { " \u2212456xyz", NumberStyles.Integer, nonInvariantSignFormat, -456, 6 };
yield return new object[] { " \u2212456", NumberStyles.Integer, nonInvariantSignFormat, -456, 6 };
yield return new object[] { " \u2212 456xyz", NumberStyles.Integer, nonInvariantSignFormat, -456, 7 };
yield return new object[] { " 123 abc", NumberStyles.Integer, nonInvariantSignFormat, 123, 7 };
}

Expand Down Expand Up @@ -1202,6 +1225,10 @@ public static void TryParsePartial(string value, NumberStyles style, IFormatProv
{
Assert.Equal(expectedCharsConsumed, bytesConsumed);
}
else
{
Assert.Equal(Encoding.UTF8.GetByteCount(value.AsSpan(0, expectedCharsConsumed)), bytesConsumed);
}
}

public static IEnumerable<object[]> TryParsePartial_Invalid_TestData()
Expand Down
Loading