"round down" optimization per ridiculous_fish.
This is free and unencumbered software. Any copyright is dedicated to the Public Domain.
+
+
+License notice for mimalloc
+-----------------------------------
+
+MIT License
+
+Copyright (c) 2019 Microsoft Corporation, Daan Leijen
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
internal static partial class Interop
{
- internal static partial class Sys
+ internal static unsafe partial class Sys
{
- [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_MemAlloc")]
- internal static extern IntPtr MemAlloc(nuint sizeInBytes);
+ [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_AlignedAlloc")]
+ internal static extern void* AlignedAlloc(nuint alignment, nuint size);
- [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_MemReAlloc")]
- internal static extern IntPtr MemReAlloc(IntPtr ptr, nuint newSize);
+ [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_AlignedFree")]
+ internal static extern void AlignedFree(void* ptr);
- [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_MemFree")]
- internal static extern void MemFree(IntPtr ptr);
+ [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_AlignedRealloc")]
+ internal static extern void* AlignedRealloc(void* ptr, nuint alignment, nuint new_size);
+
+ [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_Calloc")]
+ internal static extern void* Calloc(nuint num, nuint size);
+
+ [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_Free")]
+ internal static extern void Free(void* ptr);
+
+ [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_Malloc")]
+ internal static extern void* Malloc(nuint size);
+
+ [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_Realloc")]
+ internal static extern void* Realloc(void* ptr, nuint new_size);
}
}
internal const string GlobalizationNative = "System.Globalization.Native";
internal const string MsQuic = "msquic.dll";
internal const string HostPolicy = "hostpolicy.dll";
+ internal const string Ucrtbase = "ucrtbase.dll";
}
}
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System;
+using System.Runtime.InteropServices;
+
+internal static partial class Interop
+{
+ internal static unsafe partial class Ucrtbase
+ {
+ [DllImport(Libraries.Ucrtbase, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern void* _aligned_malloc(nuint size, nuint alignment);
+
+ [DllImport(Libraries.Ucrtbase, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern void _aligned_free(void* ptr);
+
+ [DllImport(Libraries.Ucrtbase, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern void* _aligned_realloc(void* ptr, nuint size, nuint alignment);
+
+ [DllImport(Libraries.Ucrtbase, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern void* calloc(nuint num, nuint size);
+
+ [DllImport(Libraries.Ucrtbase, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern void free(void* ptr);
+
+ [DllImport(Libraries.Ucrtbase, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern void* malloc(nuint size);
+
+ [DllImport(Libraries.Ucrtbase, CallingConvention = CallingConvention.Cdecl, ExactSpelling = true)]
+ internal static extern void* realloc(void* ptr, nuint new_size);
+ }
+}
#cmakedefine01 HAVE_GETGROUPLIST
#cmakedefine01 HAVE_SYS_PROCINFO_H
#cmakedefine01 HAVE_IOSS_H
+#cmakedefine01 HAVE_ALIGNED_ALLOC
+#cmakedefine01 HAVE_MALLOC_SIZE
+#cmakedefine01 HAVE_MALLOC_USABLE_SIZE
+#cmakedefine01 HAVE_MALLOC_USABLE_SIZE_NP
+#cmakedefine01 HAVE_POSIX_MEMALIGN
// Mac OS X has stat64, but it is deprecated since plain stat now
// provides the same 64-bit aware struct when targeting OS X > 10.5
DllImportEntry(SystemNative_LChflagsCanSetHiddenFlag)
DllImportEntry(SystemNative_ReadProcessStatusInfo)
DllImportEntry(SystemNative_Log)
- DllImportEntry(SystemNative_MemAlloc)
- DllImportEntry(SystemNative_MemReAlloc)
- DllImportEntry(SystemNative_MemFree)
+ DllImportEntry(SystemNative_AlignedAlloc)
+ DllImportEntry(SystemNative_AlignedFree)
+ DllImportEntry(SystemNative_AlignedRealloc)
+ DllImportEntry(SystemNative_Calloc)
+ DllImportEntry(SystemNative_Free)
+ DllImportEntry(SystemNative_GetUsableSize)
+ DllImportEntry(SystemNative_Malloc)
DllImportEntry(SystemNative_MemSet)
+ DllImportEntry(SystemNative_Realloc)
DllImportEntry(SystemNative_GetSpaceInfoForMountPoint)
DllImportEntry(SystemNative_GetFormatInfoForMountPoint)
DllImportEntry(SystemNative_GetAllMountPoints)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+#include "pal_config.h"
#include "pal_memory.h"
+#include <assert.h>
#include <stdlib.h>
#include <string.h>
-void* SystemNative_MemAlloc(uintptr_t size)
+#if HAVE_MALLOC_SIZE
+ #include <malloc/malloc.h>
+#elif HAVE_MALLOC_USABLE_SIZE
+ #include <malloc.h>
+#elif HAVE_MALLOC_USABLE_SIZE_NP
+ #include <malloc_np.h>
+#else
+ #error "Platform doesn't support malloc_usable_size or malloc_size"
+#endif
+
+void* SystemNative_AlignedAlloc(uintptr_t alignment, uintptr_t size)
{
- return malloc(size);
+#if HAVE_ALIGNED_ALLOC
+ // We want to prefer the standardized aligned_alloc function. However
+ // it cannot be used on __APPLE__ since we target 10.13 and it was
+ // only added in 10.15, but we might be compiling on a 10.15 box.
+ return aligned_alloc(alignment, size);
+#elif HAVE_POSIX_MEMALIGN
+ void* result = NULL;
+ posix_memalign(&result, alignment, size);
+ return result;
+#else
+ #error "Platform doesn't support aligned_alloc or posix_memalign"
+#endif
+}
+
+void SystemNative_AlignedFree(void* ptr)
+{
+ free(ptr);
+}
+
+void* SystemNative_AlignedRealloc(void* ptr, uintptr_t alignment, uintptr_t new_size)
+{
+ void* result = SystemNative_AlignedAlloc(alignment, new_size);
+
+ if (result != NULL)
+ {
+ uintptr_t old_size = SystemNative_GetUsableSize(ptr);
+ assert((ptr != NULL) || (old_size == 0));
+
+ memcpy(result, ptr, (new_size < old_size) ? new_size : old_size);
+ SystemNative_AlignedFree(ptr);
+ }
+
+ return result;
}
-void* SystemNative_MemReAlloc(void* ptr, uintptr_t size)
+void* SystemNative_Calloc(uintptr_t num, uintptr_t size)
{
- return realloc(ptr, size);
+ return calloc(num, size);
}
-void SystemNative_MemFree(void* ptr)
+void SystemNative_Free(void* ptr)
{
free(ptr);
}
+uintptr_t SystemNative_GetUsableSize(void* ptr)
+{
+#if HAVE_MALLOC_SIZE
+ return malloc_size(ptr);
+#elif HAVE_MALLOC_USABLE_SIZE || HAVE_MALLOC_USABLE_SIZE_NP
+ return malloc_usable_size(ptr);
+#else
+ #error "Platform doesn't support malloc_usable_size or malloc_size"
+#endif
+}
+
+void* SystemNative_Malloc(uintptr_t size)
+{
+ return malloc(size);
+}
+
void* SystemNative_MemSet(void* s, int c, uintptr_t n)
{
return memset(s, c, n);
}
+
+void* SystemNative_Realloc(void* ptr, uintptr_t new_size)
+{
+ return realloc(ptr, new_size);
+}
#include "pal_types.h"
/**
- * C runtime malloc
+ * C runtime aligned_alloc
*/
-PALEXPORT void* SystemNative_MemAlloc(uintptr_t size);
+PALEXPORT void* SystemNative_AlignedAlloc(uintptr_t alignment, uintptr_t size);
/**
- * C runtime realloc
+ * Free for C runtime aligned_alloc
+ */
+PALEXPORT void SystemNative_AlignedFree(void* ptr);
+
+/**
+ * Realloc for C runtime aligned_alloc
*/
-PALEXPORT void* SystemNative_MemReAlloc(void* ptr, uintptr_t size);
+PALEXPORT void* SystemNative_AlignedRealloc(void* ptr, uintptr_t alignment, uintptr_t size);
+
+/**
+ * C runtime calloc
+ */
+PALEXPORT void* SystemNative_Calloc(uintptr_t num, uintptr_t size);
/**
* C runtime free
*/
-PALEXPORT void SystemNative_MemFree(void* ptr);
+PALEXPORT void SystemNative_Free(void* ptr);
+
+/**
+ * Get usable size of C runtime malloc
+ */
+PALEXPORT uintptr_t SystemNative_GetUsableSize(void* ptr);
+
+/**
+ * C runtime malloc
+ */
+PALEXPORT void* SystemNative_Malloc(uintptr_t size);
/**
* C runtime memset
*/
PALEXPORT void* SystemNative_MemSet(void* s, int c, uintptr_t n);
+
+/**
+ * C runtime realloc
+ */
+PALEXPORT void* SystemNative_Realloc(void* ptr, uintptr_t new_size);
set(HAVE_SUPPORT_FOR_DUAL_MODE_IPV4_PACKET_INFO 1)
endif ()
+check_symbol_exists(
+ malloc_size
+ malloc/malloc.h
+ HAVE_MALLOC_SIZE)
+check_symbol_exists(
+ malloc_usable_size
+ malloc.h
+ HAVE_MALLOC_USABLE_SIZE)
+check_symbol_exists(
+ malloc_usable_size
+ malloc_np.h
+ HAVE_MALLOC_USABLE_SIZE_NP)
+check_symbol_exists(
+ posix_memalign
+ stdlib.h
+ HAVE_POSIX_MEMALIGN)
+
if(CLR_CMAKE_TARGET_IOS)
# Manually set results from check_c_source_runs() since it's not possible to actually run it during CMake configure checking
unset(HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP)
+ unset(HAVE_ALIGNED_ALLOC) # only exists on iOS 13+
unset(HAVE_CLOCK_MONOTONIC) # only exists on iOS 10+
unset(HAVE_CLOCK_REALTIME) # only exists on iOS 10+
unset(HAVE_FORK) # exists but blocked by kernel
# Manually set results from check_c_source_runs() since it's not possible to actually run it during CMake configure checking
# TODO: test to see if these all actually hold true on Mac Catalyst
unset(HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP)
+ unset(HAVE_ALIGNED_ALLOC) # only exists on iOS 13+
unset(HAVE_CLOCK_MONOTONIC) # only exists on iOS 10+
unset(HAVE_CLOCK_REALTIME) # only exists on iOS 10+
unset(HAVE_FORK) # exists but blocked by kernel
elseif(CLR_CMAKE_TARGET_TVOS)
# Manually set results from check_c_source_runs() since it's not possible to actually run it during CMake configure checking
unset(HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP)
+ unset(HAVE_ALIGNED_ALLOC) # only exists on iOS 13+
unset(HAVE_CLOCK_MONOTONIC) # only exists on iOS 10+
unset(HAVE_CLOCK_REALTIME) # only exists on iOS 10+
unset(HAVE_FORK) # exists but blocked by kernel
elseif(CLR_CMAKE_TARGET_ANDROID)
# Manually set results from check_c_source_runs() since it's not possible to actually run it during CMake configure checking
unset(HAVE_SHM_OPEN_THAT_WORKS_WELL_ENOUGH_WITH_MMAP)
+ unset(HAVE_ALIGNED_ALLOC) # only exists on newer Android
set(HAVE_CLOCK_MONOTONIC 1)
set(HAVE_CLOCK_REALTIME 1)
-elseif (CLR_CMAKE_TARGET_BROWSER)
+elseif(CLR_CMAKE_TARGET_BROWSER)
set(HAVE_FORK 0)
else()
+ if(CLR_CMAKE_TARGET_OSX)
+ unset(HAVE_ALIGNED_ALLOC) # only exists on OSX 10.15+
+ else()
+ check_symbol_exists(
+ aligned_alloc
+ stdlib.h
+ HAVE_ALIGNED_ALLOC)
+ endif()
+
check_c_source_runs(
"
#include <sys/mman.h>
<data name="Argument_AdjustmentRulesOutOfOrder" xml:space="preserve">
<value>The elements of the AdjustmentRule array must be in chronological order and must not overlap.</value>
</data>
+ <data name="Argument_AlignmentMustBePow2" xml:space="preserve">
+ <value>The alignment must be a power of two.</value>
+ </data>
<data name="Argument_AlreadyACCW" xml:space="preserve">
<value>The object already has a CCW associated with it.</value>
</data>
<data name="Arg_MemberInfoNotFound" xml:space="preserve">
<value>A MemberInfo that matches '{0}' could not be found.</value>
</data>
-</root>
\ No newline at end of file
+</root>
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MarshalAsAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MarshalDirectiveException.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\MemoryMarshal.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\NativeMemory.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\UnmanagedCallConvAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\UnmanagedCallersOnlyAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\NativeLibrary.cs" />
<Compile Include="$(CommonPath)Interop\Windows\Shell32\Interop.SHGetKnownFolderPath.cs">
<Link>Common\Interop\Windows\Shell32\Interop.SHGetKnownFolderPath.cs</Link>
</Compile>
+ <Compile Include="$(CommonPath)Interop\Windows\Ucrtbase\Interop.MemAlloc.cs">
+ <Link>Common\Interop\Windows\Ucrtbase\Interop.MemAlloc.cs</Link>
+ </Compile>
<Compile Include="$(CommonPath)Interop\Windows\User32\Interop.Constants.cs">
<Link>Common\Interop\Windows\User32\Interop.Constants.cs</Link>
</Compile>
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Loader\LibraryNameVariation.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\MemoryFailPoint.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshal.Windows.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\NativeMemory.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\StandardOleMarshalObject.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\LowLevelMonitor.Windows.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\Loader\LibraryNameVariation.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\MemoryFailPoint.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\Marshal.Unix.cs" />
+ <Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\NativeMemory.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Runtime\InteropServices\StandardOleMarshalObject.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Security\SecureString.Unix.cs" />
<Compile Include="$(MSBuildThisFileDirectory)System\Threading\LowLevelMonitor.Unix.cs" />
public static bool IsPow2(ulong value) => (value & (value - 1)) == 0 && value != 0;
/// <summary>
- /// Round the given integral value up to a power of 2.
+ /// Evaluate whether a given integral value is a power of 2.
/// </summary>
/// <param name="value">The value.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static bool IsPow2(nint value) => (value & (value - 1)) == 0 && value > 0;
+
+ /// <summary>
+ /// Evaluate whether a given integral value is a power of 2.
+ /// </summary>
+ /// <param name="value">The value.</param>
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ internal static bool IsPow2(nuint value) => (value & (value - 1)) == 0 && value != 0;
+
+ /// <summary>Round the given integral value up to a power of 2.</summary>
+ /// <param name="value">The value.</param>
/// <returns>
/// The smallest power of 2 which is greater than or equal to <paramref name="value"/>.
/// If <paramref name="value"/> is 0 or the result overflows, returns 0.
bytes[actualByteLength] = 0;
}
- public static IntPtr AllocHGlobal(IntPtr cb)
+ public static unsafe IntPtr AllocHGlobal(IntPtr cb)
{
- nuint cbNative = (nuint)(nint)cb;
-
- // Avoid undefined malloc behavior by always allocating at least one byte
- IntPtr pNewMem = Interop.Sys.MemAlloc((cbNative != 0) ? cbNative : 1);
- if (pNewMem == IntPtr.Zero)
- {
- throw new OutOfMemoryException();
- }
-
- return pNewMem;
+ return (nint)NativeMemory.Alloc((nuint)(nint)cb);
}
- public static void FreeHGlobal(IntPtr hglobal)
+ public static unsafe void FreeHGlobal(IntPtr hglobal)
{
- if (hglobal != IntPtr.Zero)
- {
- Interop.Sys.MemFree(hglobal);
- }
+ NativeMemory.Free((void*)(nint)hglobal);
}
- public static IntPtr ReAllocHGlobal(IntPtr pv, IntPtr cb)
+ public static unsafe IntPtr ReAllocHGlobal(IntPtr pv, IntPtr cb)
{
- nuint cbNative = (nuint)(nint)cb;
-
- if (cbNative == 0)
- {
- // ReAllocHGlobal never returns null, even for 0 size (different from standard C/C++ realloc)
-
- // Avoid undefined realloc behavior by always allocating at least one byte
- cbNative = 1;
- }
-
- IntPtr pNewMem = Interop.Sys.MemReAlloc(pv, cbNative);
- if (pNewMem == IntPtr.Zero)
- {
- throw new OutOfMemoryException();
- }
- return pNewMem;
+ return (nint)NativeMemory.Realloc((void*)(nint)pv, (nuint)(nint)cb);
}
public static IntPtr AllocCoTaskMem(int cb) => AllocHGlobal((nint)(uint)cb);
public static void FreeCoTaskMem(IntPtr ptr) => FreeHGlobal(ptr);
- public static IntPtr ReAllocCoTaskMem(IntPtr pv, int cb)
+ public static unsafe IntPtr ReAllocCoTaskMem(IntPtr pv, int cb)
{
nuint cbNative = (nuint)(uint)cb;
+ void* pvNative = (void*)(nint)pv;
- if (cbNative == 0)
+ if ((cbNative == 0) && (pvNative != null))
{
- if (pv != IntPtr.Zero)
- {
- Interop.Sys.MemFree(pv);
- return IntPtr.Zero;
- }
- // Avoid undefined realloc behavior by always allocating at least one byte
- cbNative = 1;
+ Interop.Sys.Free(pvNative);
+ return IntPtr.Zero;
}
- IntPtr pNewMem = Interop.Sys.MemReAlloc(pv, cbNative);
- if (pNewMem == IntPtr.Zero)
- {
- throw new OutOfMemoryException();
- }
- return pNewMem;
+ return (nint)NativeMemory.Realloc((void*)(nint)pv, cbNative);
}
internal static unsafe IntPtr AllocBSTR(int length)
const nuint WIN32_ALLOC_ALIGN = 15;
ulong cbNative = 2 * (ulong)(uint)length + (uint)sizeof(IntPtr) + (uint)sizeof(char) + WIN32_ALLOC_ALIGN;
+
if (cbNative > uint.MaxValue)
{
throw new OutOfMemoryException();
}
- IntPtr p = Interop.Sys.MemAlloc((nuint)cbNative & ~WIN32_ALLOC_ALIGN);
- if (p == IntPtr.Zero)
+ void* p = Interop.Sys.Malloc((nuint)cbNative & ~WIN32_ALLOC_ALIGN);
+
+ if (p == null)
{
throw new OutOfMemoryException();
}
- IntPtr s = p + sizeof(IntPtr);
+ void* s = (byte*)p + sizeof(nuint);
*(((uint*)s) - 1) = (uint)(length * sizeof(char));
((char*)s)[length] = '\0';
- return s;
+ return (nint)s;
}
internal static unsafe IntPtr AllocBSTRByteLen(uint length)
const nuint WIN32_ALLOC_ALIGN = 15;
ulong cbNative = (ulong)(uint)length + (uint)sizeof(IntPtr) + (uint)sizeof(char) + WIN32_ALLOC_ALIGN;
+
if (cbNative > uint.MaxValue)
{
throw new OutOfMemoryException();
}
- IntPtr p = Interop.Sys.MemAlloc((nuint)cbNative & ~WIN32_ALLOC_ALIGN);
- if (p == IntPtr.Zero)
+ void* p = Interop.Sys.Malloc((nuint)cbNative & ~WIN32_ALLOC_ALIGN);
+
+ if (p == null)
{
throw new OutOfMemoryException();
}
- IntPtr s = p + sizeof(IntPtr);
+ void* s = (byte*)p + sizeof(nuint);
*(((uint*)s) - 1) = (uint)length;
// NULL-terminate with both a narrow and wide zero.
*(byte*)((byte*)s + length) = (byte)'\0';
*(short*)((byte*)s + ((length + 1) & ~1)) = 0;
- return s;
+ return (nint)s;
}
public static unsafe void FreeBSTR(IntPtr ptr)
{
- if (ptr != IntPtr.Zero)
+ void* ptrNative = (void*)(nint)ptr;
+
+ if (ptrNative != null)
{
- Interop.Sys.MemFree(ptr - sizeof(IntPtr));
+ Interop.Sys.Free((byte*)ptr - sizeof(nuint));
}
}
--- /dev/null
+// 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 = null;
+
+ 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;
+ }
+ }
+}
--- /dev/null
+// 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 The Windows implementation requires a power of 2
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AlignmentMustBePow2);
+ }
+
+ // Unlike the C standard and POSIX, Windows does not requires size to be a multiple of alignment. However, we do want an "empty" allocation for zero
+ void* result = Interop.Ucrtbase._aligned_malloc((byteCount != 0) ? byteCount : 1, alignment);
+
+ 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.Ucrtbase._aligned_free(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 The Windows implementation requires a power of 2
+ ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AlignmentMustBePow2);
+ }
+
+ // Unlike the C standard and POSIX, Windows does not requires size to be a multiple of alignment. However, we do want an "empty" allocation for zero
+ void* result = Interop.Ucrtbase._aligned_realloc(ptr, (byteCount != 0) ? byteCount : 1, alignment);
+
+ 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 Windows implementation handles size == 0 as we expect
+ void* result = Interop.Ucrtbase.malloc(byteCount);
+
+ 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)
+ {
+ // The Windows implementation handles num == 0 && size == 0 as we expect
+ void* result = Interop.Ucrtbase.calloc(elementCount, elementSize);
+
+ 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.Ucrtbase.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 Windows implementation treats size == 0 as Free, we want an "empty" allocation
+ void* result = Interop.Ucrtbase.realloc(ptr, (byteCount != 0) ? byteCount : 1);
+
+ if (result == null)
+ {
+ ThrowHelper.ThrowOutOfMemoryException();
+ }
+
+ return result;
+ }
+ }
+}
--- /dev/null
+// 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
+{
+ public static unsafe partial class NativeMemory
+ {
+ /// <summary>Allocates 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 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>malloc</c> API.</para>
+ /// </remarks>
+ [CLSCompliant(false)]
+ public static void* Alloc(nuint elementCount, nuint elementSize)
+ {
+ nuint byteCount = GetByteCount(elementCount, elementSize);
+ return Alloc(byteCount);
+ }
+
+ /// <summary>Allocates and zeroes 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 and zeroed 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>calloc</c> API.</para>
+ /// </remarks>
+ [CLSCompliant(false)]
+ public static void* AllocZeroed(nuint byteCount)
+ {
+ return AllocZeroed(byteCount, elementSize: 1);
+ }
+
+ [MethodImpl(MethodImplOptions.AggressiveInlining)]
+ private static nuint GetByteCount(nuint elementCount, nuint elementSize)
+ {
+ // This is based on the `mi_count_size_overflow` and `mi_mul_overflow` methods from microsoft/mimalloc.
+ // Original source is Copyright (c) 2019 Microsoft Corporation, Daan Leijen. Licensed under the MIT license
+
+ // sqrt(nuint.MaxValue)
+ nuint multiplyNoOverflow = (nuint)1 << (4 * sizeof(nuint));
+
+ return ((elementSize >= multiplyNoOverflow) || (elementCount >= multiplyNoOverflow)) && (elementSize > 0) && ((nuint.MaxValue / elementSize) < elementCount) ? nuint.MaxValue : (elementCount * elementSize);
+ }
+ }
+}
return SR.Argument_InvalidFlag;
case ExceptionResource.CancellationTokenSource_Disposed:
return SR.CancellationTokenSource_Disposed;
+ case ExceptionResource.Argument_AlignmentMustBePow2:
+ return SR.Argument_AlignmentMustBePow2;
default:
Debug.Fail("The enum value is not defined, please check the ExceptionResource Enum.");
return "";
Argument_SpansMustHaveSameLength,
Argument_InvalidFlag,
CancellationTokenSource_Disposed,
+ Argument_AlignmentMustBePow2,
}
}
public static bool TryLoad(string libraryPath, out System.IntPtr handle) { throw null; }
public static bool TryLoad(string libraryName, System.Reflection.Assembly assembly, System.Runtime.InteropServices.DllImportSearchPath? searchPath, out System.IntPtr handle) { throw null; }
}
+ public static unsafe partial class NativeMemory
+ {
+ [System.CLSCompliantAttribute(false)]
+ public static void* AlignedAlloc(nuint byteCount, nuint alignment) { throw null; }
+ [System.CLSCompliantAttribute(false)]
+ public static void AlignedFree(void* ptr) { }
+ [System.CLSCompliantAttribute(false)]
+ public static void* AlignedRealloc(void* ptr, nuint byteCount, nuint alignment) { throw null; }
+ [System.CLSCompliantAttribute(false)]
+ public static void* Alloc(nuint byteCount) { throw null; }
+ [System.CLSCompliantAttribute(false)]
+ public static void* Alloc(nuint elementCount, nuint elementSize) { throw null; }
+ [System.CLSCompliantAttribute(false)]
+ public static void* AllocZeroed(nuint byteCount) { throw null; }
+ [System.CLSCompliantAttribute(false)]
+ public static void* AllocZeroed(nuint elementCount, nuint elementSize) { throw null; }
+ [System.CLSCompliantAttribute(false)]
+ public static void Free(void* ptr) { }
+ [System.CLSCompliantAttribute(false)]
+ public static void* Realloc(void* ptr, nuint byteCount) { throw null; }
+ }
public readonly struct NFloat : IEquatable<NFloat>
{
public NFloat(float value) { }
<Compile Include="System\Runtime\InteropServices\BStrWrapperTests.cs" />
<Compile Include="System\Runtime\InteropServices\CallingConventionTests.cs" />
<Compile Include="System\Runtime\InteropServices\ClassInterfaceAttributeTests.cs" />
+ <Compile Include="System\Runtime\InteropServices\NativeMemoryTests.cs" />
<Compile Include="System\Runtime\InteropServices\NFloatTests.cs" />
<Compile Include="System\Runtime\InteropServices\CULongTests.cs" />
<Compile Include="System\Runtime\InteropServices\CLongTests.cs" />
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using Xunit;
+
+namespace System.Runtime.InteropServices.Tests
+{
+ public unsafe class NativeMemoryTests
+ {
+ [Theory]
+ [InlineData(1)]
+ [InlineData(2)]
+ [InlineData(4)]
+ [InlineData(8)]
+ [InlineData(16)]
+ [InlineData(32)]
+ [InlineData(64)]
+ [InlineData(128)]
+ [InlineData(256)]
+ [InlineData(512)]
+ [InlineData(1 * 1024)]
+ [InlineData(2 * 1024)]
+ [InlineData(4 * 1024)]
+ [InlineData(8 * 1024)]
+ [InlineData(16 * 1024)]
+ [InlineData(64 * 1024)]
+ [InlineData(1 * 1024 * 1024)]
+ [InlineData(2 * 1024 * 1024)]
+ [InlineData(4 * 1024 * 1024)]
+ public void AlignedAllocTest(uint alignment)
+ {
+ void* ptr = NativeMemory.AlignedAlloc(1, alignment);
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % alignment == 0);
+
+ NativeMemory.AlignedFree(ptr);
+ }
+
+ [Fact]
+ public void AlignedAllocLessThanVoidPtrAlignmentTest()
+ {
+ void* ptr = NativeMemory.AlignedAlloc(1, 1);
+ Assert.True(ptr != null);
+ NativeMemory.AlignedFree(ptr);
+ }
+
+ [Fact]
+ public void AlignedAllocOOMTest()
+ {
+ Assert.Throws<OutOfMemoryException>(() => NativeMemory.AlignedAlloc(nuint.MaxValue - ((uint)sizeof(nuint) - 1), (uint)sizeof(nuint)));
+ }
+
+ [Fact]
+ public void AlignedAllocZeroAlignmentTest()
+ {
+ Assert.Throws<ArgumentException>(() => NativeMemory.AlignedAlloc((uint)sizeof(nuint), 0));
+ }
+
+ [Fact]
+ public void AlignedAllocNonPowerOfTwoAlignmentTest()
+ {
+ Assert.Throws<ArgumentException>(() => NativeMemory.AlignedAlloc((uint)sizeof(nuint), (uint)sizeof(nuint) + 1));
+ Assert.Throws<ArgumentException>(() => NativeMemory.AlignedAlloc((uint)sizeof(nuint), (uint)sizeof(nuint) * 3));
+ }
+
+ [Fact]
+ public void AlignedAllocOverflowByteCountTest()
+ {
+ // POSIX requires byteCount to be a multiple of alignment and so we will internally upsize.
+ // This upsizing can overflow for certain values since we do (byteCount + (alignment - 1)) & ~(alignment - 1)
+ //
+ // However, this overflow is "harmless" since it will result in a value that is less than alignment
+ // given that alignment is a power of two and will ultimately be a value less than alignment which
+ // will be treated as invalid and result in OOM.
+ //
+ // Take for example a 64-bit system where the max power of two is (1UL << 63): 9223372036854775808
+ // * 9223372036854775808 + 9223372036854775807 == ulong.MaxValue, so no overflow
+ // * 9223372036854775809 + 9223372036854775807 == 0, so overflows and is less than alignment
+ // * ulong.MaxValue + 9223372036854775807 == 9223372036854775806, so overflows and is less than alignment
+ //
+ // Likewise, for small alignments such as 8 (which is the smallest on a 64-bit system for POSIX):
+ // * 18446744073709551608 + 7 == ulong.MaxValue, so no overflow
+ // * 18446744073709551609 + 7 == 0, so overflows and is less than alignment
+ // * ulong.MaxValue + 7 == 6, so overflows and is less than alignment
+
+ nuint maxAlignment = (nuint)1 << ((sizeof(nuint) * 8) - 1);
+ Assert.Throws<OutOfMemoryException>(() => NativeMemory.AlignedAlloc(maxAlignment + 1, maxAlignment));
+
+ Assert.Throws<OutOfMemoryException>(() => NativeMemory.AlignedAlloc(nuint.MaxValue, (uint)sizeof(nuint)));
+ }
+
+ [Fact]
+ public void AlignedAllocZeroSizeTest()
+ {
+ void* ptr = NativeMemory.AlignedAlloc(0, (uint)sizeof(nuint));
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0);
+
+ NativeMemory.AlignedFree(ptr);
+ }
+
+ [Fact]
+ public void AlignedFreeTest()
+ {
+ // This should not throw
+ NativeMemory.AlignedFree(null);
+ }
+
+ [Theory]
+ [InlineData(1)]
+ [InlineData(2)]
+ [InlineData(4)]
+ [InlineData(8)]
+ [InlineData(16)]
+ [InlineData(32)]
+ [InlineData(64)]
+ [InlineData(128)]
+ [InlineData(256)]
+ [InlineData(512)]
+ [InlineData(1 * 1024)]
+ [InlineData(2 * 1024)]
+ [InlineData(4 * 1024)]
+ [InlineData(8 * 1024)]
+ [InlineData(16 * 1024)]
+ [InlineData(64 * 1024)]
+ [InlineData(1 * 1024 * 1024)]
+ [InlineData(2 * 1024 * 1024)]
+ [InlineData(4 * 1024 * 1024)]
+ public void AlignedReallocTest(uint alignment)
+ {
+ void* ptr = NativeMemory.AlignedAlloc(1, alignment);
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % alignment == 0);
+
+ void* newPtr = NativeMemory.AlignedRealloc(ptr, 1, alignment);
+
+ Assert.True(newPtr != null);
+ Assert.True((nuint)newPtr % alignment == 0);
+
+ NativeMemory.AlignedFree(newPtr);
+ }
+
+ [Fact]
+ public void AlignedReallocLessThanVoidPtrAlignmentTest()
+ {
+ void* ptr = NativeMemory.AlignedAlloc(1, 1);
+ Assert.True(ptr != null);
+
+ void* newPtr = NativeMemory.AlignedRealloc(ptr, 1, 1);
+ Assert.True(newPtr != null);
+ NativeMemory.AlignedFree(newPtr);
+ }
+
+ [Fact]
+ public void AlignedReallocNullPtrTest()
+ {
+ void* ptr = NativeMemory.AlignedRealloc(null, 1, (uint)sizeof(nuint));
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0);
+
+ NativeMemory.AlignedFree(ptr);
+ }
+
+ [Fact]
+ public void AlignedReallocNullPtrOOMTest()
+ {
+ Assert.Throws<OutOfMemoryException>(() => NativeMemory.AlignedRealloc(null, nuint.MaxValue, (uint)sizeof(nuint)));
+ }
+
+ [Fact]
+ public void AlignedReallocNullPtrZeroSizeTest()
+ {
+ void* ptr = NativeMemory.AlignedRealloc(null, 0, (uint)sizeof(nuint));
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0);
+
+ NativeMemory.AlignedFree(ptr);
+ }
+
+ [Fact]
+ public void AlignedReallocZeroAlignmentTest()
+ {
+ void* ptr = NativeMemory.AlignedAlloc(1, (uint)sizeof(nuint));
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0);
+
+ Assert.Throws<ArgumentException>(() => NativeMemory.AlignedRealloc(ptr, (uint)sizeof(nuint), 0));
+ NativeMemory.AlignedFree(ptr);
+ }
+
+ [Fact]
+ public void AlignedReallocNonPowerOfTwoAlignmentTest()
+ {
+ void* ptr = NativeMemory.AlignedAlloc(1, (uint)sizeof(nuint));
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0);
+
+ Assert.Throws<ArgumentException>(() => NativeMemory.AlignedRealloc(ptr, (uint)sizeof(nuint), (uint)sizeof(nuint) + 1));
+ Assert.Throws<ArgumentException>(() => NativeMemory.AlignedRealloc(ptr, (uint)sizeof(nuint), (uint)sizeof(nuint) * 3));
+ NativeMemory.AlignedFree(ptr);
+ }
+
+ [Fact]
+ public void AlignedReallocZeroSizeTest()
+ {
+ void* ptr = NativeMemory.AlignedAlloc(1, (uint)sizeof(nuint));
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % (uint)sizeof(nuint) == 0);
+
+ void* newPtr = NativeMemory.AlignedRealloc(ptr, 0, (uint)sizeof(nuint));
+
+ Assert.True(newPtr != null);
+ Assert.True((nuint)newPtr % (uint)sizeof(nuint) == 0);
+
+ NativeMemory.AlignedFree(newPtr);
+ }
+
+ [Fact]
+ public void AlignedReallocSmallerToLargerTest()
+ {
+ void* ptr = NativeMemory.AlignedAlloc(16, 16);
+
+ Assert.True(ptr != null);
+ Assert.True((nuint)ptr % 16 == 0);
+
+ for (int i = 0; i < 16; i++)
+ {
+ ((byte*)ptr)[i] = (byte)i;
+ }
+
+ void* newPtr = NativeMemory.AlignedRealloc(ptr, 32, 16);
+
+ Assert.True(newPtr != null);
+ Assert.True((nuint)newPtr % 16 == 0);
+
+ for (int i = 0; i < 16; i++)
+ {
+ Assert.True(((byte*)newPtr)[i] == i);
+ }
+
+ NativeMemory.AlignedFree(newPtr);
+ }
+
+ [Fact]
+ public void AllocByteCountTest()
+ {
+ void* ptr = NativeMemory.Alloc(1);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocElementCountTest()
+ {
+ void* ptr = NativeMemory.Alloc(1, 1);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocByteCountOOMTest()
+ {
+ Assert.Throws<OutOfMemoryException>(() => NativeMemory.Alloc(nuint.MaxValue));
+ }
+
+ [Fact]
+ public void AllocElementCountOOMTest()
+ {
+ Assert.Throws<OutOfMemoryException>(() => NativeMemory.Alloc(1, nuint.MaxValue));
+ Assert.Throws<OutOfMemoryException>(() => NativeMemory.Alloc(nuint.MaxValue, 1));
+ Assert.Throws<OutOfMemoryException>(() => NativeMemory.Alloc(nuint.MaxValue, nuint.MaxValue));
+ }
+
+ [Fact]
+ public void AllocZeroByteCountTest()
+ {
+ void* ptr = NativeMemory.Alloc(0);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocZeroElementCountTest()
+ {
+ void* ptr = NativeMemory.Alloc(0, 1);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocZeroElementSizeTest()
+ {
+ void* ptr = NativeMemory.Alloc(1, 0);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocZeroedByteCountTest()
+ {
+ void* ptr = NativeMemory.AllocZeroed(1);
+
+ Assert.True(ptr != null);
+ Assert.Equal(expected: 0, actual: ((byte*)ptr)[0]);
+
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocZeroedElementCountTest()
+ {
+ void* ptr = NativeMemory.AllocZeroed(1, 1);
+
+ Assert.True(ptr != null);
+ Assert.Equal(expected: 0, actual: ((byte*)ptr)[0]);
+
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocZeroedByteCountOOMTest()
+ {
+ Assert.Throws<OutOfMemoryException>(() => NativeMemory.AllocZeroed(nuint.MaxValue));
+ }
+
+ [Fact]
+ public void AllocZeroedElementCountOOMTest()
+ {
+ Assert.Throws<OutOfMemoryException>(() => NativeMemory.AllocZeroed(1, nuint.MaxValue));
+ Assert.Throws<OutOfMemoryException>(() => NativeMemory.AllocZeroed(nuint.MaxValue, 1));
+ Assert.Throws<OutOfMemoryException>(() => NativeMemory.AllocZeroed(nuint.MaxValue, nuint.MaxValue));
+ }
+
+ [Fact]
+ public void AllocZeroedZeroByteCountTest()
+ {
+ void* ptr = NativeMemory.AllocZeroed(0);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocZeroedZeroElementCountTest()
+ {
+ void* ptr = NativeMemory.AllocZeroed(0, 1);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void AllocZeroedZeroElementSizeTest()
+ {
+ void* ptr = NativeMemory.AllocZeroed(1, 0);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void FreeTest()
+ {
+ // This should not throw
+ NativeMemory.Free(null);
+ }
+
+ [Fact]
+ public void ReallocTest()
+ {
+ void* ptr = NativeMemory.Alloc(1);
+ Assert.True(ptr != null);
+
+ void* newPtr = NativeMemory.Realloc(ptr, 1);
+ Assert.True(newPtr != null);
+ NativeMemory.Free(newPtr);
+ }
+
+ [Fact]
+ public void ReallocNullPtrTest()
+ {
+ void* ptr = NativeMemory.Realloc(null, 1);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void ReallocNullPtrOOMTest()
+ {
+ Assert.Throws<OutOfMemoryException>(() => NativeMemory.Realloc(null, nuint.MaxValue));
+ }
+
+ [Fact]
+ public void ReallocNullPtrZeroSizeTest()
+ {
+ void* ptr = NativeMemory.Realloc(null, 0);
+ Assert.True(ptr != null);
+ NativeMemory.Free(ptr);
+ }
+
+ [Fact]
+ public void ReallocZeroSizeTest()
+ {
+ void* ptr = NativeMemory.Alloc(1);
+ Assert.True(ptr != null);
+
+ void* newPtr = NativeMemory.Realloc(ptr, 0);
+ Assert.True(newPtr != null);
+ NativeMemory.Free(newPtr);
+ }
+
+ [Fact]
+ public void ReallocSmallerToLargerTest()
+ {
+ void* ptr = NativeMemory.Alloc(16);
+ Assert.True(ptr != null);
+
+ for (int i = 0; i < 16; i++)
+ {
+ ((byte*)ptr)[i] = (byte)i;
+ }
+
+ void* newPtr = NativeMemory.Realloc(ptr, 32);
+ Assert.True(newPtr != null);
+
+ for (int i = 0; i < 16; i++)
+ {
+ Assert.True(((byte*)newPtr)[i] == i);
+ }
+
+ NativeMemory.Free(newPtr);
+ }
+ }
+}