Avoid some unnecessary static readonly arrays (#72727)
authorStephen Toub <stoub@microsoft.com>
Sun, 24 Jul 2022 22:50:09 +0000 (18:50 -0400)
committerGitHub <noreply@github.com>
Sun, 24 Jul 2022 22:50:09 +0000 (18:50 -0400)
* Avoid some unnecessary static readonly arrays

* Address PR feedback

* Address PR feedback

23 files changed:
src/libraries/Common/src/Interop/Windows/Version/Interop.GetFileVersionInfoEx.cs
src/libraries/Common/src/Interop/Windows/Version/Interop.VerQueryValue.cs
src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Windows.cs
src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/MultiProxy.cs
src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerResponse.cs
src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpListenerRequest.Managed.cs
src/libraries/System.Net.Primitives/src/System/Net/Cookie.cs
src/libraries/System.Private.CoreLib/src/System/Convert.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.cs
src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerableFactory.cs
src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemName.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs
src/libraries/System.Private.CoreLib/src/System/Security/SecurityElement.cs
src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonGlobals.cs
src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs
src/libraries/System.Private.Uri/src/System/DomainNameHelper.cs
src/libraries/System.Private.Uri/src/System/Uri.cs
src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeReader.cs
src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigator.cs
src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X500NameEncoder.cs

index 0b2dd9d..a1f15df 100644 (file)
@@ -10,11 +10,11 @@ internal static partial class Interop
     {
         [LibraryImport(Libraries.Version, EntryPoint = "GetFileVersionInfoExW", StringMarshalling = StringMarshalling.Utf16)]
         [return: MarshalAs(UnmanagedType.Bool)]
-        internal static partial bool GetFileVersionInfoEx(
+        internal static unsafe partial bool GetFileVersionInfoEx(
                     uint dwFlags,
                     string lpwstrFilename,
                     uint dwHandle,
                     uint dwLen,
-                    IntPtr lpData);
+                    void* lpData);
     }
 }
index 4bd3a4a..242b01e 100644 (file)
@@ -10,6 +10,6 @@ internal static partial class Interop
     {
         [LibraryImport(Libraries.Version, EntryPoint = "VerQueryValueW", StringMarshalling = StringMarshalling.Utf16)]
         [return: MarshalAs(UnmanagedType.Bool)]
-        internal static partial bool VerQueryValue(IntPtr pBlock, string lpSubBlock, out IntPtr lplpBuffer, out uint puLen);
+        internal static unsafe partial bool VerQueryValue(void* pBlock, string lpSubBlock, out void* lplpBuffer, out uint puLen);
     }
 }
index fa5234b..8c8eca5 100644 (file)
@@ -12,65 +12,38 @@ namespace System.Diagnostics
         {
             _fileName = fileName;
 
-            uint handle;  // This variable is not used, but we need an out variable.
-            uint infoSize = Interop.Version.GetFileVersionInfoSizeEx(
-                (uint)Interop.Version.FileVersionInfoType.FILE_VER_GET_LOCALISED, _fileName, out handle);
-
+            uint infoSize = Interop.Version.GetFileVersionInfoSizeEx(Interop.Version.FileVersionInfoType.FILE_VER_GET_LOCALISED, _fileName, out _);
             if (infoSize != 0)
             {
-                byte[] mem = new byte[infoSize];
-                fixed (byte* memPtr = &mem[0])
+                void* memPtr = NativeMemory.Alloc(infoSize);
+                try
                 {
-                    IntPtr memIntPtr = new IntPtr((void*)memPtr);
                     if (Interop.Version.GetFileVersionInfoEx(
-                            (uint)Interop.Version.FileVersionInfoType.FILE_VER_GET_LOCALISED | (uint)Interop.Version.FileVersionInfoType.FILE_VER_GET_NEUTRAL,
-                            _fileName,
-                            0U,
-                            infoSize,
-                            memIntPtr))
+                        Interop.Version.FileVersionInfoType.FILE_VER_GET_LOCALISED | Interop.Version.FileVersionInfoType.FILE_VER_GET_NEUTRAL,
+                        _fileName,
+                        0U,
+                        infoSize,
+                        memPtr))
                     {
-                        uint langid = GetVarEntry(memIntPtr);
-                        if (!GetVersionInfoForCodePage(memIntPtr, ConvertTo8DigitHex(langid)))
-                        {
-                            // Some DLLs might not contain correct codepage information. In these cases we will fail during lookup.
-                            // Explorer will take a few shots in dark by trying several specific lang-codepages
-                            // (Explorer also randomly guesses 041D04B0=Swedish+CP_UNICODE and 040704B0=German+CP_UNICODE sometimes).
-                            // We will try to simulate similar behavior here.
-                            foreach (uint id in s_fallbackLanguageCodePages)
-                            {
-                                if (id != langid)
-                                {
-                                    if (GetVersionInfoForCodePage(memIntPtr, ConvertTo8DigitHex(id)))
-                                    {
-                                        break;
-                                    }
-                                }
-                            }
-                        }
+                        // Some dlls might not contain correct codepage information, in which case the lookup will fail. Explorer will take
+                        // a few shots in dark. We'll simulate similar behavior by falling back to the following lang-codepages.
+                        uint lcp = GetLanguageAndCodePage(memPtr);
+                        _ = GetVersionInfoForCodePage(memPtr, lcp.ToString("X8")) ||
+                            (lcp != 0x040904B0 && GetVersionInfoForCodePage(memPtr, "040904B0")) || // US English + CP_UNICODE
+                            (lcp != 0x040904E4 && GetVersionInfoForCodePage(memPtr, "040904E4")) || // US English + CP_USASCII
+                            (lcp != 0x04090000 && GetVersionInfoForCodePage(memPtr, "04090000"));   // US English + unknown codepage
                     }
                 }
