using System; using System.Runtime.InteropServices; namespace Dalamud.Game.Libc; /// /// An address wrapper around the class. /// public sealed partial class OwnedStdString { private readonly DeallocatorDelegate dealloc; /// /// Initializes a new instance of the class. /// Construct a wrapper around std::string. /// /// /// Violating any of these might cause an undefined hehaviour. /// 1. This function takes the ownership of the address. /// 2. A memory pointed by address argument is assumed to be allocated by Marshal.AllocHGlobal thus will try to call Marshal.FreeHGlobal on the address. /// 3. std::string object pointed by address must be initialized before calling this function. /// /// The address of the owned std string. /// A deallocator function. internal OwnedStdString(IntPtr address, DeallocatorDelegate dealloc) { this.Address = address; this.dealloc = dealloc; } /// /// The delegate type that deallocates a std string. /// /// Address to deallocate. internal delegate void DeallocatorDelegate(IntPtr address); /// /// Gets the address of the std string. /// public IntPtr Address { get; private set; } /// /// Read the wrapped StdString. /// /// The StdString. public StdString Read() => StdString.ReadFromPointer(this.Address); } /// /// Implements IDisposable. /// public sealed partial class OwnedStdString : IDisposable { private bool isDisposed; /// /// Finalizes an instance of the class. /// ~OwnedStdString() => this.Dispose(false); /// /// Dispose of managed and unmanaged resources. /// public void Dispose() { GC.SuppressFinalize(this); this.Dispose(true); } /// /// Dispose of managed and unmanaged resources. /// /// A value indicating whether this was called via Dispose or finalized. public void Dispose(bool disposing) { if (this.isDisposed) return; this.isDisposed = true; if (disposing) { } if (this.Address == IntPtr.Zero) { // Something got seriously fucked. throw new AccessViolationException(); } // Deallocate inner string first this.dealloc(this.Address); // Free the heap Marshal.FreeHGlobal(this.Address); // Better safe (running on a nullptr) than sorry. (running on a dangling pointer) this.Address = IntPtr.Zero; } }