@@ -21,19 +21,20 @@ namespace System.Text
2121{
2222 internal static partial class ASCIIUtility
2323 {
24- /// <summary>
25- /// Returns <see langword="true"/> iff all bytes in <paramref name="value"/> are ASCII.
26- /// </summary>
27- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
28- private static bool AllBytesInUInt32AreAscii ( uint value )
24+ #if DEBUG
25+ static ASCIIUtility ( )
2926 {
30- return ( ( value & 0x80808080u ) == 0 ) ;
27+ Debug . Assert ( sizeof ( nint ) == IntPtr . Size && nint . MinValue < 0 , "nint is defined incorrectly." ) ;
28+ Debug . Assert ( sizeof ( nuint ) == IntPtr . Size && nuint . MinValue == 0 , "nuint is defined incorrectly." ) ;
3129 }
30+ #endif // DEBUG
3231
3332 [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
3433 private static bool AllBytesInUInt64AreAscii ( ulong value )
3534 {
36- return ( ( value & 0x80808080_80808080ul ) == 0 ) ;
35+ // If the high bit of any byte is set, that byte is non-ASCII.
36+
37+ return ( ( value & UInt64HighBitsOnlyMask ) == 0 ) ;
3738 }
3839
3940 /// <summary>
@@ -54,56 +55,6 @@ private static bool AllCharsInUInt64AreAscii(ulong value)
5455 return ( ( value & ~ 0x007F007F_007F007Ful ) == 0 ) ;
5556 }
5657
57- /// <summary>
58- /// Given a 24-bit integer which represents a three-byte buffer read in machine endianness,
59- /// counts the number of consecutive ASCII bytes starting from the beginning of the buffer.
60- /// Returns a value 0 - 3, inclusive.
61- /// </summary>
62- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
63- private static uint CountNumberOfLeadingAsciiBytesFrom24BitInteger ( uint value )
64- {
65- // This implementation seems to have better performance than tzcnt.
66-
67- // The 'allBytesUpToNowAreAscii' DWORD uses bit twiddling to hold a 1 or a 0 depending
68- // on whether all processed bytes were ASCII. Then we accumulate all of the
69- // results to calculate how many consecutive ASCII bytes are present.
70-
71- value = ~ value ;
72-
73- if ( BitConverter . IsLittleEndian )
74- {
75- // Read first byte
76- uint allBytesUpToNowAreAscii = ( value >>= 7 ) & 1 ;
77- uint numAsciiBytes = allBytesUpToNowAreAscii ;
78-
79- // Read second byte
80- allBytesUpToNowAreAscii &= ( value >>= 8 ) ;
81- numAsciiBytes += allBytesUpToNowAreAscii ;
82-
83- // Read third byte
84- allBytesUpToNowAreAscii &= ( value >>= 8 ) ;
85- numAsciiBytes += allBytesUpToNowAreAscii ;
86-
87- return numAsciiBytes ;
88- }
89- else
90- {
91- // Read first byte
92- uint allBytesUpToNowAreAscii = ( value = ROL32 ( value , 1 ) ) & 1 ;
93- uint numAsciiBytes = allBytesUpToNowAreAscii ;
94-
95- // Read second byte
96- allBytesUpToNowAreAscii &= ( value = ROL32 ( value , 8 ) ) ;
97- numAsciiBytes += allBytesUpToNowAreAscii ;
98-
99- // Read third byte
100- allBytesUpToNowAreAscii &= ( value = ROL32 ( value , 8 ) ) ;
101- numAsciiBytes += allBytesUpToNowAreAscii ;
102-
103- return numAsciiBytes ;
104- }
105- }
106-
10758 /// <summary>
10859 /// Given a DWORD which represents two packed chars in machine-endian order,
10960 /// <see langword="true"/> iff the first char (in machine-endian order) is ASCII.
@@ -273,7 +224,7 @@ private static unsafe nuint GetIndexOfFirstNonAsciiByte_Default(byte* pBuffer, n
273224 // we get to the high byte; or (b) all of the earlier bytes are ASCII, so the high byte must be
274225 // non-ASCII. In both cases we only care about the low 24 bits.
275226
276- pBuffer += CountNumberOfLeadingAsciiBytesFrom24BitInteger ( currentUInt32 ) ;
227+ pBuffer += CountNumberOfLeadingAsciiBytesFromUInt32WithSomeNonAsciiData ( currentUInt32 ) ;
277228 goto Finish ;
278229 }
279230
@@ -435,7 +386,7 @@ private static unsafe nuint GetIndexOfFirstNonAsciiByte_Sse2(byte* pBuffer, nuin
435386
436387 uint currentDWord ;
437388 Debug . Assert ( ! AllBytesInUInt32AreAscii ( currentDWord ) , "Shouldn't be here unless we see non-ASCII data." ) ;
438- pBuffer += CountNumberOfLeadingAsciiBytesFrom24BitInteger ( currentDWord ) ;
389+ pBuffer += CountNumberOfLeadingAsciiBytesFromUInt32WithSomeNonAsciiData ( currentDWord ) ;
439390
440391 goto Finish ;
441392
@@ -461,7 +412,7 @@ private static unsafe nuint GetIndexOfFirstNonAsciiByte_Sse2(byte* pBuffer, nuin
461412 // Clear everything but the high bit of each byte, then tzcnt.
462413 // Remember the / 8 at the end to convert bit count to byte count.
463414
464- candidateUInt64 &= 0x80808080_80808080ul ;
415+ candidateUInt64 &= UInt64HighBitsOnlyMask ;
465416 pBuffer += ( nuint ) ( Bmi1 . X64 . TrailingZeroCount ( candidateUInt64 ) / 8 ) ;
466417 goto Finish ;
467418 }
@@ -1395,17 +1346,7 @@ private static unsafe nuint NarrowUtf16ToAscii_Sse2(char* pUtf16Buffer, byte* pA
13951346 // Turn the 8 ASCII chars we just read into 8 ASCII bytes, then copy it to the destination.
13961347
13971348 Vector128 < byte > asciiVector = Sse2 . PackUnsignedSaturate ( utf16VectorFirst , utf16VectorFirst ) ;
1398-
1399- if ( Sse41 . X64 . IsSupported )
1400- {
1401- // Use PEXTRQ instruction if available, since it can extract from the vector directly to the destination address.
1402- Unsafe . WriteUnaligned < ulong > ( pAsciiBuffer , Sse41 . X64 . Extract ( asciiVector . AsUInt64 ( ) , 0 ) ) ;
1403- }
1404- else
1405- {
1406- // Bounce this through a temporary register (with potential stack spillage) before writing to memory.
1407- Unsafe . WriteUnaligned < ulong > ( pAsciiBuffer , asciiVector . AsUInt64 ( ) . GetElement ( 0 ) ) ;
1408- }
1349+ Sse2 . StoreLow ( ( ulong * ) pAsciiBuffer , asciiVector . AsUInt64 ( ) ) ; // ulong* calculated here is UNALIGNED
14091350
14101351 nuint currentOffsetInElements = SizeOfVector128 / 2 ; // we processed 8 elements so far
14111352
@@ -1444,16 +1385,7 @@ private static unsafe nuint NarrowUtf16ToAscii_Sse2(char* pUtf16Buffer, byte* pA
14441385
14451386 // Turn the 8 ASCII chars we just read into 8 ASCII bytes, then copy it to the destination.
14461387 asciiVector = Sse2 . PackUnsignedSaturate ( utf16VectorFirst , utf16VectorFirst ) ;
1447-
1448- // See comments earlier in this method for information about how this works.
1449- if ( Sse41 . X64 . IsSupported )
1450- {
1451- Unsafe . WriteUnaligned < ulong > ( pAsciiBuffer + currentOffsetInElements , Sse41 . X64 . Extract ( asciiVector . AsUInt64 ( ) , 0 ) ) ;
1452- }
1453- else
1454- {
1455- Unsafe . WriteUnaligned < ulong > ( pAsciiBuffer + currentOffsetInElements , asciiVector . AsUInt64 ( ) . GetElement ( 0 ) ) ;
1456- }
1388+ Sse2 . StoreLow ( ( ulong * ) ( pAsciiBuffer + currentOffsetInElements ) , asciiVector . AsUInt64 ( ) ) ; // ulong* calculated here is UNALIGNED
14571389 }
14581390
14591391 // Calculate how many elements we wrote in order to get pAsciiBuffer to its next alignment
@@ -1529,26 +1461,12 @@ private static unsafe nuint NarrowUtf16ToAscii_Sse2(char* pUtf16Buffer, byte* pA
15291461
15301462 Debug . Assert ( ( ( nuint ) pAsciiBuffer + currentOffsetInElements ) % sizeof ( ulong ) == 0 , "Destination should be ulong-aligned." ) ;
15311463
1532- // See comments earlier in this method for information about how this works.
1533- if ( Sse41 . X64 . IsSupported )
1534- {
1535- * ( ulong * ) ( pAsciiBuffer + currentOffsetInElements ) = Sse41. X64 . Extract ( asciiVector . AsUInt64 ( ) , 0 ) ;
1536- }
1537- else
1538- {
1539- * ( ulong * ) ( pAsciiBuffer + currentOffsetInElements ) = asciiVector. AsUInt64 ( ) . GetElement ( 0 ) ;
1540- }
1464+ Sse2 . StoreLow ( ( ulong * ) ( pAsciiBuffer + currentOffsetInElements ) , asciiVector . AsUInt64 ( ) ) ; // ulong* calculated here is aligned
15411465 currentOffsetInElements += SizeOfVector128 / 2 ;
15421466
15431467 goto Finish ;
15441468 }
15451469
1546- /// <summary>
1547- /// Rotates a <see cref="uint"/> left. The JIT is smart enough to turn this into a ROL / ROR instruction.
1548- /// </summary>
1549- [ MethodImpl ( MethodImplOptions . AggressiveInlining ) ]
1550- private static uint ROL32 ( uint value , int shift ) => ( value << shift ) | ( value >> ( 32 - shift ) ) ;
1551-
15521470 /// <summary>
15531471 /// Copies as many ASCII bytes (00..7F) as possible from <paramref name="pAsciiBuffer"/>
15541472 /// to <paramref name="pUtf16Buffer"/>, stopping when the first non-ASCII byte is encountered
0 commit comments