+                finally
+                {
+                    NativeMemory.Free(memPtr);
+                }
             }
         }
 
-        // Some dlls might not contain correct codepage information,
-        // in which case the lookup will fail. Explorer will take
-        // a few shots in dark. We'll simulate similar behavior by
-        // falling back to the following lang-codepages:
-        private static readonly uint[] s_fallbackLanguageCodePages = new uint[]
-        {
-            0x040904B0, // US English + CP_UNICODE
-            0x040904E4, // US English + CP_USASCII
-            0x04090000  // US English + unknown codepage
-        };
-
-        private static string ConvertTo8DigitHex(uint value)
-        {
-            return value.ToString("X8");
-        }
-
-        private static unsafe Interop.Version.VS_FIXEDFILEINFO GetFixedFileInfo(IntPtr memPtr)
+        private static unsafe Interop.Version.VS_FIXEDFILEINFO GetFixedFileInfo(void* memPtr)
         {
-            if (Interop.Version.VerQueryValue(memPtr, "\\", out IntPtr memRef, out _))
+            if (Interop.Version.VerQueryValue(memPtr, "\\", out void* memRef, out _))
             {
                 return *(Interop.Version.VS_FIXEDFILEINFO*)memRef;
             }
@@ -78,9 +51,9 @@ namespace System.Diagnostics
             return default;
         }
 
-        private static unsafe string GetFileVersionLanguage(IntPtr memPtr)
+        private static unsafe string GetFileVersionLanguage(void* memPtr)
         {
-            uint langid = GetVarEntry(memPtr) >> 16;
+            uint langid = GetLanguageAndCodePage(memPtr) >> 16;
 
             const int MaxLength = 256;
             char* lang = stackalloc char[MaxLength];
@@ -88,34 +61,34 @@ namespace System.Diagnostics
             return new string(lang, 0, charsWritten);
         }
 
-        private static string GetFileVersionString(IntPtr memPtr, string name)
+        private static unsafe string GetFileVersionString(void* memPtr, string name)
         {
-            if (Interop.Version.VerQueryValue(memPtr, name, out IntPtr memRef, out _))
+            if (Interop.Version.VerQueryValue(memPtr, name, out void* memRef, out _) &&
+                memRef is not null)
             {
-                if (memRef != IntPtr.Zero)
-                {
-                    return Marshal.PtrToStringUni(memRef)!;
-                }
+                return Marshal.PtrToStringUni((IntPtr)memRef)!;
             }
 
             return string.Empty;
         }
 
-        private static uint GetVarEntry(IntPtr memPtr)
+        private static unsafe uint GetLanguageAndCodePage(void* memPtr)
         {
-            if (Interop.Version.VerQueryValue(memPtr, "\\VarFileInfo\\Translation", out IntPtr memRef, out _))
+            if (Interop.Version.VerQueryValue(memPtr, "\\VarFileInfo\\Translation", out void* memRef, out _))
             {
-                return (uint)((Marshal.ReadInt16(memRef) << 16) + Marshal.ReadInt16((IntPtr)((long)memRef + 2)));
+                return
+                    (uint)((*(ushort*)memRef << 16) +
+                    *((ushort*)memRef + 1));
             }
 
-            return 0x040904E4;
+            return 0x040904E4; // US English + CP_USASCII
         }
 
         //
         // This function tries to find version information for a specific codepage.
         // Returns true when version information is found.
         //
