Optimize string.EndsWith(char) for const values (#69038)
authorLevi Broderick <GrabYourPitchforks@users.noreply.github.com>
Fri, 20 May 2022 05:21:16 +0000 (22:21 -0700)
committerGitHub <noreply@github.com>
Fri, 20 May 2022 05:21:16 +0000 (22:21 -0700)
32 files changed:
src/coreclr/System.Private.CoreLib/src/System/RuntimeType.CoreCLR.cs
src/libraries/Common/src/System/Net/CookieParser.cs
src/libraries/System.IO.Pipes/src/System/IO/Pipes/PipeStream.Unix.cs
src/libraries/System.Net.Http/src/System/Net/Http/Headers/CacheControlHeaderValue.cs
src/libraries/System.Net.Http/src/System/Net/Http/Headers/NameValueHeaderValue.cs
src/libraries/System.Net.Http/src/System/Net/Http/HttpContent.cs
src/libraries/System.Net.HttpListener/src/System/Net/HttpListener.cs
src/libraries/System.Net.HttpListener/src/System/Net/HttpListenerRequest.cs
src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpEndPointListener.cs
src/libraries/System.Net.Mail/src/System/Net/Mail/Attachment.cs
src/libraries/System.Net.Mail/src/System/Net/Mail/MailAddress.cs
src/libraries/System.Net.Primitives/src/System/Net/Cookie.cs
src/libraries/System.Net.Primitives/src/System/Net/CookieContainer.cs
src/libraries/System.Net.Requests/src/System/Net/FtpControlStream.cs
src/libraries/System.Private.CoreLib/src/System/Environment.GetFolderPathCore.Unix.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.Nls.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/CultureData.cs
src/libraries/System.Private.CoreLib/src/System/Globalization/DateTimeFormatInfoScanner.cs
src/libraries/System.Private.CoreLib/src/System/Reflection/Module.cs
src/libraries/System.Private.CoreLib/src/System/String.Comparison.cs
src/libraries/System.Private.DataContractSerialization/src/System/Xml/XmlBaseWriter.cs
src/libraries/System.Private.Uri/src/System/Uri.cs
src/libraries/System.Private.Xml/src/System/Xml/Core/XmlTextWriter.cs
src/libraries/System.Private.Xml/src/System/Xml/Core/XmlWellFormedWriter.cs
src/libraries/System.Private.Xml/src/System/Xml/Dom/XmlEntityReference.cs
src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReader.cs
src/libraries/System.Private.Xml/src/System/Xml/Serialization/XmlSerializationReaderILGen.cs
src/libraries/System.Private.Xml/src/System/Xml/ValidateNames.cs
src/libraries/System.Private.Xml/src/System/Xml/XmlConvert.cs
src/libraries/System.Security.Cryptography/src/System/Security/Cryptography/X509Certificates/OpenSslX509ChainProcessor.cs
src/libraries/System.Web.HttpUtility/src/System/Web/HttpUtility.cs
src/mono/wasm/debugger/DebuggerTestSuite/Inspector.cs

index ed4c563..9b708c4 100644 (file)
@@ -2079,7 +2079,7 @@ namespace System
                     listType = MemberListType.CaseSensitive;
                 }
 
