Factor IPAddress.Loopback.MapToIPv6() into IPAddress.IsLooback (dotnet/corefx#35463)
authorStephen Toub <stoub@microsoft.com>
Thu, 21 Feb 2019 00:24:39 +0000 (19:24 -0500)
committerGitHub <noreply@github.com>
Thu, 21 Feb 2019 00:24:39 +0000 (19:24 -0500)
Simply change to check for IPAddress.Loopback.MapToIPv6() in addition to IPAddress.Loopback and IPAddress.IPv6Loopback in IPAddress.IsLoopback.  Doing so caused a small regression in throughput for IPAddress.Equals, so I made a few other tweaks to avoid the regression.

Commit migrated from https://github.com/dotnet/corefx/commit/a8945a36bb2d6068dc2e6b07573f0207e4eb8b85

src/libraries/System.Net.Primitives/src/System/Net/IPAddress.cs
src/libraries/System.Net.Primitives/tests/FunctionalTests/IPAddressTest.cs

index 3b5b8ba..faa856f 100644 (file)
@@ -28,6 +28,8 @@ namespace System.Net
         public static readonly IPAddress IPv6Loopback = new IPAddress(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }, 0);
         public static readonly IPAddress IPv6None = new IPAddress(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, 0);
 
+        private static readonly IPAddress s_loopbackMappedToIPv6 = new IPAddress(new byte[] { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 255, 255, 127, 0, 0, 1 }, 0);
+
         /// <summary>
         /// For IPv4 addresses, this field stores the Address.
         /// For IPv6 addresses, this field stores the ScopeId.
@@ -425,7 +427,7 @@ namespace System.Net
             if (address.IsIPv6)
             {
                 // Do Equals test for IPv6 addresses
-                return address.Equals(IPv6Loopback);
+                return address.Equals(IPv6Loopback) || address.Equals(s_loopbackMappedToIPv6);
             }
             else
             {
@@ -542,33 +544,31 @@ namespace System.Net
             }
         }
 
-        internal bool Equals(object comparandObj, bool compareScopeId)
+        /// <summary>Compares two IP addresses.</summary>
+        public override bool Equals(object comparand)
         {
-            IPAddress comparand = comparandObj as IPAddress;
+            return comparand is IPAddress address && Equals(address);
+        }
 
-            if (comparand == null)
-            {
-                return false;
-            }
+        internal bool Equals(IPAddress comparand)
+        {
+            Debug.Assert(comparand != null);
 
             // Compare families before address representations
             if (AddressFamily != comparand.AddressFamily)
             {
                 return false;
             }
+
             if (IsIPv6)
             {
-                // For IPv6 addresses, we must compare the full 128-bit representation.
-                for (int i = 0; i < NumberOfLabels; i++)
-                {
-                    if (comparand._numbers[i] != _numbers[i])
-                    {
-                        return false;
-                    }
-                }
-
-                // The scope IDs must also match
-                return comparand.PrivateScopeId == PrivateScopeId || !compareScopeId;
+                // For IPv6 addresses, we must compare the full 128-bit representation and the scope IDs.
+                ReadOnlySpan<byte> thisNumbers = MemoryMarshal.AsBytes<ushort>(_numbers);
+                ReadOnlySpan<byte> comparandNumbers = MemoryMarshal.AsBytes<ushort>(comparand._numbers);
+                return
+                    MemoryMarshal.Read<ulong>(thisNumbers) == MemoryMarshal.Read<ulong>(comparandNumbers) &&
+                    MemoryMarshal.Read<ulong>(thisNumbers.Slice(sizeof(ulong))) == MemoryMarshal.Read<ulong>(comparandNumbers.Slice(sizeof(ulong))) &&
+                    PrivateScopeId == comparand.PrivateScopeId;
             }
             else
             {
@@ -577,16 +577,6 @@ namespace System.Net
             }
         }
 
-        /// <devdoc>
-        ///   <para>
-        ///     Compares two IP addresses.
-        ///   </para>
-        /// </devdoc>
-        public override bool Equals(object comparand)
-        {
-            return Equals(comparand, true);
-        }
-
         public override int GetHashCode()
         {
             if (_hashCode != 0)
index f4628c0..b544196 100644 (file)
@@ -191,6 +191,9 @@ namespace System.Net.Primitives.Functional.Tests
 
             ip = new IPAddress(IPAddress.IPv6Loopback.GetAddressBytes()); //IpV6 loopback
             Assert.True(IPAddress.IsLoopback(ip));
+
+            ip = new IPAddress(IPAddress.Loopback.MapToIPv6().GetAddressBytes()); // IPv4 loopback mapped to IPv6
+            Assert.Equal(!PlatformDetection.IsFullFramework, IPAddress.IsLoopback(ip)); // https://github.com/dotnet/corefx/issues/35454
         }
 
         [Fact]