using System; using System.Collections.Concurrent; namespace PkmnLibSharp.Utilities { public abstract class PointerWrapper : IDisposable { private IntPtr _ptr; internal IntPtr Ptr { get { if (IsDeleted) { throw new Exception( "Pointer access after dispose detected. This is not legal, and will cause native exceptions."); } return _ptr; } } protected bool IsDeleted { get; private set; } = false; private static readonly ConcurrentDictionary> Cached = new ConcurrentDictionary>(); private protected PointerWrapper() { } protected PointerWrapper(IntPtr ptr) { Initialize(ptr); } protected internal virtual void Initialize(IntPtr ptr) { if (ptr == IntPtr.Zero) { throw new NullReferenceException("Initializing pointer was nullptr"); } _ptr = ptr; var weakRef = new WeakReference(this); Cached.AddOrUpdate(ptr, weakRef, (intPtr, reference) => weakRef); } ~PointerWrapper() { if (!IsDeleted) Cached.TryRemove(Ptr, out _); } public static bool TryResolvePointer(IntPtr p, out T? result) where T : PointerWrapper { if (p == IntPtr.Zero) { result = null; return true; } if (Cached.TryGetValue(p, out var val)) { if (val.TryGetTarget(out var target)) { if (target is T r) { result = r; return true; } } } result = null; return false; } protected abstract void DeletePtr(); public virtual void Dispose() { if (IsDeleted) return; DeletePtr(); MarkAsDeleted(); } protected internal virtual void MarkAsDeleted() { IsDeleted = true; Cached.TryRemove(_ptr, out _); } public static bool operator ==(PointerWrapper? a, PointerWrapper? b) { return a?._ptr == b?._ptr; } public static bool operator !=(PointerWrapper? a, PointerWrapper? b) { return !(a == b); } protected bool Equals(PointerWrapper other) { return _ptr.Equals(other._ptr); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; if (obj.GetType() != this.GetType()) return false; return Equals((PointerWrapper) obj); } public override int GetHashCode() { return _ptr.GetHashCode(); } public override string ToString() { return $"(#{_ptr}) -> {GetType().FullName}"; } } }