|
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; |
| 4 | +using System.Diagnostics; |
6 | 5 | using System.IO.Pipelines;
|
7 | 6 | using System.Runtime.CompilerServices;
|
8 | 7 | using System.Runtime.InteropServices;
|
| 8 | +using System.Text; |
9 | 9 |
|
10 | 10 | namespace System.Buffers
|
11 | 11 | {
|
@@ -40,26 +40,19 @@ public static ArraySegment<byte> GetArray(this ReadOnlyMemory<byte> memory)
|
40 | 40 | return result;
|
41 | 41 | }
|
42 | 42 |
|
43 |
| - internal static unsafe void WriteAsciiNoValidation(ref this BufferWriter<PipeWriter> buffer, string data) |
| 43 | + internal static void WriteAscii(ref this BufferWriter<PipeWriter> buffer, string data) |
44 | 44 | {
|
45 | 45 | if (string.IsNullOrEmpty(data))
|
46 | 46 | {
|
47 | 47 | return;
|
48 | 48 | }
|
49 | 49 |
|
50 | 50 | var dest = buffer.Span;
|
51 |
| - var destLength = dest.Length; |
52 | 51 | var sourceLength = data.Length;
|
53 |
| - |
54 |
| - // Fast path, try copying to the available memory directly |
55 |
| - if (sourceLength <= destLength) |
| 52 | + // Fast path, try encoding to the available memory directly |
| 53 | + if (sourceLength <= dest.Length) |
56 | 54 | {
|
57 |
| - fixed (char* input = data) |
58 |
| - fixed (byte* output = dest) |
59 |
| - { |
60 |
| - EncodeAsciiCharsToBytes(input, output, sourceLength); |
61 |
| - } |
62 |
| - |
| 55 | + Encoding.ASCII.GetBytes(data, dest); |
63 | 56 | buffer.Advance(sourceLength);
|
64 | 57 | }
|
65 | 58 | else
|
@@ -140,123 +133,31 @@ private static void WriteNumericMultiWrite(ref this BufferWriter<PipeWriter> buf
|
140 | 133 | }
|
141 | 134 |
|
142 | 135 | [MethodImpl(MethodImplOptions.NoInlining)]
|
143 |
| - private unsafe static void WriteAsciiMultiWrite(ref this BufferWriter<PipeWriter> buffer, string data) |
| 136 | + private static void WriteAsciiMultiWrite(ref this BufferWriter<PipeWriter> buffer, string data) |
144 | 137 | {
|
145 |
| - var remaining = data.Length; |
146 |
| - |
147 |
| - fixed (char* input = data) |
148 |
| - { |
149 |
| - var inputSlice = input; |
150 |
| - |
151 |
| - while (remaining > 0) |
152 |
| - { |
153 |
| - var writable = Math.Min(remaining, buffer.Span.Length); |
154 |
| - |
155 |
| - if (writable == 0) |
156 |
| - { |
157 |
| - buffer.Ensure(); |
158 |
| - continue; |
159 |
| - } |
160 |
| - |
161 |
| - fixed (byte* output = buffer.Span) |
162 |
| - { |
163 |
| - EncodeAsciiCharsToBytes(inputSlice, output, writable); |
164 |
| - } |
165 |
| - |
166 |
| - inputSlice += writable; |
167 |
| - remaining -= writable; |
168 |
| - |
169 |
| - buffer.Advance(writable); |
170 |
| - } |
171 |
| - } |
172 |
| - } |
173 |
| - |
174 |
| - private static unsafe void EncodeAsciiCharsToBytes(char* input, byte* output, int length) |
175 |
| - { |
176 |
| - // Note: Not BIGENDIAN or check for non-ascii |
177 |
| - const int Shift16Shift24 = (1 << 16) | (1 << 24); |
178 |
| - const int Shift8Identity = (1 << 8) | (1); |
179 |
| - |
180 |
| - // Encode as bytes up to the first non-ASCII byte and return count encoded |
181 |
| - int i = 0; |
182 |
| - // Use Intrinsic switch |
183 |
| - if (IntPtr.Size == 8) // 64 bit |
184 |
| - { |
185 |
| - if (length < 4) goto trailing; |
186 |
| - |
187 |
| - int unaligned = (int)(((ulong)input) & 0x7) >> 1; |
188 |
| - // Unaligned chars |
189 |
| - for (; i < unaligned; i++) |
190 |
| - { |
191 |
| - char ch = *(input + i); |
192 |
| - *(output + i) = (byte)ch; // Cast convert |
193 |
| - } |
194 |
| - |
195 |
| - // Aligned |
196 |
| - int ulongDoubleCount = (length - i) & ~0x7; |
197 |
| - for (; i < ulongDoubleCount; i += 8) |
198 |
| - { |
199 |
| - ulong inputUlong0 = *(ulong*)(input + i); |
200 |
| - ulong inputUlong1 = *(ulong*)(input + i + 4); |
201 |
| - // Pack 16 ASCII chars into 16 bytes |
202 |
| - *(uint*)(output + i) = |
203 |
| - ((uint)((inputUlong0 * Shift16Shift24) >> 24) & 0xffff) | |
204 |
| - ((uint)((inputUlong0 * Shift8Identity) >> 24) & 0xffff0000); |
205 |
| - *(uint*)(output + i + 4) = |
206 |
| - ((uint)((inputUlong1 * Shift16Shift24) >> 24) & 0xffff) | |
207 |
| - ((uint)((inputUlong1 * Shift8Identity) >> 24) & 0xffff0000); |
208 |
| - } |
209 |
| - if (length - 4 > i) |
210 |
| - { |
211 |
| - ulong inputUlong = *(ulong*)(input + i); |
212 |
| - // Pack 8 ASCII chars into 8 bytes |
213 |
| - *(uint*)(output + i) = |
214 |
| - ((uint)((inputUlong * Shift16Shift24) >> 24) & 0xffff) | |
215 |
| - ((uint)((inputUlong * Shift8Identity) >> 24) & 0xffff0000); |
216 |
| - i += 4; |
217 |
| - } |
218 |
| - |
219 |
| - trailing: |
220 |
| - for (; i < length; i++) |
221 |
| - { |
222 |
| - char ch = *(input + i); |
223 |
| - *(output + i) = (byte)ch; // Cast convert |
224 |
| - } |
225 |
| - } |
226 |
| - else // 32 bit |
| 138 | + var dataLength = data.Length; |
| 139 | + var offset = 0; |
| 140 | + var bytes = buffer.Span; |
| 141 | + do |
227 | 142 | {
|
228 |
| - // Unaligned chars |
229 |
| - if ((unchecked((int)input) & 0x2) != 0) |
230 |
| - { |
231 |
| - char ch = *input; |
232 |
| - i = 1; |
233 |
| - *(output) = (byte)ch; // Cast convert |
234 |
| - } |
235 |
| - |
236 |
| - // Aligned |
237 |
| - int uintCount = (length - i) & ~0x3; |
238 |
| - for (; i < uintCount; i += 4) |
| 143 | + var writable = Math.Min(dataLength - offset, bytes.Length); |
| 144 | + // Zero length spans are possible, though unlikely. |
| 145 | + // ASCII.GetBytes and .Advance will both handle them so we won't special case for them. |
| 146 | + Encoding.ASCII.GetBytes(data.AsSpan(offset, writable), bytes); |
| 147 | + buffer.Advance(writable); |
| 148 | + |
| 149 | + offset += writable; |
| 150 | + if (offset >= dataLength) |
239 | 151 | {
|
240 |
| - uint inputUint0 = *(uint*)(input + i); |
241 |
| - uint inputUint1 = *(uint*)(input + i + 2); |
242 |
| - // Pack 4 ASCII chars into 4 bytes |
243 |
| - *(ushort*)(output + i) = (ushort)(inputUint0 | (inputUint0 >> 8)); |
244 |
| - *(ushort*)(output + i + 2) = (ushort)(inputUint1 | (inputUint1 >> 8)); |
245 |
| - } |
246 |
| - if (length - 1 > i) |
247 |
| - { |
248 |
| - uint inputUint = *(uint*)(input + i); |
249 |
| - // Pack 2 ASCII chars into 2 bytes |
250 |
| - *(ushort*)(output + i) = (ushort)(inputUint | (inputUint >> 8)); |
251 |
| - i += 2; |
| 152 | + Debug.Assert(offset == dataLength); |
| 153 | + // Encoded everything |
| 154 | + break; |
252 | 155 | }
|
253 | 156 |
|
254 |
| - if (i < length) |
255 |
| - { |
256 |
| - char ch = *(input + i); |
257 |
| - *(output + i) = (byte)ch; // Cast convert |
258 |
| - } |
259 |
| - } |
| 157 | + // Get new span, more to encode. |
| 158 | + buffer.Ensure(); |
| 159 | + bytes = buffer.Span; |
| 160 | + } while (true); |
260 | 161 | }
|
261 | 162 |
|
262 | 163 | private static byte[] NumericBytesScratch => _numericBytesScratch ?? CreateNumericBytesScratch();
|
|
0 commit comments