Use more `ReadOnlySpan<byte>`s in `System.Reflection.MetadataLoadContext` (#56207)
authorTheodore Tsirpanis <12659251+teo-tsirpanis@users.noreply.github.com>
Mon, 26 Jul 2021 15:41:50 +0000 (18:41 +0300)
committerGitHub <noreply@github.com>
Mon, 26 Jul 2021 15:41:50 +0000 (11:41 -0400)
* Use spans instead of byte arrays in `MetadataLoadContext.Utf8Constants`.

* Optimize Helpers.ToUtf16(ReadOnlySpan<byte>).

It avoids a byte array allocation on all frameworks.
And remove an unused overload of it.

* Address PR feedback.

* Fix ArgumentNullExceptions.

src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/CoreType.cs
src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/CoreTypes.cs
src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Ecma/EcmaSignatureTypeProviderForToString.cs
src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Helpers.cs
src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/TypeLoading/General/Utf8Constants.cs

index 1e7c489..227b537 100644 (file)
@@ -72,7 +72,7 @@ namespace System.Reflection.TypeLoading
 
     internal static class CoreTypeHelpers
     {
-        public static void GetFullName(this CoreType coreType, out byte[] ns, out byte[] name)
+        public static void GetFullName(this CoreType coreType, out ReadOnlySpan<byte> ns, out ReadOnlySpan<byte> name)
         {
             switch (coreType)
             {
@@ -121,7 +121,7 @@ namespace System.Reflection.TypeLoading
                 case CoreType.FieldOffsetAttribute: ns = Utf8Constants.SystemRuntimeInteropServices; name = Utf8Constants.FieldOffsetAttribute; return;
                 default:
                     Debug.Fail("Unexpected coreType passed to GetCoreTypeFullName: " + coreType);
-                    ns = name = null;
+                    ns = name = default;
                     return;
             }
         }
index dab13c3..c40fd5e 100644 (file)
@@ -26,7 +26,7 @@ namespace System.Reflection.TypeLoading
             {
                 for (int i = 0; i < numCoreTypes; i++)
                 {
-                    ((CoreType)i).GetFullName(out byte[] ns, out byte[] name);
+                    ((CoreType)i).GetFullName(out ReadOnlySpan<byte> ns, out ReadOnlySpan<byte> name);
                     RoType? type = coreAssembly.GetTypeCore(ns, name, ignoreCase: false, out e);
                     coreTypes[i] = type;
                     if (type == null)
index 7859dc1..af6ff67 100644 (file)
@@ -57,7 +57,7 @@ namespace System.Reflection.TypeLoading.Ecma
 
         public string GetPrimitiveType(PrimitiveTypeCode typeCode)
         {
-            typeCode.ToCoreType().GetFullName(out byte[] ns, out byte[] name);
+            typeCode.ToCoreType().GetFullName(out ReadOnlySpan<byte> ns, out ReadOnlySpan<byte> name);
             return ns.ToUtf16() + "." + name.ToUtf16();  // This is not safe for types outside of a namespace, but all primitive types are known to be in "System"
         }
     }
index 8381a6a..2c45856 100644 (file)
@@ -367,8 +367,22 @@ namespace System.Reflection.TypeLoading
 
         public static byte[] ToUtf8(this string s) => Encoding.UTF8.GetBytes(s);
 
-        public static string ToUtf16(this ReadOnlySpan<byte> utf8) => ToUtf16(utf8.ToArray());
-        public static string ToUtf16(this byte[] utf8) => Encoding.UTF8.GetString(utf8);
+#if NETCOREAPP3_1_OR_GREATER
+        public static string ToUtf16(this ReadOnlySpan<byte> utf8) => Encoding.UTF8.GetString(utf8);
+#else
+        public static unsafe string ToUtf16(this ReadOnlySpan<byte> utf8)
+        {
+            if (utf8.IsEmpty)
+            {
+                return string.Empty;
+            }
+
+            fixed (byte* ptr = utf8)
+            {
+                return Encoding.UTF8.GetString(ptr, utf8.Length);
+            }
+        }
+#endif
 
         // Guards ToString() implementations. Sample usage:
         //
index fe889e9..5617958 100644 (file)
@@ -5,60 +5,60 @@ namespace System.Reflection.TypeLoading
 {
     internal static class Utf8Constants
     {
-        public static readonly byte[] System = { 83, 121, 115, 116, 101, 109 };
-        public static readonly byte[] SystemReflection = { 83, 121, 115, 116, 101, 109, 46, 82, 101, 102, 108, 101, 99, 116, 105, 111, 110 };
-        public static readonly byte[] SystemCollectionsGeneric = { 83, 121, 115, 116, 101, 109, 46, 67, 111, 108, 108, 101, 99, 116, 105, 111, 110, 115, 46, 71, 101, 110, 101, 114, 105, 99 };
-        public static readonly byte[] SystemRuntimeInteropServices = { 83, 121, 115, 116, 101, 109, 46, 82, 117, 110, 116, 105, 109, 101, 46, 73, 110, 116, 101, 114, 111, 112, 83, 101, 114, 118, 105, 99, 101, 115 };
-        public static readonly byte[] SystemRuntimeCompilerServices = { 83, 121, 115, 116, 101, 109, 46, 82, 117, 110, 116, 105, 109, 101, 46, 67, 111, 109, 112, 105, 108, 101, 114, 83, 101, 114, 118, 105, 99, 101, 115 };
+        public static ReadOnlySpan<byte> System => new byte[] { 83, 121, 115, 116, 101, 109 };
+        public static ReadOnlySpan<byte> SystemReflection => new byte[] { 83, 121, 115, 116, 101, 109, 46, 82, 101, 102, 108, 101, 99, 116, 105, 111, 110 };
+        public static ReadOnlySpan<byte> SystemCollectionsGeneric => new byte[] { 83, 121, 115, 116, 101, 109, 46, 67, 111, 108, 108, 101, 99, 116, 105, 111, 110, 115, 46, 71, 101, 110, 101, 114, 105, 99 };
+        public static ReadOnlySpan<byte> SystemRuntimeInteropServices => new byte[] { 83, 121, 115, 116, 101, 109, 46, 82, 117, 110, 116, 105, 109, 101, 46, 73, 110, 116, 101, 114, 111, 112, 83, 101, 114, 118, 105, 99, 101, 115 };
+        public static ReadOnlySpan<byte> SystemRuntimeCompilerServices => new byte[] { 83, 121, 115, 116, 101, 109, 46, 82, 117, 110, 116, 105, 109, 101, 46, 67, 111, 109, 112, 105, 108, 101, 114, 83, 101, 114, 118, 105, 99, 101, 115 };
 
-        public static readonly byte[] Array = { 65, 114, 114, 97, 121 };
-        public static readonly byte[] Boolean = { 66, 111, 111, 108, 101, 97, 110 };
-        public static readonly byte[] Byte = { 66, 121, 116, 101 };
-        public static readonly byte[] Char = { 67, 104, 97, 114 };
-        public static readonly byte[] Double = { 68, 111, 117, 98, 108, 101 };
-        public static readonly byte[] Enum = { 69, 110, 117, 109 };
-        public static readonly byte[] Int16 = { 73, 110, 116, 49, 54 };
-        public static readonly byte[] Int32 = { 73, 110, 116, 51, 50 };
-        public static readonly byte[] Int64 = { 73, 110, 116, 54, 52 };
-        public static readonly byte[] IntPtr = { 73, 110, 116, 80, 116, 114 };
-        public static readonly byte[] Object = { 79, 98, 106, 101, 99, 116 };
-        public static readonly byte[] NullableT = { 78, 117, 108, 108, 97, 98, 108, 101, 96, 49 };
-        public static readonly byte[] SByte = { 83, 66, 121, 116, 101 };
-        public static readonly byte[] Single = { 83, 105, 110, 103, 108, 101 };
-        public static readonly byte[] String = { 83, 116, 114, 105, 110, 103 };
-        public static readonly byte[] TypedReference = { 84, 121, 112, 101, 100, 82, 101, 102, 101, 114, 101, 110, 99, 101 };
-        public static readonly byte[] UInt16 = { 85, 73, 110, 116, 49, 54 };
-        public static readonly byte[] UInt32 = { 85, 73, 110, 116, 51, 50 };
-        public static readonly byte[] UInt64 = { 85, 73, 110, 116, 54, 52 };
-        public static readonly byte[] UIntPtr = { 85, 73, 110, 116, 80, 116, 114 };
-        public static readonly byte[] ValueType = { 86, 97, 108, 117, 101, 84, 121, 112, 101 };
-        public static readonly byte[] Void = { 86, 111, 105, 100 };
-        public static readonly byte[] MulticastDelegate = { 77, 117, 108, 116, 105, 99, 97, 115, 116, 68, 101, 108, 101, 103, 97, 116, 101 };
-        public static readonly byte[] IEnumerableT = { 73, 69, 110, 117, 109, 101, 114, 97, 98, 108, 101, 96, 49 };
-        public static readonly byte[] ICollectionT = { 73, 67, 111, 108, 108, 101, 99, 116, 105, 111, 110, 96, 49 };
-        public static readonly byte[] IListT = { 73, 76, 105, 115, 116, 96, 49 };
-        public static readonly byte[] IReadOnlyListT = { 73, 82, 101, 97, 100, 79, 110, 108, 121, 76, 105, 115, 116, 96, 49 };
-        public static readonly byte[] Type = { 84, 121, 112, 101 };
-        public static readonly byte[] DBNull = { 68, 66, 78, 117, 108, 108 };
-        public static readonly byte[] Decimal = { 68, 101, 99, 105, 109, 97, 108 };
-        public static readonly byte[] DateTime = { 68, 97, 116, 101, 84, 105, 109, 101 };
-        public static readonly byte[] ComImportAttribute = { 67, 111, 109, 73, 109, 112, 111, 114, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
-        public static readonly byte[] DllImportAttribute = { 68, 108, 108, 73, 109, 112, 111, 114, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
-        public static readonly byte[] CallingConvention = { 67, 97, 108, 108, 105, 110, 103, 67, 111, 110, 118, 101, 110, 116, 105, 111, 110 };
-        public static readonly byte[] CharSet = { 67, 104, 97, 114, 83, 101, 116 };
-        public static readonly byte[] MarshalAsAttribute = { 77, 97, 114, 115, 104, 97, 108, 65, 115, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
-        public static readonly byte[] UnmanagedType = { 85, 110, 109, 97, 110, 97, 103, 101, 100, 84, 121, 112, 101 };
-        public static readonly byte[] VarEnum = { 86, 97, 114, 69, 110, 117, 109 };
-        public static readonly byte[] InAttribute = { 73, 110, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
-        public static readonly byte[] OutAttriubute = { 79, 117, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
-        public static readonly byte[] OptionalAttribute = { 79, 112, 116, 105, 111, 110, 97, 108, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
-        public static readonly byte[] PreserveSigAttribute = { 80, 114, 101, 115, 101, 114, 118, 101, 83, 105, 103, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
-        public static readonly byte[] FieldOffsetAttribute = { 70, 105, 101, 108, 100, 79, 102, 102, 115, 101, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
-        public static readonly byte[] IsByRefLikeAttribute = { 73, 115, 66, 121, 82, 101, 102, 76, 105, 107, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
-        public static readonly byte[] DecimalConstantAttribute = { 68, 101, 99, 105, 109, 97, 108, 67, 111, 110, 115, 116, 97, 110, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
-        public static readonly byte[] CustomConstantAttribute = { 67, 117, 115, 116, 111, 109, 67, 111, 110, 115, 116, 97, 110, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
-        public static readonly byte[] GuidAttribute = { 71, 117, 105, 100, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
-        public static readonly byte[] DefaultMemberAttribute = { 68, 101, 102, 97, 117, 108, 116, 77, 101, 109, 98, 101, 114, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
-        public static readonly byte[] DateTimeConstantAttribute = { 68, 97, 116, 101, 84, 105, 109, 101, 67, 111, 110, 115, 116, 97, 110, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
+        public static ReadOnlySpan<byte> Array => new byte[] { 65, 114, 114, 97, 121 };
+        public static ReadOnlySpan<byte> Boolean => new byte[] { 66, 111, 111, 108, 101, 97, 110 };
+        public static ReadOnlySpan<byte> Byte => new byte[] { 66, 121, 116, 101 };
+        public static ReadOnlySpan<byte> Char => new byte[] { 67, 104, 97, 114 };
+        public static ReadOnlySpan<byte> Double => new byte[] { 68, 111, 117, 98, 108, 101 };
+        public static ReadOnlySpan<byte> Enum => new byte[] { 69, 110, 117, 109 };
+        public static ReadOnlySpan<byte> Int16 => new byte[] { 73, 110, 116, 49, 54 };
+        public static ReadOnlySpan<byte> Int32 => new byte[] { 73, 110, 116, 51, 50 };
+        public static ReadOnlySpan<byte> Int64 => new byte[] { 73, 110, 116, 54, 52 };
+        public static ReadOnlySpan<byte> IntPtr => new byte[] { 73, 110, 116, 80, 116, 114 };
+        public static ReadOnlySpan<byte> Object => new byte[] { 79, 98, 106, 101, 99, 116 };
+        public static ReadOnlySpan<byte> NullableT => new byte[] { 78, 117, 108, 108, 97, 98, 108, 101, 96, 49 };
+        public static ReadOnlySpan<byte> SByte => new byte[] { 83, 66, 121, 116, 101 };
+        public static ReadOnlySpan<byte> Single => new byte[] { 83, 105, 110, 103, 108, 101 };
+        public static ReadOnlySpan<byte> String => new byte[] { 83, 116, 114, 105, 110, 103 };
+        public static ReadOnlySpan<byte> TypedReference => new byte[] { 84, 121, 112, 101, 100, 82, 101, 102, 101, 114, 101, 110, 99, 101 };
+        public static ReadOnlySpan<byte> UInt16 => new byte[] { 85, 73, 110, 116, 49, 54 };
+        public static ReadOnlySpan<byte> UInt32 => new byte[] { 85, 73, 110, 116, 51, 50 };
+        public static ReadOnlySpan<byte> UInt64 => new byte[] { 85, 73, 110, 116, 54, 52 };
+        public static ReadOnlySpan<byte> UIntPtr => new byte[] { 85, 73, 110, 116, 80, 116, 114 };
+        public static ReadOnlySpan<byte> ValueType => new byte[] { 86, 97, 108, 117, 101, 84, 121, 112, 101 };
+        public static ReadOnlySpan<byte> Void => new byte[] { 86, 111, 105, 100 };
+        public static ReadOnlySpan<byte> MulticastDelegate => new byte[] { 77, 117, 108, 116, 105, 99, 97, 115, 116, 68, 101, 108, 101, 103, 97, 116, 101 };
+        public static ReadOnlySpan<byte> IEnumerableT => new byte[] { 73, 69, 110, 117, 109, 101, 114, 97, 98, 108, 101, 96, 49 };
+        public static ReadOnlySpan<byte> ICollectionT => new byte[] { 73, 67, 111, 108, 108, 101, 99, 116, 105, 111, 110, 96, 49 };
+        public static ReadOnlySpan<byte> IListT => new byte[] { 73, 76, 105, 115, 116, 96, 49 };
+        public static ReadOnlySpan<byte> IReadOnlyListT => new byte[] { 73, 82, 101, 97, 100, 79, 110, 108, 121, 76, 105, 115, 116, 96, 49 };
+        public static ReadOnlySpan<byte> Type => new byte[] { 84, 121, 112, 101 };
+        public static ReadOnlySpan<byte> DBNull => new byte[] { 68, 66, 78, 117, 108, 108 };
+        public static ReadOnlySpan<byte> Decimal => new byte[] { 68, 101, 99, 105, 109, 97, 108 };
+        public static ReadOnlySpan<byte> DateTime => new byte[] { 68, 97, 116, 101, 84, 105, 109, 101 };
+        public static ReadOnlySpan<byte> ComImportAttribute => new byte[] { 67, 111, 109, 73, 109, 112, 111, 114, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
+        public static ReadOnlySpan<byte> DllImportAttribute => new byte[] { 68, 108, 108, 73, 109, 112, 111, 114, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
+        public static ReadOnlySpan<byte> CallingConvention => new byte[] { 67, 97, 108, 108, 105, 110, 103, 67, 111, 110, 118, 101, 110, 116, 105, 111, 110 };
+        public static ReadOnlySpan<byte> CharSet => new byte[] { 67, 104, 97, 114, 83, 101, 116 };
+        public static ReadOnlySpan<byte> MarshalAsAttribute => new byte[] { 77, 97, 114, 115, 104, 97, 108, 65, 115, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
+        public static ReadOnlySpan<byte> UnmanagedType => new byte[] { 85, 110, 109, 97, 110, 97, 103, 101, 100, 84, 121, 112, 101 };
+        public static ReadOnlySpan<byte> VarEnum => new byte[] { 86, 97, 114, 69, 110, 117, 109 };
+        public static ReadOnlySpan<byte> InAttribute => new byte[] { 73, 110, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
+        public static ReadOnlySpan<byte> OutAttriubute => new byte[] { 79, 117, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
+        public static ReadOnlySpan<byte> OptionalAttribute => new byte[] { 79, 112, 116, 105, 111, 110, 97, 108, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
+        public static ReadOnlySpan<byte> PreserveSigAttribute => new byte[] { 80, 114, 101, 115, 101, 114, 118, 101, 83, 105, 103, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
+        public static ReadOnlySpan<byte> FieldOffsetAttribute => new byte[] { 70, 105, 101, 108, 100, 79, 102, 102, 115, 101, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
+        public static ReadOnlySpan<byte> IsByRefLikeAttribute => new byte[] { 73, 115, 66, 121, 82, 101, 102, 76, 105, 107, 101, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
+        public static ReadOnlySpan<byte> DecimalConstantAttribute => new byte[] { 68, 101, 99, 105, 109, 97, 108, 67, 111, 110, 115, 116, 97, 110, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
+        public static ReadOnlySpan<byte> CustomConstantAttribute => new byte[] { 67, 117, 115, 116, 111, 109, 67, 111, 110, 115, 116, 97, 110, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
+        public static ReadOnlySpan<byte> GuidAttribute => new byte[] { 71, 117, 105, 100, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
+        public static ReadOnlySpan<byte> DefaultMemberAttribute => new byte[] { 68, 101, 102, 97, 117, 108, 116, 77, 101, 109, 98, 101, 114, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
+        public static ReadOnlySpan<byte> DateTimeConstantAttribute => new byte[] { 68, 97, 116, 101, 84, 105, 109, 101, 67, 111, 110, 115, 116, 97, 110, 116, 65, 116, 116, 114, 105, 98, 117, 116, 101 };
     }
 }