// 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;
// 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';
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
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)
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" };
continue;
}
- if (!Ttext[data[offset]] || data[offset] == ' ')
+ if (!s_charactersAllowedInTokens.Contains(data[offset]) || data[offset] == ' ')
{
builder.Append('"');
for (; offset < data.Length; offset++)