Browse Source

C# wrapper: improved native memory management, fixed memory leaks

Marcin Ziąbek 1 year ago
parent
commit
ad7b7686ae

+ 1 - 1
Source/QuestPDF/Skia/SkCanvas.cs

@@ -7,7 +7,7 @@ namespace QuestPDF.Skia;
 internal sealed class SkCanvas : IDisposable
 {
     public IntPtr Instance { get; private set; }
-    private bool DisposeNativeObject = true;
+    private bool DisposeNativeObject { get; }
 
     public SkCanvas(IntPtr instance, bool disposeNativeObject = true)
     {

+ 6 - 15
Source/QuestPDF/Skia/SkData.cs

@@ -7,17 +7,10 @@ namespace QuestPDF.Skia;
 internal sealed class SkData : IDisposable
 {
     public IntPtr Instance { get; private set; }
-    private bool DisposeNativeObject = true;
-    
-    public SkData(IntPtr instance, bool disposeNativeObject = true)
-    {
-        Instance = instance;
-        DisposeNativeObject = disposeNativeObject;
-    }
 
-    public void SetAsOwnedByAnotherObject()
+    public SkData(IntPtr instance)
     {
-        DisposeNativeObject = false;
+        Instance = instance;
     }
     
     public static SkData FromFile(string filePath)
@@ -48,12 +41,12 @@ internal sealed class SkData : IDisposable
     public byte[] ToBytes()
     {
         var content = API.data_get_bytes(Instance);
-        var result = new byte[content.length];
+        byte[] result = new byte[content.length];
         Marshal.Copy(content.bytes, result, 0, content.length);
 
         return result;
     }
-
+    
     ~SkData()
     {
         Dispose();
@@ -64,9 +57,7 @@ internal sealed class SkData : IDisposable
         if (Instance == IntPtr.Zero)
             return;
         
-        if (DisposeNativeObject)
-            API.data_delete(Instance);
-        
+        API.data_unref(Instance);
         Instance = IntPtr.Zero;
     }
     
@@ -89,6 +80,6 @@ internal sealed class SkData : IDisposable
         public static extern GetBytesFromDataResult data_get_bytes(IntPtr data);
     
         [DllImport(SkiaAPI.LibraryName)]
-        public static extern void data_delete(IntPtr data);
+        public static extern void data_unref(IntPtr data);
     }
 }

+ 3 - 3
Source/QuestPDF/Skia/SkDocument.cs

@@ -15,7 +15,7 @@ internal sealed class SkDocument : IDisposable
     public SkCanvas BeginPage(float width, float height)
     {
         var instance = API.document_begin_page(Instance, width, height);
-        return new SkCanvas(instance, false);
+        return new SkCanvas(instance, disposeNativeObject: false);
     }
     
     public void EndPage()
@@ -38,7 +38,7 @@ internal sealed class SkDocument : IDisposable
         if (Instance == IntPtr.Zero)
             return;
         
-        API.document_delete(Instance);
+        API.document_unref(Instance);
         Instance = IntPtr.Zero;
     }
     
@@ -54,6 +54,6 @@ internal sealed class SkDocument : IDisposable
         public static extern void document_close(IntPtr document);
     
         [DllImport(SkiaAPI.LibraryName)]
-        public static extern void document_delete(IntPtr document);
+        public static extern void document_unref(IntPtr document);
     }
 }

+ 3 - 4
Source/QuestPDF/Skia/SkImage.cs

@@ -30,7 +30,6 @@ internal sealed class SkImage : IDisposable
         if (instance == IntPtr.Zero)
             throw new Exception("Cannot decode the provided image.");
         
-        data.SetAsOwnedByAnotherObject();
         return new SkImage(instance);
     }
     
@@ -54,7 +53,7 @@ internal sealed class SkImage : IDisposable
     public SkData GetEncodedData()
     {
         var dataInstance = API.image_get_encoded_data(Instance);
-        return new SkData(dataInstance, false);
+        return new SkData(dataInstance);
     }
     
     ~SkImage()
@@ -67,7 +66,7 @@ internal sealed class SkImage : IDisposable
         if (Instance == IntPtr.Zero)
             return;
         
-        API.image_delete(Instance);
+        API.image_unref(Instance);
         Instance = IntPtr.Zero;
     }
     
@@ -77,7 +76,7 @@ internal sealed class SkImage : IDisposable
         public static extern IntPtr image_create_from_data(IntPtr data);
         
         [DllImport(SkiaAPI.LibraryName)]
-        public static extern void image_delete(IntPtr image);
+        public static extern void image_unref(IntPtr image);
         
         [DllImport(SkiaAPI.LibraryName)]
         public static extern IntPtr image_resize_and_compress(IntPtr image, int targetImageWidth, int targetImageHeight, int compressionQuality);

