Add benchmarks for matcher implementations

This commit is contained in:
Ryan Nowak 2018-06-05 21:46:20 -07:00
parent aef5dc4e93
commit 49f839a286
15 changed files with 23504 additions and 0 deletions

View File

@ -0,0 +1,60 @@
// 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.Threading.Tasks;
using BenchmarkDotNet.Attributes;
namespace Microsoft.AspNetCore.Routing.Matchers
{
// Generated from https://github.com/Azure/azure-rest-api-specs
public partial class AzureMatcherBenchmark : MatcherBenchmarkBase
{
private const int SampleCount = 100;
private Matcher _route;
private Matcher _tree;
private int[] _samples;
[GlobalSetup]
public void Setup()
{
SetupEndpoints();
SetupRequests();
// The perf is kinda slow for these benchmarks, so we do some sampling
// of the request data.
_samples = SampleRequests(EndpointCount, SampleCount);
_route = SetupMatcher(RouteMatcher.CreateBuilder());
_tree = SetupMatcher(TreeRouterMatcher.CreateBuilder());
}
[Benchmark(OperationsPerInvoke = SampleCount)]
public async Task LegacyRoute()
{
var feature = new EndpointFeature();
for (var i = 0; i < SampleCount; i++)
{
var sample = _samples[i];
var httpContext = _requests[sample];
await _route.MatchAsync(httpContext, feature);
Validate(httpContext, _endpoints[sample], feature.Endpoint);
}
}
[Benchmark(OperationsPerInvoke = SampleCount)]
public async Task LegacyTreeRouter()
{
var feature = new EndpointFeature();
for (var i = 0; i < SampleCount; i++)
{
var sample = _samples[i];
var httpContext = _requests[sample];
await _tree.MatchAsync(httpContext, feature);
Validate(httpContext, _endpoints[sample], feature.Endpoint);
}
}
}
}

View File

@ -0,0 +1,353 @@
// 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 System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Template;
namespace Microsoft.AspNetCore.Routing.Matchers
{
internal class DfaMatcher : Matcher
{
public static MatcherBuilder CreateBuilder() => new Builder();
private readonly State[] _states;
private DfaMatcher(State[] states)
{
_states = states;
}
public override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
if (feature == null)
{
throw new ArgumentNullException(nameof(feature));
}
var states = _states;
var current = 0;
var path = httpContext.Request.Path.Value;
var start = 1; // PathString always has a leading slash
var length = 0;
var end = 0;
while ((end = path.IndexOf('/', start + 1)) >= 0)
{
current = states[current].Transitions.GetDestination(path, start, end - start);
start = end;
}
// residue
length = path.Length - start;
if (length > 0)
{
current = states[current].Transitions.GetDestination(path, start, length);
}
var matches = states[current].Matches;
feature.Endpoint = matches.Length == 0 ? null : matches[0];
return Task.CompletedTask;
}
private struct State
{
public bool IsAccepting;
public Endpoint[] Matches;
public JumpTable Transitions;
}
private abstract class JumpTable
{
public abstract int GetDestination(string text, int start, int length);
}
private 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());
}
}
private 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 override int GetDestination(string text, int start, int length)
{
for (var i = 0; i < _entries.Length; i++)
{
if (length == _entries[i].text.Length &&
string.Compare(
text,
start,
_entries[i].text,
0,
length,
StringComparison.OrdinalIgnoreCase) == 0)
{
return _entries[i].destination;
}
}
return _exit;
}
}
private class Entry
{
public int Order;
public decimal Precedence;
public RouteTemplate Pattern;
public Endpoint Endpoint;
}
private class Node
{
public int Depth { get; set; }
public List<Entry> Matches { get; } = new List<Entry>();
public Dictionary<string, Node> Literals { get; } = new Dictionary<string, Node>();
}
private class Builder : MatcherBuilder
{
private List<Entry> _entries = new List<Entry>();
public override void AddEntry(string pattern, MatcherEndpoint endpoint)
{
var parsed = TemplateParser.Parse(pattern);
_entries.Add(new Entry()
{
Order = 0,
Pattern = parsed,
Precedence = RoutePrecedence.ComputeInbound(parsed),
Endpoint = endpoint,
});
}
public override Matcher Build()
{
Sort(_entries);
var root = new Node() { Depth = -1 };
// Since we overlay parameters onto the literal entries, we do two passes, first we create
// all of the literal nodes, then we 'spread' parameters
for (var i = 0; i < _entries.Count; i++)
{
var entry = _entries[i];
var parent = root;
for (var depth = 0; depth < entry.Pattern.Segments.Count; depth++)
{
var segment = entry.Pattern.Segments[depth];
if (segment.IsSimple && segment.Parts[0].IsLiteral)
{
if (!parent.Literals.TryGetValue(segment.Parts[0].Text, out var next))
{
next = new Node() { Depth = depth, };
parent.Literals.Add(segment.Parts[0].Text, next);
}
parent = next;
}
else if (segment.IsSimple && segment.Parts[0].IsParameter)
{
if (!parent.Literals.TryGetValue("*", out var next))
{
next = new Node() { Depth = depth, };
parent.Literals.Add("*", next);
}
parent = next;
}
}
parent.Matches.Add(entry);
}
for (var i = 0; i < _entries.Count; i++)
{
var entry = _entries[i];
var parents = new List<Node>() { root, };
for (var depth = 0; depth < entry.Pattern.Segments.Count; depth++)
{
var segment = entry.Pattern.Segments[depth];
if (segment.IsSimple && segment.Parts[0].IsLiteral)
{
var next = new List<Node>();
for (var j = 0; j < parents.Count; j++)
{
if (!parents[j].Literals.TryGetValue(segment.Parts[0].Text, out var child))
{
child = new Node() { Depth = depth, };
if (parents[j].Literals.TryGetValue("*", out var parameter))
{
child.Matches.AddRange(parameter.Matches);
foreach (var kvp in parameter.Literals)
{
child.Literals.Add(kvp.Key, DeepCopy(kvp.Value));
}
}
parents[j].Literals.Add(segment.Parts[0].Text, child);
}
next.Add(child);
}
parents = next;
}
else if (segment.IsSimple && segment.Parts[0].IsParameter)
{
var next = new List<Node>();
for (var j = 0; j < parents.Count; j++)
{
next.AddRange(parents[j].Literals.Values);
}
parents = next;
}
}
for (var j = 0; j < parents.Count; j++)
{
if (!parents[j].Matches.Contains(entry))
{
parents[j].Matches.Add(entry);
}
}
}
var states = new List<State>();
var tables = new List<JumpTableBuilder>();
AddNode(root, states, tables);
var exit = states.Count;
states.Add(new State() { IsAccepting = false, Matches = Array.Empty<Endpoint>(), });
tables.Add(new JumpTableBuilder() { Exit = exit, });
for (var i = 0; i < tables.Count; i++)
{
if (tables[i].Exit == -1)
{
tables[i].Exit = exit;
}
}
for (var i = 0; i < states.Count; i++)
{
states[i] = new State()
{
IsAccepting = states[i].IsAccepting,
Matches = states[i].Matches,
Transitions = tables[i].Build(),
};
}
return new DfaMatcher(states.ToArray());
}
private static int AddNode(Node node, List<State> states, List<JumpTableBuilder> tables)
{
Sort(node.Matches);
var index = states.Count;
states.Add(new State() { Matches = node.Matches.Select(e => e.Endpoint).ToArray(), IsAccepting = node.Matches.Count > 0 });
var table = new JumpTableBuilder() { Depth = node.Depth, };
tables.Add(table);
foreach (var kvp in node.Literals)
{
if (kvp.Key == "*")
{
continue;
}
var transition = AddNode(kvp.Value, states, tables);
table.AddEntry(kvp.Key, transition);
}
var exitIndex = -1;
if (node.Literals.TryGetValue("*", out var exit))
{
exitIndex = AddNode(exit, states, tables);
}
table.Exit = exitIndex;
return index;
}
private static void Sort(List<Entry> entries)
{
entries.Sort((x, y) =>
{
var comparison = x.Order.CompareTo(y.Order);
if (comparison != 0)
{
return comparison;
}
comparison = y.Precedence.CompareTo(x.Precedence);
if (comparison != 0)
{
return comparison;
}
return x.Pattern.TemplateText.CompareTo(y.Pattern.TemplateText);
});
}
private static Node DeepCopy(Node node)
{
var copy = new Node() { Depth = node.Depth, };
copy.Matches.AddRange(node.Matches);
foreach (var kvp in node.Literals)
{
copy.Literals.Add(kvp.Key, DeepCopy(kvp.Value));
}
return node;
}
}
}
}

View File

@ -0,0 +1,51 @@
// 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.Threading.Tasks;
using BenchmarkDotNet.Attributes;
namespace Microsoft.AspNetCore.Routing.Matchers
{
// Generated from https://github.com/APIs-guru/openapi-directory
// Use https://editor2.swagger.io/ to convert from yaml to json-
public partial class GithubMatcherBenchmark : MatcherBenchmarkBase
{
private Matcher _route;
private Matcher _tree;
[GlobalSetup]
public void Setup()
{
SetupEndpoints();
SetupRequests();
_route = SetupMatcher(RouteMatcher.CreateBuilder());
_tree = SetupMatcher(TreeRouterMatcher.CreateBuilder());
}
[Benchmark(OperationsPerInvoke = EndpointCount)]
public async Task LegacyRoute()
{
var feature = new EndpointFeature();
for (var i = 0; i < EndpointCount; i++)
{
var httpContext = _requests[i];
await _route.MatchAsync(httpContext, feature);
Validate(httpContext, _endpoints[i], feature.Endpoint);
}
}
[Benchmark(OperationsPerInvoke = EndpointCount)]
public async Task LegacyTreeRouter()
{
var feature = new EndpointFeature();
for (var i = 0; i < EndpointCount; i++)
{
var httpContext = _requests[i];
await _tree.MatchAsync(httpContext, feature);
Validate(httpContext, _endpoints[i], feature.Endpoint);
}
}
}
}

View File

