[ghex] gtkhex: Segregate mmap backend as plugin, etc.



commit 92b0b6219a14dc28c2eb8bbea95fb3d6802bb513
Author: Logan Rathbone <poprocks gmail com>
Date:   Mon Apr 4 22:16:18 2022 -0400

    gtkhex: Segregate mmap backend as plugin, etc.
    
    I didn't want to make this sweeping of a change between beta and stable,
    but I think the work needed to be done before the initial stable release
    of the library was complete.
    
    The mmap buffer being a compile-time option only for 4.0 is fine, but we
    can't have divergent APIs due to compile-time differences.
    
    Add new hex_buffer_util_new function to spin up a valid implementation
    of a HexBuffer interface, to abstract the API from the underlying
    implementations. The `malloc` buffer is now baked into the library as a
    fallback.
    
    Make the requisite changes to the meson scripts.
    
    The versioning has also been pinned down in this commit. The initial
    stable release of GHex the application will be 42.0 and will include
    libgtkhex 4.0.0.

 data/gtkhex-4.pc.in     |  2 +-
 data/meson.build        |  3 +-
 docs/meson.build        |  2 +-
 meson.build             | 17 +++++++----
 meson_options.txt       |  2 +-
 src/hex-buffer-iface.c  | 80 ++++++++++++++++++++++++++++++++++++++++++++++++-
 src/hex-buffer-iface.h  | 14 +++++++++
 src/hex-buffer-malloc.c | 10 +++++--
 src/hex-buffer-malloc.h |  3 +-
 src/hex-buffer-mmap.c   | 10 +++++--
 src/hex-buffer-mmap.h   |  3 +-
 src/hex-document.c      | 14 ++-------
 src/main.c              |  6 ----
 src/meson.build         | 51 +++++++++++--------------------
 14 files changed, 147 insertions(+), 70 deletions(-)
---
diff --git a/data/gtkhex-4.pc.in b/data/gtkhex-4.pc.in
index 61eca64..f388d57 100644
--- a/data/gtkhex-4.pc.in
+++ b/data/gtkhex-4.pc.in
@@ -5,7 +5,7 @@ includedir=@includedir@
 
 Name: gtkhex
 Description: GtkHex - A hex display widget.
-Version: @VERSION@
+Version: @LIBGTKHEX_VERSION@
 Requires: gtk4
 Libs: -L${libdir} -lgtkhex-4
 Cflags: -I${includedir}/gtkhex-4
diff --git a/data/meson.build b/data/meson.build
index 5835a14..dddda61 100644
--- a/data/meson.build
+++ b/data/meson.build
@@ -42,8 +42,7 @@ pkg_conf.set('exec_prefix', ghex_prefix)
 pkg_conf.set('libdir', ghex_libdir)
 pkg_conf.set('includedir', ghex_includedir)
 
