@@ -17,10 +17,10 @@ namespace Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure
1717{
1818 internal static class StringUtilities
1919 {
20- private static readonly SpanAction < char , IntPtr > s_getAsciiOrUtf8StringNonNullCharacters = GetAsciiStringNonNullCharacters ;
21-
22- private static string GetAsciiOrUTF8StringNonNullCharacters ( this Span < byte > span , Encoding defaultEncoding )
23- => GetAsciiOrUTF8StringNonNullCharacters ( ( ReadOnlySpan < byte > ) span , defaultEncoding ) ;
20+ private static readonly SpanAction < char , IntPtr > s_getAsciiOrUTF8StringNonNullCharacters = GetAsciiStringNonNullCharactersWithMarker ;
21+ private static readonly SpanAction < char , IntPtr > s_getAsciiStringNonNullCharacters = GetAsciiStringNonNullCharacters ;
22+ private static readonly SpanAction < char , IntPtr > s_getLatin1StringNonNullCharacters = GetLatin1StringNonNullCharacters ;
23+ private static readonly SpanAction < char , ( string ? str , char separator , uint number ) > s_populateSpanWithHexSuffix = PopulateSpanWithHexSuffix ;
2424
2525 public static unsafe string GetAsciiOrUTF8StringNonNullCharacters ( this ReadOnlySpan < byte > span , Encoding defaultEncoding )
2626 {
@@ -31,7 +31,7 @@ public static unsafe string GetAsciiOrUTF8StringNonNullCharacters(this ReadOnlyS
3131
3232 fixed ( byte * source = & MemoryMarshal . GetReference ( span ) )
3333 {
34- var resultString = string . Create ( span . Length , new IntPtr ( source ) , s_getAsciiOrUtf8StringNonNullCharacters ) ;
34+ var resultString = string . Create ( span . Length , ( IntPtr ) source , s_getAsciiOrUTF8StringNonNullCharacters ) ;
3535
3636 // If resultString is marked, perform UTF-8 encoding
3737 if ( resultString [ 0 ] == '\0 ' )
@@ -56,11 +56,11 @@ public static unsafe string GetAsciiOrUTF8StringNonNullCharacters(this ReadOnlyS
5656 }
5757 }
5858
59- private static unsafe void GetAsciiStringNonNullCharacters ( Span < char > buffer , IntPtr state )
59+ private static unsafe void GetAsciiStringNonNullCharactersWithMarker ( Span < char > buffer , IntPtr state )
6060 {
6161 fixed ( char * output = & MemoryMarshal . GetReference ( buffer ) )
6262 {
63- // This version if AsciiUtilities returns false if there are any null ('\0') or non-Ascii
63+ // This version of AsciiUtilities returns false if there are any null ('\0') or non-Ascii
6464 // character (> 127) in the string.
6565 if ( ! TryGetAsciiString ( ( byte * ) state . ToPointer ( ) , output , buffer . Length ) )
6666 {
@@ -70,27 +70,55 @@ private static unsafe void GetAsciiStringNonNullCharacters(Span<char> buffer, In
7070 }
7171 }
7272
73+ public static unsafe string GetAsciiStringNonNullCharacters ( this ReadOnlySpan < byte > span )
74+ {
75+ if ( span . IsEmpty )
76+ {
77+ return string . Empty ;
78+ }
79+
80+ fixed ( byte * source = & MemoryMarshal . GetReference ( span ) )
81+ {
82+ return string . Create ( span . Length , ( IntPtr ) source , s_getAsciiStringNonNullCharacters ) ;
83+ }
84+ }
85+
86+ private static unsafe void GetAsciiStringNonNullCharacters ( Span < char > buffer , IntPtr state )
87+ {
88+ fixed ( char * output = & MemoryMarshal . GetReference ( buffer ) )
89+ {
90+ // This version of AsciiUtilities returns false if there are any null ('\0') or non-Ascii
91+ // character (> 127) in the string.
92+ if ( ! TryGetAsciiString ( ( byte * ) state . ToPointer ( ) , output , buffer . Length ) )
93+ {
94+ throw new InvalidOperationException ( ) ;
95+ }
96+ }
97+ }
98+
7399 public static unsafe string GetLatin1StringNonNullCharacters ( this ReadOnlySpan < byte > span )
74100 {
75101 if ( span . IsEmpty )
76102 {
77103 return string . Empty ;
78104 }
79105
80- var resultString = new string ( '\0 ' , span . Length ) ;
106+ fixed ( byte * source = & MemoryMarshal . GetReference ( span ) )
107+ {
108+ return string . Create ( span . Length , ( IntPtr ) source , s_getLatin1StringNonNullCharacters ) ;
109+ }
110+ }
81111
82- fixed ( char * output = resultString )
83- fixed ( byte * buffer = span )
112+ private static unsafe void GetLatin1StringNonNullCharacters ( Span < char > buffer , IntPtr state )
113+ {
114+ fixed ( char * output = & MemoryMarshal . GetReference ( buffer ) )
84115 {
85- // This returns false if there are any null (0 byte) characters in the string.
86- if ( ! TryGetLatin1String ( buffer , output , span . Length ) )
116+ if ( ! TryGetLatin1String ( ( byte * ) state . ToPointer ( ) , output , buffer . Length ) )
87117 {
88118 // null characters are considered invalid
89119 throw new InvalidOperationException ( ) ;
90120 }
91121 }
92-
93- return resultString ;
94122 }
95123
96124 [ MethodImpl ( MethodImplOptions . AggressiveOptimization ) ]
@@ -299,7 +327,7 @@ public static unsafe bool TryGetLatin1String(byte* input, char* output, int coun
299327 // If Vector not-accelerated or remaining less than vector size
300328 if ( ! Vector . IsHardwareAccelerated || input > end - Vector < sbyte > . Count )
301329 {
302- if ( IntPtr . Size == 8 ) // Use Intrinsic switch for branch elimination
330+ if ( Environment . Is64BitProcess ) // Use Intrinsic switch for branch elimination
303331 {
304332 // 64-bit: Loop longs by default
305333 while ( input <= end - sizeof ( long ) )
@@ -403,10 +431,6 @@ public static bool BytesOrdinalEqualsStringAndAscii(string previousValue, ReadOn
403431 goto NotEqual ;
404432 }
405433
406- // Use IntPtr values rather than int, to avoid unnecessary 32 -> 64 movs on 64-bit.
407- // Unfortunately this means we also need to cast to byte* for comparisons as IntPtr doesn't
408- // support operator comparisons (e.g. <=, >, etc).
409- //
410434 // Note: Pointer comparison is unsigned, so we use the compare pattern (offset + length <= count)
411435 // rather than (offset <= count - length) which we'd do with signed comparison to avoid overflow.
412436 // This isn't problematic as we know the maximum length is max string length (from test above)
@@ -666,7 +690,6 @@ private static bool IsValidHeaderString(string value)
666690 return false ;
667691 }
668692 }
669- private static readonly SpanAction < char , ( string ? str , char separator , uint number ) > s_populateSpanWithHexSuffix = PopulateSpanWithHexSuffix ;
670693
671694 /// <summary>
672695 /// A faster version of String.Concat(<paramref name="str"/>, <paramref name="separator"/>, <paramref name="number"/>.ToString("X8"))
0 commit comments