@ -0,0 +1,959 @@
// 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 Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing.Matchers
{
// This code was generated by the Swaggatherer
public partial class GithubMatcherBenchmark : MatcherBenchmarkBase
{
private const int EndpointCount = 155;
private void SetupEndpoints()
{
_endpoints = new MatcherEndpoint[155];
_endpoints[0] = CreateEndpoint("/emojis");
_endpoints[1] = CreateEndpoint("/events");
_endpoints[2] = CreateEndpoint("/feeds");
_endpoints[3] = CreateEndpoint("/gists");
_endpoints[4] = CreateEndpoint("/issues");
_endpoints[5] = CreateEndpoint("/markdown");
_endpoints[6] = CreateEndpoint("/meta");
_endpoints[7] = CreateEndpoint("/notifications");
_endpoints[8] = CreateEndpoint("/rate_limit");
_endpoints[9] = CreateEndpoint("/repositories");
_endpoints[10] = CreateEndpoint("/user");
_endpoints[11] = CreateEndpoint("/users");
_endpoints[12] = CreateEndpoint("/gists/public");
_endpoints[13] = CreateEndpoint("/gists/starred");
_endpoints[14] = CreateEndpoint("/gitignore/templates");
_endpoints[15] = CreateEndpoint("/markdown/raw");
_endpoints[16] = CreateEndpoint("/search/code");
_endpoints[17] = CreateEndpoint("/search/issues");
_endpoints[18] = CreateEndpoint("/search/repositories");
_endpoints[19] = CreateEndpoint("/search/users");
_endpoints[20] = CreateEndpoint("/user/emails");
_endpoints[21] = CreateEndpoint("/user/followers");
_endpoints[22] = CreateEndpoint("/user/following");
_endpoints[23] = CreateEndpoint("/user/issues");
_endpoints[24] = CreateEndpoint("/user/keys");
_endpoints[25] = CreateEndpoint("/user/orgs");
_endpoints[26] = CreateEndpoint("/user/repos");
_endpoints[27] = CreateEndpoint("/user/starred");
_endpoints[28] = CreateEndpoint("/user/subscriptions");
_endpoints[29] = CreateEndpoint("/user/teams");
_endpoints[30] = CreateEndpoint("/legacy/repos/search/{keyword}");
_endpoints[31] = CreateEndpoint("/legacy/user/email/{email}");
_endpoints[32] = CreateEndpoint("/legacy/user/search/{keyword}");
_endpoints[33] = CreateEndpoint("/legacy/issues/search/{owner}/{repository}/{state}/{keyword}");
_endpoints[34] = CreateEndpoint("/gitignore/templates/{language}");
_endpoints[35] = CreateEndpoint("/notifications/threads/{id}");
_endpoints[36] = CreateEndpoint("/user/following/{username}");
_endpoints[37] = CreateEndpoint("/user/keys/{keyId}");
_endpoints[38] = CreateEndpoint("/notifications/threads/{id}/subscription");
_endpoints[39] = CreateEndpoint("/user/starred/{owner}/{repo}");
_endpoints[40] = CreateEndpoint("/user/subscriptions/{owner}/{repo}");
_endpoints[41] = CreateEndpoint("/gists/{id}");
_endpoints[42] = CreateEndpoint("/orgs/{org}");
_endpoints[43] = CreateEndpoint("/teams/{teamId}");
_endpoints[44] = CreateEndpoint("/users/{username}");
_endpoints[45] = CreateEndpoint("/gists/{id}/comments");
_endpoints[46] = CreateEndpoint("/gists/{id}/forks");
_endpoints[47] = CreateEndpoint("/gists/{id}/star");
_endpoints[48] = CreateEndpoint("/orgs/{org}/events");
_endpoints[49] = CreateEndpoint("/orgs/{org}/issues");
_endpoints[50] = CreateEndpoint("/orgs/{org}/members");
_endpoints[51] = CreateEndpoint("/orgs/{org}/public_members");
_endpoints[52] = CreateEndpoint("/orgs/{org}/repos");
_endpoints[53] = CreateEndpoint("/orgs/{org}/teams");
_endpoints[54] = CreateEndpoint("/teams/{teamId}/members");
_endpoints[55] = CreateEndpoint("/teams/{teamId}/repos");
_endpoints[56] = CreateEndpoint("/users/{username}/events");
_endpoints[57] = CreateEndpoint("/users/{username}/followers");
_endpoints[58] = CreateEndpoint("/users/{username}/gists");
_endpoints[59] = CreateEndpoint("/users/{username}/keys");
_endpoints[60] = CreateEndpoint("/users/{username}/orgs");
_endpoints[61] = CreateEndpoint("/users/{username}/received_events");
_endpoints[62] = CreateEndpoint("/users/{username}/repos");
_endpoints[63] = CreateEndpoint("/users/{username}/starred");
_endpoints[64] = CreateEndpoint("/users/{username}/subscriptions");
_endpoints[65] = CreateEndpoint("/users/{username}/received_events/public");
_endpoints[66] = CreateEndpoint("/users/{username}/events/orgs/{org}");
_endpoints[67] = CreateEndpoint("/gists/{id}/comments/{commentId}");
_endpoints[68] = CreateEndpoint("/orgs/{org}/members/{username}");
_endpoints[69] = CreateEndpoint("/orgs/{org}/public_members/{username}");
_endpoints[70] = CreateEndpoint("/teams/{teamId}/members/{username}");
_endpoints[71] = CreateEndpoint("/teams/{teamId}/memberships/{username}");
_endpoints[72] = CreateEndpoint("/users/{username}/following/{targetUser}");
_endpoints[73] = CreateEndpoint("/teams/{teamId}/repos/{owner}/{repo}");
_endpoints[74] = CreateEndpoint("/repos/{owner}/{repo}");
_endpoints[75] = CreateEndpoint("/networks/{owner}/{repo}/events");
_endpoints[76] = CreateEndpoint("/repos/{owner}/{repo}/assignees");
_endpoints[77] = CreateEndpoint("/repos/{owner}/{repo}/branches");
_endpoints[78] = CreateEndpoint("/repos/{owner}/{repo}/collaborators");
_endpoints[79] = CreateEndpoint("/repos/{owner}/{repo}/comments");
_endpoints[80] = CreateEndpoint("/repos/{owner}/{repo}/commits");
_endpoints[81] = CreateEndpoint("/repos/{owner}/{repo}/contributors");
_endpoints[82] = CreateEndpoint("/repos/{owner}/{repo}/deployments");
_endpoints[83] = CreateEndpoint("/repos/{owner}/{repo}/downloads");
_endpoints[84] = CreateEndpoint("/repos/{owner}/{repo}/events");
_endpoints[85] = CreateEndpoint("/repos/{owner}/{repo}/forks");
_endpoints[86] = CreateEndpoint("/repos/{owner}/{repo}/hooks");
_endpoints[87] = CreateEndpoint("/repos/{owner}/{repo}/issues");
_endpoints[88] = CreateEndpoint("/repos/{owner}/{repo}/keys");
_endpoints[89] = CreateEndpoint("/repos/{owner}/{repo}/labels");
_endpoints[90] = CreateEndpoint("/repos/{owner}/{repo}/languages");
_endpoints[91] = CreateEndpoint("/repos/{owner}/{repo}/merges");
_endpoints[92] = CreateEndpoint("/repos/{owner}/{repo}/milestones");
_endpoints[93] = CreateEndpoint("/repos/{owner}/{repo}/notifications");
_endpoints[94] = CreateEndpoint("/repos/{owner}/{repo}/pulls");
_endpoints[95] = CreateEndpoint("/repos/{owner}/{repo}/readme");
_endpoints[96] = CreateEndpoint("/repos/{owner}/{repo}/releases");
_endpoints[97] = CreateEndpoint("/repos/{owner}/{repo}/stargazers");
_endpoints[98] = CreateEndpoint("/repos/{owner}/{repo}/subscribers");
_endpoints[99] = CreateEndpoint("/repos/{owner}/{repo}/subscription");
_endpoints[100] = CreateEndpoint("/repos/{owner}/{repo}/tags");
_endpoints[101] = CreateEndpoint("/repos/{owner}/{repo}/teams");
_endpoints[102] = CreateEndpoint("/repos/{owner}/{repo}/watchers");
_endpoints[103] = CreateEndpoint("/repos/{owner}/{repo}/git/blobs");
_endpoints[104] = CreateEndpoint("/repos/{owner}/{repo}/git/commits");
_endpoints[105] = CreateEndpoint("/repos/{owner}/{repo}/git/refs");
_endpoints[106] = CreateEndpoint("/repos/{owner}/{repo}/git/tags");
_endpoints[107] = CreateEndpoint("/repos/{owner}/{repo}/git/trees");
_endpoints[108] = CreateEndpoint("/repos/{owner}/{repo}/issues/comments");
_endpoints[109] = CreateEndpoint("/repos/{owner}/{repo}/issues/events");
_endpoints[110] = CreateEndpoint("/repos/{owner}/{repo}/pulls/comments");
_endpoints[111] = CreateEndpoint("/repos/{owner}/{repo}/stats/code_frequency");
_endpoints[112] = CreateEndpoint("/repos/{owner}/{repo}/stats/commit_activity");
_endpoints[113] = CreateEndpoint("/repos/{owner}/{repo}/stats/contributors");
_endpoints[114] = CreateEndpoint("/repos/{owner}/{repo}/stats/participation");
_endpoints[115] = CreateEndpoint("/repos/{owner}/{repo}/stats/punch_card");
_endpoints[116] = CreateEndpoint("/repos/{owner}/{repo}/git/blobs/{shaCode}");
_endpoints[117] = CreateEndpoint("/repos/{owner}/{repo}/git/commits/{shaCode}");
_endpoints[118] = CreateEndpoint("/repos/{owner}/{repo}/git/refs/{ref}");
_endpoints[119] = CreateEndpoint("/repos/{owner}/{repo}/git/tags/{shaCode}");
_endpoints[120] = CreateEndpoint("/repos/{owner}/{repo}/git/trees/{shaCode}");
_endpoints[121] = CreateEndpoint("/repos/{owner}/{repo}/issues/comments/{commentId}");
_endpoints[122] = CreateEndpoint("/repos/{owner}/{repo}/issues/events/{eventId}");
_endpoints[123] = CreateEndpoint("/repos/{owner}/{repo}/pulls/comments/{commentId}");
_endpoints[124] = CreateEndpoint("/repos/{owner}/{repo}/releases/assets/{id}");
_endpoints[125] = CreateEndpoint("/repos/{owner}/{repo}/assignees/{assignee}");
_endpoints[126] = CreateEndpoint("/repos/{owner}/{repo}/branches/{branch}");
_endpoints[127] = CreateEndpoint("/repos/{owner}/{repo}/collaborators/{user}");
_endpoints[128] = CreateEndpoint("/repos/{owner}/{repo}/comments/{commentId}");
_endpoints[129] = CreateEndpoint("/repos/{owner}/{repo}/commits/{shaCode}");
_endpoints[130] = CreateEndpoint("/repos/{owner}/{repo}/contents/{path}");
_endpoints[131] = CreateEndpoint("/repos/{owner}/{repo}/downloads/{downloadId}");
_endpoints[132] = CreateEndpoint("/repos/{owner}/{repo}/hooks/{hookId}");
_endpoints[133] = CreateEndpoint("/repos/{owner}/{repo}/issues/{number}");
_endpoints[134] = CreateEndpoint("/repos/{owner}/{repo}/keys/{keyId}");
_endpoints[135] = CreateEndpoint("/repos/{owner}/{repo}/labels/{name}");
_endpoints[136] = CreateEndpoint("/repos/{owner}/{repo}/milestones/{number}");
_endpoints[137] = CreateEndpoint("/repos/{owner}/{repo}/pulls/{number}");
_endpoints[138] = CreateEndpoint("/repos/{owner}/{repo}/releases/{id}");
_endpoints[139] = CreateEndpoint("/repos/{owner}/{repo}/statuses/{ref}");
_endpoints[140] = CreateEndpoint("/repos/{owner}/{repo}/commits/{ref}/status");
_endpoints[141] = CreateEndpoint("/repos/{owner}/{repo}/commits/{shaCode}/comments");
_endpoints[142] = CreateEndpoint("/repos/{owner}/{repo}/deployments/{id}/statuses");
_endpoints[143] = CreateEndpoint("/repos/{owner}/{repo}/hooks/{hookId}/tests");
_endpoints[144] = CreateEndpoint("/repos/{owner}/{repo}/issues/{number}/comments");
_endpoints[145] = CreateEndpoint("/repos/{owner}/{repo}/issues/{number}/events");
_endpoints[146] = CreateEndpoint("/repos/{owner}/{repo}/issues/{number}/labels");
_endpoints[147] = CreateEndpoint("/repos/{owner}/{repo}/milestones/{number}/labels");
_endpoints[148] = CreateEndpoint("/repos/{owner}/{repo}/pulls/{number}/comments");
_endpoints[149] = CreateEndpoint("/repos/{owner}/{repo}/pulls/{number}/commits");
_endpoints[150] = CreateEndpoint("/repos/{owner}/{repo}/pulls/{number}/files");
_endpoints[151] = CreateEndpoint("/repos/{owner}/{repo}/pulls/{number}/merge");
_endpoints[152] = CreateEndpoint("/repos/{owner}/{repo}/releases/{id}/assets");
_endpoints[153] = CreateEndpoint("/repos/{owner}/{repo}/issues/{number}/labels/{name}");
_endpoints[154] = CreateEndpoint("/repos/{owner}/{repo}/{archive_format}/{path}");
}
private void SetupRequests()
{
_requests = new HttpContext[155];
_requests[0] = new DefaultHttpContext();
_requests[0].RequestServices = CreateServices();
_requests[0].Request.Method = "GET";
_requests[0].Request.Path = "/emojis";
_requests[1] = new DefaultHttpContext();
_requests[1].RequestServices = CreateServices();
_requests[1].Request.Method = "GET";
_requests[1].Request.Path = "/events";
_requests[2] = new DefaultHttpContext();
_requests[2].RequestServices = CreateServices();
_requests[2].Request.Method = "GET";
_requests[2].Request.Path = "/feeds";
_requests[3] = new DefaultHttpContext();
_requests[3].RequestServices = CreateServices();
_requests[3].Request.Method = "POST";
_requests[3].Request.Path = "/gists";
_requests[4] = new DefaultHttpContext();
_requests[4].RequestServices = CreateServices();
_requests[4].Request.Method = "GET";
_requests[4].Request.Path = "/issues";
_requests[5] = new DefaultHttpContext();
_requests[5].RequestServices = CreateServices();
_requests[5].Request.Method = "POST";
_requests[5].Request.Path = "/markdown";
_requests[6] = new DefaultHttpContext();
_requests[6].RequestServices = CreateServices();
_requests[6].Request.Method = "GET";
_requests[6].Request.Path = "/meta";
_requests[7] = new DefaultHttpContext();
_requests[7].RequestServices = CreateServices();
_requests[7].Request.Method = "PUT";
_requests[7].Request.Path = "/notifications";
_requests[8] = new DefaultHttpContext();
_requests[8].RequestServices = CreateServices();
_requests[8].Request.Method = "GET";
_requests[8].Request.Path = "/rate_limit";
_requests[9] = new DefaultHttpContext();
_requests[9].RequestServices = CreateServices();
_requests[9].Request.Method = "GET";
_requests[9].Request.Path = "/repositories";
_requests[10] = new DefaultHttpContext();
_requests[10].RequestServices = CreateServices();
_requests[10].Request.Method = "PATCH";
_requests[10].Request.Path = "/user";
_requests[11] = new DefaultHttpContext();
_requests[11].RequestServices = CreateServices();
_requests[11].Request.Method = "GET";
_requests[11].Request.Path = "/users";
_requests[12] = new DefaultHttpContext();
_requests[12].RequestServices = CreateServices();
_requests[12].Request.Method = "GET";
_requests[12].Request.Path = "/gists/public";
_requests[13] = new DefaultHttpContext();
_requests[13].RequestServices = CreateServices();
_requests[13].Request.Method = "GET";
_requests[13].Request.Path = "/gists/starred";
_requests[14] = new DefaultHttpContext();
_requests[14].RequestServices = CreateServices();
_requests[14].Request.Method = "GET";
_requests[14].Request.Path = "/gitignore/templates";
_requests[15] = new DefaultHttpContext();
_requests[15].RequestServices = CreateServices();
_requests[15].Request.Method = "POST";
_requests[15].Request.Path = "/markdown/raw";
_requests[16] = new DefaultHttpContext();
_requests[16].RequestServices = CreateServices();
_requests[16].Request.Method = "GET";
_requests[16].Request.Path = "/search/code";
_requests[17] = new DefaultHttpContext();
_requests[17].RequestServices = CreateServices();
_requests[17].Request.Method = "GET";
_requests[17].Request.Path = "/search/issues";
_requests[18] = new DefaultHttpContext();
_requests[18].RequestServices = CreateServices();
_requests[18].Request.Method = "GET";
_requests[18].Request.Path = "/search/repositories";
_requests[19] = new DefaultHttpContext();
_requests[19].RequestServices = CreateServices();
_requests[19].Request.Method = "GET";
_requests[19].Request.Path = "/search/users";
_requests[20] = new DefaultHttpContext();
_requests[20].RequestServices = CreateServices();
_requests[20].Request.Method = "POST";
_requests[20].Request.Path = "/user/emails";
_requests[21] = new DefaultHttpContext();
_requests[21].RequestServices = CreateServices();
_requests[21].Request.Method = "GET";
_requests[21].Request.Path = "/user/followers";
_requests[22] = new DefaultHttpContext();
_requests[22].RequestServices = CreateServices();
_requests[22].Request.Method = "GET";
_requests[22].Request.Path = "/user/following";
_requests[23] = new DefaultHttpContext();
_requests[23].RequestServices = CreateServices();
_requests[23].Request.Method = "GET";
_requests[23].Request.Path = "/user/issues";
_requests[24] = new DefaultHttpContext();
_requests[24].RequestServices = CreateServices();
_requests[24].Request.Method = "POST";
_requests[24].Request.Path = "/user/keys";
_requests[25] = new DefaultHttpContext();
_requests[25].RequestServices = CreateServices();
_requests[25].Request.Method = "GET";
_requests[25].Request.Path = "/user/orgs";
_requests[26] = new DefaultHttpContext();
_requests[26].RequestServices = CreateServices();
_requests[26].Request.Method = "POST";
_requests[26].Request.Path = "/user/repos";
_requests[27] = new DefaultHttpContext();
_requests[27].RequestServices = CreateServices();
_requests[27].Request.Method = "GET";
_requests[27].Request.Path = "/user/starred";
_requests[28] = new DefaultHttpContext();
_requests[28].RequestServices = CreateServices();
_requests[28].Request.Method = "GET";
_requests[28].Request.Path = "/user/subscriptions";
_requests[29] = new DefaultHttpContext();
_requests[29].RequestServices = CreateServices();
_requests[29].Request.Method = "GET";
_requests[29].Request.Path = "/user/teams";
_requests[30] = new DefaultHttpContext();
_requests[30].RequestServices = CreateServices();
_requests[30].Request.Method = "GET";
_requests[30].Request.Path = "/legacy/repos/search/98f2805";
_requests[31] = new DefaultHttpContext();
_requests[31].RequestServices = CreateServices();
_requests[31].Request.Method = "GET";
_requests[31].Request.Path = "/legacy/user/email/d65b3";
_requests[32] = new DefaultHttpContext();
_requests[32].RequestServices = CreateServices();
_requests[32].Request.Method = "GET";
_requests[32].Request.Path = "/legacy/user/search/edc2298";
_requests[33] = new DefaultHttpContext();
_requests[33].RequestServices = CreateServices();
_requests[33].Request.Method = "GET";
_requests[33].Request.Path = "/legacy/issues/search/7f959/b12ea0da-b/49a3b/b371d06";
_requests[34] = new DefaultHttpContext();
_requests[34].RequestServices = CreateServices();
_requests[34].Request.Method = "GET";
_requests[34].Request.Path = "/gitignore/templates/0cb5dc32";
_requests[35] = new DefaultHttpContext();
_requests[35].RequestServices = CreateServices();
_requests[35].Request.Method = "PATCH";
_requests[35].Request.Path = "/notifications/threads/4bd8e";
_requests[36] = new DefaultHttpContext();
_requests[36].RequestServices = CreateServices();
_requests[36].Request.Method = "PUT";
_requests[36].Request.Path = "/user/following/e2c3a018";
_requests[37] = new DefaultHttpContext();
_requests[37].RequestServices = CreateServices();
_requests[37].Request.Method = "GET";
_requests[37].Request.Path = "/user/keys/7d82b";
_requests[38] = new DefaultHttpContext();
_requests[38].RequestServices = CreateServices();
_requests[38].Request.Method = "PUT";
_requests[38].Request.Path = "/notifications/threads/dd67d/subscription";
_requests[39] = new DefaultHttpContext();
_requests[39].RequestServices = CreateServices();
_requests[39].Request.Method = "PUT";
_requests[39].Request.Path = "/user/starred/0b99c/8783e";
_requests[40] = new DefaultHttpContext();
_requests[40].RequestServices = CreateServices();
_requests[40].Request.Method = "PUT";
_requests[40].Request.Path = "/user/subscriptions/384c7/2c13f";
_requests[41] = new DefaultHttpContext();
_requests[41].RequestServices = CreateServices();
_requests[41].Request.Method = "PATCH";
_requests[41].Request.Path = "/gists/e6d49";
_requests[42] = new DefaultHttpContext();
_requests[42].RequestServices = CreateServices();
_requests[42].Request.Method = "PATCH";
_requests[42].Request.Path = "/orgs/8ae9c";
_requests[43] = new DefaultHttpContext();
_requests[43].RequestServices = CreateServices();
_requests[43].Request.Method = "PATCH";
_requests[43].Request.Path = "/teams/4a7cc7";
_requests[44] = new DefaultHttpContext();
_requests[44].RequestServices = CreateServices();
_requests[44].Request.Method = "GET";
_requests[44].Request.Path = "/users/0a04d291";
_requests[45] = new DefaultHttpContext();
_requests[45].RequestServices = CreateServices();
_requests[45].Request.Method = "POST";
_requests[45].Request.Path = "/gists/08da6/comments";
_requests[46] = new DefaultHttpContext();
_requests[46].RequestServices = CreateServices();
_requests[46].Request.Method = "POST";
_requests[46].Request.Path = "/gists/fca54/forks";
_requests[47] = new DefaultHttpContext();
_requests[47].RequestServices = CreateServices();
_requests[47].Request.Method = "PUT";
_requests[47].Request.Path = "/gists/31f75/star";
_requests[48] = new DefaultHttpContext();
_requests[48].RequestServices = CreateServices();
_requests[48].Request.Method = "GET";
_requests[48].Request.Path = "/orgs/2b352/events";
_requests[49] = new DefaultHttpContext();
_requests[49].RequestServices = CreateServices();
_requests[49].Request.Method = "GET";
_requests[49].Request.Path = "/orgs/e08a9/issues";
_requests[50] = new DefaultHttpContext();
_requests[50].RequestServices = CreateServices();
_requests[50].Request.Method = "GET";
_requests[50].Request.Path = "/orgs/fddd5/members";
_requests[51] = new DefaultHttpContext();
_requests[51].RequestServices = CreateServices();
_requests[51].Request.Method = "GET";
_requests[51].Request.Path = "/orgs/a0018/public_members";
_requests[52] = new DefaultHttpContext();
_requests[52].RequestServices = CreateServices();
_requests[52].Request.Method = "POST";
_requests[52].Request.Path = "/orgs/459e6/repos";
_requests[53] = new DefaultHttpContext();
_requests[53].RequestServices = CreateServices();
_requests[53].Request.Method = "POST";
_requests[53].Request.Path = "/orgs/c50a3/teams";
_requests[54] = new DefaultHttpContext();
_requests[54].RequestServices = CreateServices();
_requests[54].Request.Method = "GET";
_requests[54].Request.Path = "/teams/6d7c70/members";
_requests[55] = new DefaultHttpContext();
_requests[55].RequestServices = CreateServices();
_requests[55].Request.Method = "GET";
_requests[55].Request.Path = "/teams/2c72a5/repos";
_requests[56] = new DefaultHttpContext();
_requests[56].RequestServices = CreateServices();
_requests[56].Request.Method = "GET";
_requests[56].Request.Path = "/users/0d7c82bc/events";
_requests[57] = new DefaultHttpContext();
_requests[57].RequestServices = CreateServices();
_requests[57].Request.Method = "GET";
_requests[57].Request.Path = "/users/dad59333/followers";
_requests[58] = new DefaultHttpContext();
_requests[58].RequestServices = CreateServices();
_requests[58].Request.Method = "GET";
_requests[58].Request.Path = "/users/d3cfc2f7/gists";
_requests[59] = new DefaultHttpContext();
_requests[59].RequestServices = CreateServices();
_requests[59].Request.Method = "GET";
_requests[59].Request.Path = "/users/74de59eb/keys";
_requests[60] = new DefaultHttpContext();
_requests[60].RequestServices = CreateServices();
_requests[60].Request.Method = "GET";
_requests[60].Request.Path = "/users/c8078ace/orgs";
_requests[61] = new DefaultHttpContext();
_requests[61].RequestServices = CreateServices();
_requests[61].Request.Method = "GET";
_requests[61].Request.Path = "/users/7bb096ec/received_events";
_requests[62] = new DefaultHttpContext();
_requests[62].RequestServices = CreateServices();
_requests[62].Request.Method = "GET";
_requests[62].Request.Path = "/users/8208dce8/repos";
_requests[63] = new DefaultHttpContext();
_requests[63].RequestServices = CreateServices();
_requests[63].Request.Method = "GET";
_requests[63].Request.Path = "/users/8da3e05c/starred";
_requests[64] = new DefaultHttpContext();
_requests[64].RequestServices = CreateServices();
_requests[64].Request.Method = "GET";
_requests[64].Request.Path = "/users/ddcd1749/subscriptions";
_requests[65] = new DefaultHttpContext();
_requests[65].RequestServices = CreateServices();
_requests[65].Request.Method = "GET";
_requests[65].Request.Path = "/users/528d35ef/received_events/public";
_requests[66] = new DefaultHttpContext();
_requests[66].RequestServices = CreateServices();
_requests[66].Request.Method = "GET";
_requests[66].Request.Path = "/users/9a36a9de/events/orgs/3864a";
_requests[67] = new DefaultHttpContext();
_requests[67].RequestServices = CreateServices();
_requests[67].Request.Method = "PATCH";
_requests[67].Request.Path = "/gists/29a38/comments/f955540b-";
_requests[68] = new DefaultHttpContext();
_requests[68].RequestServices = CreateServices();
_requests[68].Request.Method = "GET";
_requests[68].Request.Path = "/orgs/7b33c/members/fa7c6147";
_requests[69] = new DefaultHttpContext();
_requests[69].RequestServices = CreateServices();
_requests[69].Request.Method = "PUT";
_requests[69].Request.Path = "/orgs/97a4b/public_members/35fecf7f";
_requests[70] = new DefaultHttpContext();
_requests[70].RequestServices = CreateServices();
_requests[70].Request.Method = "PUT";
_requests[70].Request.Path = "/teams/969145/members/194d27ad";
_requests[71] = new DefaultHttpContext();
_requests[71].RequestServices = CreateServices();
_requests[71].Request.Method = "PUT";
_requests[71].Request.Path = "/teams/90a622/memberships/88477f8b";
_requests[72] = new DefaultHttpContext();
_requests[72].RequestServices = CreateServices();
_requests[72].Request.Method = "GET";
_requests[72].Request.Path = "/users/efc0aad4/following/bc58bd8c-7";
_requests[73] = new DefaultHttpContext();
_requests[73].RequestServices = CreateServices();
_requests[73].Request.Method = "GET";
_requests[73].Request.Path = "/teams/98d389/repos/74b04/05c77";
_requests[74] = new DefaultHttpContext();
_requests[74].RequestServices = CreateServices();
_requests[74].Request.Method = "PATCH";
_requests[74].Request.Path = "/repos/fbc82/446db";
_requests[75] = new DefaultHttpContext();
_requests[75].RequestServices = CreateServices();
_requests[75].Request.Method = "GET";
_requests[75].Request.Path = "/networks/d914d/57c64/events";
_requests[76] = new DefaultHttpContext();
_requests[76].RequestServices = CreateServices();
_requests[76].Request.Method = "GET";
_requests[76].Request.Path = "/repos/67afd/b1bc5/assignees";
_requests[77] = new DefaultHttpContext();
_requests[77].RequestServices = CreateServices();
_requests[77].Request.Method = "GET";
_requests[77].Request.Path = "/repos/b2f87/ced1b/branches";
_requests[78] = new DefaultHttpContext();
_requests[78].RequestServices = CreateServices();
_requests[78].Request.Method = "GET";
_requests[78].Request.Path = "/repos/4c0cd/a3d94/collaborators";
_requests[79] = new DefaultHttpContext();
_requests[79].RequestServices = CreateServices();
_requests[79].Request.Method = "GET";
_requests[79].Request.Path = "/repos/1eab2/4cbac/comments";
_requests[80] = new DefaultHttpContext();
_requests[80].RequestServices = CreateServices();
_requests[80].Request.Method = "GET";
_requests[80].Request.Path = "/repos/5981b/de5a8/commits";
_requests[81] = new DefaultHttpContext();
_requests[81].RequestServices = CreateServices();
_requests[81].Request.Method = "GET";
_requests[81].Request.Path = "/repos/7ad40/db6ee/contributors";
_requests[82] = new DefaultHttpContext();
_requests[82].RequestServices = CreateServices();
_requests[82].Request.Method = "POST";
_requests[82].Request.Path = "/repos/8972a/86bb7/deployments";
_requests[83] = new DefaultHttpContext();
_requests[83].RequestServices = CreateServices();
_requests[83].Request.Method = "GET";
_requests[83].Request.Path = "/repos/c619b/d8f5a/downloads";
_requests[84] = new DefaultHttpContext();
_requests[84].RequestServices = CreateServices();
_requests[84].Request.Method = "GET";
_requests[84].Request.Path = "/repos/91509/bb53c/events";
_requests[85] = new DefaultHttpContext();
_requests[85].RequestServices = CreateServices();
_requests[85].Request.Method = "POST";
_requests[85].Request.Path = "/repos/9da59/f78cd/forks";
_requests[86] = new DefaultHttpContext();
_requests[86].RequestServices = CreateServices();
_requests[86].Request.Method = "POST";
_requests[86].Request.Path = "/repos/1484a/4e077/hooks";
_requests[87] = new DefaultHttpContext();
_requests[87].RequestServices = CreateServices();
_requests[87].Request.Method = "POST";
_requests[87].Request.Path = "/repos/c5793/4f77e/issues";
_requests[88] = new DefaultHttpContext();
_requests[88].RequestServices = CreateServices();
_requests[88].Request.Method = "POST";
_requests[88].Request.Path = "/repos/d4e39/2dd22/keys";
_requests[89] = new DefaultHttpContext();
_requests[89].RequestServices = CreateServices();
_requests[89].Request.Method = "POST";
_requests[89].Request.Path = "/repos/bd4f0/9cc66/labels";
_requests[90] = new DefaultHttpContext();
_requests[90].RequestServices = CreateServices();
_requests[90].Request.Method = "GET";
_requests[90].Request.Path = "/repos/6b934/aa4bd/languages";
_requests[91] = new DefaultHttpContext();
_requests[91].RequestServices = CreateServices();
_requests[91].Request.Method = "POST";
_requests[91].Request.Path = "/repos/8830a/88a98/merges";
_requests[92] = new DefaultHttpContext();
_requests[92].RequestServices = CreateServices();
_requests[92].Request.Method = "POST";
_requests[92].Request.Path = "/repos/9ce99/29222/milestones";
_requests[93] = new DefaultHttpContext();
_requests[93].RequestServices = CreateServices();
_requests[93].Request.Method = "PUT";
_requests[93].Request.Path = "/repos/a106c/ac6a4/notifications";
_requests[94] = new DefaultHttpContext();
_requests[94].RequestServices = CreateServices();
_requests[94].Request.Method = "POST";
_requests[94].Request.Path = "/repos/0e52f/ca9e5/pulls";
_requests[95] = new DefaultHttpContext();
_requests[95].RequestServices = CreateServices();
_requests[95].Request.Method = "GET";
_requests[95].Request.Path = "/repos/37e7a/7aa03/readme";
_requests[96] = new DefaultHttpContext();
_requests[96].RequestServices = CreateServices();
_requests[96].Request.Method = "POST";
_requests[96].Request.Path = "/repos/cf543/a26e5/releases";
_requests[97] = new DefaultHttpContext();
_requests[97].RequestServices = CreateServices();
_requests[97].Request.Method = "GET";
_requests[97].Request.Path = "/repos/26330/2998f/stargazers";
_requests[98] = new DefaultHttpContext();
_requests[98].RequestServices = CreateServices();
_requests[98].Request.Method = "GET";
_requests[98].Request.Path = "/repos/4192a/8d012/subscribers";
_requests[99] = new DefaultHttpContext();
_requests[99].RequestServices = CreateServices();
_requests[99].Request.Method = "PUT";
_requests[99].Request.Path = "/repos/7bf2f/ec3e8/subscription";
_requests[100] = new DefaultHttpContext();
_requests[100].RequestServices = CreateServices();
_requests[100].Request.Method = "GET";
_requests[100].Request.Path = "/repos/9285d/2386a/tags";
_requests[101] = new DefaultHttpContext();
_requests[101].RequestServices = CreateServices();
_requests[101].Request.Method = "GET";
_requests[101].Request.Path = "/repos/ba9c2/c995c/teams";
_requests[102] = new DefaultHttpContext();
_requests[102].RequestServices = CreateServices();
_requests[102].Request.Method = "GET";
_requests[102].Request.Path = "/repos/15c8d/35395/watchers";
_requests[103] = new DefaultHttpContext();
_requests[103].RequestServices = CreateServices();
_requests[103].Request.Method = "POST";
_requests[103].Request.Path = "/repos/3e849/531ef/git/blobs";
_requests[104] = new DefaultHttpContext();
_requests[104].RequestServices = CreateServices();
_requests[104].Request.Method = "POST";
_requests[104].Request.Path = "/repos/3bcbc/5d8bc/git/commits";
_requests[105] = new DefaultHttpContext();
_requests[105].RequestServices = CreateServices();
_requests[105].Request.Method = "POST";
_requests[105].Request.Path = "/repos/65917/4297b/git/refs";
_requests[106] = new DefaultHttpContext();
_requests[106].RequestServices = CreateServices();
_requests[106].Request.Method = "POST";
_requests[106].Request.Path = "/repos/5aadb/450d4/git/tags";
_requests[107] = new DefaultHttpContext();
_requests[107].RequestServices = CreateServices();
_requests[107].Request.Method = "POST";
_requests[107].Request.Path = "/repos/4c306/f056f/git/trees";
_requests[108] = new DefaultHttpContext();
_requests[108].RequestServices = CreateServices();
_requests[108].Request.Method = "GET";
_requests[108].Request.Path = "/repos/9da82/d041c/issues/comments";
_requests[109] = new DefaultHttpContext();
_requests[109].RequestServices = CreateServices();
_requests[109].Request.Method = "GET";
_requests[109].Request.Path = "/repos/06bb5/1352f/issues/events";
_requests[110] = new DefaultHttpContext();
_requests[110].RequestServices = CreateServices();
_requests[110].Request.Method = "GET";
_requests[110].Request.Path = "/repos/f87fb/e0543/pulls/comments";
_requests[111] = new DefaultHttpContext();
_requests[111].RequestServices = CreateServices();
_requests[111].Request.Method = "GET";
_requests[111].Request.Path = "/repos/b6d34/d0b39/stats/code_frequency";
_requests[112] = new DefaultHttpContext();
_requests[112].RequestServices = CreateServices();
_requests[112].Request.Method = "GET";
_requests[112].Request.Path = "/repos/e8d05/4707e/stats/commit_activity";
_requests[113] = new DefaultHttpContext();
_requests[113].RequestServices = CreateServices();
_requests[113].Request.Method = "GET";
_requests[113].Request.Path = "/repos/48d7e/0ecd3/stats/contributors";
_requests[114] = new DefaultHttpContext();
_requests[114].RequestServices = CreateServices();
_requests[114].Request.Method = "GET";
_requests[114].Request.Path = "/repos/8964b/6e8f5/stats/participation";
_requests[115] = new DefaultHttpContext();
_requests[115].RequestServices = CreateServices();
_requests[115].Request.Method = "GET";
_requests[115].Request.Path = "/repos/37374/f039d/stats/punch_card";
_requests[116] = new DefaultHttpContext();
_requests[116].RequestServices = CreateServices();
_requests[116].Request.Method = "GET";
_requests[116].Request.Path = "/repos/14566/a474b/git/blobs/04b85a8";
_requests[117] = new DefaultHttpContext();
_requests[117].RequestServices = CreateServices();
_requests[117].Request.Method = "GET";
_requests[117].Request.Path = "/repos/31a30/3ea34/git/commits/27948b9";
_requests[118] = new DefaultHttpContext();
_requests[118].RequestServices = CreateServices();
_requests[118].Request.Method = "PATCH";
_requests[118].Request.Path = "/repos/2c8ad/e2765/git/refs/67cc5";
_requests[119] = new DefaultHttpContext();
_requests[119].RequestServices = CreateServices();
_requests[119].Request.Method = "GET";
_requests[119].Request.Path = "/repos/739be/3acb7/git/tags/6c6430c";
_requests[120] = new DefaultHttpContext();
_requests[120].RequestServices = CreateServices();
_requests[120].Request.Method = "GET";
_requests[120].Request.Path = "/repos/5a112/21ae4/git/trees/fe88304";
_requests[121] = new DefaultHttpContext();
_requests[121].RequestServices = CreateServices();
_requests[121].Request.Method = "PATCH";
_requests[121].Request.Path = "/repos/76ed1/51c63/issues/comments/c9208b29-";
_requests[122] = new DefaultHttpContext();
_requests[122].RequestServices = CreateServices();
_requests[122].Request.Method = "GET";
_requests[122].Request.Path = "/repos/8230d/17f20/issues/events/5e4ea0f";
_requests[123] = new DefaultHttpContext();
_requests[123].RequestServices = CreateServices();
_requests[123].Request.Method = "PATCH";
_requests[123].Request.Path = "/repos/fa5f3/b4ecf/pulls/comments/1e93db69-";
_requests[124] = new DefaultHttpContext();
_requests[124].RequestServices = CreateServices();
_requests[124].Request.Method = "PATCH";
_requests[124].Request.Path = "/repos/f3b86/5793e/releases/assets/b21e5";
_requests[125] = new DefaultHttpContext();
_requests[125].RequestServices = CreateServices();
_requests[125].Request.Method = "GET";
_requests[125].Request.Path = "/repos/0df2f/f1d8e/assignees/87a41d15";
_requests[126] = new DefaultHttpContext();
_requests[126].RequestServices = CreateServices();
_requests[126].Request.Method = "GET";
_requests[126].Request.Path = "/repos/f0062/df752/branches/d18f91";
_requests[127] = new DefaultHttpContext();
_requests[127].RequestServices = CreateServices();
_requests[127].Request.Method = "PUT";
_requests[127].Request.Path = "/repos/022eb/1696a/collaborators/59494";
_requests[128] = new DefaultHttpContext();
_requests[128].RequestServices = CreateServices();
_requests[128].Request.Method = "PATCH";
_requests[128].Request.Path = "/repos/0f74c/4f864/comments/5a8b1f18-";
_requests[129] = new DefaultHttpContext();
_requests[129].RequestServices = CreateServices();
_requests[129].Request.Method = "GET";
_requests[129].Request.Path = "/repos/08262/d4859/commits/0459132";
_requests[130] = new DefaultHttpContext();
_requests[130].RequestServices = CreateServices();
_requests[130].Request.Method = "PUT";
_requests[130].Request.Path = "/repos/9e587/5da72/contents/3b37c";
_requests[131] = new DefaultHttpContext();
_requests[131].RequestServices = CreateServices();
_requests[131].Request.Method = "GET";
_requests[131].Request.Path = "/repos/aeabf/fb931/downloads/928b7eef-a";
_requests[132] = new DefaultHttpContext();
_requests[132].RequestServices = CreateServices();
_requests[132].Request.Method = "PATCH";
_requests[132].Request.Path = "/repos/cb76c/71512/hooks/e0a971";
_requests[133] = new DefaultHttpContext();
_requests[133].RequestServices = CreateServices();
_requests[133].Request.Method = "PATCH";
_requests[133].Request.Path = "/repos/a44e1/81a70/issues/fedabf";
_requests[134] = new DefaultHttpContext();
_requests[134].RequestServices = CreateServices();
_requests[134].Request.Method = "GET";
_requests[134].Request.Path = "/repos/d776f/19a02/keys/9e5b3";
_requests[135] = new DefaultHttpContext();
_requests[135].RequestServices = CreateServices();
_requests[135].Request.Method = "PATCH";
_requests[135].Request.Path = "/repos/b8532/04123/labels/d3c1d";
_requests[136] = new DefaultHttpContext();
_requests[136].RequestServices = CreateServices();
_requests[136].Request.Method = "PATCH";
_requests[136].Request.Path = "/repos/1d10a/9efd4/milestones/f673c2";
_requests[137] = new DefaultHttpContext();
_requests[137].RequestServices = CreateServices();
_requests[137].Request.Method = "PATCH";
_requests[137].Request.Path = "/repos/3aa16/84330/pulls/6dcd1f";
_requests[138] = new DefaultHttpContext();
_requests[138].RequestServices = CreateServices();
_requests[138].Request.Method = "PATCH";
_requests[138].Request.Path = "/repos/eb98f/fd492/releases/a070f";
_requests[139] = new DefaultHttpContext();
_requests[139].RequestServices = CreateServices();
_requests[139].Request.Method = "POST";
_requests[139].Request.Path = "/repos/f1040/ca625/statuses/a1554";
_requests[140] = new DefaultHttpContext();
_requests[140].RequestServices = CreateServices();
_requests[140].Request.Method = "GET";
_requests[140].Request.Path = "/repos/5e573/09410/commits/740ca/status";
_requests[141] = new DefaultHttpContext();
_requests[141].RequestServices = CreateServices();
_requests[141].Request.Method = "POST";
_requests[141].Request.Path = "/repos/25dd0/256a3/commits/088d12d/comments";
_requests[142] = new DefaultHttpContext();
_requests[142].RequestServices = CreateServices();
_requests[142].Request.Method = "POST";
_requests[142].Request.Path = "/repos/50b76/80795/deployments/faedd/statuses";
_requests[143] = new DefaultHttpContext();
_requests[143].RequestServices = CreateServices();
_requests[143].Request.Method = "POST";
_requests[143].Request.Path = "/repos/0c54e/6485c/hooks/82463a/tests";
_requests[144] = new DefaultHttpContext();
_requests[144].RequestServices = CreateServices();
_requests[144].Request.Method = "POST";
_requests[144].Request.Path = "/repos/07cf6/23b13/issues/f0dd8f/comments";
_requests[145] = new DefaultHttpContext();
_requests[145].RequestServices = CreateServices();
_requests[145].Request.Method = "GET";
_requests[145].Request.Path = "/repos/23f9b/f2fd8/issues/3fe64f/events";
_requests[146] = new DefaultHttpContext();
_requests[146].RequestServices = CreateServices();
_requests[146].Request.Method = "PUT";
_requests[146].Request.Path = "/repos/5b3d7/ca96e/issues/936334/labels";
_requests[147] = new DefaultHttpContext();
_requests[147].RequestServices = CreateServices();
_requests[147].Request.Method = "GET";
_requests[147].Request.Path = "/repos/cd644/3304a/milestones/166fae/labels";
_requests[148] = new DefaultHttpContext();
_requests[148].RequestServices = CreateServices();
_requests[148].Request.Method = "POST";
_requests[148].Request.Path = "/repos/33da8/9f808/pulls/f1e922/comments";
_requests[149] = new DefaultHttpContext();
_requests[149].RequestServices = CreateServices();
_requests[149].Request.Method = "GET";
_requests[149].Request.Path = "/repos/be208/fc905/pulls/7e240f/commits";
_requests[150] = new DefaultHttpContext();
_requests[150].RequestServices = CreateServices();
_requests[150].Request.Method = "GET";
_requests[150].Request.Path = "/repos/3c706/a56bc/pulls/651d9a/files";
_requests[151] = new DefaultHttpContext();
_requests[151].RequestServices = CreateServices();
_requests[151].Request.Method = "PUT";
_requests[151].Request.Path = "/repos/ea5a0/36e73/pulls/760f68/merge";
_requests[152] = new DefaultHttpContext();
_requests[152].RequestServices = CreateServices();
_requests[152].Request.Method = "GET";
_requests[152].Request.Path = "/repos/9362e/2a504/releases/4105d/assets";
_requests[153] = new DefaultHttpContext();
_requests[153].RequestServices = CreateServices();
_requests[153].Request.Method = "DELETE";
_requests[153].Request.Path = "/repos/39dc3/47180/issues/5b5277/labels/fc82a";
_requests[154] = new DefaultHttpContext();
_requests[154].RequestServices = CreateServices();
_requests[154].Request.Method = "GET";
_requests[154].Request.Path = "/repos/91437/d62a4/5582f507-3991-/3e89b";
}
private Matcher SetupMatcher(MatcherBuilder builder)
{
builder.AddEntry("/emojis", _endpoints[0]);
builder.AddEntry("/events", _endpoints[1]);
builder.AddEntry("/feeds", _endpoints[2]);
builder.AddEntry("/gists", _endpoints[3]);
builder.AddEntry("/issues", _endpoints[4]);
builder.AddEntry("/markdown", _endpoints[5]);
builder.AddEntry("/meta", _endpoints[6]);
builder.AddEntry("/notifications", _endpoints[7]);
builder.AddEntry("/rate_limit", _endpoints[8]);
builder.AddEntry("/repositories", _endpoints[9]);
builder.AddEntry("/user", _endpoints[10]);
builder.AddEntry("/users", _endpoints[11]);
builder.AddEntry("/gists/public", _endpoints[12]);
builder.AddEntry("/gists/starred", _endpoints[13]);
builder.AddEntry("/gitignore/templates", _endpoints[14]);
builder.AddEntry("/markdown/raw", _endpoints[15]);
builder.AddEntry("/search/code", _endpoints[16]);
builder.AddEntry("/search/issues", _endpoints[17]);
builder.AddEntry("/search/repositories", _endpoints[18]);
builder.AddEntry("/search/users", _endpoints[19]);
builder.AddEntry("/user/emails", _endpoints[20]);
builder.AddEntry("/user/followers", _endpoints[21]);
builder.AddEntry("/user/following", _endpoints[22]);
builder.AddEntry("/user/issues", _endpoints[23]);
builder.AddEntry("/user/keys", _endpoints[24]);
builder.AddEntry("/user/orgs", _endpoints[25]);
builder.AddEntry("/user/repos", _endpoints[26]);
builder.AddEntry("/user/starred", _endpoints[27]);
builder.AddEntry("/user/subscriptions", _endpoints[28]);
builder.AddEntry("/user/teams", _endpoints[29]);
builder.AddEntry("/legacy/repos/search/{keyword}", _endpoints[30]);
builder.AddEntry("/legacy/user/email/{email}", _endpoints[31]);
builder.AddEntry("/legacy/user/search/{keyword}", _endpoints[32]);
builder.AddEntry("/legacy/issues/search/{owner}/{repository}/{state}/{keyword}", _endpoints[33]);
builder.AddEntry("/gitignore/templates/{language}", _endpoints[34]);
builder.AddEntry("/notifications/threads/{id}", _endpoints[35]);
builder.AddEntry("/user/following/{username}", _endpoints[36]);
builder.AddEntry("/user/keys/{keyId}", _endpoints[37]);
builder.AddEntry("/notifications/threads/{id}/subscription", _endpoints[38]);
builder.AddEntry("/user/starred/{owner}/{repo}", _endpoints[39]);
builder.AddEntry("/user/subscriptions/{owner}/{repo}", _endpoints[40]);
builder.AddEntry("/gists/{id}", _endpoints[41]);
builder.AddEntry("/orgs/{org}", _endpoints[42]);
builder.AddEntry("/teams/{teamId}", _endpoints[43]);
builder.AddEntry("/users/{username}", _endpoints[44]);
builder.AddEntry("/gists/{id}/comments", _endpoints[45]);
builder.AddEntry("/gists/{id}/forks", _endpoints[46]);
builder.AddEntry("/gists/{id}/star", _endpoints[47]);
builder.AddEntry("/orgs/{org}/events", _endpoints[48]);
builder.AddEntry("/orgs/{org}/issues", _endpoints[49]);
builder.AddEntry("/orgs/{org}/members", _endpoints[50]);
builder.AddEntry("/orgs/{org}/public_members", _endpoints[51]);
builder.AddEntry("/orgs/{org}/repos", _endpoints[52]);
builder.AddEntry("/orgs/{org}/teams", _endpoints[53]);
builder.AddEntry("/teams/{teamId}/members", _endpoints[54]);
builder.AddEntry("/teams/{teamId}/repos", _endpoints[55]);
builder.AddEntry("/users/{username}/events", _endpoints[56]);
builder.AddEntry("/users/{username}/followers", _endpoints[57]);
builder.AddEntry("/users/{username}/gists", _endpoints[58]);
builder.AddEntry("/users/{username}/keys", _endpoints[59]);
builder.AddEntry("/users/{username}/orgs", _endpoints[60]);
builder.AddEntry("/users/{username}/received_events", _endpoints[61]);
builder.AddEntry("/users/{username}/repos", _endpoints[62]);
builder.AddEntry("/users/{username}/starred", _endpoints[63]);
builder.AddEntry("/users/{username}/subscriptions", _endpoints[64]);
builder.AddEntry("/users/{username}/received_events/public", _endpoints[65]);
builder.AddEntry("/users/{username}/events/orgs/{org}", _endpoints[66]);
builder.AddEntry("/gists/{id}/comments/{commentId}", _endpoints[67]);
builder.AddEntry("/orgs/{org}/members/{username}", _endpoints[68]);
builder.AddEntry("/orgs/{org}/public_members/{username}", _endpoints[69]);
builder.AddEntry("/teams/{teamId}/members/{username}", _endpoints[70]);
builder.AddEntry("/teams/{teamId}/memberships/{username}", _endpoints[71]);
builder.AddEntry("/users/{username}/following/{targetUser}", _endpoints[72]);
builder.AddEntry("/teams/{teamId}/repos/{owner}/{repo}", _endpoints[73]);
builder.AddEntry("/repos/{owner}/{repo}", _endpoints[74]);
builder.AddEntry("/networks/{owner}/{repo}/events", _endpoints[75]);
builder.AddEntry("/repos/{owner}/{repo}/assignees", _endpoints[76]);
builder.AddEntry("/repos/{owner}/{repo}/branches", _endpoints[77]);
builder.AddEntry("/repos/{owner}/{repo}/collaborators", _endpoints[78]);
builder.AddEntry("/repos/{owner}/{repo}/comments", _endpoints[79]);
builder.AddEntry("/repos/{owner}/{repo}/commits", _endpoints[80]);
builder.AddEntry("/repos/{owner}/{repo}/contributors", _endpoints[81]);
builder.AddEntry("/repos/{owner}/{repo}/deployments", _endpoints[82]);
builder.AddEntry("/repos/{owner}/{repo}/downloads", _endpoints[83]);
builder.AddEntry("/repos/{owner}/{repo}/events", _endpoints[84]);
builder.AddEntry("/repos/{owner}/{repo}/forks", _endpoints[85]);
builder.AddEntry("/repos/{owner}/{repo}/hooks", _endpoints[86]);
builder.AddEntry("/repos/{owner}/{repo}/issues", _endpoints[87]);
builder.AddEntry("/repos/{owner}/{repo}/keys", _endpoints[88]);
builder.AddEntry("/repos/{owner}/{repo}/labels", _endpoints[89]);
builder.AddEntry("/repos/{owner}/{repo}/languages", _endpoints[90]);
builder.AddEntry("/repos/{owner}/{repo}/merges", _endpoints[91]);
builder.AddEntry("/repos/{owner}/{repo}/milestones", _endpoints[92]);
builder.AddEntry("/repos/{owner}/{repo}/notifications", _endpoints[93]);
builder.AddEntry("/repos/{owner}/{repo}/pulls", _endpoints[94]);
builder.AddEntry("/repos/{owner}/{repo}/readme", _endpoints[95]);
builder.AddEntry("/repos/{owner}/{repo}/releases", _endpoints[96]);
builder.AddEntry("/repos/{owner}/{repo}/stargazers", _endpoints[97]);
builder.AddEntry("/repos/{owner}/{repo}/subscribers", _endpoints[98]);
builder.AddEntry("/repos/{owner}/{repo}/subscription", _endpoints[99]);
builder.AddEntry("/repos/{owner}/{repo}/tags", _endpoints[100]);
builder.AddEntry("/repos/{owner}/{repo}/teams", _endpoints[101]);
builder.AddEntry("/repos/{owner}/{repo}/watchers", _endpoints[102]);
builder.AddEntry("/repos/{owner}/{repo}/git/blobs", _endpoints[103]);
builder.AddEntry("/repos/{owner}/{repo}/git/commits", _endpoints[104]);
builder.AddEntry("/repos/{owner}/{repo}/git/refs", _endpoints[105]);
builder.AddEntry("/repos/{owner}/{repo}/git/tags", _endpoints[106]);
builder.AddEntry("/repos/{owner}/{repo}/git/trees", _endpoints[107]);
builder.AddEntry("/repos/{owner}/{repo}/issues/comments", _endpoints[108]);
builder.AddEntry("/repos/{owner}/{repo}/issues/events", _endpoints[109]);
builder.AddEntry("/repos/{owner}/{repo}/pulls/comments", _endpoints[110]);
builder.AddEntry("/repos/{owner}/{repo}/stats/code_frequency", _endpoints[111]);
builder.AddEntry("/repos/{owner}/{repo}/stats/commit_activity", _endpoints[112]);
builder.AddEntry("/repos/{owner}/{repo}/stats/contributors", _endpoints[113]);
builder.AddEntry("/repos/{owner}/{repo}/stats/participation", _endpoints[114]);
builder.AddEntry("/repos/{owner}/{repo}/stats/punch_card", _endpoints[115]);
builder.AddEntry("/repos/{owner}/{repo}/git/blobs/{shaCode}", _endpoints[116]);
builder.AddEntry("/repos/{owner}/{repo}/git/commits/{shaCode}", _endpoints[117]);
builder.AddEntry("/repos/{owner}/{repo}/git/refs/{ref}", _endpoints[118]);
builder.AddEntry("/repos/{owner}/{repo}/git/tags/{shaCode}", _endpoints[119]);
builder.AddEntry("/repos/{owner}/{repo}/git/trees/{shaCode}", _endpoints[120]);
builder.AddEntry("/repos/{owner}/{repo}/issues/comments/{commentId}", _endpoints[121]);
builder.AddEntry("/repos/{owner}/{repo}/issues/events/{eventId}", _endpoints[122]);
builder.AddEntry("/repos/{owner}/{repo}/pulls/comments/{commentId}", _endpoints[123]);
builder.AddEntry("/repos/{owner}/{repo}/releases/assets/{id}", _endpoints[124]);
builder.AddEntry("/repos/{owner}/{repo}/assignees/{assignee}", _endpoints[125]);
builder.AddEntry("/repos/{owner}/{repo}/branches/{branch}", _endpoints[126]);
builder.AddEntry("/repos/{owner}/{repo}/collaborators/{user}", _endpoints[127]);
builder.AddEntry("/repos/{owner}/{repo}/comments/{commentId}", _endpoints[128]);
builder.AddEntry("/repos/{owner}/{repo}/commits/{shaCode}", _endpoints[129]);
builder.AddEntry("/repos/{owner}/{repo}/contents/{path}", _endpoints[130]);
builder.AddEntry("/repos/{owner}/{repo}/downloads/{downloadId}", _endpoints[131]);
builder.AddEntry("/repos/{owner}/{repo}/hooks/{hookId}", _endpoints[132]);
builder.AddEntry("/repos/{owner}/{repo}/issues/{number}", _endpoints[133]);
builder.AddEntry("/repos/{owner}/{repo}/keys/{keyId}", _endpoints[134]);
builder.AddEntry("/repos/{owner}/{repo}/labels/{name}", _endpoints[135]);
builder.AddEntry("/repos/{owner}/{repo}/milestones/{number}", _endpoints[136]);
builder.AddEntry("/repos/{owner}/{repo}/pulls/{number}", _endpoints[137]);
builder.AddEntry("/repos/{owner}/{repo}/releases/{id}", _endpoints[138]);
builder.AddEntry("/repos/{owner}/{repo}/statuses/{ref}", _endpoints[139]);
builder.AddEntry("/repos/{owner}/{repo}/commits/{ref}/status", _endpoints[140]);
builder.AddEntry("/repos/{owner}/{repo}/commits/{shaCode}/comments", _endpoints[141]);
builder.AddEntry("/repos/{owner}/{repo}/deployments/{id}/statuses", _endpoints[142]);
builder.AddEntry("/repos/{owner}/{repo}/hooks/{hookId}/tests", _endpoints[143]);
builder.AddEntry("/repos/{owner}/{repo}/issues/{number}/comments", _endpoints[144]);
builder.AddEntry("/repos/{owner}/{repo}/issues/{number}/events", _endpoints[145]);
builder.AddEntry("/repos/{owner}/{repo}/issues/{number}/labels", _endpoints[146]);
builder.AddEntry("/repos/{owner}/{repo}/milestones/{number}/labels", _endpoints[147]);
builder.AddEntry("/repos/{owner}/{repo}/pulls/{number}/comments", _endpoints[148]);
builder.AddEntry("/repos/{owner}/{repo}/pulls/{number}/commits", _endpoints[149]);
builder.AddEntry("/repos/{owner}/{repo}/pulls/{number}/files", _endpoints[150]);
builder.AddEntry("/repos/{owner}/{repo}/pulls/{number}/merge", _endpoints[151]);
builder.AddEntry("/repos/{owner}/{repo}/releases/{id}/assets", _endpoints[152]);
builder.AddEntry("/repos/{owner}/{repo}/issues/{number}/labels/{name}", _endpoints[153]);
builder.AddEntry("/repos/{owner}/{repo}/{archive_format}/{path}", _endpoints[154]);
return builder.Build();
}
}
}

