File: src\libraries\System.Private.CoreLib\src\System\Buffer.cs
Web Access
Project: src\src\coreclr\System.Private.CoreLib\System.Private.CoreLib.csproj (System.Private.CoreLib)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
 
namespace System
{
    public static partial class Buffer
    {
        // Copies from one primitive array to another primitive array without
        // respecting types.  This calls memmove internally.  The count and
        // offset parameters here are in bytes.  If you want to use traditional
        // array element indices and counts, use Array.Copy.
        public static unsafe void BlockCopy(Array src, int srcOffset, Array dst, int dstOffset, int count)
        {
            ArgumentNullException.ThrowIfNull(src);
            ArgumentNullException.ThrowIfNull(dst);
 
            nuint uSrcLen = src.NativeLength;
            if (src.GetType() != typeof(byte[]))
            {
                if (!src.GetCorElementTypeOfElementType().IsPrimitiveType())
                    throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(src));
                uSrcLen *= (nuint)src.GetElementSize();
            }
 
            nuint uDstLen = uSrcLen;
            if (src != dst)
            {
                uDstLen = dst.NativeLength;
                if (dst.GetType() != typeof(byte[]))
                {
                    if (!dst.GetCorElementTypeOfElementType().IsPrimitiveType())
                        throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(dst));
                    uDstLen *= (nuint)dst.GetElementSize();
                }
            }
 
            ArgumentOutOfRangeException.ThrowIfNegative(srcOffset);
            ArgumentOutOfRangeException.ThrowIfNegative(dstOffset);
            ArgumentOutOfRangeException.ThrowIfNegative(count);
 
            nuint uCount = (nuint)count;
            nuint uSrcOffset = (nuint)srcOffset;
            nuint uDstOffset = (nuint)dstOffset;
 
            if ((uSrcLen < uSrcOffset + uCount) || (uDstLen < uDstOffset + uCount))
                throw new ArgumentException(SR.Argument_InvalidOffLen);
 
            Memmove(ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(dst), uDstOffset), ref Unsafe.AddByteOffset(ref MemoryMarshal.GetArrayDataReference(src), uSrcOffset), uCount);
        }
 
        public static int ByteLength(Array array)
        {
            ArgumentNullException.ThrowIfNull(array);
 
            // Is it of primitive types?
            if (!array.GetCorElementTypeOfElementType().IsPrimitiveType())
                throw new ArgumentException(SR.Arg_MustBePrimArray, nameof(array));
 
            nuint byteLength = array.NativeLength * (nuint)array.GetElementSize();
 
            // This API is exposed both as Buffer.ByteLength and also used indirectly in argument
            // checks for Buffer.GetByte/SetByte.
            //
            // If somebody called Get/SetByte on 2GB+ arrays, there is a decent chance that
            // the computation of the index has overflowed. Thus we intentionally always
            // throw on 2GB+ arrays in Get/SetByte argument checks (even for indices <2GB)
            // to prevent people from running into a trap silently.
 
            return checked((int)byteLength);
        }
 
        public static byte GetByte(Array array, int index)
        {
            // array argument validation done via ByteLength
            if ((uint)index >= (uint)ByteLength(array))
            {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);
            }
 
            return Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), index);
        }
 
        public static void SetByte(Array array, int index, byte value)
        {
            // array argument validation done via ByteLength
            if ((uint)index >= (uint)ByteLength(array))
            {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index);
            }
 
            Unsafe.Add(ref MemoryMarshal.GetArrayDataReference(array), index) = value;
        }
 
        // The attributes on this method are chosen for best JIT performance.
        // Please do not edit unless intentional.
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        [CLSCompliant(false)]
        public static unsafe void MemoryCopy(void* source, void* destination, long destinationSizeInBytes, long sourceBytesToCopy)
        {
            if (sourceBytesToCopy > destinationSizeInBytes)
            {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceBytesToCopy);
            }
 
            Memmove(ref *(byte*)destination, ref *(byte*)source, checked((nuint)sourceBytesToCopy));
        }
 
        // The attributes on this method are chosen for best JIT performance.
        // Please do not edit unless intentional.
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        [CLSCompliant(false)]
        public static unsafe void MemoryCopy(void* source, void* destination, ulong destinationSizeInBytes, ulong sourceBytesToCopy)
        {
            if (sourceBytesToCopy > destinationSizeInBytes)
            {
                ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.sourceBytesToCopy);
            }
 
            Memmove(ref *(byte*)destination, ref *(byte*)source, checked((nuint)sourceBytesToCopy));
        }
 
        // Non-inlinable wrapper around the QCall that avoids polluting the fast path
        // with P/Invoke prolog/epilog.
        [MethodImpl(MethodImplOptions.NoInlining)]
        internal static unsafe void _Memmove(ref byte dest, ref byte src, nuint len)
        {
            fixed (byte* pDest = &dest)
            fixed (byte* pSrc = &src)
                __Memmove(pDest, pSrc, len);
        }
 
        // Non-inlinable wrapper around the QCall that avoids polluting the fast path
        // with P/Invoke prolog/epilog.
        [MethodImpl(MethodImplOptions.NoInlining)]
        internal static unsafe void _ZeroMemory(ref byte b, nuint byteLength)
        {
            fixed (byte* bytePointer = &b)
            {
                __ZeroMemory(bytePointer, byteLength);
            }
        }
 
