PkmnLibRSharp/PkmnLibRSharp/Utils/FFIHandle.cs

97 lines
3.0 KiB
C#

using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using PkmnLibSharp.FFI;
namespace PkmnLibSharp.Utils
{
internal static class FFIHandleHandler
{
private static Dictionary<ulong, WeakReference> _handleReferences = new();
internal static void ReleaseHandle(ulong handle)
{
ffi_release_handle(handle);
_handleReferences.Remove(handle);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
internal static FFIHandle.FFIHandleReference GetHandleReference(ulong handle)
{
if (_handleReferences.TryGetValue(handle, out var weakReference))
{
if (weakReference.IsAlive)
return (FFIHandle.FFIHandleReference)weakReference.Target;
}
var reference = new FFIHandle.FFIHandleReference(handle);
_handleReferences[handle] = new WeakReference(reference);
return reference;
}
[DllImport(Data.DllName, CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
private static extern void ffi_release_handle(ulong handle);
}
internal struct FFIHandleValue
{
public static implicit operator FFIHandleValue(FFIHandle handle)
{
return new FFIHandleValue(handle.Handle);
}
public FFIHandleValue()
{
}
private FFIHandleValue(ulong handle)
{
Handle = handle;
}
public ulong Handle { get; }
public bool IsNull => Handle == 0;
public FFIHandle Resolve()
{
if (Handle == 0)
throw new NullReferenceException("Handle is null");
return new FFIHandle(Handle, FFIHandleHandler.GetHandleReference(Handle));
}
}
public struct FFIHandle
{
internal FFIHandle(ulong handle, FFIHandleReference handleReference)
{
Handle = handle;
_handleReference = handleReference;
}
public ulong Handle { get; }
public static FFIHandle Zero => new(0, null!);
/// <summary>
/// This is only here for reference counting purposes. As soon as every handle is released, the FFIHandleReference
/// will be garbage collected, and the finalizer will be called, which will release the handle, signifying to
/// PkmnLib that we no longer need the handle.
/// </summary>
// ReSharper disable once NotAccessedField.Local
private readonly FFIHandleReference _handleReference;
internal class FFIHandleReference
{
private readonly ulong _handle;
public FFIHandleReference(ulong handle)
{
_handle = handle;
}
~FFIHandleReference()
{
FFIHandleHandler.ReleaseHandle(this._handle);
}
}
}
}