1- // Copyright (c) .NET Foundation. All rights reserved.
1+ // Copyright (c) .NET Foundation. All rights reserved.
22// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33
44using System ;
@@ -16,13 +16,15 @@ internal sealed class DfaMatcher : Matcher
1616 private readonly EndpointSelector _selector ;
1717 private readonly DfaState [ ] _states ;
1818 private readonly int _maxSegmentCount ;
19+ private readonly bool _isDefaultEndpointSelector ;
1920
2021 public DfaMatcher ( ILogger < DfaMatcher > logger , EndpointSelector selector , DfaState [ ] states , int maxSegmentCount )
2122 {
2223 _logger = logger ;
2324 _selector = selector ;
2425 _states = states ;
2526 _maxSegmentCount = maxSegmentCount ;
27+ _isDefaultEndpointSelector = selector is DefaultEndpointSelector ;
2628 }
2729
2830 public sealed override Task MatchAsync ( HttpContext httpContext , EndpointSelectorContext context )
@@ -53,7 +55,8 @@ public sealed override Task MatchAsync(HttpContext httpContext, EndpointSelector
5355 // FindCandidateSet will process the DFA and return a candidate set. This does
5456 // some preliminary matching of the URL (mostly the literal segments).
5557 var ( candidates , policies ) = FindCandidateSet ( httpContext , path , segments ) ;
56- if ( candidates . Length == 0 )
58+ var candidateCount = candidates . Length ;
59+ if ( candidateCount == 0 )
5760 {
5861 if ( log )
5962 {
@@ -68,6 +71,23 @@ public sealed override Task MatchAsync(HttpContext httpContext, EndpointSelector
6871 Logger . CandidatesFound ( _logger , path , candidates ) ;
6972 }
7073
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+
7191 // At this point we have a candidate set, defined as a list of endpoints in
7292 // priority order.
7393 //
@@ -83,7 +103,7 @@ public sealed override Task MatchAsync(HttpContext httpContext, EndpointSelector
83103 // `candidateSet` is the mutable state that we pass to the EndpointSelector.
84104 var candidateSet = new CandidateSet ( candidates ) ;
85105
86- for ( var i = 0 ; i < candidates . Length ; i ++ )
106+ for ( var i = 0 ; i < candidateCount ; i ++ )
87107 {
88108 // PERF: using ref here to avoid copying around big structs.
89109 //
@@ -165,7 +185,7 @@ public sealed override Task MatchAsync(HttpContext httpContext, EndpointSelector
165185 }
166186 }
167187
168- if ( policies . Length == 0 )
188+ if ( policyCount == 0 )
169189 {
170190 // Perf: avoid a state machine if there are no polices
171191 return _selector . SelectAsync ( httpContext , context , candidateSet ) ;
0 commit comments