-                if (allowPrefixLookup && name.EndsWith("*", StringComparison.Ordinal))
+                if (allowPrefixLookup && name.EndsWith('*'))
                 {
                     // We set prefixLookup to true if name ends with a "*".
                     // We will also set listType to All so that all members are included in
index e50a374..1e0ee27 100644 (file)
@@ -855,10 +855,9 @@ namespace System.Net
 
         internal static string CheckQuoted(string value)
         {
-            if (value.Length < 2 || value[0] != '\"' || value[value.Length - 1] != '\"')
-                return value;
-
-            return value.Length == 2 ? string.Empty : value.Substring(1, value.Length - 2);
+            return (value.Length >= 2 && value.StartsWith('\"') && value.EndsWith('\"'))
+                ? value.Substring(1, value.Length - 2)
+                : value;
         }
 
         internal bool EndofHeader()
index cefced3..165c978 100644 (file)
@@ -202,7 +202,7 @@ namespace System.IO.Pipes
             // cross-platform with Windows (which has only '\' as an invalid char).
             if (Path.IsPathRooted(pipeName))
             {
-                if (pipeName.IndexOfAny(s_invalidPathNameChars) >= 0 || pipeName[pipeName.Length - 1] == Path.DirectorySeparatorChar)
+                if (pipeName.IndexOfAny(s_invalidPathNameChars) >= 0 || pipeName.EndsWith(Path.DirectorySeparatorChar))
                     throw new PlatformNotSupportedException(SR.PlatformNotSupported_InvalidPipeNameChars);
 
                 // Caller is in full control of file location.
index 8eacd11..a40b6b9 100644 (file)
@@ -537,7 +537,7 @@ namespace System.Net.Http.Headers
             // We need the string to be at least 3 chars long: 2x quotes and at least 1 character. Also make sure we
             // have a quoted string. Note that NameValueHeaderValue will never have leading/trailing whitespace.
             string valueString = nameValue.Value;
-            if ((valueString.Length < 3) || (valueString[0] != '\"') || (valueString[valueString.Length - 1] != '\"'))
+            if ((valueString.Length < 3) || !valueString.StartsWith('\"') || !valueString.EndsWith('\"'))
             {
                 return false;
             }
index 9ddbd0d..9084597 100644 (file)
@@ -358,7 +358,7 @@ namespace System.Net.Http.Headers
             }
 
             // Trailing/leading space are not allowed
-            if (value[0] == ' ' || value[0] == '\t' || value[^1] == ' ' || value[^1] == '\t')
+            if (value.StartsWith(' ') || value.StartsWith('\t') || value.EndsWith(' ') || value.EndsWith('\t'))
             {
                 ThrowFormatException(value);
             }
index 9b1b944..8e508e8 100644 (file)
@@ -192,8 +192,8 @@ namespace System.Net.Http
                 {
                     // Remove at most a single set of quotes.
                     if (charset.Length > 2 &&
-                        charset[0] == '\"' &&
-                        charset[charset.Length - 1] == '\"')
+                        charset.StartsWith('\"') &&
+                        charset.EndsWith('\"'))
                     {
                         encoding = Encoding.GetEncoding(charset.Substring(1, charset.Length - 2));
                     }
index 4cb2679..a8f2a68 100644 (file)
@@ -146,7 +146,7 @@ namespace System.Net
                 {
                     throw new ArgumentException(SR.net_listener_host, nameof(uriPrefix));
                 }
-                if (uriPrefix[uriPrefix.Length - 1] != '/')
+                if (!uriPrefix.EndsWith('/'))
                 {
                     throw new ArgumentException(SR.net_listener_slash, nameof(uriPrefix));
                 }
index 5bfecb4..b44dc6d 100644 (file)
@@ -537,8 +537,8 @@ namespace System.Net
 
             internal static void FillFromString(NameValueCollection nvc, string s, bool urlencoded, Encoding encoding)
             {
+                int i = s.StartsWith('?') ? 1 : 0;
                 int l = s.Length;
-                int i = (l > 0 && s[0] == '?') ? 1 : 0;
 
                 while (i < l)
                 {
index 351ad0c..6753f34 100644 (file)
@@ -178,7 +178,7 @@ namespace System.Net
             string host = uri.Host;
             int port = uri.Port;
             string path = WebUtility.UrlDecode(uri.AbsolutePath);
-            string pathSlash = path[path.Length - 1] == '/' ? path : path + "/";
+            string pathSlash = path.EndsWith('/') ? path : path + "/";
 
             HttpListener? bestMatch = null;
             int bestLength = -1;
index aa8430f..ab7c709 100644 (file)
@@ -204,7 +204,7 @@ namespace System.Net.Mail
                     ContentId = cid;
                     return cid;
                 }
-                if (cid.Length >= 2 && cid[0] == '<' && cid[cid.Length - 1] == '>')
+                if (cid.StartsWith('<') && cid.EndsWith('>'))
                 {
                     return cid.Substring(1, cid.Length - 2);
                 }
index beb6d52..2af110a 100644 (file)
@@ -141,9 +141,9 @@ namespace System.Net.Mail
                     return false;
                 }
 
-                if (displayName.Length >= 2 && displayName[0] == '\"' && displayName[^1] == '\"')
+                if (displayName.Length >= 2 && displayName.StartsWith('\"') && displayName.EndsWith('\"'))
                 {
-                    // Peal bounding quotes, they'll get re-added later.
+                    // Peel bounding quotes, they'll get re-added later.
                     displayName = displayName.Substring(1, displayName.Length - 2);
                 }
             }
