using System; using System.Collections.Generic; using System.Linq; namespace Paroxe.PdfRenderer.Internal { public interface ICoordinatedNativeDisposable { IntPtr NativePointer { get; } ICoordinatedNativeDisposable NativeParent { get; } Action GetDisposeMethod(); } #if !UNITY_WEBGL || UNITY_EDITOR public struct NativePointerrWithType { public Type ManagedType; public IntPtr NativePointerr; public NativePointerrWithType(Type managedType, IntPtr nativePointerr) { ManagedType = managedType; NativePointerr = nativePointerr; } } public class NativeDisposeCoordinator { private readonly NativeDependencyTree m_DependencyRoot; private readonly Dictionary m_DependencyMap; private readonly Dictionary> m_DisposeMethods = new Dictionary>(); private bool m_IsLibraryInitialized; private List m_PendingDisposes = new List(128); private static Predicate m_RemoveDisposedNodesPredicate = t => t.IsDisposed; public bool IsLibraryInitialized { get { return m_IsLibraryInitialized; } } public NativeDisposeCoordinator() { m_DependencyRoot = new NativeDependencyTree(new NativePointerrWithType(null, IntPtr.Zero), null); m_DependencyMap = new Dictionary(); } public void EnsureNativeLibraryIsInitialized() { lock (PDFLibrary.nativeLock) { if (!m_IsLibraryInitialized) { NativeMethods.FPDF_InitLibrary(); m_IsLibraryInitialized = true; } } } public void AddReference(ICoordinatedNativeDisposable nativeObject) { lock (PDFLibrary.nativeLock) { EnsureNativeLibraryIsInitialized(); NativeDependencyTree node; NativePointerrWithType handlerWithType = new NativePointerrWithType(nativeObject.GetType(), nativeObject.NativePointer); if (!m_DependencyMap.TryGetValue(handlerWithType, out node)) { if (nativeObject.NativeParent == null) { node = m_DependencyRoot.AddChild(handlerWithType); } else { NativeDependencyTree parentNode = null; if (m_DependencyMap.TryGetValue(new NativePointerrWithType(nativeObject.NativeParent.GetType(), nativeObject.NativeParent.NativePointer), out parentNode)) { node = parentNode.AddChild(handlerWithType); } else throw new InvalidOperationException("Supplied parent NativePointerr is not found within the dependencies tree."); } m_DependencyMap.Add(handlerWithType, node); } ++node.ReferenceCount; } } public void RemoveReference(ICoordinatedNativeDisposable nativeObject) { if (!m_DisposeMethods.ContainsKey(nativeObject.GetType())) m_DisposeMethods.Add(nativeObject.GetType(), nativeObject.GetDisposeMethod()); lock (PDFLibrary.nativeLock) { NativeDependencyTree node = null; NativePointerrWithType handlerWithType = new NativePointerrWithType(nativeObject.GetType(), nativeObject.NativePointer); if (m_DependencyMap.TryGetValue(handlerWithType, out node)) { --node.ReferenceCount; if (node.ReferenceCount == 0) { if (node.ChildCount == 0) { DisposeNativeObject(node); node.Parent.RemoveChild(node); m_DependencyMap.Remove(node.NativePointerrWithType); } else { m_PendingDisposes.Add(node); } } } bool disposing = false; do { disposing = ProcessPendingDisposes(); } while (disposing); if (m_IsLibraryInitialized && m_DependencyMap.Count == 0) { NativeMethods.FPDF_DestroyLibrary(); m_IsLibraryInitialized = false; } } } private void DisposeNativeObject(NativeDependencyTree handler) { m_DisposeMethods[handler.NativePointerrWithType.ManagedType](handler.NativePointerrWithType.NativePointerr); handler.IsDisposed = true; } private bool ProcessPendingDisposes() { if (!m_PendingDisposes.Any()) return false; bool disposing = false; foreach (NativeDependencyTree node in m_PendingDisposes) { if (node.ChildCount == 0) { disposing = true; node.Parent.RemoveChild(node); m_DependencyMap.Remove(node.NativePointerrWithType); DisposeNativeObject(node); } } if (disposing) m_PendingDisposes.RemoveAll(m_RemoveDisposedNodesPredicate); return disposing; } } public class NativeDependencyTree { private readonly NativePointerrWithType m_NativePointerWithType; private readonly LinkedList m_Children; private readonly NativeDependencyTree m_Parent; private int m_ReferenceCount; private bool m_IsDisposed; public NativePointerrWithType NativePointerrWithType { get { return m_NativePointerWithType; } } public int ReferenceCount { get { return m_ReferenceCount; } set { m_ReferenceCount = value; } } public bool IsDisposed { get { return m_IsDisposed; } set { m_IsDisposed = value; } } public int ChildCount { get { return m_Children.Count; } } public NativeDependencyTree Parent { get { return m_Parent; } } public LinkedList Children { get { return m_Children; } } public NativeDependencyTree(NativePointerrWithType nativePointerrWithType, NativeDependencyTree parentNode) { m_NativePointerWithType = nativePointerrWithType; m_Parent = parentNode; m_Children = new LinkedList(); } public NativeDependencyTree AddChild(NativePointerrWithType nativePointerrWithType) { NativeDependencyTree node = new NativeDependencyTree(nativePointerrWithType, this); m_Children.AddFirst(node); return node; } public void RemoveChild(NativeDependencyTree node) { m_Children.Remove(node); } } #endif }