View File

@ -0,0 +1,479 @@
// 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;
using Microsoft.AspNetCore.Routing.Template;
namespace Microsoft.AspNetCore.Routing.Matchers
{
internal class InstructionMatcher : Matcher
{
public static MatcherBuilder CreateBuilder() => new Builder();
private State _state;
private InstructionMatcher(Instruction[] instructions, Endpoint[] endpoints, JumpTable[] tables)
{
_state = new State()
{
Instructions = instructions,
Endpoints = endpoints,
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 int[32];
var segment = 0;
var index = -1;
while ((index = path.IndexOf('/', index + 1)) >= 0)
{
buffer[segment++] = index + 1;
}
buffer[segment] = path.Length;
var i = 0;
Endpoint result = null;
while (i < state.Instructions.Length)
{
var instruction = state.Instructions[i];
switch (instruction.Code)
{
case InstructionCode.Accept:
{
result = state.Endpoints[instruction.Payload];
i++;
break;
}
case InstructionCode.Branch:
{
var table = state.Tables[instruction.Payload];
i = table.GetDestination(buffer, path);
break;
}
case InstructionCode.Jump:
{
i = instruction.Payload;
break;
}
}
}
feature.Endpoint = result;
return Task.CompletedTask;
}
private class State
{
public Endpoint[] Endpoints;
public Instruction[] Instructions;
public JumpTable[] Tables;
}
[DebuggerDisplay("{ToDebugString(),nq}")]
[StructLayout(LayoutKind.Explicit)]
private struct Instruction
{
[FieldOffset(3)]
public InstructionCode Code;
[FieldOffset(4)]
public int Payload;
private string ToDebugString()
{
return $"{Code}: {Payload}";
}
}
private enum InstructionCode : byte
{
Accept,
Branch,
Jump,
Pop, // Only used during the instruction builder phase
}
private abstract class JumpTable
{
public unsafe abstract int GetDestination(int* segments, string text);
}
private 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());
}
}
private 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(int* segments, string text)
{
var start = segments[_depth];
var length = segments[_depth + 1] - start;
for (var i = 0; i < _entries.Length; i++)
{
if (length == _entries[i].text.Length &&
string.Compare(
text,
start,
_entries[i].text,
0,
length,
StringComparison.OrdinalIgnoreCase) == 0)
{
return _entries[i].destination;
}
}
return _exit;
}
}
private class Entry
{
public int Order;
public decimal Precedence;
public RouteTemplate Pattern;
public Endpoint Endpoint;
}
private class InstructionBuilder
{
private readonly List<Instruction> _instructions = new List<Instruction>();
private readonly List<Endpoint> _endpoints = new List<Endpoint>();
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, Payload = end };
}
}
_blocks.RemoveAt(_blocks.Count - 1);
}
public int AddInstruction(Instruction instruction)
{
_instructions.Add(instruction);
return _instructions.Count - 1;
}
public int AddEndpoint(Endpoint 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 Endpoint[] 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, 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, });
}
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(Endpoint endpoint)
{
Endpoint = endpoint;
}
public Endpoint Endpoint { get; }
public override void Lower(InstructionBuilder builder)
{
builder.AddInstruction(new Instruction()
{
Code = InstructionCode.Accept,
Payload = builder.AddEndpoint(Endpoint),
});
}
}
private class Builder : MatcherBuilder
{
private List<Entry> _entries = new List<Entry>();
public override void AddEntry(string pattern, MatcherEndpoint endpoint)
{
var parsed = TemplateParser.Parse(pattern);
_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 = y.Precedence.CompareTo(x.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);
for (var depth = 0; 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 = branch.Literals.IndexOf(segment.Parts[0].Text);
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(entry.Endpoint));
}
var builder = new InstructionBuilder();
for (var i = 0; i < roots.Count; i++)
{
roots[i].Lower(builder);
}
var (instructions, endpoints, tables) = builder;
return new InstructionMatcher(instructions, endpoints, 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;
}
}
}
}

