From 508379d704f6f2bd8207eefc69fe07c5930c0c61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andreas=20M=C3=BCller?= Date: Sun, 16 Jun 2024 21:09:38 +0200 Subject: [PATCH] Adding Domain and IPAddress comparer --- .../AMWD.Common.EntityFrameworkCore.csproj | 12 ++-- AMWD.Common/AMWD.Common.csproj | 2 +- AMWD.Common/Comparer/DomainComparer.cs | 57 +++++++++++++++ AMWD.Common/Comparer/IPAddressComparer.cs | 71 +++++++++++++++++++ AMWD.Common/Comparer/VersionStringComparer.cs | 16 ++--- CHANGELOG.md | 2 + .../Common/Comparer/DomainComparerTests.cs | 59 +++++++++++++++ .../Common/Comparer/IPAddressComparerTests.cs | 58 +++++++++++++++ 8 files changed, 262 insertions(+), 15 deletions(-) create mode 100644 AMWD.Common/Comparer/DomainComparer.cs create mode 100644 AMWD.Common/Comparer/IPAddressComparer.cs create mode 100644 UnitTests/Common/Comparer/DomainComparerTests.cs create mode 100644 UnitTests/Common/Comparer/IPAddressComparerTests.cs 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); + } + } +}