Skip to content

Commit

Permalink
Improve MemoryHelpers, add lot of new unsafe helpers + use NativeMemo…
Browse files Browse the repository at this point in the history
…ry on NET6 or greather instead of Marshal.
  • Loading branch information
amerkoleci committed Oct 9, 2023
1 parent c54a6d5 commit 33376aa
Show file tree
Hide file tree
Showing 6 changed files with 401 additions and 35 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<Project>

<PropertyGroup>
<VersionPrefix Condition="'$(VersionPrefix)' == ''">2.1.1</VersionPrefix>
<VersionPrefix Condition="'$(VersionPrefix)' == ''">2.1.2</VersionPrefix>
<VersionSuffix Condition="'$(VersionSuffix)' == ''">beta</VersionSuffix>
<Version>$(VersionPrefix)-$(VersionSuffix)</Version>
</PropertyGroup>
Expand Down
2 changes: 1 addition & 1 deletion SharpGen.Runtime/CallbackBase.ReflectionImpl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public abstract unsafe partial class CallbackBase
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2062", Justification = $"{nameof(ShadowAttribute.Type)} is already marked `DynamicallyAccessedMemberTypes.PublicConstructors` and the existing check via `Debug.Assert(holder.GetTypeInfo().GetConstructor(Type.EmptyTypes)` will ensure correctness.")]
[UnconditionalSuppressMessage("ReflectionAnalysis", "IL2111", Justification = "Same as above.")]
#endif
protected virtual void InitializeCallableWrappers(IDictionary<Guid, IntPtr> ccw)
protected virtual void InitializeCallableWrappers(IDictionary<Guid, nint> ccw)
{
// Associate all shadows with their interfaces.
var typeInfo = GetTypeInfo();
Expand Down
19 changes: 17 additions & 2 deletions SharpGen.Runtime/MarshallingHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,21 @@ namespace SharpGen.Runtime;

public static partial class MarshallingHelpers
{
/// <summary>
/// Instantiate a CppObject from a native pointer.
/// </summary>
/// <typeparam name="T">The CppObject class that will be returned</typeparam>
/// <param name="cppObjectPtr">The native pointer to a C++ object.</param>
/// <returns>An instance of T bound to the native pointer</returns>
public static T? FromPointer<T>(IntPtr cppObjectPtr, Func<IntPtr, T> factory) where T : CppObject
{
if (cppObjectPtr == IntPtr.Zero)
return default;

T? result = factory(cppObjectPtr);
return result;
}

/// <summary>
/// Instantiate a CppObject from a native pointer.
/// </summary>
Expand Down Expand Up @@ -76,12 +91,12 @@ callback switch
/// <typeparam name="TCallback">The type of the callback.</typeparam>
/// <param name="callback">The callback.</param>
/// <returns>A pointer to the unmanaged C++ object of the callback</returns>
public static IntPtr ToCallbackPtr<TCallback>(ICallbackable callback) where TCallback : ICallbackable =>
public static nint ToCallbackPtr<TCallback>(ICallbackable callback) where TCallback : ICallbackable =>
callback switch
{
CppObject cpp => cpp.NativePointer,
CallbackBase managed => managed.Find<TCallback>(),
_ => IntPtr.Zero,
_ => 0,
};

/// <summary>
Expand Down
105 changes: 97 additions & 8 deletions SharpGen.Runtime/MemoryHelpers.Alloc.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
using System.Diagnostics;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Diagnostics.CodeAnalysis;

namespace SharpGen.Runtime;

public static partial class MemoryHelpers
public static unsafe partial class MemoryHelpers
{
/// <summary>
/// Allocate an aligned memory buffer.
Expand All @@ -17,14 +18,20 @@ public static partial class MemoryHelpers
/// <remarks>
/// To free this buffer, call <see cref="FreeMemory(void*)"/>.
/// </remarks>
public static unsafe void* AllocateMemory(nuint sizeInBytes, uint align = 16)
public static void* AllocateMemory(nuint sizeInBytes, uint alignment = 16)
{
nuint mask = align - 1u;
#if NET6_0_OR_GREATER
var ptr = NativeMemory.AlignedAlloc(sizeInBytes, alignment);
Debug.Assert(IsMemoryAligned(ptr, alignment));
return ptr;
#else
nuint mask = alignment - 1u;
var memPtr = Marshal.AllocHGlobal((nint) (sizeInBytes + mask) + sizeof(void*));
var ptr = (nuint) ((byte*) memPtr + sizeof(void*) + mask) & ~mask;
Debug.Assert(IsMemoryAligned(ptr, align));
Debug.Assert(IsMemoryAligned(ptr, alignment));
((IntPtr*) ptr)[-1] = memPtr;
return (void*) ptr;
#endif
}

/// <summary>
Expand All @@ -38,21 +45,91 @@ public static partial class MemoryHelpers
/// </remarks>
[Obsolete("Use void*(nuint, uint) overload instead")]
[EditorBrowsable(EditorBrowsableState.Advanced)]
public static unsafe IntPtr AllocateMemory(int sizeInBytes, int align = 16) =>
public static IntPtr AllocateMemory(int sizeInBytes, int align = 16) =>
new(AllocateMemory((nuint) sizeInBytes, (uint) align));

/// <summary>Allocates a chunk of unmanaged memory.</summary>
/// <param name="count">The count of elements contained in the allocation.</param>
/// <param name="size">The size, in bytes, of the elements in the allocation.</param>
/// <param name="zero"><c>true</c> if the allocated memory should be zeroed; otherwise, <c>false</c>.</param>
/// <returns>The address to an allocated chunk of memory that is at least <paramref name="size" /> bytes in length.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static void* AllocateArray(nuint count, nuint size, bool zero = false)
{
#if NET6_0_OR_GREATER
void* result = NativeMemory.Alloc(count, size);

#else
void* result = (void*)Marshal.AllocHGlobal(checked((int) (count * size)));
#endif

if (result == null)
{
ThrowOutOfMemoryException(count, size);
}

if(zero)
{
#if NET6_0_OR_GREATER
NativeMemory.Clear(result, count * size);
#else
ClearMemory(result, count * size);
#endif
}

return result;
}

/// <summary>Allocates a chunk of unmanaged memory.</summary>
/// <typeparam name="T">The type used to compute the size, in bytes, of the elements in the allocation.</typeparam>
/// <param name="count">The count of elements contained in the allocation.</param>
/// <param name="zero"><c>true</c> if the allocated memory should be zeroed; otherwise, <c>false</c>.</param>
/// <returns>The address to an allocated chunk of memory that is at least <c>sizeof(<typeparamref name="T" />)</c> bytes in length.</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static T* AllocateArray<T>(nuint count, bool zero = false)
where T : unmanaged
{

#if NET6_0_OR_GREATER
T* result = (T*)NativeMemory.Alloc(count, SizeOf<T>());

#else
T* result = (T*) Marshal.AllocHGlobal(checked((int) (count * SizeOf<T>())));
#endif

if (result == null)
{
ThrowOutOfMemoryException(count, SizeOf<T>());
}

if (zero)
{
#if NET6_0_OR_GREATER
NativeMemory.Clear(result, count * SizeOf<T>());
#else
ClearMemory(result, count * SizeOf<T>());
#endif
}

return result;
}

/// <summary>
/// Free an aligned memory buffer.
/// </summary>
/// <returns>A pointer to a buffer aligned.</returns>
/// <remarks>
/// The buffer must have been allocated with <see cref="AllocateMemory"/>.
/// </remarks>
public static unsafe void FreeMemory(void* alignedBuffer)
public static void FreeMemory(void* alignedBuffer)
{
if (alignedBuffer == default) return;

#if NET6_0_OR_GREATER
NativeMemory.AlignedFree(alignedBuffer);
#else
Marshal.FreeHGlobal(((IntPtr*) alignedBuffer)[-1]);
#endif
}

/// <summary>
Expand All @@ -63,7 +140,7 @@ public static unsafe void FreeMemory(void* alignedBuffer)
/// The buffer must have been allocated with <see cref="AllocateMemory"/>.
/// </remarks>
[MethodImpl(Utilities.MethodAggressiveOptimization)]
public static unsafe void FreeMemory(UIntPtr alignedBuffer) => FreeMemory(alignedBuffer.ToPointer());
public static void FreeMemory(UIntPtr alignedBuffer) => FreeMemory(alignedBuffer.ToPointer());

/// <summary>
/// Free an aligned memory buffer.
Expand All @@ -73,5 +150,17 @@ public static unsafe void FreeMemory(void* alignedBuffer)
/// The buffer must have been allocated with <see cref="AllocateMemory"/>.
/// </remarks>
[MethodImpl(Utilities.MethodAggressiveOptimization)]
public static unsafe void FreeMemory(IntPtr alignedBuffer) => FreeMemory(alignedBuffer.ToPointer());
public static void FreeMemory(IntPtr alignedBuffer) => FreeMemory(alignedBuffer.ToPointer());

[DoesNotReturn]
private static void ThrowOutOfMemoryException(ulong size)
{
throw new OutOfMemoryException($"The allocation of '{size}' bytes failed");
}

[DoesNotReturn]
public static void ThrowOutOfMemoryException(ulong count, ulong size)
{
throw new OutOfMemoryException($"The allocation of '{count}x{size}' bytes failed");
}
}
Loading

0 comments on commit 33376aa

Please sign in to comment.