View File

@ -0,0 +1,73 @@
// 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.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.AspNetCore.Routing.Matchers
{
public abstract class MatcherBenchmarkBase
{
internal MatcherEndpoint[] _endpoints;
internal HttpContext[] _requests;
// The older routing implementations retrieve services when they first execute.
internal static IServiceProvider CreateServices()
{
var services = new ServiceCollection();
services.AddLogging();
return services.BuildServiceProvider();
}
internal static MatcherEndpoint CreateEndpoint(string template)
{
return new MatcherEndpoint(
(next) => (context) => Task.CompletedTask,
template,
new { },
0,
EndpointMetadataCollection.Empty,
template);
}
internal static int[] SampleRequests(int endpointCount, int count)
{
// This isn't very high tech, but it's at least regular distribution.
// We sort the route templates by precedence, so this should result in
// an even distribution of the 'complexity' of the routes that are exercised.
var frequency = endpointCount / count;
if (frequency < 2)
{
throw new InvalidOperationException(
"The sample count is too high. This won't produce an accurate sampling" +
"of the request data.");
}
var samples = new int[count];
for (var i = 0; i < samples.Length; i++)
{
samples[i] = i * frequency;
}
return samples;
}
[MethodImpl(MethodImplOptions.NoInlining)]
internal void Validate(HttpContext httpContext, Endpoint expected, Endpoint actual)
{
if (!object.ReferenceEquals(expected, actual))
{
var message = new StringBuilder();
message.AppendLine($"Validation failed for request {Array.IndexOf(_requests, httpContext)}");
message.AppendLine($"{httpContext.Request.Method} {httpContext.Request.Path}");
message.AppendLine($"expected: '{((MatcherEndpoint)expected)?.DisplayName ?? "null"}'");
message.AppendLine($"actual: '{((MatcherEndpoint)actual)?.DisplayName ?? "null"}'");
throw new InvalidOperationException(message.ToString());
}
}
}
}

