未验证 提交 09d83464 编写于 作者: M Maryam Ariyan 提交者: GitHub

Makes GetChildKeys more efficient (#67186)

上级 018b85a3
......@@ -11,7 +11,7 @@ namespace Microsoft.Extensions.Configuration
/// </summary>
public class ConfigurationKeyComparer : IComparer<string>
{
private static readonly string[] _keyDelimiterArray = new[] { ConfigurationPath.KeyDelimiter };
private const char KeyDelimiter = ':';
/// <summary>
/// The default instance.
......@@ -29,29 +29,61 @@ public class ConfigurationKeyComparer : IComparer<string>
/// <returns>Less than 0 if x is less than y, 0 if x is equal to y and greater than 0 if x is greater than y.</returns>
public int Compare(string? x, string? y)
{
string[] xParts = x?.Split(_keyDelimiterArray, StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
string[] yParts = y?.Split(_keyDelimiterArray, StringSplitOptions.RemoveEmptyEntries) ?? Array.Empty<string>();
ReadOnlySpan<char> xSpan = x.AsSpan();
ReadOnlySpan<char> ySpan = y.AsSpan();
xSpan = SkipAheadOnDelimiter(xSpan);
ySpan = SkipAheadOnDelimiter(ySpan);
// Compare each part until we get two parts that are not equal
for (int i = 0; i < Math.Min(xParts.Length, yParts.Length); i++)
while (!xSpan.IsEmpty && !ySpan.IsEmpty)
{
x = xParts[i];
y = yParts[i];
int xDelimiterIndex = xSpan.IndexOf(KeyDelimiter);
int yDelimiterIndex = ySpan.IndexOf(KeyDelimiter);
int compareResult = Compare(
xDelimiterIndex == -1 ? xSpan : xSpan.Slice(0, xDelimiterIndex),
yDelimiterIndex == -1 ? ySpan : ySpan.Slice(0, yDelimiterIndex));
if (compareResult != 0)
{
return compareResult;
}
int value1 = 0;
int value2 = 0;
xSpan = xDelimiterIndex == -1 ? default :
SkipAheadOnDelimiter(xSpan.Slice(xDelimiterIndex + 1));
ySpan = yDelimiterIndex == -1 ? default :
SkipAheadOnDelimiter(ySpan.Slice(yDelimiterIndex + 1));
}
bool xIsInt = x != null && int.TryParse(x, out value1);
bool yIsInt = y != null && int.TryParse(y, out value2);
return xSpan.IsEmpty ? (ySpan.IsEmpty ? 0 : -1) : 1;
static ReadOnlySpan<char> SkipAheadOnDelimiter(ReadOnlySpan<char> a)
{
while (!a.IsEmpty && a[0] == KeyDelimiter)
{
a = a.Slice(1);
}
return a;
}
static int Compare(ReadOnlySpan<char> a, ReadOnlySpan<char> b)
{
#if NETCOREAPP
bool aIsInt = int.TryParse(a, out int value1);
bool bIsInt = int.TryParse(b, out int value2);
#else
bool aIsInt = int.TryParse(a.ToString(), out int value1);
bool bIsInt = int.TryParse(b.ToString(), out int value2);
#endif
int result;
if (!xIsInt && !yIsInt)
if (!aIsInt && !bIsInt)
{
// Both are strings
result = string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
result = a.CompareTo(b, StringComparison.OrdinalIgnoreCase);
}
else if (xIsInt && yIsInt)
else if (aIsInt && bIsInt)
{
// Both are int
result = value1 - value2;
......@@ -59,19 +91,11 @@ public int Compare(string? x, string? y)
else
{
// Only one of them is int
result = xIsInt ? -1 : 1;
result = aIsInt ? -1 : 1;
}
if (result != 0)
{
// One of them is different
return result;
}
return result;
}
// If we get here, the common parts are equal.
// If they are of the same length, then they are totally identical
return xParts.Length - yParts.Length;
}
}
}
......@@ -14,6 +14,8 @@ public void CompareWithNull()
ComparerTest(null, null, 0);
ComparerTest(null, "a", -1);
ComparerTest("b", null, 1);
ComparerTest(null, "a:b", -1);
ComparerTest(null, "a:b:c", -1);
}
[Fact]
......@@ -32,6 +34,20 @@ public void CompareWithDifferentLengths()
ComparerTest("aa", "a", 1);
}
[Fact]
public void CompareWithEmpty()
{
ComparerTest(":", "", 0);
ComparerTest(":", "::", 0);
ComparerTest(null, "", 0);
ComparerTest(":", null, 0);
ComparerTest("::", null, 0);
ComparerTest(" : : ", null, 1);
ComparerTest("b: :a", "b::a", -1);
ComparerTest("b:\t:a", "b::a", -1);
ComparerTest("b::a: ", "b::a:", 1);
}
[Fact]
public void CompareWithLetters()
{
......
......@@ -59,6 +59,64 @@ public void LoadAndCombineKeyValuePairsFromDifferentConfigurationProviders()
Assert.Null(config["NotExist"]);
}
[Fact]
private void GetChildKeys_CanChainEmptyKeys()
{
var input = new Dictionary<string, string>() { };
for (int i = 0; i < 1000; i++)
{
input.Add(new string(' ', i), string.Empty);
}
IConfigurationRoot configurationRoot = new ConfigurationBuilder()
.Add(new MemoryConfigurationSource
{
InitialData = input
})
.Build();
var chainedConfigurationSource = new ChainedConfigurationSource
{
Configuration = configurationRoot,
ShouldDisposeConfiguration = false,
};
var chainedConfiguration = new ChainedConfigurationProvider(chainedConfigurationSource);
IEnumerable<string> childKeys = chainedConfiguration.GetChildKeys(new string[0], null);
Assert.Equal(1000, childKeys.Count());
Assert.Equal(string.Empty, childKeys.First());
Assert.Equal(999, childKeys.Last().Length);
}
[Fact]
private void GetChildKeys_CanChainKeyWithNoDelimiter()
{
var input = new Dictionary<string, string>() { };
for (int i = 1000; i < 2000; i++)
{
input.Add(i.ToString(), string.Empty);
}
IConfigurationRoot configurationRoot = new ConfigurationBuilder()
.Add(new MemoryConfigurationSource
{
InitialData = input
})
.Build();
var chainedConfigurationSource = new ChainedConfigurationSource
{
Configuration = configurationRoot,
ShouldDisposeConfiguration = false,
};
var chainedConfiguration = new ChainedConfigurationProvider(chainedConfigurationSource);
IEnumerable<string> childKeys = chainedConfiguration.GetChildKeys(new string[0], null);
Assert.Equal(1000, childKeys.Count());
Assert.Equal("1000", childKeys.First());
Assert.Equal("1999", childKeys.Last());
}
[Fact]
public void CanChainConfiguration()
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册