Skip to content

Commit

Permalink
Fix handling of '\0' in Ascii.Trim (#105350)
Browse files Browse the repository at this point in the history
  • Loading branch information
stephentoub authored Jul 23, 2024
1 parent 1a79a65 commit f68d8e1
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,11 @@ public static partial class Ascii
private static Range TrimHelper<T>(ReadOnlySpan<T> value, TrimType trimType)
where T : unmanaged, IBinaryInteger<T>
{
// A bitmap with a bit set for each ASCII whitespace character. The set bit is at the
// index of the character minus 1, since we're using a 32-bit value and space would otherwise
// be at index 32; with -1, it's at index 31.
const uint TrimMask =
(1u << (0x09 - 1))
(1u << (0x09 - 1))
| (1u << (0x0A - 1))
| (1u << (0x0B - 1))
| (1u << (0x0C - 1))
Expand All @@ -53,8 +56,8 @@ private static Range TrimHelper<T>(ReadOnlySpan<T> value, TrimType trimType)
{
for (; start < value.Length; start++)
{
uint elementValue = uint.CreateTruncating(value[start]);
if ((elementValue > 0x20) || ((TrimMask & (1u << ((int)elementValue - 1))) == 0))
uint elementValueM1 = uint.CreateTruncating(value[start]) - 1;
if ((elementValueM1 > 0x1F) || ((TrimMask & (1u << ((int)elementValueM1))) == 0))
{
break;
}
Expand All @@ -66,8 +69,8 @@ private static Range TrimHelper<T>(ReadOnlySpan<T> value, TrimType trimType)
{
for (; start <= end; end--)
{
uint elementValue = uint.CreateTruncating(value[end]);
if ((elementValue > 0x20) || ((TrimMask & (1u << ((int)elementValue - 1))) == 0))
uint elementValueM1 = uint.CreateTruncating(value[end]) - 1;
if ((elementValueM1 > 0x1F) || ((TrimMask & (1u << ((int)elementValueM1))) == 0))
{
break;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public static void EmptyInput()
[InlineData("1")]
[InlineData("abc")]
[InlineData("a\tb c\rd\ne")]
[InlineData("\0abcde\0")]
public static void NothingToTrimNonEmptyInput(string text)
{
ReadOnlySpan<byte> bytes = Encoding.ASCII.GetBytes(text);
Expand Down Expand Up @@ -113,5 +114,15 @@ public static void StartingAndEndingWithWhitespace(string text, int leadingWhite
Assert.Equal(0..(text.Length - trailingWhitespaceCount), Ascii.TrimEnd(bytes));
Assert.Equal(0..(text.Length - trailingWhitespaceCount), Ascii.TrimEnd(text));
}

[Fact]
public static void OnlyAsciiWhitespaceCharactersAreTrimmed()
{
for (int i = 0; i <= char.MaxValue; i++)
{
bool trimmed = Ascii.Trim(((char)i).ToString()).Equals(1..1);
Assert.True(((char)i is '\t' or '\n' or '\v' or '\f' or '\r' or ' ') == trimmed, $"Failed at {i}");
}
}
}
}

0 comments on commit f68d8e1

Please sign in to comment.