View File

@ -0,0 +1,12 @@
// 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
{
internal abstract class MatcherBuilder
{
public abstract void AddEntry(string template, MatcherEndpoint endpoint);
public abstract Matcher Build();
}
}

View File

@ -0,0 +1,62 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing.Matchers
{
internal class MinimalMatcher : Matcher
{
public static MatcherBuilder CreateBuilder() => new Builder();
private readonly (string pattern, Endpoint endpoint)[] _entries;
private MinimalMatcher((string pattern, Endpoint endpoint)[] entries)
{
_entries = entries;
}
public override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
if (feature == null)
{
throw new ArgumentNullException(nameof(feature));
}
var path = httpContext.Request.Path.Value;
for (var i = 0; i < _entries.Length; i++)
{
if (string.Equals(_entries[i].pattern, path, StringComparison.OrdinalIgnoreCase))
{
feature.Endpoint = _entries[i].endpoint;
return Task.CompletedTask;
}
}
return Task.CompletedTask;
}
private class Builder : MatcherBuilder
{
private List<(string pattern, Endpoint endpoint)> _entries = new List<(string pattern, Endpoint endpoint)>();
public override void AddEntry(string pattern, MatcherEndpoint endpoint)
{
_entries.Add((pattern, endpoint));
}
public override Matcher Build()
{
return new MinimalMatcher(_entries.ToArray());
}
}
}
}

