[ghex: 1/5] HexBufferIface: Initial commit re: mmap backend




commit 5f6986c3d6fbde4cfbda5111ad37a8c0a84365ed
Author: Logan Rathbone <poprocks gmail com>
Date:   Wed Dec 15 21:25:07 2021 -0500

    HexBufferIface: Initial commit re: mmap backend
    
    - Enable with -Dexperimental-mmap=true
    
    nb: at this time, enabling it bakes it in and there is no way to disable
    it. It is HIGHLY EXPERIMENTAL and unstable! Strictly for people
    interested on hacking it at this time.

 ALPHA-INFO.txt                |  63 ++++++++
 PRE-ALPHA-INFO.txt            |  55 -------
 meson.build                   |  48 ++++--
 meson_options.txt             |   1 +
 src/ghex-application-window.c |  18 ---
 src/hex-buffer-iface.c        |  13 ++
 src/hex-buffer-iface.h        |   5 +-
 src/hex-buffer-malloc.c       |  24 +--
 src/hex-buffer-mmap.c         | 339 ++++++++++++++++++++++++++++++++----------
 src/hex-buffer-mmap.h         |   4 +-
 src/hex-document.c            |   7 +-
 src/meson.build               |   1 +
 12 files changed, 392 insertions(+), 186 deletions(-)
---
diff --git a/ALPHA-INFO.txt b/ALPHA-INFO.txt
new file mode 100644
index 0000000..ff006dd
--- /dev/null
+++ b/ALPHA-INFO.txt
@@ -0,0 +1,63 @@
+GHex 4 pre-alpha information
+============================
+
+GHex 4 is currently in pre-alpha state.  As it has been substantially reworked
+from GHex 3.x and, as of the time of writing, has only been recently merged to
+the `master` branch (December 2021), it will require some substantial testing
+and translation updates.
+
+Visibly to the end user, some new features include:
+
+ - More modern and up-to-date look and feel, consistent with the GNOME HIG.
+
+ - Tabbed interface.
+
+ - Dark Mode (by default, the GTK system default is used, but can be
+   overridden with a checkbox and a switch in the Preferences dialog).
+
+ - libadwaita colour compatibility; if you use the Adwaita theme and GNOME
+   apps that utilize libadwaita, GHex will match the colour scheme. libadwaita
+   is not a planned dependency for GHex at this time as many of its users are
+   GTK users who do not run GNOME, and I want to keep GHex's dependency
+   requirements to a minimum.
+
+ - Custom clipboard data, for less error-prone copying and pasting of binary
+   data; this will fall back to plaintext when unavailable.
+
+ - Copy and Paste Special dialogs, which allow you to, for instance, copy and
+   paste hex pairs from GHex into other applications, and from other
+   applications into GHex.
+
+I'll break up the known issues into two categories:  items I would consider to
+be blockers for a beta, blockers for stable release, and other known issues,
+which will be earmarked for priority after the first stable release of GHex 4.
+
+I have no more blockers for alpha. The first alpha will be released shortly
+after a minor cleanup.
+
+Blockers for beta:
+
+ - HexBufferIface: Implement asynchronous API for read/save operations.
+
+Blockers for stable release:
+
+ - Implementation of some missing keybindings.
+
+ - Improvement of some redraw issues, at least on X11 (if you experience this,
+   try just slightly resizing the window).
+
+ - The offsets column shows some spurious lines when a file is first loaded.
+   This can be cleared by clicking anywhere on the hex widget to clear it.
+
+Other known issues to be addressed in a future version:
+
+ - Memory-mapping:  I have started work on a memory-mapping backend for the
+   HexBuffer interface. This will at least be available as an experimental
+   compile-time feature by beta, but I cannot guarantee it will be enabled by
+   default or swappable on the fly for the first stable release of GHex 4.
+
+ - Users have requested other features as well. Some will be implemented, some
+   will not, and some will be taken under advisement.
+   See https://gitlab.gnome.org/GNOME/ghex/-/issues for some discussions.
+
+(Logan Rathbone <poprocks gmail com>)
diff --git a/meson.build b/meson.build
index 2c857fa..0821e66 100644
--- a/meson.build
+++ b/meson.build
@@ -46,6 +46,9 @@ i18n = import('i18n')
 
 cc = meson.get_compiler('c')
 
