Optimize Span.Copy and Span.TryCopyTo (#15947)
[platform/upstream/coreclr.git] / src / mscorlib / src / System / Buffer.cs
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.
4
5 #if AMD64 || ARM64 || (BIT32 && !ARM)
6 #define HAS_CUSTOM_BLOCKS
7 #endif
8
9 namespace System
10 {
11     //Only contains static methods.  Does not require serialization
12
13     using System;
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;
20     using System.Runtime;
21     using Internal.Runtime.CompilerServices;
22
23 #if BIT64
24     using nint = System.Int64;
25     using nuint = System.UInt64;
26 #else // BIT64
27     using nint = System.Int32;
28     using nuint = System.UInt32;
29 #endif // BIT64
30
31     public static class Buffer
32     {
33         // Copies from one primitive array to another primitive array without
34         // respecting types.  This calls memmove internally.  The count and 
35         // offset parameters here are in bytes.  If you want to use traditional
36         // array element indices and counts, use Array.Copy.
37         [MethodImplAttribute(MethodImplOptions.InternalCall)]
38         public static extern void BlockCopy(Array src, int srcOffset,
39             Array dst, int dstOffset, int count);
40
41         // This is ported from the optimized CRT assembly in memchr.asm. The JIT generates 
42         // pretty good code here and this ends up being within a couple % of the CRT asm.
43         // It is however cross platform as the CRT hasn't ported their fast version to 64-bit
44         // platforms.
45         //
46         internal unsafe static int IndexOfByte(byte* src, byte value, int index, int count)
47         {
48             Debug.Assert(src != null, "src should not be null");
49
50             byte* pByte = src + index;
51
52             // Align up the pointer to sizeof(int).
53             while (((int)pByte & 3) != 0)
54             {
55                 if (count == 0)
56                     return -1;
57                 else if (*pByte == value)
58                     return (int)(pByte - src);
59
60                 count--;
61                 pByte++;
62             }
63
64             // Fill comparer with value byte for comparisons
65             //
66             // comparer = 0/0/value/value
67             uint comparer = (((uint)value << 8) + (uint)value);
68             // comparer = value/value/value/value
69             comparer = (comparer << 16) + comparer;
70
71             // Run through buffer until we hit a 4-byte section which contains
72             // the byte we're looking for or until we exhaust the buffer.
73             while (count > 3)
74             {
75                 // Test the buffer for presence of value. comparer contains the byte
76                 // replicated 4 times.
77                 uint t1 = *(uint*)pByte;
78                 t1 = t1 ^ comparer;
79                 uint t2 = 0x7efefeff + t1;
80                 t1 = t1 ^ 0xffffffff;
81                 t1 = t1 ^ t2;
82                 t1 = t1 & 0x81010100;
83
84                 // if t1 is zero then these 4-bytes don't contain a match
85                 if (t1 != 0)
86                 {
87                     // We've found a match for value, figure out which position it's in.
88                     int foundIndex = (int)(pByte - src);
89                     if (pByte[0] == value)
90                         return foundIndex;
91                     else if (pByte[1] == value)
92                         return foundIndex + 1;
93                     else if (pByte[2] == value)
94                         return foundIndex + 2;
95                     else if (pByte[3] == value)
96                         return foundIndex + 3;
97                 }
98
99                 count -= 4;
100                 pByte += 4;
101             }
102
103             // Catch any bytes that might be left at the tail of the buffer
104             while (count > 0)
105             {
106                 if (*pByte == value)
107                     return (int)(pByte - src);
108
109                 count--;
110                 pByte++;
111             }
112
113             // If we don't have a match return -1;
114             return -1;
115         }
116
117         // Returns a bool to indicate if the array is of primitive data types
118         // or not.
119         [MethodImplAttribute(MethodImplOptions.InternalCall)]
120         private static extern bool IsPrimitiveTypeArray(Array array);
121
122         // Gets a particular byte out of the array.  The array must be an
123         // array of primitives.  
124         //
125         // This essentially does the following: 
126         // return ((byte*)array) + index.
127         //
128         [MethodImplAttribute(MethodImplOptions.InternalCall)]
129         private static extern byte _GetByte(Array array, int index);
130
131         public static byte GetByte(Array array, int index)
132         {
133             // Is the array present?
134             if (array == null)
135                 throw new ArgumentNullException(nameof(array));
136
137             // Is it of primitive types?
138             if (!IsPrimitiveTypeArray(array))
139                 throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(array));
140
141             // Is the index in valid range of the array?
142             if (index < 0 || index >= _ByteLength(array))
143                 throw new ArgumentOutOfRangeException(nameof(index));
144
145             return _GetByte(array, index);
146         }
147
148         // Sets a particular byte in an the array.  The array must be an
149         // array of primitives.  
150         //
151         // This essentially does the following: 
152         // *(((byte*)array) + index) = value.
153         //
154         [MethodImplAttribute(MethodImplOptions.InternalCall)]
155         private static extern void _SetByte(Array array, int index, byte value);
156
157         public static void SetByte(Array array, int index, byte value)
158         {
159             // Is the array present?
160             if (array == null)
161                 throw new ArgumentNullException(nameof(array));
162
163             // Is it of primitive types?
164             if (!IsPrimitiveTypeArray(array))
165                 throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(array));
166
167             // Is the index in valid range of the array?
168             if (index < 0 || index >= _ByteLength(array))
169                 throw new ArgumentOutOfRangeException(nameof(index));
170
171             // Make the FCall to do the work
172             _SetByte(array, index, value);
173         }
174
175
176         // Gets a particular byte out of the array.  The array must be an
177         // array of primitives.  
178         //
179         // This essentially does the following: 
180         // return array.length * sizeof(array.UnderlyingElementType).
181         //
182         [MethodImplAttribute(MethodImplOptions.InternalCall)]
183         private static extern int _ByteLength(Array array);
184
185         public static int ByteLength(Array array)
186         {
187             // Is the array present?
188             if (array == null)
189                 throw new ArgumentNullException(nameof(array));
190
191             // Is it of primitive types?
192             if (!IsPrimitiveTypeArray(array))
193                 throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(array));
194
195             return _ByteLength(array);
196         }
197
198         internal unsafe static void ZeroMemory(byte* src, long len)
199         {
200             while (len-- > 0)
201                 *(src + len) = 0;
202         }
203
204         internal unsafe static void Memcpy(byte[] dest, int destIndex, byte* src, int srcIndex, int len)
205         {
206             Debug.Assert((srcIndex >= 0) && (destIndex >= 0) && (len >= 0), "Index and length must be non-negative!");
207             Debug.Assert(dest.Length - destIndex >= len, "not enough bytes in dest");
208             // If dest has 0 elements, the fixed statement will throw an 
209             // IndexOutOfRangeException.  Special-case 0-byte copies.
210             if (len == 0)
211                 return;
212             fixed (byte* pDest = dest)
213             {
214                 Memcpy(pDest + destIndex, src + srcIndex, len);
215             }
216         }
217
218         internal unsafe static void Memcpy(byte* pDest, int destIndex, byte[] src, int srcIndex, int len)
219         {
220             Debug.Assert((srcIndex >= 0) && (destIndex >= 0) && (len >= 0), "Index and length must be non-negative!");
221             Debug.Assert(src.Length - srcIndex >= len, "not enough bytes in src");
222             // If dest has 0 elements, the fixed statement will throw an 
223             // IndexOutOfRangeException.  Special-case 0-byte copies.
224             if (len == 0)
225                 return;
226             fixed (byte* pSrc = src)
227             {
228                 Memcpy(pDest + destIndex, pSrc + srcIndex, len);
229             }
230         }
231
232         // This is tricky to get right AND fast, so lets make it useful for the whole Fx.
233         // E.g. System.Runtime.WindowsRuntime!WindowsRuntimeBufferExtensions.MemCopy uses it.
234
235         // This method has a slightly different behavior on arm and other platforms.
236         // On arm this method behaves like memcpy and does not handle overlapping buffers.
237         // While on other platforms it behaves like memmove and handles overlapping buffers.
238         // This behavioral difference is unfortunate but intentional because
239         // 1. This method is given access to other internal dlls and this close to release we do not want to change it.
240         // 2. It is difficult to get this right for arm and again due to release dates we would like to visit it later.
241 #if ARM
242         [MethodImplAttribute(MethodImplOptions.InternalCall)]
243         internal unsafe static extern void Memcpy(byte* dest, byte* src, int len);
244 #else // ARM
245         [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
246         internal unsafe static void Memcpy(byte* dest, byte* src, int len)
247         {
248             Debug.Assert(len >= 0, "Negative length in memcopy!");
249             Memmove(dest, src, (uint)len);
250         }
251 #endif // ARM
252
253         // This method has different signature for x64 and other platforms and is done for performance reasons.
254         internal unsafe static void Memmove(byte* dest, byte* src, nuint len)
255         {
256 #if AMD64 || (BIT32 && !ARM)
257             const nuint CopyThreshold = 2048;
258 #elif ARM64
259 #if PLATFORM_WINDOWS
260             // Determined optimal value for Windows.
261             // https://github.com/dotnet/coreclr/issues/13843
262             const nuint CopyThreshold = UInt64.MaxValue;
263 #else // PLATFORM_WINDOWS
264             // Managed code is currently faster than glibc unoptimized memmove
265             // TODO-ARM64-UNIX-OPT revisit when glibc optimized memmove is in Linux distros
266             // https://github.com/dotnet/coreclr/issues/13844
267             const nuint CopyThreshold = UInt64.MaxValue;
268 #endif // PLATFORM_WINDOWS
269 #else
270             const nuint CopyThreshold = 512;
271 #endif // AMD64 || (BIT32 && !ARM)
272
273             // P/Invoke into the native version when the buffers are overlapping.
274
275             if (((nuint)dest - (nuint)src < len) || ((nuint)src - (nuint)dest < len)) goto PInvoke;
276
277             byte* srcEnd = src + len;
278             byte* destEnd = dest + len;
279
280             if (len <= 16) goto MCPY02;
281             if (len > 64) goto MCPY05;
282
283             MCPY00:
284             // Copy bytes which are multiples of 16 and leave the remainder for MCPY01 to handle.
285             Debug.Assert(len > 16 && len <= 64);
286 #if HAS_CUSTOM_BLOCKS
287             *(Block16*)dest = *(Block16*)src;                   // [0,16]
288 #elif BIT64
289             *(long*)dest = *(long*)src;
290             *(long*)(dest + 8) = *(long*)(src + 8);             // [0,16]
291 #else
292             *(int*)dest = *(int*)src;
293             *(int*)(dest + 4) = *(int*)(src + 4);
294             *(int*)(dest + 8) = *(int*)(src + 8);
295             *(int*)(dest + 12) = *(int*)(src + 12);             // [0,16]
296 #endif
297             if (len <= 32) goto MCPY01;
298 #if HAS_CUSTOM_BLOCKS
299             *(Block16*)(dest + 16) = *(Block16*)(src + 16);     // [0,32]
300 #elif BIT64
301             *(long*)(dest + 16) = *(long*)(src + 16);
302             *(long*)(dest + 24) = *(long*)(src + 24);           // [0,32]
303 #else
304             *(int*)(dest + 16) = *(int*)(src + 16);
305             *(int*)(dest + 20) = *(int*)(src + 20);
306             *(int*)(dest + 24) = *(int*)(src + 24);
307             *(int*)(dest + 28) = *(int*)(src + 28);             // [0,32]
308 #endif
309             if (len <= 48) goto MCPY01;
310 #if HAS_CUSTOM_BLOCKS
311             *(Block16*)(dest + 32) = *(Block16*)(src + 32);     // [0,48]
312 #elif BIT64
313             *(long*)(dest + 32) = *(long*)(src + 32);
314             *(long*)(dest + 40) = *(long*)(src + 40);           // [0,48]
315 #else
316             *(int*)(dest + 32) = *(int*)(src + 32);
317             *(int*)(dest + 36) = *(int*)(src + 36);
318             *(int*)(dest + 40) = *(int*)(src + 40);
319             *(int*)(dest + 44) = *(int*)(src + 44);             // [0,48]
320 #endif
321
322             MCPY01:
323             // Unconditionally copy the last 16 bytes using destEnd and srcEnd and return.
324             Debug.Assert(len > 16 && len <= 64);
325 #if HAS_CUSTOM_BLOCKS
326             *(Block16*)(destEnd - 16) = *(Block16*)(srcEnd - 16);
327 #elif BIT64
328             *(long*)(destEnd - 16) = *(long*)(srcEnd - 16);
329             *(long*)(destEnd - 8) = *(long*)(srcEnd - 8);
330 #else
331             *(int*)(destEnd - 16) = *(int*)(srcEnd - 16);
332             *(int*)(destEnd - 12) = *(int*)(srcEnd - 12);
333             *(int*)(destEnd - 8) = *(int*)(srcEnd - 8);
334             *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
335 #endif
336             return;
337
338             MCPY02:
339             // Copy the first 8 bytes and then unconditionally copy the last 8 bytes and return.
340             if ((len & 24) == 0) goto MCPY03;
341             Debug.Assert(len >= 8 && len <= 16);
342 #if BIT64
343             *(long*)dest = *(long*)src;
344             *(long*)(destEnd - 8) = *(long*)(srcEnd - 8);
345 #else
346             *(int*)dest = *(int*)src;
347             *(int*)(dest + 4) = *(int*)(src + 4);
348             *(int*)(destEnd - 8) = *(int*)(srcEnd - 8);
349             *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
350 #endif
351             return;
352
353             MCPY03:
354             // Copy the first 4 bytes and then unconditionally copy the last 4 bytes and return.
355             if ((len & 4) == 0) goto MCPY04;
356             Debug.Assert(len >= 4 && len < 8);
357             *(int*)dest = *(int*)src;
358             *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
359             return;
360
361             MCPY04:
362             // Copy the first byte. For pending bytes, do an unconditionally copy of the last 2 bytes and return.
363             Debug.Assert(len < 4);
364             if (len == 0) return;
365             *dest = *src;
366             if ((len & 2) == 0) return;
367             *(short*)(destEnd - 2) = *(short*)(srcEnd - 2);
368             return;
369
370             MCPY05:
371             // PInvoke to the native version when the copy length exceeds the threshold.
372             if (len > CopyThreshold)
373             {
374                 goto PInvoke;
375             }
376             // Copy 64-bytes at a time until the remainder is less than 64.
377             // If remainder is greater than 16 bytes, then jump to MCPY00. Otherwise, unconditionally copy the last 16 bytes and return.
378             Debug.Assert(len > 64 && len <= CopyThreshold);
379             nuint n = len >> 6;
380
381             MCPY06:
382 #if HAS_CUSTOM_BLOCKS
383             *(Block64*)dest = *(Block64*)src;
384 #elif BIT64
385             *(long*)dest = *(long*)src;
386             *(long*)(dest + 8) = *(long*)(src + 8);
387             *(long*)(dest + 16) = *(long*)(src + 16);
388             *(long*)(dest + 24) = *(long*)(src + 24);
389             *(long*)(dest + 32) = *(long*)(src + 32);
390             *(long*)(dest + 40) = *(long*)(src + 40);
391             *(long*)(dest + 48) = *(long*)(src + 48);
392             *(long*)(dest + 56) = *(long*)(src + 56);
393 #else
394             *(int*)dest = *(int*)src;
395             *(int*)(dest + 4) = *(int*)(src + 4);
396             *(int*)(dest + 8) = *(int*)(src + 8);
397             *(int*)(dest + 12) = *(int*)(src + 12);
398             *(int*)(dest + 16) = *(int*)(src + 16);
399             *(int*)(dest + 20) = *(int*)(src + 20);
400             *(int*)(dest + 24) = *(int*)(src + 24);
401             *(int*)(dest + 28) = *(int*)(src + 28);
402             *(int*)(dest + 32) = *(int*)(src + 32);
403             *(int*)(dest + 36) = *(int*)(src + 36);
404             *(int*)(dest + 40) = *(int*)(src + 40);
405             *(int*)(dest + 44) = *(int*)(src + 44);
406             *(int*)(dest + 48) = *(int*)(src + 48);
407             *(int*)(dest + 52) = *(int*)(src + 52);
408             *(int*)(dest + 56) = *(int*)(src + 56);
409             *(int*)(dest + 60) = *(int*)(src + 60);
410 #endif
411             dest += 64;
412             src += 64;
413             n--;
414             if (n != 0) goto MCPY06;
415
416             len %= 64;
417             if (len > 16) goto MCPY00;
418 #if HAS_CUSTOM_BLOCKS
419             *(Block16*)(destEnd - 16) = *(Block16*)(srcEnd - 16);
420 #elif BIT64
421             *(long*)(destEnd - 16) = *(long*)(srcEnd - 16);
422             *(long*)(destEnd - 8) = *(long*)(srcEnd - 8);
423 #else
424             *(int*)(destEnd - 16) = *(int*)(srcEnd - 16);
425             *(int*)(destEnd - 12) = *(int*)(srcEnd - 12);
426             *(int*)(destEnd - 8) = *(int*)(srcEnd - 8);
427             *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
428 #endif
429             return;
430
431             PInvoke:
432             _Memmove(dest, src, len);
433         }
434         
435         // This method has different signature for x64 and other platforms and is done for performance reasons.
436         [MethodImpl(MethodImplOptions.AggressiveInlining)]
437         internal static void Memmove<T>(ref T destination, ref T source, nuint elementCount)
438         {
439             if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
440             {
441                 // Blittable memmove
442
443                 Memmove(
444                     new ByReference<byte>(ref Unsafe.As<T, byte>(ref destination)),
445                     new ByReference<byte>(ref Unsafe.As<T, byte>(ref source)),
446                     elementCount * (nuint)Unsafe.SizeOf<T>());
447             }
448             else
449             {
450                 // Non-blittable memmove
451
452                 // Try to avoid calling RhBulkMoveWithWriteBarrier if we can get away
453                 // with a no-op.
454                 if (!Unsafe.AreSame(ref destination, ref source) && elementCount != 0)
455                 {
456                     RuntimeImports.RhBulkMoveWithWriteBarrier(
457                         ref Unsafe.As<T, byte>(ref destination),
458                         ref Unsafe.As<T, byte>(ref source),
459                         elementCount * (nuint)Unsafe.SizeOf<T>());
460                 }
461             }
462         }
463
464         // This method has different signature for x64 and other platforms and is done for performance reasons.
465         private static void Memmove(ByReference<byte> dest, ByReference<byte> src, nuint len)
466         {
467 #if AMD64 || (BIT32 && !ARM)
468             const nuint CopyThreshold = 2048;
469 #elif ARM64
470 #if PLATFORM_WINDOWS
471             // Determined optimal value for Windows.
472             // https://github.com/dotnet/coreclr/issues/13843
473             const nuint CopyThreshold = UInt64.MaxValue;
474 #else // PLATFORM_WINDOWS
475             // Managed code is currently faster than glibc unoptimized memmove
476             // TODO-ARM64-UNIX-OPT revisit when glibc optimized memmove is in Linux distros
477             // https://github.com/dotnet/coreclr/issues/13844
478             const nuint CopyThreshold = UInt64.MaxValue;
479 #endif // PLATFORM_WINDOWS
480 #else
481             const nuint CopyThreshold = 512;
482 #endif // AMD64 || (BIT32 && !ARM)
483
484             // P/Invoke into the native version when the buffers are overlapping.            
485
486             if (((nuint)Unsafe.ByteOffset(ref src.Value, ref dest.Value) < len) || ((nuint)Unsafe.ByteOffset(ref dest.Value, ref src.Value) < len))
487             {
488                 goto BuffersOverlap;
489             }
490
491             // Use "(IntPtr)(nint)len" to avoid overflow checking on the explicit cast to IntPtr
492
493             ref byte srcEnd = ref Unsafe.Add(ref src.Value, (IntPtr)(nint)len);
494             ref byte destEnd = ref Unsafe.Add(ref dest.Value, (IntPtr)(nint)len);
495
496             if (len <= 16)
497                 goto MCPY02;
498             if (len > 64)
499                 goto MCPY05;
500
501 MCPY00:
502 // Copy bytes which are multiples of 16 and leave the remainder for MCPY01 to handle.
503             Debug.Assert(len > 16 && len <= 64);
504 #if HAS_CUSTOM_BLOCKS
505             Unsafe.As<byte, Block16>(ref dest.Value) = Unsafe.As<byte, Block16>(ref src.Value); // [0,16]
506 #elif BIT64
507             Unsafe.As<byte, long>(ref dest.Value) = Unsafe.As<byte, long>(ref src.Value);
508             Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 8)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 8)); // [0,16]
509 #else
510             Unsafe.As<byte, int>(ref dest.Value) = Unsafe.As<byte, int>(ref src.Value);
511             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 4));
512             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 8)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 8));
513             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 12)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 12)); // [0,16]
514 #endif
515             if (len <= 32)
516                 goto MCPY01;
517 #if HAS_CUSTOM_BLOCKS
518             Unsafe.As<byte, Block16>(ref Unsafe.Add(ref dest.Value, 16)) = Unsafe.As<byte, Block16>(ref Unsafe.Add(ref src.Value, 16)); // [0,32]
519 #elif BIT64
520             Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 16)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 16));
521             Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 24)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 24)); // [0,32]
522 #else
523             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 16)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 16));
524             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 20)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 20));
525             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 24)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 24));
526             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 28)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 28)); // [0,32]
527 #endif
528             if (len <= 48)
529                 goto MCPY01;
530 #if HAS_CUSTOM_BLOCKS
531             Unsafe.As<byte, Block16>(ref Unsafe.Add(ref dest.Value, 32)) = Unsafe.As<byte, Block16>(ref Unsafe.Add(ref src.Value, 32)); // [0,48]
532 #elif BIT64
533             Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 32)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 32));
534             Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 40)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 40)); // [0,48]
535 #else
536             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 32)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 32));
537             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 36)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 36));
538             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 40)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 40));
539             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 44)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 44)); // [0,48]
540 #endif
541
542 MCPY01:
543 // Unconditionally copy the last 16 bytes using destEnd and srcEnd and return.
544             Debug.Assert(len > 16 && len <= 64);
545 #if HAS_CUSTOM_BLOCKS
546             Unsafe.As<byte, Block16>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, Block16>(ref Unsafe.Add(ref srcEnd, -16));
547 #elif BIT64
548             Unsafe.As<byte, long>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref srcEnd, -16));
549             Unsafe.As<byte, long>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref srcEnd, -8));
550 #else
551             Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -16));
552             Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -12)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -12));
553             Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -8));
554             Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -4));
555 #endif
556             return;
557
558 MCPY02:
559 // Copy the first 8 bytes and then unconditionally copy the last 8 bytes and return.
560             if ((len & 24) == 0)
561                 goto MCPY03;
562             Debug.Assert(len >= 8 && len <= 16);
563 #if BIT64
564             Unsafe.As<byte, long>(ref dest.Value) = Unsafe.As<byte, long>(ref src.Value);
565             Unsafe.As<byte, long>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref srcEnd, -8));
566 #else
567             Unsafe.As<byte, int>(ref dest.Value) = Unsafe.As<byte, int>(ref src.Value);
568             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 4));
569             Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -8));
570             Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -4));
571 #endif
572             return;
573
574 MCPY03:
575 // Copy the first 4 bytes and then unconditionally copy the last 4 bytes and return.
576             if ((len & 4) == 0)
577                 goto MCPY04;
578             Debug.Assert(len >= 4 && len < 8);
579             Unsafe.As<byte, int>(ref dest.Value) = Unsafe.As<byte, int>(ref src.Value);
580             Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -4));
581             return;
582
583 MCPY04:
584 // Copy the first byte. For pending bytes, do an unconditionally copy of the last 2 bytes and return.
585             Debug.Assert(len < 4);
586             if (len == 0)
587                 return;
588             dest.Value = src.Value;
589             if ((len & 2) == 0)
590                 return;
591             Unsafe.As<byte, short>(ref Unsafe.Add(ref destEnd, -2)) = Unsafe.As<byte, short>(ref Unsafe.Add(ref srcEnd, -2));
592             return;
593
594 MCPY05:
595 // PInvoke to the native version when the copy length exceeds the threshold.
596             if (len > CopyThreshold)
597             {
598                 goto PInvoke;
599             }
600             // Copy 64-bytes at a time until the remainder is less than 64.
601             // If remainder is greater than 16 bytes, then jump to MCPY00. Otherwise, unconditionally copy the last 16 bytes and return.
602             Debug.Assert(len > 64 && len <= CopyThreshold);
603             nuint n = len >> 6;
604
605 MCPY06:
606 #if HAS_CUSTOM_BLOCKS
607             Unsafe.As<byte, Block64>(ref dest.Value) = Unsafe.As<byte, Block64>(ref src.Value);
608 #elif BIT64
609             Unsafe.As<byte, long>(ref dest.Value) = Unsafe.As<byte, long>(ref src.Value);
610             Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 8)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 8));
611             Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 16)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 16));
612             Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 24)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 24));
613             Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 32)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 32));
614             Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 40)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 40));
615             Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 48)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 48));
616             Unsafe.As<byte, long>(ref Unsafe.Add(ref dest.Value, 56)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref src.Value, 56));
617 #else
618             Unsafe.As<byte, int>(ref dest.Value) = Unsafe.As<byte, int>(ref src.Value);
619             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 4));
620             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 8)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 8));
621             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 12)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 12));
622             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 16)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 16));
623             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 20)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 20));
624             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 24)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 24));
625             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 28)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 28));
626             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 32)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 32));
627             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 36)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 36));
628             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 40)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 40));
629             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 44)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 44));
630             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 48)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 48));
631             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 52)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 52));
632             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 56)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 56));
633             Unsafe.As<byte, int>(ref Unsafe.Add(ref dest.Value, 60)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref src.Value, 60));
634 #endif
635             dest = new ByReference<byte>(ref Unsafe.Add(ref dest.Value, 64));
636             src = new ByReference<byte>(ref Unsafe.Add(ref src.Value, 64));
637             n--;
638             if (n != 0)
639                 goto MCPY06;
640
641             len %= 64;
642             if (len > 16)
643                 goto MCPY00;
644 #if HAS_CUSTOM_BLOCKS
645             Unsafe.As<byte, Block16>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, Block16>(ref Unsafe.Add(ref srcEnd, -16));
646 #elif BIT64
647             Unsafe.As<byte, long>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref srcEnd, -16));
648             Unsafe.As<byte, long>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, long>(ref Unsafe.Add(ref srcEnd, -8));
649 #else
650             Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -16)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -16));
651             Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -12)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -12));
652             Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -8)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -8));
653             Unsafe.As<byte, int>(ref Unsafe.Add(ref destEnd, -4)) = Unsafe.As<byte, int>(ref Unsafe.Add(ref srcEnd, -4));
654 #endif
655             return;
656
657 BuffersOverlap:
658             // If the buffers overlap perfectly, there's no point to copying the data.
659             if (Unsafe.AreSame(ref dest.Value, ref src.Value))
660             {
661                 return;
662             }
663
664 PInvoke:
665             _Memmove(ref dest.Value, ref src.Value, len);
666         }
667
668         // Non-inlinable wrapper around the QCall that avoids polluting the fast path
669         // with P/Invoke prolog/epilog.
670         [MethodImplAttribute(MethodImplOptions.NoInlining)]
671         private unsafe static void _Memmove(byte* dest, byte* src, nuint len)
672         {
673             __Memmove(dest, src, len);
674         }
675
676         // Non-inlinable wrapper around the QCall that avoids polluting the fast path
677         // with P/Invoke prolog/epilog.
678         [MethodImplAttribute(MethodImplOptions.NoInlining)]
679         private unsafe static void _Memmove(ref byte dest, ref byte src, nuint len)
680         {
681             fixed (byte* pDest = &dest)
682             fixed (byte* pSrc = &src)
683                 __Memmove(pDest, pSrc, len);
684         }
685
686         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
687         extern private unsafe static void __Memmove(byte* dest, byte* src, nuint len);
688
689         // The attributes on this method are chosen for best JIT performance. 
690         // Please do not edit unless intentional.
691         [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
692         [CLSCompliant(false)]
693         public static unsafe void MemoryCopy(void* source, void* destination, long destinationSizeInBytes, long sourceBytesToCopy)
694         {
695             if (sourceBytesToCopy > destinationSizeInBytes)
696             {
697                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceBytesToCopy);
698             }
699             Memmove((byte*)destination, (byte*)source, checked((nuint)sourceBytesToCopy));
700         }
701
702
703         // The attributes on this method are chosen for best JIT performance. 
704         // Please do not edit unless intentional.
705         [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
706         [CLSCompliant(false)]
707         public static unsafe void MemoryCopy(void* source, void* destination, ulong destinationSizeInBytes, ulong sourceBytesToCopy)
708         {
709             if (sourceBytesToCopy > destinationSizeInBytes)
710             {
711                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceBytesToCopy);
712             }
713 #if BIT64
714             Memmove((byte*)destination, (byte*)source, sourceBytesToCopy);
715 #else // BIT64
716             Memmove((byte*)destination, (byte*)source, checked((uint)sourceBytesToCopy));
717 #endif // BIT64
718         }
719         
720 #if HAS_CUSTOM_BLOCKS        
721         [StructLayout(LayoutKind.Sequential, Size = 16)]
722         private struct Block16 { }
723
724         [StructLayout(LayoutKind.Sequential, Size = 64)]
725         private struct Block64 { } 
726 #endif // HAS_CUSTOM_BLOCKS         
727     }
728 }