Merge pull request #596 from dotnet-maestro-bot/merge/release/2.2-to-master
[automated] Merge branch 'release/2.2' => 'master'
This commit is contained in:
commit
3e1b336d6d
|
|
@ -14,7 +14,6 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
|
|
||||||
private BarebonesMatcher _baseline;
|
private BarebonesMatcher _baseline;
|
||||||
private Matcher _dfa;
|
private Matcher _dfa;
|
||||||
private Matcher _instruction;
|
|
||||||
private Matcher _route;
|
private Matcher _route;
|
||||||
private Matcher _tree;
|
private Matcher _tree;
|
||||||
|
|
||||||
|
|
@ -34,7 +33,6 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
|
|
||||||
_baseline = (BarebonesMatcher)SetupMatcher(new BarebonesMatcherBuilder());
|
_baseline = (BarebonesMatcher)SetupMatcher(new BarebonesMatcherBuilder());
|
||||||
_dfa = SetupMatcher(new DfaMatcherBuilder());
|
_dfa = SetupMatcher(new DfaMatcherBuilder());
|
||||||
_instruction = SetupMatcher(new InstructionMatcherBuilder());
|
|
||||||
_route = SetupMatcher(new RouteMatcherBuilder());
|
_route = SetupMatcher(new RouteMatcherBuilder());
|
||||||
_tree = SetupMatcher(new TreeRouterMatcherBuilder());
|
_tree = SetupMatcher(new TreeRouterMatcherBuilder());
|
||||||
|
|
||||||
|
|
@ -67,19 +65,6 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = SampleCount)]
|
|
||||||
public async Task Instruction()
|
|
||||||
{
|
|
||||||
var feature = _feature;
|
|
||||||
for (var i = 0; i < SampleCount; i++)
|
|
||||||
{
|
|
||||||
var sample = _samples[i];
|
|
||||||
var httpContext = _requests[sample];
|
|
||||||
await _instruction.MatchAsync(httpContext, feature);
|
|
||||||
Validate(httpContext, _endpoints[sample], feature.Endpoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = SampleCount)]
|
[Benchmark(OperationsPerInvoke = SampleCount)]
|
||||||
public async Task LegacyRoute()
|
public async Task LegacyRoute()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
{
|
{
|
||||||
private BarebonesMatcher _baseline;
|
private BarebonesMatcher _baseline;
|
||||||
private Matcher _dfa;
|
private Matcher _dfa;
|
||||||
private Matcher _instruction;
|
|
||||||
private Matcher _route;
|
private Matcher _route;
|
||||||
private Matcher _tree;
|
private Matcher _tree;
|
||||||
|
|
||||||
|
|
@ -28,7 +27,6 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
|
|
||||||
_baseline = (BarebonesMatcher)SetupMatcher(new BarebonesMatcherBuilder());
|
_baseline = (BarebonesMatcher)SetupMatcher(new BarebonesMatcherBuilder());
|
||||||
_dfa = SetupMatcher(new DfaMatcherBuilder());
|
_dfa = SetupMatcher(new DfaMatcherBuilder());
|
||||||
_instruction = SetupMatcher(new InstructionMatcherBuilder());
|
|
||||||
_route = SetupMatcher(new RouteMatcherBuilder());
|
_route = SetupMatcher(new RouteMatcherBuilder());
|
||||||
_tree = SetupMatcher(new TreeRouterMatcherBuilder());
|
_tree = SetupMatcher(new TreeRouterMatcherBuilder());
|
||||||
|
|
||||||
|
|
@ -59,18 +57,6 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = EndpointCount)]
|
|
||||||
public async Task Instruction()
|
|
||||||
{
|
|
||||||
var feature = _feature;
|
|
||||||
for (var i = 0; i < EndpointCount; i++)
|
|
||||||
{
|
|
||||||
var httpContext = _requests[i];
|
|
||||||
await _instruction.MatchAsync(httpContext, feature);
|
|
||||||
Validate(httpContext, _endpoints[i], feature.Endpoint);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark(OperationsPerInvoke = EndpointCount)]
|
[Benchmark(OperationsPerInvoke = EndpointCount)]
|
||||||
public async Task LegacyRoute()
|
public async Task LegacyRoute()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
{
|
{
|
||||||
private Matcher _baseline;
|
private Matcher _baseline;
|
||||||
private Matcher _dfa;
|
private Matcher _dfa;
|
||||||
private Matcher _instruction;
|
|
||||||
private Matcher _route;
|
private Matcher _route;
|
||||||
private Matcher _tree;
|
private Matcher _tree;
|
||||||
|
|
||||||
|
|
@ -30,7 +29,6 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
|
|
||||||
_baseline = SetupMatcher(new TrivialMatcherBuilder());
|
_baseline = SetupMatcher(new TrivialMatcherBuilder());
|
||||||
_dfa = SetupMatcher(new DfaMatcherBuilder());
|
_dfa = SetupMatcher(new DfaMatcherBuilder());
|
||||||
_instruction = SetupMatcher(new InstructionMatcherBuilder());
|
|
||||||
_route = SetupMatcher(new RouteMatcherBuilder());
|
_route = SetupMatcher(new RouteMatcherBuilder());
|
||||||
_tree = SetupMatcher(new TreeRouterMatcherBuilder());
|
_tree = SetupMatcher(new TreeRouterMatcherBuilder());
|
||||||
|
|
||||||
|
|
@ -59,14 +57,6 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
Validate(_requests[0], _endpoints[0], feature.Endpoint);
|
Validate(_requests[0], _endpoints[0], feature.Endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public async Task Instruction()
|
|
||||||
{
|
|
||||||
var feature = _feature;
|
|
||||||
await _instruction.MatchAsync(_requests[0], feature);
|
|
||||||
Validate(_requests[0], _endpoints[0], feature.Endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public async Task LegacyRoute()
|
public async Task LegacyRoute()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,6 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
{
|
{
|
||||||
private Matcher _baseline;
|
private Matcher _baseline;
|
||||||
private Matcher _dfa;
|
private Matcher _dfa;
|
||||||
private Matcher _instruction;
|
|
||||||
private Matcher _route;
|
private Matcher _route;
|
||||||
private Matcher _tree;
|
private Matcher _tree;
|
||||||
|
|
||||||
|
|
@ -26,7 +25,6 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
|
|
||||||
_baseline = SetupMatcher(new TrivialMatcherBuilder());
|
_baseline = SetupMatcher(new TrivialMatcherBuilder());
|
||||||
_dfa = SetupMatcher(new DfaMatcherBuilder());
|
_dfa = SetupMatcher(new DfaMatcherBuilder());
|
||||||
_instruction = SetupMatcher(new InstructionMatcherBuilder());
|
|
||||||
_route = SetupMatcher(new RouteMatcherBuilder());
|
_route = SetupMatcher(new RouteMatcherBuilder());
|
||||||
_tree = SetupMatcher(new TreeRouterMatcherBuilder());
|
_tree = SetupMatcher(new TreeRouterMatcherBuilder());
|
||||||
|
|
||||||
|
|
@ -89,14 +87,6 @@ namespace Microsoft.AspNetCore.Routing.Matchers
|
||||||
Validate(_requests[0], _endpoints[9], feature.Endpoint);
|
Validate(_requests[0], _endpoints[9], feature.Endpoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Benchmark]
|
|
||||||
public async Task Instruction()
|
|
||||||
{
|
|
||||||
var feature = _feature;
|
|
||||||
await _instruction.MatchAsync(_requests[0], feature);
|
|
||||||
Validate(_requests[0], _endpoints[9], feature.Endpoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Benchmark]
|
[Benchmark]
|
||||||
public async Task LegacyRoute()
|
public async Task LegacyRoute()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -33,12 +33,6 @@
|
||||||
<Compile Include="..\..\test\Microsoft.AspNetCore.Routing.Tests\Matchers\DfaMatcherBuilder.cs">
|
<Compile Include="..\..\test\Microsoft.AspNetCore.Routing.Tests\Matchers\DfaMatcherBuilder.cs">
|
||||||
<Link>Matchers\DfaMatcherBuilder.cs</Link>
|
<Link>Matchers\DfaMatcherBuilder.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
<Compile Include="..\..\test\Microsoft.AspNetCore.Routing.Tests\Matchers\InstructionMatcher.cs">
|
|
||||||
<Link>Matchers\InstructionMatcher.cs</Link>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="..\..\test\Microsoft.AspNetCore.Routing.Tests\Matchers\InstructionMatcherBuilder.cs">
|
|
||||||
<Link>Matchers\InstructionMatcherBuilder.cs</Link>
|
|
||||||
</Compile>
|
|
||||||
<Compile Include="..\..\test\Microsoft.AspNetCore.Routing.Tests\Matchers\RouteMatcher.cs">
|
<Compile Include="..\..\test\Microsoft.AspNetCore.Routing.Tests\Matchers\RouteMatcher.cs">
|
||||||
<Link>Matchers\RouteMatcher.cs</Link>
|
<Link>Matchers\RouteMatcher.cs</Link>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
|
|
||||||
|
|
@ -1,214 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Routing.Matchers
|
|
||||||
{
|
|
||||||
internal class InstructionMatcher : Matcher
|
|
||||||
{
|
|
||||||
private State _state;
|
|
||||||
|
|
||||||
public InstructionMatcher(Instruction[] instructions, Candidate[] candidates, JumpTable[] tables)
|
|
||||||
{
|
|
||||||
_state = new State()
|
|
||||||
{
|
|
||||||
Instructions = instructions,
|
|
||||||
Candidates = candidates,
|
|
||||||
Tables = tables,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature)
|
|
||||||
{
|
|
||||||
if (httpContext == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(httpContext));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (feature == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(feature));
|
|
||||||
}
|
|
||||||
|
|
||||||
var state = _state;
|
|
||||||
|
|
||||||
var path = httpContext.Request.Path.Value;
|
|
||||||
var buffer = stackalloc PathSegment[32];
|
|
||||||
var count = FastPathTokenizer.Tokenize(path, buffer, 32);
|
|
||||||
|
|
||||||
var i = 0;
|
|
||||||
var candidates = new List<Candidate>();
|
|
||||||
while (i < state.Instructions.Length)
|
|
||||||
{
|
|
||||||
var instruction = state.Instructions[i];
|
|
||||||
switch (instruction.Code)
|
|
||||||
{
|
|
||||||
case InstructionCode.Accept:
|
|
||||||
{
|
|
||||||
if (count == instruction.Depth)
|
|
||||||
{
|
|
||||||
candidates.Add(state.Candidates[instruction.Payload]);
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case InstructionCode.Branch:
|
|
||||||
{
|
|
||||||
var table = state.Tables[instruction.Payload];
|
|
||||||
i = table.GetDestination(buffer, count, path);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case InstructionCode.Jump:
|
|
||||||
{
|
|
||||||
i = instruction.Payload;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var matches = new List<(Endpoint, RouteValueDictionary)>();
|
|
||||||
for (i = 0; i < candidates.Count; i++)
|
|
||||||
{
|
|
||||||
var values = new RouteValueDictionary();
|
|
||||||
var parameters = candidates[i].Parameters;
|
|
||||||
if (parameters != null)
|
|
||||||
{
|
|
||||||
for (var j = 0; j < parameters.Length; j++)
|
|
||||||
{
|
|
||||||
var parameter = parameters[j];
|
|
||||||
if (parameter != null && buffer[j].Length == 0)
|
|
||||||
{
|
|
||||||
goto notmatch;
|
|
||||||
}
|
|
||||||
else if (parameter != null)
|
|
||||||
{
|
|
||||||
var value = path.Substring(buffer[j].Start, buffer[j].Length);
|
|
||||||
values.Add(parameter, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
matches.Add((candidates[i].Endpoint, values));
|
|
||||||
|
|
||||||
notmatch:;
|
|
||||||
}
|
|
||||||
|
|
||||||
feature.Endpoint = matches.Count == 0 ? null : matches[0].Item1;
|
|
||||||
feature.Values = matches.Count == 0 ? null : matches[0].Item2;
|
|
||||||
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct Candidate
|
|
||||||
{
|
|
||||||
public Endpoint Endpoint;
|
|
||||||
public string[] Parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class State
|
|
||||||
{
|
|
||||||
public Candidate[] Candidates;
|
|
||||||
public Instruction[] Instructions;
|
|
||||||
public JumpTable[] Tables;
|
|
||||||
}
|
|
||||||
|
|
||||||
[DebuggerDisplay("{ToDebugString(),nq}")]
|
|
||||||
[StructLayout(LayoutKind.Explicit)]
|
|
||||||
public struct Instruction
|
|
||||||
{
|
|
||||||
[FieldOffset(0)]
|
|
||||||
public byte Depth;
|
|
||||||
|
|
||||||
[FieldOffset(3)]
|
|
||||||
public InstructionCode Code;
|
|
||||||
|
|
||||||
[FieldOffset(4)]
|
|
||||||
public int Payload;
|
|
||||||
|
|
||||||
private string ToDebugString()
|
|
||||||
{
|
|
||||||
return $"{Code}: {Payload}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum InstructionCode : byte
|
|
||||||
{
|
|
||||||
Accept,
|
|
||||||
Branch,
|
|
||||||
Jump,
|
|
||||||
Pop, // Only used during the instruction builder phase
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class JumpTable
|
|
||||||
{
|
|
||||||
public unsafe abstract int GetDestination(PathSegment* segments, int depth, string path);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class JumpTableBuilder
|
|
||||||
{
|
|
||||||
private readonly List<(string text, int destination)> _entries = new List<(string text, int destination)>();
|
|
||||||
|
|
||||||
public int Depth { get; set; }
|
|
||||||
|
|
||||||
public int Exit { get; set; }
|
|
||||||
|
|
||||||
public void AddEntry(string text, int destination)
|
|
||||||
{
|
|
||||||
_entries.Add((text, destination));
|
|
||||||
}
|
|
||||||
|
|
||||||
public JumpTable Build()
|
|
||||||
{
|
|
||||||
return new SimpleJumpTable(Depth, Exit, _entries.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SimpleJumpTable : JumpTable
|
|
||||||
{
|
|
||||||
private readonly (string text, int destination)[] _entries;
|
|
||||||
private readonly int _depth;
|
|
||||||
private readonly int _exit;
|
|
||||||
|
|
||||||
public SimpleJumpTable(int depth, int exit, (string text, int destination)[] entries)
|
|
||||||
{
|
|
||||||
_depth = depth;
|
|
||||||
_exit = exit;
|
|
||||||
_entries = entries;
|
|
||||||
}
|
|
||||||
|
|
||||||
public unsafe override int GetDestination(PathSegment* segments, int count, string path)
|
|
||||||
{
|
|
||||||
if (_depth == count)
|
|
||||||
{
|
|
||||||
return _exit;
|
|
||||||
}
|
|
||||||
|
|
||||||
var start = segments[_depth].Start;
|
|
||||||
var length = segments[_depth].Length;
|
|
||||||
|
|
||||||
for (var i = 0; i < _entries.Length; i++)
|
|
||||||
{
|
|
||||||
if (length == _entries[i].text.Length &&
|
|
||||||
string.Compare(
|
|
||||||
path,
|
|
||||||
start,
|
|
||||||
_entries[i].text,
|
|
||||||
0,
|
|
||||||
length,
|
|
||||||
StringComparison.OrdinalIgnoreCase) == 0)
|
|
||||||
{
|
|
||||||
return _entries[i].destination;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _exit;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,356 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using Microsoft.AspNetCore.Routing.Template;
|
|
||||||
using static Microsoft.AspNetCore.Routing.Matchers.InstructionMatcher;
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Routing.Matchers
|
|
||||||
{
|
|
||||||
internal class InstructionMatcherBuilder : MatcherBuilder
|
|
||||||
{
|
|
||||||
private List<Entry> _entries = new List<Entry>();
|
|
||||||
|
|
||||||
public override void AddEndpoint(MatcherEndpoint endpoint)
|
|
||||||
{
|
|
||||||
var parsed = TemplateParser.Parse(endpoint.Template);
|
|
||||||
_entries.Add(new Entry()
|
|
||||||
{
|
|
||||||
Order = 0,
|
|
||||||
Pattern = parsed,
|
|
||||||
Precedence = RoutePrecedence.ComputeInbound(parsed),
|
|
||||||
Endpoint = endpoint,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Matcher Build()
|
|
||||||
{
|
|
||||||
_entries.Sort((x, y) =>
|
|
||||||
{
|
|
||||||
var comparison = x.Order.CompareTo(y.Order);
|
|
||||||
if (comparison != 0)
|
|
||||||
{
|
|
||||||
return comparison;
|
|
||||||
}
|
|
||||||
|
|
||||||
comparison = x.Precedence.CompareTo(y.Precedence);
|
|
||||||
if (comparison != 0)
|
|
||||||
{
|
|
||||||
return comparison;
|
|
||||||
}
|
|
||||||
|
|
||||||
return x.Pattern.TemplateText.CompareTo(y.Pattern.TemplateText);
|
|
||||||
});
|
|
||||||
|
|
||||||
var roots = new List<OrderNode>();
|
|
||||||
|
|
||||||
for (var i = 0; i < _entries.Count; i++)
|
|
||||||
{
|
|
||||||
var entry = _entries[i];
|
|
||||||
|
|
||||||
var parent = (SequenceNode)GetOrCreateRootNode(roots, entry.Order);
|
|
||||||
|
|
||||||
var depth = 0;
|
|
||||||
for (; depth < entry.Pattern.Segments.Count; depth++)
|
|
||||||
{
|
|
||||||
var segment = entry.Pattern.Segments[depth];
|
|
||||||
if (segment.IsSimple && segment.Parts[0].IsLiteral)
|
|
||||||
{
|
|
||||||
var branch = parent.GetNode<BranchNode>() ?? parent.AddNode(new BranchNode(depth));
|
|
||||||
|
|
||||||
var index = -1;
|
|
||||||
for (var j = 0; j < branch.Literals.Count; j++)
|
|
||||||
{
|
|
||||||
if (string.Equals(segment.Parts[0].Text, branch.Literals[j], StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
index = j;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (index == -1)
|
|
||||||
{
|
|
||||||
branch.Literals.Add(segment.Parts[0].Text);
|
|
||||||
branch.AddNode(new SequenceNode(depth + 1));
|
|
||||||
index = branch.Children.Count - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
parent = (SequenceNode)branch.Children[index];
|
|
||||||
}
|
|
||||||
else if (segment.IsSimple && segment.Parts[0].IsParameter)
|
|
||||||
{
|
|
||||||
var parameter = parent.GetNode<ParameterNode>() ?? parent.AddNode(new ParameterNode(depth));
|
|
||||||
if (parameter.Children.Count == 0)
|
|
||||||
{
|
|
||||||
parameter.AddNode(new SequenceNode(depth + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
parent = (SequenceNode)parameter.Children[0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new InvalidOperationException("Not implemented!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parent.AddNode(new AcceptNode(depth, entry.Endpoint));
|
|
||||||
}
|
|
||||||
|
|
||||||
var builder = new InstructionBuilder();
|
|
||||||
for (var i = 0; i < roots.Count; i++)
|
|
||||||
{
|
|
||||||
roots[i].Lower(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
var (instructions, endpoints, tables) = builder;
|
|
||||||
var candidates = new Candidate[endpoints.Length];
|
|
||||||
for (var i = 0; i < endpoints.Length; i++)
|
|
||||||
{
|
|
||||||
candidates[i] = CreateCandidate(endpoints[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return new InstructionMatcher(instructions, candidates, tables);
|
|
||||||
}
|
|
||||||
|
|
||||||
private OrderNode GetOrCreateRootNode(List<OrderNode> roots, int order)
|
|
||||||
{
|
|
||||||
OrderNode root = null;
|
|
||||||
for (var j = 0; j < roots.Count; j++)
|
|
||||||
{
|
|
||||||
if (roots[j].Order == order)
|
|
||||||
{
|
|
||||||
root = roots[j];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (root == null)
|
|
||||||
{
|
|
||||||
// Nodes are guaranteed to be in order because the entries are in order.
|
|
||||||
root = new OrderNode(order);
|
|
||||||
roots.Add(root);
|
|
||||||
}
|
|
||||||
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Candidate CreateCandidate(MatcherEndpoint endpoint)
|
|
||||||
{
|
|
||||||
var parsed = TemplateParser.Parse(endpoint.Template);
|
|
||||||
return new Candidate()
|
|
||||||
{
|
|
||||||
Endpoint = endpoint,
|
|
||||||
Parameters = parsed.Segments.Select(s => s.IsSimple && s.Parts[0].IsParameter ? s.Parts[0].Name : null).ToArray(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private class Entry
|
|
||||||
{
|
|
||||||
public int Order;
|
|
||||||
public decimal Precedence;
|
|
||||||
public RouteTemplate Pattern;
|
|
||||||
public MatcherEndpoint Endpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class InstructionBuilder
|
|
||||||
{
|
|
||||||
private readonly List<Instruction> _instructions = new List<Instruction>();
|
|
||||||
private readonly List<MatcherEndpoint> _endpoints = new List<MatcherEndpoint>();
|
|
||||||
private readonly List<JumpTableBuilder> _tables = new List<JumpTableBuilder>();
|
|
||||||
|
|
||||||
private readonly List<int> _blocks = new List<int>();
|
|
||||||
|
|
||||||
public int Next => _instructions.Count;
|
|
||||||
|
|
||||||
public void BeginBlock()
|
|
||||||
{
|
|
||||||
_blocks.Add(Next);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void EndBlock()
|
|
||||||
{
|
|
||||||
var start = _blocks[_blocks.Count - 1];
|
|
||||||
var end = Next;
|
|
||||||
for (var i = start; i < end; i++)
|
|
||||||
{
|
|
||||||
if (_instructions[i].Code == InstructionCode.Pop)
|
|
||||||
{
|
|
||||||
_instructions[i] = new Instruction()
|
|
||||||
{
|
|
||||||
Code = InstructionCode.Jump,
|
|
||||||
Depth = _instructions[i].Depth,
|
|
||||||
Payload = end,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_blocks.RemoveAt(_blocks.Count - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int AddInstruction(Instruction instruction)
|
|
||||||
{
|
|
||||||
_instructions.Add(instruction);
|
|
||||||
return _instructions.Count - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int AddEndpoint(MatcherEndpoint endpoint)
|
|
||||||
{
|
|
||||||
_endpoints.Add(endpoint);
|
|
||||||
return _endpoints.Count - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int AddJumpTable(JumpTableBuilder table)
|
|
||||||
{
|
|
||||||
_tables.Add(table);
|
|
||||||
return _tables.Count - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Deconstruct(
|
|
||||||
out Instruction[] instructions,
|
|
||||||
out MatcherEndpoint[] endpoints,
|
|
||||||
out JumpTable[] tables)
|
|
||||||
{
|
|
||||||
instructions = _instructions.ToArray();
|
|
||||||
endpoints = _endpoints.ToArray();
|
|
||||||
|
|
||||||
tables = new JumpTable[_tables.Count];
|
|
||||||
for (var i = 0; i < _tables.Count; i++)
|
|
||||||
{
|
|
||||||
tables[i] = _tables[i].Build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private abstract class Node
|
|
||||||
{
|
|
||||||
public int Depth { get; protected set; }
|
|
||||||
public List<Node> Children { get; } = new List<Node>();
|
|
||||||
|
|
||||||
public abstract void Lower(InstructionBuilder builder);
|
|
||||||
|
|
||||||
public TNode GetNode<TNode>() where TNode : Node
|
|
||||||
{
|
|
||||||
for (var i = 0; i < Children.Count; i++)
|
|
||||||
{
|
|
||||||
if (Children[i] is TNode match)
|
|
||||||
{
|
|
||||||
return match;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public TNode AddNode<TNode>(TNode node) where TNode : Node
|
|
||||||
{
|
|
||||||
// We already ordered the routes into precedence order
|
|
||||||
Children.Add(node);
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class SequenceNode : Node
|
|
||||||
{
|
|
||||||
public SequenceNode(int depth)
|
|
||||||
{
|
|
||||||
Depth = depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Lower(InstructionBuilder builder)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < Children.Count; i++)
|
|
||||||
{
|
|
||||||
Children[i].Lower(builder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class OrderNode : SequenceNode
|
|
||||||
{
|
|
||||||
public OrderNode(int order)
|
|
||||||
: base(0)
|
|
||||||
{
|
|
||||||
Order = order;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int Order { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private class BranchNode : Node
|
|
||||||
{
|
|
||||||
public BranchNode(int depth)
|
|
||||||
{
|
|
||||||
Depth = depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<string> Literals { get; } = new List<string>();
|
|
||||||
|
|
||||||
public override void Lower(InstructionBuilder builder)
|
|
||||||
{
|
|
||||||
var table = new JumpTableBuilder() { Depth = Depth, };
|
|
||||||
var index = builder.AddJumpTable(table);
|
|
||||||
builder.AddInstruction(new Instruction()
|
|
||||||
{
|
|
||||||
Code = InstructionCode.Branch,
|
|
||||||
Depth = (byte)Depth,
|
|
||||||
Payload = index
|
|
||||||
});
|
|
||||||
|
|
||||||
builder.BeginBlock();
|
|
||||||
|
|
||||||
for (var i = 0; i < Children.Count; i++)
|
|
||||||
{
|
|
||||||
table.AddEntry(Literals[i], builder.Next);
|
|
||||||
Children[i].Lower(builder);
|
|
||||||
builder.AddInstruction(new Instruction()
|
|
||||||
{
|
|
||||||
Code = InstructionCode.Pop,
|
|
||||||
Depth = (byte)Depth,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.EndBlock();
|
|
||||||
table.Exit = builder.Next;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class ParameterNode : Node
|
|
||||||
{
|
|
||||||
public ParameterNode(int depth)
|
|
||||||
{
|
|
||||||
Depth = depth;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Lower(InstructionBuilder builder)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < Children.Count; i++)
|
|
||||||
{
|
|
||||||
Children[i].Lower(builder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class AcceptNode : Node
|
|
||||||
{
|
|
||||||
public AcceptNode(int depth, MatcherEndpoint endpoint)
|
|
||||||
{
|
|
||||||
Depth = depth;
|
|
||||||
Endpoint = endpoint;
|
|
||||||
}
|
|
||||||
|
|
||||||
public MatcherEndpoint Endpoint { get; }
|
|
||||||
|
|
||||||
public override void Lower(InstructionBuilder builder)
|
|
||||||
{
|
|
||||||
builder.AddInstruction(new Instruction()
|
|
||||||
{
|
|
||||||
Code = InstructionCode.Accept,
|
|
||||||
Depth = (byte)Depth,
|
|
||||||
Payload = builder.AddEndpoint(Endpoint),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,18 +0,0 @@
|
||||||
// Copyright (c) .NET Foundation. All rights reserved.
|
|
||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
|
||||||
|
|
||||||
namespace Microsoft.AspNetCore.Routing.Matchers
|
|
||||||
{
|
|
||||||
public class InstructionMatcherConformanceTest : MatcherConformanceTest
|
|
||||||
{
|
|
||||||
internal override Matcher CreateMatcher(params MatcherEndpoint[] endpoints)
|
|
||||||
{
|
|
||||||
var builder = new InstructionMatcherBuilder();
|
|
||||||
for (int i = 0; i < endpoints.Length; i++)
|
|
||||||
{
|
|
||||||
builder.AddEndpoint(endpoints[i]);
|
|
||||||
}
|
|
||||||
return builder.Build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue