|
6 | 6 | using System.Diagnostics; |
7 | 7 | using System.Numerics; |
8 | 8 | using System.Runtime.CompilerServices; |
9 | | -using System.Runtime.Intrinsics.X86; |
10 | 9 | using Internal.Runtime.CompilerServices; |
11 | 10 |
|
12 | 11 | namespace System.Text.Unicode |
@@ -61,47 +60,27 @@ private static uint ExtractCharFromFirstTwoByteSequence(uint value) |
61 | 60 | } |
62 | 61 |
|
63 | 62 | /// <summary> |
64 | | - /// Given a machine-endian DWORD which four bytes of UTF-8 data, interprets the input as a |
| 63 | + /// Given a machine-endian DWORD which represents four bytes of UTF-8 data, interprets the input as a |
65 | 64 | /// four-byte UTF-8 sequence and returns the machine-endian DWORD of the UTF-16 representation. |
66 | 65 | /// </summary> |
67 | 66 | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
68 | 67 | private static uint ExtractCharsFromFourByteSequence(uint value) |
69 | 68 | { |
70 | 69 | if (BitConverter.IsLittleEndian) |
71 | 70 | { |
72 | | - if (Bmi2.IsSupported) |
73 | | - { |
74 | | - // need to reverse endianness for bit manipulation to work correctly |
75 | | - value = BinaryPrimitives.ReverseEndianness(value); |
76 | | - |
77 | | - // value = [ 11110uuu 10uuzzzz 10yyyyyy 10xxxxxx ] |
78 | | - // want to return [ 110110wwwwxxxxxx 110111xxxxxxxxxx ] |
79 | | - // where wwww = uuuuu - 1 |
80 | | - |
81 | | - uint highSurrogateChar = Bmi2.ParallelBitExtract(value, 0b00000111_00111111_00110000_00000000u); |
82 | | - uint lowSurrogateChar = Bmi2.ParallelBitExtract(value, 0b00000000_00000000_00001111_00111111u); |
83 | | - |
84 | | - uint combined = (lowSurrogateChar << 16) + highSurrogateChar; |
85 | | - combined -= 0x40u; // wwww = uuuuu - 1 |
86 | | - combined += 0xDC00_D800u; // add surrogate markers |
87 | | - return combined; |
88 | | - } |
89 | | - else |
90 | | - { |
91 | | - // input is UTF8 [ 10xxxxxx 10yyyyyy 10uuzzzz 11110uuu ] = scalar 000uuuuu zzzzyyyy yyxxxxxx |
92 | | - // want to return UTF16 scalar 000uuuuuzzzzyyyyyyxxxxxx = [ 110111yy yyxxxxxx 110110ww wwzzzzyy ] |
93 | | - // where wwww = uuuuu - 1 |
94 | | - uint retVal = (uint)(byte)value << 8; // retVal = [ 00000000 00000000 11110uuu 00000000 ] |
95 | | - retVal |= (value & 0x0000_3F00u) >> 6; // retVal = [ 00000000 00000000 11110uuu uuzzzz00 ] |
96 | | - retVal |= (value & 0x0030_0000u) >> 20; // retVal = [ 00000000 00000000 11110uuu uuzzzzyy ] |
97 | | - retVal |= (value & 0x3F00_0000u) >> 8; // retVal = [ 00000000 00xxxxxx 11110uuu uuzzzzyy ] |
98 | | - retVal |= (value & 0x000F_0000u) << 6; // retVal = [ 000000yy yyxxxxxx 11110uuu uuzzzzyy ] |
99 | | - retVal -= 0x0000_0040u; // retVal = [ 000000yy yyxxxxxx 111100ww wwzzzzyy ] |
100 | | - retVal -= 0x0000_2000u; // retVal = [ 000000yy yyxxxxxx 110100ww wwzzzzyy ] |
101 | | - retVal += 0x0000_0800u; // retVal = [ 000000yy yyxxxxxx 110110ww wwzzzzyy ] |
102 | | - retVal += 0xDC00_0000u; // retVal = [ 110111yy yyxxxxxx 110110ww wwzzzzyy ] |
103 | | - return retVal; |
104 | | - } |
| 71 | + // input is UTF8 [ 10xxxxxx 10yyyyyy 10uuzzzz 11110uuu ] = scalar 000uuuuu zzzzyyyy yyxxxxxx |
| 72 | + // want to return UTF16 scalar 000uuuuuzzzzyyyyyyxxxxxx = [ 110111yy yyxxxxxx 110110ww wwzzzzyy ] |
| 73 | + // where wwww = uuuuu - 1 |
| 74 | + uint retVal = (uint)(byte)value << 8; // retVal = [ 00000000 00000000 11110uuu 00000000 ] |
| 75 | + retVal |= (value & 0x0000_3F00u) >> 6; // retVal = [ 00000000 00000000 11110uuu uuzzzz00 ] |
| 76 | + retVal |= (value & 0x0030_0000u) >> 20; // retVal = [ 00000000 00000000 11110uuu uuzzzzyy ] |
| 77 | + retVal |= (value & 0x3F00_0000u) >> 8; // retVal = [ 00000000 00xxxxxx 11110uuu uuzzzzyy ] |
| 78 | + retVal |= (value & 0x000F_0000u) << 6; // retVal = [ 000000yy yyxxxxxx 11110uuu uuzzzzyy ] |
| 79 | + retVal -= 0x0000_0040u; // retVal = [ 000000yy yyxxxxxx 111100ww wwzzzzyy ] |
| 80 | + retVal -= 0x0000_2000u; // retVal = [ 000000yy yyxxxxxx 110100ww wwzzzzyy ] |
| 81 | + retVal += 0x0000_0800u; // retVal = [ 000000yy yyxxxxxx 110110ww wwzzzzyy ] |
| 82 | + retVal += 0xDC00_0000u; // retVal = [ 110111yy yyxxxxxx 110110ww wwzzzzyy ] |
| 83 | + return retVal; |
105 | 84 | } |
106 | 85 | else |
107 | 86 | { |
@@ -135,37 +114,19 @@ private static uint ExtractFourUtf8BytesFromSurrogatePair(uint value) |
135 | 114 | // input = [ 110111yyyyxxxxxx 110110wwwwzzzzyy ] = scalar (000uuuuu zzzzyyyy yyxxxxxx) |
136 | 115 | // must return [ 10xxxxxx 10yyyyyy 10uuzzzz 11110uuu ], where wwww = uuuuu - 1 |
137 | 116 |
|
138 | | - if (Bmi2.IsSupported) |
139 | | - { |
140 | | - // Since pdep and pext have high latencies and can only be dispatched to a single execution port, we want |
141 | | - // to use them conservatively. Here, we'll build up the scalar value (this would normally be pext) via simple |
142 | | - // logical and arithmetic operations, and use only pdep for the expensive step of exploding the scalar across |
143 | | - // all four output bytes. |
144 | | - |
145 | | - uint unmaskedScalar = (value << 10) + (value >> 16) + (0x40u << 10) /* uuuuu = wwww + 1 */ - 0xDC00u /* remove low surrogate marker */; |
146 | | - |
147 | | - // Now, unmaskedScalar = [ xxxxxx11 011uuuuu zzzzyyyy yyxxxxxx ]. There's a bit of unneeded junk at the beginning |
148 | | - // that should normally be masked out via an and, but we'll just direct pdep to ignore it. |
| 117 | + value += 0x0000_0040u; // = [ 110111yyyyxxxxxx 11011uuuuuzzzzyy ] |
149 | 118 |
|
150 | | - uint exploded = Bmi2.ParallelBitDeposit(unmaskedScalar, 0b00000111_00111111_00111111_00111111u); // = [ 00000uuu 00uuzzzz 00yyyyyy 00xxxxxx ] |
151 | | - return BinaryPrimitives.ReverseEndianness(exploded + 0xF080_8080u); // = [ 10xxxxxx 10yyyyyy 10uuzzzz 11110uuu ] |
152 | | - } |
153 | | - else |
154 | | - { |
155 | | - value += 0x0000_0040u; // = [ 110111yyyyxxxxxx 11011uuuuuzzzzyy ] |
| 119 | + uint tempA = BinaryPrimitives.ReverseEndianness(value & 0x003F_0700u); // = [ 00000000 00000uuu 00xxxxxx 00000000 ] |
| 120 | + tempA = BitOperations.RotateLeft(tempA, 16); // = [ 00xxxxxx 00000000 00000000 00000uuu ] |
156 | 121 |
|
157 | | - uint tempA = BinaryPrimitives.ReverseEndianness(value & 0x003F_0700u); // = [ 00000000 00000uuu 00xxxxxx 00000000 ] |
158 | | - tempA = BitOperations.RotateLeft(tempA, 16); // = [ 00xxxxxx 00000000 00000000 00000uuu ] |
| 122 | + uint tempB = (value & 0x00FCu) << 6; // = [ 00000000 00000000 00uuzzzz 00000000 ] |
| 123 | + uint tempC = (value >> 6) & 0x000F_0000u; // = [ 00000000 0000yyyy 00000000 00000000 ] |
| 124 | + tempC |= tempB; |
159 | 125 |
|
160 | | - uint tempB = (value & 0x00FCu) << 6; // = [ 00000000 00000000 00uuzzzz 00000000 ] |
161 | | - uint tempC = (value >> 6) & 0x000F_0000u; // = [ 00000000 0000yyyy 00000000 00000000 ] |
162 | | - tempC |= tempB; |
| 126 | + uint tempD = (value & 0x03u) << 20; // = [ 00000000 00yy0000 00000000 00000000 ] |
| 127 | + tempD |= 0x8080_80F0u; |
163 | 128 |
|
164 | | - uint tempD = (value & 0x03u) << 20; // = [ 00000000 00yy0000 00000000 00000000 ] |
165 | | - tempD |= 0x8080_80F0u; |
166 | | - |
167 | | - return tempD | tempA | tempC; // = [ 10xxxxxx 10yyyyyy 10uuzzzz 11110uuu ] |
168 | | - } |
| 129 | + return tempD | tempA | tempC; // = [ 10xxxxxx 10yyyyyy 10uuzzzz 11110uuu ] |
169 | 130 | } |
170 | 131 | else |
171 | 132 | { |
@@ -756,43 +717,6 @@ private static bool UInt32ThirdByteIsAscii(uint value) |
756 | 717 | || (!BitConverter.IsLittleEndian && ((value & 0x8000u) == 0)); |
757 | 718 | } |
758 | 719 |
|
759 | | - /// <summary> |
760 | | - /// Given a DWORD which represents a buffer of 4 ASCII bytes, widen each byte to a 16-bit WORD |
761 | | - /// and writes the resulting QWORD into the destination with machine endianness. |
762 | | - /// </summary> |
763 | | - [MethodImpl(MethodImplOptions.AggressiveInlining)] |
764 | | - private static void Widen4AsciiBytesToCharsAndWrite(ref char outputBuffer, uint value) |
765 | | - { |
766 | | - if (Bmi2.X64.IsSupported) |
767 | | - { |
768 | | - // BMI2 will work regardless of the processor's endianness. |
769 | | - Unsafe.WriteUnaligned(ref Unsafe.As<char, byte>(ref outputBuffer), Bmi2.X64.ParallelBitDeposit(value, 0x00FF00FF_00FF00FFul)); |
770 | | - } |
771 | | - else |
772 | | - { |
773 | | - if (BitConverter.IsLittleEndian) |
774 | | - { |
775 | | - outputBuffer = (char)(byte)value; |
776 | | - value >>= 8; |
777 | | - Unsafe.Add(ref outputBuffer, 1) = (char)(byte)value; |
778 | | - value >>= 8; |
779 | | - Unsafe.Add(ref outputBuffer, 2) = (char)(byte)value; |
780 | | - value >>= 8; |
781 | | - Unsafe.Add(ref outputBuffer, 3) = (char)value; |
782 | | - } |
783 | | - else |
784 | | - { |
785 | | - Unsafe.Add(ref outputBuffer, 3) = (char)(byte)value; |
786 | | - value >>= 8; |
787 | | - Unsafe.Add(ref outputBuffer, 2) = (char)(byte)value; |
788 | | - value >>= 8; |
789 | | - Unsafe.Add(ref outputBuffer, 1) = (char)(byte)value; |
790 | | - value >>= 8; |
791 | | - outputBuffer = (char)value; |
792 | | - } |
793 | | - } |
794 | | - } |
795 | | - |
796 | 720 | /// <summary> |
797 | 721 | /// Given a DWORD which represents a buffer of 2 packed UTF-16 values in machine endianess, |
798 | 722 | /// converts those scalar values to their 3-byte UTF-8 representation and writes the |
|
0 commit comments