Delete FriendAccessAllowedAttribute and associated dead code (#15101)
[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
22 #if BIT64
23     using nuint = System.UInt64;
24 #else // BIT64
25     using nuint = System.UInt32;
26 #endif // BIT64
27
28     public static class Buffer
29     {
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);
37
38         // A very simple and efficient memmove that assumes all of the
39         // parameter validation has already been done.  The count and offset
40         // parameters here are in bytes.  If you want to use traditional
41         // array element indices and counts, use Array.Copy.
42         [MethodImplAttribute(MethodImplOptions.InternalCall)]
43         internal static extern void InternalBlockCopy(Array src, int srcOffsetBytes,
44             Array dst, int dstOffsetBytes, int byteCount);
45
46         // This is ported from the optimized CRT assembly in memchr.asm. The JIT generates 
47         // pretty good code here and this ends up being within a couple % of the CRT asm.
48         // It is however cross platform as the CRT hasn't ported their fast version to 64-bit
49         // platforms.
50         //
51         internal unsafe static int IndexOfByte(byte* src, byte value, int index, int count)
52         {
53             Debug.Assert(src != null, "src should not be null");
54
55             byte* pByte = src + index;
56
57             // Align up the pointer to sizeof(int).
58             while (((int)pByte & 3) != 0)
59             {
60                 if (count == 0)
61                     return -1;
62                 else if (*pByte == value)
63                     return (int)(pByte - src);
64
65                 count--;
66                 pByte++;
67             }
68
69             // Fill comparer with value byte for comparisons
70             //
71             // comparer = 0/0/value/value
72             uint comparer = (((uint)value << 8) + (uint)value);
73             // comparer = value/value/value/value
74             comparer = (comparer << 16) + comparer;
75
76             // Run through buffer until we hit a 4-byte section which contains
77             // the byte we're looking for or until we exhaust the buffer.
78             while (count > 3)
79             {
80                 // Test the buffer for presence of value. comparer contains the byte
81                 // replicated 4 times.
82                 uint t1 = *(uint*)pByte;
83                 t1 = t1 ^ comparer;
84                 uint t2 = 0x7efefeff + t1;
85                 t1 = t1 ^ 0xffffffff;
86                 t1 = t1 ^ t2;
87                 t1 = t1 & 0x81010100;
88
89                 // if t1 is zero then these 4-bytes don't contain a match
90                 if (t1 != 0)
91                 {
92                     // We've found a match for value, figure out which position it's in.
93                     int foundIndex = (int)(pByte - src);
94                     if (pByte[0] == value)
95                         return foundIndex;
96                     else if (pByte[1] == value)
97                         return foundIndex + 1;
98                     else if (pByte[2] == value)
99                         return foundIndex + 2;
100                     else if (pByte[3] == value)
101                         return foundIndex + 3;
102                 }
103
104                 count -= 4;
105                 pByte += 4;
106             }
107
108             // Catch any bytes that might be left at the tail of the buffer
109             while (count > 0)
110             {
111                 if (*pByte == value)
112                     return (int)(pByte - src);
113
114                 count--;
115                 pByte++;
116             }
117
118             // If we don't have a match return -1;
119             return -1;
120         }
121
122         // Returns a bool to indicate if the array is of primitive data types
123         // or not.
124         [MethodImplAttribute(MethodImplOptions.InternalCall)]
125         private static extern bool IsPrimitiveTypeArray(Array array);
126
127         // Gets a particular byte out of the array.  The array must be an
128         // array of primitives.  
129         //
130         // This essentially does the following: 
131         // return ((byte*)array) + index.
132         //
133         [MethodImplAttribute(MethodImplOptions.InternalCall)]
134         private static extern byte _GetByte(Array array, int index);
135
136         public static byte GetByte(Array array, int index)
137         {
138             // Is the array present?
139             if (array == null)
140                 throw new ArgumentNullException(nameof(array));
141
142             // Is it of primitive types?
143             if (!IsPrimitiveTypeArray(array))
144                 throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(array));
145
146             // Is the index in valid range of the array?
147             if (index < 0 || index >= _ByteLength(array))
148                 throw new ArgumentOutOfRangeException(nameof(index));
149
150             return _GetByte(array, index);
151         }
152
153         // Sets a particular byte in an the array.  The array must be an
154         // array of primitives.  
155         //
156         // This essentially does the following: 
157         // *(((byte*)array) + index) = value.
158         //
159         [MethodImplAttribute(MethodImplOptions.InternalCall)]
160         private static extern void _SetByte(Array array, int index, byte value);
161
162         public static void SetByte(Array array, int index, byte value)
163         {
164             // Is the array present?
165             if (array == null)
166                 throw new ArgumentNullException(nameof(array));
167
168             // Is it of primitive types?
169             if (!IsPrimitiveTypeArray(array))
170                 throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(array));
171
172             // Is the index in valid range of the array?
173             if (index < 0 || index >= _ByteLength(array))
174                 throw new ArgumentOutOfRangeException(nameof(index));
175
176             // Make the FCall to do the work
177             _SetByte(array, index, value);
178         }
179
180
181         // Gets a particular byte out of the array.  The array must be an
182         // array of primitives.  
183         //
184         // This essentially does the following: 
185         // return array.length * sizeof(array.UnderlyingElementType).
186         //
187         [MethodImplAttribute(MethodImplOptions.InternalCall)]
188         private static extern int _ByteLength(Array array);
189
190         public static int ByteLength(Array array)
191         {
192             // Is the array present?
193             if (array == null)
194                 throw new ArgumentNullException(nameof(array));
195
196             // Is it of primitive types?
197             if (!IsPrimitiveTypeArray(array))
198                 throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(array));
199
200             return _ByteLength(array);
201         }
202
203         internal unsafe static void ZeroMemory(byte* src, long len)
204         {
205             while (len-- > 0)
206                 *(src + len) = 0;
207         }
208
209         internal unsafe static void Memcpy(byte[] dest, int destIndex, byte* src, int srcIndex, int len)
210         {
211             Debug.Assert((srcIndex >= 0) && (destIndex >= 0) && (len >= 0), "Index and length must be non-negative!");
212             Debug.Assert(dest.Length - destIndex >= len, "not enough bytes in dest");
213             // If dest has 0 elements, the fixed statement will throw an 
214             // IndexOutOfRangeException.  Special-case 0-byte copies.
215             if (len == 0)
216                 return;
217             fixed (byte* pDest = dest)
218             {
219                 Memcpy(pDest + destIndex, src + srcIndex, len);
220             }
221         }
222
223         internal unsafe static void Memcpy(byte* pDest, int destIndex, byte[] src, int srcIndex, int len)
224         {
225             Debug.Assert((srcIndex >= 0) && (destIndex >= 0) && (len >= 0), "Index and length must be non-negative!");
226             Debug.Assert(src.Length - srcIndex >= len, "not enough bytes in src");
227             // If dest has 0 elements, the fixed statement will throw an 
228             // IndexOutOfRangeException.  Special-case 0-byte copies.
229             if (len == 0)
230                 return;
231             fixed (byte* pSrc = src)
232             {
233                 Memcpy(pDest + destIndex, pSrc + srcIndex, len);
234             }
235         }
236
237         // This is tricky to get right AND fast, so lets make it useful for the whole Fx.
238         // E.g. System.Runtime.WindowsRuntime!WindowsRuntimeBufferExtensions.MemCopy uses it.
239
240         // This method has a slightly different behavior on arm and other platforms.
241         // On arm this method behaves like memcpy and does not handle overlapping buffers.
242         // While on other platforms it behaves like memmove and handles overlapping buffers.
243         // This behavioral difference is unfortunate but intentional because
244         // 1. This method is given access to other internal dlls and this close to release we do not want to change it.
245         // 2. It is difficult to get this right for arm and again due to release dates we would like to visit it later.
246 #if ARM
247         [MethodImplAttribute(MethodImplOptions.InternalCall)]
248         internal unsafe static extern void Memcpy(byte* dest, byte* src, int len);
249 #else // ARM
250         [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
251         internal unsafe static void Memcpy(byte* dest, byte* src, int len)
252         {
253             Debug.Assert(len >= 0, "Negative length in memcopy!");
254             Memmove(dest, src, (uint)len);
255         }
256 #endif // ARM
257
258         // This method has different signature for x64 and other platforms and is done for performance reasons.
259         internal unsafe static void Memmove(byte* dest, byte* src, nuint len)
260         {
261 #if AMD64 || (BIT32 && !ARM)
262             const nuint CopyThreshold = 2048;
263 #elif ARM64
264 #if PLATFORM_WINDOWS
265             // Determined optimal value for Windows.
266             // https://github.com/dotnet/coreclr/issues/13843
267             const nuint CopyThreshold = UInt64.MaxValue;
268 #else // PLATFORM_WINDOWS
269             // Managed code is currently faster than glibc unoptimized memmove
270             // TODO-ARM64-UNIX-OPT revisit when glibc optimized memmove is in Linux distros
271             // https://github.com/dotnet/coreclr/issues/13844
272             const nuint CopyThreshold = UInt64.MaxValue;
273 #endif // PLATFORM_WINDOWS
274 #else
275             const nuint CopyThreshold = 512;
276 #endif // AMD64 || (BIT32 && !ARM)
277
278             // P/Invoke into the native version when the buffers are overlapping.
279
280             if (((nuint)dest - (nuint)src < len) || ((nuint)src - (nuint)dest < len)) goto PInvoke;
281
282             byte* srcEnd = src + len;
283             byte* destEnd = dest + len;
284
285             if (len <= 16) goto MCPY02;
286             if (len > 64) goto MCPY05;
287
288             MCPY00:
289             // Copy bytes which are multiples of 16 and leave the remainder for MCPY01 to handle.
290             Debug.Assert(len > 16 && len <= 64);
291 #if HAS_CUSTOM_BLOCKS
292             *(Block16*)dest = *(Block16*)src;                   // [0,16]
293 #elif BIT64
294             *(long*)dest = *(long*)src;
295             *(long*)(dest + 8) = *(long*)(src + 8);             // [0,16]
296 #else
297             *(int*)dest = *(int*)src;
298             *(int*)(dest + 4) = *(int*)(src + 4);
299             *(int*)(dest + 8) = *(int*)(src + 8);
300             *(int*)(dest + 12) = *(int*)(src + 12);             // [0,16]
301 #endif
302             if (len <= 32) goto MCPY01;
303 #if HAS_CUSTOM_BLOCKS
304             *(Block16*)(dest + 16) = *(Block16*)(src + 16);     // [0,32]
305 #elif BIT64
306             *(long*)(dest + 16) = *(long*)(src + 16);
307             *(long*)(dest + 24) = *(long*)(src + 24);           // [0,32]
308 #else
309             *(int*)(dest + 16) = *(int*)(src + 16);
310             *(int*)(dest + 20) = *(int*)(src + 20);
311             *(int*)(dest + 24) = *(int*)(src + 24);
312             *(int*)(dest + 28) = *(int*)(src + 28);             // [0,32]
313 #endif
314             if (len <= 48) goto MCPY01;
315 #if HAS_CUSTOM_BLOCKS
316             *(Block16*)(dest + 32) = *(Block16*)(src + 32);     // [0,48]
317 #elif BIT64
318             *(long*)(dest + 32) = *(long*)(src + 32);
319             *(long*)(dest + 40) = *(long*)(src + 40);           // [0,48]
320 #else
321             *(int*)(dest + 32) = *(int*)(src + 32);
322             *(int*)(dest + 36) = *(int*)(src + 36);
323             *(int*)(dest + 40) = *(int*)(src + 40);
324             *(int*)(dest + 44) = *(int*)(src + 44);             // [0,48]
325 #endif
326
327             MCPY01:
328             // Unconditionally copy the last 16 bytes using destEnd and srcEnd and return.
329             Debug.Assert(len > 16 && len <= 64);
330 #if HAS_CUSTOM_BLOCKS
331             *(Block16*)(destEnd - 16) = *(Block16*)(srcEnd - 16);
332 #elif BIT64
333             *(long*)(destEnd - 16) = *(long*)(srcEnd - 16);
334             *(long*)(destEnd - 8) = *(long*)(srcEnd - 8);
335 #else
336             *(int*)(destEnd - 16) = *(int*)(srcEnd - 16);
337             *(int*)(destEnd - 12) = *(int*)(srcEnd - 12);
338             *(int*)(destEnd - 8) = *(int*)(srcEnd - 8);
339             *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
340 #endif
341             return;
342
343             MCPY02:
344             // Copy the first 8 bytes and then unconditionally copy the last 8 bytes and return.
345             if ((len & 24) == 0) goto MCPY03;
346             Debug.Assert(len >= 8 && len <= 16);
347 #if BIT64
348             *(long*)dest = *(long*)src;
349             *(long*)(destEnd - 8) = *(long*)(srcEnd - 8);
350 #else
351             *(int*)dest = *(int*)src;
352             *(int*)(dest + 4) = *(int*)(src + 4);
353             *(int*)(destEnd - 8) = *(int*)(srcEnd - 8);
354             *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
355 #endif
356             return;
357
358             MCPY03:
359             // Copy the first 4 bytes and then unconditionally copy the last 4 bytes and return.
360             if ((len & 4) == 0) goto MCPY04;
361             Debug.Assert(len >= 4 && len < 8);
362             *(int*)dest = *(int*)src;
363             *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
364             return;
365
366             MCPY04:
367             // Copy the first byte. For pending bytes, do an unconditionally copy of the last 2 bytes and return.
368             Debug.Assert(len < 4);
369             if (len == 0) return;
370             *dest = *src;
371             if ((len & 2) == 0) return;
372             *(short*)(destEnd - 2) = *(short*)(srcEnd - 2);
373             return;
374
375             MCPY05:
376             // PInvoke to the native version when the copy length exceeds the threshold.
377             if (len > CopyThreshold)
378             {
379                 goto PInvoke;
380             }
381             // Copy 64-bytes at a time until the remainder is less than 64.
382             // If remainder is greater than 16 bytes, then jump to MCPY00. Otherwise, unconditionally copy the last 16 bytes and return.
383             Debug.Assert(len > 64 && len <= CopyThreshold);
384             nuint n = len >> 6;
385
386             MCPY06:
387 #if HAS_CUSTOM_BLOCKS
388             *(Block64*)dest = *(Block64*)src;
389 #elif BIT64
390             *(long*)dest = *(long*)src;
391             *(long*)(dest + 8) = *(long*)(src + 8);
392             *(long*)(dest + 16) = *(long*)(src + 16);
393             *(long*)(dest + 24) = *(long*)(src + 24);
394             *(long*)(dest + 32) = *(long*)(src + 32);
395             *(long*)(dest + 40) = *(long*)(src + 40);
396             *(long*)(dest + 48) = *(long*)(src + 48);
397             *(long*)(dest + 56) = *(long*)(src + 56);
398 #else
399             *(int*)dest = *(int*)src;
400             *(int*)(dest + 4) = *(int*)(src + 4);
401             *(int*)(dest + 8) = *(int*)(src + 8);
402             *(int*)(dest + 12) = *(int*)(src + 12);
403             *(int*)(dest + 16) = *(int*)(src + 16);
404             *(int*)(dest + 20) = *(int*)(src + 20);
405             *(int*)(dest + 24) = *(int*)(src + 24);
406             *(int*)(dest + 28) = *(int*)(src + 28);
407             *(int*)(dest + 32) = *(int*)(src + 32);
408             *(int*)(dest + 36) = *(int*)(src + 36);
409             *(int*)(dest + 40) = *(int*)(src + 40);
410             *(int*)(dest + 44) = *(int*)(src + 44);
411             *(int*)(dest + 48) = *(int*)(src + 48);
412             *(int*)(dest + 52) = *(int*)(src + 52);
413             *(int*)(dest + 56) = *(int*)(src + 56);
414             *(int*)(dest + 60) = *(int*)(src + 60);
415 #endif
416             dest += 64;
417             src += 64;
418             n--;
419             if (n != 0) goto MCPY06;
420
421             len %= 64;
422             if (len > 16) goto MCPY00;
423 #if HAS_CUSTOM_BLOCKS
424             *(Block16*)(destEnd - 16) = *(Block16*)(srcEnd - 16);
425 #elif BIT64
426             *(long*)(destEnd - 16) = *(long*)(srcEnd - 16);
427             *(long*)(destEnd - 8) = *(long*)(srcEnd - 8);
428 #else
429             *(int*)(destEnd - 16) = *(int*)(srcEnd - 16);
430             *(int*)(destEnd - 12) = *(int*)(srcEnd - 12);
431             *(int*)(destEnd - 8) = *(int*)(srcEnd - 8);
432             *(int*)(destEnd - 4) = *(int*)(srcEnd - 4);
433 #endif
434             return;
435
436             PInvoke:
437             _Memmove(dest, src, len);
438         }
439         
440         // Non-inlinable wrapper around the QCall that avoids poluting the fast path
441         // with P/Invoke prolog/epilog.
442         [MethodImplAttribute(MethodImplOptions.NoInlining)]
443         private unsafe static void _Memmove(byte* dest, byte* src, nuint len)
444         {
445             __Memmove(dest, src, len);
446         }
447
448         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
449         extern private unsafe static void __Memmove(byte* dest, byte* src, nuint len);
450
451         // The attributes on this method are chosen for best JIT performance. 
452         // Please do not edit unless intentional.
453         [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
454         [CLSCompliant(false)]
455         public static unsafe void MemoryCopy(void* source, void* destination, long destinationSizeInBytes, long sourceBytesToCopy)
456         {
457             if (sourceBytesToCopy > destinationSizeInBytes)
458             {
459                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceBytesToCopy);
460             }
461             Memmove((byte*)destination, (byte*)source, checked((nuint)sourceBytesToCopy));
462         }
463
464
465         // The attributes on this method are chosen for best JIT performance. 
466         // Please do not edit unless intentional.
467         [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
468         [CLSCompliant(false)]
469         public static unsafe void MemoryCopy(void* source, void* destination, ulong destinationSizeInBytes, ulong sourceBytesToCopy)
470         {
471             if (sourceBytesToCopy > destinationSizeInBytes)
472             {
473                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceBytesToCopy);
474             }
475 #if BIT64
476             Memmove((byte*)destination, (byte*)source, sourceBytesToCopy);
477 #else // BIT64
478             Memmove((byte*)destination, (byte*)source, checked((uint)sourceBytesToCopy));
479 #endif // BIT64
480         }
481         
482 #if HAS_CUSTOM_BLOCKS        
483         [StructLayout(LayoutKind.Sequential, Size = 16)]
484         private struct Block16 { }
485
486         [StructLayout(LayoutKind.Sequential, Size = 64)]
487         private struct Block64 { } 
488 #endif // HAS_CUSTOM_BLOCKS         
489     }
490 }