Description
INumberBase<>.TryParsePartial returns the correct value but a charsConsumed count that omits any leading whitespace, for every culture that does not use exactly "+" and "-" as its positive and negative signs.
96 of the 786 cultures on my machine are affected, including sv-SE, fi-FI, nb-NO, et-EE, lt-LT and eu-ES as well as the Arabic, Persian and Hebrew locales.
It also requires the style to permit both leading whitespace and a leading sign, as NumberStyles.Integer does. With AllowLeadingWhite alone the count is correct even in an affected culture, because the sign-parsing block is never entered.
Reproduction Steps
This is the example from the .NET 11 release notes for this API, continued the way its own CSV motivation implies - skip the delimiter, parse the next field - and run under two cultures:
using System.Globalization;
foreach (var (name, culture) in new[] {
("invariant", CultureInfo.InvariantCulture), ("sv-SE", new CultureInfo("sv-SE")) })
{
Console.WriteLine($"--- {name} ---");
ReadOnlySpan<char> input = "123; 456"; // the release-notes input, verbatim
int field = 0;
while (input.Length > 0 && field < 5)
{
if (!int.TryParsePartial(input, NumberStyles.Integer, culture, out int value, out int consumed))
{
input = input[1..]; // not a number here: skip the delimiter
continue;
}
Console.WriteLine($" field {field++}: value={value} consumed={consumed} remaining=\"{input[consumed..]}\"");
if (consumed == 0) break;
input = input[consumed..];
}
}
Output:
--- invariant ---
field 0: value=123 consumed=3 remaining="; 456"
field 1: value=456 consumed=4 remaining=""
--- sv-SE ---
field 0: value=123 consumed=3 remaining="; 456"
field 1: value=456 consumed=3 remaining="6"
field 2: value=6 consumed=1 remaining=""
"123; 456" holds two fields. Under sv-SE the reader produces three: the digits left behind by the short count are read as a further field.
The first field parses correctly because it has no leading whitespace - which is why the release-notes snippet looks right as written. The discrepancy appears on the second field, " 456", where the space following the delimiter is consumed but not counted. Whitespace after a delimiter is ordinary in CSV.
A single value shows the same thing more directly:
int.TryParsePartial(" 5", NumberStyles.Integer, new CultureInfo("sv-SE"), out int v, out int consumed);
// v = 5, consumed = 1 - expected 2
Expected behavior
charsConsumed counts every character the call consumed, so that text[..charsConsumed] is exactly the text that was parsed and text[charsConsumed..] is exactly what remains. This is what happens under the invariant culture.
Actual behavior
The leading whitespace is not counted. The value itself is scanned correctly - only the count is short - so a caller advancing by charsConsumed rewinds into the number it has already read and scans that tail a second time, reporting those digits as a further value.
Regression?
No. TryParsePartial is new in .NET 11.
Known Workarounds
Omit NumberStyles.AllowLeadingWhite or trim the input before calling.
Configuration
.NET SDK 11.0.100-preview.7.26381.103, runtime 11.0.0-preview.7.26381.103, linux-x64, windows-x64
Other information
Observed on different numeric types - not only int type.
Found by differential fuzzing.
Description
INumberBase<>.TryParsePartialreturns the correct value but acharsConsumedcount that omits any leading whitespace, for every culture that does not use exactly"+"and"-"as its positive and negative signs.96 of the 786 cultures on my machine are affected, including
sv-SE,fi-FI,nb-NO,et-EE,lt-LTandeu-ESas well as the Arabic, Persian and Hebrew locales.It also requires the style to permit both leading whitespace and a leading sign, as
NumberStyles.Integerdoes. WithAllowLeadingWhitealone the count is correct even in an affected culture, because the sign-parsing block is never entered.Reproduction Steps
This is the example from the .NET 11 release notes for this API, continued the way its own CSV motivation implies - skip the delimiter, parse the next field - and run under two cultures:
Output:
"123; 456"holds two fields. Undersv-SEthe reader produces three: the digits left behind by the short count are read as a further field.The first field parses correctly because it has no leading whitespace - which is why the release-notes snippet looks right as written. The discrepancy appears on the second field,
" 456", where the space following the delimiter is consumed but not counted. Whitespace after a delimiter is ordinary in CSV.A single value shows the same thing more directly:
Expected behavior
charsConsumedcounts every character the call consumed, so thattext[..charsConsumed]is exactly the text that was parsed andtext[charsConsumed..]is exactly what remains. This is what happens under the invariant culture.Actual behavior
The leading whitespace is not counted. The value itself is scanned correctly - only the count is short - so a caller advancing by
charsConsumedrewinds into the number it has already read and scans that tail a second time, reporting those digits as a further value.Regression?
No.
TryParsePartialis new in .NET 11.Known Workarounds
Omit
NumberStyles.AllowLeadingWhiteor trim the input before calling.Configuration
.NET SDK 11.0.100-preview.7.26381.103, runtime 11.0.0-preview.7.26381.103, linux-x64, windows-x64
Other information
Observed on different numeric types - not only
inttype.Found by differential fuzzing.