Use IndexOfAnyValues in MailBnfHelper (#81486)
authorStephen Toub <stoub@microsoft.com>
Wed, 1 Feb 2023 22:54:07 +0000 (17:54 -0500)
committerGitHub <noreply@github.com>
Wed, 1 Feb 2023 22:54:07 +0000 (17:54 -0500)
src/libraries/System.Net.Mail/src/System/Net/Mail/MailBnfHelper.cs

index 9ce43f271bbf87b734b87ed974a3f9121bf93eb8..cc75fb5be9413d9861a4987f466f8cbd3ac018c2 100644 (file)
@@ -1,6 +1,7 @@
 // Licensed to the .NET Foundation under one or more agreements.
 // The .NET Foundation licenses this file to you under the MIT license.
 
+using System.Buffers;
 using System.Diagnostics;
 using System.Text;
 
@@ -17,15 +18,17 @@ namespace System.Net.Mime
         // characters allowed in domain literals
         internal static readonly bool[] Dtext = CreateCharactersAllowedInDomainLiterals();
 
-        // characters allowed in header names
-        internal static readonly bool[] Ftext = CreateCharactersAllowedInHeaderNames();
-
-        // characters allowed in tokens
-        internal static readonly bool[] Ttext = CreateCharactersAllowedInTokens();
-
         // characters allowed inside of comments
         internal static readonly bool[] Ctext = CreateCharactersAllowedInComments();
 
+        private static readonly IndexOfAnyValues<char> s_charactersAllowedInHeaderNames =
+            // ftext = %d33-57 / %d59-126
+            IndexOfAnyValues.Create("!\"#$%&'()*+,-./0123456789;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~");
+
+        private static readonly IndexOfAnyValues<char> s_charactersAllowedInTokens =
+            // ttext = %d33-126 except '()<>@,;:\"/[]?='
+            IndexOfAnyValues.Create("!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz{|}~");
+
         internal const char Quote = '\"';
         internal const char Space = ' ';
         internal const char Tab = '\t';
@@ -101,38 +104,6 @@ namespace System.Net.Mime
             return dtext;
         }
 
-        private static bool[] CreateCharactersAllowedInHeaderNames()
-        {
-            // ftext = %d33-57 / %d59-126
-            var ftext = new bool[128];
-            for (int i = 33; i <= 57; i++) { ftext[i] = true; }
-            for (int i = 59; i <= 126; i++) { ftext[i] = true; }
-            return ftext;
-        }
-
-        private static bool[] CreateCharactersAllowedInTokens()
-        {
-            // ttext = %d33-126 except '()<>@,;:\"/[]?='
-            var ttext = new bool[128];
-            for (int i = 33; i <= 126; i++) { ttext[i] = true; }
-            ttext['('] = false;
-            ttext[')'] = false;
-            ttext['<'] = false;
-            ttext['>'] = false;
-            ttext['@'] = false;
-            ttext[','] = false;
-            ttext[';'] = false;
-            ttext[':'] = false;
-            ttext['\\'] = false;
-            ttext['"'] = false;
-            ttext['/'] = false;
-            ttext['['] = false;
-            ttext[']'] = false;
-            ttext['?'] = false;
-            ttext['='] = false;
-            return ttext;
-        }
-
         private static bool[] CreateCharactersAllowedInComments()
         {
             // ctext- %d1-8 / %d11 / %d12 / %d14-31 / %33-39 / %42-91 / %93-127
@@ -175,14 +146,10 @@ namespace System.Net.Mime
 
         internal static void ValidateHeaderName(string data)
         {
-            int offset = 0;
-            for (; offset < data.Length; offset++)
+            if (data.Length == 0 || data.AsSpan().IndexOfAnyExcept(s_charactersAllowedInHeaderNames) >= 0)
             {
-                if (data[offset] > Ftext.Length || !Ftext[data[offset]])
-                    throw new FormatException(SR.InvalidHeaderName);
-            }
-            if (offset == 0)
                 throw new FormatException(SR.InvalidHeaderName);
+            }
         }
 
         internal static string? ReadQuotedString(string data, ref int offset, StringBuilder? builder)
@@ -253,24 +220,28 @@ namespace System.Net.Mime
         internal static string ReadToken(string data, ref int offset)
         {
             int start = offset;
-            for (; offset < data.Length; offset++)
+
+            if (start >= data.Length)
             {
-                if (!Ascii.IsValid(data[offset]))
-                {
-                    throw new FormatException(SR.Format(SR.MailHeaderFieldInvalidCharacter, data[offset]));
-                }
-                else if (!Ttext[data[offset]])
+                return string.Empty;
+            }
+
+            ReadOnlySpan<char> span = data.AsSpan(start);
+            int i = span.IndexOfAnyExcept(s_charactersAllowedInTokens);
+            if (i >= 0)
+            {
+                if (i == 0 || !Ascii.IsValid(span[i]))
                 {
-                    break;
+                    throw new FormatException(SR.Format(SR.MailHeaderFieldInvalidCharacter, span[i]));
                 }
             }
-
-            if (start == offset && offset < data.Length)
+            else
             {
-                throw new FormatException(SR.Format(SR.MailHeaderFieldInvalidCharacter, data[offset]));
+                i = span.Length;
             }
 
-            return data.Substring(start, offset - start);
+            offset += i;
+            return data.Substring(start, i);
         }
 
         private static readonly string?[] s_months = new string?[] { null, "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
@@ -328,7 +299,7 @@ namespace System.Net.Mime
                     continue;
                 }
 
-                if (!Ttext[data[offset]] || data[offset] == ' ')
+                if (!s_charactersAllowedInTokens.Contains(data[offset]) || data[offset] == ' ')
                 {
                     builder.Append('"');
                     for (; offset < data.Length; offset++)