View File

@ -0,0 +1,70 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Routing.Matchers
{
internal class RouteMatcher : Matcher
{
public static MatcherBuilder CreateBuilder() => new Builder();
private IRouter _inner;
private RouteMatcher(IRouter inner)
{
_inner = inner;
}
public async override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
if (feature == null)
{
throw new ArgumentNullException(nameof(feature));
}
var context = new RouteContext(httpContext);
await _inner.RouteAsync(context);
if (context.Handler != null)
{
httpContext.Features.Set<IEndpointFeature>(feature);
await context.Handler(httpContext);
}
}
private class Builder : MatcherBuilder
{
private readonly RouteCollection _routes = new RouteCollection();
private readonly IInlineConstraintResolver _constraintResolver;
public Builder()
{
_constraintResolver = new DefaultInlineConstraintResolver(Options.Create(new RouteOptions()));
}
public override void AddEntry(string pattern, MatcherEndpoint endpoint)
{
var handler = new RouteHandler(c =>
{
c.Features.Get<IEndpointFeature>().Endpoint = endpoint;
return Task.CompletedTask;
});
_routes.Add(new Route(handler, pattern, _constraintResolver));
}
public override Matcher Build()
{
return new RouteMatcher(_routes);
}
}
}
}

View File

@ -0,0 +1,82 @@
// 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.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing.Matchers
{
public class SingleEntryMatcherBenchmark : MatcherBenchmarkBase
{
private Matcher _minimal;
private Matcher _dfa;
private Matcher _instruction;
private Matcher _route;
private Matcher _tree;
[GlobalSetup]
public void Setup()
{
_minimal = SetupMatcher(MinimalMatcher.CreateBuilder());
_dfa = SetupMatcher(DfaMatcher.CreateBuilder());
_instruction = SetupMatcher(InstructionMatcher.CreateBuilder());
_route = SetupMatcher(RouteMatcher.CreateBuilder());
_tree = SetupMatcher(TreeRouterMatcher.CreateBuilder());
_endpoints = new MatcherEndpoint[0];
_endpoints[0] = CreateEndpoint("/plaintext");
_requests = new HttpContext[0];
_requests[0] = new DefaultHttpContext();
_requests[0].RequestServices = CreateServices();
_requests[0].Request.Path = "/plaintext";
}
private Matcher SetupMatcher(MatcherBuilder builder)
{
builder.AddEntry("/plaintext", _endpoints[0]);
return builder.Build();
}
[Benchmark(Baseline = true)]
public async Task Minimal()
{
var feature = new EndpointFeature();
await _minimal.MatchAsync(_requests[0], feature);
Validate(_requests[0], _endpoints[0], feature.Endpoint);
}
[Benchmark]
public async Task Dfa()
{
var feature = new EndpointFeature();
await _dfa.MatchAsync(_requests[0], feature);
Validate(_requests[0], _endpoints[0], feature.Endpoint);
}
[Benchmark]
public async Task Instruction()
{
var feature = new EndpointFeature();
await _instruction.MatchAsync(_requests[0], feature);
Validate(_requests[0], _endpoints[0], feature.Endpoint);
}
[Benchmark]
public async Task LegacyRoute()
{
var feature = new EndpointFeature();
await _route.MatchAsync(_requests[0], feature);
Validate(_requests[0], _endpoints[0], feature.Endpoint);
}
[Benchmark]
public async Task LegacyTreeRouter()
{
var feature = new EndpointFeature();
await _tree.MatchAsync(_requests[0], feature);
Validate(_requests[0], _endpoints[0], feature.Endpoint);
}
}
}

