Skip to content

Commit 80a77ff

Browse files
committed
Use static readonly span-actions as this gives a boost due to not having a null check for the compiler generated cached delegate
1 parent 64e58ae commit 80a77ff

File tree

1 file changed

+51
-35
lines changed

1 file changed

+51
-35
lines changed

src/Servers/Kestrel/Core/src/Internal/Infrastructure/HttpUtilities.cs

Lines changed: 51 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System;
5+
using System.Buffers;
56
using System.Diagnostics;
67
using System.Runtime.CompilerServices;
78
using System.Runtime.InteropServices;
@@ -87,6 +88,7 @@ private static unsafe ulong GetMaskAsLong(byte[] bytes)
8788
}
8889

8990
// The same as GetAsciiStringNonNullCharacters but throws BadRequest
91+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
9092
public static unsafe string GetHeaderName(this ReadOnlySpan<byte> span)
9193
{
9294
if (span.IsEmpty)
@@ -96,24 +98,29 @@ public static unsafe string GetHeaderName(this ReadOnlySpan<byte> span)
9698

9799
fixed (byte* source = &MemoryMarshal.GetReference(span))
98100
{
99-
return string.Create(span.Length, new IntPtr(source), (buffer, state) =>
101+
return string.Create(span.Length, new IntPtr(source), s_getHeaderName);
102+
}
103+
}
104+
105+
private static readonly SpanAction<char, IntPtr> s_getHeaderName = GetHeaderName;
106+
107+
private static unsafe void GetHeaderName(Span<char> buffer, IntPtr state)
108+
{
109+
fixed (char* output = &MemoryMarshal.GetReference(buffer))
110+
{
111+
// This version if AsciiUtilities returns null if there are any null (0 byte) characters
112+
// in the string
113+
if (!StringUtilities.TryGetAsciiString((byte*)state.ToPointer(), output, buffer.Length))
100114
{
101-
fixed (char* output = &MemoryMarshal.GetReference(buffer))
102-
{
103-
// This version if AsciiUtilities returns null if there are any null (0 byte) characters
104-
// in the string
105-
if (!StringUtilities.TryGetAsciiString((byte*)state.ToPointer(), output, buffer.Length))
106-
{
107-
BadHttpRequestException.Throw(RequestRejectionReason.InvalidCharactersInHeaderName);
108-
}
109-
}
110-
});
115+
BadHttpRequestException.Throw(RequestRejectionReason.InvalidCharactersInHeaderName);
116+
}
111117
}
112118
}
113119

114120
public static string GetAsciiStringNonNullCharacters(this Span<byte> span)
115121
=> GetAsciiStringNonNullCharacters((ReadOnlySpan<byte>)span);
116122

123+
[MethodImpl(MethodImplOptions.AggressiveInlining)]
117124
public static unsafe string GetAsciiStringNonNullCharacters(this ReadOnlySpan<byte> span)
118125
{
119126
if (span.IsEmpty)
@@ -123,18 +130,22 @@ public static unsafe string GetAsciiStringNonNullCharacters(this ReadOnlySpan<by
123130

124131
fixed (byte* source = &MemoryMarshal.GetReference(span))
125132
{
126-
return string.Create(span.Length, new IntPtr(source), (buffer, state) =>
133+
return string.Create(span.Length, new IntPtr(source), s_getAsciiStringNonNullCharacters);
134+
}
135+
}
136+
137+
private static readonly SpanAction<char, IntPtr> s_getAsciiStringNonNullCharacters = GetAsciiStringNonNullCharacters;
138+
139+
private static unsafe void GetAsciiStringNonNullCharacters(Span<char> buffer, IntPtr state)
140+
{
141+
fixed (char* output = &MemoryMarshal.GetReference(buffer))
142+
{
143+
// This version if AsciiUtilities returns null if there are any null (0 byte) characters
144+
// in the string
145+
if (!StringUtilities.TryGetAsciiString((byte*)state.ToPointer(), output, buffer.Length))
127146
{
128-
fixed (char* output = &MemoryMarshal.GetReference(buffer))
129-
{
130-
// This version if AsciiUtilities returns null if there are any null (0 byte) characters
131-
// in the string
132-
if (!StringUtilities.TryGetAsciiString((byte*)state.ToPointer(), output, buffer.Length))
133-
{
134-
throw new InvalidOperationException();
135-
}
136-
}
137-
});
147+
throw new InvalidOperationException();
148+
}
138149
}
139150
}
140151

@@ -150,23 +161,12 @@ public static unsafe string GetAsciiOrUTF8StringNonNullCharacters(this ReadOnlyS
150161

151162
fixed (byte* source = &MemoryMarshal.GetReference(span))
152163
{
153-
var resultString = string.Create(span.Length, new IntPtr(source), (buffer, state) =>
154-
{
155-
fixed (char* output = &MemoryMarshal.GetReference(buffer))
156-
{
157-
// This version if AsciiUtilities returns null if there are any null (0 byte) characters
158-
// in the string
159-
if (!StringUtilities.TryGetAsciiString((byte*)state.ToPointer(), output, buffer.Length))
160-
{
161-
// Mark resultString for UTF-8 encoding
162-
output[0] = '\0';
163-
}
164-
}
165-
});
164+
var resultString = string.Create(span.Length, new IntPtr(source), s_getAsciiOrUtf8StringNonNullCharacters);
166165

167166
// If rersultString is marked, perform UTF-8 encoding
168167
if (resultString[0] == '\0')
169168
{
169+
// null characters are considered invalid
170170
if (span.IndexOf((byte)0) != -1)
171171
{
172172
throw new InvalidOperationException();
@@ -186,6 +186,22 @@ public static unsafe string GetAsciiOrUTF8StringNonNullCharacters(this ReadOnlyS
186186
}
187187
}
188188

189+
private static readonly SpanAction<char, IntPtr> s_getAsciiOrUtf8StringNonNullCharacters = GetAsciiOrUTF8StringNonNullCharacters;
190+
191+
private static unsafe void GetAsciiOrUTF8StringNonNullCharacters(Span<char> buffer, IntPtr state)
192+
{
193+
fixed (char* output = &MemoryMarshal.GetReference(buffer))
194+
{
195+
// This version if AsciiUtilities returns null if there are any null (0 byte) characters
196+
// in the string
197+
if (!StringUtilities.TryGetAsciiString((byte*)state.ToPointer(), output, buffer.Length))
198+
{
199+
// Mark resultString for UTF-8 encoding
200+
output[0] = '\0';
201+
}
202+
}
203+
}
204+
189205
public static string GetAsciiStringEscaped(this Span<byte> span, int maxChars)
190206
{
191207
var sb = new StringBuilder();

0 commit comments

Comments
 (0)