aspnetcore/src/Microsoft.AspNetCore.Routing/Matching/CandidateSet.cs

185 lines
7.3 KiB
C#

// 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;
namespace Microsoft.AspNetCore.Routing.Matching
{
/// <summary>
/// Represents a set of <see cref="Endpoint"/> candidates that have been matched
/// by the routing system. Used by implementations of <see cref="EndpointSelector"/>
/// and <see cref="IEndpointSelectorPolicy"/>.
/// </summary>
public sealed class CandidateSet
{
// We inline storage for 4 candidates here to avoid allocations in common
// cases. There's no real reason why 4 is important, it just seemed like
// a plausible number.
private CandidateState _state0;
private CandidateState _state1;
private CandidateState _state2;
private CandidateState _state3;
private CandidateState[] _additionalCandidates;
/// <summary>
/// <para>
/// Initializes a new instances of the candidate set structure with the provided list of endpoints
/// and associated scores.
/// </para>
/// <para>
/// The constructor is provided to enable unit tests of implementations of <see cref="EndpointSelector"/>
/// and <see cref="IEndpointSelectorPolicy"/>.
/// </para>
/// </summary>
/// <param name="endpoints">The list of endpoints, sorted in descending priority order.</param>
/// <param name="scores">The list of endpoint scores. <see cref="CandidateState.Score"/>.</param>
public CandidateSet(MatcherEndpoint[] endpoints, int[] scores)
{
Count = endpoints.Length;
switch (endpoints.Length)
{
case 0:
return;
case 1:
_state0 = new CandidateState(endpoints[0], score: scores[0]);
break;
case 2:
_state0 = new CandidateState(endpoints[0], score: scores[0]);
_state1 = new CandidateState(endpoints[1], score: scores[1]);
break;
case 3:
_state0 = new CandidateState(endpoints[0], score: scores[0]);
_state1 = new CandidateState(endpoints[1], score: scores[1]);
_state2 = new CandidateState(endpoints[2], score: scores[2]);
break;
case 4:
_state0 = new CandidateState(endpoints[0], score: scores[0]);
_state1 = new CandidateState(endpoints[1], score: scores[1]);
_state2 = new CandidateState(endpoints[2], score: scores[2]);
_state3 = new CandidateState(endpoints[3], score: scores[3]);
break;
default:
_state0 = new CandidateState(endpoints[0], score: scores[0]);
_state1 = new CandidateState(endpoints[1], score: scores[1]);
_state2 = new CandidateState(endpoints[2], score: scores[2]);
_state3 = new CandidateState(endpoints[3], score: scores[3]);
_additionalCandidates = new CandidateState[endpoints.Length - 4];
for (var i = 4; i < endpoints.Length; i++)
{
_additionalCandidates[i - 4] = new CandidateState(endpoints[i], score: scores[i]);
}
break;
}
}
internal CandidateSet(Candidate[] candidates)
{
Count = candidates.Length;
switch (candidates.Length)
{
case 0:
return;
case 1:
_state0 = new CandidateState(candidates[0].Endpoint, candidates[0].Score);
break;
case 2:
_state0 = new CandidateState(candidates[0].Endpoint, candidates[0].Score);
_state1 = new CandidateState(candidates[1].Endpoint, candidates[1].Score);
break;
case 3:
_state0 = new CandidateState(candidates[0].Endpoint, candidates[0].Score);
_state1 = new CandidateState(candidates[1].Endpoint, candidates[1].Score);
_state2 = new CandidateState(candidates[2].Endpoint, candidates[2].Score);
break;
case 4:
_state0 = new CandidateState(candidates[0].Endpoint, candidates[0].Score);
_state1 = new CandidateState(candidates[1].Endpoint, candidates[1].Score);
_state2 = new CandidateState(candidates[2].Endpoint, candidates[2].Score);
_state3 = new CandidateState(candidates[3].Endpoint, candidates[3].Score);
break;
default:
_state0 = new CandidateState(candidates[0].Endpoint, candidates[0].Score);
_state1 = new CandidateState(candidates[1].Endpoint, candidates[1].Score);
_state2 = new CandidateState(candidates[2].Endpoint, candidates[2].Score);
_state3 = new CandidateState(candidates[3].Endpoint, candidates[3].Score);
_additionalCandidates = new CandidateState[candidates.Length - 4];
for (var i = 4; i < candidates.Length; i++)
{
_additionalCandidates[i - 4] = new CandidateState(candidates[i].Endpoint, candidates[i].Score);
}
break;
}
}
/// <summary>
/// Gets the count of candidates in the set.
/// </summary>
public int Count { get; }
/// <summary>
/// Gets the <see cref="CandidateState"/> associated with the candidate <see cref="Endpoint"/>
/// at <paramref name="index"/>.
/// </summary>
/// <param name="index">The candidate index.</param>
/// <returns>
/// A reference to the <see cref="CandidateState"/>. The result is returned by reference
/// and intended to be mutated.
/// </returns>
public ref CandidateState this[int index]
{
// Note that this is a ref-return because of both mutability and performance.
// We don't want to copy these fat structs if it can be avoided.
// PERF: Force inlining
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get
{
// Friendliness for inlining
if ((uint)index >= Count)
{
ThrowIndexArgumentOutOfRangeException();
}
switch (index)
{
case 0:
return ref _state0;
case 1:
return ref _state1;
case 2:
return ref _state2;
case 3:
return ref _state3;
default:
return ref _additionalCandidates[index - 4];
}
}
}
private static void ThrowIndexArgumentOutOfRangeException()
{
throw new ArgumentOutOfRangeException("index");
}
}
}