|
1 | 1 | // Copyright (c) .NET Foundation. All rights reserved.
|
2 | 2 | // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
3 | 3 |
|
4 |
| -using System; |
5 |
| -using System.Buffers; |
6 | 4 | using System.Diagnostics;
|
7 | 5 | using System.IO.Pipelines;
|
8 | 6 | using System.Runtime.CompilerServices;
|
9 | 7 | using System.Runtime.InteropServices;
|
10 |
| -using System.Runtime.Intrinsics.X86; |
| 8 | +using System.Text; |
11 | 9 |
|
12 | 10 | namespace System.Buffers
|
13 | 11 | {
|
@@ -42,27 +40,26 @@ public static ArraySegment<byte> GetArray(this ReadOnlyMemory<byte> memory)
|
42 | 40 | return result;
|
43 | 41 | }
|
44 | 42 |
|
45 |
| - internal static unsafe void WriteAsciiNoValidation(ref this BufferWriter<PipeWriter> buffer, string data) |
| 43 | + internal static unsafe void WriteAscii(ref this BufferWriter<PipeWriter> buffer, string data) |
46 | 44 | {
|
47 | 45 | if (string.IsNullOrEmpty(data))
|
48 | 46 | {
|
49 | 47 | return;
|
50 | 48 | }
|
51 | 49 |
|
52 |
| - var dest = buffer.Span; |
53 |
| - var destLength = dest.Length; |
54 |
| - var sourceLength = data.Length; |
| 50 | + var dataLength = data.Length; |
| 51 | + var bytes = buffer.Span; |
| 52 | + var bytesLength = bytes.Length; |
55 | 53 |
|
56 |
| - // Fast path, try copying to the available memory directly |
57 |
| - if (sourceLength <= destLength) |
| 54 | + // Fast path, try encoding to the available memory directly |
| 55 | + if (dataLength <= bytesLength) |
58 | 56 | {
|
59 |
| - fixed (char* input = data) |
60 |
| - fixed (byte* output = dest) |
| 57 | + fixed (char* charsPtr = data) |
| 58 | + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) |
61 | 59 | {
|
62 |
| - EncodeAsciiCharsToBytes(input, output, sourceLength); |
| 60 | + Encoding.ASCII.GetBytes(charsPtr, dataLength, bytesPtr, bytesLength); |
| 61 | + buffer.Advance(dataLength); |
63 | 62 | }
|
64 |
| - |
65 |
| - buffer.Advance(sourceLength); |
66 | 63 | }
|
67 | 64 | else
|
68 | 65 | {
|
@@ -144,170 +141,43 @@ private static void WriteNumericMultiWrite(ref this BufferWriter<PipeWriter> buf
|
144 | 141 | [MethodImpl(MethodImplOptions.NoInlining)]
|
145 | 142 | private unsafe static void WriteAsciiMultiWrite(ref this BufferWriter<PipeWriter> buffer, string data)
|
146 | 143 | {
|
147 |
| - var remaining = data.Length; |
148 |
| - |
149 |
| - fixed (char* input = data) |
150 |
| - { |
151 |
| - var inputSlice = input; |
152 |
| - |
153 |
| - while (remaining > 0) |
154 |
| - { |
155 |
| - var writable = Math.Min(remaining, buffer.Span.Length); |
156 |
| - |
157 |
| - if (writable == 0) |
158 |
| - { |
159 |
| - buffer.Ensure(); |
160 |
| - continue; |
161 |
| - } |
162 |
| - |
163 |
| - fixed (byte* output = buffer.Span) |
164 |
| - { |
165 |
| - EncodeAsciiCharsToBytes(inputSlice, output, writable); |
166 |
| - } |
167 |
| - |
168 |
| - inputSlice += writable; |
169 |
| - remaining -= writable; |
170 |
| - |
171 |
| - buffer.Advance(writable); |
172 |
| - } |
173 |
| - } |
174 |
| - } |
175 |
| - |
176 |
| - private static unsafe void EncodeAsciiCharsToBytes(char* input, byte* output, int length) |
177 |
| - { |
178 |
| - // Note: Not BIGENDIAN or check for non-ascii |
179 |
| - if (Bmi2.IsSupported) |
180 |
| - { |
181 |
| - if (length < 4) |
182 |
| - { |
183 |
| - // Convert the chars to bytes one by one if there are less than 4. |
184 |
| - for (int i = 0; i < length; i++) |
185 |
| - { |
186 |
| - char ch = input[i]; |
187 |
| - output[i] = (byte)ch; // Cast convert |
188 |
| - } |
189 |
| - } |
190 |
| - else if (Bmi2.X64.IsSupported) // 64-bit, 4+ chars |
191 |
| - { |
192 |
| - // Convert all the 4 char sequences, except final 1 - 4 char sequence. |
193 |
| - int firstLength = length - sizeof(int); |
194 |
| - Debug.Assert(firstLength >= 0); |
195 |
| - for (int i = 0; i < firstLength; i += sizeof(int)) |
196 |
| - { |
197 |
| - *(uint*)(output + i) = (uint)Bmi2.X64.ParallelBitExtract( |
198 |
| - *(ulong*)(input + i), |
199 |
| - 0x00FF00FF_00FF00FFul); |
200 |
| - } |
| 144 | + Debug.Assert(!string.IsNullOrEmpty(data)); |
201 | 145 |
|
202 |
| - // Convert the final sequence of 4 from the end. |
203 |
| - // This may overlap with the last sequence of the loop, if length is not a multiple of 4. |
204 |
| - *(uint*)(output + firstLength) = (uint)Bmi2.X64.ParallelBitExtract( |
205 |
| - *(ulong*)(input + firstLength), |
206 |
| - 0x00FF00FF_00FF00FFul); |
207 |
| - } |
208 |
| - else // 32-bit, 4+ chars |
209 |
| - { |
210 |
| - // Convert all the 2 char sequences, except final 1 - 2 char sequence |
211 |
| - int firstLength = length - sizeof(ushort); |
212 |
| - Debug.Assert(firstLength >= 0); |
213 |
| - for (int i = 0; i < firstLength; i += sizeof(ushort)) |
214 |
| - { |
215 |
| - *(ushort*)(output + i) = (ushort)Bmi2.ParallelBitExtract( |
216 |
| - *(uint*)(input + i), |
217 |
| - 0x00FF00FFu); |
218 |
| - } |
219 |
| - |
220 |
| - // Convert the final sequence of 2 from the end. |
221 |
| - // This may overlap with the last sequence of the loop, if length is not a multiple of 2 |
222 |
| - *(ushort*)(output + firstLength) = (ushort)Bmi2.ParallelBitExtract( |
223 |
| - *(uint*)(input + firstLength), |
224 |
| - 0x00FF00FFu); |
225 |
| - } |
226 |
| - } |
227 |
| - else |
| 146 | + fixed (char* charsPtr = data) |
228 | 147 | {
|
229 |
| - const int Shift16Shift24 = (1 << 16) | (1 << 24); |
230 |
| - const int Shift8Identity = (1 << 8) | (1); |
231 |
| - |
232 |
| - int i = 0; |
233 |
| - // Use Intrinsic switch |
234 |
| - if (IntPtr.Size == 8) // 64 bit |
| 148 | + var dataLength = data.Length; |
| 149 | + var offset = 0; |
| 150 | + var bytes = buffer.Span; |
| 151 | + var bytesLength = bytes.Length; |
| 152 | + do |
235 | 153 | {
|
236 |
| - if (length < 4) goto trailing; |
237 |
| - |
238 |
| - int unaligned = (int)(((ulong)input) & 0x7) >> 1; |
239 |
| - // Unaligned chars |
240 |
| - for (; i < unaligned; i++) |
241 |
| - { |
242 |
| - char ch = input[i]; |
243 |
| - output[i] = (byte)ch; // Cast convert |
244 |
| - } |
245 |
| - |
246 |
| - // Aligned |
247 |
| - int ulongDoubleCount = (length - i) & ~0x7; |
248 |
| - for (; i < ulongDoubleCount; i += 8) |
| 154 | + var writable = Math.Min(dataLength - offset, bytesLength); |
| 155 | + // Zero length spans are possible |
| 156 | + if (writable > 0) |
249 | 157 | {
|
250 |
| - ulong inputUlong0 = *(ulong*)(input + i); |
251 |
| - ulong inputUlong1 = *(ulong*)(input + i + 4); |
252 |
| - // Pack 16 ASCII chars into 16 bytes |
253 |
| - *(uint*)(output + i) = |
254 |
| - ((uint)((inputUlong0 * Shift16Shift24) >> 24) & 0xffff) | |
255 |
| - ((uint)((inputUlong0 * Shift8Identity) >> 24) & 0xffff0000); |
256 |
| - *(uint*)(output + i + 4) = |
257 |
| - ((uint)((inputUlong1 * Shift16Shift24) >> 24) & 0xffff) | |
258 |
| - ((uint)((inputUlong1 * Shift8Identity) >> 24) & 0xffff0000); |
259 |
| - } |
260 |
| - if (length - 4 > i) |
261 |
| - { |
262 |
| - ulong inputUlong = *(ulong*)(input + i); |
263 |
| - // Pack 8 ASCII chars into 8 bytes |
264 |
| - *(uint*)(output + i) = |
265 |
| - ((uint)((inputUlong * Shift16Shift24) >> 24) & 0xffff) | |
266 |
| - ((uint)((inputUlong * Shift8Identity) >> 24) & 0xffff0000); |
267 |
| - i += 4; |
268 |
| - } |
| 158 | + fixed (byte* bytesPtr = &MemoryMarshal.GetReference(bytes)) |
| 159 | + { |
| 160 | + Encoding.ASCII.GetBytes(charsPtr + offset, writable, bytesPtr, bytesLength); |
269 | 161 |
|
270 |
| - trailing: |
271 |
| - for (; i < length; i++) |
272 |
| - { |
273 |
| - char ch = input[i]; |
274 |
| - output[i] = (byte)ch; // Cast convert |
275 |
| - } |
276 |
| - } |
277 |
| - else // 32 bit |
278 |
| - { |
279 |
| - // Unaligned chars |
280 |
| - if ((unchecked((int)input) & 0x2) != 0) |
281 |
| - { |
282 |
| - char ch = *input; |
283 |
| - i = 1; |
284 |
| - output[0] = (byte)ch; // Cast convert |
| 162 | + buffer.Advance(writable); |
| 163 | + offset += writable; |
| 164 | + } |
285 | 165 | }
|
286 | 166 |
|
287 |
| - // Aligned |
288 |
| - int uintCount = (length - i) & ~0x3; |
289 |
| - for (; i < uintCount; i += 4) |
290 |
| - { |
291 |
| - uint inputUint0 = *(uint*)(input + i); |
292 |
| - uint inputUint1 = *(uint*)(input + i + 2); |
293 |
| - // Pack 4 ASCII chars into 4 bytes |
294 |
| - *(ushort*)(output + i) = (ushort)(inputUint0 | (inputUint0 >> 8)); |
295 |
| - *(ushort*)(output + i + 2) = (ushort)(inputUint1 | (inputUint1 >> 8)); |
296 |
| - } |
297 |
| - if (length - 1 > i) |
| 167 | + // Get new span if more to encode, and reset bytesLength |
| 168 | + if (offset < dataLength) |
298 | 169 | {
|
299 |
| - uint inputUint = *(uint*)(input + i); |
300 |
| - // Pack 2 ASCII chars into 2 bytes |
301 |
| - *(ushort*)(output + i) = (ushort)(inputUint | (inputUint >> 8)); |
302 |
| - i += 2; |
| 170 | + buffer.Ensure(); |
| 171 | + bytes = buffer.Span; |
| 172 | + bytesLength = bytes.Length; |
| 173 | + continue; |
303 | 174 | }
|
304 |
| - |
305 |
| - if (i < length) |
| 175 | + else |
306 | 176 | {
|
307 |
| - char ch = input[i]; |
308 |
| - output[i] = (byte)ch; // Cast convert |
| 177 | + // Encoded everything |
| 178 | + break; |
309 | 179 | }
|
310 |
| - } |
| 180 | + } while (true); |
311 | 181 | }
|
312 | 182 | }
|
313 | 183 |
|
|
0 commit comments