Skip to content

Commit

Permalink
Merge pull request dotnet/corefx#23224 from Paxxi/ipaddress_span
Browse files Browse the repository at this point in the history
Add IPAddress Span-based APIs

Commit migrated from dotnet/corefx@1fc5e1e
  • Loading branch information
stephentoub authored Aug 21, 2017
2 parents 4c1636d + 0f62ec5 commit 0b96e65
Show file tree
Hide file tree
Showing 16 changed files with 926 additions and 418 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,9 @@ public partial class IPAddress
public static readonly System.Net.IPAddress Loopback;
public static readonly System.Net.IPAddress None;
public IPAddress(byte[] address) { }
public IPAddress(ReadOnlySpan<byte> address) { }
public IPAddress(byte[] address, long scopeid) { }
public IPAddress(ReadOnlySpan<byte> address, long scopeid) { }
public IPAddress(long newAddress) { }
public System.Net.Sockets.AddressFamily AddressFamily { get { throw null; } }
public bool IsIPv4MappedToIPv6 { get { throw null; } }
Expand All @@ -202,6 +204,7 @@ public IPAddress(long newAddress) { }
public long ScopeId { get { throw null; } set { } }
public override bool Equals(object comparand) { throw null; }
public byte[] GetAddressBytes() { throw null; }
public bool TryWriteBytes(Span<byte> destination, out int bytesWritten) { throw null; }
public override int GetHashCode() { throw null; }
public static short HostToNetworkOrder(short host) { throw null; }
public static int HostToNetworkOrder(int host) { throw null; }
Expand All @@ -213,8 +216,11 @@ public IPAddress(long newAddress) { }
public static int NetworkToHostOrder(int network) { throw null; }
public static long NetworkToHostOrder(long network) { throw null; }
public static System.Net.IPAddress Parse(string ipString) { throw null; }
public static System.Net.IPAddress Parse(ReadOnlySpan<char> ipString) { throw null; }
public override string ToString() { throw null; }
public bool TryFormat(Span<char> destination, out int charsWritten) { throw null; }
public static bool TryParse(string ipString, out System.Net.IPAddress address) { throw null; }
public static bool TryParse(ReadOnlySpan<char> ipString, out System.Net.IPAddress address) { throw null; }
[Obsolete("This property has been deprecated. It is address family dependent. Please use IPAddress.Equals method to perform comparisons. http://go.microsoft.com/fwlink/?linkid=14202")]
public long Address { get { throw null; } set { } }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,10 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Microsoft.Win32.Primitives\ref\Microsoft.Win32.Primitives.csproj" />
<ProjectReference Include="..\..\System.Memory\ref\System.Memory.csproj" />
<ProjectReference Include="..\..\System.Runtime\ref\System.Runtime.csproj" />
<ProjectReference Include="..\..\System.Runtime.InteropServices\ref\System.Runtime.InteropServices.csproj" />
<ProjectReference Include="..\..\System.Runtime.Handles\ref\System.Runtime.Handles.csproj" />
</ItemGroup>
<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), dir.targets))\dir.targets" />
</Project>
</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@
<Reference Include="System.Diagnostics.Debug" />
<Reference Include="System.Diagnostics.Tracing" />
<Reference Include="System.Resources.ResourceManager" />
<Reference Include="System.Memory" />
<Reference Include="System.Runtime" />
<Reference Include="System.Runtime.Extensions" />
<Reference Include="System.Runtime.InteropServices" />
Expand Down
182 changes: 104 additions & 78 deletions src/libraries/System.Net.Primitives/src/System/Net/IPAddress.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using System.Diagnostics;
using System.Net.Sockets;
using System.Runtime.CompilerServices;