-        private bool GetVersionInfoForCodePage(IntPtr memIntPtr, string codepage)
+        private unsafe bool GetVersionInfoForCodePage(void* memIntPtr, string codepage)
         {
             Span<char> stackBuffer = stackalloc char[256];
 
@@ -144,24 +117,18 @@ namespace System.Diagnostics
             _productBuild = (int)HIWORD(ffi.dwProductVersionLS);
             _productPrivate = (int)LOWORD(ffi.dwProductVersionLS);
 
-            _isDebug = (ffi.dwFileFlags & (uint)Interop.Version.FileVersionInfo.VS_FF_DEBUG) != 0;
-            _isPatched = (ffi.dwFileFlags & (uint)Interop.Version.FileVersionInfo.VS_FF_PATCHED) != 0;
-            _isPrivateBuild = (ffi.dwFileFlags & (uint)Interop.Version.FileVersionInfo.VS_FF_PRIVATEBUILD) != 0;
-            _isPreRelease = (ffi.dwFileFlags & (uint)Interop.Version.FileVersionInfo.VS_FF_PRERELEASE) != 0;
-            _isSpecialBuild = (ffi.dwFileFlags & (uint)Interop.Version.FileVersionInfo.VS_FF_SPECIALBUILD) != 0;
+            _isDebug = (ffi.dwFileFlags & Interop.Version.FileVersionInfo.VS_FF_DEBUG) != 0;
+            _isPatched = (ffi.dwFileFlags & Interop.Version.FileVersionInfo.VS_FF_PATCHED) != 0;
+            _isPrivateBuild = (ffi.dwFileFlags & Interop.Version.FileVersionInfo.VS_FF_PRIVATEBUILD) != 0;
+            _isPreRelease = (ffi.dwFileFlags & Interop.Version.FileVersionInfo.VS_FF_PRERELEASE) != 0;
+            _isSpecialBuild = (ffi.dwFileFlags & Interop.Version.FileVersionInfo.VS_FF_SPECIALBUILD) != 0;
 
             // fileVersion is chosen based on best guess. Other fields can be used if appropriate.
             return (_fileVersion != string.Empty);
         }
 
-        private static uint HIWORD(uint dword)
-        {
-            return (dword >> 16) & 0xffff;
-        }
+        private static uint HIWORD(uint dword) => (dword >> 16) & 0xffff;
 
-        private static uint LOWORD(uint dword)
-        {
-            return dword & 0xffff;
-        }
+        private static uint LOWORD(uint dword) => dword & 0xffff;
     }
 }
