Skip to content

Commit

Permalink
Fix parsing of standard format string (#47353)
Browse files Browse the repository at this point in the history
* BigInteger changes

* sq

* Runtime side unit tests

* Half tests

* Address feedback

* sq

* Move to a ThrowHelper class

* Move to invariant number format

* Move to canonical throw

* sq

* sq

* Fix bad unicode char

* Add the percent tests back
  • Loading branch information
Prashanth Govindarajan authored Jan 27, 2021
1 parent ca7f199 commit ad7a50e
Show file tree
Hide file tree
Showing 16 changed files with 211 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -684,9 +684,14 @@ internal static char ParseFormatSpecifier(ReadOnlySpan<char> format, out int dig
// digits. Further, for compat, we need to stop when we hit a null char.
int n = 0;
int i = 1;
while (i < format.Length && (((uint)format[i] - '0') < 10) && n < 10)
while (i < format.Length && (((uint)format[i] - '0') < 10))
{
n = (n * 10) + format[i++] - '0';
int temp = (n * 10) + format[i++] - '0';
if (temp < n)
{
throw new FormatException(SR.Argument_BadFormatSpecifier);
}
n = temp;
}

// If we're at the end of the digits rather than having stopped because we hit something
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1707,9 +1707,14 @@ internal static unsafe char ParseFormatSpecifier(ReadOnlySpan<char> format, out
// digits. Further, for compat, we need to stop when we hit a null char.
int n = 0;
int i = 1;
while (i < format.Length && (((uint)format[i] - '0') < 10) && n < 10)
while (i < format.Length && (((uint)format[i] - '0') < 10))
{
n = (n * 10) + format[i++] - '0';
int temp = ((n * 10) + format[i++] - '0');
if (temp < n)
{
throw new FormatException(SR.Argument_BadFormatSpecifier);
}
n = temp;
}

// If we're at the end of the digits rather than having stopped because we hit something
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -476,9 +476,12 @@ internal static char ParseFormatSpecifier(ReadOnlySpan<char> format, out int dig
n = format[i++] - '0';
while (i < format.Length && format[i] >= '0' && format[i] <= '9')
{
n = n * 10 + (format[i++] - '0');
if (n >= 10)
break;
int temp = n * 10 + (format[i++] - '0');
if (temp < n)
{
throw new FormatException(SR.Argument_BadFormatSpecifier);
}
n = temp;
}
}
if (i >= format.Length || format[i] == '\0')
Expand Down

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions src/libraries/System.Runtime/tests/System/ByteTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,17 @@ public static IEnumerable<object[]> ToString_TestData()

yield return new object[] { (byte)0x24, "x", emptyFormat, "24" };
yield return new object[] { (byte)24, "N", emptyFormat, string.Format("{0:N}", 24.00) };


}
NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo;
yield return new object[] { (byte)32, "C100", invariantFormat, "¤32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { (byte)32, "P100", invariantFormat, "3,200.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 %" };
yield return new object[] { (byte)32, "D100", invariantFormat, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032" };
yield return new object[] { (byte)32, "E100", invariantFormat, "3.2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E+001" };
yield return new object[] { (byte)32, "F100", invariantFormat, "32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { (byte)32, "N100", invariantFormat, "32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { (byte)32, "X100", invariantFormat, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020" };

var customFormat = new NumberFormatInfo()
{
Expand Down
11 changes: 11 additions & 0 deletions src/libraries/System.Runtime/tests/System/DecimalTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1407,8 +1407,16 @@ public static IEnumerable<object[]> ToString_TestData()
yield return new object[] { (decimal)2468, "N", defaultFormat, "2,468.00" };

yield return new object[] { (decimal)2467, "[#-##-#]", defaultFormat, "[2-46-7]" };

}

NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo;
yield return new object[] { 32.5m, "C100", invariantFormat, "¤32.5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { 32.5m, "P100", invariantFormat, "3,250.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 %" };
yield return new object[] { 32.5m, "E100", invariantFormat, "3.2500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E+001" };
yield return new object[] { 32.5m, "F100", invariantFormat, "32.5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { 32.5m, "N100", invariantFormat, "32.5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };

// Changing the negative pattern doesn't do anything without also passing in a format string
var customFormat1 = new NumberFormatInfo();
customFormat1.NumberNegativePattern = 0;
Expand Down Expand Up @@ -1485,6 +1493,9 @@ public static void ToString_InvalidFormat_ThrowsFormatException()
decimal f = 123;
Assert.Throws<FormatException>(() => f.ToString("Y"));
Assert.Throws<FormatException>(() => f.ToString("Y", null));
long intMaxPlus1 = (long)int.MaxValue + 1;
string intMaxPlus1String = intMaxPlus1.ToString();
Assert.Throws<FormatException>(() => f.ToString("E" + intMaxPlus1String));
}

public static IEnumerable<object[]> Truncate_TestData()
Expand Down
10 changes: 9 additions & 1 deletion src/libraries/System.Runtime/tests/System/DoubleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System.Collections.Generic;
using System.Globalization;
using Microsoft.DotNet.RemoteExecutor;
using Xunit;

#pragma warning disable xUnit1025 // reporting duplicate test cases due to not distinguishing 0.0 from -0.0, NaN from -NaN
Expand Down Expand Up @@ -444,13 +443,19 @@ public static IEnumerable<object[]> ToString_TestData_NotNetFramework()
yield return testData;
}


yield return new object[] { double.MinValue, "G", null, "-1.7976931348623157E+308" };
yield return new object[] { double.MaxValue, "G", null, "1.7976931348623157E+308" };

yield return new object[] { double.Epsilon, "G", null, "5E-324" };

NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo;
yield return new object[] { double.Epsilon, "G", invariantFormat, "5E-324" };
yield return new object[] { 32.5, "C100", invariantFormat, "¤32.5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { 32.5, "P100", invariantFormat, "3,250.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 %" };
yield return new object[] { 32.5, "E100", invariantFormat, "3.2500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E+001" };
yield return new object[] { 32.5, "F100", invariantFormat, "32.5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { 32.5, "N100", invariantFormat, "32.5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
}

[Fact]
Expand Down Expand Up @@ -494,6 +499,9 @@ public static void ToString_InvalidFormat_ThrowsFormatException()
double d = 123.0;
Assert.Throws<FormatException>(() => d.ToString("Y")); // Invalid format
Assert.Throws<FormatException>(() => d.ToString("Y", null)); // Invalid format
long intMaxPlus1 = (long)int.MaxValue + 1;
string intMaxPlus1String = intMaxPlus1.ToString();
Assert.Throws<FormatException>(() => d.ToString("E" + intMaxPlus1String));
}

[Theory]
Expand Down
18 changes: 18 additions & 0 deletions src/libraries/System.Runtime/tests/System/HalfTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,12 @@ public static IEnumerable<object[]> ToString_TestData_NotNetFramework()

NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo;
yield return new object[] { Half.Epsilon, "G", invariantFormat, "6E-08" };

yield return new object[] { 32.5f, "C100", invariantFormat, "¤32.5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { 32.5f, "P100", invariantFormat, "3,250.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 %" };
yield return new object[] { 32.5f, "E100", invariantFormat, "3.2500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E+001" };
yield return new object[] { 32.5f, "F100", invariantFormat, "32.5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { 32.5f, "N100", invariantFormat, "32.5000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
}

[Fact]
Expand Down Expand Up @@ -871,6 +877,18 @@ private static void ToStringTest(Half f, string format, IFormatProvider provider
Assert.Equal(expected.Replace('E', 'e'), f.ToString(format.ToLowerInvariant(), provider));
}

[Fact]
public static void ToString_InvalidFormat_ThrowsFormatException()
{
Half f = (Half)123.0f;
Assert.Throws<FormatException>(() => f.ToString("Y")); // Invalid format
Assert.Throws<FormatException>(() => f.ToString("Y", null)); // Invalid format
long intMaxPlus1 = (long)int.MaxValue + 1;
string intMaxPlus1String = intMaxPlus1.ToString();
Assert.Throws<FormatException>(() => f.ToString("E" + intMaxPlus1String));
}


public static IEnumerable<object[]> Parse_ValidWithOffsetCount_TestData()
{
foreach (object[] inputs in Parse_Valid_TestData())
Expand Down
11 changes: 11 additions & 0 deletions src/libraries/System.Runtime/tests/System/Int16Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,19 @@ public static IEnumerable<object[]> ToString_TestData()
yield return new object[] { (short)0x2468, "x", defaultFormat, "2468" };
yield return new object[] { (short)-0x2468, "x", defaultFormat, "db98" };
yield return new object[] { (short)2468, "N", defaultFormat, string.Format("{0:N}", 2468.00) };


}

NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo;
yield return new object[] { (short)32, "C100", invariantFormat, "¤32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { (short)32, "P100", invariantFormat, "3,200.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 %" };
yield return new object[] { (short)32, "D100", invariantFormat, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032" };
yield return new object[] { (short)32, "E100", invariantFormat, "3.2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E+001" };
yield return new object[] { (short)32, "F100", invariantFormat, "32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { (short)32, "N100", invariantFormat, "32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { (short)32, "X100", invariantFormat, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020" };

var customFormat = new NumberFormatInfo()
{
NegativeSign = "#",
Expand Down
12 changes: 11 additions & 1 deletion src/libraries/System.Runtime/tests/System/Int32Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,18 @@ public static IEnumerable<object[]> ToString_TestData()
yield return new object[] { 0x2468, "x", defaultFormat, "2468" };
yield return new object[] { -0x2468, "x", defaultFormat, "ffffdb98" };
yield return new object[] { 2468, "N", defaultFormat, string.Format("{0:N}", 2468.00) };

}

NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo;
yield return new object[] { 32, "C100", invariantFormat, "¤32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { 32, "P100", invariantFormat, "3,200.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 %" };
yield return new object[] { 32, "D100", invariantFormat, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032" };
yield return new object[] { 32, "E100", invariantFormat, "3.2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E+001" };
yield return new object[] { 32, "F100", invariantFormat, "32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { 32, "N100", invariantFormat, "32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { 32, "X100", invariantFormat, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020" };

var customFormat = new NumberFormatInfo()
{
NegativeSign = "#",
Expand Down Expand Up @@ -247,7 +257,7 @@ public static IEnumerable<object[]> Parse_Valid_TestData()
yield return new object[] { "$1000", NumberStyles.Currency, currencyFormat, 1000 };
yield return new object[] { "$ 1000", NumberStyles.Currency, currencyFormat, 1000 };
yield return new object[] { "1000", NumberStyles.Currency, currencyFormat, 1000 };
yield return new object[] { "$(1000)", NumberStyles.Currency, currencyFormat, -1000};
yield return new object[] { "$(1000)", NumberStyles.Currency, currencyFormat, -1000 };
yield return new object[] { "($1000)", NumberStyles.Currency, currencyFormat, -1000 };
yield return new object[] { "$-1000", NumberStyles.Currency, currencyFormat, -1000 };
yield return new object[] { "-$1000", NumberStyles.Currency, currencyFormat, -1000 };
Expand Down
11 changes: 11 additions & 0 deletions src/libraries/System.Runtime/tests/System/Int64Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,19 @@ public static IEnumerable<object[]> ToString_TestData()
yield return new object[] { (long)0x2468, "x", defaultFormat, "2468" };
yield return new object[] { (long)-0x2468, "x", defaultFormat, "ffffffffffffdb98" };
yield return new object[] { (long)2468, "N", defaultFormat, string.Format("{0:N}", 2468.00) };


}

NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo;
yield return new object[] { (long)32, "C100", invariantFormat, "¤32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { (long)32, "P100", invariantFormat, "3,200.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 %" };
yield return new object[] { (long)32, "D100", invariantFormat, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032" };
yield return new object[] { (long)32, "E100", invariantFormat, "3.2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E+001" };
yield return new object[] { (long)32, "F100", invariantFormat, "32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { (long)32, "N100", invariantFormat, "32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { (long)32, "X100", invariantFormat, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020" };

var customFormat = new NumberFormatInfo()
{
NegativeSign = "#",
Expand Down
11 changes: 11 additions & 0 deletions src/libraries/System.Runtime/tests/System/SByteTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,19 @@ public static IEnumerable<object[]> ToString_TestData()
yield return new object[] { (sbyte)0x24, "x", defaultFormat, "24" };
yield return new object[] { (sbyte)-0x24, "x", defaultFormat, "dc" };
yield return new object[] { (sbyte)24, "N", defaultFormat, string.Format("{0:N}", 24.00) };


}

NumberFormatInfo invariantFormat = NumberFormatInfo.InvariantInfo;
yield return new object[] { (sbyte)32, "C100", invariantFormat, "¤32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { (sbyte)32, "P100", invariantFormat, "3,200.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000 %" };
yield return new object[] { (sbyte)32, "D100", invariantFormat, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000032" };
yield return new object[] { (sbyte)32, "E100", invariantFormat, "3.2000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000E+001" };
yield return new object[] { (sbyte)32, "F100", invariantFormat, "32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { (sbyte)32, "N100", invariantFormat, "32.0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" };
yield return new object[] { (sbyte)32, "X100", invariantFormat, "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000020" };

var customFormat = new NumberFormatInfo()
{
NegativeSign = "#",
Expand Down
Loading

0 comments on commit ad7a50e

Please sign in to comment.