Background and motivation
Too many overloads of searching in ReadOnlySpan<T> and yet they cover only very simple search cases. In addition to those methods, in each invocation, they calculate or examine several cases before actually find the best course of how to optimally perform the search. See for example SpanHelpers.IndexOfAny or in the same class what IndexOf does when searching for a sequence.
We need a class where not only all those cases will be calculated or examined once and used thousand or even million of times, but also a class that will support more complex searching scenarios.
API Proposal
A. Base class
publicabstractclassScanner<T>{publicabstractboolContain(Tvalue);// Actually a placeholder. This should be overridden by all implementationspublicvirtualintIndexOf(ReadOnlySpan<T>span){for(inti=0;i<span.Length;i++){if(Contain(span[i]))returni;}return-1;}publicvirtualintLastIndexOf(ReadOnlySpan<T>span){for(inti=span.Length-1;i>=0;i--){if(Contain(span[i]))returni;}return-1;}publicintIndexOf(ReadOnlySpan<T>span,intindex){intidx=IndexOf(span.Slice(index));if(idx>=0)returnidx+index;return-1;}//..More IndexOf, LastIndexOf and others follow, that actually call the virtual methods aboveprotectedvirtualScanner<T>GetInverse(){returnnewScannerInverse(this);}protectedScanner<T>_inverse;/// <summary>/// Get the scanner that performs the inverse search/// </summary>publicScanner<T>Inverse{get=>_inverse??=GetInverse();protectedinternalset=>_inverse=value;}}B. An example of a Scanner that is searching for a simple value. The example is incomplete
publicclassScanOne<T>:Scanner<T>whereT:struct,IEquatable<T>{privatereadonlyT_value;publicScanOne(Tvalue){_value=value;}publicoverrideboolContain(Tvalue){return_value.Equals(value);}publicoverrideintIndexOf(ReadOnlySpan<T>span){returnspan.IndexOf(_value);}publicoverrideintLastIndexOf(ReadOnlySpan<T>span){returnspan.LastIndexOf(_value);}protectedoverrideScanner<T>GetInverse(){returnnewInv<T>(this);}classInv<T>:Scanner<T>whereT:struct,IEquatable<T>{privatereadonlyT_value;publicoverrideboolContain(Tvalue){return!_value.Equals(value);}publicInv(ScanOne<T>parent){this.Inverse=parent;_value=parent._value;}publicoverrideintIndexOf(ReadOnlySpan<T>span){Tvalue=_value;for(inti=0;i<span.Length;i++){if(!span[i].Equals(value))returni;}return-1;}}}C. An example of a Scanner that is using an ASCII lookup table. The example is incomplete
publicclassTable16CharScanner:Scanner<char>{privatereadonlyushort_mask;privateushort[]_table;publicTable16CharScanner(ushortmask,ushort[]table){_mask=mask;_table=table;}/// <summary>/// Check for characters that are above 0x7F/// </summary>/// <param name="ch">character to check</param>/// <returns>true if is found;false otherwise</returns>protectedinternalvirtualboolContainExt(charch){returnfalse;}protectedoverrideScanner<char>GetInverse(){returnnewTable16CharScannerInverse(this,_mask,_table);}publicsealedoverrideboolContain(charvalue){if(value<_table.Length){return(_table[value]&_mask)!=0;}returnContainExt(value);}publicoverrideintIndexOf(ReadOnlySpan<char>span){varmask=_mask;vartableLength=_table.Length;vartable=_table;for(inti=0;i<span.Length;i++){varch=span[i];if(ch<tableLength){if((table[ch]&mask)!=0){returni;}}elseif(ContainExt(ch)){returni;}}return-1;}D. ReadOnlySpan extensions
publicstaticclassScannerExtensions{publicintIndexOf<T>(thisReadOnlySpan<T>span,Scanner<T>scanner);publicintLastIndexOf<T>(thisReadOnlySpan<T>span,Scanner<T>scanner);publicReadOnlySpan<T>Trim<T>(thisReadOnlySpan<T>span,Scanner<T>trim);publicReadOnlySpan<T>Skip<T>(thisReadOnlySpan<T>span,Scanner<T>scanner);publicReadOnlySpan<T>Remain<T>(thisReadOnlySpan<T>span,Scanner<T>scanner);publicReadOnlySpan<T>Trim<T>(thisReadOnlySpan<T>span,Scanner<T>trimStart,Scanner<T>trimEnd);publicReadOnlySpan<T>TrimStart<T>(thisReadOnlySpan<T>span,Scanner<T>trimStart);publicReadOnlySpan<T>TrimEnd<T>(thisReadOnlySpan<T>span,Scanner<T>trimEnd);publicSplitIterator<T>Split<T>(thisReadOnlySpan<T>span,Scanner<T>search);publicSplitIterator<T>Split<T>(thisReadOnlySpan<T>span,Scanner<T>search,Scanner<T>trim,boolskipEmpty);publicintCount(thisReadOnlySpan<char>span,Scanner<T>search);}API Usage
ReadOnlySpan<char>span="..";// trim Unicode whitespacespan=span.Trim(WhiteSpaceScanner);// trim what JS spec considers white spaces from the start and WhiteSpace or ';' or ',' from the endspan=span.Trim(JavascriptWhitespace,WhiteSpaceSemicolonOrComma);// skip invalid id chars, find id until first invalidspan=span.Skip(HtmlIdScanner).Remain(HtmlIdScanner.Inverse);// split on ';' or ',' and trim whitespaces for each entryvariterator=span.Split(SemicolonOrCommaScanner,WhiteSpaceScanner);
Risks
The current API of Regular expressions can't be optimally encapsulated by a Scanner because it requires a string as input
Background and motivation
Too many overloads of searching in
ReadOnlySpan<T>and yet they cover only very simple search cases. In addition to those methods, in each invocation, they calculate or examine several cases before actually find the best course of how to optimally perform the search. See for exampleSpanHelpers.IndexOfAnyor in the same class whatIndexOfdoes when searching for a sequence.We need a class where not only all those cases will be calculated or examined once and used thousand or even million of times, but also a class that will support more complex searching scenarios.
API Proposal
A. Base class
B. An example of a Scanner that is searching for a simple value. The example is incomplete
C. An example of a Scanner that is using an ASCII lookup table. The example is incomplete
D. ReadOnlySpan extensions
API Usage
Risks
The current API of Regular expressions can't be optimally encapsulated by a Scanner because it requires a string as input