perf(advanced-search): span-based leaf evaluator for EvaluateBoolean
Custom LeafEvaluator<TState> delegate takes the leaf as ReadOnlySpan<char> (Func<> can't take a ref struct) and threads caller state through, so the property-test recursion allocates neither a per-leaf string nor a capturing closure per entity.
This commit is contained in:
parent
b22a35e4ad
commit
3516aa6d85
3 changed files with 12 additions and 6 deletions
|
|
@ -51,7 +51,8 @@ public class AdvancedSearchUtilitiesTests
|
|||
[InlineData("F|F", false)]
|
||||
public void EvaluateBoolean_Precedence(string expr, bool expected)
|
||||
{
|
||||
var result = AdvancedSearchUtilities.EvaluateBoolean(expr, leaf => leaf == "T");
|
||||
// State is unused here; the leaf evaluator just checks the span equals "T".
|
||||
var result = AdvancedSearchUtilities.EvaluateBoolean(expr, 0, static (_, leaf) => leaf.SequenceEqual("T"));
|
||||
Assert.Equal(expected, result);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -349,7 +349,7 @@ public class AdvancedSearchThreadWorker
|
|||
}
|
||||
|
||||
private static bool EvaluateRecursive(IEntity entity, ReadOnlySpan<char> span) =>
|
||||
AdvancedSearchUtilities.EvaluateBoolean(span, leaf => EvaluateSingleExpression(entity, leaf));
|
||||
AdvancedSearchUtilities.EvaluateBoolean(span, entity, static (e, leaf) => EvaluateSingleExpression(e, leaf));
|
||||
|
||||
private static bool EvaluateSingleExpression(IEntity entity, ReadOnlySpan<char> expression)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -375,22 +375,27 @@ public static class AdvancedSearchUtilities
|
|||
return false;
|
||||
}
|
||||
|
||||
// Evaluates one trimmed leaf atom against caller-supplied state. A custom delegate is required
|
||||
// because ReadOnlySpan<char> cannot be a Func<> type argument; passing state avoids a per-call
|
||||
// capturing closure, so the recursion allocates neither a string nor a closure.
|
||||
internal delegate bool LeafEvaluator<in TState>(TState state, ReadOnlySpan<char> leaf);
|
||||
|
||||
// OR ('|') binds looser than AND ('@'); split on the outermost OR first, then AND.
|
||||
internal static bool EvaluateBoolean(ReadOnlySpan<char> expr, Func<string, bool> evalLeaf)
|
||||
internal static bool EvaluateBoolean<TState>(ReadOnlySpan<char> expr, TState state, LeafEvaluator<TState> evalLeaf)
|
||||
{
|
||||
var orIndex = expr.IndexOf('|');
|
||||
if (orIndex != -1)
|
||||
{
|
||||
return EvaluateBoolean(expr[..orIndex], evalLeaf) || EvaluateBoolean(expr[(orIndex + 1)..], evalLeaf);
|
||||
return EvaluateBoolean(expr[..orIndex], state, evalLeaf) || EvaluateBoolean(expr[(orIndex + 1)..], state, evalLeaf);
|
||||
}
|
||||
|
||||
var andIndex = expr.IndexOf('@');
|
||||
if (andIndex != -1)
|
||||
{
|
||||
return EvaluateBoolean(expr[..andIndex], evalLeaf) && EvaluateBoolean(expr[(andIndex + 1)..], evalLeaf);
|
||||
return EvaluateBoolean(expr[..andIndex], state, evalLeaf) && EvaluateBoolean(expr[(andIndex + 1)..], state, evalLeaf);
|
||||
}
|
||||
|
||||
return evalLeaf(expr.Trim().ToString());
|
||||
return evalLeaf(state, expr.Trim());
|
||||
}
|
||||
|
||||
private static int GetEnumSize(Type enumType) =>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue