9d4d693322eea0a05d6be785f92a682865e28922
[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         // 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
41         // platforms.
42         //
43         internal unsafe static int IndexOfByte(byte* src, byte value, int index, int count)
44         {
45             Debug.Assert(src != null, "src should not be null");
46
47             byte* pByte = src + index;
48
49             // Align up the pointer to sizeof(int).
50             while (((int)pByte & 3) != 0)
51             {
52                 if (count == 0)
53                     return -1;
54                 else if (*pByte == value)
55                     return (int)(pByte - src);
56
57                 count--;
58                 pByte++;
59             }
60
61             // Fill comparer with value byte for comparisons
62             //
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;
67
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.
70             while (count > 3)
71             {
72                 // Test the buffer for presence of value. comparer contains the byte
73                 // replicated 4 times.
74                 uint t1 = *(uint*)pByte;
75                 t1 = t1 ^ comparer;
76                 uint t2 = 0x7efefeff + t1;
77                 t1 = t1 ^ 0xffffffff;
78                 t1 = t1 ^ t2;
79                 t1 = t1 & 0x81010100;
80
81                 // if t1 is zero then these 4-bytes don't contain a match
82                 if (t1 != 0)
83                 {
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)
87                         return foundIndex;
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;
94                 }
95
96                 count -= 4;
97                 pByte += 4;
98             }
99
100             // Catch any bytes that might be left at the tail of the buffer
101             while (count > 0)
102             {
103                 if (*pByte == value)
104                     return (int)(pByte - src);
105
106                 count--;
107                 pByte++;
108             }
109
110             // If we don't have a match return -1;
111             return -1;
112         }
113
114         // Returns a bool to indicate if the array is of primitive data types
115         // or not.
116         [MethodImplAttribute(MethodImplOptions.InternalCall)]
117         private static extern bool IsPrimitiveTypeArray(Array array);
118
119         // Gets a particular byte out of the array.  The array must be an
120         // array of primitives.  
121         //
122         // This essentially does the following: 
123         // return ((byte*)array) + index.
124         //
125         [MethodImplAttribute(MethodImplOptions.InternalCall)]
126         private static extern byte _GetByte(Array array, int index);
127
128         public static byte GetByte(Array array, int index)
129         {
130             // Is the array present?
131             if (array == null)
132                 throw new ArgumentNullException(nameof(array));
133
134             // Is it of primitive types?
135             if (!IsPrimitiveTypeArray(array))
136                 throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(array));
137
138             // Is the index in valid range of the array?
139             if (index < 0 || index >= _ByteLength(array))
140                 throw new ArgumentOutOfRangeException(nameof(index));
141
142             return _GetByte(array, index);
143         }
144
145         // Sets a particular byte in an the array.  The array must be an
146         // array of primitives.  
147         //
148         // This essentially does the following: 
149         // *(((byte*)array) + index) = value.
150         //
151         [MethodImplAttribute(MethodImplOptions.InternalCall)]
152         private static extern void _SetByte(Array array, int index, byte value);
153
154         public static void SetByte(Array array, int index, byte value)
155         {
156             // Is the array present?
157             if (array == null)
158                 throw new ArgumentNullException(nameof(array));
159
160             // Is it of primitive types?
161             if (!IsPrimitiveTypeArray(array))
162                 throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(array));
163
164             // Is the index in valid range of the array?
165             if (index < 0 || index >= _ByteLength(array))
166                 throw new ArgumentOutOfRangeException(nameof(index));
167
168             // Make the FCall to do the work
169             _SetByte(array, index, value);
170         }
171
172
173         // Gets a particular byte out of the array.  The array must be an
174         // array of primitives.  
175         //
176         // This essentially does the following: 
177         // return array.length * sizeof(array.UnderlyingElementType).
178         //
179         [MethodImplAttribute(MethodImplOptions.InternalCall)]
180         private static extern int _ByteLength(Array array);
181
182         public static int ByteLength(Array array)
183         {
184             // Is the array present?
185             if (array == null)
186                 throw new ArgumentNullException(nameof(array));
187
188             // Is it of primitive types?
189             if (!IsPrimitiveTypeArray(array))
190                 throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(array));
191
192             return _ByteLength(array);
193         }
194
195         internal unsafe static void ZeroMemory(byte* src, long len)
196         {
197             while (len-- > 0)
198                 *(src + len) = 0;
199         }
200
201         internal unsafe static void Memcpy(byte[] dest, int destIndex, byte* src, int srcIndex, int len)
202         {
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.
207             if (len == 0)
208                 return;
209             fixed (byte* pDest = dest)
210             {
211                 Memcpy(pDest + destIndex, src + srcIndex, len);
212             }
213         }
214
215         internal unsafe static void Memcpy(byte* pDest, int destIndex, byte[] src, int srcIndex, int len)
216         {
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.
221             if (len == 0)
222                 return;
223             fixed (byte* pSrc = src)
224             {
225                 Memcpy(pDest + destIndex, pSrc + srcIndex, len);
226             }
227         }
228
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.
231
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.
238 #if ARM
239         [MethodImplAttribute(MethodImplOptions.InternalCall)]
240         internal unsafe static extern void Memcpy(byte* dest, byte* src, int len);
241 #else // ARM
242         [MethodImplAttribute(MethodImplOptions.AggressiveInlining)]
243         internal unsafe static void Memcpy(byte* dest, byte* src, int len)
244         {
245             Debug.Assert(len >= 0, "Negative length in memcopy!");
246             Memmove(dest, src, (uint)len);
247         }
248 #endif // ARM
249
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)
252         {
253 #if AMD64 || (BIT32 && !ARM)
254             const nuint CopyThreshold = 2048;
255 #elif ARM64
256 #if PLATFORM_WINDOWS
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
266 #else
267             const nuint CopyThreshold = 512;
268 #endif // AMD64 || (BIT32 && !ARM)
269
270             // P/Invoke into the native version when the buffers are overlapping.
271
272             if (((nuint)dest - (nuint)src < len) || ((nuint)src - (nuint)dest < len)) goto PInvoke;
273
274             byte* srcEnd = src + len;
275             byte* destEnd = dest + len;
276
277             if (len <= 16) goto MCPY02;
278             if (len > 64) goto MCPY05;
279
280             MCPY00:
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]
285 #elif BIT64
286             *(long*)dest = *(long*)src;
287             *(long*)(dest + 8) = *(long*)(src + 8);             // [0,16]
288 #else
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]
293 #endif
294             if (len <= 32) goto MCPY01;
295 #if HAS_CUSTOM_BLOCKS
296             *(Block16*)(dest + 16) = *(Block16*)(src + 16);     // [0,32]
297 #elif BIT64
298             *(long*)(dest + 16) = *(long*)(src + 16);
299             *(long*)(dest + 24) = *(long*)(src + 24);           // [0,32]
300 #else
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]
305 #endif
306             if (len <= 48) goto MCPY01;
307 #if HAS_CUSTOM_BLOCKS
308             *(Block16*)(dest + 32) = *(Block16*)(src + 32);     // [0,48]
309 #elif BIT64
310             *(long*)(dest + 32) = *(long*)(src + 32);
311             *(long*)(dest + 40) = *(long*)(src + 40);           // [0,48]
312 #else
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]
317 #endif
318
319             MCPY01:
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);
324 #elif BIT64
325             *(long*)(destEnd - 16) = *(long*)(srcEnd - 16);
326             *(long*)(destEnd - 8) = *(long*)(srcEnd - 8);
327 #else
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);
332 #endif
333             return;
334
335             MCPY02:
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);
339 #if BIT64
340             *(long*)dest = *(long*)src;
341             *(long*)(destEnd - 8) = *(long*)(srcEnd - 8);
342 #else
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);
347 #endif
348             return;
349
350             MCPY03:
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);
356             return;
357
358             MCPY04:
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;
362             *dest = *src;
363             if ((len & 2) == 0) return;
364             *(short*)(destEnd - 2) = *(short*)(srcEnd - 2);
365             return;
366
367             MCPY05:
368             // PInvoke to the native version when the copy length exceeds the threshold.
369             if (len > CopyThreshold)
370             {
371                 goto PInvoke;
372             }
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);
376             nuint n = len >> 6;
377
378             MCPY06:
379 #if HAS_CUSTOM_BLOCKS
380             *(Block64*)dest = *(Block64*)src;
381 #elif BIT64
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);
390 #else
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);
407 #endif
408             dest += 64;
409             src += 64;
410             n--;
411             if (n != 0) goto MCPY06;
412
413             len %= 64;
414             if (len > 16) goto MCPY00;
415 #if HAS_CUSTOM_BLOCKS
416             *(Block16*)(destEnd - 16) = *(Block16*)(srcEnd - 16);
417 #elif BIT64
418             *(long*)(destEnd - 16) = *(long*)(srcEnd - 16);
419             *(long*)(destEnd - 8) = *(long*)(srcEnd - 8);
420 #else
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);
425 #endif
426             return;
427
428             PInvoke:
429             _Memmove(dest, src, len);
430         }
431         
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)
436         {
437             __Memmove(dest, src, len);
438         }
439
440         [DllImport(JitHelpers.QCall, CharSet = CharSet.Unicode)]
441         extern private unsafe static void __Memmove(byte* dest, byte* src, nuint len);
442
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)
448         {
449             if (sourceBytesToCopy > destinationSizeInBytes)
450             {
451                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceBytesToCopy);
452             }
453             Memmove((byte*)destination, (byte*)source, checked((nuint)sourceBytesToCopy));
454         }
455
456
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)
462         {
463             if (sourceBytesToCopy > destinationSizeInBytes)
464             {
465                 ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceBytesToCopy);
466             }
467 #if BIT64
468             Memmove((byte*)destination, (byte*)source, sourceBytesToCopy);
469 #else // BIT64
470             Memmove((byte*)destination, (byte*)source, checked((uint)sourceBytesToCopy));
471 #endif // BIT64
472         }
473         
474 #if HAS_CUSTOM_BLOCKS        
475         [StructLayout(LayoutKind.Sequential, Size = 16)]
476         private struct Block16 { }
477
478         [StructLayout(LayoutKind.Sequential, Size = 64)]
479         private struct Block64 { } 
480 #endif // HAS_CUSTOM_BLOCKS         
481     }
482 }