MCPcopy Create free account
hub / github.com/FlaxEngine/FlaxEngine / ValueTypeUnboxer

Class ValueTypeUnboxer

Source/Engine/Engine/NativeInterop.cs:1527–1603  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

1525 }
1526
1527 internal static class ValueTypeUnboxer
1528 {
1529 private static GCHandle[] pinnedBoxedValues = new GCHandle[256];
1530 private static uint pinnedBoxedValuesPointer = 0;
1531 private static (IntPtr ptr, int size)[] pinnedAllocations = new (IntPtr ptr, int size)[256];
1532 private static uint pinnedAllocationsPointer = 0;
1533
1534 private delegate IntPtr UnboxerDelegate(object value, object converter);
1535
1536 private static ConcurrentDictionary<Type, (UnboxerDelegate deleg, object toNativeDeleg)> unboxers = new(1, 3);
1537 private static MethodInfo unboxerMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointer), BindingFlags.Static | BindingFlags.NonPublic);
1538 private static MethodInfo unboxerToNativeMethod = typeof(ValueTypeUnboxer).GetMethod(nameof(ValueTypeUnboxer.UnboxPointerWithConverter), BindingFlags.Static | BindingFlags.NonPublic);
1539
1540 internal static IntPtr GetPointer(object value, Type type)
1541 {
1542 if (!unboxers.TryGetValue(type, out var tuple))
1543 {
1544 // Non-POD structures use internal layout (eg. SpriteHandleManaged in C++ with SpriteHandleMarshaller.SpriteHandleInternal in C#) so convert C# data into C++ data
1545 var attr = type.GetCustomAttribute<System.Runtime.InteropServices.Marshalling.NativeMarshallingAttribute>();
1546 var toNativeMethod = attr?.NativeType.GetMethod("ToNative", BindingFlags.Static | BindingFlags.NonPublic);
1547 if (toNativeMethod != null)
1548 {
1549 tuple.deleg = unboxerToNativeMethod.MakeGenericMethod(type, toNativeMethod.ReturnType).CreateDelegate<UnboxerDelegate>();
1550 tuple.toNativeDeleg = toNativeMethod.CreateDelegate(typeof(ToNativeDelegate<,>).MakeGenericType(type, toNativeMethod.ReturnType));
1551 }
1552 else
1553 {
1554 tuple.deleg = unboxerMethod.MakeGenericMethod(type).CreateDelegate<UnboxerDelegate>();
1555 }
1556 tuple = unboxers.GetOrAdd(type, tuple);
1557 }
1558 return tuple.deleg(value, tuple.toNativeDeleg);
1559 }
1560
1561 private static void PinValue(object value)
1562 {
1563 // Prevent garbage collector from relocating the boxed value by pinning it temporarily.
1564 // The pointer should remain valid quite long time but will be eventually unpinned.
1565 uint index = Interlocked.Increment(ref pinnedBoxedValuesPointer) % (uint)pinnedBoxedValues.Length;
1566 ref GCHandle handle = ref pinnedBoxedValues[index];
1567 if (handle.IsAllocated)
1568 handle.Free();
1569 handle = GCHandle.Alloc(value, GCHandleType.Pinned);
1570 }
1571
1572 private static IntPtr PinValue<T>(T value) where T : struct
1573 {
1574 // Store the converted value in unmanaged memory so it will not be relocated by the garbage collector.
1575 int size = TypeHelpers<T>.MarshalSize;
1576 uint index = Interlocked.Increment(ref pinnedAllocationsPointer) % (uint)pinnedAllocations.Length;
1577 ref (IntPtr ptr, int size) alloc = ref pinnedAllocations[index];
1578 if (alloc.size < size)
1579 {
1580 if (alloc.ptr != IntPtr.Zero)
1581 NativeFree(alloc.ptr.ToPointer());
1582 alloc.ptr = new IntPtr(NativeAlloc(size));
1583 alloc.size = size;
1584 }

Callers

nothing calls this directly

Calls 1

GetMethodMethod · 0.45

Tested by

no test coverage detected