Optimize PrefixContainer
There's really no need for us to sanitize null. This code handles null correctly. Additionally, CopyTo should be able to do better than our hand-written foreach in the worst case, and avoids an enumerator allocation. Removed a custom implementation of IndexOfAny.
This commit is contained in:
parent
698502df8c
commit
dacebacb90
|
|
@ -14,6 +14,9 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public class PrefixContainer
|
public class PrefixContainer
|
||||||
{
|
{
|
||||||
|
private static readonly string[] EmptyArray = new string[0];
|
||||||
|
private static readonly char[] Delimiters = new char[] { '[', '.' };
|
||||||
|
|
||||||
private readonly ICollection<string> _originalValues;
|
private readonly ICollection<string> _originalValues;
|
||||||
private readonly string[] _sortedValues;
|
private readonly string[] _sortedValues;
|
||||||
|
|
||||||
|
|
@ -25,8 +28,17 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
|
|
||||||
_originalValues = values;
|
_originalValues = values;
|
||||||
_sortedValues = ToArrayWithoutNulls(_originalValues);
|
|
||||||
Array.Sort(_sortedValues, StringComparer.OrdinalIgnoreCase);
|
if (_originalValues.Count == 0)
|
||||||
|
{
|
||||||
|
_sortedValues = EmptyArray;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_sortedValues = new string[_originalValues.Count];
|
||||||
|
_originalValues.CopyTo(_sortedValues, 0);
|
||||||
|
Array.Sort(_sortedValues, StringComparer.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ContainsPrefix(string prefix)
|
public bool ContainsPrefix(string prefix)
|
||||||
|
|
@ -41,6 +53,11 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
return _sortedValues.Length > 0; // only match empty string when we have some value
|
return _sortedValues.Length > 0; // only match empty string when we have some value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_sortedValues.Length == 0)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return BinarySearch(prefix) > -1;
|
return BinarySearch(prefix) > -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -80,7 +97,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
string key;
|
string key;
|
||||||
string fullName;
|
string fullName;
|
||||||
var delimiterPosition = IndexOfDelimiter(entry, 0);
|
var delimiterPosition = entry.IndexOfAny(Delimiters, 0);
|
||||||
|
|
||||||
if (delimiterPosition == 0 && entry[0] == '[')
|
if (delimiterPosition == 0 && entry[0] == '[')
|
||||||
{
|
{
|
||||||
|
|
@ -118,7 +135,7 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
{
|
{
|
||||||
case '.':
|
case '.':
|
||||||
// Handle an entry such as "prefix.key", "prefix.key.property" and "prefix.key[index]".
|
// Handle an entry such as "prefix.key", "prefix.key.property" and "prefix.key[index]".
|
||||||
var delimiterPosition = IndexOfDelimiter(entry, keyPosition);
|
var delimiterPosition = entry.IndexOfAny(Delimiters, keyPosition);
|
||||||
if (delimiterPosition == -1)
|
if (delimiterPosition == -1)
|
||||||
{
|
{
|
||||||
// Neither '.' nor '[' found later in the name. Use rest of the string.
|
// Neither '.' nor '[' found later in the name. Use rest of the string.
|
||||||
|
|
@ -195,58 +212,6 @@ namespace Microsoft.AspNetCore.Mvc.Internal
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static int IndexOfDelimiter(string entry, int startIndex)
|
|
||||||
{
|
|
||||||
int delimiterPosition;
|
|
||||||
var bracketPosition = entry.IndexOf('[', startIndex);
|
|
||||||
var dotPosition = entry.IndexOf('.', startIndex);
|
|
||||||
|
|
||||||
if (dotPosition == -1)
|
|
||||||
{
|
|
||||||
delimiterPosition = bracketPosition;
|
|
||||||
}
|
|
||||||
else if (bracketPosition == -1)
|
|
||||||
{
|
|
||||||
delimiterPosition = dotPosition;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
delimiterPosition = Math.Min(dotPosition, bracketPosition);
|
|
||||||
}
|
|
||||||
|
|
||||||
return delimiterPosition;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Convert an ICollection to an array, removing null values. Fast path for case where
|
|
||||||
/// there are no null values.
|
|
||||||
/// </summary>
|
|
||||||
private static TElement[] ToArrayWithoutNulls<TElement>(ICollection<TElement> collection) where TElement : class
|
|
||||||
{
|
|
||||||
Debug.Assert(collection != null);
|
|
||||||
|
|
||||||
var result = new TElement[collection.Count];
|
|
||||||
var count = 0;
|
|
||||||
foreach (TElement value in collection)
|
|
||||||
{
|
|
||||||
if (value != null)
|
|
||||||
{
|
|
||||||
result[count] = value;
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (count == collection.Count)
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var trimmedResult = new TElement[count];
|
|
||||||
Array.Copy(result, trimmedResult, count);
|
|
||||||
return trimmedResult;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int BinarySearch(string prefix)
|
private int BinarySearch(string prefix)
|
||||||
{
|
{
|
||||||
var start = 0;
|
var start = 0;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue