Avoid resizing large struct arrays (#767)
This commit is contained in:
parent
0f5d471dfd
commit
abc378d3dc
|
|
@ -26,6 +26,8 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
private readonly List<(RoutePatternPathSegment pathSegment, int segmentIndex)> _complexSegments;
|
private readonly List<(RoutePatternPathSegment pathSegment, int segmentIndex)> _complexSegments;
|
||||||
private readonly List<KeyValuePair<string, IRouteConstraint>> _constraints;
|
private readonly List<KeyValuePair<string, IRouteConstraint>> _constraints;
|
||||||
|
|
||||||
|
private int _stateIndex;
|
||||||
|
|
||||||
public DfaMatcherBuilder(
|
public DfaMatcherBuilder(
|
||||||
ParameterPolicyFactory parameterPolicyFactory,
|
ParameterPolicyFactory parameterPolicyFactory,
|
||||||
EndpointSelector selector,
|
EndpointSelector selector,
|
||||||
|
|
@ -273,22 +275,29 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
{
|
{
|
||||||
var root = BuildDfaTree();
|
var root = BuildDfaTree();
|
||||||
|
|
||||||
|
// State count is the number of nodes plus an exit state
|
||||||
|
var stateCount = 1;
|
||||||
var maxSegmentCount = 0;
|
var maxSegmentCount = 0;
|
||||||
root.Visit((node) => maxSegmentCount = Math.Max(maxSegmentCount, node.PathDepth));
|
root.Visit((node) =>
|
||||||
|
{
|
||||||
|
stateCount++;
|
||||||
|
maxSegmentCount = Math.Max(maxSegmentCount, node.PathDepth);
|
||||||
|
});
|
||||||
|
_stateIndex = 0;
|
||||||
|
|
||||||
// The max segment count is the maximum path-node-depth +1. We need
|
// The max segment count is the maximum path-node-depth +1. We need
|
||||||
// the +1 to capture any additional content after the 'last' segment.
|
// the +1 to capture any additional content after the 'last' segment.
|
||||||
maxSegmentCount++;
|
maxSegmentCount++;
|
||||||
|
|
||||||
var states = new List<DfaState>();
|
var states = new DfaState[stateCount];
|
||||||
var tableBuilders = new List<(JumpTableBuilder pathBuilder, PolicyJumpTableBuilder policyBuilder)>();
|
var tableBuilders = new (JumpTableBuilder pathBuilder, PolicyJumpTableBuilder policyBuilder)[stateCount];
|
||||||
AddNode(root, states, tableBuilders);
|
AddNode(root, states, tableBuilders);
|
||||||
|
|
||||||
var exit = states.Count;
|
var exit = stateCount - 1;
|
||||||
states.Add(new DfaState(Array.Empty<Candidate>(), null, null));
|
states[exit] = new DfaState(Array.Empty<Candidate>(), null, null);
|
||||||
tableBuilders.Add((new JumpTableBuilder() { DefaultDestination = exit, ExitDestination = exit, }, null));
|
tableBuilders[exit] = (new JumpTableBuilder() { DefaultDestination = exit, ExitDestination = exit, }, null);
|
||||||
|
|
||||||
for (var i = 0; i < tableBuilders.Count; i++)
|
for (var i = 0; i < tableBuilders.Length; i++)
|
||||||
{
|
{
|
||||||
if (tableBuilders[i].pathBuilder?.DefaultDestination == JumpTableBuilder.InvalidDestination)
|
if (tableBuilders[i].pathBuilder?.DefaultDestination == JumpTableBuilder.InvalidDestination)
|
||||||
{
|
{
|
||||||
|
|
@ -306,31 +315,31 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < states.Count; i++)
|
for (var i = 0; i < states.Length; i++)
|
||||||
{
|
{
|
||||||
states[i] = new DfaState(
|
states[i] = new DfaState(
|
||||||
states[i].Candidates,
|
states[i].Candidates,
|
||||||
tableBuilders[i].pathBuilder?.Build(),
|
tableBuilders[i].pathBuilder?.Build(),
|
||||||
tableBuilders[i].policyBuilder?.Build());
|
tableBuilders[i].policyBuilder?.Build());
|
||||||
}
|
}
|
||||||
|
|
||||||
return new DfaMatcher(_selector, states.ToArray(), maxSegmentCount);
|
return new DfaMatcher(_selector, states, maxSegmentCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int AddNode(
|
private int AddNode(
|
||||||
DfaNode node,
|
DfaNode node,
|
||||||
List<DfaState> states,
|
DfaState[] states,
|
||||||
List<(JumpTableBuilder pathBuilder, PolicyJumpTableBuilder policyBuilder)> tableBuilders)
|
(JumpTableBuilder pathBuilder, PolicyJumpTableBuilder policyBuilder)[] tableBuilders)
|
||||||
{
|
{
|
||||||
node.Matches?.Sort(_comparer);
|
node.Matches?.Sort(_comparer);
|
||||||
|
|
||||||
var stateIndex = states.Count;
|
var currentStateIndex = _stateIndex;
|
||||||
|
|
||||||
var candidates = CreateCandidates(node.Matches);
|
var candidates = CreateCandidates(node.Matches);
|
||||||
states.Add(new DfaState(candidates, null, null));
|
states[currentStateIndex] = new DfaState(candidates, null, null);
|
||||||
|
|
||||||
var pathBuilder = new JumpTableBuilder();
|
var pathBuilder = new JumpTableBuilder();
|
||||||
tableBuilders.Add((pathBuilder, null));
|
tableBuilders[currentStateIndex] = (pathBuilder, null);
|
||||||
|
|
||||||
if (node.Literals != null)
|
if (node.Literals != null)
|
||||||
{
|
{
|
||||||
|
|
@ -377,7 +386,7 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
if (node.PolicyEdges != null && node.PolicyEdges.Count > 0)
|
if (node.PolicyEdges != null && node.PolicyEdges.Count > 0)
|
||||||
{
|
{
|
||||||
var policyBuilder = new PolicyJumpTableBuilder(node.NodeBuilder);
|
var policyBuilder = new PolicyJumpTableBuilder(node.NodeBuilder);
|
||||||
tableBuilders[stateIndex] = (pathBuilder, policyBuilder);
|
tableBuilders[currentStateIndex] = (pathBuilder, policyBuilder);
|
||||||
|
|
||||||
foreach (var kvp in node.PolicyEdges)
|
foreach (var kvp in node.PolicyEdges)
|
||||||
{
|
{
|
||||||
|
|
@ -385,12 +394,20 @@ namespace Microsoft.AspNetCore.Routing.Matching
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return stateIndex;
|
return currentStateIndex;
|
||||||
|
|
||||||
int Transition(DfaNode next)
|
int Transition(DfaNode next)
|
||||||
{
|
{
|
||||||
// Break cycles
|
// Break cycles
|
||||||
return ReferenceEquals(node, next) ? stateIndex : AddNode(next, states, tableBuilders);
|
if (ReferenceEquals(node, next))
|
||||||
|
{
|
||||||
|
return _stateIndex;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_stateIndex++;
|
||||||
|
return AddNode(next, states, tableBuilders);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue