1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
5 #if AMD64 || ARM64 || (BIT32 && !ARM)
6 #define HAS_CUSTOM_BLOCKS
11 //Only contains static methods. Does not require serialization
14 using System.Runtime.CompilerServices;
15 using System.Runtime.ConstrainedExecution;
16 using System.Runtime.InteropServices;
17 using System.Runtime.Versioning;
18 using System.Diagnostics;
19 using System.Security;
23 using nuint = System.UInt64;
25 using nuint = System.UInt32;
28 public static class Buffer
30 // Copies from one primitive array to another primitive array without
31 // respecting types. This calls memmove internally. The count and
32 // offset parameters here are in bytes. If you want to use traditional
33 // array element indices and counts, use Array.Copy.
34 [MethodImplAttribute(MethodImplOptions.InternalCall)]
35 public static extern void BlockCopy(Array src, int srcOffset,
36 Array dst, int dstOffset, int count);
38 // This is ported from the optimized CRT assembly in memchr.asm. The JIT generates
39 // pretty good code here and this ends up being within a couple % of the CRT asm.
40 // It is however cross platform as the CRT hasn't ported their fast version to 64-bit
43 internal unsafe static int IndexOfByte(byte* src, byte value, int index, int count)
45 Debug.Assert(src != null, "src should not be null");
47 byte* pByte = src + index;
49 // Align up the pointer to sizeof(int).
50 while (((int)pByte & 3) != 0)
54 else if (*pByte == value)
55 return (int)(pByte - src);
61 // Fill comparer with value byte for comparisons
63 // comparer = 0/0/value/value
64 uint comparer = (((uint)value << 8) + (uint)value);
65 // comparer = value/value/value/value
66 comparer = (comparer << 16) + comparer;
68 // Run through buffer until we hit a 4-byte section which contains
69 // the byte we're looking for or until we exhaust the buffer.
72 // Test the buffer for presence of value. comparer contains the byte
73 // replicated 4 times.
74 uint t1 = *(uint*)pByte;
76 uint t2 = 0x7efefeff + t1;
81 // if t1 is zero then these 4-bytes don't contain a match
84 // We've found a match for value, figure out which position it's in.
85 int foundIndex = (int)(pByte - src);
86 if (pByte[0] == value)
88 else if (pByte[1] == value)
89 return foundIndex + 1;
90 else if (pByte[2] == value)
91 return foundIndex + 2;
92 else if (pByte[3] == value)
93 return foundIndex + 3;
100 // Catch any bytes that might be left at the tail of the buffer
104 return (int)(pByte - src);
110 // If we don't have a match return -1;
114 // Returns a bool to indicate if the array is of primitive data types
116 [MethodImplAttribute(MethodImplOptions.InternalCall)]
117 private static extern bool IsPrimitiveTypeArray(Array array);
119 // Gets a particular byte out of the array. The array must be an
120 // array of primitives.
122 // This essentially does the following:
123 // return ((byte*)array) + index.
125 [MethodImplAttribute(MethodImplOptions.InternalCall)]
126 private static extern byte _GetByte(Array array, int index);
128 public static byte GetByte(Array array, int index)
130 // Is the array present?
132 throw new ArgumentNullException(nameof(array));
134 // Is it of primitive types?
135 if (!IsPrimitiveTypeArray(array))
136 throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(array));
138 // Is the index in valid range of the array?
139 if (index < 0 || index >= _ByteLength(array))
140 throw new ArgumentOutOfRangeException(nameof(index));
142 return _GetByte(array, index);
145 // Sets a particular byte in an the array. The array must be an
146 // array of primitives.
148 // This essentially does the following:
149 // *(((byte*)array) + index) = value.
151 [MethodImplAttribute(MethodImplOptions.InternalCall)]
152 private static extern void _SetByte(Array array, int index, byte value);
154 public static void SetByte(Array array, int index, byte value)
156 // Is the array present?
158 throw new ArgumentNullException(nameof(array));
160 // Is it of primitive types?
161 if (!IsPrimitiveTypeArray(array))
162 throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(array));
164 // Is the index in valid range of the array?
165 if (index < 0 || index >= _ByteLength(array))
166 throw new ArgumentOutOfRangeException(nameof(index));
168 // Make the FCall to do the work
169 _SetByte(array, index, value);
173 // Gets a particular byte out of the array. The array must be an
174 // array of primitives.
176 // This essentially does the following:
177 // return array.length * sizeof(array.UnderlyingElementType).
179 [MethodImplAttribute(MethodImplOptions.InternalCall)]
180 private static extern int _ByteLength(Array array);
182 public static int ByteLength(Array array)
184 // Is the array present?
186 throw new ArgumentNullException(nameof(array));
188 // Is it of primitive types?
189 if (!IsPrimitiveTypeArray(array))
190 throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(array));
192 return _ByteLength(array);
195 internal unsafe static void ZeroMemory(byte* src, long len)
201 internal unsafe static void Memcpy(byte[] dest, int destIndex, byte* src, int srcIndex, int len)
203 Debug.Assert((srcIndex >= 0) && (destIndex >= 0) && (len >= 0), "Index and length must be non-negative!");
204 Debug.Assert(dest.Length - destIndex >= len, "not enough bytes in dest");
205 // If dest has 0 elements, the fixed statement will throw an
206 // IndexOutOfRangeException. Special-case 0-byte copies.
209 fixed (byte* pDest = dest)
211 Memcpy(pDest + destIndex, src + srcIndex, len);
215 internal unsafe static void Memcpy(byte* pDest, int destIndex, byte[] src, int srcIndex, int len)
217 Debug.Assert((srcIndex >= 0) && (destIndex >= 0) && (len >= 0), "Index and length must be non-negative!");
218 Debug.Assert(src.Length - srcIndex >= len, "not enough bytes in src");
219 // If dest has 0 elements, the fixed statement will throw an
220 // IndexOutOfRangeException. Special-case 0-byte copies.
223 fixed (byte* pSrc = src)
225 Memcpy(pDest + destIndex, pSrc + srcIndex, len);
229 // This is tricky to get right AND fast, so lets make it useful for the whole Fx.
230 // E.g. System.Runtime.WindowsRuntime!WindowsRuntimeBufferExtensions.MemCopy uses it.
232 // This method has a slightly different behavior on arm and other platforms.
233 // On arm this method behaves like memcpy and does not handle overlapping buffers.
234 // While on other platforms it behaves like memmove and handles overlapping buffers.
235 // This behavioral difference is unfortunate but intentional because
236 // 1. This method is given access to other internal dlls and this close to release we do not want to change it.
237 // 2. It is difficult to get this right for arm and again due to release dates we would like to visit it later.
239 [MethodImplAttribute(MethodImplOptions.InternalCall)]
240 internal unsafe static extern void Memcpy(byte* dest, byte* src, int len);
242 [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
243 internal unsafe static void Memcpy(byte* dest, byte* src, int len)
245 Debug.Assert(len >= 0, "Negative length in memcopy!");
246 Memmove(dest, src, (uint)len);
250 // This method has different signature for x64 and other platforms and is done for performance reasons.
251 internal unsafe static void Memmove(byte* dest, byte* src, nuint len)
253 #if AMD64 || (BIT32 && !ARM)
254 const nuint CopyThreshold = 2048;
257 // Determined optimal value for Windows.
258 // https://github.com/dotnet/coreclr/issues/13843
259 const nuint CopyThreshold = UInt64.MaxValue;
260 #else // PLATFORM_WINDOWS
261 // Managed code is currently faster than glibc unoptimized memmove
262 // TODO-ARM64-UNIX-OPT revisit when glibc optimized memmove is in Linux distros
263 // https://github.com/dotnet/coreclr/issues/13844
264 const nuint CopyThreshold = UInt64.MaxValue;
265 #endif // PLATFORM_WINDOWS
267 const nuint CopyThreshold = 512;
268 #endif // AMD64 || (BIT32 && !ARM)
270 // P/Invoke into the native version when the buffers are overlapping.
272 if (((nuint)dest - (nuint)src < len) || ((nuint)src - (nuint)dest < len)) goto PInvoke;
274 byte* srcEnd = src + len;
275 byte* destEnd = dest + len;
277 if (len <= 16) goto MCPY02;
278 if (len > 64) goto MCPY05;
281 // Copy bytes which are multiples of 16 and leave the remainder for MCPY01 to handle.
282 Debug.Assert(len > 16 && len <= 64);
283 #if HAS_CUSTOM_BLOCKS
284 *(Block16*)dest = *(Block16*)src; // [0,16]
286 *(long*)dest = *(long*)src;
287 *(long*)(dest + 8) = *(long*)(src + 8); // [0,16]
289 *(int*)dest = *(int*)src;
290 *(int*)(dest + 4) = *(int*)(src + 4);
291 *(int*)(dest + 8) = *(int*)(src + 8);
292 *(int*)(dest + 12) = *(int*)(src + 12); // [0,16]
294 if (len <= 32) goto MCPY01;
295 #if HAS_CUSTOM_BLOCKS
296 *(Block16*)(dest + 16) = *(Block16*)(src + 16); // [0,32]
298 *(long*)(dest + 16) = *(long*)(src + 16);
299 *(long*)(dest + 24) = *(long*)(src + 24); // [0,32]
301 *(int*)(dest + 16) = *(int*)(src + 16);
302 *(int*)(dest + 20) = *(int*)(src + 20);
303 *(int*)(dest + 24) = *(int*)(src + 24);
304 *(int*)(dest + 28) = *(int*)(src + 28); // [0,32]
306 if (len <= 48) goto MCPY01;
307 #if HAS_CUSTOM_BLOCKS
308 *(Block16*)(dest + 32) = *(Block16*)(src + 32); // [0,48]
310 *(long*)(dest + 32) = *(long*)(src + 32);
311 *(long*)(dest + 40) = *(long*)(src + 40); // [0,48]
313 *(int*)(dest + 32) = *(int*)(src + 32);
314 *(int*)(dest + 36) = *(int*)(src + 36);
315 *(int*)(dest + 40) = *(int*)(src + 40);
316 *(int*)(dest + 44) = *(int*)(src + 44); // [0,48]
320 // Unconditionally copy the last 16 bytes using destEnd and srcEnd and return.
321 Debug.Assert(len > 16 && len <= 64);
322 #if HAS_CUSTOM_BLOCKS
323 *(Block16*)(destEnd - 16) = *(Block16*)(srcEnd - 16);
325 *(long*)(destEnd - 16) = *(long*)(srcEnd - 16);
326 *(long*)(destEnd - 8) = *(long*)(srcEnd - 8);
328 *(int*)(destEnd - 16) = *(int*)(srcEnd - 16);
329 *(int*)(destEnd - 12) = *(int*)(srcEnd - 12);
330 *(int*)(destEnd - 8) = *(int*)(srcEnd - 8);
331 *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
336 // Copy the first 8 bytes and then unconditionally copy the last 8 bytes and return.
337 if ((len & 24) == 0) goto MCPY03;
338 Debug.Assert(len >= 8 && len <= 16);
340 *(long*)dest = *(long*)src;
341 *(long*)(destEnd - 8) = *(long*)(srcEnd - 8);
343 *(int*)dest = *(int*)src;
344 *(int*)(dest + 4) = *(int*)(src + 4);
345 *(int*)(destEnd - 8) = *(int*)(srcEnd - 8);
346 *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
351 // Copy the first 4 bytes and then unconditionally copy the last 4 bytes and return.
352 if ((len & 4) == 0) goto MCPY04;
353 Debug.Assert(len >= 4 && len < 8);
354 *(int*)dest = *(int*)src;
355 *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
359 // Copy the first byte. For pending bytes, do an unconditionally copy of the last 2 bytes and return.
360 Debug.Assert(len < 4);
361 if (len == 0) return;
363 if ((len & 2) == 0) return;
364 *(short*)(destEnd - 2) = *(short*)(srcEnd - 2);
368 // PInvoke to the native version when the copy length exceeds the threshold.
369 if (len > CopyThreshold)
373 // Copy 64-bytes at a time until the remainder is less than 64.
374 // If remainder is greater than 16 bytes, then jump to MCPY00. Otherwise, unconditionally copy the last 16 bytes and return.
375 Debug.Assert(len > 64 && len <= CopyThreshold);
379 #if HAS_CUSTOM_BLOCKS
380 *(Block64*)dest = *(Block64*)src;
382 *(long*)dest = *(long*)src;
383 *(long*)(dest + 8) = *(long*)(src + 8);
384 *(long*)(dest + 16) = *(long*)(src + 16);
385 *(long*)(dest + 24) = *(long*)(src + 24);
386 *(long*)(dest + 32) = *(long*)(src + 32);
387 *(long*)(dest + 40) = *(long*)(src + 40);
388 *(long*)(dest + 48) = *(long*)(src + 48);
389 *(long*)(dest + 56) = *(long*)(src + 56);
391 *(int*)dest = *(int*)src;
392 *(int*)(dest + 4) = *(int*)(src + 4);
393 *(int*)(dest + 8) = *(int*)(src + 8);
394 *(int*)(dest + 12) = *(int*)(src + 12);
395 *(int*)(dest + 16) = *(int*)(src + 16);
396 *(int*)(dest + 20) = *(int*)(src + 20);
397 *(int*)(dest + 24) = *(int*)(src + 24);
398 *(int*)(dest + 28) = *(int*)(src + 28);
399 *(int*)(dest + 32) = *(int*)(src + 32);
400 *(int*)(dest + 36) = *(int*)(src + 36);
401 *(int*)(dest + 40) = *(int*)(src + 40);
402 *(int*)(dest + 44) = *(int*)(src + 44);
403 *(int*)(dest + 48) = *(int*)(src + 48);
404 *(int*)(dest + 52) = *(int*)(src + 52);
405 *(int*)(dest + 56) = *(int*)(src + 56);
406 *(int*)(dest + 60) = *(int*)(src + 60);
411 if (n != 0) goto MCPY06;
414 if (len > 16) goto MCPY00;
415 #if HAS_CUSTOM_BLOCKS
416 *(Block16*)(destEnd - 16) = *(Block16*)(srcEnd - 16);
418 *(long*)(destEnd - 16) = *(long*)(srcEnd - 16);
419 *(long*)(destEnd - 8) = *(long*)(srcEnd - 8);
421 *(int*)(destEnd - 16) = *(int*)(srcEnd - 16);
422 *(int*)(destEnd - 12) = *(int*)(srcEnd - 12);
423 *(int*)(destEnd - 8) = *(int*)(srcEnd - 8);
424 *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
429 _Memmove(dest, src, len);
432 // Non-inlinable wrapper around the QCall that avoids poluting the fast path
433 // with P/Invoke prolog/epilog.
434 [MethodImplAttribute(MethodImplOptions.NoInlining)]
435 private unsafe static void _Memmove(byte* dest, byte* src, nuint len)
437 __Memmove(dest, src, len);
440 [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
441 extern private unsafe static void __Memmove(byte* dest, byte* src, nuint len);
443 // The attributes on this method are chosen for best JIT performance.
444 // Please do not edit unless intentional.
445 [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
446 [CLSCompliant(false)]
447 public static unsafe void MemoryCopy(void* source, void* destination, long destinationSizeInBytes, long sourceBytesToCopy)
449 if (sourceBytesToCopy > destinationSizeInBytes)
451 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceBytesToCopy);
453 Memmove((byte*)destination, (byte*)source, checked((nuint)sourceBytesToCopy));
457 // The attributes on this method are chosen for best JIT performance.
458 // Please do not edit unless intentional.
459 [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
460 [CLSCompliant(false)]
461 public static unsafe void MemoryCopy(void* source, void* destination, ulong destinationSizeInBytes, ulong sourceBytesToCopy)
463 if (sourceBytesToCopy > destinationSizeInBytes)
465 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceBytesToCopy);
468 Memmove((byte*)destination, (byte*)source, sourceBytesToCopy);
470 Memmove((byte*)destination, (byte*)source, checked((uint)sourceBytesToCopy));
474 #if HAS_CUSTOM_BLOCKS
475 [StructLayout(LayoutKind.Sequential, Size = 16)]
476 private struct Block16 { }
478 [StructLayout(LayoutKind.Sequential, Size = 64)]
479 private struct Block64 { }
480 #endif // HAS_CUSTOM_BLOCKS