Background and motivation
I'm proposing a new way of creating SearchValues - instead of some values, it would represent all values except that value (CreateExcept), or a range of values. I know I can achieve this via IndexOfAnyExcept, IndexOfAnyInRange and IndexOfAnyExceptInRange, but encapsulating the search inside SearchValues seems like a much better design to me than method explosions like this, and the three methods I mentioned would be now unnecessary and effectively obsolete. It would be convenient to be able to always store SearchValues<T>, even in case of ranges, and then use the one method that uses it.
API Proposal
namespace System.Buffers;
public static partial class SearchValues
{
public static SearchValues<T> CreateExcept<T>(params ReadOnlySpan<T> values);
public static SearchValues<T> CreateRange<T>(T lowInclusive, T highInclusive);
public static SearchValues<T> CreateExceptRange<T>(T lowInclusive, T highInclusive);
public static SearchValues<T> CreateRanges<T>(params ReadOnlySpan<Range<T>> ranges);
public static SearchValues<T> CreateExceptRanges<T>(params ReadOnlySpan<Range<T>> ranges);
public readonly struct Range<T> : IEquatable<Range<T>>
{
public Range(T value);
public Range(T lowInclusive, T highInclusive);
public T LowInclusive { get; }
public T HighInclusive { get; }
public void Deconstruct(out T lowInclusive, out T highInclusive);
public static implicit operator Range<T>(T value);
public static implicit operator Range<T>((T lowInclusive, T highInclusive) range);
}
}
Note: The T might have to be expanded to a list of overloads for concrete types instead of being generic.
Also, if I'm unsure whether to use CreateRange with 2 or 3 sequential elements or Create with those elements, no matter what I pick, it would be nice if the implementation chose the right and fastest form of searching for such a set no matter which method I choose, so I don't have to worry about it. Tbat's the benefit of using SearchValues, even for ranges.
API Usage
var searchValues = SearchValues.CreateExceptRange<char>('a', 'z');
var index = span.IndexOfAny(searchValues);
var invalidChars = SearchValues.CreateRanges<char>(((char)0, (char)31), '@', '#', '|');
var index2 = span.IndexOfAny(invalidChars);
// or, get all control characters except for newline
var invalidChars2 = SearchValues.CreateRanges<char>(((char)0, (char)9), ((char)11, (char)31));
Alternative Designs
As an alternative, only the CreateRange and CreateRanges APIs could be added - those are the ones I'd like to use.
Risks
Idk. cc @stephentoub
Background and motivation
I'm proposing a new way of creating
SearchValues- instead of some values, it would represent all values except that value (CreateExcept), or a range of values. I know I can achieve this viaIndexOfAnyExcept,IndexOfAnyInRangeandIndexOfAnyExceptInRange, but encapsulating the search insideSearchValuesseems like a much better design to me than method explosions like this, and the three methods I mentioned would be now unnecessary and effectively obsolete. It would be convenient to be able to always storeSearchValues<T>, even in case of ranges, and then use the one method that uses it.API Proposal
Note: The T might have to be expanded to a list of overloads for concrete types instead of being generic.
Also, if I'm unsure whether to use CreateRange with 2 or 3 sequential elements or Create with those elements, no matter what I pick, it would be nice if the implementation chose the right and fastest form of searching for such a set no matter which method I choose, so I don't have to worry about it. Tbat's the benefit of using SearchValues, even for ranges.
API Usage
Alternative Designs
As an alternative, only the
CreateRangeandCreateRangesAPIs could be added - those are the ones I'd like to use.Risks
Idk. cc @stephentoub