From b6e104f22f6baec437828014611a196f4cf72c4b Mon Sep 17 00:00:00 2001
From: mgravell <marc.gravell@gmail.com>
Date: Thu, 4 Jul 2019 08:21:17 +0100
Subject: [PATCH] make use of Encoding.GetString(byte*, int) when available;
 poly-fill when not available (NET45); move related logic to extension class

---
 src/csharp/Grpc.Core.Api/Metadata.cs          | 17 ++-------
 .../Grpc.Core.Api/Utils/EncodingExtensions.cs | 36 +++++++++++++++++++
 src/csharp/Grpc.Core/Internal/MarshalUtils.cs | 23 ++++--------
 3 files changed, 45 insertions(+), 31 deletions(-)
 create mode 100644 src/csharp/Grpc.Core.Api/Utils/EncodingExtensions.cs

diff --git a/src/csharp/Grpc.Core.Api/Metadata.cs b/src/csharp/Grpc.Core.Api/Metadata.cs
index 7a04a2ee859..cd1ea3b4676 100644
--- a/src/csharp/Grpc.Core.Api/Metadata.cs
+++ b/src/csharp/Grpc.Core.Api/Metadata.cs
@@ -19,7 +19,7 @@ using System.Collections;
 using System.Collections.Generic;
 using System.Runtime.InteropServices;
 using System.Text;
-using System.Text.RegularExpressions;
+using Grpc.Core.Api.Utils;
 
 using Grpc.Core.Utils;
 
@@ -364,20 +364,7 @@ namespace Grpc.Core
                 }
                 else
                 {
-                    string s;
-                    if (length == 0)
-                    {
-                        s = "";
-                    }
-                    else
-                    {
-                        int charCount = EncodingASCII.GetCharCount(source, length);
-                        s = new string('\0', charCount);
-                        fixed (char* cPtr = s)
-                        {
-                            EncodingASCII.GetChars(source, length, cPtr, charCount);
-                        }
-                    }
+                    string s = length == 0 ? "" : EncodingASCII.GetString(source, length);
                     return new Entry(key, s, null);
                 }
             }
diff --git a/src/csharp/Grpc.Core.Api/Utils/EncodingExtensions.cs b/src/csharp/Grpc.Core.Api/Utils/EncodingExtensions.cs
new file mode 100644
index 00000000000..ef38baf5cc4
--- /dev/null
+++ b/src/csharp/Grpc.Core.Api/Utils/EncodingExtensions.cs
@@ -0,0 +1,36 @@
+using System;
+using System.Text;
+
+namespace Grpc.Core.Api.Utils
+{
+
+    internal static class EncodingExtensions
+    {
+#if NET45 // back-fill over a method missing in NET45
+        /// <summary>
+        /// Converts <c>byte*</c> pointing to an encoded byte array to a <c>string</c> using the provided <c>Encoding</c>.
+        /// </summary>
+        public static unsafe string GetString(this Encoding encoding, byte* source, int byteCount)
+        {
+            if (byteCount == 0) return ""; // most callers will have already checked, but: make sure
+
+            // allocate a right-sized string and decode into it
+            int charCount = encoding.GetCharCount(source, byteCount);
+            string s = new string('\0', charCount);
+            fixed (char* cPtr = s)
+            {
+                encoding.GetChars(source, byteCount, cPtr, charCount);
+            }
+            return s;
+        }
+#endif
+        /// <summary>
+        /// Converts <c>IntPtr</c> pointing to a encoded byte array to a <c>string</c> using the provided <c>Encoding</c>.
+        /// </summary>
+        public static unsafe string GetString(this Encoding encoding, IntPtr ptr, int len)
+        {
+            return len == 0 ? "" : encoding.GetString((byte*)ptr.ToPointer(), len);
+        }
+    }
+
+}
diff --git a/src/csharp/Grpc.Core/Internal/MarshalUtils.cs b/src/csharp/Grpc.Core/Internal/MarshalUtils.cs
index 54b4370935d..313e2b5c9d7 100644
--- a/src/csharp/Grpc.Core/Internal/MarshalUtils.cs
+++ b/src/csharp/Grpc.Core/Internal/MarshalUtils.cs
@@ -17,8 +17,9 @@
 #endregion
 
 using System;
-using System.Runtime.InteropServices;
+using System.Runtime.CompilerServices;
 using System.Text;
+using Grpc.Core.Api.Utils;
 
 namespace Grpc.Core.Internal
 {
@@ -32,22 +33,10 @@ namespace Grpc.Core.Internal
         /// <summary>
         /// Converts <c>IntPtr</c> pointing to a UTF-8 encoded byte array to <c>string</c>.
         /// </summary>
-        public static unsafe string PtrToStringUTF8(IntPtr ptr, int len)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        public static string PtrToStringUTF8(IntPtr ptr, int len)
         {
-            if (len == 0)
-            {
-                return "";
-            }
-
-            // allocate a right-sized string and decode into it
-            byte* source = (byte*)ptr.ToPointer();
-            int charCount = EncodingUTF8.GetCharCount(source, len);
-            string s = new string('\0', charCount);
-            fixed(char* cPtr = s)
-            {
-                EncodingUTF8.GetChars(source, len, cPtr, charCount);
-            }
-            return s;
+            return EncodingUTF8.GetString(ptr, len);
         }
 
         /// <summary>
@@ -66,6 +55,7 @@ namespace Grpc.Core.Internal
         /// <summary>
         /// Returns the maximum number of bytes required to encode a given string.
         /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static int GetMaxByteCountUTF8(string str)
         {
             return EncodingUTF8.GetMaxByteCount(str.Length);
@@ -74,6 +64,7 @@ namespace Grpc.Core.Internal
         /// <summary>
         /// Returns the actual number of bytes required to encode a given string.
         /// </summary>
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static int GetByteCountUTF8(string str)
         {
             return EncodingUTF8.GetByteCount(str);