diff --git a/src/Enclave.FastPacket/ValueIpAddress.cs b/src/Enclave.FastPacket/ValueIpAddress.cs index 8a8a261..5e09499 100644 --- a/src/Enclave.FastPacket/ValueIpAddress.cs +++ b/src/Enclave.FastPacket/ValueIpAddress.cs @@ -2,7 +2,6 @@ using System.Buffers.Binary; using System.Net; using System.Net.Sockets; -using System.Runtime.ConstrainedExecution; namespace Enclave.FastPacket; @@ -11,8 +10,8 @@ namespace Enclave.FastPacket; /// public readonly struct ValueIpAddress : IEquatable { - private readonly long _addr1; - private readonly long _addr2; + private readonly ulong _addr1; + private readonly ulong _addr2; private readonly AddressFamily _addrFamily; /// @@ -50,7 +49,7 @@ namespace Enclave.FastPacket; /// public static ValueIpAddress CreateIpv4(ReadOnlySpan address) { - if (BinaryPrimitives.TryReadInt32BigEndian(address, out var uintAddr)) + if (BinaryPrimitives.TryReadUInt32BigEndian(address, out var uintAddr)) { return new ValueIpAddress(AddressFamily.InterNetwork, uintAddr, 0); } @@ -65,8 +64,8 @@ public static ValueIpAddress CreateIpv4(ReadOnlySpan address) /// public static ValueIpAddress CreateIpv6(ReadOnlySpan address) { - if (BinaryPrimitives.TryReadInt64BigEndian(address, out var addr1) && - BinaryPrimitives.TryReadInt64BigEndian(address.Slice(sizeof(long)), out var addr2)) + if (BinaryPrimitives.TryReadUInt64BigEndian(address, out var addr2) && + BinaryPrimitives.TryReadUInt64BigEndian(address.Slice(sizeof(ulong)), out var addr1)) { return new ValueIpAddress(AddressFamily.InterNetworkV6, addr1, addr2); } @@ -107,20 +106,20 @@ public ValueIpAddress(ReadOnlySpan data) { if (data.Length > 4) { - BinaryPrimitives.TryReadInt64BigEndian(data, out _addr1); - BinaryPrimitives.TryReadInt64BigEndian(data.Slice(sizeof(long)), out _addr2); + BinaryPrimitives.TryReadUInt64BigEndian(data, out _addr2); + BinaryPrimitives.TryReadUInt64BigEndian(data.Slice(sizeof(ulong)), out _addr1); _addrFamily = AddressFamily.InterNetworkV6; } else { - BinaryPrimitives.TryReadInt32BigEndian(data, out var uintAddr); + BinaryPrimitives.TryReadUInt32BigEndian(data, out var uintAddr); _addr1 = uintAddr; _addr2 = 0; _addrFamily = AddressFamily.InterNetwork; } } - private ValueIpAddress(AddressFamily addrFamily, long addr1, long addr2) + private ValueIpAddress(AddressFamily addrFamily, ulong addr1, ulong addr2) { _addr1 = addr1; _addr2 = addr2; @@ -170,8 +169,8 @@ public IPAddress ToIpAddress() { Span destSpan = stackalloc byte[16]; - BinaryPrimitives.WriteInt64BigEndian(destSpan, _addr1); - BinaryPrimitives.WriteInt64BigEndian(destSpan.Slice(sizeof(long)), _addr2); + BinaryPrimitives.WriteUInt64BigEndian(destSpan, _addr2); + BinaryPrimitives.WriteUInt64BigEndian(destSpan.Slice(sizeof(ulong)), _addr1); return new IPAddress(destSpan); } @@ -179,7 +178,7 @@ public IPAddress ToIpAddress() { Span destSpan = stackalloc byte[4]; - BinaryPrimitives.WriteInt32BigEndian(destSpan, (int)_addr1); + BinaryPrimitives.WriteUInt32BigEndian(destSpan, (uint)_addr1); return new IPAddress(destSpan); } @@ -197,8 +196,8 @@ public void CopyTo(Span destination) throw new FastPacketException("Destination too small for ipv6", destination); } - BinaryPrimitives.WriteInt64BigEndian(destination, _addr1); - BinaryPrimitives.WriteInt64BigEndian(destination.Slice(sizeof(long)), _addr2); + BinaryPrimitives.WriteUInt64BigEndian(destination, _addr2); + BinaryPrimitives.WriteUInt64BigEndian(destination.Slice(sizeof(ulong)), _addr1); } else { @@ -207,7 +206,7 @@ public void CopyTo(Span destination) throw new FastPacketException("Destination too small for ipv4", destination); } - BinaryPrimitives.WriteInt32BigEndian(destination, (int)_addr1); + BinaryPrimitives.WriteUInt32BigEndian(destination, (uint)_addr1); } } @@ -232,4 +231,80 @@ public override string ToString() { return !(left == right); } + + /// + /// Greater than operator. + /// + public static bool operator >(ValueIpAddress left, ValueIpAddress right) + { + if (left._addrFamily != right._addrFamily) + { + throw new InvalidOperationException("Comparison requires both addresses to be of the same address family."); + } + + if (left._addrFamily == AddressFamily.InterNetwork) + { + return left._addr1 > right._addr1; + } + + return left._addr2 > right._addr2 || + (left._addr2 == right._addr2 && left._addr1 > right._addr1); + } + + /// + /// Greater than or equal operator. + /// + public static bool operator >=(ValueIpAddress left, ValueIpAddress right) + { + if (left._addrFamily != right._addrFamily) + { + throw new InvalidOperationException("Comparison requires both addresses to be of the same address family."); + } + + if (left._addrFamily == AddressFamily.InterNetwork) + { + return left._addr1 >= right._addr1; + } + + return left._addr2 > right._addr2 || + (left._addr2 == right._addr2 && left._addr1 >= right._addr1); + } + + /// + /// Less than operator. + /// + public static bool operator <(ValueIpAddress left, ValueIpAddress right) + { + if (left._addrFamily != right._addrFamily) + { + throw new InvalidOperationException("Comparison requires both addresses to be of the same address family."); + } + + if (left._addrFamily == AddressFamily.InterNetwork) + { + return left._addr1 < right._addr1; + } + + return left._addr2 < right._addr2 || + (left._addr2 == right._addr2 && left._addr1 < right._addr1); + } + + /// + /// Less than or equal operator. + /// + public static bool operator <=(ValueIpAddress left, ValueIpAddress right) + { + if (left._addrFamily != right._addrFamily) + { + throw new InvalidOperationException("Comparison requires both addresses to be of the same address family."); + } + + if (left._addrFamily == AddressFamily.InterNetwork) + { + return left._addr1 <= right._addr1; + } + + return left._addr2 < right._addr2 || + (left._addr2 == right._addr2 && left._addr1 <= right._addr1); + } } diff --git a/tests/Enclave.FastPacket.Tests/ValueIpAddressTests.cs b/tests/Enclave.FastPacket.Tests/ValueIpAddressTests.cs index 69c3eeb..8e3acf7 100644 --- a/tests/Enclave.FastPacket.Tests/ValueIpAddressTests.cs +++ b/tests/Enclave.FastPacket.Tests/ValueIpAddressTests.cs @@ -61,4 +61,130 @@ public void EmptyIpv6AddressNotSameAsEmptyIpv4() valueIpv6.Should().NotBe(valueIpv4); } + + [Theory] + [InlineData("100.1.1.1", "101.1.1.1")] + [InlineData("1.255.255.255", "2.0.0.0")] + [InlineData("1.1.1.1", "1.1.1.2")] + [InlineData("0.0.0.0", "0.0.0.1")] + public void CanCompareLessThanIpv4Address(string left, string right) + { + var ipAddress1 = IPAddress.Parse(left); + var ipAddress2 = IPAddress.Parse(right); + + var valueIp1 = ValueIpAddress.Create(ipAddress1); + var valueIp2 = ValueIpAddress.Create(ipAddress2); + var repeat1 = ValueIpAddress.Create(ipAddress1); + + (valueIp1 < valueIp2).Should().BeTrue(); + (valueIp2 < valueIp1).Should().BeFalse(); + (valueIp1 < repeat1).Should().BeFalse(); + + } + + [Theory] + [InlineData("100.1.1.1", "101.1.1.1")] + [InlineData("1.255.255.255", "2.0.0.0")] + [InlineData("1.1.1.1", "1.1.1.2")] + [InlineData("0.0.0.0", "0.0.0.1")] + public void CanCompareLessThanOrEqualIpv4Address(string left, string right) + { + var ipAddress1 = IPAddress.Parse(left); + var ipAddress2 = IPAddress.Parse(right); + + var valueIp1 = ValueIpAddress.Create(ipAddress1); + var valueIp2 = ValueIpAddress.Create(ipAddress2); + var repeat1 = ValueIpAddress.Create(ipAddress1); + + (valueIp1 <= valueIp2).Should().BeTrue(); + (valueIp2 <= valueIp1).Should().BeFalse(); + (valueIp1 <= repeat1).Should().BeTrue(); + + } + + [Theory] + [InlineData("101.1.1.1", "100.1.1.1")] + [InlineData("2.0.0.0", "1.255.255.255")] + [InlineData("1.1.1.2", "1.1.1.1")] + [InlineData("0.0.0.1", "0.0.0.0")] + public void CanCompareGreaterThanIpv4Address(string left, string right) + { + var ipAddress1 = IPAddress.Parse(left); + var ipAddress2 = IPAddress.Parse(right); + + var valueIp1 = ValueIpAddress.Create(ipAddress1); + var valueIp2 = ValueIpAddress.Create(ipAddress2); + var repeat1 = ValueIpAddress.Create(ipAddress1); + + (valueIp1 > valueIp2).Should().BeTrue(); + (valueIp2 > valueIp1).Should().BeFalse(); + (valueIp1 > repeat1).Should().BeFalse(); + + } + + [Theory] + [InlineData("101.1.1.1", "100.1.1.1")] + [InlineData("2.0.0.0", "1.255.255.255")] + [InlineData("1.1.1.2", "1.1.1.1")] + [InlineData("0.0.0.1", "0.0.0.0")] + public void CanCompareGreaterThanOrEqualIpv4Address(string left, string right) + { + var ipAddress1 = IPAddress.Parse(left); + var ipAddress2 = IPAddress.Parse(right); + + var valueIp1 = ValueIpAddress.Create(ipAddress1); + var valueIp2 = ValueIpAddress.Create(ipAddress2); + var repeat1 = ValueIpAddress.Create(ipAddress1); + + (valueIp1 >= valueIp2).Should().BeTrue(); + (valueIp2 >= valueIp1).Should().BeFalse(); + (valueIp1 >= repeat1).Should().BeTrue(); + + } + + [Theory] + [InlineData("2001:db8:3333:4444:5555:6666:7777:8889", "2001:db8:3333:4444:5555:6666:7777:8888")] + [InlineData("2001:db8:3333:4445:5555:6666:7777:8888", "2001:db8:3333:4444:5555:6666:7777:8888")] + [InlineData("2002:db8:3333:4444:5555:6666:7777:8888", "2001:db8:3333:4444:5555:6666:7777:8888")] + [InlineData("::1", "::")] + [InlineData("::18.52.86.121", "::18.52.86.120")] + [InlineData("2002::", "2001:db8::1234:5678")] + [InlineData("1::", "::1")] + public void CanCompareGreaterThanIpv6Address(string left, string right) + { + var ipAddress1 = IPAddress.Parse(left); + var ipAddress2 = IPAddress.Parse(right); + + var valueIp1 = ValueIpAddress.Create(ipAddress1); + var valueIp2 = ValueIpAddress.Create(ipAddress2); + var repeat1 = ValueIpAddress.Create(ipAddress1); + + (valueIp1 > valueIp2).Should().BeTrue(); + (valueIp2 > valueIp1).Should().BeFalse(); + (valueIp1 > repeat1).Should().BeFalse(); + + } + + [Theory] + [InlineData("2001:db8:3333:4444:5555:6666:7777:8889", "2001:db8:3333:4444:5555:6666:7777:8888")] + [InlineData("2001:db8:3333:4445:5555:6666:7777:8888", "2001:db8:3333:4444:5555:6666:7777:8888")] + [InlineData("2002:db8:3333:4444:5555:6666:7777:8888", "2001:db8:3333:4444:5555:6666:7777:8888")] + [InlineData("::1", "::")] + [InlineData("::18.52.86.121", "::18.52.86.120")] + [InlineData("2002::", "2001:db8::1234:5678")] + [InlineData("1::", "::1")] + public void CanCompareGreaterThanOrEqualIpv6Address(string left, string right) + { + var ipAddress1 = IPAddress.Parse(left); + var ipAddress2 = IPAddress.Parse(right); + + var valueIp1 = ValueIpAddress.Create(ipAddress1); + var valueIp2 = ValueIpAddress.Create(ipAddress2); + var repeat1 = ValueIpAddress.Create(ipAddress1); + + (valueIp1 >= valueIp2).Should().BeTrue(); + (valueIp2 >= valueIp1).Should().BeFalse(); + (valueIp1 >= repeat1).Should().BeTrue(); + + } }