SearchValues<T>: Turbocharged String Scanning

Searching for characters in a string sounds like a solved problem, right? Just call IndexOfAny(char[]) and move on. But if you’re doing it in a parser, a tokenizer, or anything that processes megabytes of text, that naive approach leaves a lot of performance on the table. SearchValues<T> is .NET’s way of saying, “Give me your search set once, and I’ll vectorize the heck out of it.”

Here’s the basic setup: create a SearchValues<char> once and reuse it:

using System.Buffers;
// Create once, reuse everywhere
var delimiters = SearchValues.Create([',', ';', '|', '\t', ' ']);
var input = "hello,world;this|is\ta test with spaces";
// Find the first delimiter
var index = input.AsSpan().IndexOfAny(delimiters);
Console.WriteLine($"First delimiter at index {index}: '{input[index]}'");
// Split manually using spans (zero allocation!)
var remaining = input.AsSpan();
while (remaining.Length > 0)
{
var delimIndex = remaining.IndexOfAny(delimiters);
if (delimIndex < 0)
{
Console.WriteLine($"Token: \"{remaining}\"");
break;
}
Console.WriteLine($"Token: \"{remaining[..delimIndex]}\"");
remaining = remaining[(delimIndex + 1)..];
}

When you pass a SearchValues<char> to IndexOfAny, the runtime picks the optimal SIMD strategy based on your hardware and the search set. Small sets might use a bitmap lookup. Larger sets might use AVX2 or ARM NEON vector comparisons. You don’t need to know the details; just know that it’s often 3-10x faster than passing a plain char[].

It also works with SearchValues<string> for multi-value string searching in .NET 9, which is fantastic for things like scanning for keywords or finding HTML tags:

var keywords = SearchValues.Create(
["async", "await", "Task", "ValueTask"],
StringComparison.Ordinal);
var code = "The async method returns a Task<int> that you can await";
var span = code.AsSpan();
var pos = span.IndexOfAny(keywords);
Console.WriteLine($"First keyword found at position {pos}: starts with '{span[pos..][..5]}'");

The key insight is that SearchValues<T> is a precomputed search structure. The upfront cost of Create() is tiny, and every subsequent search benefits from the optimized representation. If you have any hot-path code doing IndexOfAny with a static set of characters or strings, this is the single easiest performance win you’ll find.