namespace System.Net
{
Expand Down Expand Up @@ -121,13 +122,13 @@ public IPAddress(long newAddress)
/// Constructor for an IPv6 Address with a specified Scope.
/// </para>
/// </devdoc>
public IPAddress(byte[] address, long scopeid)
public IPAddress(byte[] address, long scopeid) :
this(new ReadOnlySpan<byte>(address ?? ThrowAddressNullException()), scopeid)
{
if (address == null)
{
throw new ArgumentNullException(nameof(address));
}
}

public IPAddress(ReadOnlySpan<byte> address, long scopeid)
{
if (address.Length != IPAddressParserStatics.IPv6AddressBytes)
{
throw new ArgumentException(SR.dns_bad_ip_address, nameof(address));
Expand All @@ -150,27 +151,6 @@ public IPAddress(byte[] address, long scopeid)
PrivateScopeId = (uint)scopeid;
}

internal unsafe IPAddress(byte* address, int addressLength, long scopeid)
{
Debug.Assert(address != null);
Debug.Assert(addressLength == IPAddressParserStatics.IPv6AddressBytes);

// Consider: Since scope is only valid for link-local and site-local
// addresses we could implement some more robust checking here
if (scopeid < 0 || scopeid > 0x00000000FFFFFFFF)
{
throw new ArgumentOutOfRangeException(nameof(scopeid));
}

_numbers = new ushort[NumberOfLabels];
for (int i = 0; i < NumberOfLabels; i++)
{
_numbers[i] = (ushort)(address[i * 2] * 256 + address[i * 2 + 1]);
}

PrivateScopeId = (uint)scopeid;
}

internal unsafe IPAddress(ushort* numbers, int numbersLength, uint scopeid)
{
Debug.Assert(numbers != null);
Expand Down Expand Up @@ -200,22 +180,18 @@ private IPAddress(ushort[] numbers, uint scopeid)
/// Constructor for IPv4 and IPv6 Address.
/// </para>
/// </devdoc>
public IPAddress(byte[] address)
public IPAddress(byte[] address) :
this(new ReadOnlySpan<byte>(address ?? ThrowAddressNullException()))
{
if (address == null)
{
throw new ArgumentNullException(nameof(address));
}
if (address.Length != IPAddressParserStatics.IPv4AddressBytes && address.Length != IPAddressParserStatics.IPv6AddressBytes)
{
throw new ArgumentException(SR.dns_bad_ip_address, nameof(address));
}
}

public IPAddress(ReadOnlySpan<byte> address)
{
if (address.Length == IPAddressParserStatics.IPv4AddressBytes)
{
PrivateAddress = (uint)((address[3] << 24 | address[2] << 16 | address[1] << 8 | address[0]) & 0x0FFFFFFFF);
}
else
else if (address.Length == IPAddressParserStatics.IPv6AddressBytes)
{
_numbers = new ushort[NumberOfLabels];

Expand All @@ -224,27 +200,9 @@ public IPAddress(byte[] address)
_numbers[i] = (ushort)(address[i * 2] * 256 + address[i * 2 + 1]);
}
}
}

internal unsafe IPAddress(byte* address, int addressLength)
{
Debug.Assert(address != null);
Debug.Assert(addressLength > 0);
Debug.Assert(
addressLength == IPAddressParserStatics.IPv4AddressBytes ||
addressLength == IPAddressParserStatics.IPv6AddressBytes);

if (addressLength == IPAddressParserStatics.IPv4AddressBytes)
{
PrivateAddress = (uint)((address[3] << 24 | address[2] << 16 | address[1] << 8 | address[0]) & 0x0FFFFFFFF);
}
else
{
_numbers = new ushort[NumberOfLabels];
for (int i = 0; i < NumberOfLabels; i++)
{
_numbers[i] = (ushort)(address[i * 2] * 256 + address[i * 2 + 1]);
}
throw new ArgumentException(SR.dns_bad_ip_address, nameof(address));
}
}

Expand All @@ -262,13 +220,85 @@ internal IPAddress(int newAddress)
/// </devdoc>
public static bool TryParse(string ipString, out IPAddress address)
{
address = IPAddressParser.Parse(ipString, true);
if (ipString == null)
{
address = null;
return false;
}

address = IPAddressParser.Parse(ipString.AsReadOnlySpan(), tryParse: true);
return (address != null);
}

public static bool TryParse(ReadOnlySpan<char> ipSpan, out IPAddress address)
{
address = IPAddressParser.Parse(ipSpan, tryParse: true);
return (address != null);
}

public static IPAddress Parse(string ipString)
{
return IPAddressParser.Parse(ipString, false);
if (ipString == null)
{
throw new ArgumentNullException(nameof(ipString));
}

return IPAddressParser.Parse(ipString.AsReadOnlySpan(), tryParse: false);
}

public static IPAddress Parse(ReadOnlySpan<char> ipSpan)
{
return IPAddressParser.Parse(ipSpan, tryParse: false);
}

public bool TryWriteBytes(Span<byte> destination, out int bytesWritten)
{
if (IsIPv6)
{
if (destination.Length < IPAddressParserStatics.IPv6AddressBytes)
{
bytesWritten = 0;
return false;
}

WriteIPv6Bytes(destination);
bytesWritten = IPAddressParserStatics.IPv6AddressBytes;
}
else
{
if (destination.Length < IPAddressParserStatics.IPv4AddressBytes)
{
bytesWritten = 0;
return false;
}

WriteIPv4Bytes(destination);
bytesWritten = IPAddressParserStatics.IPv4AddressBytes;
}

return true;
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteIPv6Bytes(Span<byte> destination)
{
Debug.Assert(_numbers != null && _numbers.Length == NumberOfLabels);
int j = 0;
for (int i = 0; i < NumberOfLabels; i++)
{
destination[j++] = (byte)((_numbers[i] >> 8) & 0xFF);
destination[j++] = (byte)((_numbers[i]) & 0xFF);
}
}

[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteIPv4Bytes(Span<byte> destination)
{
uint address = PrivateAddress;
destination[0] = (byte)(address);
destination[1] = (byte)(address >> 8);
destination[2] = (byte)(address >> 16);
destination[3] = (byte)(address >> 24);
}

/// <devdoc>
Expand All @@ -278,33 +308,19 @@ public static IPAddress Parse(string ipString)
/// </devdoc>
public byte[] GetAddressBytes()
{
byte[] bytes;
if (IsIPv6)
{
Debug.Assert(_numbers != null && _numbers.Length == NumberOfLabels);

bytes = new byte[IPAddressParserStatics.IPv6AddressBytes];
int j = 0;
for (int i = 0; i < NumberOfLabels; i++)
{
bytes[j++] = (byte)((_numbers[i] >> 8) & 0xFF);
bytes[j++] = (byte)((_numbers[i]) & 0xFF);
}
byte[] bytes = new byte[IPAddressParserStatics.IPv6AddressBytes];
WriteIPv6Bytes(bytes);
return bytes;
}
else
{
uint address = PrivateAddress;
bytes = new byte[IPAddressParserStatics.IPv4AddressBytes];

unchecked
{
bytes[0] = (byte)(address);
bytes[1] = (byte)(address >> 8);
bytes[2] = (byte)(address >> 16);
bytes[3] = (byte)(address >> 24);
}
byte[] bytes = new byte[IPAddressParserStatics.IPv4AddressBytes];
WriteIPv4Bytes(bytes);
return bytes;
}
return bytes;
}

public AddressFamily AddressFamily
Expand Down Expand Up @@ -369,6 +385,13 @@ public override string ToString()
return _toString;
}

public bool TryFormat(Span<char> destination, out int charsWritten)
{
return IsIPv4 ?
IPAddressParser.IPv4AddressToString(PrivateAddress, destination, out charsWritten) :
IPAddressParser.IPv6AddressToString(_numbers, PrivateScopeId, destination, out charsWritten);
}

public static long HostToNetworkOrder(long host)
{
#if BIGENDIAN
Expand Down Expand Up @@ -417,7 +440,7 @@ public static bool IsLoopback(IPAddress address)
{
if (address == null)
{
throw new ArgumentNullException(nameof(address));
ThrowAddressNullException();
}

if (address.IsIPv6)
Expand Down Expand Up @@ -645,5 +668,8 @@ public IPAddress MapToIPv4()

return new IPAddress(address);
}

[MethodImpl(MethodImplOptions.NoInlining)]
private static byte[] ThrowAddressNullException() => throw new ArgumentNullException("address");
}
}
Loading

0 comments on commit 0b96e65

Please sign in to comment.