diff --git a/src/Microsoft.AspNetCore.Routing/Internal/ArrayBuilder.cs b/src/Microsoft.AspNetCore.Routing/Internal/ArrayBuilder.cs
new file mode 100644
index 0000000000..868b3cfc27
--- /dev/null
+++ b/src/Microsoft.AspNetCore.Routing/Internal/ArrayBuilder.cs
@@ -0,0 +1,169 @@
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+// See the LICENSE file in the project root for more information.
+
+// LICENSING NOTE: This file is from the dotnet corefx repository.
+//
+// See https://github.com/dotnet/corefx/blob/143df51926f2ad397fef9c9ca7ede88e2721e801/src/Common/src/System/Collections/Generic/ArrayBuilder.cs
+
+
+using System;
+using System.Diagnostics;
+
+namespace Microsoft.AspNetCore.Routing.Internal
+{
+ ///
+ /// Helper type for avoiding allocations while building arrays.
+ ///
+ /// The element type.
+ internal struct ArrayBuilder
+ {
+ private const int DefaultCapacity = 4;
+ private const int MaxCoreClrArrayLength = 0x7fefffff; // For byte arrays the limit is slightly larger
+
+ private T[] _array; // Starts out null, initialized on first Add.
+ private int _count; // Number of items into _array we're using.
+
+ ///
+ /// Initializes the with a specified capacity.
+ ///
+ /// The capacity of the array to allocate.
+ public ArrayBuilder(int capacity) : this()
+ {
+ Debug.Assert(capacity >= 0);
+ if (capacity > 0)
+ {
+ _array = new T[capacity];
+ }
+ }
+
+ ///
+ /// Gets the number of items this instance can store without re-allocating,
+ /// or 0 if the backing array is null.
+ ///
+ public int Capacity => _array?.Length ?? 0;
+
+ /// Gets the current underlying array.
+ public T[] Buffer => _array;
+
+ ///
+ /// Gets the number of items in the array currently in use.
+ ///
+ public int Count => _count;
+
+ ///
+ /// Gets or sets the item at a certain index in the array.
+ ///
+ /// The index into the array.
+ public T this[int index]
+ {
+ get
+ {
+ Debug.Assert(index >= 0 && index < _count);
+ return _array[index];
+ }
+ }
+
+ ///
+ /// Adds an item to the backing array, resizing it if necessary.
+ ///
+ /// The item to add.
+ public void Add(T item)
+ {
+ if (_count == Capacity)
+ {
+ EnsureCapacity(_count + 1);
+ }
+
+ UncheckedAdd(item);
+ }
+
+ ///
+ /// Gets the first item in this builder.
+ ///
+ public T First()
+ {
+ Debug.Assert(_count > 0);
+ return _array[0];
+ }
+
+ ///
+ /// Gets the last item in this builder.
+ ///
+ public T Last()
+ {
+ Debug.Assert(_count > 0);
+ return _array[_count - 1];
+ }
+
+ ///
+ /// Creates an array from the contents of this builder.
+ ///
+ ///
+ /// Do not call this method twice on the same builder.
+ ///
+ public T[] ToArray()
+ {
+ if (_count == 0)
+ {
+ return Array.Empty();
+ }
+
+ Debug.Assert(_array != null); // Nonzero _count should imply this
+
+ T[] result = _array;
+ if (_count < result.Length)
+ {
+ // Avoid a bit of overhead (method call, some branches, extra codegen)
+ // which would be incurred by using Array.Resize
+ result = new T[_count];
+ Array.Copy(_array, 0, result, 0, _count);
+ }
+
+#if DEBUG
+ // Try to prevent callers from using the ArrayBuilder after ToArray, if _count != 0.
+ _count = -1;
+ _array = null;
+#endif
+
+ return result;
+ }
+
+ ///
+ /// Adds an item to the backing array, without checking if there is room.
+ ///
+ /// The item to add.
+ ///
+ /// Use this method if you know there is enough space in the
+ /// for another item, and you are writing performance-sensitive code.
+ ///
+ public void UncheckedAdd(T item)
+ {
+ Debug.Assert(_count < Capacity);
+
+ _array[_count++] = item;
+ }
+
+ private void EnsureCapacity(int minimum)
+ {
+ Debug.Assert(minimum > Capacity);
+
+ int capacity = Capacity;
+ int nextCapacity = capacity == 0 ? DefaultCapacity : 2 * capacity;
+
+ if ((uint)nextCapacity > (uint)MaxCoreClrArrayLength)
+ {
+ nextCapacity = Math.Max(capacity + 1, MaxCoreClrArrayLength);
+ }
+
+ nextCapacity = Math.Max(nextCapacity, minimum);
+
+ T[] next = new T[nextCapacity];
+ if (_count > 0)
+ {
+ Array.Copy(_array, 0, next, 0, _count);
+ }
+ _array = next;
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Microsoft.AspNetCore.Routing/Patterns/RouteParameterParser.cs b/src/Microsoft.AspNetCore.Routing/Patterns/RouteParameterParser.cs
index 49860dcb0f..0cb7e48989 100644
--- a/src/Microsoft.AspNetCore.Routing/Patterns/RouteParameterParser.cs
+++ b/src/Microsoft.AspNetCore.Routing/Patterns/RouteParameterParser.cs
@@ -2,8 +2,7 @@
// 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.Internal;
namespace Microsoft.AspNetCore.Routing.Patterns
{
@@ -77,7 +76,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
currentIndex++;
}
- var parseResults = ParseConstraints(parameter, parameterName, currentIndex, endIndex);
+ var parseResults = ParseConstraints(parameter, currentIndex, endIndex);
currentIndex = parseResults.CurrentIndex;
string defaultValue = null;
@@ -91,17 +90,16 @@ namespace Microsoft.AspNetCore.Routing.Patterns
parameterName,
defaultValue,
parameterKind,
- parseResults.ParameterPolicies.ToArray(),
+ parseResults.ParameterPolicies,
encodeSlashes);
}
private static ParameterPolicyParseResults ParseConstraints(
string text,
- string parameterName,
int currentIndex,
int endIndex)
{
- var constraints = new List();
+ var constraints = new ArrayBuilder(0);
var state = ParseState.Start;
var startIndex = currentIndex;
do
@@ -234,7 +232,7 @@ namespace Microsoft.AspNetCore.Routing.Patterns
} while (state != ParseState.End);
- return new ParameterPolicyParseResults(currentIndex, constraints);
+ return new ParameterPolicyParseResults(currentIndex, constraints.ToArray());
}
private enum ParseState
@@ -249,9 +247,9 @@ namespace Microsoft.AspNetCore.Routing.Patterns
{
public readonly int CurrentIndex;
- public readonly IReadOnlyList ParameterPolicies;
+ public readonly RoutePatternParameterPolicyReference[] ParameterPolicies;
- public ParameterPolicyParseResults(int currentIndex, IReadOnlyList parameterPolicies)
+ public ParameterPolicyParseResults(int currentIndex, RoutePatternParameterPolicyReference[] parameterPolicies)
{
CurrentIndex = currentIndex;
ParameterPolicies = parameterPolicies;