Skip to content

Commit e3a9ce9

Browse files
committed
Arm support. Small performance optimization.
1 parent 3b31e72 commit e3a9ce9

File tree

1 file changed

+101
-23
lines changed

1 file changed

+101
-23
lines changed

src/Files.App/Helpers/NaturalStringComparer.cs

Lines changed: 101 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,32 +7,110 @@ public sealed class NaturalStringComparer
77
{
88
public static IComparer<object> GetForProcessor()
99
{
10-
return Win32Helper.IsRunningOnArm ? new StringComparerArm64() : new StringComparerDefault();
10+
return new NaturalComparer(StringComparison.CurrentCulture);
1111
}
12-
13-
private sealed class StringComparerArm64 : IComparer<object>
12+
13+
private sealed class NaturalComparer : IComparer<object?>, IComparer<string?>, IComparer<ReadOnlyMemory<char>>
1414
{
15-
public int Compare(object a, object b)
16-
{
17-
return StringComparer.CurrentCulture.Compare(a, b);
18-
}
19-
}
15+
private readonly StringComparison stringComparison;
2016

21-
private sealed class StringComparerDefault : IComparer<object>
22-
{
23-
public int Compare(object a, object b)
24-
{
25-
return Win32PInvoke.CompareStringEx(
26-
Win32PInvoke.LOCALE_NAME_USER_DEFAULT,
27-
Win32PInvoke.SORT_DIGITSASNUMBERS, // Add other flags if required.
28-
a?.ToString(),
29-
a?.ToString().Length ?? 0,
30-
b?.ToString(),
31-
b?.ToString().Length ?? 0,
32-
IntPtr.Zero,
33-
IntPtr.Zero,
34-
0) - 2;
35-
}
17+
public NaturalComparer(StringComparison stringComparison = StringComparison.Ordinal)
18+
{
19+
this.stringComparison = stringComparison;
20+
}
21+
22+
public int Compare(object? x, object? y)
23+
{
24+
if (x == y) return 0;
25+
if (x == null) return -1;
26+
if (y == null) return 1;
27+
28+
return x switch
29+
{
30+
string x1 when y is string y1 => Compare(x1.AsSpan(), y1.AsSpan(), stringComparison),
31+
IComparable comparable => comparable.CompareTo(y),
32+
_ => StringComparer.FromComparison(stringComparison).Compare(x, y)
33+
};
34+
}
35+
36+
public int Compare(string? x, string? y)
37+
{
38+
if (ReferenceEquals(x, y)) return 0;
39+
if (x is null) return -1;
40+
if (y is null) return 1;
41+
42+
return Compare(x.AsSpan(), y.AsSpan(), stringComparison);
43+
}
44+
45+
public int Compare(ReadOnlySpan<char> x, ReadOnlySpan<char> y)
46+
{
47+
return Compare(x, y, stringComparison);
48+
}
49+
50+
public int Compare(ReadOnlyMemory<char> x, ReadOnlyMemory<char> y)
51+
{
52+
return Compare(x.Span, y.Span, stringComparison);
53+
}
54+
55+
public static int Compare(ReadOnlySpan<char> x, ReadOnlySpan<char> y, StringComparison stringComparison)
56+
{
57+
var length = Math.Min(x.Length, y.Length);
58+
59+
for (var i = 0; i < length; i++)
60+
{
61+
if (char.IsDigit(x[i]) && char.IsDigit(y[i]))
62+
{
63+
var xOut = GetNumber(x.Slice(i), out var xNumAsSpan);
64+
var yOut = GetNumber(y.Slice(i), out var yNumAsSpan);
65+
66+
var compareResult = CompareNumValues(xNumAsSpan, yNumAsSpan);
67+
68+
if (compareResult != 0) return compareResult;
69+
70+
i = -1;
71+
length = Math.Min(xOut.Length, yOut.Length);
72+
73+
x = xOut;
74+
y = yOut;
75+
continue;
76+
}
77+
78+
var charCompareResult = x.Slice(i, 1).CompareTo(y.Slice(i, 1), stringComparison);
79+
if (charCompareResult != 0) return charCompareResult;
80+
}
81+
82+
return x.Length.CompareTo(y.Length);
83+
}
84+
85+
private static ReadOnlySpan<char> GetNumber(ReadOnlySpan<char> span, out ReadOnlySpan<char> number)
86+
{
87+
var i = 0;
88+
while (i < span.Length && char.IsDigit(span[i]))
89+
{
90+
i++;
91+
}
92+
93+
number = span.Slice(0, i);
94+
return span.Slice(i);
95+
}
96+
97+
private static int CompareNumValues(ReadOnlySpan<char> numValue1, ReadOnlySpan<char> numValue2)
98+
{
99+
var num1AsSpan = numValue1.TrimStart('0');
100+
var num2AsSpan = numValue2.TrimStart('0');
101+
102+
if (num1AsSpan.Length < num2AsSpan.Length) return -1;
103+
104+
if (num1AsSpan.Length > num2AsSpan.Length) return 1;
105+
106+
var compareResult = num1AsSpan.CompareTo(num2AsSpan, StringComparison.Ordinal);
107+
108+
if (compareResult != 0) return Math.Sign(compareResult);
109+
110+
if (numValue2.Length == numValue1.Length) return compareResult;
111+
112+
return numValue2.Length < numValue1.Length ? -1 : 1; // "033" < "33" === true
113+
}
36114
}
37115
}
38116
}

0 commit comments

Comments
 (0)