Skip to content

Commit f9242b2

Browse files
authored
Remove some split/substring/trim allocations from System.Net.* (#80471)
1 parent 0e17421 commit f9242b2

File tree

6 files changed

+40
-55
lines changed

6 files changed

+40
-55
lines changed

src/libraries/Common/src/System/Net/Security/Unix/SafeFreeNegoCredentials.cs

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -37,32 +37,25 @@ public bool IsDefault
3737
get { return _isDefault; }
3838
}
3939

40-
public SafeFreeNegoCredentials(Interop.NetSecurityNative.PackageType packageType, string username, string password, string domain)
40+
public SafeFreeNegoCredentials(Interop.NetSecurityNative.PackageType packageType, string username, string password, ReadOnlySpan<char> domain)
4141
: base(IntPtr.Zero, true)
4242
{
4343
Debug.Assert(username != null && password != null, "Username and Password can not be null");
44-
const char At = '@';
45-
const char Backwhack = '\\';
4644

4745
// any invalid user format will not be manipulated and passed as it is.
48-
int index = username.IndexOf(Backwhack);
49-
if (index > 0 && username.IndexOf(Backwhack, index + 1) < 0 && string.IsNullOrEmpty(domain))
46+
int index = username.IndexOf('\\');
47+
if (index > 0 && username.IndexOf('\\', index + 1) < 0 && domain.IsEmpty)
5048
{
51-
domain = username.Substring(0, index);
49+
domain = username.AsSpan(0, index);
5250
username = username.Substring(index + 1);
5351
}
5452

5553
// remove any leading and trailing whitespace
56-
if (domain != null)
57-
{
58-
domain = domain.Trim();
59-
}
60-
6154
username = username.Trim();
62-
63-
if ((username.IndexOf(At) < 0) && !string.IsNullOrEmpty(domain))
55+
domain = domain.Trim();
56+
if (!username.Contains('@') && !domain.IsEmpty)
6457
{
65-
username += At + domain;
58+
username = string.Concat(username, "@", domain);
6659
}
6760

6861
bool ignore = false;

src/libraries/System.Net.HttpListener/src/System/Net/Managed/HttpListenerRequest.Managed.cs

Lines changed: 12 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
3232
//
3333

34+
using System.Buffers;
3435
using System.Collections.Specialized;
3536
using System.Diagnostics;
3637
using System.Globalization;
@@ -63,8 +64,8 @@ private sealed class Context : TransportContext
6364
private Stream? _inputStream;
6465
private HttpListenerContext _context;
6566
private bool _isChunked;
66-
67-
private static byte[] s_100continue = "HTTP/1.1 100 Continue\r\n\r\n"u8.ToArray();
67+
private static readonly IndexOfAnyValues<char> s_validMethodChars = IndexOfAnyValues.Create("!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~");
68+
private static readonly byte[] s_100continue = "HTTP/1.1 100 Continue\r\n\r\n"u8.ToArray();
6869

6970
internal HttpListenerRequest(HttpListenerContext context)
7071
{
@@ -75,37 +76,32 @@ internal HttpListenerRequest(HttpListenerContext context)
7576

7677
internal void SetRequestLine(string req)
7778
{
78-
string[] parts = req.Split(' ', 3);
79-
if (parts.Length != 3)
79+
Span<Range> parts = stackalloc Range[3];
80+
if (req.AsSpan().Split(parts, ' ') != 3)
8081
{
8182
_context.ErrorMessage = "Invalid request line (parts).";
8283
return;
8384
}
8485

85-
_method = parts[0];
86-
foreach (char c in _method)
86+
_method = req[parts[0]];
87+
if (_method.AsSpan().IndexOfAnyExcept(s_validMethodChars) >= 0)
8788
{
88-
if (char.IsAsciiLetterUpper(c) ||
89-
(c > 32 && c < 127 && c != '(' && c != ')' && c != '<' &&
90-
c != '<' && c != '>' && c != '@' && c != ',' && c != ';' &&
91-
c != ':' && c != '\\' && c != '"' && c != '/' && c != '[' &&
92-
c != ']' && c != '?' && c != '=' && c != '{' && c != '}'))
93-
continue;
94-
9589
_context.ErrorMessage = "(Invalid verb)";
9690
return;
9791
}
9892

99-
_rawUrl = parts[1];
100-
if (parts[2].Length != 8 || !parts[2].StartsWith("HTTP/", StringComparison.Ordinal))
93+
_rawUrl = req[parts[1]];
94+
95+
ReadOnlySpan<char> version = req.AsSpan()[parts[2]];
96+
if (version.Length != 8 || !version.StartsWith("HTTP/", StringComparison.Ordinal))
10197
{
10298
_context.ErrorMessage = "Invalid request line (version).";
10399
return;
104100
}
105101

106102
try
107103
{
108-
_version = new Version(parts[2].Substring(5));
104+
_version = Version.Parse(version.Slice("HTTP/".Length));
109105
}
110106
catch
111107
{
@@ -231,15 +227,6 @@ internal void FinishInitialization()
231227
}
232228
}
233229

234-
internal static string Unquote(string str)
235-
{
236-
int start = str.IndexOf('\"');
237-
int end = str.LastIndexOf('\"');
238-
if (start >= 0 && end >= 0)
239-
str = str.Substring(start + 1, end - 1);
240-
return str.Trim();
241-
}
242-
243230
internal void AddHeader(string header)
244231
{
245232
int colon = header.IndexOf(':');

src/libraries/System.Net.NetworkInformation/src/System/Net/NetworkInformation/StringParsingHelpers.Misc.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ internal static int ParseDefaultTtlFromFile(string filePath)
101101
internal static int ParseRawIntFile(string filePath)
102102
{
103103
int ret;
104-
if (!int.TryParse(ReadAllText(filePath).Trim(), out ret))
104+
if (!int.TryParse(ReadAllText(filePath).AsSpan().Trim(), out ret))
105105
{
106106
throw ExceptionHelper.CreateForParseFailure();
107107
}

src/libraries/System.Net.Requests/src/System/Net/HttpWebResponse.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -272,7 +272,7 @@ public string? CharacterSet
272272
string srchString = contentType.ToLowerInvariant();
273273

274274
//media subtypes of text type has a default as specified by rfc 2616
275-
if (srchString.Trim().StartsWith("text/", StringComparison.Ordinal))
275+
if (srchString.AsSpan().Trim().StartsWith("text/", StringComparison.Ordinal))
276276
{
277277
_characterSet = "ISO-8859-1";
278278
}

src/libraries/System.Net.WebHeaderCollection/src/System.Net.WebHeaderCollection.csproj

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@
1111
<Compile Include="System\Net\HeaderInfoTable.cs" />
1212
<Compile Include="$(CommonPath)System\Net\CaseInsensitiveAscii.cs"
1313
Link="Common\System\Net\CaseInsensitiveAscii.cs" />
14-
<Compile Include="$(CommonPath)System\StringExtensions.cs"
15-
Link="Common\System\StringExtensions.cs" />
1614
<Compile Include="$(CommonPath)System\Net\HttpKnownHeaderNames.cs"
1715
Link="Common\System\Net\HttpKnownHeaderNames.cs" />
1816
<Compile Include="$(CommonPath)System\Net\HttpValidationHelpers.cs"

src/libraries/System.Net.WebHeaderCollection/src/System/Net/HeaderInfoTable.cs

Lines changed: 19 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,10 @@ private static string[] ParseValueHelper(string value, bool isSetCookie)
3535
}
3636
else if ((value[i] == ',') && !inquote)
3737
{
38-
string singleValue = value.SubstringTrim(startIndex, length);
38+
ReadOnlySpan<char> singleValue = value.AsSpan(startIndex, length).Trim();
3939
if (!isSetCookie || !IsDuringExpiresAttributeParsing(singleValue))
4040
{
41-
tempStringCollection.Add(singleValue);
41+
tempStringCollection.Add(singleValue.ToString());
4242
startIndex = i + 1;
4343
length = 0;
4444
continue;
@@ -50,7 +50,7 @@ private static string[] ParseValueHelper(string value, bool isSetCookie)
5050
// Now add the last of the header values to the string table.
5151
if (startIndex < value.Length && length > 0)
5252
{
53-
tempStringCollection.Add(value.SubstringTrim(startIndex, length));
53+
tempStringCollection.Add(value.AsSpan(startIndex, length).Trim().ToString());
5454
}
5555

5656
return tempStringCollection.ToArray();
@@ -60,18 +60,25 @@ private static string[] ParseValueHelper(string value, bool isSetCookie)
6060
// for Set-Cookie header. It needs to check two conditions: 1. If current attribute
6161
// is Expires. 2. Have we finished parsing it yet. Because the Expires attribute
6262
// will contain exactly one comma, no comma means we are still parsing it.
63-
private static bool IsDuringExpiresAttributeParsing(string singleValue)
63+
private static bool IsDuringExpiresAttributeParsing(ReadOnlySpan<char> singleValue)
6464
{
65-
// Current cookie doesn't contain any attributes.
66-
if (!singleValue.Contains(';')) return false;
67-
68-
string lastElement = singleValue.Split(';')[^1];
69-
bool noComma = !lastElement.Contains(',');
65+
int semiPos = singleValue.LastIndexOf(';');
66+
if (semiPos >= 0)
67+
{
68+
ReadOnlySpan<char> lastElement = singleValue.Slice(semiPos + 1);
69+
if (!lastElement.Contains(','))
70+
{
71+
int equalsPos = lastElement.IndexOf('=');
72+
if (equalsPos < 0)
73+
{
74+
equalsPos = lastElement.Length;
75+
}
7076

71-
string lastAttribute = lastElement.Split('=')[0].Trim();
72-
bool isExpires = string.Equals(lastAttribute, "Expires", StringComparison.OrdinalIgnoreCase);
77+
return lastElement.Slice(0, equalsPos).Trim().Equals("Expires", StringComparison.OrdinalIgnoreCase);
78+
}
79+
}
7380

74-
return (isExpires && noComma);
81+
return false;
7582
}
7683

7784
private static Hashtable CreateHeaderHashtable()

0 commit comments

Comments
 (0)