Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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
11 changes: 8 additions & 3 deletions src/libraries/Common/src/System/Number.Parsing.Common.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,9 @@ internal enum ParsingStatus

private static bool IsSpaceReplacingChar(uint c) => (c == '\u00a0') || (c == '\u202f');

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static uint NormalizeSpaceReplacingChar(uint c) => IsSpaceReplacingChar(c) ? '\u0020' : c;

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static unsafe TChar* MatchNegativeSignChars<TChar>(TChar* p, TChar* pEnd, NumberFormatInfo info)
where TChar : unmanaged, IUtfChar<TChar>
Expand All @@ -345,14 +348,16 @@ internal enum ParsingStatus
if (TChar.CastToUInt32(*str) != '\0')
{
// We only hurt the failure case
// This fix is for French or Kazakh cultures. Since a user cannot type 0xA0 or 0x202F as a
// space character we use 0x20 space character instead to mean the same.
// This fix is for cultures that use NBSP (U+00A0) or narrow NBSP (U+202F) as group/decimal separators
// (e.g., French, Kazakh, Ukrainian). Since a user cannot easily type these characters,
// we accept regular space (U+0020) as equivalent.
// We also need to handle the reverse case where the input has NBSP and the format string has space.
while (true)
{
uint cp = (p < pEnd) ? TChar.CastToUInt32(*p) : '\0';
uint val = TChar.CastToUInt32(*str);

if ((cp != val) && !(IsSpaceReplacingChar(val) && (cp == '\u0020')))
if (cp != val && NormalizeSpaceReplacingChar(cp) != NormalizeSpaceReplacingChar(val))
{
break;
}
Expand Down
25 changes: 25 additions & 0 deletions src/libraries/System.Runtime.Numerics/tests/BigInteger/parse.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1331,6 +1331,31 @@ private static void Eval(BigInteger x, string expected)
}
Assert.Equal(expected, actual);
}

[Fact]
public static void ParseWithNBSPAsGroupSeparator()
{
// Culture has NBSP as group separator; input has regular spaces.
// Exercises MatchChars path: cp=='\u0020' && IsSpaceReplacingChar(val=='\u00A0')
CultureInfo nbspCulture = new CultureInfo("en-US");
nbspCulture.NumberFormat.NumberGroupSeparator = "\u00A0";

BigInteger result = BigInteger.Parse("1 234 567", NumberStyles.AllowThousands, nbspCulture);
Assert.Equal((BigInteger)1234567, result);

// Culture has regular space as group separator; input has NBSP.
// Exercises MatchChars path: val=='\u0020' && IsSpaceReplacingChar(cp=='\u00A0')
CultureInfo spaceCulture = new CultureInfo("en-US");
spaceCulture.NumberFormat.NumberGroupSeparator = " ";

result = BigInteger.Parse("1\u00A0234\u00A0567", NumberStyles.AllowThousands, spaceCulture);
Assert.Equal((BigInteger)1234567, result);

// Culture has regular space as group separator; input has narrow NBSP (U+202F).
// Exercises IsSpaceReplacingChar matching U+202F against U+0020
result = BigInteger.Parse("1\u202F234\u202F567", NumberStyles.AllowThousands, spaceCulture);
Assert.Equal((BigInteger)1234567, result);
}
}

[Collection(nameof(DisableParallelization))]
Expand Down
Loading