index 01e82b6..ece5a17 100644 (file)
@@ -235,7 +235,11 @@ namespace System.Net
         }
         internal bool InternalSetName(string? value)
         {
-            if (string.IsNullOrEmpty(value) || value[0] == '$' || value.IndexOfAny(ReservedToName) != -1 || value[0] == ' ' || value[value.Length - 1] == ' ')
+            if (string.IsNullOrEmpty(value)
+                || value.StartsWith('$')
+                || value.StartsWith(' ')
+                || value.EndsWith(' ')
+                || value.IndexOfAny(ReservedToName) >= 0)
             {
                 m_name = string.Empty;
                 return false;
@@ -339,7 +343,11 @@ namespace System.Net
             }
 
             // Check the name
-            if (string.IsNullOrEmpty(m_name) || m_name[0] == '$' || m_name.IndexOfAny(ReservedToName) != -1 || m_name[0] == ' ' || m_name[m_name.Length - 1] == ' ')
+            if (string.IsNullOrEmpty(m_name) ||
+                m_name.StartsWith('$') ||
+                m_name.StartsWith(' ') ||
+                m_name.EndsWith(' ') ||
+                m_name.IndexOfAny(ReservedToName) >= 0)
             {
                 if (shouldThrow)
                 {
@@ -350,7 +358,7 @@ namespace System.Net
 
             // Check the value
             if (m_value == null ||
-                (!(m_value.Length > 2 && m_value[0] == '\"' && m_value[m_value.Length - 1] == '\"') && m_value.IndexOfAny(ReservedToValue) != -1))
+                (!(m_value.Length > 2 && m_value.StartsWith('\"') && m_value.EndsWith('\"')) && m_value.IndexOfAny(ReservedToValue) >= 0))
             {
                 if (shouldThrow)
                 {
@@ -360,8 +368,8 @@ namespace System.Net
             }
 
             // Check Comment syntax
-            if (Comment != null && !(Comment.Length > 2 && Comment[0] == '\"' && Comment[Comment.Length - 1] == '\"')
-                && (Comment.IndexOfAny(ReservedToValue) != -1))
+            if (Comment != null && !(Comment.Length > 2 && Comment.StartsWith('\"') && Comment.EndsWith('\"'))
+                && (Comment.IndexOfAny(ReservedToValue) >= 0))
             {
                 if (shouldThrow)
                 {
@@ -371,8 +379,8 @@ namespace System.Net
             }
 
             // Check Path syntax
-            if (Path != null && !(Path.Length > 2 && Path[0] == '\"' && Path[Path.Length - 1] == '\"')
-                && (Path.IndexOfAny(ReservedToValue) != -1))
+            if (Path != null && !(Path.Length > 2 && Path.StartsWith('\"') && Path.EndsWith('\"'))
+                && (Path.IndexOfAny(ReservedToValue) >= 0))
             {
                 if (shouldThrow)
                 {
@@ -498,7 +506,7 @@ namespace System.Net
                         // Note: Normally Uri.AbsolutePath contains at least one "/" after parsing,
                         //       but it's possible construct Uri with an empty path using a custom UriParser
                         int lastSlash;
-                        if (path.Length == 0 || path[0] != '/' || (lastSlash = path.LastIndexOf('/')) == 0)
+                        if (!path.StartsWith('/') || (lastSlash = path.LastIndexOf('/')) == 0)
                         {
                             m_path = "/";
                             break;
@@ -587,35 +595,29 @@ namespace System.Net
                 else
                 {
                     // Parse port list
-                    if (value[0] != '\"' || value[value.Length - 1] != '\"')
+                    if (!value.StartsWith('\"') || !value.EndsWith('\"'))
                     {
                         throw new CookieException(SR.Format(SR.net_cookie_attribute, CookieFields.PortAttributeName, value));
                     }
-                    string[] ports = value.Split(PortSplitDelimiters);
-
-                    List<int> portList = new List<int>();
-                    int port;
+                    string[] ports = value.Split(PortSplitDelimiters, StringSplitOptions.RemoveEmptyEntries);
+                    int[] parsedPorts = new int[ports.Length];
 
                     for (int i = 0; i < ports.Length; ++i)
                     {
-                        // Skip spaces
-                        if (ports[i] != string.Empty)
+                        if (!int.TryParse(ports[i], out int port))
                         {
-                            if (!int.TryParse(ports[i], out port))
-                            {
-                                throw new CookieException(SR.Format(SR.net_cookie_attribute, CookieFields.PortAttributeName, value));
-                            }
-
-                            // valid values for port 0 - 0xFFFF
-                            if ((port < 0) || (port > 0xFFFF))
-                            {
-                                throw new CookieException(SR.Format(SR.net_cookie_attribute, CookieFields.PortAttributeName, value));
-                            }
+                            throw new CookieException(SR.Format(SR.net_cookie_attribute, CookieFields.PortAttributeName, value));
+                        }
 
-                            portList.Add(port);
+                        // valid values for port 0 - 0xFFFF
+                        if ((port < 0) || (port > 0xFFFF))
+                        {
+                            throw new CookieException(SR.Format(SR.net_cookie_attribute, CookieFields.PortAttributeName, value));
                         }
+
+                        parsedPorts[i] = port;
                     }
-                    m_port_list = portList.ToArray();
+                    m_port_list = parsedPorts;
                     m_port = value;
                     m_version = MaxSupportedVersion;
                     m_cookieVariant = CookieVariant.Rfc2965;
index 3825092..86187da 100644 (file)
@@ -927,7 +927,7 @@ namespace System.Net
             if (!requestPath.StartsWith(cookiePath, StringComparison.Ordinal))
                 return false;
             return requestPath.Length == cookiePath.Length ||
-                   cookiePath.Length > 0 && cookiePath[^1] == '/' ||
+                   cookiePath.EndsWith('/') ||
                    requestPath[cookiePath.Length] == '/';
         }
 
index 5ff4ba7..a093ef4 100644 (file)
@@ -602,8 +602,7 @@ namespace System.Net
                 commandList.Add(new PipelineEntry(FormatFtpCommand("RNFR", baseDir + requestFilename), flags));
 
                 string renameTo;
-                if (!string.IsNullOrEmpty(request.RenameTo)
-                    && request.RenameTo.StartsWith("/", StringComparison.OrdinalIgnoreCase))
+                if (request.RenameTo is not null && request.RenameTo.StartsWith('/'))
                 {
                     renameTo = request.RenameTo; // Absolute path
                 }
@@ -774,7 +773,7 @@ namespace System.Net
             }
 
             // strip off trailing '/' on directory if present
-            if (directory.Length > 1 && directory[directory.Length - 1] == '/')
+            if (directory.Length > 1 && directory.EndsWith('/'))
                 directory = directory.Substring(0, directory.Length - 1);
         }
 
@@ -954,11 +953,11 @@ namespace System.Net
             escapedFilename = escapedFilename.Replace("#", "%23");
 
             // help us out if the user forgot to add a slash to the directory name
-            string orginalPath = baseUri.AbsolutePath;
-            if (orginalPath.Length > 0 && orginalPath[orginalPath.Length - 1] != '/')
+            string originalPath = baseUri.AbsolutePath;
+            if (originalPath.Length > 0 && !originalPath.EndsWith('/'))
             {
                 UriBuilder uriBuilder = new UriBuilder(baseUri);
-                uriBuilder.Path = orginalPath + "/";
+                uriBuilder.Path = originalPath + "/";
                 baseUri = uriBuilder.Uri;
             }
 
index 27dbc32..7990feb 100644 (file)
@@ -93,7 +93,7 @@ namespace System
                     // "$XDG_DATA_HOME defines the base directory relative to which user specific data files should be stored."
                     // "If $XDG_DATA_HOME is either not set or empty, a default equal to $HOME/.local/share should be used."
                     string? data = GetEnvironmentVariable("XDG_DATA_HOME");
-                    if (string.IsNullOrEmpty(data) || data[0] != '/')
+                    if (data is null || !data.StartsWith('/'))
                     {
                         data = Path.Combine(home, ".local", "share");
                     }
@@ -137,7 +137,7 @@ namespace System
             // "$XDG_CONFIG_HOME defines the base directory relative to which user specific configuration files should be stored."
             // "If $XDG_CONFIG_HOME is either not set or empty, a default equal to $HOME/.config should be used."
             string? config = GetEnvironmentVariable("XDG_CONFIG_HOME");
-            if (string.IsNullOrEmpty(config) || config[0] != '/')
+            if (config is null || !config.StartsWith('/'))
             {
                 config = Path.Combine(home, ".config");
             }
@@ -151,7 +151,7 @@ namespace System
             Debug.Assert(!string.IsNullOrEmpty(fallback), $"Expected non-empty fallback");
 
             string? envPath = GetEnvironmentVariable(key);
-            if (!string.IsNullOrEmpty(envPath) && envPath[0] == '/')
+            if (envPath is not null && envPath.StartsWith('/'))
             {
                 return envPath;
             }
index 9614d22..496eb65 100644 (file)
@@ -290,14 +290,14 @@ namespace System.Globalization
                 return new int[] { 3 };
             }
 
-            if (win32Str[0] == '0')
+            if (win32Str.StartsWith('0'))
             {
                 return new int[] { 0 };
             }
 
             // Since its in n;n;n;n;n format, we can always get the length quickly
             int[] values;
-            if (win32Str[^1] == '0')
+            if (win32Str.EndsWith('0'))
             {
                 // Trailing 0 gets dropped. 1;0 -> 1
                 values = new int[win32Str.Length / 2];
index dd5e58b..8212dee 100644 (file)
@@ -999,7 +999,7 @@ namespace System.Globalization
                             // Our existing names mostly look like:
                             // "English" + "United States" -> "English (United States)"
                             // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
-                            if (EnglishLanguageName[^1] == ')')
+                            if (EnglishLanguageName.EndsWith(')'))
                             {
                                 // "Azeri (Latin)" + "Azerbaijan" -> "Azeri (Latin, Azerbaijan)"
                                 englishDisplayName = string.Concat(
index 463a5da..34ba2a9 100644 (file)
@@ -228,7 +228,7 @@ namespace System.Globalization
                     m_dateWords.Add(str);
                 }
 
-                if (str[^1] == '.')
+                if (str.EndsWith('.'))
                 {
                     // Old version ignore the trailing dot in the date words. Support this as well.
                     string strWithoutDot = str[0..^1];
index 08b077c..e3fe7fa 100644 (file)
@@ -182,7 +182,7 @@ namespace System.Reflection
                 throw new InvalidFilterCriteriaException(SR.InvalidFilterCriteriaException_CritString);
             }
             // Check to see if this is a prefix or exact match requirement
-            if (str.Length > 0 && str[^1] == '*')
+            if (str.EndsWith('*'))
             {
                 ReadOnlySpan<char> slice = str.AsSpan(0, str.Length - 1);
                 return cls.Name.AsSpan().StartsWith(slice, comparison);
index c70e9e5..76d8ac6 100644 (file)
@@ -594,6 +594,20 @@ namespace System
 
         public bool EndsWith(char value)
         {
+            // If the string is empty, *(&_firstChar + length - 1) will deref within
+            // the _stringLength field, which will be all-zero. We must forbid '\0'
+            // from going down the optimized code path because otherwise empty strings
+            // would appear to end with '\0', which is incorrect.
+            // n.b. This optimization relies on the layout of string and is not valid
+            // for other data types like char[] or Span<char>.
+            if (RuntimeHelpers.IsKnownConstant(value) && value != '\0')
+            {
+                // deref Length now to front-load the null check; also take this time to zero-extend
+                // n.b. (localLength - 1) could be negative!
+                nuint localLength = (uint)Length;
+                return Unsafe.Add(ref _firstChar, (nint)localLength - 1) == value;
+            }
+
             int lastPos = Length - 1;
             return ((uint)lastPos < (uint)Length) && this[lastPos] == value;
         }
@@ -1004,10 +1018,16 @@ namespace System
 
         public bool StartsWith(char value)
         {
+            // If the string is empty, _firstChar will contain the null terminator.
+            // We forbid '\0' from going down the optimized code path because otherwise
+            // empty strings would appear to begin with '\0', which is incorrect.
+            // n.b. This optimization relies on the layout of string and is not valid
+            // for other data types like char[] or Span<char>.
             if (RuntimeHelpers.IsKnownConstant(value) && value != '\0')
             {
                 return _firstChar == value;
             }
+
             return Length != 0 && _firstChar == value;
         }
 
index 8ae2896..3d9e7c9 100644 (file)
@@ -442,7 +442,7 @@ namespace System.Xml
             {
                 text = string.Empty;
             }
-            else if (text.IndexOf("--", StringComparison.Ordinal) != -1 || (text.Length > 0 && text[text.Length - 1] == '-'))
+            else if (text.Contains("--") || text.StartsWith('-'))
             {
                 throw System.Runtime.Serialization.DiagnosticUtility.ExceptionUtility.ThrowHelperError(new ArgumentException(SR.XmlInvalidCommentChars, nameof(text)));
             }
index 655da9b..ad28303 100644 (file)
@@ -1279,7 +1279,7 @@ namespace System
             {
                 fixed (char* fixedName = name)
                 {
-                    if (name[0] == '[' && name[name.Length - 1] == ']')
+                    if (name.StartsWith('[') && name.EndsWith(']'))
                     {
                         // we require that _entire_ name is recognized as ipv6 address
                         if (IPv6AddressHelper.IsValid(fixedName, 1, ref end) && end == name.Length)
index 08ec978..6a6a623 100644 (file)
@@ -763,7 +763,7 @@ namespace System.Xml
         {
             try
             {
-                if (null != text && (text.Contains("--") || (text.Length != 0 && text[text.Length - 1] == '-')))
+                if (null != text && (text.Contains("--") || text.StartsWith('-')))
                 {
                     throw new ArgumentException(SR.Xml_InvalidCommentChars);
                 }
index b62d6c6..5a44dde 100644 (file)
@@ -1795,7 +1795,7 @@ namespace System.Xml
             {
                 throw new ArgumentException(SR.Format(SR.Xml_NamespaceDeclXmlXmlns, prefix));
             }
-            if (prefix.Length > 0 && prefix[0] == 'x')
+            if (prefix.StartsWith('x'))
             {
                 if (prefix == "xml")
                 {
index d4b0bb6..c88f12f 100644 (file)
@@ -36,7 +36,7 @@ namespace System.Xml
         {
             if (!doc.IsLoading)
             {
-                if (name.Length > 0 && name[0] == '#')
+                if (name.StartsWith('#'))
                 {
                     throw new ArgumentException(SR.Xdom_InvalidCharacter_EntityReference);
                 }
index a578730..11480e1 100644 (file)
@@ -3940,12 +3940,12 @@ namespace System.Xml.Serialization
                     else
                     {
                         bool useReflection = typeDesc.UseReflection;
-                        if (member.Source[member.Source.Length - 1] == '(' || member.Source[member.Source.Length - 1] == '{')
+                        if (member.Source.EndsWith('(') || member.Source.EndsWith('{'))
                         {
                             WriteCreateInstance(typeDescFullName, a, useReflection, typeDesc.CannotNew);
                             Writer.Write(member.Source);
                             Writer.Write(a);
-                            if (member.Source[member.Source.Length - 1] == '{')
+                            if (member.Source.EndsWith('{'))
                                 Writer.WriteLine("});");
                             else
                                 Writer.WriteLine(");");
@@ -4511,7 +4511,7 @@ namespace System.Xml.Serialization
         private void WriteSourceBegin(string source)
         {
             Writer.Write(source);
-            if (source[source.Length - 1] != '(' && source[source.Length - 1] != '{')
+            if (!source.EndsWith('(') && !source.EndsWith('{'))
                 Writer.Write(" = ");
         }
 
@@ -4519,9 +4519,9 @@ namespace System.Xml.Serialization
         {
             // source could be of the form "var", "arrayVar[i]",
             // "collection.Add(" or "methodInfo.Invoke(collection, new object[] {"
-            if (source[source.Length - 1] == '(')
+            if (source.EndsWith('('))
                 Writer.Write(")");
-            else if (source[source.Length - 1] == '{')
+            else if (source.EndsWith('{'))
                 Writer.Write("})");
         }
 
index 7936339..d2cca45 100644 (file)
@@ -2186,7 +2186,7 @@ namespace System.Xml.Serialization
                     }
                     else
                     {
-                        if (member.Source[member.Source.Length - 1] == '(' || member.Source[member.Source.Length - 1] == '{')
+                        if (member.Source.EndsWith('(') || member.Source.EndsWith('{'))
                         {
                             WriteCreateInstance(a, typeDesc.CannotNew, typeDesc.Type!);
                             WriteSourceBegin(member.Source);
index 2e73ecb..007d236 100644 (file)
@@ -306,7 +306,7 @@ namespace System.Xml
         {
             int len, lenLocal, offset;
 
-            if (s.Length != 0 && s[0] == '*')
+            if (s.StartsWith('*'))
             {
                 // '*' as a NameTest
                 prefix = localName = null;
index 68e658f..6b51af6 100644 (file)
@@ -429,7 +429,10 @@ namespace System.Xml
                 return token;
             }
 
-            if (token[0] == ' ' || token[token.Length - 1] == ' ' || token.IndexOfAny(crt) != -1 || token.IndexOf("  ", StringComparison.Ordinal) != -1)
+            if (token.StartsWith(' ') ||
+                token.EndsWith(' ') ||
+                token.IndexOfAny(crt) >= 0 ||
+                token.Contains("  "))
             {
                 throw new XmlException(SR.Sch_NotTokenString, token);
             }
@@ -438,12 +441,15 @@ namespace System.Xml
 
         internal static Exception? TryVerifyTOKEN(string token)
         {
-            if (token == null || token.Length == 0)
+            if (string.IsNullOrEmpty(token))
             {
                 return null;
             }
 
-            if (token[0] == ' ' || token[token.Length - 1] == ' ' || token.IndexOfAny(crt) != -1 || token.IndexOf("  ", StringComparison.Ordinal) != -1)
+            if (token.StartsWith(' ') ||
+                token.EndsWith(' ') ||
+                token.IndexOfAny(crt) >= 0 ||
+                token.Contains("  "))
             {
                 return new XmlException(SR.Sch_NotTokenString, token);
             }
index 906f1ed..ecb7808 100644 (file)
@@ -817,7 +817,7 @@ namespace System.Security.Cryptography.X509Certificates
 
             int count = baseUri.Length + resource.Length;
 
-            if (baseUri[baseUri.Length - 1] == '/')
+            if (baseUri.EndsWith('/'))
             {
                 return string.Create(
                     count,
index ddb3a33..0d8cc4a 100644 (file)
@@ -87,7 +87,7 @@ namespace System.Web
 
             HttpQSCollection result = new HttpQSCollection();
             int queryLength = query.Length;
-            int namePos = queryLength > 0 && query[0] == '?' ? 1 : 0;
+            int namePos = query.StartsWith('?') ? 1 : 0;
             if (queryLength == namePos)
             {
                 return result;
index aecc48b..d0c0284 100644 (file)
@@ -176,7 +176,7 @@ namespace DebuggerTests
             }
             else
             {
-                if (output.Length > 0 && output[^1] == '\n')
+                if (output.EndsWith('\n'))
                     output = output[..^1];
             }