#if !MONO // Mono BulkMoveWithWriteBarrier is in terms of elements (not bytes) and takes a type handle.
 
        [Intrinsic]
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        internal static unsafe void Memmove<T>(ref T destination, ref T source, nuint elementCount)
        {
#pragma warning disable 8500 // sizeof of managed types
            if (!RuntimeHelpers.IsReferenceOrContainsReferences<T>())
            {
                // Blittable memmove
                SpanHelpers.Memmove(
                    ref Unsafe.As<T, byte>(ref destination),
                    ref Unsafe.As<T, byte>(ref source),
                    elementCount * (nuint)sizeof(T));
            }
            else
            {
                // Non-blittable memmove
                BulkMoveWithWriteBarrier(
                    ref Unsafe.As<T, byte>(ref destination),
                    ref Unsafe.As<T, byte>(ref source),
                    elementCount * (nuint)sizeof(T));
            }
#pragma warning restore 8500
        }
 
        // The maximum block size to for __BulkMoveWithWriteBarrier FCall. This is required to avoid GC starvation.
#if DEBUG // Stress the mechanism in debug builds
        private const uint BulkMoveWithWriteBarrierChunk = 0x400;
#else
        private const uint BulkMoveWithWriteBarrierChunk = 0x4000;
#endif
 
#if NATIVEAOT
        [System.Runtime.RuntimeExport("RhBuffer_BulkMoveWithWriteBarrier")]
#endif
        internal static void BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount)
        {
            if (byteCount <= BulkMoveWithWriteBarrierChunk)
                __BulkMoveWithWriteBarrier(ref destination, ref source, byteCount);
            else
                _BulkMoveWithWriteBarrier(ref destination, ref source, byteCount);
        }
 
        // Non-inlinable wrapper around the loop for copying large blocks in chunks
        [MethodImpl(MethodImplOptions.NoInlining)]
        private static void _BulkMoveWithWriteBarrier(ref byte destination, ref byte source, nuint byteCount)
        {
            Debug.Assert(byteCount > BulkMoveWithWriteBarrierChunk);
 
            if (Unsafe.AreSame(ref source, ref destination))
                return;
 
            // This is equivalent to: (destination - source) >= byteCount || (destination - source) < 0
            if ((nuint)(nint)Unsafe.ByteOffset(ref source, ref destination) >= byteCount)
            {
                // Copy forwards
                do
                {
                    byteCount -= BulkMoveWithWriteBarrierChunk;
                    __BulkMoveWithWriteBarrier(ref destination, ref source, BulkMoveWithWriteBarrierChunk);
                    destination = ref Unsafe.AddByteOffset(ref destination, BulkMoveWithWriteBarrierChunk);
                    source = ref Unsafe.AddByteOffset(ref source, BulkMoveWithWriteBarrierChunk);
                }
                while (byteCount > BulkMoveWithWriteBarrierChunk);
            }
            else
            {
                // Copy backwards
                do
                {
                    byteCount -= BulkMoveWithWriteBarrierChunk;
                    __BulkMoveWithWriteBarrier(ref Unsafe.AddByteOffset(ref destination, byteCount), ref Unsafe.AddByteOffset(ref source, byteCount), BulkMoveWithWriteBarrierChunk);
                }
                while (byteCount > BulkMoveWithWriteBarrierChunk);
            }
            __BulkMoveWithWriteBarrier(ref destination, ref source, byteCount);
        }
 
#endif // !MONO
    }
}