-
Notifications
You must be signed in to change notification settings - Fork 10.3k
Add option to interpret request headers as Latin1 #18255
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
a8968a7
f8c8829
e24e0bc
6db7e3b
15dc9ae
9a4d28b
273f489
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -2,7 +2,6 @@ | |||||||||||||||||||||||||||||||||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
using System; | ||||||||||||||||||||||||||||||||||||
using System.Buffers.Binary; | ||||||||||||||||||||||||||||||||||||
using System.Diagnostics; | ||||||||||||||||||||||||||||||||||||
using System.Numerics; | ||||||||||||||||||||||||||||||||||||
using System.Runtime.CompilerServices; | ||||||||||||||||||||||||||||||||||||
|
@@ -17,6 +16,9 @@ internal class StringUtilities | |||||||||||||||||||||||||||||||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)] | ||||||||||||||||||||||||||||||||||||
public static unsafe bool TryGetAsciiString(byte* input, char* output, int count) | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
Debug.Assert(input != null); | ||||||||||||||||||||||||||||||||||||
Debug.Assert(output != null); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
// Calculate end position | ||||||||||||||||||||||||||||||||||||
var end = input + count; | ||||||||||||||||||||||||||||||||||||
// Start as valid | ||||||||||||||||||||||||||||||||||||
|
@@ -115,6 +117,111 @@ out Unsafe.AsRef<Vector<short>>(output), | |||||||||||||||||||||||||||||||||||
return isValid; | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)] | ||||||||||||||||||||||||||||||||||||
public static unsafe bool TryGetLatin1String(byte* input, char* output, int count) | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
Debug.Assert(input != null); | ||||||||||||||||||||||||||||||||||||
Debug.Assert(output != null); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
// Calculate end position | ||||||||||||||||||||||||||||||||||||
var end = input + count; | ||||||||||||||||||||||||||||||||||||
// Start as valid | ||||||||||||||||||||||||||||||||||||
var isValid = true; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
do | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
// If Vector not-accelerated or remaining less than vector size | ||||||||||||||||||||||||||||||||||||
if (!Vector.IsHardwareAccelerated || input > end - Vector<sbyte>.Count) | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This clause should be |
||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
if (IntPtr.Size == 8) // Use Intrinsic switch for branch elimination | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
// 64-bit: Loop longs by default | ||||||||||||||||||||||||||||||||||||
while (input <= end - sizeof(long)) | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This clause should be |
||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
isValid &= CheckBytesNotNull(((long*)input)[0]); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
output[0] = (char)input[0]; | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You could use aspnetcore/src/Shared/ServerInfrastructure/StringUtilities.cs Lines 121 to 137 in 80a77ff
|
||||||||||||||||||||||||||||||||||||
output[1] = (char)input[1]; | ||||||||||||||||||||||||||||||||||||
output[2] = (char)input[2]; | ||||||||||||||||||||||||||||||||||||
output[3] = (char)input[3]; | ||||||||||||||||||||||||||||||||||||
output[4] = (char)input[4]; | ||||||||||||||||||||||||||||||||||||
output[5] = (char)input[5]; | ||||||||||||||||||||||||||||||||||||
output[6] = (char)input[6]; | ||||||||||||||||||||||||||||||||||||
output[7] = (char)input[7]; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
input += sizeof(long); | ||||||||||||||||||||||||||||||||||||
output += sizeof(long); | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
if (input <= end - sizeof(int)) | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
isValid &= CheckBytesNotNull(((int*)input)[0]); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
output[0] = (char)input[0]; | ||||||||||||||||||||||||||||||||||||
output[1] = (char)input[1]; | ||||||||||||||||||||||||||||||||||||
output[2] = (char)input[2]; | ||||||||||||||||||||||||||||||||||||
output[3] = (char)input[3]; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
input += sizeof(int); | ||||||||||||||||||||||||||||||||||||
output += sizeof(int); | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
else | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
// 32-bit: Loop ints by default | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no need for this branch. Just do loops of stride 8 on all platforms, and ignore the |
||||||||||||||||||||||||||||||||||||
while (input <= end - sizeof(int)) | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
isValid &= CheckBytesNotNull(((int*)input)[0]); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
output[0] = (char)input[0]; | ||||||||||||||||||||||||||||||||||||
output[1] = (char)input[1]; | ||||||||||||||||||||||||||||||||||||
output[2] = (char)input[2]; | ||||||||||||||||||||||||||||||||||||
output[3] = (char)input[3]; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
input += sizeof(int); | ||||||||||||||||||||||||||||||||||||
output += sizeof(int); | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
if (input <= end - sizeof(short)) | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
isValid &= CheckBytesNotNull(((short*)input)[0]); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
output[0] = (char)input[0]; | ||||||||||||||||||||||||||||||||||||
output[1] = (char)input[1]; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
input += sizeof(short); | ||||||||||||||||||||||||||||||||||||
output += sizeof(short); | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
if (input < end) | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
isValid &= CheckBytesNotNull(((sbyte*)input)[0]); | ||||||||||||||||||||||||||||||||||||
output[0] = (char)input[0]; | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
return isValid; | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
// do/while as entry condition already checked | ||||||||||||||||||||||||||||||||||||
do | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
// Use byte/ushort instead of signed equivalents to ensure it doesn't fill based on the high bit. | ||||||||||||||||||||||||||||||||||||
var vector = Unsafe.AsRef<Vector<byte>>(input); | ||||||||||||||||||||||||||||||||||||
isValid &= CheckBytesNotNull(vector); | ||||||||||||||||||||||||||||||||||||
Vector.Widen( | ||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If this method is hot, you could add explicit paths for AVX2, SSE2, because
For reference see
Except There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We don't expect this method to be hot. This is mostly here so Bing can accept non-UTF8/ASCII encoded headers from legacy clients. This is opt-in via an undocumented IConfiguration flag. Even when opted-in, this method will only be hit to decode non-ASCII request headers. I'll look into merging #17556 to master soon. I'll convert GetLatin1String to use your TryGetAsciiString optimizations when I do. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is if we don't just end up using Encoding.Latin1 in .NET 5 which we very well might. |
||||||||||||||||||||||||||||||||||||
vector, | ||||||||||||||||||||||||||||||||||||
out Unsafe.AsRef<Vector<ushort>>(output), | ||||||||||||||||||||||||||||||||||||
out Unsafe.AsRef<Vector<ushort>>(output + Vector<ushort>.Count)); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
input += Vector<byte>.Count; | ||||||||||||||||||||||||||||||||||||
output += Vector<byte>.Count; | ||||||||||||||||||||||||||||||||||||
} while (input <= end - Vector<byte>.Count); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
// Vector path done, loop back to do non-Vector | ||||||||||||||||||||||||||||||||||||
// If is a exact multiple of vector size, bail now | ||||||||||||||||||||||||||||||||||||
} while (input < end); | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
return isValid; | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
[MethodImpl(MethodImplOptions.AggressiveOptimization)] | ||||||||||||||||||||||||||||||||||||
public unsafe static bool BytesOrdinalEqualsStringAndAscii(string previousValue, Span<byte> newValue) | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
|
@@ -421,7 +528,7 @@ private static bool CheckBytesInAsciiRange(Vector<sbyte> check) | |||||||||||||||||||||||||||||||||||
// Validate: bytes != 0 && bytes <= 127 | ||||||||||||||||||||||||||||||||||||
// Subtract 1 from all bytes to move 0 to high bits | ||||||||||||||||||||||||||||||||||||
// bitwise or with self to catch all > 127 bytes | ||||||||||||||||||||||||||||||||||||
// mask off high bits and check if 0 | ||||||||||||||||||||||||||||||||||||
// mask off non high bits and check if 0 | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] // Needs a push | ||||||||||||||||||||||||||||||||||||
private static bool CheckBytesInAsciiRange(long check) | ||||||||||||||||||||||||||||||||||||
|
@@ -444,5 +551,39 @@ private static bool CheckBytesInAsciiRange(short check) | |||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
private static bool CheckBytesInAsciiRange(sbyte check) | ||||||||||||||||||||||||||||||||||||
=> check > 0; | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] // Needs a push | ||||||||||||||||||||||||||||||||||||
private static bool CheckBytesNotNull(Vector<byte> check) | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
// Vectorized byte range check, signed byte != null | ||||||||||||||||||||||||||||||||||||
return !Vector.EqualsAny(check, Vector<byte>.Zero); | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
// Validate: bytes != 0 | ||||||||||||||||||||||||||||||||||||
// Subtract 1 from all bytes to move 0 to high bits | ||||||||||||||||||||||||||||||||||||
// bitwise and with ~check so high bits are only set for bytes that were originally 0 | ||||||||||||||||||||||||||||||||||||
// mask off non high bits and check if 0 | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] // Needs a push | ||||||||||||||||||||||||||||||||||||
private static bool CheckBytesNotNull(long check) | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
const long HighBits = unchecked((long)0x8080808080808080L); | ||||||||||||||||||||||||||||||||||||
return ((check - 0x0101010101010101L) & ~check & HighBits) == 0; | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
private static bool CheckBytesNotNull(int check) | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
const int HighBits = unchecked((int)0x80808080); | ||||||||||||||||||||||||||||||||||||
return ((check - 0x01010101) & ~check & HighBits) == 0; | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
private static bool CheckBytesNotNull(short check) | ||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||
const short HighBits = unchecked((short)0x8080); | ||||||||||||||||||||||||||||||||||||
return ((check - 0x0101) & ~check & HighBits) == 0; | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||
private static bool CheckBytesNotNull(sbyte check) | ||||||||||||||||||||||||||||||||||||
=> check != 0; | ||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we using unsafe code here instead of Span and Span? To match TryGetAsciiString?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah. I'm trying to keep this as close to TryGetAsciiString as possible since it's conceptually quite similar.