View File

@ -0,0 +1,93 @@
// 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.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Microsoft.AspNetCore.Http;
namespace Microsoft.AspNetCore.Routing.Matchers
{
public class SmallEntryCountLiteralMatcherBenchark : MatcherBenchmarkBase
{
private Matcher _minimal;
private Matcher _dfa;
private Matcher _instruction;
private Matcher _route;
private Matcher _tree;
[GlobalSetup]
public void Setup()
{
_minimal = SetupMatcher(MinimalMatcher.CreateBuilder());
_dfa = SetupMatcher(DfaMatcher.CreateBuilder());
_instruction = SetupMatcher(InstructionMatcher.CreateBuilder());
_route = SetupMatcher(RouteMatcher.CreateBuilder());
_tree = SetupMatcher(TreeRouterMatcher.CreateBuilder());
_endpoints = new MatcherEndpoint[1];
_endpoints[0] = CreateEndpoint("/plaintext");
_requests = new HttpContext[1];
_requests[0] = new DefaultHttpContext();
_requests[0].RequestServices = CreateServices();
_requests[0].Request.Path = "/plaintext";
}
// For this case we're specifically targeting the last entry to hit 'worst case'
// performance for the matchers that scale linearly.
private Matcher SetupMatcher(MatcherBuilder builder)
{
builder.AddEntry("/another-really-cool-entry", null);
builder.AddEntry("/Some-Entry", null);
builder.AddEntry("/a/path/with/more/segments", null);
builder.AddEntry("/random/name", null);
builder.AddEntry("/random/name2", null);
builder.AddEntry("/random/name3", null);
builder.AddEntry("/random/name4", null);
builder.AddEntry("/plaintext1", null);
builder.AddEntry("/plaintext2", null);
builder.AddEntry("/plaintext", _endpoints[0]);
return builder.Build();
}
[Benchmark(Baseline = true)]
public async Task Minimal()
{
var feature = new EndpointFeature();
await _minimal.MatchAsync(_requests[0], feature);
Validate(_requests[0], _endpoints[0], feature.Endpoint);
}
[Benchmark]
public async Task Dfa()
{
var feature = new EndpointFeature();
await _dfa.MatchAsync(_requests[0], feature);
Validate(_requests[0], _endpoints[0], feature.Endpoint);
}
[Benchmark]
public async Task Instruction()
{
var feature = new EndpointFeature();
await _instruction.MatchAsync(_requests[0], feature);
Validate(_requests[0], _endpoints[0], feature.Endpoint);
}
[Benchmark]
public async Task LegacyRoute()
{
var feature = new EndpointFeature();
await _route.MatchAsync(_requests[0], feature);
Validate(_requests[0], _endpoints[0], feature.Endpoint);
}
[Benchmark]
public async Task LegacyTreeRouter()
{
var feature = new EndpointFeature();
await _tree.MatchAsync(_requests[0], feature);
Validate(_requests[0], _endpoints[0], feature.Endpoint);
}
}
}

