File: src\libraries\System.Private.CoreLib\src\System\Runtime\InteropServices\NativeMemory.Unix.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.Numerics;
using System.Runtime.CompilerServices;
 
namespace System.Runtime.InteropServices
{
    /// <summary>This class contains methods that are mainly used to manage native memory.</summary>
    public static unsafe partial class NativeMemory
    {
        /// <summary>Allocates an aligned block of memory of the specified size and alignment, in bytes.</summary>
        /// <param name="byteCount">The size, in bytes, of the block to allocate.</param>
        /// <param name="alignment">The alignment, in bytes, of the block to allocate. This must be a power of <c>2</c>.</param>
        /// <returns>A pointer to the allocated aligned block of memory.</returns>
        /// <exception cref="ArgumentException"><paramref name="alignment" /> is not a power of two.</exception>
        /// <exception cref="OutOfMemoryException">Allocating <paramref name="byteCount" /> of memory with <paramref name="alignment" /> failed.</exception>
        /// <remarks>
        ///     <para>This method allows <paramref name="byteCount" /> to be <c>0</c> and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.</para>
        ///     <para>This method is a thin wrapper over the C <c>aligned_alloc</c> API or a platform dependent aligned allocation API such as <c>_aligned_malloc</c> on Win32.</para>
        ///     <para>This method is not compatible with <see cref="Free" /> or <see cref="Realloc" />, instead <see cref="AlignedFree" /> or <see cref="AlignedRealloc" /> should be called.</para>
        /// </remarks>
        [CLSCompliant(false)]
        public static void* AlignedAlloc(nuint byteCount, nuint alignment)
        {
            if (!BitOperations.IsPow2(alignment))
            {
                // The C standard doesn't define what a valid alignment is, however Windows and POSIX requires a power of 2
                ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AlignmentMustBePow2);
            }
 
            // The C standard and POSIX requires size to be a multiple of alignment and we want an "empty" allocation for zero
            // POSIX additionally requires alignment to be at least sizeof(void*)
 
            // The adjustment for byteCount can overflow here, and such overflow is generally "harmless". This is because of the
            // requirement that alignment be a power of two and that byteCount be a multiple of alignment. Given both of these
            // constraints we should only overflow for byteCount > (nuint.MaxValue & ~(alignment - 1)). When such an overflow
            // occurs we will get a result that is less than alignment which will cause the allocation to fail.
            //
            // However, posix_memalign differs from aligned_alloc in that it may return a valid pointer for zero and we need to
            // ensure we OOM for this scenario (which can occur for `nuint.MaxValue`) and so we have to check the adjusted size.
 
            nuint adjustedAlignment = Math.Max(alignment, (uint)sizeof(void*));
            nuint adjustedByteCount = (byteCount != 0) ? (byteCount + (adjustedAlignment - 1)) & ~(adjustedAlignment - 1) : adjustedAlignment;
 
            void* result = (adjustedByteCount < byteCount) ? null : Interop.Sys.AlignedAlloc(adjustedAlignment, adjustedByteCount);
 
            if (result == null)
            {
                ThrowHelper.ThrowOutOfMemoryException();
            }
 
            return result;
        }
 
        /// <summary>Frees an aligned block of memory.</summary>
        /// <param name="ptr">A pointer to the aligned block of memory that should be freed.</param>
        /// <remarks>
        ///    <para>This method does nothing if <paramref name="ptr" /> is <c>null</c>.</para>
        ///    <para>This method is a thin wrapper over the C <c>free</c> API or a platform dependent aligned free API such as <c>_aligned_free</c> on Win32.</para>
        /// </remarks>
        [CLSCompliant(false)]
        public static void AlignedFree(void* ptr)
        {
            if (ptr != null)
            {
                Interop.Sys.AlignedFree(ptr);
            }
        }
 
        /// <summary>Reallocates an aligned block of memory of the specified size and alignment, in bytes.</summary>
        /// <param name="ptr">The previously allocated block of memory.</param>
        /// <param name="byteCount">The size, in bytes, of the block to allocate.</param>
        /// <param name="alignment">The alignment, in bytes, of the block to allocate. This must be a power of <c>2</c>.</param>
        /// <returns>A pointer to the reallocated aligned block of memory.</returns>
        /// <exception cref="ArgumentException"><paramref name="alignment" /> is not a power of two.</exception>
        /// <exception cref="OutOfMemoryException">Reallocating <paramref name="byteCount" /> of memory with <paramref name="alignment" /> failed.</exception>
        /// <remarks>
        ///     <para>This method acts as <see cref="AlignedAlloc" /> if <paramref name="ptr" /> is <c>null</c>.</para>
        ///     <para>This method allows <paramref name="byteCount" /> to be <c>0</c> and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.</para>
        ///     <para>This method is a platform dependent aligned reallocation API such as <c>_aligned_realloc</c> on Win32.</para>
        ///     <para>This method is not compatible with <see cref="Free" /> or <see cref="Realloc" />, instead <see cref="AlignedFree" /> or <see cref="AlignedRealloc" /> should be called.</para>
        /// </remarks>
        [CLSCompliant(false)]
        public static void* AlignedRealloc(void* ptr, nuint byteCount, nuint alignment)
        {
            if (!BitOperations.IsPow2(alignment))
            {
                // The C standard doesn't define what a valid alignment is, however Windows and POSIX requires a power of 2
                ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AlignmentMustBePow2);
            }
 
