1
- // Copyright (c) .NET Foundation. All rights reserved.
1
+ // Copyright (c) .NET Foundation. All rights reserved.
2
2
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
3
3
4
4
using System ;
@@ -16,13 +16,15 @@ internal sealed class DfaMatcher : Matcher
16
16
private readonly EndpointSelector _selector ;
17
17
private readonly DfaState [ ] _states ;
18
18
private readonly int _maxSegmentCount ;
19
+ private readonly bool _isDefaultEndpointSelector ;
19
20
20
21
public DfaMatcher ( ILogger < DfaMatcher > logger , EndpointSelector selector , DfaState [ ] states , int maxSegmentCount )
21
22
{
22
23
_logger = logger ;
23
24
_selector = selector ;
24
25
_states = states ;
25
26
_maxSegmentCount = maxSegmentCount ;
27
+ _isDefaultEndpointSelector = selector is DefaultEndpointSelector ;
26
28
}
27
29
28
30
public sealed override Task MatchAsync ( HttpContext httpContext , EndpointSelectorContext context )
@@ -53,7 +55,8 @@ public sealed override Task MatchAsync(HttpContext httpContext, EndpointSelector
53
55
// FindCandidateSet will process the DFA and return a candidate set. This does
54
56
// some preliminary matching of the URL (mostly the literal segments).
55
57
var ( candidates , policies ) = FindCandidateSet ( httpContext , path , segments ) ;
56
- if ( candidates . Length == 0 )
58
+ var candidateCount = candidates . Length ;
59
+ if ( candidateCount == 0 )
57
60
{
58
61
if ( log )
59
62
{
@@ -68,6 +71,23 @@ public sealed override Task MatchAsync(HttpContext httpContext, EndpointSelector
68
71
Logger . CandidatesFound ( _logger , path , candidates ) ;
69
72
}
70
73
74
+ var policyCount = policies . Length ;
75
+
76
+ // This is a fast path for single candidate, 0 policies and default selector
77
+ if ( candidateCount == 1 && policyCount == 0 && _isDefaultEndpointSelector )
78
+ {
79
+ ref var candidate = ref candidates [ 0 ] ;
80
+
81
+ // Just strict path matching
82
+ if ( candidate . Flags == Candidate . CandidateFlags . None )
83
+ {
84
+ context . Endpoint = candidate . Endpoint ;
85
+
86
+ // We're done
87
+ return Task . CompletedTask ;
88
+ }
89
+ }
90
+
71
91
// At this point we have a candidate set, defined as a list of endpoints in
72
92
// priority order.
73
93
//
@@ -83,7 +103,7 @@ public sealed override Task MatchAsync(HttpContext httpContext, EndpointSelector
83
103
// `candidateSet` is the mutable state that we pass to the EndpointSelector.
84
104
var candidateSet = new CandidateSet ( candidates ) ;
85
105
86
- for ( var i = 0 ; i < candidates . Length ; i ++ )
106
+ for ( var i = 0 ; i < candidateCount ; i ++ )
87
107
{
88
108
// PERF: using ref here to avoid copying around big structs.
89
109
//
@@ -165,7 +185,7 @@ public sealed override Task MatchAsync(HttpContext httpContext, EndpointSelector
165
185
}
166
186
}
167
187
168
- if ( policies . Length == 0 )
188
+ if ( policyCount == 0 )
169
189
{
170
190
// Perf: avoid a state machine if there are no polices
171
191
return _selector . SelectAsync ( httpContext , context , candidateSet ) ;
0 commit comments