View File

@ -0,0 +1,77 @@
// 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.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing.Internal;
using Microsoft.AspNetCore.Routing.Template;
using Microsoft.AspNetCore.Routing.Tree;
using Microsoft.Extensions.Logging.Abstractions;
using Microsoft.Extensions.ObjectPool;
using Microsoft.Extensions.Options;
namespace Microsoft.AspNetCore.Routing.Matchers
{
internal class TreeRouterMatcher : Matcher
{
public static MatcherBuilder CreateBuilder() => new Builder();
private TreeRouter _inner;
private TreeRouterMatcher(TreeRouter inner)
{
_inner = inner;
}
public async override Task MatchAsync(HttpContext httpContext, IEndpointFeature feature)
{
if (httpContext == null)
{
throw new ArgumentNullException(nameof(httpContext));
}
if (feature == null)
{
throw new ArgumentNullException(nameof(feature));
}
var context = new RouteContext(httpContext);
await _inner.RouteAsync(context);
if (context.Handler != null)
{
httpContext.Features.Set<IEndpointFeature>(feature);
await context.Handler(httpContext);
}
}
private class Builder : MatcherBuilder
{
private readonly TreeRouteBuilder _inner;
public Builder()
{
_inner = new TreeRouteBuilder(
NullLoggerFactory.Instance,
new DefaultObjectPool<UriBuildingContext>(new UriBuilderContextPooledObjectPolicy()),
new DefaultInlineConstraintResolver(Options.Create(new RouteOptions())));
}
public override void AddEntry(string template, MatcherEndpoint endpoint)
{
var handler = new RouteHandler(c =>
{
c.Features.Get<IEndpointFeature>().Endpoint = endpoint;
return Task.CompletedTask;
});
_inner.MapInbound(handler, TemplateParser.Parse(template), "default", 0);
}
public override Matcher Build()
{
return new TreeRouterMatcher(_inner.Build());
}
}
}
}

View File

@ -7,6 +7,7 @@
<ServerGarbageCollection>true</ServerGarbageCollection>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsPackable>false</IsPackable>
<RootNamespace>Microsoft.AspNetCore.Routing</RootNamespace>
</PropertyGroup>
<ItemGroup>

View File

@ -3,4 +3,5 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Routing.Performance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.AspNetCore.Routing.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]