+ 4 - 31
Source/QuestPDF/Skia/SkNativeDependencyCompatibilityChecker.cs

@@ -1,6 +1,5 @@
 using System;
 using System.Runtime.InteropServices;
-using QuestPDF.Drawing.Exceptions;
 
 namespace QuestPDF.Skia;
 
@@ -10,15 +9,9 @@ public static class SkNativeDependencyCompatibilityChecker
         
     public static void Test()
     {
-        const string exceptionBaseMessage = "The QuestPDF library has encountered an issue while loading one of its dependencies.";
-        var newLine = Environment.NewLine;
-        var paragraph = newLine + newLine;
-        
         if (IsCompatibilityChecked)
             return;
             
-        // test with dotnet-based mechanism where native files are provided
-        // in the "runtimes/{rid}/native" folder on Core, or by the targets file on .NET Framework
         var innerException = CheckIfExceptionIsThrownWhenLoadingNativeDependencies();
 
         if (innerException == null)
@@ -27,31 +20,11 @@ public static class SkNativeDependencyCompatibilityChecker
             return;
         }
 
-        if (!SkNativeDependencyProvider.IsCurrentPlatformSupported())
-        {
-            var message = 
-                $"{exceptionBaseMessage}{paragraph}" +
-                "Your runtime is currently not supported by QuestPDF. " +
-                "Currently supported runtimes are: win-x64, linux-x64, osx-x64, osx-arm64.";
-            
-            if (RuntimeInformation.ProcessArchitecture == Architecture.X64)
-                message += $"{paragraph}Please consider setting the 'Platform target' property to 'x64' in your project settings.";
-            
-            throw new InitializationException(message, innerException);
-        }
-        
-        // detect platform, copy appropriate native files and test compatibility again
-        SkNativeDependencyProvider.EnsureNativeFileAvailability();
+        // TODO: improve error message
+        var initializationExceptionMessage =
+            $"The QuestPDF library has encountered an issue while loading one of its dependencies.";
         
-        innerException = CheckIfExceptionIsThrownWhenLoadingNativeDependencies();
-
-        if (innerException == null)
-        {
-            IsCompatibilityChecked = true;
-            return;
-        }
-
-        throw new Exception(exceptionBaseMessage, innerException);
+        throw new Exception(initializationExceptionMessage, innerException);
     }
     
     private static Exception? CheckIfExceptionIsThrownWhenLoadingNativeDependencies()

+ 2 - 2
Source/QuestPDF/Skia/SkPicture.cs

@@ -34,14 +34,14 @@ internal sealed class SkPicture : IDisposable
         if (Instance == IntPtr.Zero)
             return;
         
-        API.picture_delete(Instance);
+        API.picture_unref(Instance);
         Instance = IntPtr.Zero;
     }
     
     private static class API
     {
         [DllImport(SkiaAPI.LibraryName)]
-        public static extern void picture_delete(IntPtr picture);
+        public static extern void picture_unref(IntPtr picture);
         
         [DllImport(SkiaAPI.LibraryName)]
         public static extern IntPtr picture_serialize(IntPtr picture);

+ 1 - 1
Source/QuestPDF/Skia/SkPictureRecorder.cs

@@ -15,7 +15,7 @@ internal sealed class SkPictureRecorder : IDisposable
     public SkCanvas BeginRecording(float width, float height)
     {
         var canvasInstance = API.picture_recorder_begin_recording(Instance, width, height);
-        return new SkCanvas(canvasInstance, false);
+        return new SkCanvas(canvasInstance, disposeNativeObject: false);
     }
     
     public SkPicture EndRecording()

+ 1 - 1
Source/QuestPDF/Skia/SkSvgCanvas.cs

@@ -9,7 +9,7 @@ internal sealed class SkSvgCanvas
     {
         var bounds = new SkRect(0, 0, width, height);
         var instance = API.svg_create_canvas(bounds, writeStream.Instance);
-        return new SkCanvas(instance);
+        return new SkCanvas(instance, disposeNativeObject: false);
     }
     
     private static class API

+ 3 - 4
Source/QuestPDF/Skia/SkSvgImage.cs

@@ -11,8 +11,7 @@ internal sealed class SkSvgImage : IDisposable
     public SkSvgImage(string svgString)
     {
         using var data = SkData.FromBinary(System.Text.Encoding.UTF8.GetBytes(svgString));
-        data.SetAsOwnedByAnotherObject();
-        
+
         Instance = API.svg_create(data.Instance);
         
         if (Instance == IntPtr.Zero)
@@ -31,7 +30,7 @@ internal sealed class SkSvgImage : IDisposable
         if (Instance == IntPtr.Zero)
             return;
         
-        API.svg_delete(Instance);
+        API.svg_unref(Instance);
         Instance = IntPtr.Zero;
     }
     
@@ -41,7 +40,7 @@ internal sealed class SkSvgImage : IDisposable
         public static extern IntPtr svg_create(IntPtr data);
         
         [DllImport(SkiaAPI.LibraryName)]
-        public static extern void svg_delete(IntPtr svg);
+        public static extern void svg_unref(IntPtr svg);
         
         [DllImport(SkiaAPI.LibraryName)]
         public static extern void svg_get_viewbox(IntPtr svg, out SkRect viewbox);

+ 2 - 2
Source/QuestPDF/Skia/SkWriteStream.cs

@@ -38,9 +38,9 @@ internal sealed class SkWriteStream : IDisposable
         public static extern IntPtr write_stream_create();
     
         [DllImport(SkiaAPI.LibraryName)]
-        public static extern void write_stream_delete(IntPtr typeface);
+        public static extern void write_stream_delete(IntPtr stream);
         
         [DllImport(SkiaAPI.LibraryName)]
-        public static extern IntPtr write_stream_detach_data(IntPtr typeface);    
+        public static extern IntPtr write_stream_detach_data(IntPtr stream);    
     }
 }

