| File: ConfigurationKeyComparer.cs | Web Access |
| Project: src\runtime\src\libraries\Microsoft.Extensions.Configuration\src\Microsoft.Extensions.Configuration.csproj (Microsoft.Extensions.Configuration) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System; using System.Collections.Generic; namespace Microsoft.Extensions.Configuration { /// <summary> /// Implements IComparer to order configuration keys. /// </summary> public class ConfigurationKeyComparer : IComparer<string> { private const char KeyDelimiter = ':'; /// <summary> /// Gets the default instance. /// </summary> public static ConfigurationKeyComparer Instance { get; } = new ConfigurationKeyComparer(); /// <summary>A comparer delegate with the default instance.</summary> internal static Comparison<string> Comparison { get; } = Instance.Compare; /// <summary> /// Compares two strings. /// </summary> /// <param name="x">First string.</param> /// <param name="y">Second string.</param> /// <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) { 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 while (!xSpan.IsEmpty && !ySpan.IsEmpty) { 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; } xSpan = xDelimiterIndex == -1 ? default : SkipAheadOnDelimiter(xSpan.Slice(xDelimiterIndex + 1)); ySpan = yDelimiterIndex == -1 ? default : SkipAheadOnDelimiter(ySpan.Slice(yDelimiterIndex + 1)); } 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 NET 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 (!aIsInt && !bIsInt) { // Both are strings result = a.CompareTo(b, StringComparison.OrdinalIgnoreCase); } else if (aIsInt && bIsInt) { // Both are int result = value1 - value2; } else { // Only one of them is int result = aIsInt ? -1 : 1; } return result; } } } }