+# nb: this option will likely be removed in a future release
+experimental_mmap = get_option('experimental-mmap')
+
 # just to avoid manually punching it into the XML files as well as the source.
 shaded_box_max = 1000
 
@@ -64,16 +67,17 @@ config_h.set('GETTEXT_PACKAGE', 'PACKAGE_NAME')
 config_h.set('LOCALEDIR', 'PACKAGE_LOCALE_DIR')
 
 config_h.set('CONFIG_H_SHADED_BOX_MAX', shaded_box_max)
+config_h.set('EXPERIMENTAL_MMAP', experimental_mmap)
 
 config_h.set_quoted('LIBGTKHEX_RELEASE_STRING', 'gtkhex-@0@.0'.format(libghex_version_major))
 
 config_h.set('DEBUG', get_option('debug'))
 
-# Always enable; it's generated by Meson anyway.
-config_h.set('HAVE_CONFIG_H', true)
-
-config_h.set10('ENABLE_NLS', true) # Always enabled
+# i18n:  Always enabled
+config_h.set10('ENABLE_NLS', true) 
 
+# Check for required headers and functions
+# FIXME - unistd.h & friends shouldn't be required
 check_headers = [
   'dlfcn.h',
   'inttypes.h',
@@ -83,13 +87,11 @@ check_headers = [
   'string.h',
   'sys/stat.h',
   'sys/types.h',
-  'unistd.h'
+  'unistd.h',
 ]
 
 foreach h : check_headers
-  if cc.has_header(h)
-    config_h.set('HAVE_' + h.underscorify().to_upper(), 1)
-  endif
+  cc.has_header(h, required: true)
 endforeach
 
 check_functions = [
@@ -99,11 +101,36 @@ check_functions = [
 ]
 
 foreach f : check_functions
-  if cc.has_function(f)
-    config_h.set('HAVE_' + f.underscorify().to_upper(), 1)
+  if cc.has_function(f) == false
+    error('Required C function not found: @0@'.format(f))
   endif
 endforeach
 
+# mmap: Check for required headers and fcns
+if experimental_mmap
+  message('Checking dependencies for experimental mmap backend...')
+
+  check_headers_mmap = [
+    'sys/mman.h',
+    'errno.h',
+    ]
+  foreach h : check_headers_mmap
+    cc.has_header(h, required: true)
+  endforeach
+  
+  check_functions_mmap = [
+    'mmap',
+    'mremap',
+  ]
+  foreach f : check_functions_mmap
+    if cc.has_function(f) == false
+      error('Required C function not found: @0@'.format(f))
+    endif
+  endforeach
+
+  message('...DONE')
+endif
+
 gio_dep = dependency('gio-2.0', version: '>= 2.66.0')
 gtk_dep = dependency('gtk4', version: '>= 4.0.0')
 
@@ -133,4 +160,5 @@ summary({'prefix': ghex_prefix,
         }, section: 'Directories')
 
 summary({'Development build': get_option('development'),
+         'Experimental mmap backend': experimental_mmap,
         }, section: 'Configuration')
diff --git a/meson_options.txt b/meson_options.txt
index 5991092..d89e20f 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -1 +1,2 @@
 option('development', type: 'boolean', value: false, description: 'If this is a development build')
+option('experimental-mmap', type: 'boolean', value: false, description: 'Enable EXPERIMENTAL `mmap` 
buffering backend')
diff --git a/src/ghex-application-window.c b/src/ghex-application-window.c
index 5df0548..851007d 100644
--- a/src/ghex-application-window.c
+++ b/src/ghex-application-window.c
@@ -158,7 +158,6 @@ static gboolean assess_can_save (HexDocument *doc);
        for (i = gtk_notebook_get_n_pages(notebook) - 1; i >= 0; --i) {                 \
                GHexNotebookTab *tab;                                                                         
                  \
                GtkHex *gh;                                                                                   
                                  \
-               g_debug ("%s: Working on %d'th page", __func__, i);                                     \
                gh = GTK_HEX(gtk_notebook_get_nth_page (notebook, i));                          \
                g_return_if_fail (GTK_IS_HEX (gh));                                                           
          \
                tab = GHEX_NOTEBOOK_TAB(gtk_notebook_get_tab_label (notebook,           \
@@ -197,8 +196,6 @@ set_dark_mode_from_settings (GHexApplicationWindow *self)
 
        gtk_settings = gtk_settings_get_default ();
 
-       g_debug ("%s: def_dark_mode: %d", __func__, def_dark_mode);
-
        if (def_dark_mode == DARK_MODE_SYSTEM) {
                g_object_set (G_OBJECT(gtk_settings),
                                "gtk-application-prefer-dark-theme",
@@ -354,8 +351,6 @@ close_all_tabs (GHexApplicationWindow *self)
                GHexNotebookTab *tab;
                GtkHex *gh;
 
-               g_debug ("%s: Working on %d'th page", __func__, i);
-
                gh = GTK_HEX(gtk_notebook_get_nth_page (notebook, i));
                g_return_if_fail (GTK_IS_HEX (gh));
 
@@ -446,7 +441,6 @@ close_request_cb (GtkWindow *window,
 {
        GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(user_data);
 
-       g_debug ("%s: Window wants to be closed.",      __func__);
        check_close_window (self);
 
        return GDK_EVENT_STOP;
@@ -695,8 +689,6 @@ tab_close_cb (GHexNotebookTab *tab,
        GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(user_data);
        HexDocument *doc;
 
-       g_debug ("%s: start", __func__);
-
        doc = gtk_hex_get_document (self->gh);
        g_return_if_fail (HEX_IS_DOCUMENT (doc));
 
@@ -907,9 +899,6 @@ cursor_moved_cb (GtkHex *gh, gpointer user_data)
        /* If the cursor has been moved by a function call for a GtkHex that is
         * *not* in view, we're not interested. */
        if (self->gh != gh) {
-               g_debug("%s: Cursor has been moved for a GtkHex widget not in view: "
-                               "%p (currently in view == %p)",
-                               __func__, (void *)gh, (void *)self->gh);
                return;
        }
        else {
@@ -924,8 +913,6 @@ static void                                                                               
                                                  \
 ghex_application_window_set_show_ ##WIDGET (GHexApplicationWindow *self,       \
                gboolean show)                                                                                
                          \
 {                                                                                                            
                                          \
-       g_debug("%s: start - show: %d", __func__, show);                                                \
-                                                                                                             
                                          \
        if (show)                                                                                             
                                  \
        {                                                                                                     
                                          \
                if (! GTK_IS_WIDGET(self->WIDGET)) {                                                          
  \
@@ -955,7 +942,6 @@ static void                                                                               
                                                  \
 ghex_application_window_set_show_ ##WIDGET (GHexApplicationWindow *self,       \
                gboolean show)                                                                                
                          \
 {                                                                                                            
                                          \
-       g_debug("%s: start - show: %d", __func__, show);                                                \
        if (show) {                                                                                           
                                  \
                ghex_application_window_set_show_ ## OTHER1 (self, FALSE);                      \
                ghex_application_window_set_show_ ## OTHER2 (self, FALSE);                      \
@@ -981,7 +967,6 @@ ghex_application_window_set_can_save (GHexApplicationWindow *self,
        g_return_if_fail (GHEX_IS_APPLICATION_WINDOW (self));
 
        self->can_save = can_save;
-       g_debug("%s: start - can_save: %d", __func__, can_save);
 
        gtk_widget_action_set_enabled (GTK_WIDGET(self),
                        "ghex.save", can_save);
@@ -1926,8 +1911,6 @@ ghex_application_window_activate_tab (GHexApplicationWindow *self,
        g_return_if_fail (GTK_IS_NOTEBOOK (notebook));
 
        page_num = gtk_notebook_page_num (notebook, GTK_WIDGET(gh));
-       g_debug ("%s: got page_num: %d - setting notebook to that page.",
-                       __func__, page_num);
 
        gtk_notebook_set_current_page (notebook, page_num);
        gtk_widget_grab_focus (GTK_WIDGET(gh));
@@ -1987,7 +1970,6 @@ ghex_application_window_add_hex (GHexApplicationWindow *self,
 
        /* Generate a tab */
        tab = ghex_notebook_tab_new ();
-       g_debug ("%s: CREATED TAB -- %p", __func__, (void *)tab);
        ghex_notebook_tab_add_hex (GHEX_NOTEBOOK_TAB(tab), gh);
        g_signal_connect (tab, "closed",
                        G_CALLBACK(tab_close_cb), self);
diff --git a/src/hex-buffer-iface.c b/src/hex-buffer-iface.c
index 1dc5de9..1a5baf1 100644
--- a/src/hex-buffer-iface.c
+++ b/src/hex-buffer-iface.c
@@ -112,3 +112,16 @@ hex_buffer_get_payload_size (HexBuffer *self)
 
        return iface->get_payload_size (self);
 }
+
+/* Utility functions */
+
+size_t /* converted from guint64 at the backend anyway... */
+hex_buffer_util_get_file_size (GFile *file)
+{
+       GFileInfo *info;
+
+       info = g_file_query_info (file,
+                       G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, NULL);
+
+       return g_file_info_get_size (info);
+}
diff --git a/src/hex-buffer-iface.h b/src/hex-buffer-iface.h
index 14a4a4a..d61d495 100644
--- a/src/hex-buffer-iface.h
+++ b/src/hex-buffer-iface.h
@@ -55,7 +55,7 @@ struct _HexBufferInterface
        gpointer padding[12];
 };
 
-
+/* Interface functions */
 
 char * hex_buffer_get_data (HexBuffer *self,
                size_t offset,
@@ -80,6 +80,9 @@ gboolean hex_buffer_write_to_file (HexBuffer *self,
 
 size_t hex_buffer_get_payload_size (HexBuffer *self);
 
+/* Common utility functions */
+
+size_t hex_buffer_util_get_file_size (GFile *file);
 
 G_END_DECLS
 #endif
diff --git a/src/hex-buffer-malloc.c b/src/hex-buffer-malloc.c
index 96db6a2..9f023bb 100644
--- a/src/hex-buffer-malloc.c
+++ b/src/hex-buffer-malloc.c
@@ -28,32 +28,10 @@ G_DEFINE_TYPE_WITH_CODE (HexBufferMalloc, hex_buffer_malloc, G_TYPE_OBJECT,
 
 /* PRIVATE FUNCTIONS */
 
-static off_t
-get_file_size (GFile *file)
-{
-       GFileInfo *info;
-
-       info = g_file_query_info (file,
-                       G_FILE_ATTRIBUTE_STANDARD_SIZE, G_FILE_QUERY_INFO_NONE, NULL, NULL);
-
-       return g_file_info_get_size (info);
-       
-#if 0
-       static struct stat stats;
-
-       if (stat(file_name, &stats) == 0  &&  S_ISREG(stats.st_mode))
-       {
-               return stats.st_size;
-       }
-       else
-               return 0;
-#endif
-}
-
 static gboolean
 update_payload_size_from_file (HexBufferMalloc *self)
 {
-       self->payload_size = get_file_size (self->file);
+       self->payload_size = hex_buffer_util_get_file_size (self->file);
 
        if (!self->payload_size)
        {
diff --git a/src/hex-buffer-mmap.c b/src/hex-buffer-mmap.c
index b855d50..9c6944b 100644
--- a/src/hex-buffer-mmap.c
+++ b/src/hex-buffer-mmap.c
@@ -13,8 +13,6 @@
  * Copyright © 2021 Logan Rathbone
  */
 
-// WIP -- NOT WORKING CODE!!
-
 #include "hex-buffer-mmap.h"
 
 #define HEX_BUFFER_MMAP_ERROR hex_buffer_mmap_error_quark ()
@@ -24,19 +22,27 @@ hex_buffer_mmap_error_quark (void)
   return g_quark_from_static_string ("hex-buffer-mmap-error-quark");
 }
 
+static char *invalid_path_msg = N_("The file appears to have an invalid path.");
+
 struct _HexBufferMmap
 {
        GObject parent_instance;
 
+       GFile *file;
        GError *error;          /* no custom codes; use codes from errno */
        int last_errno;         /* cache in case we need to re-report errno error. */
 
-       char *data;
+       char *data;                     /* buffer for modification and info */
        size_t payload;
        size_t mapped;
        size_t gap;
-       int fd;
-       char *path;
+       char *tmpfile_path;     /* path to buffer tmpfile in mkstemp format */
+       int fd;                         /* file descriptor of tmpfile. */
+
+       char *clean;            /* unmodified content, mmap'ed */
+       size_t clean_bytes;
+       int clean_fd;
+
        size_t pagesize;        /* is only fetched once and cached. */
 };
 
@@ -45,23 +51,8 @@ static void hex_buffer_mmap_iface_init (HexBufferInterface *iface);
 G_DEFINE_TYPE_WITH_CODE (HexBufferMmap, hex_buffer_mmap, G_TYPE_OBJECT,
                G_IMPLEMENT_INTERFACE (HEX_TYPE_BUFFER, hex_buffer_mmap_iface_init))
 
-/* PRIVATE FUNCTIONS */
-
-// text.c
-
-
-
-
-
-
-// file.c
-
-
-
-
-
 
-// buffer.c
+/* PRIVATE FUNCTIONS */
 
 /* Helper wrapper for g_set_error and to cache errno */
 static void
@@ -76,6 +67,7 @@ set_error (HexBufferMmap *self, const char *blurb)
         * (eg, 'No such file or directory').
         */
                message = g_strdup_printf (_("%s: %s"), blurb, g_strerror (errno));
+               g_debug ("%s: %s", __func__, message);
        }
 
        g_set_error (&self->error,
@@ -102,6 +94,7 @@ static void
 hex_buffer_mmap_init (HexBufferMmap *self)
 {
        self->pagesize = getpagesize ();
+       self->fd = -1;
 }
 
 static void
@@ -123,10 +116,10 @@ hex_buffer_mmap_finalize (GObject *gobject)
        if (self->fd >= 0)
        {
                close (self->fd);
-               unlink (self->path);
+               unlink (self->tmpfile_path);
        }
 
-       g_free (self->path);
+       g_free (self->tmpfile_path);
 
        /* chain up */
        G_OBJECT_CLASS(hex_buffer_mmap_parent_class)->finalize (gobject);
@@ -141,34 +134,7 @@ hex_buffer_mmap_class_init (HexBufferMmapClass *klass)
        gobject_class->dispose = hex_buffer_mmap_dispose;
 }
 
-
-/* PUBLIC FUNCTIONS */
-
-// was: buffer_create
-HexBufferMmap *
-hex_buffer_mmap_new (char *path)
-{
-       HexBufferMmap *self = g_object_new (HEX_TYPE_BUFFER_MMAP, NULL);
-
-       if (path && *path)
-       {
-               self->path = g_strdup ("hexmmapbufXXXXXX");
-               errno = 0;
-               self->fd = mkstemp (self->path);
-
-               if (self->fd < 0) {
-                       set_error (self, _("Failed to open file"));
-               }
-       }
-       else
-       {
-               self->fd = -1;
-       }
-       return self;
-}
-
-// was: place_gap
-void   /* FIXME - make static if possible? */
+static void
 hex_buffer_mmap_place_gap (HexBufferMmap *self, size_t offset)
 {
        g_return_if_fail (HEX_IS_BUFFER_MMAP (self));
@@ -193,10 +159,7 @@ hex_buffer_mmap_place_gap (HexBufferMmap *self, size_t offset)
                memset (self->data + self->gap, ' ', gapsize);
 }
 
-
-
-// was: resize
-void   /* FIXME - make static if possible? */
+static void
 hex_buffer_mmap_resize (HexBufferMmap *self, size_t payload_bytes)
 {
        void *p;
@@ -224,7 +187,7 @@ hex_buffer_mmap_resize (HexBufferMmap *self, size_t payload_bytes)
                {
                        char *errmsg = g_strdup_printf (
                                        _("Could not adjust %s from %lu to %lu bytes"),
-                                               self->path, (long)self->mapped, (long)map_bytes);
+                                       self->tmpfile_path, (long)self->mapped, (long)map_bytes);
 
                        set_error (self, errmsg);
                        g_free (errmsg);
@@ -297,14 +260,11 @@ done:
        if (offset + bytes > self->payload)             \
                bytes = self->payload - offset;         \
 
-// was: buffer_raw             - gets raw ptr to data - does not copy
-// can't pass a pointer to a NULL cp with this, so I added a sanity check
 size_t
 hex_buffer_mmap_raw (HexBufferMmap *self,
                char **out, size_t offset, size_t bytes)
 {
        g_assert (HEX_IS_BUFFER_MMAP (self));
-       g_assert (*out != NULL);
        
        ADJUST_OFFSET_AND_BYTES
 
@@ -323,13 +283,8 @@ hex_buffer_mmap_raw (HexBufferMmap *self,
        return bytes;
 }
 
-
-
-
-// was: buffer_get     - gets & copies data - need to allocate 1st!!!
-//
 size_t
-hex_buffer_mmap_get (HexBufferMmap *self,
+hex_buffer_mmap_copy_data (HexBufferMmap *self,
                void *out, size_t offset, size_t bytes)
 {
        size_t left;
@@ -362,7 +317,6 @@ hex_buffer_mmap_get (HexBufferMmap *self,
        return bytes;
 }
 
-// was: buffer_delete
 size_t
 hex_buffer_mmap_delete (HexBufferMmap *self,
                     size_t offset, size_t bytes)
@@ -378,9 +332,7 @@ hex_buffer_mmap_delete (HexBufferMmap *self,
 }
 #undef ADJUST_OFFSET_AND_BYTES
 
-// was: buffer_insert
-//
-size_t
+static size_t
 hex_buffer_mmap_insert (HexBufferMmap *self,
                const void *in, size_t offset, size_t bytes)
 {
@@ -407,8 +359,6 @@ hex_buffer_mmap_insert (HexBufferMmap *self,
        return bytes;
 }
 
-// was: buffer_move
-//
 size_t
 hex_buffer_mmap_move (HexBufferMmap *to,
                size_t to_offset,
@@ -424,8 +374,6 @@ hex_buffer_mmap_move (HexBufferMmap *to,
        return hex_buffer_mmap_delete (from, from_offset, bytes);
 }
 
-// was: buffer_snap
-//
 void 
 hex_buffer_mmap_snap (HexBufferMmap *self)
 {
@@ -440,13 +388,252 @@ hex_buffer_mmap_snap (HexBufferMmap *self)
        }
 }
 
+char * hex_buffer_mmap_get_data (HexBuffer *buf,
+               size_t offset,
+               size_t len)
+{
+       HexBufferMmap *self = HEX_BUFFER_MMAP (buf);
+       char *data;
+
+       data = g_malloc (len);
+       hex_buffer_mmap_copy_data (self, data, offset, len);
+
+       return data;
+}
+
+char hex_buffer_mmap_get_byte (HexBuffer *buf,
+               size_t offset)
+{
+       HexBufferMmap *self = HEX_BUFFER_MMAP (buf);
+       char *cp;
+       char c;
+
+       cp = hex_buffer_mmap_get_data (buf, offset, 1);
+       c = *cp;
+       
+       return c;
+}
+
+static size_t
+hex_buffer_mmap_get_payload_size (HexBuffer *buf)
+{
+       HexBufferMmap *self = HEX_BUFFER_MMAP (buf);
+
+       return self->payload;
+}
+
+static gboolean
+hex_buffer_mmap_set_file (HexBuffer *buf, GFile *file)
+{
+       HexBufferMmap *self = HEX_BUFFER_MMAP (buf);
+       const char *file_path;
+
+       g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+       file_path = g_file_peek_path (file);
+       if (! file_path)
+       {
+               set_error (self, _(invalid_path_msg));
+               return FALSE;
+       }
+       self->file = file;
+
+       return TRUE;
+}
+
+/* helper */
+static int
+create_fd_from_path (HexBufferMmap *self, const char *path)
+{
+       int fd = -1;
+       struct stat statbuf;
+
+       errno = 0;
+
+       if (stat (path, &statbuf))
+       {
+               if (errno != ENOENT) {
+                       set_error (self,
+                               _("Unable to retrieve file or directory information"));
+                       return -1;
+               }
+
+               errno = 0;
+               fd = open(path, O_CREAT|O_TRUNC|O_RDWR,
+                               S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
+
+               if (fd < 0) {
+                       set_error (self, _("Unable to create file"));
+                       return -1;
+               }
+       } 
+       else
+       {
+               /* FIXME - this is probably overkill - hex editor users may wish to
+                * open a 'non-regular' file.
+                */
+               if (!S_ISREG(statbuf.st_mode)) {
+                       set_error (self, _("Not a regular file"));
+                       return -1;
+               }
+
+               fd = open (path, O_RDWR);
+
+               if (fd < 0) {
+                       errno = 0;
+                       fd = open (path, O_RDONLY);
+                       if (fd < 0) {
+                               set_error (self, _("Unable to open file for reading"));
+                               return -1;
+                       }
+               }
+       }
+       return fd;
+}
+
+static gboolean
+create_buffer (HexBufferMmap *self)
+{
+       self->tmpfile_path = g_strdup ("hexmmapbufXXXXXX");
+       errno = 0;
+       self->fd = mkstemp (self->tmpfile_path);
+
+       if (self->fd < 0) {
+               set_error (self, _("Failed to open temporary file."));
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static gboolean
+hex_buffer_mmap_read (HexBuffer *buf)
+{
+       HexBufferMmap *self = HEX_BUFFER_MMAP (buf);
+       void *p;
+       size_t bytes = 0;
+       size_t pages;
+       const char *file_path;
+       int tmp_clean_fd;
+
+       g_return_val_if_fail (G_IS_FILE (self->file), FALSE);
+
+       file_path = g_file_peek_path (self->file);
+       if (! file_path)
+       {
+               set_error (self, _(invalid_path_msg));
+               return FALSE;
+       }
+
+       bytes = hex_buffer_util_get_file_size (self->file);
+       pages = (bytes + self->pagesize - 1) / self->pagesize;
+
+       /* Set up a clean buffer (read-only memory mapped version of O.G. file)
+        */
+       if (self->clean)
+               munmap (self->clean, self->clean_bytes);
+
+       self->clean_bytes = bytes;
+       self->clean = NULL;
+
+       if (!pages)
+               return FALSE;
+
+       tmp_clean_fd = create_fd_from_path (self, file_path);
+       if (tmp_clean_fd < 0)
+               return FALSE;
+
+       self->clean_fd = tmp_clean_fd;
+
+       p = mmap (0, pages * self->pagesize, PROT_READ, MAP_SHARED,
+                       self->clean_fd, 0);
+
+       if (p == MAP_FAILED)
+               return FALSE;
+
+       self->clean = p;
+
+       /* Create dirty buffer for writing etc. */
+       create_buffer (self);
+
+       /* FIXME/TODO - sanity check against # of bytes read? */
+       hex_buffer_mmap_insert (self, self->clean, 0, self->clean_bytes);
+
+       return TRUE;
+}
+
+static gboolean hex_buffer_mmap_set_data (HexBuffer *buf,
+               size_t offset,
+               size_t len,
+               size_t rep_len,
+               char *data)
+{
+       HexBufferMmap *self = HEX_BUFFER_MMAP (buf);
+
+       if (offset > self->payload)
+       {
+               g_debug ("%s: offset greater than payload size; returning.", __func__);
+               return FALSE;
+       }
+
+       hex_buffer_mmap_insert (self, data, offset, len);
+       hex_buffer_mmap_delete (self, offset + len, rep_len);
+
+       return TRUE;
+}
+
+static gboolean hex_buffer_mmap_write_to_file (HexBuffer *buf,
+               GFile *file)
+{
+       HexBufferMmap *self = HEX_BUFFER_MMAP (buf);
+       char *raw;
+       gboolean retval;
+
+       g_return_val_if_fail (G_IS_FILE (self->file), FALSE);
+
+       hex_buffer_mmap_raw (self, &raw, 0, self->payload);
+
+       retval = g_file_replace_contents (self->file,
+               /* const char* contents, */                     raw,    
+               /* gsize length, */                                     self->payload,
+               /* const char* etag, */                         NULL,
+               /* gboolean make_backup, */                     FALSE,  /* FIXME - make optional? */
+               /* GFileCreateFlags flags, */           G_FILE_CREATE_NONE,
+               /* char** new_etag, */                          NULL,
+               /* GCancellable* cancellable, */        NULL,   /* FIXME */
+               /* GError** error */                            &self->error);
+
+       return retval;
+}
+
+/* PUBLIC FUNCTIONS */
+
+HexBufferMmap *
+hex_buffer_mmap_new (GFile *file)
+{
+       HexBufferMmap *self = g_object_new (HEX_TYPE_BUFFER_MMAP, NULL);
+
+       if (file)
+       {
+               /* If a path is provided but it can't be set, nullify the object */
+               if (! hex_buffer_mmap_set_file (HEX_BUFFER(self), file))
+                       g_clear_object (&self);
+       }
+
+       return self;
+}
+
 /* INTERFACE IMPLEMENTATION FUNCTIONS */
 
 static void
 hex_buffer_mmap_iface_init (HexBufferInterface *iface)
 {
-#if 0
-       iface->blah = blah_blah_function;
-#endif
+       iface->get_data = hex_buffer_mmap_get_data;
+       iface->get_byte = hex_buffer_mmap_get_byte;
+       iface->set_data = hex_buffer_mmap_set_data;
+       iface->set_file = hex_buffer_mmap_set_file;
+       iface->read = hex_buffer_mmap_read;
+       iface->write_to_file = hex_buffer_mmap_write_to_file;
+       iface->get_payload_size = hex_buffer_mmap_get_payload_size;
 }
 
diff --git a/src/hex-buffer-mmap.h b/src/hex-buffer-mmap.h
index f517cbf..535fd31 100644
--- a/src/hex-buffer-mmap.h
+++ b/src/hex-buffer-mmap.h
@@ -24,6 +24,8 @@
 
 #include <errno.h>
 #include <unistd.h>
+#include <fcntl.h>
+#include <sys/stat.h>
 #include <sys/mman.h>
 
 G_BEGIN_DECLS
@@ -31,7 +33,7 @@ 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 (char *path);
+HexBufferMmap *hex_buffer_mmap_new (GFile *file);
 
 G_END_DECLS
 #endif
diff --git a/src/hex-document.c b/src/hex-document.c
index 78ab7ee..b824494 100644
--- a/src/hex-document.c
+++ b/src/hex-document.c
@@ -38,7 +38,8 @@
 /* FIXME / TODO - Allow for swappability. Hardcoding for now for testing
  * purposes.
  */
-#include "hex-buffer-malloc.h"
+//#include "hex-buffer-malloc.h"
+#include "hex-buffer-mmap.h"
 
 #include <stdio.h>
 #include <unistd.h>
@@ -311,7 +312,9 @@ hex_document_class_init (HexDocumentClass *klass)
 static void
 hex_document_init (HexDocument *doc)
 {
-       doc->buffer = HEX_BUFFER(hex_buffer_malloc_new (NULL));
+//     doc->buffer = HEX_BUFFER(hex_buffer_malloc_new (NULL));
+       // TEST
+       doc->buffer = HEX_BUFFER(hex_buffer_mmap_new (NULL));
        doc->undo_max = DEFAULT_UNDO_DEPTH;
 }
 
diff --git a/src/meson.build b/src/meson.build
index e2c95a0..5132fa1 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -2,6 +2,7 @@ libghex_sources = [
   'gtkhex.c',
   'hex-buffer-iface.c',              # TEST
   'hex-buffer-malloc.c',      # TEST
+  'hex-buffer-mmap.c',        # TEST
   'gtkhex-layout-manager.c',
   'gtkhex-paste-data.c',
   'hex-document.c'


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