Skip to content

Ascii.Equals #84887

@adamsitnik

Description

@adamsitnik

Background and motivation

Based on an ASP.NET and dotnet/runtime code study, I concluded that from all of the methods that were initially proposed in #28230 and were reverted in 0d69abd before #75012 got merged, we need only Equals and EqualsIgnoreCase.

The tricky part was deciding what should happen when both input values are equal, but they are not valid ASCII. Example:

Ascii.Equals(new byte[] { 128 }, new char[] { (char)128 });

The initial proposal (#28230) suggested to throw. We have received a very strong push back about it. Based on real-life use cases from dotnet/runtime and ASP.NET:

https://github.com/dotnet/aspnetcore/blob/8968058c9e5fdfdd1242426a03dc80609997edab/src/Http/Routing/src/Matching/Ascii.cs#L16
https://github.com/dotnet/aspnetcore/blob/8968058c9e5fdfdd1242426a03dc80609997edab/src/Shared/ServerInfrastructure/StringUtilities.cs#L420

together with @stephentoub and @GrabYourPitchforks we came to agreement, that these methods should be used only when at least one of the input buffers is guaranteed to be valid ASCII (common use case: comparing user input with a const value) and the equality checks should return false in such case.

API Proposal

namespace System.Text;

public static partial class Ascii
{
    /// <summary>
    /// Determines whether the provided buffers contain equal ASCII characters.
    /// </summary>
    /// <param name="left">The buffer to compare with <paramref name="right" />.</param>
    /// <param name="right">The buffer to compare with <paramref name="left" />.</param>
    /// <returns><see langword="true" /> if the corresponding elements in <paramref name="left" /> and <paramref name="right" /> were equal and ASCII. <see langword="false" /> otherwise.</returns>
    /// <remarks>If both buffers contain equal, but non-ASCII characters, the method returns <see langword="false" />.</remarks>
    public static bool Equals(ReadOnlySpan<byte> left, ReadOnlySpan<byte> right);
    public static bool Equals(ReadOnlySpan<byte> left, ReadOnlySpan<char> right);
    public static bool Equals(ReadOnlySpan<char> left, ReadOnlySpan<char> right);
    
    /// <summary>
    /// Determines whether the provided buffers contain equal ASCII characters, ignoring case considerations.
    /// </summary>
    /// <param name="left">The buffer to compare with <paramref name="right" />.</param>
    /// <param name="right">The buffer to compare with <paramref name="left" />.</param>
    /// <returns><see langword="true" /> if the corresponding elements in <paramref name="left" /> and <paramref name="right" /> were equal ignoring case considerations and ASCII. <see langword="false" /> otherwise.</returns>
    /// <remarks>If both buffers contain equal, but non-ASCII characters, the method returns <see langword="false" />.</remarks>
    public static bool EqualsIgnoreCase(ReadOnlySpan<byte> left, ReadOnlySpan<byte> right);
    public static bool EqualsIgnoreCase(ReadOnlySpan<byte> left, ReadOnlySpan<char> right);
    public static bool EqualsIgnoreCase(ReadOnlySpan<char> left, ReadOnlySpan<char> right);
}

Alternative Designs

Equals(ReadOnlySpan<byte> left, ReadOnlySpan<byte> right) and Equals(ReadOnlySpan<char> left, ReadOnlySpan<char> right) could be removed, as they can be expressed by calling left.SequenceEqual(right) with Ascii.IsValid calls if needed.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions