diff --git a/AMWD.Common.EntityFrameworkCore/AMWD.Common.EntityFrameworkCore.csproj b/AMWD.Common.EntityFrameworkCore/AMWD.Common.EntityFrameworkCore.csproj
index 4d76a0a..a85eb4e 100644
--- a/AMWD.Common.EntityFrameworkCore/AMWD.Common.EntityFrameworkCore.csproj
+++ b/AMWD.Common.EntityFrameworkCore/AMWD.Common.EntityFrameworkCore.csproj
@@ -22,17 +22,17 @@
-
-
-
+
+
+
-
-
+
+
-
+
diff --git a/AMWD.Common/AMWD.Common.csproj b/AMWD.Common/AMWD.Common.csproj
index 332ec50..e37a629 100644
--- a/AMWD.Common/AMWD.Common.csproj
+++ b/AMWD.Common/AMWD.Common.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/AMWD.Common/Comparer/DomainComparer.cs b/AMWD.Common/Comparer/DomainComparer.cs
new file mode 100644
index 0000000..a290e55
--- /dev/null
+++ b/AMWD.Common/Comparer/DomainComparer.cs
@@ -0,0 +1,57 @@
+using System.Collections.Generic;
+
+namespace AMWD.Common.Comparer
+{
+ ///
+ /// Defines a method to compare two domain strings.
+ ///
+ public class DomainComparer : IComparer
+ {
+ ///
+ /// Compares two domain strings and returns a value indicating
+ /// whether one is less than, equal to, or greater than the other.
+ ///
+ /// The first domain string.
+ /// The second domain string.
+ ///
+ /// A signed integer that indicates the relative values of x and y, as shown in the following table:
+ ///
+ ///
+ /// Value
+ /// Meaning
+ ///
+ /// -
+ /// Less than zero
+ /// x is less than y
+ ///
+ /// -
+ /// Zero
+ /// x equals y
+ ///
+ /// -
+ /// Greater than zero
+ /// x is greater than y
+ ///
+ ///
+ ///
+ public int Compare(string x, string y)
+ {
+ string[] left = x.Split('.');
+ string[] right = y.Split('.');
+
+ int result = left.Length.CompareTo(right.Length);
+ if (result != 0)
+ return result;
+
+ // Compare from TLD to subdomain with string comparison
+ for (int i = left.Length - 1; i >= 0; i--)
+ {
+ result = left[i].CompareTo(right[i]);
+ if (result != 0)
+ return result;
+ }
+
+ return 0;
+ }
+ }
+}
diff --git a/AMWD.Common/Comparer/IPAddressComparer.cs b/AMWD.Common/Comparer/IPAddressComparer.cs
new file mode 100644
index 0000000..9b0a4bb
--- /dev/null
+++ b/AMWD.Common/Comparer/IPAddressComparer.cs
@@ -0,0 +1,71 @@
+using System.Collections.Generic;
+using System.Net;
+
+namespace AMWD.Common.Comparer
+{
+ ///
+ /// Defines a method to compare two IP addresses.
+ ///
+ public class IPAddressComparer : IComparer
+ {
+ ///
+ /// Compares two IP addresses and returns a value indicating whether one is less than,
+ /// equal to, or greater than the other.
+ ///
+ /// The first IP address.
+ /// The second IP address.
+ ///
+ /// A signed integer that indicates the relative values of x and y, as shown in the following table.
+ ///
+ ///
+ /// Value
+ /// Meaning
+ ///
+ /// -
+ /// Less than zero
+ /// x is less than y.
+ ///
+ /// -
+ /// Zero
+ /// x equals y.
+ ///
+ /// -
+ /// Greater than zero
+ /// x is greater than y.
+ ///
+ ///
+ ///
+ public int Compare(IPAddress x, IPAddress y)
+ {
+ if (x == null && y == null)
+ return 0;
+
+ if (x == null)
+ return -1;
+
+ if (y == null)
+ return 1;
+
+ byte[] xBytes = x.GetAddressBytes();
+ byte[] yBytes = y.GetAddressBytes();
+
+ // Make IPv4 and IPv6 comparable
+ byte[] left = xBytes.Length == 16
+ ? xBytes
+ : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, xBytes[0], xBytes[1], xBytes[2], xBytes[3]];
+ byte[] right = yBytes.Length == 16
+ ? yBytes
+ : [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, yBytes[0], yBytes[1], yBytes[2], yBytes[3]];
+
+ // Compare byte for byte
+ for (int i = 0; i < left.Length; i++)
+ {
+ int result = left[i].CompareTo(right[i]);
+ if (result != 0)
+ return result;
+ }
+
+ return 0;
+ }
+ }
+}
diff --git a/AMWD.Common/Comparer/VersionStringComparer.cs b/AMWD.Common/Comparer/VersionStringComparer.cs
index aa5e1b2..caf0f38 100644
--- a/AMWD.Common/Comparer/VersionStringComparer.cs
+++ b/AMWD.Common/Comparer/VersionStringComparer.cs
@@ -15,7 +15,7 @@ namespace AMWD.Common.Comparer
public class VersionStringComparer : IComparer
{
- private readonly Regex _versionRegex = new("([0-9.]+)");
+ private readonly Regex _versionRegex = new("([0-9.]+)", RegexOptions.Compiled);
#endif
///
@@ -32,16 +32,16 @@ namespace AMWD.Common.Comparer
/// Meaning
///
/// -
- /// -1
- /// x is less than y
+ /// Less than zero
+ /// x is less than y.
///
/// -
- /// 0
- /// x equals y
+ /// Zero
+ /// x equals y.
///
/// -
- /// 1
- /// x is greater than y
+ /// Greater than zero
+ /// x is greater than y.
///
///
///
@@ -79,7 +79,7 @@ namespace AMWD.Common.Comparer
}
#if NET8_0_OR_GREATER
- [GeneratedRegex("([0-9.]+)")]
+ [GeneratedRegex("([0-9.]+)", RegexOptions.Compiled)]
private static partial Regex VersionRegex();
#endif
}
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1b43cb5..b9ff10d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- `TarReader` and `TarWriter` for TAR archives
- `.AddRange()` for collections
- `.AddIfNotNull()` for collections
+- `DomainComparer` ordering alphabetically from TLD to sub-domain
+- `IPAddressComparer` able to compare IPv4 and IPv6
- `VersionStringComparer` to compare version strings (SemVer)
### Changed
diff --git a/UnitTests/Common/Comparer/DomainComparerTests.cs b/UnitTests/Common/Comparer/DomainComparerTests.cs
new file mode 100644
index 0000000..5912293
--- /dev/null
+++ b/UnitTests/Common/Comparer/DomainComparerTests.cs
@@ -0,0 +1,59 @@
+using AMWD.Common.Comparer;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace UnitTests.Common.Comparer
+{
+ [TestClass]
+ public class DomainComparerTests
+ {
+ [DataTestMethod]
+ [DataRow("int", "internal")]
+ [DataRow("int", "dom.int")]
+ [DataRow("a.ins", "a.int")]
+ [DataRow("a.internal", "b.internal")]
+ [DataRow("sub1.domain.internal", "sub2.domain.internal")]
+ public void ShouldBeLessThan(string left, string right)
+ {
+ // Arrange
+ var comparer = new DomainComparer();
+
+ // Act
+ int result = comparer.Compare(left, right);
+
+ Assert.AreEqual(-1, result);
+ }
+
+ [DataTestMethod]
+ [DataRow("internal", "int")]
+ [DataRow("dom.int", "int")]
+ [DataRow("a.int", "a.ins")]
+ [DataRow("b.internal", "a.internal")]
+ [DataRow("sub2.domain.internal", "sub1.domain.internal")]
+ public void ShouldBeGreaterThan(string left, string right)
+ {
+ // Arrange
+ var comparer = new DomainComparer();
+
+ // Act
+ int result = comparer.Compare(left, right);
+
+ Assert.AreEqual(1, result);
+ }
+
+ [DataTestMethod]
+ [DataRow("internal", "internal")]
+ [DataRow("dom.int", "dom.int")]
+ [DataRow("a.internal", "a.internal")]
+ [DataRow("sub.domain.internal", "sub.domain.internal")]
+ public void ShouldBeEqual(string left, string right)
+ {
+ // Arrange
+ var comparer = new DomainComparer();
+
+ // Act
+ int result = comparer.Compare(left, right);
+
+ Assert.AreEqual(0, result);
+ }
+ }
+}
diff --git a/UnitTests/Common/Comparer/IPAddressComparerTests.cs b/UnitTests/Common/Comparer/IPAddressComparerTests.cs
new file mode 100644
index 0000000..525b87c
--- /dev/null
+++ b/UnitTests/Common/Comparer/IPAddressComparerTests.cs
@@ -0,0 +1,58 @@
+using System.Net;
+using AMWD.Common.Comparer;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+
+namespace UnitTests.Common.Comparer
+{
+ [TestClass]
+ public class IPAddressComparerTests
+ {
+ [DataTestMethod]
+ [DataRow("127.0.0.0", "127.0.0.1")]
+ [DataRow("fe80::", "fe80::1")]
+ [DataRow("::ffff:7f00:0", "127.0.0.1")]
+ public void ShouldBeLessThan(string left, string right)
+ {
+ // Arrange
+ var comparer = new IPAddressComparer();
+
+ // Act
+ int result = comparer.Compare(IPAddress.Parse(left), IPAddress.Parse(right));
+
+ // Assert
+ Assert.AreEqual(-1, result);
+ }
+
+ [DataTestMethod]
+ [DataRow("127.0.0.1", "127.0.0.0")]
+ [DataRow("fe80::1", "fe80::")]
+ [DataRow("::ffff:7f00:1", "127.0.0.0")]
+ public void ShouldBeGreaterThan(string left, string right)
+ {
+ // Arrange
+ var comparer = new IPAddressComparer();
+
+ // Act
+ int result = comparer.Compare(IPAddress.Parse(left), IPAddress.Parse(right));
+
+ // Assert
+ Assert.AreEqual(1, result);
+ }
+
+ [DataTestMethod]
+ [DataRow("127.0.0.1", "127.0.0.1")]
+ [DataRow("fe80::1", "fe80::1")]
+ [DataRow("::ffff:7f00:1", "127.0.0.1")]
+ public void ShouldBeEqual(string left, string right)
+ {
+ // Arrange
+ var comparer = new IPAddressComparer();
+
+ // Act
+ int result = comparer.Compare(IPAddress.Parse(left), IPAddress.Parse(right));
+
+ // Assert
+ Assert.AreEqual(0, result);
+ }
+ }
+}