Improve SocketAddress.ToString() performance (dotnet/corefx#37973)
authorStephen Toub <stoub@microsoft.com>
Wed, 29 May 2019 00:40:11 +0000 (20:40 -0400)
committerGitHub <noreply@github.com>
Wed, 29 May 2019 00:40:11 +0000 (20:40 -0400)
* Improve SocketAddress.ToString() performance

* Disable test on Unix

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

src/libraries/Common/src/System/Net/SocketAddress.cs
src/libraries/System.Net.NetworkInformation/src/System.Net.NetworkInformation.csproj
src/libraries/System.Net.Primitives/tests/FunctionalTests/SocketAddressTest.cs

index c551c6071cb5b6ba6e42dded511eff298df6ac03..e9f495ed75e853e058c858e04def133b865a93da 100644 (file)
@@ -229,18 +229,50 @@ namespace System.Net.Internals
 
         public override string ToString()
         {
-            var sb = new StringBuilder().Append(Family.ToString()).Append(':').Append(Size).Append(":{");
+            // Get the address family string.  In almost all cases, this should be a cached string
+            // from the enum and won't actually allocate.
+            string familyString = Family.ToString();
 
+            // Determine the maximum length needed to format.
+            int maxLength =
+                familyString.Length + // AddressFamily
+                1 + // :
+                10 + // Size (max length for a positive Int32)
+                2 + // :{
+                (Size - DataOffset) * 4 + // at most ','+3digits per byte
+                1; // }
+
+            Span<char> result = maxLength <= 256 ? // arbitrary limit that should be large enough for the vast majority of cases
+                stackalloc char[256] :
+                new char[maxLength];
+
+            familyString.AsSpan().CopyTo(result);
+            int length = familyString.Length;
+
+            result[length++] = ':';
+
+            bool formatted = Size.TryFormat(result.Slice(length), out int charsWritten);
+            Debug.Assert(formatted);
+            length += charsWritten;
+
+            result[length++] = ':';
+            result[length++] = '{';
+
+            byte[] buffer = Buffer;
             for (int i = DataOffset; i < Size; i++)
             {
                 if (i > DataOffset)
                 {
-                    sb.Append(',');
+                    result[length++] = ',';
                 }
-                sb.Append(this[i]);
+
+                formatted = buffer[i].TryFormat(result.Slice(length), out charsWritten);
+                Debug.Assert(formatted);
+                length += charsWritten;
             }
 
-            return sb.Append('}').ToString();
+            result[length++] = '}';
+            return result.Slice(0, length).ToString();
         }
     }
 }
index 27a0801c29d512837515e9bbc3b35d566eaa67e9..594a34b098613ef3db668f22e93cbd5ce7314efe 100644 (file)
     <Reference Include="System.Collections" />
     <Reference Include="System.Diagnostics.Debug" />
     <Reference Include="System.Diagnostics.Tracing" />
+    <Reference Include="System.Memory" />
     <Reference Include="System.Net.Primitives" />
     <Reference Include="System.Net.Sockets" />
     <Reference Include="System.Resources.ResourceManager" />
index 90c7644ea02d6c4a99634659d4f150a569b66805..e851b40e026fc1487cd172d0682fee8471090cf9 100644 (file)
@@ -53,29 +53,32 @@ namespace System.Net.Primitives.Functional.Tests
             Assert.NotEqual(sa1, sa4);
         }
 
-        [ActiveIssue(30523, TestPlatforms.AnyUnix)]
-        [Fact]
-        public static void ToString_Compare_Success()
+        [Theory]
+        [InlineData(AddressFamily.InterNetwork, 64, false, "InterNetwork:64:{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}")]
+        [InlineData(AddressFamily.InterNetwork, 48, false, "InterNetwork:48:{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}")]
+        [InlineData(AddressFamily.InterNetworkV6, 48, false, "InterNetworkV6:48:{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}")]
+        [InlineData(AddressFamily.InterNetworkV6, 48, true, "InterNetworkV6:48:{2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47}")]
+        [InlineData(AddressFamily.Unix, 255, true, "Unix:255:{2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254}")]
+        [InlineData(AddressFamily.Unix, 256, true, "Unix:256:{2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255}")]
+        [InlineData(AddressFamily.Unix, 257, true, "Unix:257:{2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,0}")]
+        [InlineData(AddressFamily.Unix, 258, true, "Unix:258:{2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255,0,1}")]
+        public static void ToString_Expected_Success(AddressFamily family, int size, bool fillBytes, string expected)
         {
-            SocketAddress sa1 = new SocketAddress(AddressFamily.InterNetwork, 64);
-            SocketAddress sa2 = new SocketAddress(AddressFamily.InterNetwork, 64);
-            SocketAddress sa3 = new SocketAddress(AddressFamily.InterNetwork, 48);
-
-            SocketAddress sa4 = new SocketAddress(AddressFamily.InterNetworkV6, 48);
-
-            Assert.Equal(sa1.ToString(), sa2.ToString());
-            Assert.NotEqual(sa1.ToString(), sa3.ToString());
-            Assert.NotEqual(sa1.ToString(), sa4.ToString());
-
-            Assert.Equal("InterNetwork:64:{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}", sa1.ToString());
-            Assert.Equal("InterNetworkV6:48:{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}", sa4.ToString());
-
-            SocketAddress sa5 = new SocketAddress(AddressFamily.InterNetworkV6, 48);
-            for (int i = 2; i < sa5.Size; i++)
+            var sa = size >= 0 ? new SocketAddress(family, size) : new SocketAddress(family);
+            if (fillBytes)
             {
-                sa5[i] = (byte)i;
+                for (int i = 2; i < sa.Size; i++)
+                {
+                    sa[i] = (byte)i;
+                }
             }
-            Assert.EndsWith("2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47}", sa5.ToString());
+            Assert.Equal(expected, sa.ToString());
         }
+
+        [ActiveIssue(37997, TestPlatforms.AnyUnix)]
+        [Theory] // recombine into ToString_Expected_Success when #37997 is fixed
+        [InlineData((AddressFamily)12345, -1, false, "12345:32:{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}")]
+        public static void ToString_UnknownFamily_Success(AddressFamily family, int size, bool fillBytes, string expected) =>
+            ToString_Expected_Success(family, size, fillBytes, expected);
     }
 }