            // The C standard and POSIX requires size to be a multiple of alignment and we want an "empty" allocation for zero
            // POSIX additionally requires alignment to be at least sizeof(void*)
 
            // The adjustment for byteCount can overflow here, and such overflow is generally "harmless". This is because of the
            // requirement that alignment be a power of two and that byteCount be a multiple of alignment. Given both of these
            // constraints we should only overflow for byteCount > (nuint.MaxValue & ~(alignment - 1)). When such an overflow
            // occurs we will get a result that is less than alignment which will cause the allocation to fail.
            //
            // However, posix_memalign differs from aligned_alloc in that it may return a valid pointer for zero and we need to
            // ensure we OOM for this scenario (which can occur for `nuint.MaxValue`) and so we have to check the adjusted size.
 
            nuint adjustedAlignment = Math.Max(alignment, (uint)sizeof(void*));
            nuint adjustedByteCount = (byteCount != 0) ? (byteCount + (adjustedAlignment - 1)) & ~(adjustedAlignment - 1) : adjustedAlignment;
 
            void* result = (adjustedByteCount < byteCount) ? null : Interop.Sys.AlignedRealloc(ptr, adjustedAlignment, adjustedByteCount);
 
            if (result == null)
            {
                ThrowHelper.ThrowOutOfMemoryException();
            }
 
            return result;
        }
 
        /// <summary>Allocates a block of memory of the specified size, in bytes.</summary>
        /// <param name="byteCount">The size, in bytes, of the block to allocate.</param>
        /// <returns>A pointer to the allocated block of memory.</returns>
        /// <exception cref="OutOfMemoryException">Allocating <paramref name="byteCount" /> of memory failed.</exception>
        /// <remarks>
        ///     <para>This method allows <paramref name="byteCount" /> to be <c>0</c> and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.</para>
        ///     <para>This method is a thin wrapper over the C <c>malloc</c> API.</para>
        /// </remarks>
        [CLSCompliant(false)]
        public static void* Alloc(nuint byteCount)
        {
            // The C standard does not define what happens when size == 0, we want an "empty" allocation
            void* result = Interop.Sys.Malloc((byteCount != 0) ? byteCount : 1);
 
            if (result == null)
            {
                ThrowHelper.ThrowOutOfMemoryException();
            }
 
            return result;
        }
 
        /// <summary>Allocates and zeroes a block of memory of the specified size, in elements.</summary>
        /// <param name="elementCount">The count, in elements, of the block to allocate.</param>
        /// <param name="elementSize">The size, in bytes, of each element in the allocation.</param>
        /// <returns>A pointer to the allocated and zeroed block of memory.</returns>
        /// <exception cref="OutOfMemoryException">Allocating <paramref name="elementCount" /> * <paramref name="elementSize" /> bytes of memory failed.</exception>
        /// <remarks>
        ///     <para>This method allows <paramref name="elementCount" /> and/or <paramref name="elementSize" /> to be <c>0</c> and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.</para>
        ///     <para>This method is a thin wrapper over the C <c>calloc</c> API.</para>
        /// </remarks>
        [CLSCompliant(false)]
        public static void* AllocZeroed(nuint elementCount, nuint elementSize)
        {
            void* result;
 
            if ((elementCount != 0) && (elementSize != 0))
            {
                result = Interop.Sys.Calloc(elementCount, elementSize);
            }
            else
            {
                // The C standard does not define what happens when num == 0 or size == 0, we want an "empty" allocation
                result = Interop.Sys.Malloc(1);
            }
 
            if (result == null)
            {
                ThrowHelper.ThrowOutOfMemoryException();
            }
 
            return result;
        }
 
        /// <summary>Frees a block of memory.</summary>
        /// <param name="ptr">A pointer to the block of memory that should be freed.</param>
        /// <remarks>
        ///    <para>This method does nothing if <paramref name="ptr" /> is <c>null</c>.</para>
        ///    <para>This method is a thin wrapper over the C <c>free</c> API.</para>
        /// </remarks>
        [CLSCompliant(false)]
        public static void Free(void* ptr)
        {
            if (ptr != null)
            {
                Interop.Sys.Free(ptr);
            }
        }
 
        /// <summary>Reallocates a block of memory to be the specified size, in bytes.</summary>
        /// <param name="ptr">The previously allocated block of memory.</param>
        /// <param name="byteCount">The size, in bytes, of the reallocated block.</param>
        /// <returns>A pointer to the reallocated block of memory.</returns>
        /// <exception cref="OutOfMemoryException">Reallocating <paramref name="byteCount" /> of memory failed.</exception>
        /// <remarks>
        ///     <para>This method acts as <see cref="Alloc" /> if <paramref name="ptr" /> is <c>null</c>.</para>
        ///     <para>This method allows <paramref name="byteCount" /> to be <c>0</c> and will return a valid pointer that should not be dereferenced and that should be passed to free to avoid memory leaks.</para>
        ///     <para>This method is a thin wrapper over the C <c>realloc</c> API.</para>
        /// </remarks>
        [CLSCompliant(false)]
        public static void* Realloc(void* ptr, nuint byteCount)
        {
            // The C standard does not define what happens when size == 0, we want an "empty" allocation
            void* result = Interop.Sys.Realloc(ptr, (byteCount != 0) ? byteCount : 1);
 
            if (result == null)
            {
                ThrowHelper.ThrowOutOfMemoryException();
            }
 
            return result;
        }
    }
}