diff --git a/src/libraries/Common/src/Interop/Windows/Version/Interop.GetFileVersionInfoEx.cs b/src/libraries/Common/src/Interop/Windows/Version/Interop.GetFileVersionInfoEx.cs index 0b2dd9d6cdbf6..a1f15df3e8f81 100644 --- a/src/libraries/Common/src/Interop/Windows/Version/Interop.GetFileVersionInfoEx.cs +++ b/src/libraries/Common/src/Interop/Windows/Version/Interop.GetFileVersionInfoEx.cs @@ -10,11 +10,11 @@ internal static partial class Version { [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); } } diff --git a/src/libraries/Common/src/Interop/Windows/Version/Interop.VerQueryValue.cs b/src/libraries/Common/src/Interop/Windows/Version/Interop.VerQueryValue.cs index 4bd3a4a9fcbf2..242b01e52d575 100644 --- a/src/libraries/Common/src/Interop/Windows/Version/Interop.VerQueryValue.cs +++ b/src/libraries/Common/src/Interop/Windows/Version/Interop.VerQueryValue.cs @@ -10,6 +10,6 @@ internal static partial class Version { [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); } } diff --git a/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Windows.cs b/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Windows.cs index fa5234bde62de..8c8eca5907102 100644 --- a/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Windows.cs +++ b/src/libraries/System.Diagnostics.FileVersionInfo/src/System/Diagnostics/FileVersionInfo.Windows.cs @@ -12,65 +12,38 @@ private unsafe FileVersionInfo(string fileName) { _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 @@ private static unsafe Interop.Version.VS_FIXEDFILEINFO GetFixedFileInfo(IntPtr m 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 @@ private static unsafe string GetFileVersionLanguage(IntPtr memPtr) 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 stackBuffer = stackalloc char[256]; @@ -144,24 +117,18 @@ private bool GetVersionInfoForCodePage(IntPtr memIntPtr, string codepage) _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; } } diff --git a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/MultiProxy.cs b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/MultiProxy.cs index 087d104153b10..141317329c035 100644 --- a/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/MultiProxy.cs +++ b/src/libraries/System.Net.Http/src/System/Net/Http/SocketsHttpHandler/MultiProxy.cs @@ -11,7 +11,6 @@ namespace System.Net.Http /// 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 @@ internal struct MultiProxy private int _currentIndex; private Uri? _currentUri; - public static MultiProxy Empty => new MultiProxy(null, Array.Empty()); - private MultiProxy(FailedProxyCache? failedProxyCache, Uri[] uris) { _failedProxyCache = failedProxyCache; @@ -41,6 +38,8 @@ private MultiProxy(FailedProxyCache failedProxyCache, string proxyConfig, bool s _currentUri = null; } + public static MultiProxy Empty => new MultiProxy(null, Array.Empty()); + /// /// Parses a WinHTTP proxy config into a MultiProxy instance. /// @@ -198,6 +197,7 @@ private static bool TryParseProxyConfigPart(ReadOnlySpan proxyString, bool { 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 @@ private static bool TryParseProxyConfigPart(ReadOnlySpan proxyString, bool { // 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 @@ private static bool TryParseProxyConfigPart(ReadOnlySpan proxyString, bool } // Find the next delimiter, or end of string. - iter = proxyString.IndexOfAny(s_proxyDelimiters); + iter = proxyString.IndexOfAny(ProxyDelimiters); if (iter < 0) { iter = proxyString.Length; diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerResponse.cs b/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerResponse.cs index bb52043594685..40c9e8b71a7df 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerResponse.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerResponse.cs @@ -79,20 +79,11 @@ public bool SendChunked 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 { diff --git a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpListenerRequest.Managed.cs b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpListenerRequest.Managed.cs index e42cfb07a05f6..448ce0c767f42 100644 --- a/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpListenerRequest.Managed.cs +++ b/src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpListenerRequest.Managed.cs @@ -73,11 +73,9 @@ internal HttpListenerRequest(HttpListenerContext context) _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)."; diff --git a/src/libraries/System.Net.Primitives/src/System/Net/Cookie.cs b/src/libraries/System.Net.Primitives/src/System/Net/Cookie.cs index bca1fb1341c71..ff51ae38c7348 100644 --- a/src/libraries/System.Net.Primitives/src/System/Net/Cookie.cs +++ b/src/libraries/System.Net.Primitives/src/System/Net/Cookie.cs @@ -41,8 +41,7 @@ public sealed class Cookie 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 @@ internal bool InternalSetName(string? value) || 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 @@ internal bool VerifySetDefaults(CookieVariant variant, Uri uri, bool isLocalDoma 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 @@ internal bool VerifySetDefaults(CookieVariant variant, Uri uri, bool isLocalDoma // 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 @@ internal bool VerifySetDefaults(CookieVariant variant, Uri uri, bool isLocalDoma // 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 @@ internal bool VerifySetDefaults(CookieVariant variant, Uri uri, bool isLocalDoma // Check Path syntax if (Path != null && !(Path.Length > 2 && Path.StartsWith('\"') && Path.EndsWith('\"')) - && (Path.IndexOfAny(ReservedToValue) >= 0)) + && (Path.AsSpan().IndexOfAny(';', ',') != -1)) { if (shouldThrow) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Convert.cs b/src/libraries/System.Private.CoreLib/src/System/Convert.cs index 5a55edfba1a6e..f217a5279eb17 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Convert.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Convert.cs @@ -101,13 +101,9 @@ public static partial class Convert // 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 @@ private static unsafe int ConvertToBase64Array(char* outChars, byte* inData, int // 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 @@ private static unsafe int ConvertToBase64Array(char* outChars, byte* inData, int // 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 @@ private static int ToBase64_CalculateAndValidateOutputLength(int inputLength, bo if (insertLineBreaks) { - (uint newLines, uint remainder) = Math.DivRem(outlen, base64LineBreakPosition); + (uint newLines, uint remainder) = Math.DivRem(outlen, Base64LineBreakPosition); if (remainder == 0) { --newLines; diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs index bb5917a06fd13..23618566bb8f4 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormat.cs @@ -120,18 +120,12 @@ based on ISO 8601. */ // 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 @@ internal static string[] GetAllDateTimes(DateTime dateTime, DateTimeFormatInfo d { List results = new List(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); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs index 4a70413b14607..26a182843ac8b 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfo.cs @@ -1266,14 +1266,14 @@ public string[] GetAllDateTimePatterns() { List results = new List(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(); } diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs index 027178c149dba..5e559d1c98f1f 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeParse.cs @@ -5438,8 +5438,6 @@ internal bool MatchSpecifiedWord(string target) => 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 @@ internal bool MatchSpecifiedWords(string target, bool checkWordBoundary, ref int // 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 @@ internal bool MatchSpecifiedWords(string target, bool checkWordBoundary, ref int 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) { diff --git a/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.cs b/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.cs index be74bb7a1dfdc..80e2c4e73bdae 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Globalization/IdnMapping.cs @@ -183,9 +183,6 @@ private static unsafe string GetStringForOutput(string originalString, char* inp 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 @@ private static string PunycodeEncode(string unicode) // 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) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerableFactory.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerableFactory.cs index 33b8c92a32ff3..085983305bea9 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerableFactory.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemEnumerableFactory.cs @@ -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 = { '\\', '"', '<', '>' }; - /// /// 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. /// @@ -76,7 +70,11 @@ internal static bool NormalizeInputs(ref string directory, ref string expression } 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("\\", "\\\\"); diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemName.cs b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemName.cs index 7ac46feb9ab9d..b958d1e45f7d6 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemName.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/Enumeration/FileSystemName.cs @@ -9,18 +9,6 @@ namespace System.IO.Enumeration /// Provides methods for matching file system names. 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 = - { - '*', '?' - }; - /// Translates the given Win32 expression. Change '*' and '?' to '<', '>' and '"' to match Win32 behavior. /// The expression to translate. /// A string with the translated Win32 expression. @@ -161,7 +149,13 @@ private static bool MatchPattern(ReadOnlySpan expression, ReadOnlySpan 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" diff --git a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs index e245714e12017..ab6dce32c2b95 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Reflection/AssemblyNameParser.cs @@ -51,7 +51,6 @@ private enum AttributeKind ContentType = 32 } - private static readonly char[] s_illegalCharactersInSimpleName = { '/', '\\', ':' }; private ReadOnlySpan _input; private int _index; @@ -90,7 +89,7 @@ private AssemblyNameParts Parse() 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; diff --git a/src/libraries/System.Private.CoreLib/src/System/Security/SecurityElement.cs b/src/libraries/System.Private.CoreLib/src/System/Security/SecurityElement.cs index 67f4184a75724..fc993e7058e88 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Security/SecurityElement.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Security/SecurityElement.cs @@ -18,7 +18,7 @@ public sealed class SecurityElement 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 @@ private static string GetEscapeSequence(char c) 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 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) diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonGlobals.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonGlobals.cs index b4a99668d1357..34c6ea80c4772 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonGlobals.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonGlobals.cs @@ -24,7 +24,6 @@ internal static class JsonGlobals 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"; diff --git a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs index c6e4945072ef8..078c4c5179339 100644 --- a/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs +++ b/src/libraries/System.Private.DataContractSerialization/src/System/Runtime/Serialization/Json/JsonObjectDataContract.cs @@ -66,7 +66,7 @@ internal static object ParseJsonNumber(string value, out TypeCode objectTypeCode 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)) diff --git a/src/libraries/System.Private.Uri/src/System/DomainNameHelper.cs b/src/libraries/System.Private.Uri/src/System/DomainNameHelper.cs index 6c93827e75fbb..c2fb988909393 100644 --- a/src/libraries/System.Private.Uri/src/System/DomainNameHelper.cs +++ b/src/libraries/System.Private.Uri/src/System/DomainNameHelper.cs @@ -389,11 +389,7 @@ private static bool IsValidDomainLabelCharacter(char character, ref bool notCano // 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; } } diff --git a/src/libraries/System.Private.Uri/src/System/Uri.cs b/src/libraries/System.Private.Uri/src/System/Uri.cs index 107a0256b641e..0fa6975f04539 100644 --- a/src/libraries/System.Private.Uri/src/System/Uri.cs +++ b/src/libraries/System.Private.Uri/src/System/Uri.cs @@ -1806,16 +1806,12 @@ public Uri MakeRelativeUri(Uri uri) // // 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) => diff --git a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeReader.cs b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeReader.cs index 930dd7092a2f5..3f31cd085e927 100644 --- a/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeReader.cs +++ b/src/libraries/System.Private.Xml.Linq/src/System/Xml/Linq/XNodeReader.cs @@ -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 @@ public override XmlSpace XmlSpace 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; diff --git a/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigator.cs b/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigator.cs index b0cac8d88790e..80fa5d24688fe 100644 --- a/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigator.cs +++ b/src/libraries/System.Private.Xml/src/System/Xml/XPath/XPathNavigator.cs @@ -1730,25 +1730,19 @@ internal uint IndexInParent } } - 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 @@ internal virtual string UniqueId } 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'); diff --git a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X500NameEncoder.cs b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X500NameEncoder.cs index 7dc8d039bef9c..d8a3be4164490 100644 --- a/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X500NameEncoder.cs +++ b/src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/X500NameEncoder.cs @@ -12,20 +12,6 @@ internal static partial class X500NameEncoder { 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 s_useSemicolonSeparators = new List(1) { ';' }; private static readonly List s_useCommaSeparators = new List(1) { ',' }; private static readonly List s_useNewlineSeparators = new List(2) { '\r', '\n' }; @@ -132,22 +118,17 @@ internal static byte[] X500DistinguishedNameEncode( return writer.Encode(); } - private static bool NeedsQuoting(string rdnValue) + private static bool NeedsQuoting(ReadOnlySpan 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)