1+ using System . Diagnostics ;
12using System . Diagnostics . CodeAnalysis ;
23using System . Runtime . CompilerServices ;
34using System . Runtime . InteropServices ;
@@ -65,29 +66,77 @@ public OpaqueValue(T? value)
6566
6667 internal OpaqueValue ( nint handle ) => this . handle = handle ;
6768
69+ /// <summary>
70+ /// Returns the cleanup action of the <see cref="CallConvCdecl"/> calling convention that releases the opaque value.
71+ /// </summary>
72+ /// <param name="opaque">The opaque value.</param>
73+ /// <returns>The cleanup action that can be called from the unmanaged code.</returns>
74+ [ CLSCompliant ( false ) ]
75+ public static unsafe explicit operator delegate * unmanaged[ Cdecl] < nint , void > ( OpaqueValue < T > opaque )
76+ => ( delegate * unmanaged[ Cdecl] < nint , void > ) opaque . GetCleaner < CallConvCdecl , OpaqueValueCleaner > ( ) ;
77+
78+ /// <summary>
79+ /// Returns the cleanup action of the <see cref="CallConvStdcall"/> calling convention that releases the opaque value.
80+ /// </summary>
81+ /// <param name="opaque">The opaque value.</param>
82+ /// <returns>The cleanup action that can be called from the unmanaged code.</returns>
83+ [ CLSCompliant ( false ) ]
84+ public static unsafe explicit operator delegate * unmanaged[ Stdcall] < nint , void > ( OpaqueValue < T > opaque )
85+ => ( delegate * unmanaged[ Stdcall] < nint , void > ) opaque . GetCleaner < CallConvStdcall , OpaqueValueCleaner > ( ) ;
86+
87+ /// <summary>
88+ /// Returns the cleanup action of the native calling convention for the current platform that releases the opaque value.
89+ /// </summary>
90+ /// <remarks>
91+ /// The returned callback has the same effect as <see cref="Dispose"/> method.
92+ /// </remarks>
93+ /// <param name="opaque">The opaque value.</param>
94+ /// <returns>The cleanup action that can be called from the unmanaged code.</returns>
95+ [ CLSCompliant ( false ) ]
96+ public static unsafe explicit operator delegate * unmanaged< nint , void > ( OpaqueValue < T > opaque )
97+ => ( delegate * unmanaged< nint , void > ) opaque . GetCleaner < CallConvAuto , OpaqueValueCleaner > ( ) ;
98+
99+ private bool IsNotAllocated => default ( T ) is IPointer || typeof ( T ) == typeof ( GCHandle ) || handle is 0 ;
100+
101+ private unsafe void * GetCleaner < TConvention , TCleaner > ( )
102+ where TConvention : class , new ( )
103+ where TCleaner : struct , ICleaner < TConvention > , allows ref struct
104+ {
105+ if ( IsNotAllocated )
106+ return null ;
107+
108+ if ( RuntimeHelpers . IsReferenceOrContainsReferences < T > ( ) )
109+ return TCleaner . FreeRef ;
110+
111+ if ( Unsafe . IsNaturallyAligned < T > ( ) )
112+ return TCleaner . Free ;
113+
114+ return TCleaner . FreeAligned ;
115+ }
116+
68117 /// <summary>
69118 /// Releases the underlying storage for the value.
70119 /// </summary>
71120 /// <remarks>
72121 /// This method is not idempotent and should not be called twice.
73122 /// </remarks>
74- public unsafe void Dispose ( )
123+ public void Dispose ( )
75124 {
76- if ( default ( T ) is IPointer || typeof ( T ) == typeof ( GCHandle ) || handle is 0 )
125+ if ( IsNotAllocated )
77126 {
78127 // nothing to do
79128 }
80129 else if ( RuntimeHelpers . IsReferenceOrContainsReferences < T > ( ) )
81130 {
82- GCHandle < object > . FromIntPtr ( handle ) . Dispose ( ) ;
131+ ICleaner . FreeRef ( handle ) ;
83132 }
84133 else if ( Unsafe . IsNaturallyAligned < T > ( ) )
85134 {
86- NativeMemory . Free ( handle . ToPointer ( ) ) ;
135+ ICleaner . Free ( handle ) ;
87136 }
88137 else
89138 {
90- NativeMemory . AlignedFree ( handle . ToPointer ( ) ) ;
139+ ICleaner . FreeAligned ( handle ) ;
91140 }
92141 }
93142
@@ -193,4 +242,142 @@ public object? Value
193242
194243 private GCHandle AsHandle ( ) => GCHandle . FromIntPtr ( opaque . handle ) ;
195244 }
196- }
245+ }
246+
247+ internal interface ICleaner
248+ {
249+ public static void FreeRef ( nint handle )
250+ {
251+ Debug . Assert ( handle is not 0 ) ;
252+
253+ GCHandle < object > . FromIntPtr ( handle ) . Dispose ( ) ;
254+ }
255+
256+ public static unsafe void Free ( nint handle )
257+ {
258+ Debug . Assert ( handle is not 0 ) ;
259+
260+ NativeMemory . Free ( handle . ToPointer ( ) ) ;
261+ }
262+
263+ public static unsafe void FreeAligned ( nint handle )
264+ {
265+ Debug . Assert ( handle is not 0 ) ;
266+
267+ NativeMemory . AlignedFree ( handle . ToPointer ( ) ) ;
268+ }
269+ }
270+
271+ internal unsafe interface ICleaner < TConvention > : ICleaner
272+ where TConvention : class , new ( )
273+ {
274+ public new static abstract void * FreeRef { get ; }
275+
276+ public new static abstract void * Free { get ; }
277+
278+ public new static abstract void * FreeAligned { get ; }
279+ }
280+
281+ file readonly unsafe ref struct OpaqueValueCleaner : ICleaner < CallConvCdecl > , ICleaner < CallConvStdcall > , ICleaner < CallConvAuto >
282+ {
283+ static void * ICleaner < CallConvCdecl > . FreeRef
284+ {
285+ get
286+ {
287+ return ( delegate * unmanaged[ Cdecl] < nint , void > ) & FreeCore ;
288+
289+ [ UnmanagedCallersOnly ( CallConvs = [ typeof ( CallConvCdecl ) ] ) ]
290+ static void FreeCore ( nint handle ) => ICleaner . FreeRef ( handle ) ;
291+ }
292+ }
293+
294+ static void * ICleaner < CallConvCdecl > . Free
295+ {
296+ get
297+ {
298+ return ( delegate * unmanaged[ Cdecl] < nint , void > ) & FreeCore ;
299+
300+ [ UnmanagedCallersOnly ( CallConvs = [ typeof ( CallConvCdecl ) ] ) ]
301+ static void FreeCore ( nint handle ) => ICleaner . Free ( handle ) ;
302+ }
303+ }
304+
305+ static void * ICleaner < CallConvCdecl > . FreeAligned
306+ {
307+ get
308+ {
309+ return ( delegate * unmanaged[ Cdecl] < nint , void > ) & FreeCore ;
310+
311+ [ UnmanagedCallersOnly ( CallConvs = [ typeof ( CallConvCdecl ) ] ) ]
312+ static void FreeCore ( nint handle ) => ICleaner . FreeAligned ( handle ) ;
313+ }
314+ }
315+
316+ static void * ICleaner < CallConvStdcall > . FreeRef
317+ {
318+ get
319+ {
320+ return ( delegate * unmanaged[ Stdcall] < nint , void > ) & FreeCore ;
321+
322+ [ UnmanagedCallersOnly ( CallConvs = [ typeof ( CallConvStdcall ) ] ) ]
323+ static void FreeCore ( nint handle ) => ICleaner . FreeRef ( handle ) ;
324+ }
325+ }
326+
327+ static void * ICleaner < CallConvStdcall > . Free
328+ {
329+ get
330+ {
331+ return ( delegate * unmanaged[ Stdcall] < nint , void > ) & FreeCore ;
332+
333+ [ UnmanagedCallersOnly ( CallConvs = [ typeof ( CallConvStdcall ) ] ) ]
334+ static void FreeCore ( nint handle ) => ICleaner . Free ( handle ) ;
335+ }
336+ }
337+
338+ static void * ICleaner < CallConvStdcall > . FreeAligned
339+ {
340+ get
341+ {
342+ return ( delegate * unmanaged[ Stdcall] < nint , void > ) & FreeCore ;
343+
344+ [ UnmanagedCallersOnly ( CallConvs = [ typeof ( CallConvStdcall ) ] ) ]
345+ static void FreeCore ( nint handle ) => ICleaner . FreeAligned ( handle ) ;
346+ }
347+ }
348+
349+ static void * ICleaner < CallConvAuto > . FreeRef
350+ {
351+ get
352+ {
353+ return ( delegate * unmanaged< nint , void > ) & FreeCore ;
354+
355+ [ UnmanagedCallersOnly ]
356+ static void FreeCore ( nint handle ) => ICleaner . FreeRef ( handle ) ;
357+ }
358+ }
359+
360+ static void * ICleaner < CallConvAuto > . Free
361+ {
362+ get
363+ {
364+ return ( delegate * unmanaged< nint , void > ) & FreeCore ;
365+
366+ [ UnmanagedCallersOnly ]
367+ static void FreeCore ( nint handle ) => ICleaner . Free ( handle ) ;
368+ }
369+ }
370+
371+ static void * ICleaner < CallConvAuto > . FreeAligned
372+ {
373+ get
374+ {
375+ return ( delegate * unmanaged< nint , void > ) & FreeCore ;
376+
377+ [ UnmanagedCallersOnly ]
378+ static void FreeCore ( nint handle ) => ICleaner . FreeAligned ( handle ) ;
379+ }
380+ }
381+ }
382+
383+ file sealed class CallConvAuto ;
0 commit comments