Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions DALib/Comparers/NaturalStringComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#region
using System.Collections.Generic;
#endregion

namespace DALib.Comparers;

/// <summary>
/// A natural string comparer intended to work similarly to how windows orders files. Strings with numbers will be
/// compared numerically, rather than lexicographically.
/// </summary>
public sealed class NaturalStringComparer : IComparer<string>
{
/// <summary>
/// Comparers are basically static methods, no point in creating many instances
/// </summary>
public static NaturalStringComparer Instance { get; } = new();

/// <inheritdoc />
public int Compare(string? x, string? y)
{
if (ReferenceEquals(x, y))
return 0;

if (x == null)
return -1;

if (y == null)
return 1;

var ix = 0;
var iy = 0;

while ((ix < x.Length) && (iy < y.Length))
if (char.IsDigit(x[ix]) && char.IsDigit(y[iy]))
{
// Compare numeric portions
var numResult = CompareNumericPortion(
x,
y,
ref ix,
ref iy);

if (numResult != 0)
return numResult;
} else
{
// Compare single characters
if (x[ix] != y[iy])
return x[ix]
.CompareTo(y[iy]);

ix++;
iy++;
}

// Handle case where one string is exhausted
if (ix < x.Length)
return 1; // x has remaining characters

if (iy < y.Length)
return -1; // y has remaining characters

return 0; // both strings exhausted simultaneously
}

private static int CompareNumericPortion(
string x,
string y,
ref int ix,
ref int iy)
{
// Skip leading zeros in x
while ((ix < x.Length) && (x[ix] == '0'))
ix++;

// Skip leading zeros in y
while ((iy < y.Length) && (y[iy] == '0'))
iy++;

// Count significant digits
var xDigitStart = ix;
var yDigitStart = iy;

while ((ix < x.Length) && char.IsDigit(x[ix]))
ix++;

while ((iy < y.Length) && char.IsDigit(y[iy]))
iy++;

var xDigitCount = ix - xDigitStart;
var yDigitCount = iy - yDigitStart;

// Compare by digit count first (longer number is larger)
if (xDigitCount != yDigitCount)
return xDigitCount.CompareTo(yDigitCount);

// Same number of significant digits, compare lexicographically
for (var i = 0; i < xDigitCount; i++)
{
var xDigit = x[xDigitStart + i];
var yDigit = y[yDigitStart + i];

if (xDigit != yDigit)
return xDigit.CompareTo(yDigit);
}

return 0; // Numbers are equal
}
}
52 changes: 52 additions & 0 deletions DALib/Comparers/PreferUnderscoreIgnoreCaseStringComparer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;

namespace DALib.Comparers;

/// <summary>
/// Compares two strings and returns a value indicating whether one is less than, equal to, or greater than the other.
/// Underscore will be considered less than any other character
/// </summary>
public sealed class PreferUnderscoreIgnoreCaseStringComparer : IComparer<string>
{
/// <summary>
/// Gets the default instance of the <see cref="PreferUnderscoreIgnoreCaseStringComparer" /> class.
/// </summary>
public static IComparer<string> Instance { get; } = new PreferUnderscoreIgnoreCaseStringComparer();

/// <inheritdoc />
public int Compare(string? x, string? y)
{
if (StringComparer.OrdinalIgnoreCase.Equals(x, y))
return 0;

if (x == null)
return -1;

if (y == null)
return 1;

var xLength = x.Length;
var yLength = y.Length;
var minLength = xLength < yLength ? xLength : yLength;

for (var i = 0; i < minLength; i++)
{
var xChar = char.ToUpperInvariant(x[i]);
var yChar = char.ToUpperInvariant(y[i]);

if (xChar == yChar)
continue;

if (xChar == '_')
return -1;

if (yChar == '_')
return 1;

return xChar.CompareTo(yChar);
}

return xLength.CompareTo(yLength);
}
}
7 changes: 4 additions & 3 deletions DALib/DALib.csproj
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!--suppress MsbuildTargetFrameworkTagInspection -->
<TargetFrameworks>net8.0</TargetFrameworks>
<PackageId>DALib</PackageId>
<PackageVersion>0.5.3</PackageVersion>
<PackageReadmeFile>README.md</PackageReadmeFile>
Expand All @@ -22,6 +21,8 @@
<Nullable>enable</Nullable>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TargetFramework>net9.0</TargetFramework>
<LangVersion>latestmajor</LangVersion>
</PropertyGroup>
<ItemGroup>
<None Include="$(MSBuildThisFileDirectory)README.md" Pack="true" PackagePath=""/>
Expand All @@ -30,8 +31,8 @@
<None Include="hybrasyl-512x512.png" Pack="true" PackagePath=""/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="KGySoft.Drawing.SkiaSharp" Version="7.2.0"/>
<PackageReference Include="SkiaSharp" Version="2.88.6"/>
<PackageReference Include="KGySoft.Drawing.SkiaSharp" Version="8.1.0"/>
<PackageReference Include="SkiaSharp" Version="3.116.0"/>
</ItemGroup>
<ItemGroup>
<SupportedPlatform Include="Linux"/>
Expand Down
Loading
Loading