97 lines
3.0 KiB
C#
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);
|
|
}
|
|
}
|
|
}
|
|
} |