index 087d104..1413173 100644 (file)
@@ -11,7 +11,6 @@ namespace System.Net.Http
     /// </summary>
     internal struct MultiProxy
     {
-        private static readonly char[] s_proxyDelimiters = { ';', ' ', '\n', '\r', '\t' };
         private readonly FailedProxyCache? _failedProxyCache;
         private readonly Uri[]? _uris;
         private readonly string? _proxyConfig;
@@ -19,8 +18,6 @@ namespace System.Net.Http
         private int _currentIndex;
         private Uri? _currentUri;
 
-        public static MultiProxy Empty => new MultiProxy(null, Array.Empty<Uri>());
-
         private MultiProxy(FailedProxyCache? failedProxyCache, Uri[] uris)
         {
             _failedProxyCache = failedProxyCache;
@@ -41,6 +38,8 @@ namespace System.Net.Http
             _currentUri = null;
         }
 
+        public static MultiProxy Empty => new MultiProxy(null, Array.Empty<Uri>());
+
         /// <summary>
         /// Parses a WinHTTP proxy config into a MultiProxy instance.
         /// </summary>
@@ -198,6 +197,7 @@ namespace System.Net.Http
         {
             const int SECURE_FLAG = 1;
             const int INSECURE_FLAG = 2;
+            const string ProxyDelimiters = "; \n\r\t";
 
             int wantedFlag = secure ? SECURE_FLAG : INSECURE_FLAG;
             int originalLength = proxyString.Length;
@@ -206,7 +206,7 @@ namespace System.Net.Http
             {
                 // Skip any delimiters.
                 int iter = 0;
-                while (iter < proxyString.Length && Array.IndexOf(s_proxyDelimiters, proxyString[iter]) >= 0)
+                while (iter < proxyString.Length && ProxyDelimiters.Contains(proxyString[iter]))
                 {
                     ++iter;
                 }
@@ -245,7 +245,7 @@ namespace System.Net.Http
                 }
 
                 // Find the next delimiter, or end of string.
-                iter = proxyString.IndexOfAny(s_proxyDelimiters);
+                iter = proxyString.IndexOfAny(ProxyDelimiters);
                 if (iter < 0)
                 {
                     iter = proxyString.Length;
index bb52043..40c9e8b 100644 (file)
@@ -79,20 +79,11 @@ namespace System.Net
             set => EntitySendFormat = value ? EntitySendFormat.Chunked : EntitySendFormat.ContentLength;
         }
 
-        // We MUST NOT send message-body when we send responses with these Status codes
-        private static readonly int[] s_noResponseBody = { 100, 101, 204, 205, 304 };
 
-        private static bool CanSendResponseBody(int responseCode)
-        {
-            for (int i = 0; i < s_noResponseBody.Length; i++)
-            {
-                if (responseCode == s_noResponseBody[i])
-                {
-                    return false;
-                }
-            }
-            return true;
-        }
+
+        private static bool CanSendResponseBody(int responseCode) =>
+            // We MUST NOT send message-body when we send responses with these Status codes
+            responseCode is not (100 or 101 or 204 or 205 or 304);
 
         public long ContentLength64
         {
index e42cfb0..448ce0c 100644 (file)
@@ -73,11 +73,9 @@ namespace System.Net
             _version = HttpVersion.Version10;
         }
 
-        private static readonly char[] s_separators = new char[] { ' ' };
-
         internal void SetRequestLine(string req)
         {
-            string[] parts = req.Split(s_separators, 3);
+            string[] parts = req.Split(' ', 3);
             if (parts.Length != 3)
             {
                 _context.ErrorMessage = "Invalid request line (parts).";
index bca1fb1..ff51ae3 100644 (file)
@@ -41,8 +41,7 @@ namespace System.Net
 
         internal static readonly char[] PortSplitDelimiters = new char[] { ' ', ',', '\"' };
         // Space (' ') should be reserved as well per RFCs, but major web browsers support it and some web sites use it - so we support it too
-        internal static readonly char[] ReservedToName = new char[] { '\t', '\r', '\n', '=', ';', ',' };
-        internal static readonly char[] ReservedToValue = new char[] { ';', ',' };
+        internal const string ReservedToName = "\t\r\n=;,";
 
         private string m_comment = string.Empty; // Do not rename (binary serialization)
         private Uri? m_commentUri; // Do not rename (binary serialization)
@@ -239,7 +238,7 @@ namespace System.Net
                 || value.StartsWith('$')
                 || value.StartsWith(' ')
                 || value.EndsWith(' ')
-                || value.IndexOfAny(ReservedToName) >= 0)
+                || value.AsSpan().IndexOfAny(ReservedToName) >= 0)
             {
                 m_name = string.Empty;
                 return false;
@@ -347,7 +346,7 @@ namespace System.Net
                 m_name.StartsWith('$') ||
                 m_name.StartsWith(' ') ||
                 m_name.EndsWith(' ') ||
-                m_name.IndexOfAny(ReservedToName) >= 0)
+                m_name.AsSpan().IndexOfAny(ReservedToName) >= 0)
             {
                 if (shouldThrow)
                 {
@@ -358,7 +357,7 @@ namespace System.Net
 
             // Check the value
             if (m_value == null ||
-                (!(m_value.Length > 2 && m_value.StartsWith('\"') && m_value.EndsWith('\"')) && m_value.IndexOfAny(ReservedToValue) >= 0))
+                (!(m_value.Length > 2 && m_value.StartsWith('\"') && m_value.EndsWith('\"')) && m_value.AsSpan().IndexOfAny(';', ',') >= 0))
             {
                 if (shouldThrow)
                 {
@@ -369,7 +368,7 @@ namespace System.Net
 
             // Check Comment syntax
             if (Comment != null && !(Comment.Length > 2 && Comment.StartsWith('\"') && Comment.EndsWith('\"'))
-                && (Comment.IndexOfAny(ReservedToValue) >= 0))
+                && (Comment.AsSpan().IndexOfAny(';', ',') >= 0))
             {
                 if (shouldThrow)
                 {
@@ -380,7 +379,7 @@ namespace System.Net
 
             // Check Path syntax
             if (Path != null && !(Path.Length > 2 && Path.StartsWith('\"') && Path.EndsWith('\"'))
-                && (Path.IndexOfAny(ReservedToValue) >= 0))
+                && (Path.AsSpan().IndexOfAny(';', ',') != -1))
             {
                 if (shouldThrow)
                 {
index 5a55edf..f217a52 100644 (file)
@@ -101,13 +101,9 @@ namespace System
         // Need to special case Enum because typecode will be underlying type, e.g. Int32
         private static readonly Type EnumType = typeof(Enum);
 
-        internal static readonly char[] base64Table = { 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O',
-                                                        'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd',
-                                                        'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
-                                                        't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7',
-                                                        '8', '9', '+', '/', '=' };
+        internal const string Base64Table = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
 
-        private const int base64LineBreakPosition = 76;
+        private const int Base64LineBreakPosition = 76;
 
 #if DEBUG
         static Convert()
@@ -2473,14 +2469,14 @@ namespace System
             // Convert three bytes at a time to base64 notation.  This will consume 4 chars.
             int i;
 
-            // get a pointer to the base64Table to avoid unnecessary range checking
-            fixed (char* base64 = &base64Table[0])
+            // get a pointer to the Base64Table to avoid unnecessary range checking
+            fixed (char* base64 = Base64Table)
             {
                 for (i = offset; i < calcLength; i += 3)
                 {
                     if (insertLineBreaks)
                     {
-                        if (charcount == base64LineBreakPosition)
+                        if (charcount == Base64LineBreakPosition)
                         {
                             outChars[j++] = '\r';
                             outChars[j++] = '\n';
@@ -2498,7 +2494,7 @@ namespace System
                 // Where we left off before
                 i = calcLength;
 
-                if (insertLineBreaks && (lengthmod3 != 0) && (charcount == base64LineBreakPosition))
+                if (insertLineBreaks && (lengthmod3 != 0) && (charcount == Base64LineBreakPosition))
                 {
                     outChars[j++] = '\r';
                     outChars[j++] = '\n';
@@ -2536,7 +2532,7 @@ namespace System
 
             if (insertLineBreaks)
             {
-                (uint newLines, uint remainder) = Math.DivRem(outlen, base64LineBreakPosition);
+                (uint newLines, uint remainder) = Math.DivRem(outlen, Base64LineBreakPosition);
                 if (remainder == 0)
                 {
                     --newLines;
index bb5917a..2361856 100644 (file)
@@ -120,18 +120,12 @@ namespace System
     */
 
     // This class contains only static members and does not require the serializable attribute.
-    internal static
-    class DateTimeFormat
+    internal static class DateTimeFormat
     {
         internal const int MaxSecondsFractionDigits = 7;
         internal const long NullOffset = long.MinValue;
 
-        internal static char[] allStandardFormats =
-        {
-            'd', 'D', 'f', 'F', 'g', 'G',
-            'm', 'M', 'o', 'O', 'r', 'R',
-            's', 't', 'T', 'u', 'U', 'y', 'Y',
-        };
+        internal const string AllStandardFormats = "dDfFgGmMoOrRstTuUyY";
 
         internal const string RoundtripFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss.fffffffK";
         internal const string RoundtripDateTimeUnfixed = "yyyy'-'MM'-'ddTHH':'mm':'ss zzz";
@@ -1562,12 +1556,11 @@ namespace System
         {
             List<string> results = new List<string>(DEFAULT_ALL_DATETIMES_SIZE);
 
-            for (int i = 0; i < allStandardFormats.Length; i++)
+            foreach (char standardFormat in AllStandardFormats)
             {
-                string[] strings = GetAllDateTimes(dateTime, allStandardFormats[i], dtfi);
-                for (int j = 0; j < strings.Length; j++)
+                foreach (string dateTimes in GetAllDateTimes(dateTime, standardFormat, dtfi))
                 {
-                    results.Add(strings[j]);
+                    results.Add(dateTimes);
                 }
             }
 
index 4a70413..26a1828 100644 (file)
@@ -1266,14 +1266,14 @@ namespace System.Globalization
         {
             List<string> results = new List<string>(DEFAULT_ALL_DATETIMES_SIZE);
 
-            for (int i = 0; i < DateTimeFormat.allStandardFormats.Length; i++)
+            foreach (char standardFormat in DateTimeFormat.AllStandardFormats)
             {
-                string[] strings = GetAllDateTimePatterns(DateTimeFormat.allStandardFormats[i]);
-                for (int j = 0; j < strings.Length; j++)
+                foreach (string pattern in GetAllDateTimePatterns(standardFormat))
                 {
-                    results.Add(strings[j]);
+                    results.Add(pattern);
                 }
             }
+
             return results.ToArray();
         }
 
index 027178c..5e559d1 100644 (file)
@@ -5438,8 +5438,6 @@ new DS[] { DS.ERROR,  DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
             Index + target.Length <= Length &&
             m_info.Compare(Value.Slice(Index, target.Length), target, CompareOptions.IgnoreCase) == 0;
 
-        private static readonly char[] WhiteSpaceChecks = new char[] { ' ', '\u00A0' };
-
         internal bool MatchSpecifiedWords(string target, bool checkWordBoundary, ref int matchLength)
         {
             int valueRemaining = Value.Length - Index;
@@ -5450,12 +5448,14 @@ new DS[] { DS.ERROR,  DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                 // Check word by word
                 int targetPosition = 0;                 // Where we are in the target string
                 int thisPosition = Index;         // Where we are in this string
-                int wsIndex = target.IndexOfAny(WhiteSpaceChecks, targetPosition);
+                int wsIndex = target.AsSpan(targetPosition).IndexOfAny(' ', '\u00A0');
                 if (wsIndex < 0)
                 {
                     return false;
                 }
-                do
+                wsIndex += targetPosition;
+
+                while (true)
                 {
                     int segmentLength = wsIndex - targetPosition;
                     if (thisPosition >= Value.Length - segmentLength)
@@ -5491,7 +5491,15 @@ new DS[] { DS.ERROR,  DS.TX_NNN,  DS.TX_NNN,  DS.TX_NNN,  DS.ERROR,   DS.ERROR,
                         thisPosition++;
                         matchLength++;
                     }
-                } while ((wsIndex = target.IndexOfAny(WhiteSpaceChecks, targetPosition)) >= 0);
+
+                    wsIndex = target.AsSpan(targetPosition).IndexOfAny(' ', '\u00A0');
+                    if (wsIndex < 0)
+                    {
+                        break;
+                    }
+                    wsIndex += targetPosition;
+                }
+
                 // now check the last segment;
                 if (targetPosition < target.Length)
                 {
index be74bb7..80e2c4e 100644 (file)
@@ -183,9 +183,6 @@ namespace System.Globalization
         private const int c_skew = 38;
         private const int c_damp = 700;
 
-        // Legal "dot" separators (i.e: . in www.microsoft.com)
-        private static readonly char[] s_dotSeparators = { '.', '\u3002', '\uFF0E', '\uFF61' };
-
         private string GetAsciiInvariant(string unicode, int index, int count)
         {
             if (index > 0 || count < unicode.Length)
@@ -321,11 +318,12 @@ namespace System.Globalization
             // Find the next dot
             while (iNextDot < unicode.Length)
             {
+                // Legal "dot" separators (i.e: . in www.microsoft.com)
+                const string DotSeparators = ".\u3002\uFF0E\uFF61";
+
                 // Find end of this segment
-                iNextDot = unicode.IndexOfAny(s_dotSeparators, iAfterLastDot);
-                Debug.Assert(iNextDot <= unicode.Length, "[IdnMapping.punycode_encode]IndexOfAny is broken");
-                if (iNextDot < 0)
-                    iNextDot = unicode.Length;
+                iNextDot = unicode.AsSpan(iAfterLastDot).IndexOfAny(DotSeparators);
+                iNextDot = iNextDot < 0 ? unicode.Length : iNextDot + iAfterLastDot;
 
                 // Only allowed to have empty . section at end (www.microsoft.com.)
                 if (iNextDot == iAfterLastDot)
index 33b8c92..0859833 100644 (file)
@@ -9,12 +9,6 @@ namespace System.IO.Enumeration
 {
     internal static class FileSystemEnumerableFactory
     {
-        // These all have special meaning in DOS name matching. '\' is the escaping character (which conveniently
-        // is the directory separator and cannot be part of any path segment in Windows). The other three are the
-        // special case wildcards that we'll convert some * and ? into. They're also valid as filenames on Unix,
-        // which is not true in Windows and as such we'll escape any that occur on the input string.
-        private static readonly char[] s_unixEscapeChars = { '\\', '"', '<', '>' };
-
         /// <summary>
         /// Validates the directory and expression strings to check that they have no invalid characters, any special DOS wildcard characters in Win32 in the expression get replaced with their proper escaped representation, and if the expression string begins with a directory name, the directory name is moved and appended at the end of the directory string.
         /// </summary>
@@ -76,7 +70,11 @@ namespace System.IO.Enumeration
                     }
                     else
                     {
-                        if (Path.DirectorySeparatorChar != '\\' && expression.IndexOfAny(s_unixEscapeChars) >= 0)
+                        // These all have special meaning in DOS name matching. '\' is the escaping character (which conveniently
+                        // is the directory separator and cannot be part of any path segment in Windows). The other three are the
+                        // special case wildcards that we'll convert some * and ? into. They're also valid as filenames on Unix,
+                        // which is not true in Windows and as such we'll escape any that occur on the input string.
+                        if (Path.DirectorySeparatorChar != '\\' && expression.AsSpan().IndexOfAny(@"\""<>") >= 0)
                         {
                             // Backslash isn't the default separator, need to escape (e.g. Unix)
                             expression = expression.Replace("\\", "\\\\");
index 7ac46fe..b958d1e 100644 (file)
@@ -9,18 +9,6 @@ namespace System.IO.Enumeration
     /// <summary>Provides methods for matching file system names.</summary>
     public static class FileSystemName
     {
-        // [MS - FSA] 2.1.4.4 Algorithm for Determining if a FileName Is in an Expression
-        // https://msdn.microsoft.com/en-us/library/ff469270.aspx
-        private static readonly char[] s_wildcardChars =
-        {
-            '\"', '<', '>', '*', '?'
-        };
-
-        private static readonly char[] s_simpleWildcardChars =
-        {
-            '*', '?'
-        };
-
         /// <summary>Translates the given Win32 expression. Change '*' and '?' to '&lt;', '&gt;' and '"' to match Win32 behavior.</summary>
         /// <param name="expression">The expression to translate.</param>
         /// <returns>A string with the translated Win32 expression.</returns>
@@ -161,7 +149,13 @@ namespace System.IO.Enumeration
                     return true;
 
                 ReadOnlySpan<char> expressionEnd = expression.Slice(1);
-                if (expressionEnd.IndexOfAny(useExtendedWildcards ? s_wildcardChars : s_simpleWildcardChars) < 0)
+
+                // [MS - FSA] 2.1.4.4 Algorithm for Determining if a FileName Is in an Expression
+                // https://msdn.microsoft.com/en-us/library/ff469270.aspx
+                bool hasWildcards = (useExtendedWildcards ?
+                    expressionEnd.IndexOfAny("\"<>*?") :
+                    expressionEnd.IndexOfAny('*', '?')) >= 0;
+                if (!hasWildcards)
                 {
                     // Handle the special case of a single starting *, which essentially means "ends with"
 
index e245714..ab6dce3 100644 (file)
@@ -51,7 +51,6 @@ namespace System.Reflection
             ContentType = 32
         }
 
-        private static readonly char[] s_illegalCharactersInSimpleName = { '/', '\\', ':' };
         private ReadOnlySpan<char> _input;
         private int _index;
 
@@ -90,7 +89,7 @@ namespace System.Reflection
             if (token != Token.String)
                 ThrowInvalidAssemblyName();
 
-            if (name == string.Empty || name.IndexOfAny(s_illegalCharactersInSimpleName) != -1)
+            if (string.IsNullOrEmpty(name) || name.AsSpan().IndexOfAny('/', '\\', ':') != -1)
                 ThrowInvalidAssemblyName();
 
             Version? version = null;
index 67f4184..fc993e7 100644 (file)
@@ -18,7 +18,7 @@ namespace System.Security
         private const int AttributesTypical = 4 * 2;  // 4 attributes, times 2 strings per attribute
         private const int ChildrenTypical = 1;
 
-        private static readonly char[] s_escapeChars = new char[] { '<', '>', '\"', '\'', '&' };
+        private const string EscapeChars = "<>\"'&";
         private static readonly string[] s_escapeStringPairs = new string[]
         {
             // these must be all once character escape sequences or a new escaping algorithm is needed
@@ -326,36 +326,16 @@ namespace System.Security
 
             StringBuilder? sb = null;
 
-            int strLen = str.Length;
-            int index; // Pointer into the string that indicates the location of the current '&' character
-            int newIndex = 0; // Pointer into the string that indicates the start index of the "remaining" string (that still needs to be processed).
-
-            while (true)
+            ReadOnlySpan<char> span = str;
+            int pos;
+            while ((pos = span.IndexOfAny(EscapeChars)) >= 0)
             {
-                index = str.IndexOfAny(s_escapeChars, newIndex);
-
-                if (index < 0)
-                {
-                    if (sb == null)
-                        return str;
-                    else
-                    {
-                        sb.Append(str, newIndex, strLen - newIndex);
-                        return sb.ToString();
-                    }
-                }
-                else
-                {
-                    sb ??= new StringBuilder();
-
-                    sb.Append(str, newIndex, index - newIndex);
-                    sb.Append(GetEscapeSequence(str[index]));
-
-                    newIndex = (index + 1);
-                }
+                sb ??= new StringBuilder();
+                sb.Append(span.Slice(0, pos)).Append(GetEscapeSequence(span[pos]));
+                span = span.Slice(pos + 1);
             }
 
-            // no normal exit is possible
+            return sb == null ? str : sb.Append(span).ToString();
         }
 
         private static string GetUnescapeSequence(string str, int index, out int newIndex)
index b4a9966..34c6ea8 100644 (file)
@@ -24,7 +24,6 @@ namespace System.Runtime.Serialization.Json
         public static readonly UTF8Encoding ValidatingUTF8 = new UTF8Encoding(false, true);
         public const string PositiveInf = "INF";
         public const string NegativeInf = "-INF";
-        public static readonly char[] FloatingPointCharacters = new char[] { '.', 'e', 'E' };
         public const string typeString = "type";
         public const string nullString = "null";
         public const string arrayString = "array";
index c6e4945..078c4c5 100644 (file)
@@ -66,7 +66,7 @@ namespace System.Runtime.Serialization.Json
                 throw new XmlException(SR.Format(SR.XmlInvalidConversion, value, Globals.TypeOfInt));
             }
 
-            if (value.IndexOfAny(JsonGlobals.FloatingPointCharacters) == -1)
+            if (value.AsSpan().IndexOfAny('.', 'e', 'E') < 0)
             {
                 int intValue;
                 if (int.TryParse(value, NumberStyles.Float, NumberFormatInfo.InvariantInfo, out intValue))
index 6c93827..c2fb988 100644 (file)
@@ -389,11 +389,7 @@ namespace System
         // This means that a host containing Unicode characters can be normalized to contain
         // URI reserved characters, changing the meaning of a URI only when certain properties
         // such as IdnHost are accessed. To be safe, disallow control characters in normalized hosts.
-        private static readonly char[] s_UnsafeForNormalizedHost = { '\\', '/', '?', '@', '#', ':', '[', ']' };
-
-        internal static bool ContainsCharactersUnsafeForNormalizedHost(string host)
-        {
-            return host.IndexOfAny(s_UnsafeForNormalizedHost) != -1;
-        }
+        internal static bool ContainsCharactersUnsafeForNormalizedHost(string host) =>
+            host.AsSpan().IndexOfAny(@"\/?@#:[]") >= 0;
     }
 }
index 107a025..0fa6975 100644 (file)
@@ -1806,16 +1806,12 @@ namespace System
         //
         // Returns true if a colon is found in the first path segment, false otherwise
         //
-
-        // Check for anything that may terminate the first regular path segment
-        // or an illegal colon
-        private static readonly char[] s_pathDelims = { ':', '\\', '/', '?', '#' };
-
         private static bool CheckForColonInFirstPathSegment(string uriString)
         {
-            int index = uriString.IndexOfAny(s_pathDelims);
-
-            return (index >= 0 && uriString[index] == ':');
+            // Check for anything that may terminate the first regular path segment
+            // or an illegal colon
+            int index = uriString.AsSpan().IndexOfAny(@":\/?#");
+            return (uint)index < (uint)uriString.Length && uriString[index] == ':';
         }
 
         internal static string InternalEscapeString(string rawString) =>
index 930dd70..3f31cd0 100644 (file)
@@ -7,8 +7,6 @@ namespace System.Xml.Linq
 {
     internal sealed class XNodeReader : XmlReader, IXmlLineInfo
     {
-        private static readonly char[] s_WhitespaceChars = new char[] { ' ', '\t', '\n', '\r' };
-
         // The reader position is encoded by the tuple (source, parent).
         // Lazy text uses (instance, parent element). Attribute value
         // uses (instance, parent attribute). End element uses (instance,
@@ -430,7 +428,7 @@ namespace System.Xml.Linq
                         XAttribute? a = e.Attribute(name);
                         if (a != null)
                         {
-                            switch (a.Value.Trim(s_WhitespaceChars))
+                            switch (a.Value.AsSpan().Trim(" \t\n\r"))
                             {
                                 case "preserve":
                                     return XmlSpace.Preserve;
index b0cac8d..80fa5d2 100644 (file)
@@ -1730,25 +1730,19 @@ namespace System.Xml.XPath
             }
         }
 
-        internal static readonly char[] NodeTypeLetter = new char[] {
-            'R',    // Root
-            'E',    // Element
-            'A',    // Attribute
-            'N',    // Namespace
-            'T',    // Text
-            'S',    // SignificantWhitespace
-            'W',    // Whitespace
-            'P',    // ProcessingInstruction
-            'C',    // Comment
-            'X',    // All
-        };
-
-        internal static readonly char[] UniqueIdTbl = new char[] {
-            'A',  'B',  'C',  'D',  'E',  'F',  'G',  'H',  'I',  'J',
-            'K',  'L',  'M',  'N',  'O',  'P',  'Q',  'R',  'S',  'T',
-            'U',  'V',  'W',  'X',  'Y',  'Z',  '1',  '2',  '3',  '4',
-            '5',  '6'
-        };
+        // (R)oot
+        // (E)lement
+        // (A)ttribute
+        // (N)amespace
+        // (T)ext
+        // (S)ignificantWhitespace
+        // (W)hitespace
+        // (P)rocessingInstruction
+        // (C)omment
+        // (X) All
+        internal const string NodeTypeLetter = "REANTSWPCX";
+
+        internal const string UniqueIdTbl = "ABCDEFGHIJKLMNOPQRSTUVWXYZ123456";
 
         // Requirements for id:
         //  1. must consist of alphanumeric characters only
@@ -1776,14 +1770,14 @@ namespace System.Xml.XPath
                     }
                     if (idx <= 0x1f)
                     {
-                        sb.Append(UniqueIdTbl[idx]);
+                        sb.Append(UniqueIdTbl[(int)idx]);
                     }
                     else
                     {
                         sb.Append('0');
                         do
                         {
-                            sb.Append(UniqueIdTbl[idx & 0x1f]);
+                            sb.Append(UniqueIdTbl[(int)(idx & 0x1f)]);
                             idx >>= 5;
                         } while (idx != 0);
                         sb.Append('0');
index 7dc8d03..d8a3be4 100644 (file)
@@ -12,20 +12,6 @@ namespace System.Security.Cryptography.X509Certificates
     {
         private const string OidTagPrefix = "OID.";
 
-        private static readonly char[] s_quoteNeedingChars =
-        {
-            ',',
-            '+',
-            '=',
-            '\"',
-            '\n',
-            // \r is NOT in this list, because it isn't in Windows.
-            '<',
-            '>',
-            '#',
-            ';',
-        };
-
         private static readonly List<char> s_useSemicolonSeparators = new List<char>(1) { ';' };
         private static readonly List<char> s_useCommaSeparators = new List<char>(1) { ',' };
         private static readonly List<char> s_useNewlineSeparators = new List<char>(2) { '\r', '\n' };
@@ -132,22 +118,17 @@ namespace System.Security.Cryptography.X509Certificates
             return writer.Encode();
         }
 
-        private static bool NeedsQuoting(string rdnValue)
+        private static bool NeedsQuoting(ReadOnlySpan<char> rdnValue)
         {
-            if (string.IsNullOrEmpty(rdnValue))
+            if (rdnValue.IsEmpty ||
+                IsQuotableWhitespace(rdnValue[0]) ||
+                IsQuotableWhitespace(rdnValue[^1]))
             {
                 return true;
             }
 
-            if (IsQuotableWhitespace(rdnValue[0]) ||
-                IsQuotableWhitespace(rdnValue[rdnValue.Length - 1]))
-            {
-                return true;
-            }
-
-            int index = rdnValue.IndexOfAny(s_quoteNeedingChars);
-
-            return index != -1;
+            const string QuoteNeedingChars = ",+=\"\n<>#;"; // \r is NOT in this list, because it isn't in Windows.
+            return rdnValue.IndexOfAny(QuoteNeedingChars) >= 0;
         }
 
         private static bool IsQuotableWhitespace(char c)