Skip to content

Commit

Permalink
Merge pull request #7 from bcronje/feature/valueipaddress-additions
Browse files Browse the repository at this point in the history
Add ValueIpAddress comparison operators.
  • Loading branch information
enclave-alistair authored Nov 11, 2022
2 parents 8b1636c + 8972821 commit 6cf7e1b
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 16 deletions.
107 changes: 91 additions & 16 deletions src/Enclave.FastPacket/ValueIpAddress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System.Buffers.Binary;
using System.Net;
using System.Net.Sockets;
using System.Runtime.ConstrainedExecution;

namespace Enclave.FastPacket;

Expand All @@ -11,8 +10,8 @@ namespace Enclave.FastPacket;
/// </summary>
public readonly struct ValueIpAddress : IEquatable<ValueIpAddress>
{
private readonly long _addr1;
private readonly long _addr2;
private readonly ulong _addr1;
private readonly ulong _addr2;
private readonly AddressFamily _addrFamily;

/// <summary>
Expand Down Expand Up @@ -50,7 +49,7 @@ namespace Enclave.FastPacket;
/// </summary>
public static ValueIpAddress CreateIpv4(ReadOnlySpan<byte> address)
{
if (BinaryPrimitives.TryReadInt32BigEndian(address, out var uintAddr))
if (BinaryPrimitives.TryReadUInt32BigEndian(address, out var uintAddr))
{
return new ValueIpAddress(AddressFamily.InterNetwork, uintAddr, 0);
}
Expand All @@ -65,8 +64,8 @@ public static ValueIpAddress CreateIpv4(ReadOnlySpan<byte> address)
/// </summary>
public static ValueIpAddress CreateIpv6(ReadOnlySpan<byte> 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);
}
Expand Down Expand Up @@ -107,20 +106,20 @@ public ValueIpAddress(ReadOnlySpan<byte> 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;
Expand Down Expand Up @@ -170,16 +169,16 @@ public IPAddress ToIpAddress()
{
Span<byte> 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);
}
else
{
Span<byte> destSpan = stackalloc byte[4];

BinaryPrimitives.WriteInt32BigEndian(destSpan, (int)_addr1);
BinaryPrimitives.WriteUInt32BigEndian(destSpan, (uint)_addr1);

return new IPAddress(destSpan);
}
Expand All @@ -197,8 +196,8 @@ public void CopyTo(Span<byte> 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
{
Expand All @@ -207,7 +206,7 @@ public void CopyTo(Span<byte> destination)
throw new FastPacketException("Destination too small for ipv4", destination);
}

BinaryPrimitives.WriteInt32BigEndian(destination, (int)_addr1);
BinaryPrimitives.WriteUInt32BigEndian(destination, (uint)_addr1);
}
}

Expand All @@ -232,4 +231,80 @@ public override string ToString()
{
return !(left == right);
}

/// <summary>
/// Greater than operator.
/// </summary>
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);
}

/// <summary>
/// Greater than or equal operator.
/// </summary>
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);
}

/// <summary>
/// Less than operator.
/// </summary>
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);
}

/// <summary>
/// Less than or equal operator.
/// </summary>
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);
}
}
126 changes: 126 additions & 0 deletions tests/Enclave.FastPacket.Tests/ValueIpAddressTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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();

}
}

0 comments on commit 6cf7e1b

Please sign in to comment.