-pkg_conf.set('VERSION', meson.project_version())
-
+pkg_conf.set('LIBGTKHEX_VERSION', libgtkhex_version)
 
 configure_file(
   input: 'gtkhex-4.pc.in',
diff --git a/docs/meson.build b/docs/meson.build
index 327ef27..ae7a51f 100644
--- a/docs/meson.build
+++ b/docs/meson.build
@@ -6,7 +6,7 @@ toml_conf = configuration_data()
 toml_conf.set('version', meson.project_version())
 
 gidocgen = find_program('gi-docgen', required: get_option('gtk_doc'))
-gir_apiname = 'libgtkhex-@0@.0'.format(libgtkhex_version_major)
+gir_apiname = 'gtkhex-@0@.0'.format(libgtkhex_api_version)
 
 gidocgen_common_args = [
   '--quiet',
diff --git a/meson.build b/meson.build
index cdaa07e..94adbad 100644
--- a/meson.build
+++ b/meson.build
@@ -15,6 +15,9 @@ else
   resource_base_path = '/org/gnome/GHex'
 endif
 
+# Backend plugins: (nb: only mmap exists at this time along with malloc, which is baked in)
+mmap_backend = get_option('mmap-buffer-backend')
+
 gir = find_program('g-ir-scanner', required : get_option('introspection'))
 generate_gir = gir.found() and (not meson.is_cross_build() or get_option('introspection').enabled())
 build_gtk_doc = get_option('gtk_doc')
@@ -29,6 +32,7 @@ libgtkhex_api_version   = 4
 libgtkhex_version_major = 0
 libgtkhex_version_minor = 0
 libgtkhex_version_micro = 0
+libgtkhex_version = '@0@.@1@.@2@'.format(libgtkhex_api_version, libgtkhex_version_minor, 
libgtkhex_version_micro)
 
 ghex_prefix = get_option('prefix')
 ghex_libdir = join_paths(ghex_prefix, get_option('libdir'))
@@ -36,6 +40,7 @@ ghex_includedir = join_paths(ghex_prefix, get_option('includedir'))
 ghex_datadir = join_paths(ghex_prefix, get_option('datadir'))
 ghex_localedir = join_paths(ghex_prefix, get_option('localedir'))
 ghex_docdir = join_paths(ghex_prefix, get_option('docdir'))
+ghex_plugindir = join_paths(ghex_libdir, 'gtkhex-@0@.0'.format(libgtkhex_api_version))
 ghex_pkgconfigdir = join_paths(ghex_libdir, 'pkgconfig')
 ghex_applicationsdir = join_paths(ghex_datadir, 'applications')
 ghex_schemasdir = join_paths(ghex_datadir, 'glib-2.0/schemas')
@@ -63,9 +68,6 @@ i18n = import('i18n')
 
 cc = meson.get_compiler('c')
 
-# nb: this option will likely be removed in a future release
-buffer_backend = get_option('buffer-backend')
-
 # just to avoid manually punching it into the XML files as well as the source.
 shaded_box_max = 1000
 
@@ -77,6 +79,7 @@ config_h.set_quoted('PACKAGE_VERSION', meson.project_version())
 config_h.set_quoted('PACKAGE_STRING', '@0@ @1@'.format(meson.project_name(), meson.project_version()))
 config_h.set_quoted('PACKAGE_DATADIR', ghex_datadir)
 config_h.set_quoted('PACKAGE_LIBDIR', ghex_libdir)
+config_h.set_quoted('PACKAGE_PLUGINDIR', ghex_plugindir)
 config_h.set_quoted('PACKAGE_LOCALE_DIR', ghex_localedir)
 config_h.set_quoted('PACKAGE_DOCDIR', ghex_docdir)
 
@@ -86,7 +89,8 @@ config_h.set('LOCALEDIR', 'PACKAGE_LOCALE_DIR')
 
 config_h.set('CONFIG_H_SHADED_BOX_MAX', shaded_box_max)
 
-if buffer_backend == 'mmap'
+# nb: this config.h flag will likely be removed in a future release
+if mmap_backend
   config_h.set('BACKEND_MMAP', true)
 endif
 
@@ -100,7 +104,7 @@ config_h.set('DEBUG', get_option('debug'))
 config_h.set10('ENABLE_NLS', true) 
 
 # mmap: Check for required headers and fcns
-if buffer_backend == 'mmap'
+if mmap_backend
   message('Checking dependencies for `mmap` buffer backend...')
 
   check_headers_mmap = [
@@ -126,6 +130,7 @@ if buffer_backend == 'mmap'
   message('...DONE')
 endif
 
+gmodule_dep = dependency('gmodule-2.0')
 gio_dep = dependency('gio-2.0', version: '>= 2.66.0')
 gtk_dep = dependency('gtk4', version: '>= 4.0.0')
 
@@ -157,7 +162,7 @@ summary({'prefix': ghex_prefix,
         }, section: 'Directories')
 
 summary({'Development build': get_option('development'),
-         'Buffer backend': buffer_backend,
+         '`mmap` buffer backend': mmap_backend,
          'Static HTML help': get_option('static-html-help'),
          'API Documentation': build_gtk_doc,
         }, section: 'Configuration')
diff --git a/meson_options.txt b/meson_options.txt
index 2d6abf0..a5795fc 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -3,7 +3,7 @@
 option('docdir', type: 'string', value: 'share/doc', description: 'docdir (defaults to: share/doc)')
 
 option('development', type: 'boolean', value: false, description: 'If this is a development build')
-option('buffer-backend', type : 'combo', choices : ['mmap', 'malloc'], value : 'mmap')
+option('mmap-buffer-backend', type : 'boolean', value: true, description: 'Build mmap buffer backend')
 option('introspection', type: 'feature', value: 'auto', description: 'Generate gir data (requires 
gobject-introspection)')
 option('gtk_doc', type: 'boolean', value: false, description: 'Build reference manual (requires gtk-doc)')
 option('static-html-help', type: 'boolean', value: false, description: 'Install static HTML help files (for 
systems without Yelp)')
diff --git a/src/hex-buffer-iface.c b/src/hex-buffer-iface.c
index b820b15..fc10e34 100644
--- a/src/hex-buffer-iface.c
+++ b/src/hex-buffer-iface.c
@@ -23,13 +23,15 @@
  */
 
 #include "hex-buffer-iface.h"
+#include "hex-buffer-malloc.h"
+#include <config.h>
 
 /**
  * HexBuffer: 
  * 
  * #HexBuffer is an interface which can be implemented to act as a buffer
  * for [class@Hex.Document] data. This allows for a #HexDocument to be
- * manipulated at the backend by different backends.
+ * manipulated by different backends.
  *
  * Once a file has been loaded into the buffer, it can be read, written
  * to file, etc.
@@ -37,6 +39,11 @@
  * #HexBuffer makes reference to the "payload," which is the size of the
  * substantive data in the buffer, not counting items like padding, a gap,
  * etc. (all dependent upon the underlying implementation).
+ *
+ * Most clients who just want to create an spin up a #HexBuffer object should
+ * look to the [func@Hex.Buffer.util_new] utility function as a starting
+ * point, and then manipulate the returned #HexBuffer object with the methods
+ * documented herein.
  */
 G_DEFINE_INTERFACE (HexBuffer, hex_buffer, G_TYPE_OBJECT)
 
@@ -326,6 +333,77 @@ hex_buffer_get_payload_size (HexBuffer *self)
 
 /* Utility functions */
 
+/**
+ * hex_buffer_util_new:
+ * @plugin: (nullable): the name of the plugin, or %NULL
+ * @file: (nullable): file to initialize the buffer with, or %NULL
+ *
+ * Utility function to create an on object which implements the HexBuffer
+ * interface.
+ *
+ * The `plugin` parameter will be the unique part of the plugin file name (eg,
+ * if the file name is libhex-buffer-mmap.so, you would specify "mmap"). If
+ * `NULL` is passed, the fallback (presently the "malloc" backend, but this is
+ * an implementation detail and may be subject to change) will be used.
+ *
+ * The `file` parameter is a valid #GFile if you would like the buffer
+ * pre-loaded, or %NULL for an empty buffer.
+ *
+ * Returns: (transfer full): a pointer to a valid implementation of a
+ * [iface@Hex.Buffer] interface, pre-cast as type #HexBuffer, or %NULL if
+ * the operation failed.
+ */
+
+HexBuffer * hex_buffer_util_new (const char *plugin, GFile *file)
+{
+       GModule *module;
+       HexBufferNewFunc func = NULL;
+       char *plugin_soname = NULL;
+       char *plugin_path = NULL;
+       char *symbol_name = NULL;
+
+       /* If dynamic loading of plugins isn't even supported on the platform, or
+        * if NULL is passed, fall right back to `malloc` since it's the only one
+        * baked in.
+        */
+       if (!g_module_supported () || !plugin)
+       {
+               g_debug ("Modules not supported or NULL passed - falling back to `malloc` backend.");
+               return hex_buffer_malloc_new (file);
+       }
+
+       plugin_soname = g_strdup_printf ("hex-buffer-%s", plugin);
+       plugin_path = g_module_build_path (PACKAGE_PLUGINDIR, plugin_soname);
+       symbol_name = g_strdup_printf ("hex_buffer_%s_new", plugin);
+       module = g_module_open (plugin_path, G_MODULE_BIND_LAZY);
+
+       if (! module)
+       {
+               g_warning ("Unable to load plugin at %s - falling back to `malloc` backend",
+                               plugin_path);
+               func = hex_buffer_malloc_new;
+       }
+       else if (! g_module_symbol (module, symbol_name, (gpointer *)&func) ||
+                       func == NULL)
+       {
+               g_warning ("Plugin found at %s - but unable to locate symbol: %s - "
+                               "falling back to `malloc` backend.",
+                               plugin_path, symbol_name);
+               func = hex_buffer_malloc_new;
+       }
+       else
+       {
+               g_debug ("Loaded plugin: %s", plugin_path);
+       }
+
+/* out: */
+       g_free (plugin_soname);
+       g_free (plugin_path);
+       g_free (symbol_name);
+
+       return func (file);
+}
+
 /**
  * hex_buffer_util_get_file_size:
  * @file: file to obtain size of
diff --git a/src/hex-buffer-iface.h b/src/hex-buffer-iface.h
index 47c9506..e14fa38 100644
--- a/src/hex-buffer-iface.h
+++ b/src/hex-buffer-iface.h
@@ -36,6 +36,19 @@ G_BEGIN_DECLS
 #define HEX_TYPE_BUFFER hex_buffer_get_type ()
 G_DECLARE_INTERFACE (HexBuffer, hex_buffer, HEX, BUFFER, GObject)
 
+/**
+ * HexBufferNewFunc:
+ * @file: #GFile to pass to the function
+ *
+ * Specifies the type of function which the `_new` method of a #HexBuffer
+ * interface implementation must comply with.
+ *
+ * Returns: a pointer to a valid implementaion of a #HexBuffer interface,
+ * pre-cast as the #HexBuffer type.
+ */
+
+typedef HexBuffer * (*HexBufferNewFunc) (GFile *file);
+
 struct _HexBufferInterface
 {
        GTypeInterface parent_iface;
@@ -133,6 +146,7 @@ gint64 hex_buffer_get_payload_size (HexBuffer *self);
 /* Common utility functions */
 
 gint64 hex_buffer_util_get_file_size (GFile *file);
+HexBuffer * hex_buffer_util_new (const char *plugin, GFile *file);
 
 G_END_DECLS
 #endif
diff --git a/src/hex-buffer-malloc.c b/src/hex-buffer-malloc.c
index d1599cf..46ad2f3 100644
--- a/src/hex-buffer-malloc.c
+++ b/src/hex-buffer-malloc.c
@@ -530,9 +530,10 @@ hex_buffer_malloc_class_init (HexBufferMallocClass *klass)
  *
  * Create a new #HexBufferMalloc object.
  *
- * Returns: a new #HexBufferMalloc object, or %NULL if the operation failed.
+ * Returns: a new #HexBufferMalloc object, automatically cast to a #HexBuffer
+ * type, or %NULL if the operation failed.
  */
-HexBufferMalloc *
+HexBuffer *
 hex_buffer_malloc_new (GFile *file)
 {
        HexBufferMalloc *self = g_object_new (HEX_TYPE_BUFFER_MALLOC, NULL);
@@ -544,7 +545,10 @@ hex_buffer_malloc_new (GFile *file)
                        g_clear_object (&self);
        }
 
-       return self;
+       if (self)
+               return HEX_BUFFER(self);
+       else
+               return NULL;
 }
 
 
diff --git a/src/hex-buffer-malloc.h b/src/hex-buffer-malloc.h
index 8814c26..93084c4 100644
--- a/src/hex-buffer-malloc.h
+++ b/src/hex-buffer-malloc.h
@@ -39,7 +39,8 @@ G_BEGIN_DECLS
 #define HEX_TYPE_BUFFER_MALLOC hex_buffer_malloc_get_type ()
 G_DECLARE_FINAL_TYPE (HexBufferMalloc, hex_buffer_malloc, HEX, BUFFER_MALLOC, GObject)
 
-HexBufferMalloc *hex_buffer_malloc_new (GFile *file);
+/* HexBufferNewFunc declaration */
+HexBuffer *hex_buffer_malloc_new (GFile *file);
 
 G_END_DECLS
 #endif
diff --git a/src/hex-buffer-mmap.c b/src/hex-buffer-mmap.c
index 5bd07f5..9526c16 100644
--- a/src/hex-buffer-mmap.c
+++ b/src/hex-buffer-mmap.c
@@ -853,9 +853,10 @@ hex_buffer_mmap_write_to_file_async (HexBuffer *buf,
  *
  * Create a new #HexBufferMmap object.
  *
- * Returns: a new #HexBufferMmap object, or %NULL if the operation failed.
+ * Returns: a new #HexBufferMmap object, automatically cast to a #HexBuffer
+ * type, or %NULL if the operation failed.
  */
-HexBufferMmap *
+HexBuffer *
 hex_buffer_mmap_new (GFile *file)
 {
        HexBufferMmap *self = g_object_new (HEX_TYPE_BUFFER_MMAP, NULL);
@@ -867,7 +868,10 @@ hex_buffer_mmap_new (GFile *file)
                        g_clear_object (&self);
        }
 
-       return self;
+       if (self)
+               return HEX_BUFFER(self);
+       else
+               return NULL;
 }
 
 /* INTERFACE IMPLEMENTATION FUNCTIONS */
diff --git a/src/hex-buffer-mmap.h b/src/hex-buffer-mmap.h
index 0afd4dd..af2a8cb 100644
--- a/src/hex-buffer-mmap.h
+++ b/src/hex-buffer-mmap.h
@@ -46,7 +46,8 @@ G_BEGIN_DECLS
 #define HEX_TYPE_BUFFER_MMAP hex_buffer_mmap_get_type ()
 G_DECLARE_FINAL_TYPE (HexBufferMmap, hex_buffer_mmap, HEX, BUFFER_MMAP, GObject)
 
-HexBufferMmap *hex_buffer_mmap_new (GFile *file);
+/* HexBufferNewFunc declaration */
+HexBuffer *hex_buffer_mmap_new (GFile *file);
 
 G_END_DECLS
 #endif
diff --git a/src/hex-document.c b/src/hex-document.c
index 2247f9c..8b7cb91 100644
--- a/src/hex-document.c
+++ b/src/hex-document.c
@@ -39,15 +39,6 @@
 
 #include <config.h>
 
-/* TODO - Allow for swappability. Compile-time only for now.
- * Keep this include below config.h, as it is (un)def'd there.
- */
-#ifdef BACKEND_MMAP
-#  include "hex-buffer-mmap.h"
-#else
-#  include "hex-buffer-malloc.h"
-#endif
-
 static void hex_document_real_changed   (HexDocument *doc,
                                                                                 gpointer change_data,
                                                                                 gboolean undoable);
@@ -343,10 +334,11 @@ static void
 hex_document_init (HexDocument *doc)
 {
 #ifdef BACKEND_MMAP
-       doc->buffer = HEX_BUFFER(hex_buffer_mmap_new (NULL));
+       doc->buffer = hex_buffer_util_new ("mmap", NULL);
 #else
-       doc->buffer = HEX_BUFFER(hex_buffer_malloc_new (NULL));
+       doc->buffer = hex_buffer_util_new (NULL, NULL);
 #endif
+
        doc->undo_max = DEFAULT_UNDO_DEPTH;
 }
 
diff --git a/src/main.c b/src/main.c
index 1b12c47..29cc354 100644
--- a/src/main.c
+++ b/src/main.c
@@ -119,12 +119,6 @@ activate (GtkApplication *app,
 
        gtk_window_set_application (window, app);
        gtk_window_present (window);
-
-#ifdef BACKEND_MMAP
-       g_debug ("Backend: `mmap`");
-#else
-       g_debug ("Backend: `malloc`");
-#endif
 }
 
 static void
diff --git a/src/meson.build b/src/meson.build
index edcddbf..2007f20 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,8 +1,7 @@
 libgtkhex_sources = [
   'gtkhex.c',
-  'hex-buffer-iface.c',              # TEST
-  'hex-buffer-mmap.c',        # TEST
-  'hex-buffer-malloc.c',      # TEST
+  'hex-buffer-iface.c',
+  'hex-buffer-malloc.c',
   'gtkhex-layout-manager.c',
   'gtkhex-paste-data.c',
   'hex-document.c'
@@ -11,18 +10,18 @@ libgtkhex_sources = [
 libgtkhex_headers = [
   'hex-document.h',
   'hex-buffer-iface.h',
-  'hex-buffer-malloc.h',
-  'hex-buffer-mmap.h',
   'gtkhex.h',
   'gtkhex-paste-data.h'
 ]
 
 libgtkhex_deps = [
+  gmodule_dep,
+  gio_dep,
   gtk_dep,
 ]
 
 libgtkhex_c_args = [
-  '-DG_LOG_DOMAIN="libgtkhex-4"'
+  '-DG_LOG_DOMAIN="gtkhex-4"'
 ]
 
 libgtkhex_link_args = cc.get_supported_link_arguments([
@@ -39,7 +38,7 @@ lib_osx_version = [osx_current, '@0@.@1@'.format(osx_current, libgtkhex_version_
 
 libgtkhex = library(
   'gtkhex-@0@'.format(libgtkhex_api_version),
-  libgtkhex_sources + libgtkhex_headers,
+  libgtkhex_sources,
   version: '0.@0@.@1@'.format(libgtkhex_version_major, libgtkhex_version_minor),
   darwin_versions: lib_osx_version,
   include_directories: ghex_root_dir,
@@ -56,38 +55,30 @@ libgtkhex_dep = declare_dependency(
   sources: libgtkhex_headers
 )
 
-# for i in `ls *.{c,h}`; do echo "  '${i}',"; done
+if mmap_backend
+    shared_module('hex-buffer-mmap',
+    sources: 'hex-buffer-mmap.c',
+    c_args: libgtkhex_c_args,
+    dependencies: libgtkhex_dep,
+    install_dir: ghex_plugindir,
+    install: true,
+  )
+endif
+
 ghex_sources = [
   'chartable.c',
-  'chartable.h',
   'common-ui.c',
-  'common-ui.h',
   'configuration.c',
-  'configuration.h',
   'converter.c',
-  'converter.h',
   'findreplace.c',
-  'findreplace.h',
   'ghex-application-window.c',
-  'ghex-application-window.h',
   'ghex-notebook-tab.c',
-  'ghex-notebook-tab.h',
   'gtkhex-paste-data.c',
-  'gtkhex-paste-data.h',
   'hex-dialog.c',
-  'hex-dialog.h',
   'main.c',
   'paste-special.c',
-  'paste-special.h',
   'preferences.c',
-  'preferences.h',
   'print.c',
-  'print.h'
-]
-
-ghex_deps = [
-  gio_dep,
-  gtk_dep
 ]
 
 src_conf = configuration_data()
@@ -121,13 +112,11 @@ ghex = executable(
   meson.project_name(),
   ghex_sources + res,
   include_directories: ghex_root_dir,
-  dependencies: ghex_deps + [libgtkhex_dep, ],
+  dependencies: libgtkhex_dep,
   c_args: ghex_c_args,
   install: true
 )
 
-
-
 if generate_gir
   gtkhex_gir_sources = [
     'gtkhex.c',
@@ -136,10 +125,6 @@ if generate_gir
     'hex-document.h',
     'hex-buffer-iface.c',
     'hex-buffer-iface.h',
-    'hex-buffer-malloc.c',
-    'hex-buffer-malloc.h',
-    'hex-buffer-mmap.c',
-    'hex-buffer-mmap.h',
   ]
 
   gtkhex_gir = gnome.generate_gir(libgtkhex,
@@ -148,7 +133,7 @@ if generate_gir
             namespace: 'Hex',
         symbol_prefix: 'hex',
             link_with: libgtkhex,
-         dependencies: libgtkhex_deps,
+         dependencies: libgtkhex_dep,
              includes: [ 'Gtk-4.0' ],
               install: true,
       install_dir_gir: ghex_girdir,


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]