Improve char.ToUpperInvariant / ToLowerInvariant perf (#35194)
authorLevi Broderick <GrabYourPitchforks@users.noreply.github.com>
Mon, 20 Apr 2020 19:06:01 +0000 (12:06 -0700)
committerGitHub <noreply@github.com>
Mon, 20 Apr 2020 19:06:01 +0000 (12:06 -0700)
src/libraries/System.Private.CoreLib/src/System/Char.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/TextInfo.cs

index f44f376460dada9d4b212ac80381a88709b75f8e..2ccf35ee7375487148f79f1d021db150ee262bb7 100644 (file)
@@ -354,10 +354,7 @@ namespace System
         }
 
         // Converts a character to upper-case for invariant culture.
-        public static char ToUpperInvariant(char c)
-        {
-            return TextInfo.Invariant.ToUpper(c);
-        }
+        public static char ToUpperInvariant(char c) => TextInfo.ToUpperInvariant(c);
 
         /*===================================ToLower====================================
         **
@@ -383,10 +380,7 @@ namespace System
         }
 
         // Converts a character to lower-case for invariant culture.
-        public static char ToLowerInvariant(char c)
-        {
-            return TextInfo.Invariant.ToLower(c);
-        }
+        public static char ToLowerInvariant(char c) => TextInfo.ToLowerInvariant(c);
 
         //
         // IConvertible implementation
index 14d564258043a1e0d5a9956617ad7acf7931a904..eaa2042fd9e558e0464e91113b4348993731db75 100644 (file)
@@ -151,7 +151,7 @@ namespace System.Globalization
         /// </summary>
         public char ToLower(char c)
         {
-            if (GlobalizationMode.Invariant || (IsAscii(c) && IsAsciiCasingSameAsInvariant))
+            if (GlobalizationMode.Invariant || (UnicodeUtility.IsAsciiCodePoint(c) && IsAsciiCasingSameAsInvariant))
             {
                 return ToLowerAsciiInvariant(c);
             }
@@ -159,6 +159,16 @@ namespace System.Globalization
             return ChangeCase(c, toUpper: false);
         }
 
+        internal static char ToLowerInvariant(char c)
+        {
+            if (GlobalizationMode.Invariant || UnicodeUtility.IsAsciiCodePoint(c))
+            {
+                return ToLowerAsciiInvariant(c);
+            }
+
+            return Invariant.ChangeCase(c, toUpper: false);
+        }
+
         public string ToLower(string str)
         {
             if (str == null)
@@ -528,11 +538,13 @@ namespace System.Globalization
             }
         }
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private static char ToLowerAsciiInvariant(char c)
         {
-            if ((uint)(c - 'A') <= (uint)('Z' - 'A'))
+            if (UnicodeUtility.IsInRangeInclusive(c, 'A', 'Z'))
             {
-                c = (char)(c | 0x20);
+                // on x86, extending BYTE -> DWORD is more efficient than WORD -> DWORD
+                c = (char)(byte)(c | 0x20);
             }
             return c;
         }
@@ -543,7 +555,7 @@ namespace System.Globalization
         /// </summary>
         public char ToUpper(char c)
         {
-            if (GlobalizationMode.Invariant || (IsAscii(c) && IsAsciiCasingSameAsInvariant))
+            if (GlobalizationMode.Invariant || (UnicodeUtility.IsAsciiCodePoint(c) && IsAsciiCasingSameAsInvariant))
             {
                 return ToUpperAsciiInvariant(c);
             }
@@ -551,6 +563,16 @@ namespace System.Globalization
             return ChangeCase(c, toUpper: true);
         }
 
+        internal static char ToUpperInvariant(char c)
+        {
+            if (GlobalizationMode.Invariant || UnicodeUtility.IsAsciiCodePoint(c))
+            {
+                return ToUpperAsciiInvariant(c);
+            }
+
+            return Invariant.ChangeCase(c, toUpper: true);
+        }
+
         public string ToUpper(string str)
         {
             if (str == null)
@@ -566,17 +588,16 @@ namespace System.Globalization
             return ChangeCaseCommon<ToUpperConversion>(str);
         }
 
-        internal static char ToUpperAsciiInvariant(char c)
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
+        private static char ToUpperAsciiInvariant(char c)
         {
-            if ((uint)(c - 'a') <= (uint)('z' - 'a'))
+            if (UnicodeUtility.IsInRangeInclusive(c, 'a', 'z'))
             {
-                c = (char)(c & ~0x20);
+                c = (char)(c & 0x5F); // = low 7 bits of ~0x20
             }
             return c;
         }
 
-        private static bool IsAscii(char c) => c < 0x80;
-
         private bool IsAsciiCasingSameAsInvariant
         {
             [MethodImpl(MethodImplOptions.AggressiveInlining)]