+ 2 - 2
Source/QuestPDF/Skia/Text/SkFontCollection.cs

@@ -51,7 +51,7 @@ internal sealed class SkFontCollection : IDisposable
         if (Instance == IntPtr.Zero)
             return;
         
-        API.font_collection_delete(Instance);
+        API.font_collection_unref(Instance);
         Instance = IntPtr.Zero;
     }
     
@@ -61,6 +61,6 @@ internal sealed class SkFontCollection : IDisposable
         public static extern IntPtr font_collection_create(CreateCommand command);
         
         [DllImport(SkiaAPI.LibraryName)]
-        public static extern void font_collection_delete(IntPtr fontCollection);
+        public static extern void font_collection_unref(IntPtr fontCollection);
     }
 }

+ 1 - 1
Source/QuestPDF/Skia/Text/SkFontManager.cs

@@ -18,7 +18,7 @@ internal sealed class SkFontManager
     public SkTypeface CreateTypeface(SkData data)
     {
         var instance = API.font_manager_create_typeface(Instance, data.Instance);
-        return new SkTypeface(instance, disposeNativeObject: false);
+        return new SkTypeface(instance);
     }
     
     private static class API

+ 4 - 8
Source/QuestPDF/Skia/Text/SkTypeface.cs

@@ -6,12 +6,10 @@ namespace QuestPDF.Skia.Text;
 public sealed class SkTypeface : IDisposable
 {
     public IntPtr Instance { get; private set; }
-    private bool DisposeNativeObject = true;
-    
-    public SkTypeface(IntPtr instance, bool disposeNativeObject)
+
+    public SkTypeface(IntPtr instance)
     {
         Instance = instance;
-        DisposeNativeObject = disposeNativeObject;
     }
     
     ~SkTypeface()
@@ -24,15 +22,13 @@ public sealed class SkTypeface : IDisposable
         if (Instance == IntPtr.Zero)
             return;
         
-        if (DisposeNativeObject)
-            API.typeface_delete(Instance);
-        
+        API.typeface_unref(Instance);
         Instance = IntPtr.Zero;
     }
     
     private static class API
     {
         [DllImport(SkiaAPI.LibraryName)]
-        public static extern void typeface_delete(IntPtr typeface);
+        public static extern void typeface_unref(IntPtr typeface);
     }
 }

+ 2 - 2
Source/QuestPDF/Skia/Text/SkTypefaceProvider.cs

@@ -32,7 +32,7 @@ internal sealed class SkTypefaceProvider : IDisposable
         if (Instance == IntPtr.Zero)
             return;
         
-        API.typeface_font_provider_delete(Instance);
+        API.typeface_font_provider_unref(Instance);
         Instance = IntPtr.Zero;
     }
     
@@ -48,6 +48,6 @@ internal sealed class SkTypefaceProvider : IDisposable
         public static extern void typeface_font_provider_add_typeface_with_custom_alias(IntPtr typefaceProvider, IntPtr typeface, [MarshalAs(UnmanagedType.CustomMarshaler, MarshalTypeRef = typeof(Utf8StringMarshaller))] string alias);
         
         [DllImport(SkiaAPI.LibraryName)]
-        public static extern void typeface_font_provider_delete(IntPtr typefaceProvider);
+        public static extern void typeface_font_provider_unref(IntPtr typefaceProvider);
     }
 }