Skip to content

[API Proposal]: ReadOnlySpan<char>.StartsWithAny(SearchValues<string>) and ReadOnlySpan<char>.EndsWithAny(SearchValues<string>) #110115

Open

Description

@silkfire

Background and motivation

There currently doesn't seem to be a means of checking whether the beginning or the end of a string equals to any of the values contained in a specified set of values in an optimized way using SearchValues<string> that was introduced in .NET 9. There are other specialized extension APIs involving ReadOnlySpan<char> and SearchValues<string> that were recently added (e.g. IndexOfAny<T>(ReadOnlySpan<char>, SearchValues<string>), ContainsAny(ReadOnlySpan<char>, SearchValues<string>) et al.) but this particular API seems to be missing.

One use case could be if you need to efficiently check whether a string starts with any one of a particular set of prefixes in order to determine whether your application code supports or handles that particular item.

See also #94155.

API Proposal

namespace System;

public static class MemoryExtensions
{
    // Proposed
    public static bool StartsWithAny(this System.ReadOnlySpan<char> span, System.Buffers.SearchValues<string> values);
    public static bool EndsWithAny(this System.ReadOnlySpan<char> span, System.Buffers.SearchValues<string> values);
}

API Usage

var searchValues = SearchValues.Create(new[] { "P1", A99", "B44" }.AsSpan(), StringComparison.Ordinal);

var isSupported = "testValue".StartsWithAny(searchValues);
var isSupported2 = "testValue".EndsWithAny(searchValues);

Alternative Designs

N/A

Risks

N/A

Activity

  1. added
    api-suggestionEarly API idea and discussion, it is NOT ready for implementation
    on Nov 24, 2024
  2. ghost added
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Nov 24, 2024
  3. added and removed
    needs-area-labelAn area label is needed to ensure this gets routed to the appropriate area owners
    on Nov 24, 2024
  4. dotnet-policy-service commented on Nov 24, 2024

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @dotnet/area-system-memory
    See info in area-owners.md if you want to be subscribed.

  5. changed the title [-][API Proposal]: ReadOnlySpan<char>.StartsWithAny and ReadOnlySpan<char>.EndsWithAny with SearchValues<string>[/-] [+][API Proposal]: ReadOnlySpan<char>.StartsWithAny(SearchValues<string>) and ReadOnlySpan<char>.EndsWithAny(SearchValues<string>)[/+] on Nov 24, 2024
  6. MihaZupan commented on Nov 24, 2024

    @MihaZupan
    Member

    What sort of use cases do you have for EndsWithAny (note that we also didn't expose a LastIndexOfAny for SearchValues<string>)?

  7. silkfire commented on Nov 24, 2024

    @silkfire
    Author

    Good question; I actually wasn't aware of that. I assume StartsWithAny would be more common (and therefore more useful) than EndsWithAny so including the latter one would only be to make the API complete.

  8. jkoritzinsky commented on Dec 1, 2024

    @jkoritzinsky
    Member

    Re StartsWithAny, I have a use case in #104999 where I'd love to use it.

    I don't currently have a use case for EndsWithAny though.

  9. MihaZupan commented on Dec 1, 2024

    @MihaZupan
    Member

    As a workaround, you can use text.IndexOfAny(values) == 0, which would give you the same answer.
    Depending on your inputs, that could be more expensive than intended though (long haystack with no matches).

  10. jeffhandley commented on Jan 16, 2025

    @jeffhandley
    Member

    Assigned to @krwq for triage and consultation with @MihaZupan and @stephentoub.

  11. added this to the Future milestone on Jan 23, 2025
  12. removed
    untriagedNew issue has not been triaged by the area owner
    on Jan 23, 2025
  13. removed their assignment
    on Jan 23, 2025
  14. pawchen commented on Apr 15, 2025

    @pawchen

    An example for LastIndexOfAny: When generating API names from Vulkan vk.xml, a lot of names contain the suffix defined in the <tags> and somehow want to strip those.

  15. xeromorph commented on Jun 10, 2025

    @xeromorph

    another use case for LastIndexOfAny for us would be checking Uri.Host ends with specific allowed domains, e.g.

    static readonly SearchValues<string> AllowList = SearchValues.Create(
        [".subdomain1.org", ".subdomain2.org"],
        StringComparison.OrdinalIgnoreCase);
    
    bool IsAllowed(Uri uri) => uri.Host.AsSpan().EndsWithAny(AllowList);
  16. Ilchert commented on Feb 27, 2026

    @Ilchert

    One more use case is filtering files by extension

    static readonly SearchValues<string> AllowList = SearchValues.Create(
        [".json", ".subdomain2.yaml"],
        StringComparison.OrdinalIgnoreCase);
    
    bool IsAllowed(string filename) => filename.AsSpan().EndsWithAny(AllowList);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-System.Memory

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions