[ghex] hex-doc rewrite with iface: INITIAL COMMIT.



commit 769b82659d6fd3ba3946084bf75b54d1946ce2d7
Author: Logan Rathbone <poprocks gmail com>
Date:   Mon Dec 13 22:41:32 2021 -0500

    hex-doc rewrite with iface: INITIAL COMMIT.
    
    This is stage one in my efforts to implement a memory-mapping backend
    for ghex.
    
    In this commit, we streamline HexDocument considerably and add a new
    interface called HexBuffer. The legacy `malloc` gap buffer system has
    been implemented as HexBufferMalloc. This interface can be swapped out
    for another without breaking ABI in gtkhex, so this will allow for a new
    backend to be written while keeping this one as well -- in future, it
    could be used for backwards compatibility, or for smaller files.
    
    I have started another backend but it is not yet working code, so it is
    committed so it follows along, but is disabled and likely won't compile
    at this time, let alone do anything.
    
    I'm sure some further cleanups will happen as well as some further
    bugfixes as I notice them. Since this required some substantial
    refactoring of the backend, some bugs I have not caught may have slipped
    through.
    
    Please test!

 src/common-ui.c               |   8 +-
 src/converter.c               |   6 +-
 src/findreplace.c             |  43 +--
 src/ghex-application-window.c | 114 +++-----
 src/ghex-notebook-tab.c       |  25 +-
 src/gtkhex-paste-data.c       |   2 +-
 src/gtkhex.c                  |  96 +++---
 src/hex-buffer-iface.c        | 114 ++++++++
 src/hex-buffer-iface.h        |  85 ++++++
 src/hex-buffer-malloc.c       | 389 +++++++++++++++++++++++++
 src/hex-buffer-malloc.h       |  36 +++
 src/hex-buffer-mmap.c         | 452 +++++++++++++++++++++++++++++
 src/hex-buffer-mmap.h         |  37 +++
 src/hex-document.c            | 661 ++++++++++++++----------------------------
 src/hex-document.h            |  57 ++--
 src/main.c                    |   8 +-
 src/meson.build               |   9 +-
 src/print.c                   |  27 +-
 18 files changed, 1511 insertions(+), 658 deletions(-)
---
diff --git a/src/common-ui.c b/src/common-ui.c
index 41146b3..0931fc1 100644
--- a/src/common-ui.c
+++ b/src/common-ui.c
@@ -87,6 +87,8 @@ pango_font_description_to_css (PangoFontDescription *desc,
                        case PANGO_VARIANT_SMALL_CAPS:
                                g_string_append (s, "font-variant: small-caps; ");
                                break;
+                       default:
+                               break;
                }
        }
        if (set & PANGO_FONT_MASK_WEIGHT)
@@ -302,7 +304,6 @@ common_print (GtkWindow *parent, GtkHex *gh, gboolean preview)
        HexDocument *doc;
        GtkPrintOperationResult result;
        GError *error = NULL;
-       char *gtk_file_name;
        char *basename;
 
        g_return_if_fail (GTK_IS_HEX (gh));
@@ -310,9 +311,7 @@ common_print (GtkWindow *parent, GtkHex *gh, gboolean preview)
        doc = gtk_hex_get_document (gh);
        g_return_if_fail (HEX_IS_DOCUMENT (doc));
 
-       gtk_file_name = g_filename_to_utf8 (hex_document_get_file_name (doc),
-                       -1, NULL, NULL, NULL);
-       basename = g_filename_display_basename (gtk_file_name);
+       basename = g_file_get_basename (hex_document_get_file (doc));
 
        job = ghex_print_job_info_new (doc, gtk_hex_get_group_type (gh));
        job->master = gtk_print_operation_new ();
@@ -356,7 +355,6 @@ common_print (GtkWindow *parent, GtkHex *gh, gboolean preview)
        }
        ghex_print_job_info_destroy (job);
        g_free (basename);
-       g_free (gtk_file_name);
 }
 
 static void
diff --git a/src/converter.c b/src/converter.c
index db12797..69060dd 100644
--- a/src/converter.c
+++ b/src/converter.c
@@ -253,12 +253,12 @@ get_cursor_val_cb(GtkButton *button, Converter *conv)
        guint val, start;
        guint group_type;
        HexDocument *doc;
-       int file_size;
+       size_t payload;
 
        g_return_if_fail (GTK_IS_HEX(conv->gh));
 
        doc = gtk_hex_get_document (conv->gh);
-       file_size = hex_document_get_file_size (doc);
+       payload = hex_buffer_get_payload_size (hex_document_get_buffer (doc));
        group_type = gtk_hex_get_group_type (conv->gh);
        start = gtk_hex_get_cursor (conv->gh);
        start = start - start % group_type;
@@ -269,7 +269,7 @@ get_cursor_val_cb(GtkButton *button, Converter *conv)
                val |= gtk_hex_get_byte(conv->gh, start);
                start++;
        } while((start % group_type != 0) &&
-                       (start < file_size) );
+                       (start < payload) );
 
        set_values(conv, val);
 }
diff --git a/src/findreplace.c b/src/findreplace.c
index 217465b..44007ad 100644
--- a/src/findreplace.c
+++ b/src/findreplace.c
@@ -109,14 +109,16 @@ static void replace_one_cb (GtkButton *button, gpointer user_data);
 static void replace_all_cb (GtkButton *button, gpointer user_data);
 static void replace_clear_cb (GtkButton *button, gpointer user_data);
 static void goto_byte_cb (GtkButton *button, gpointer user_data);
-static gint get_search_string (HexDocument *doc, gchar **str);
+static size_t get_search_string (HexDocument *doc, gchar **str);
 
 
 static GtkWidget *
 create_hex_view (HexDocument *doc)
 {
-       /* Not going to bother reffing, since add_view does this internally. */
-    GtkWidget *gh = hex_document_add_view (doc);
+       GtkWidget *gh;
+
+       gh = gtk_hex_new (doc);
+       g_object_ref (gh);
 
        gtk_widget_set_hexpand (gh, TRUE);
        gtk_hex_set_group_type (GTK_HEX(gh), def_group_type);
@@ -127,13 +129,13 @@ create_hex_view (HexDocument *doc)
     return gh;
 }
 
-static int
+static size_t
 get_search_string (HexDocument *doc, char **str)
 {
-       int size = hex_document_get_file_size (doc);
+       size_t size = hex_buffer_get_payload_size (hex_document_get_buffer (doc));
        
        if (size > 0)
-               *str = hex_document_get_data(doc, 0, size);
+               *str = hex_buffer_get_data (hex_document_get_buffer (doc), 0, size);
        else
                *str = NULL;
 
@@ -184,7 +186,8 @@ find_common (FindDialog *self, enum FindDirection direction,
        GtkWindow *parent;
        HexDocument *doc;
        int cursor_pos;
-       int offset, str_len;
+       int str_len;
+       size_t offset;
        char *str;
        static gboolean found = FALSE;
        
@@ -309,7 +312,7 @@ goto_byte_cb (GtkButton *button, gpointer user_data)
        int is_relative = 0;
        gboolean is_hex;
        const gchar *byte_str;
-       int file_size;
+       size_t payload;
        
        (void)button;   /* unused */
 
@@ -324,7 +327,7 @@ goto_byte_cb (GtkButton *button, gpointer user_data)
 
        doc = gtk_hex_get_document(priv->gh);
        cursor_pos = gtk_hex_get_cursor(priv->gh);
-       file_size = hex_document_get_file_size (doc);
+       payload = hex_buffer_get_payload_size (hex_document_get_buffer (doc));
 
        entry = GTK_ENTRY(self->int_entry);
        buffer = gtk_entry_get_buffer (entry);
@@ -375,7 +378,7 @@ goto_byte_cb (GtkButton *button, gpointer user_data)
                        }
                        byte = byte * is_relative + cursor_pos;
                }
-               if (byte >= file_size) {
+               if (byte >= payload) {
                        display_error_dialog(parent,
                                                                 _("Can not position cursor beyond the "
                                                                   "end of file."));
@@ -405,8 +408,9 @@ replace_one_cb (GtkButton *button, gpointer user_data)
        HexDocument *doc;
        int cursor_pos;
        char *find_str = NULL, *rep_str = NULL;
-       int find_len, rep_len, offset;
-       int file_size;
+       int find_len, rep_len;
+       size_t offset;
+       size_t payload;
 
        (void)button;   /* unused */
        g_return_if_fail (REPLACE_IS_DIALOG(self));
@@ -424,7 +428,7 @@ replace_one_cb (GtkButton *button, gpointer user_data)
 
        doc = gtk_hex_get_document (priv->gh);
        cursor_pos = gtk_hex_get_cursor (priv->gh);
-       file_size = hex_document_get_file_size (doc);
+       payload = hex_buffer_get_payload_size (hex_document_get_buffer (doc));
        
        if ((find_len = get_search_string(f_priv->f_doc, &find_str)) == 0)
        {
@@ -433,7 +437,7 @@ replace_one_cb (GtkButton *button, gpointer user_data)
        }
        rep_len = get_search_string(self->r_doc, &rep_str);
        
-       if (find_len > file_size - cursor_pos)
+       if (find_len > payload - cursor_pos)
                goto clean_up;
        
        if (hex_document_compare_data(doc, find_str, cursor_pos, find_len) == 0)
@@ -470,8 +474,9 @@ replace_all_cb (GtkButton *button, gpointer user_data)
        HexDocument *doc;
        int cursor_pos;
        char *find_str = NULL, *rep_str = NULL;
-       int find_len, rep_len, offset, count;
-       int file_size;
+       size_t find_len, rep_len, count;
+       size_t offset;
+       size_t payload;
 
        (void)button;   /* unused */
        g_return_if_fail (REPLACE_IS_DIALOG (self));
@@ -488,7 +493,7 @@ replace_all_cb (GtkButton *button, gpointer user_data)
 
        doc = gtk_hex_get_document (priv->gh);
        cursor_pos = gtk_hex_get_cursor (priv->gh);
-       file_size = hex_document_get_file_size (doc);
+       payload = hex_buffer_get_payload_size (hex_document_get_buffer (doc));
 
        if ((find_len = get_search_string(f_priv->f_doc, &find_str)) == 0)
        {
@@ -497,7 +502,7 @@ replace_all_cb (GtkButton *button, gpointer user_data)
        }
        rep_len = get_search_string(self->r_doc, &rep_str);
 
-       if (find_len > file_size - cursor_pos)
+       if (find_len > payload - (unsigned)cursor_pos)
                goto clean_up;
        
        count = 0;
@@ -510,7 +515,7 @@ replace_all_cb (GtkButton *button, gpointer user_data)
                count++;
        }
        
-       gtk_hex_set_cursor(priv->gh, MIN(offset, file_size));  
+       gtk_hex_set_cursor(priv->gh, MIN(offset, payload));  
 
        if (count == 0) {
                display_info_dialog (parent, _("No occurrences were found."));
diff --git a/src/ghex-application-window.c b/src/ghex-application-window.c
index 2b9abc8..3f7cd8b 100644
--- a/src/ghex-application-window.c
+++ b/src/ghex-application-window.c
@@ -479,12 +479,15 @@ close_doc_confirmation_dialog (GHexApplicationWindow *self)
 {
        GtkWidget *dialog;
        HexDocument *doc;
+       char *basename = NULL;
 
        g_return_if_fail (GTK_IS_HEX (self->gh));
 
        doc = gtk_hex_get_document (self->gh);
        g_return_if_fail (HEX_IS_DOCUMENT (doc));
 
+       basename = g_file_get_basename (hex_document_get_file (doc));
+
        dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW(self),
                        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                        GTK_MESSAGE_QUESTION,
@@ -493,7 +496,7 @@ close_doc_confirmation_dialog (GHexApplicationWindow *self)
                         * edited. */
                        _("<big><b>%s has been edited since opening.</b></big>\n\n"
                           "Would you like to save your changes?"),
-                       hex_document_get_basename (doc));
+                       basename);
 
        gtk_dialog_add_buttons (GTK_DIALOG(dialog),
                        _("_Save Changes"),             GTK_RESPONSE_ACCEPT,
@@ -505,6 +508,7 @@ close_doc_confirmation_dialog (GHexApplicationWindow *self)
                        G_CALLBACK(close_doc_response_cb), self);
 
        gtk_widget_show (dialog);
+       g_free (basename);
 }
 
 /* FIXME / TODO - I could see this function being useful, but right now it is
@@ -626,9 +630,10 @@ static gboolean
 assess_can_save (HexDocument *doc)
 {
        gboolean can_save = FALSE;
+       GFile *file = hex_document_get_file (doc);
 
        /* Can't save if we have a new document that is still untitled. */
-       if (hex_document_get_file_name (doc))
+       if (G_IS_FILE (file)  &&  g_file_peek_path (file))
                can_save = hex_document_has_changed (doc);
 
        return can_save;
@@ -965,9 +970,6 @@ save_as_response_cb (GtkNativeDialog *dialog,
        GtkFileChooser *chooser = GTK_FILE_CHOOSER(dialog);
        HexDocument *doc;
        GFile *gfile;
-       char *new_file_path;
-       FILE *file;
-       gchar *gtk_file_name;
 
        /* If user doesn't click Save, just bail out now. */
        if (resp != GTK_RESPONSE_ACCEPT)
@@ -976,53 +978,28 @@ save_as_response_cb (GtkNativeDialog *dialog,
        /* Fetch doc. No need for sanity checks as this is just a helper. */
        doc = gtk_hex_get_document (self->gh);
 
-       /* Get filename. */
        gfile = gtk_file_chooser_get_file (chooser);
-       new_file_path = g_file_get_path (gfile);
-       g_clear_object (&gfile);
-
-       g_debug("%s: GONNA OPEN FILE FOR WRITING: %s",
-                       __func__, new_file_path);
 
-       file = fopen(new_file_path, "wb");
-
-       /* Sanity check */
-       if (file == NULL) {
-               g_debug ("%s: Error dialog not implemented! Can't open file rw!",
-                               __func__);
+       if (! hex_document_write_to_file (doc, gfile))
+       {
+               display_error_dialog (GTK_WINDOW(self),
+                               _("There was an error saving the file to the path specified."
+                                       "\n\n"
+                                       "You may not have the required permissions."));
                goto end;
        }
 
-       if (hex_document_write_to_file (doc, file))
+       if (hex_document_set_file (doc, gfile))
        {
-               gboolean change_ok;
-               char *gtk_file_name;
-
-               change_ok = hex_document_change_file_name (doc, new_file_path);
-
-               if (! change_ok) {
-                       g_error ("%s: There was a fatal error changing the name of the "
-                                       "file path. This should NOT happen and may be indicative "
-                                       "of a bug or programer error. Please file a bug report.",
-                                       __func__);
-               }
-               gtk_file_name = g_filename_to_utf8 (hex_document_get_file_name (doc),
-                               -1, NULL, NULL, NULL);
-
-               g_free(gtk_file_name);
+               hex_document_read (doc);
        }
        else
        {
-               display_error_dialog (GTK_WINDOW(self),
-                               _("There was an error saving the file to the path specified."
-                                       "\n\n"
-                                       "You may not have the required permissions."));
+               g_warning ("%s: error resetting file...", __func__);
        }
-       fclose(file);
 
 end:
-       g_debug("%s: END.", __func__);
-       g_object_unref (dialog);
+       gtk_native_dialog_destroy (GTK_NATIVE_DIALOG (dialog));
 }
 
 static void
@@ -1034,15 +1011,12 @@ save_as (GtkWidget *widget,
        GtkFileChooserNative *file_sel;
        GtkResponseType resp;
        HexDocument *doc;
-       GFile *existing_file;
 
        g_return_if_fail (GTK_IS_HEX (self->gh));
 
        doc = gtk_hex_get_document (self->gh);
        g_return_if_fail (HEX_IS_DOCUMENT (doc));
 
-       existing_file = g_file_new_for_path (hex_document_get_file_name (doc));
-
        file_sel =
                gtk_file_chooser_native_new (_("Select a file to save buffer as"),
                                GTK_WINDOW(self),
@@ -1051,16 +1025,14 @@ save_as (GtkWidget *widget,
                                NULL);  /* const char *cancel_label }                                   */
 
        /* Default suggested file == existing file. */
-       gtk_file_chooser_set_file (GTK_FILE_CHOOSER(file_sel), existing_file,
+       gtk_file_chooser_set_file (GTK_FILE_CHOOSER(file_sel),
+                       hex_document_get_file (doc),
                        NULL);  /* GError **error */
 
        g_signal_connect (file_sel, "response",
                        G_CALLBACK(save_as_response_cb), self);
 
        gtk_native_dialog_show (GTK_NATIVE_DIALOG(file_sel));
-
-       /* Clear the GFile ptr which is no longer necessary. */
-       g_clear_object (&existing_file);
 }
 
 
@@ -1071,19 +1043,14 @@ revert_response_cb (GtkDialog *dialog,
 {
        GHexApplicationWindow *self = GHEX_APPLICATION_WINDOW(user_data);
        HexDocument *doc;
-       char *gtk_file_name;
 
        if (response_id != GTK_RESPONSE_ACCEPT)
                goto end;
 
        doc = gtk_hex_get_document (self->gh);
-       gtk_file_name = g_filename_to_utf8 (hex_document_get_file_name (doc),
-                       -1, NULL, NULL, NULL);
 
        hex_document_read (doc);
 
-       g_free(gtk_file_name);
-
 end:
        gtk_window_destroy (GTK_WINDOW(dialog));
 }
@@ -1097,6 +1064,7 @@ revert (GtkWidget *widget,
        HexDocument *doc;
        GtkWidget *dialog;
        gint reply;
+       char *basename = NULL;
        
        g_return_if_fail (GTK_IS_HEX (self->gh));
 
@@ -1107,6 +1075,8 @@ revert (GtkWidget *widget,
         * to the user at all if there is nothing to revert. */
        g_return_if_fail (hex_document_has_changed (doc));
 
+       basename = g_file_get_basename (hex_document_get_file (doc));
+
        dialog = gtk_message_dialog_new_with_markup (GTK_WINDOW(self),
                        GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
                        GTK_MESSAGE_QUESTION,
@@ -1116,7 +1086,7 @@ revert (GtkWidget *widget,
                        _("<big><b>Are you sure you want to revert %s?</b></big>\n\n"
                        "Your changes will be lost.\n\n"
                        "This action cannot be undone."),
-                       hex_document_get_basename (doc));
+                       basename);
 
        gtk_dialog_add_buttons (GTK_DIALOG(dialog),
                        _("_Revert"), GTK_RESPONSE_ACCEPT,
@@ -1128,6 +1098,7 @@ revert (GtkWidget *widget,
                        G_CALLBACK (revert_response_cb), self);
 
        gtk_widget_show (dialog);
+       g_free (basename);
 }
                        
 static void
@@ -1180,33 +1151,16 @@ new_file (GtkWidget *widget,
 static GtkHex *
 new_gh_from_gfile (GFile *file)
 {
-       GFile *my_file;
-       char *path;
-       GFileInfo *info;
-       GError *error = NULL;
        HexDocument *doc;
        GtkHex *gh;
 
-       my_file = g_object_ref (file);
-       path = g_file_get_path (my_file);
-       info = g_file_query_info (my_file,
-                       G_FILE_ATTRIBUTE_STANDARD_DISPLAY_NAME,
-                       G_FILE_QUERY_INFO_NONE,                 /* GFileQueryInfoFlags flags */
-                       NULL,                                                   /* GCancellable *cancellable 
*/
-                       &error);
-
-       g_debug("%s: path acc. to GFile: %s",
-                       __func__, path);
-
-       doc = hex_document_new_from_file (path);
+       doc = hex_document_new_from_file (file);
+       /* FIXME - just return NULL and handle error in user-friendly manner? */
+       g_return_val_if_fail (HEX_IS_DOCUMENT (doc), NULL);
+               
        gh = GTK_HEX(gtk_hex_new (doc));
-
        g_return_val_if_fail (GTK_IS_HEX (gh), NULL);
 
-       if (error)      g_error_free (error);
-       g_clear_object (&info);
-       g_object_unref (my_file);
-
        return gh;
 }
 
@@ -2019,18 +1973,22 @@ void
 ghex_application_window_open_file (GHexApplicationWindow *self, GFile *file)
 {
        GtkHex *gh;
-       GFile *my_file;
 
        g_return_if_fail (GHEX_IS_APPLICATION_WINDOW(self));
 
-       my_file = g_object_ref (file);
-       gh = new_gh_from_gfile (my_file);
+       /* If we get it from the GApp :open signal, it's tfr:none - once
+        * HexDocument gets hold of it, though, it _refs it itself so we don't need
+        * to hold onto it.
+        */
+       g_object_ref (file);
+
+       gh = new_gh_from_gfile (file);
 
        ghex_application_window_add_hex (self, gh);
        ghex_application_window_set_hex (self, gh);
        ghex_application_window_activate_tab (self, gh);
 
-       g_object_unref (my_file);
+       g_object_unref (file);
 }
 
 GtkHex *
diff --git a/src/ghex-notebook-tab.c b/src/ghex-notebook-tab.c
index 8fa63d0..8deb20c 100644
--- a/src/ghex-notebook-tab.c
+++ b/src/ghex-notebook-tab.c
@@ -36,8 +36,12 @@ struct _GHexNotebookTab
        GtkWidget *label;
        GtkWidget *close_btn;
        GtkHex *gh;                             /* GtkHex widget activated when tab is clicked */
+
+       GFileMonitor *monitor;
 };
 
+static char *untitled_label = N_("Untitled document");
+
 static guint signals[LAST_SIGNAL];
 
 G_DEFINE_TYPE (GHexNotebookTab, ghex_notebook_tab, GTK_TYPE_WIDGET)
@@ -105,7 +109,7 @@ ghex_notebook_tab_init (GHexNotebookTab *self)
        
        /* Set up our label to hold the document name and the close button. */
 
-       self->label = gtk_label_new (_("Untitled document"));
+       self->label = gtk_label_new (_(untitled_label));
        self->close_btn = gtk_button_new ();
 
        gtk_widget_set_halign (self->close_btn, GTK_ALIGN_END);
@@ -132,7 +136,7 @@ ghex_notebook_tab_dispose (GObject *object)
        /* Unparent children */
        g_clear_pointer (&self->label, gtk_widget_unparent);
        g_clear_pointer (&self->close_btn, gtk_widget_unparent);
-       g_clear_object (&self->gh);
+       g_object_unref (self->gh);
 
        /* Boilerplate: chain up */
        G_OBJECT_CLASS(ghex_notebook_tab_parent_class)->dispose(object);
@@ -183,12 +187,21 @@ static void
 refresh_file_name (GHexNotebookTab *self)
 {
        HexDocument *doc;
+       char *basename;
+       GFile *file;
 
        doc = gtk_hex_get_document (self->gh);
+       file = hex_document_get_file (doc);
 
-       gtk_label_set_markup (GTK_LABEL(self->label),
-                       hex_document_get_basename (doc));
+       if (G_IS_FILE (file))
+               basename = g_file_get_basename (hex_document_get_file (doc));
+       else
+               basename = g_strdup (_(untitled_label));
+
+       gtk_label_set_markup (GTK_LABEL(self->label), basename);
        tab_bold_label (self, hex_document_has_changed (doc));
+
+       g_free (basename);
 }
 
 /* Public Methods */
@@ -227,10 +240,10 @@ ghex_notebook_tab_add_hex (GHexNotebookTab *self, GtkHex *gh)
        g_signal_connect (doc, "document-changed",
                        G_CALLBACK(ghex_notebook_tab_document_changed_cb), self);
 
-       g_signal_connect_swapped (doc, "file-name-changed",
+       g_signal_connect_swapped (doc, "file-saved",
                        G_CALLBACK(refresh_file_name), self);
 
-       g_signal_connect_swapped (doc, "file-saved",
+       g_signal_connect_swapped (doc, "file-name-changed",
                        G_CALLBACK(refresh_file_name), self);
 }
 
diff --git a/src/gtkhex-paste-data.c b/src/gtkhex-paste-data.c
index 6c95e55..9f90f4f 100644
--- a/src/gtkhex-paste-data.c
+++ b/src/gtkhex-paste-data.c
@@ -41,7 +41,7 @@ G_DEFINE_TYPE (GtkHexPasteData, gtk_hex_paste_data, G_TYPE_OBJECT)
 /* Helper Functions */
 
 /* Helper function for the copy and paste stuff, since the data returned by
- * hex_document_get_data is NOT null-temrinated.
+ * hex_buffer_get_data is NOT null-temrinated.
  *
  * String returned should be freed with g_free.
  */
diff --git a/src/gtkhex.c b/src/gtkhex.c
index 591c7ad..0d48884 100644
--- a/src/gtkhex.c
+++ b/src/gtkhex.c
@@ -55,6 +55,9 @@
 #define is_displayable(c) (((c) >= 0x20) && ((c) < 0x7f))
 #define is_copyable(c) (is_displayable(c) || (c) == 0x0a || (c) == 0x0d)
 
+#define HEX_BUFFER_PAYLOAD(X)  \
+       hex_buffer_get_payload_size (hex_document_get_buffer (X))
+
 /* ENUMS */
 
 enum {
@@ -579,7 +582,7 @@ render_cursor (GtkHex *gh,
        /* Find out if we're at the end of the row and/or the end of the file,
         * since this will make a difference when in insert mode
         */
-       if (gh->cursor_pos >= hex_document_get_file_size (gh->document))
+       if (gh->cursor_pos >= HEX_BUFFER_PAYLOAD (gh->document))
                at_file_end = TRUE;
 
        if (gh->cursor_pos % gh->cpl == 0)
@@ -985,7 +988,7 @@ render_lines (GtkHex *gh,
         * draw a single space in the pango layout as a workaround for the fact
         * that it will have no context as to how large to draw the cursor.
         */
-       if (hex_document_get_file_size (gh->document) == 0 &&
+       if (HEX_BUFFER_PAYLOAD (gh->document) == 0 &&
                        ! hex_document_has_changed (gh->document))
        {
                pango_layout_set_text (layout, " ", -1);
@@ -1004,7 +1007,7 @@ render_lines (GtkHex *gh,
                        (char *)gh->disp_buffer,
                        (gh->top_line + min_lines) * gh->cpl,
                        MIN( (gh->top_line + max_lines + 1) * gh->cpl,
-                               hex_document_get_file_size (gh->document) ));
+                               HEX_BUFFER_PAYLOAD (gh->document) ));
        
        for (i = min_lines; i <= max_lines; i++)
        {
@@ -1141,20 +1144,20 @@ recalc_displays (GtkHex *gh)
 {
        GtkWidget *widget = GTK_WIDGET (gh);
        int hex_cpl;
-       int file_size;
+       size_t payload_size;
 
        hex_cpl = gtk_hex_layout_get_hex_cpl (GTK_HEX_LAYOUT(gh->layout_manager));
-       file_size = hex_document_get_file_size (gh->document);
+       payload_size = HEX_BUFFER_PAYLOAD (gh->document);
 
        /*
         * Only change the value of the adjustment to put the cursor on screen
         * if the cursor is currently within the displayed portion.
         */
-       if (file_size == 0 || gh->cpl == 0)
+       if (payload_size == 0 || gh->cpl == 0)
                gh->lines = 1;
        else {
-               gh->lines = file_size / gh->cpl;
-               if (file_size % gh->cpl)
+               gh->lines = payload_size / gh->cpl;
+               if (payload_size % gh->cpl)
                        gh->lines++;
        }
 
@@ -1246,7 +1249,7 @@ scroll_timeout_handler(GtkHex *gh)
        else if (gh->scroll_dir > 0)
        {
                gtk_hex_set_cursor (gh,
-                               MIN (hex_document_get_file_size (gh->document) - 1,
+                               MIN (HEX_BUFFER_PAYLOAD (gh->document) - 1,
                                                gh->cursor_pos + gh->cpl));
        }
        return TRUE;
@@ -1523,11 +1526,11 @@ key_press_cb (GtkEventControllerKey *controller,
        GtkHex *gh = GTK_HEX(user_data);
        GtkWidget *widget = GTK_WIDGET(user_data);
        gboolean ret = GDK_EVENT_PROPAGATE;
-       int file_size;
+       size_t payload_size;
 
        g_return_val_if_fail (HEX_IS_DOCUMENT (gh->document), FALSE);
 
-       file_size = hex_document_get_file_size (gh->document);
+       payload_size = HEX_BUFFER_PAYLOAD (gh->document);
 
        /* don't trample over Ctrl or Alt (reserved for actions) */
        if (state & GDK_CONTROL_MASK || state & GDK_ALT_MASK) {
@@ -1558,7 +1561,7 @@ key_press_cb (GtkEventControllerKey *controller,
                        break;
 
                case GDK_KEY_Delete:
-                       if (gh->cursor_pos < file_size) {
+                       if (gh->cursor_pos < payload_size) {
                                hex_document_set_data (gh->document, gh->cursor_pos,
                                                0, 1, NULL, TRUE);
                                gtk_hex_set_cursor (gh, gh->cursor_pos);
@@ -1577,12 +1580,12 @@ key_press_cb (GtkEventControllerKey *controller,
                        break;
 
                case GDK_KEY_Page_Up:
-                       gtk_hex_set_cursor (gh, MAX(0, gh->cursor_pos - gh->vis_lines*gh->cpl));
+                       gtk_hex_set_cursor (gh, MAX (0, gh->cursor_pos - gh->vis_lines*gh->cpl));
                        ret = GDK_EVENT_STOP;
                        break;
 
                case GDK_KEY_Page_Down:
-                       gtk_hex_set_cursor(gh, MIN(file_size,
+                       gtk_hex_set_cursor(gh, MIN (payload_size,
                                                gh->cursor_pos + gh->vis_lines*gh->cpl));
                        ret = GDK_EVENT_STOP;
                        break;
@@ -1605,7 +1608,7 @@ key_press_cb (GtkEventControllerKey *controller,
                                                break;
 
                                        case GDK_KEY_Right:
-                                               if (gh->cursor_pos >= file_size) {
+                                               if (gh->cursor_pos >= payload_size) {
                                                        ret = GDK_EVENT_STOP;
                                                        break;
                                                }
@@ -1761,18 +1764,18 @@ gtk_hex_real_data_changed (GtkHex *gh, gpointer data)
        HexChangeData *change_data = (HexChangeData *)data;
        int start_line, end_line;
        int lines;
-       int file_size;
+       size_t payload_size;
 
        g_return_if_fail (HEX_IS_DOCUMENT (gh->document));
 
-       file_size = hex_document_get_file_size (gh->document);
+       payload_size = HEX_BUFFER_PAYLOAD (gh->document);
 
        if (gh->cpl == 0)
                return;
 
        if (change_data->start - change_data->end + 1 != change_data->rep_len) {
-               lines = file_size / gh->cpl;
-               if (file_size % gh->cpl)
+               lines = payload_size / gh->cpl;
+               if (payload_size % gh->cpl)
                        lines++;
                if (lines != gh->lines) {
                        gh->lines = lines;
@@ -1879,15 +1882,15 @@ gtk_hex_insert_highlight (GtkHex *gh,
                GtkHex_AutoHighlight *ahl,
                int start, int end)
 {
-       int file_size;
+       size_t payload_size;
 
        g_return_val_if_fail (HEX_IS_DOCUMENT (gh->document), NULL);
 
-       file_size = hex_document_get_file_size (gh->document);
+       payload_size = HEX_BUFFER_PAYLOAD (gh->document);
        GtkHex_Highlight *new = g_new0 (GtkHex_Highlight, 1);
 
-       new->start = CLAMP(MIN(start, end), 0, file_size);
-       new->end = MIN(MAX(start, end), file_size);
+       new->start = CLAMP(MIN(start, end), 0, payload_size);
+       new->end = MIN(MAX(start, end), payload_size);
 
        new->valid = FALSE;
 
@@ -2052,7 +2055,8 @@ gtk_hex_real_copy_to_clipboard (GtkHex *gh)
        g_return_if_fail (len);
 
        /* Grab the raw data from the HexDocument. */
-       doc_data = hex_document_get_data (gh->document, start_pos, len);
+       doc_data = hex_buffer_get_data (hex_document_get_buffer(gh->document),
+                       start_pos, len);
 
        /* Setup a union of HexPasteData and a plain C string */
        paste = gtk_hex_paste_data_new (doc_data, len);
@@ -2266,6 +2270,8 @@ gtk_hex_dispose (GObject *object)
        g_clear_object (&gh->xlayout);
        g_clear_object (&gh->alayout);
        g_clear_object (&gh->olayout);
+
+       g_object_unref (&gh->document);
        
        /* Chain up */
        G_OBJECT_CLASS(gtk_hex_parent_class)->dispose(object);
@@ -2793,21 +2799,21 @@ gtk_hex_paste_from_clipboard (GtkHex *gh)
 void
 gtk_hex_set_selection (GtkHex *gh, int start, int end)
 {
-       int file_size;
+       size_t payload_size;
        int oe, os, ne, ns;
 
        g_return_if_fail (HEX_IS_DOCUMENT (gh->document));
 
-       file_size = hex_document_get_file_size (gh->document);
+       payload_size = HEX_BUFFER_PAYLOAD (gh->document);
 
        if (end < 0)
-               end = file_size;
+               end = payload_size;
 
        os = MIN(gh->selection.start, gh->selection.end);
        oe = MAX(gh->selection.start, gh->selection.end);
 
-       gh->selection.start = CLAMP(start, 0, file_size);
-       gh->selection.end = MIN(end, file_size);
+       gh->selection.start = CLAMP(start, 0, payload_size);
+       gh->selection.end = MIN(end, payload_size);
 
        gtk_hex_invalidate_highlight(gh, &gh->selection);
 
@@ -2906,16 +2912,16 @@ gtk_hex_set_cursor (GtkHex *gh, int index)
 {
        int y;
        int old_pos;
-       int file_size;
+       size_t payload_size;
 
        g_return_if_fail (GTK_IS_HEX (gh));
 
        old_pos = gh->cursor_pos;
-       file_size = hex_document_get_file_size (gh->document);
+       payload_size = HEX_BUFFER_PAYLOAD (gh->document);
 
-       if ((index >= 0) && (index <= file_size))
+       if ((index >= 0) && (index <= payload_size))
        {
-               if (!gh->insert && index == file_size)
+               if (!gh->insert && index == payload_size)
                        index--;
 
                index = MAX(index, 0);
@@ -2941,10 +2947,10 @@ gtk_hex_set_cursor (GtkHex *gh, int index)
                        gtk_adjustment_set_value (gh->adj, y);
                }      
 
-               if(index == file_size)
+               if (index == payload_size)
                        gh->lower_nibble = FALSE;
 
-               if(gh->selecting)
+               if (gh->selecting)
                {
                        gtk_hex_set_selection(gh, gh->selection.start, gh->cursor_pos);
 
@@ -2977,18 +2983,18 @@ gtk_hex_set_cursor_xy (GtkHex *gh, int x, int y)
 {
        int cp;
        int old_pos;
-       int file_size;
+       size_t payload_size;
 
        g_return_if_fail (GTK_IS_HEX(gh));
 
        old_pos = gh->cursor_pos;
        cp = y*gh->cpl + x;
-       file_size = hex_document_get_file_size (gh->document);
+       payload_size = HEX_BUFFER_PAYLOAD (gh->document);
 
        if ((y >= 0) && (y < gh->lines) && (x >= 0) &&
-          (x < gh->cpl) && (cp <= file_size))
+          (x < gh->cpl) && (cp <= payload_size))
        {
-               if (!gh->insert && cp == file_size)
+               if (!gh->insert && cp == payload_size)
                        cp--;
 
                cp = MAX(cp, 0);
@@ -3046,8 +3052,8 @@ gtk_hex_get_byte (GtkHex *gh, int offset)
 {
        g_return_val_if_fail (GTK_IS_HEX(gh), 0);
 
-       if ((offset >= 0) && (offset < hex_document_get_file_size (gh->document)))
-               return hex_document_get_byte(gh->document, offset);
+       if ((offset >= 0) && (offset < HEX_BUFFER_PAYLOAD (gh->document)))
+               return hex_buffer_get_byte (hex_document_get_buffer (gh->document), offset);
 
        return 0;
 }
@@ -3098,15 +3104,15 @@ gtk_hex_show_offsets(GtkHex *gh, gboolean show)
 void
 gtk_hex_set_insert_mode (GtkHex *gh, gboolean insert)
 {
-       int file_size;
+       size_t payload_size;
 
        g_return_if_fail (HEX_IS_DOCUMENT (gh->document));
 
-       file_size = hex_document_get_file_size (gh->document);
+       payload_size = HEX_BUFFER_PAYLOAD (gh->document);
        gh->insert = insert;
 
-       if (!gh->insert && gh->cursor_pos > 0 && gh->cursor_pos >= file_size)
-                       gh->cursor_pos = file_size - 1;
+       if (!gh->insert && gh->cursor_pos > 0 && gh->cursor_pos >= payload_size)
+                       gh->cursor_pos = payload_size - 1;
 }
 
 GtkHex_AutoHighlight *
diff --git a/src/hex-buffer-iface.c b/src/hex-buffer-iface.c
new file mode 100644
index 0000000..1dc5de9
--- /dev/null
+++ b/src/hex-buffer-iface.c
@@ -0,0 +1,114 @@
+/* vim: ts=4 sw=4 colorcolumn=80                                                
+ * -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- *
+ */
+/* hex-buffer-iface.h - Generic buffer interface intended for use with the
+ * HexDocument API
+ *
+ * Copyright © 2021 Logan Rathbone
+ *
+ * Original GHex author: Jaka Mocnik
+ */
+
+#include "hex-buffer-iface.h"
+
+G_DEFINE_INTERFACE (HexBuffer, hex_buffer, G_TYPE_OBJECT)
+
+static void
+hex_buffer_default_init (HexBufferInterface *iface)
+{
+    /* add properties and signals to the interface here */
+}
+
+/* PUBLIC INTERFACE FUNCTIONS */
+
+char *
+hex_buffer_get_data (HexBuffer *self,
+               size_t offset,
+               size_t len)
+{
+       HexBufferInterface *iface;
+
+       g_return_val_if_fail (HEX_IS_BUFFER (self), NULL);
+       iface = HEX_BUFFER_GET_IFACE (self);
+       g_return_val_if_fail (iface->get_data != NULL, NULL);
+
+       return iface->get_data (self, offset, len);
+}
+
+char
+hex_buffer_get_byte (HexBuffer *self,
+                       size_t offset)
+{
+       HexBufferInterface *iface;
+
+       g_return_val_if_fail (HEX_IS_BUFFER (self), 0);
+       iface = HEX_BUFFER_GET_IFACE (self);
+       g_return_val_if_fail (iface->get_byte != NULL, 0);
+
+       return iface->get_byte (self, offset);
+}
+
+gboolean
+hex_buffer_set_data (HexBuffer *self,
+                       size_t offset,
+                       size_t len,
+                       size_t rep_len,
+                       char *data)
+{
+       HexBufferInterface *iface;
+
+       g_return_val_if_fail (HEX_IS_BUFFER (self), FALSE);
+       iface = HEX_BUFFER_GET_IFACE (self);
+       g_return_val_if_fail (iface->set_data != NULL, FALSE);
+
+       return iface->set_data (self, offset, len, rep_len, data);
+}
+
+gboolean
+hex_buffer_set_file (HexBuffer *self, GFile *file)
+{
+       HexBufferInterface *iface;
+
+       g_return_val_if_fail (HEX_IS_BUFFER (self), FALSE);
+       iface = HEX_BUFFER_GET_IFACE (self);
+       g_return_val_if_fail (iface->set_file != NULL, FALSE);
+
+       return iface->set_file (self, file);
+}
+
+
+gboolean
+hex_buffer_read (HexBuffer *self)
+{
+       HexBufferInterface *iface;
+
+       g_return_val_if_fail (HEX_IS_BUFFER (self), FALSE);
+       iface = HEX_BUFFER_GET_IFACE (self);
+       g_return_val_if_fail (iface->read != NULL, FALSE);
+
+       return iface->read (self);
+}
+
+gboolean
+hex_buffer_write_to_file (HexBuffer *self, GFile *file)
+{
+       HexBufferInterface *iface;
+
+       g_return_val_if_fail (HEX_IS_BUFFER (self), FALSE);
+       iface = HEX_BUFFER_GET_IFACE (self);
+       g_return_val_if_fail (iface->write_to_file != NULL, FALSE);
+
+       return iface->write_to_file (self, file);
+}
+
+size_t
+hex_buffer_get_payload_size (HexBuffer *self)
+{
+       HexBufferInterface *iface;
+
+       g_return_val_if_fail (HEX_IS_BUFFER (self), 0);
+       iface = HEX_BUFFER_GET_IFACE (self);
+       g_return_val_if_fail (iface->get_payload_size != NULL, 0);
+
+       return iface->get_payload_size (self);
+}
diff --git a/src/hex-buffer-iface.h b/src/hex-buffer-iface.h
new file mode 100644
index 0000000..14a4a4a
--- /dev/null
+++ b/src/hex-buffer-iface.h
@@ -0,0 +1,85 @@
+/* vim: ts=4 sw=4 colorcolumn=80                                                
+ * -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- *
+ */
+/* hex-buffer-iface.h - Generic buffer interface intended for use with the
+ * HexDocument API
+ *
+ * Copyright © 2021 Logan Rathbone
+ *
+ * Original GHex author: Jaka Mocnik
+ */
+
+#ifndef HEX_BUFFER_IFACE_H
+#define HEX_BUFFER_IFACE_H
+
+#define _GNU_SOURCE
+
+#include <gio/gio.h>
+#include <glib-object.h>
+#include <glib/gi18n.h>
+
+G_BEGIN_DECLS
+
+#define HEX_TYPE_BUFFER hex_buffer_get_type ()
+G_DECLARE_INTERFACE (HexBuffer, hex_buffer, HEX, BUFFER, GObject)
+
+struct _HexBufferInterface
+{
+       GTypeInterface parent_iface;
+
+       char * (*get_data) (HexBuffer *self,
+                       size_t offset,
+                       size_t len);
+
+       char (*get_byte) (HexBuffer *self,
+                       size_t offset);
+
+       gboolean (*set_data) (HexBuffer *self,
+                       size_t offset,
+                       size_t len,
+                       size_t rep_len,
+                       char *data);
+
+       gboolean (*set_file) (HexBuffer *self,
+                       GFile *file);
+
+       gboolean (*read) (HexBuffer *self);
+
+       gboolean (*write_to_file) (HexBuffer *self,
+                       GFile *file);
+
+       size_t (*get_payload_size) (HexBuffer *self);
+
+       /* --- padding starts here -- started w/ 12 extra vfuncs --- */
+
+       gpointer padding[12];
+};
+
+
+
+char * hex_buffer_get_data (HexBuffer *self,
+               size_t offset,
+               size_t len);
+
+char hex_buffer_get_byte (HexBuffer *self,
+               size_t offset);
+
+gboolean hex_buffer_set_data (HexBuffer *self,
+               size_t offset,
+               size_t len,
+               size_t rep_len,
+               char *data);
+
+gboolean hex_buffer_set_file (HexBuffer *self,
+               GFile *file);
+
+gboolean hex_buffer_read (HexBuffer *self);
+
+gboolean hex_buffer_write_to_file (HexBuffer *self,
+               GFile *file);
+
+size_t hex_buffer_get_payload_size (HexBuffer *self);
+
+
+G_END_DECLS
+#endif
diff --git a/src/hex-buffer-malloc.c b/src/hex-buffer-malloc.c
new file mode 100644
index 0000000..96db6a2
--- /dev/null
+++ b/src/hex-buffer-malloc.c
@@ -0,0 +1,389 @@
+/* vim: ts=4 sw=4 colorcolumn=80                                                
+ * -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- *
+ */
+/* hex-document-malloc.c - `malloc` implementation of the HexBuffer iface.
+ *
+ * Copyright © 2021 Logan Rathbone
+ */
+
+#include "hex-buffer-malloc.h"
+
+struct _HexBufferMalloc
+{
+       GObject parent_instance;
+
+       GFile *file;
+       char *buffer;                   /* data buffer */
+       char *gap_pos;                  /* pointer to the start of insertion gap */
+
+       size_t gap_size;                /* insertion gap size */
+       size_t buffer_size;             /* buffer size = file size + gap size */
+       size_t payload_size;
+};
+
+static void hex_buffer_malloc_iface_init (HexBufferInterface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (HexBufferMalloc, hex_buffer_malloc, G_TYPE_OBJECT,
+               G_IMPLEMENT_INTERFACE (HEX_TYPE_BUFFER, hex_buffer_malloc_iface_init))
+
+/* 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);
+
+       if (!self->payload_size)
+       {
+               g_warning ("%s: file \"%s\" is size 0 or invalid file...",
+                               __func__, g_file_get_path (self->file));
+               return FALSE;
+       }
+       else
+               return TRUE;
+}
+
+static gboolean
+hex_buffer_malloc_set_file (HexBuffer *buf, GFile *file)
+{
+       HexBufferMalloc *self = HEX_BUFFER_MALLOC (buf);
+
+       g_return_val_if_fail (G_IS_FILE (file), FALSE);
+
+       self->file = file;
+       if (! update_payload_size_from_file (self))
+       {
+               self->file = NULL;
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static char
+hex_buffer_malloc_get_byte (HexBuffer *buf, size_t offset)
+{
+       HexBufferMalloc *self = HEX_BUFFER_MALLOC (buf);
+
+       if (offset < self->payload_size)
+       {
+               if (self->gap_pos <= self->buffer + offset)
+                       offset += self->gap_size;
+
+               return self->buffer[offset];
+       }
+       else
+               return 0;
+}
+
+static char *
+hex_buffer_malloc_get_data (HexBuffer *buf, size_t offset, size_t len)
+{
+       HexBufferMalloc *self = HEX_BUFFER_MALLOC (buf);
+       char *ptr, *data, *dptr;
+
+       ptr = self->buffer + offset;
+
+       if (ptr >= self->gap_pos)
+               ptr += self->gap_size;
+
+       dptr = data = g_malloc (len);
+
+       for (size_t i = 0; i < len; ++i)
+       {
+               if (ptr >= self->gap_pos  &&  ptr < self->gap_pos + self->gap_size)
+                       ptr += self->gap_size;
+
+               *dptr++ = *ptr++;
+       }
+
+       return data;
+}
+
+static void
+hex_buffer_malloc_place_gap (HexBuffer *buf, size_t offset, size_t min_size)
+{
+       HexBufferMalloc *self = HEX_BUFFER_MALLOC (buf);
+       char *tmp, *buf_ptr, *tmp_ptr;
+
+       if (self->gap_size < min_size)
+       {
+               tmp = g_malloc (self->payload_size);
+               buf_ptr = self->buffer;
+               tmp_ptr = tmp;
+
+               while (buf_ptr < self->gap_pos)
+                       *tmp_ptr++ = *buf_ptr++;
+
+               buf_ptr += self->gap_size;
+               while (buf_ptr < self->buffer + self->buffer_size)
+                       *tmp_ptr++ = *buf_ptr++;
+
+               self->gap_size = MAX (min_size, 32);
+               self->buffer_size = self->payload_size + self->gap_size;
+               self->buffer = g_realloc (self->buffer, self->buffer_size);
+               self->gap_pos = self->buffer + offset;
+
+               buf_ptr = self->buffer;
+               tmp_ptr = tmp;
+               
+               while (buf_ptr < self->gap_pos)
+                       *buf_ptr++ = *tmp_ptr++;
+
+               buf_ptr += self->gap_size;
+               while (buf_ptr < self->buffer + self->buffer_size)
+                       *buf_ptr++ = *tmp_ptr++;
+
+               g_free(tmp);
+       }
+       else
+       {
+               if (self->buffer + offset < self->gap_pos)
+               {
+                       buf_ptr = self->gap_pos + self->gap_size - 1;
+
+                       while (self->gap_pos > self->buffer + offset)
+                               *buf_ptr-- = *(--self->gap_pos);
+               }
+               else if (self->buffer + offset > self->gap_pos)
+               {
+                       buf_ptr = self->gap_pos + self->gap_size;
+
+                       while (self->gap_pos < self->buffer + offset)
+                               *self->gap_pos++ = *buf_ptr++;
+               }
+       }
+}
+
+static gboolean
+hex_buffer_malloc_set_data (HexBuffer *buf, size_t offset, size_t len,
+                                         size_t rep_len, char *data)
+{
+       HexBufferMalloc *self = HEX_BUFFER_MALLOC (buf);
+       size_t i;
+       char *ptr;
+
+       if (offset > self->payload_size)
+       {
+               g_debug ("%s: offset greater than payload size; returning.", __func__);
+               return FALSE;
+       }
+
+       i = 0;
+       ptr = &self->buffer[offset];
+
+       if (ptr >= self->gap_pos)
+               ptr += self->gap_size;
+
+       while (offset + i < self->payload_size && i < rep_len) {
+               if (ptr >= self->gap_pos && ptr < self->gap_pos + self->gap_size)
+                       ptr += self->gap_size;
+               i++;
+       }
+
+       if (rep_len == len) {
+               if (self->buffer + offset >= self->gap_pos)
+                       offset += self->gap_size;
+       }
+       else {
+               if (rep_len > len) {
+                       hex_buffer_malloc_place_gap (buf, offset + rep_len, 1);
+               }
+               else if (rep_len < len) {
+                       hex_buffer_malloc_place_gap (buf, offset + rep_len, len - rep_len);
+               }
+               self->gap_pos -= rep_len - len;
+               self->gap_size += rep_len - len;
+               self->payload_size += len - rep_len;
+       }
+
+       ptr = &self->buffer[offset];
+       i = 0;
+       while (offset + i < self->buffer_size && i < len) {
+               *ptr++ = *data++;
+               i++;
+       }
+
+       return TRUE;
+}
+
+static gboolean
+hex_buffer_malloc_read (HexBuffer *buf)
+{
+       HexBufferMalloc *self = HEX_BUFFER_MALLOC (buf);
+       char *path;
+       FILE *file = NULL;
+       size_t fread_ret;
+       gboolean retval = FALSE;
+
+       if (! G_IS_FILE (self->file))
+               goto out;
+
+       path = g_file_get_path (self->file);
+       if (! path)
+               goto out;
+
+       if (! update_payload_size_from_file (self))
+               goto out;
+
+       if ((file = fopen(path, "r")) == NULL)
+               goto out;
+
+       self->buffer_size = self->payload_size + self->gap_size;
+       self->buffer = g_malloc (self->buffer_size);                               
+
+       fread_ret = fread (
+                       self->buffer + self->gap_size, 1, self->payload_size, file);
+       if (fread_ret != self->payload_size)
+               goto out;
+
+       self->gap_pos = self->buffer;
+       retval = TRUE;
+
+out:
+       if (file)
+               fclose (file);
+       g_free (path);
+       return retval;
+}
+
+static gboolean
+hex_buffer_malloc_write_to_file (HexBuffer *buf, GFile *file)
+{
+       HexBufferMalloc *self = HEX_BUFFER_MALLOC (buf);
+       char *path = NULL;
+       FILE *fp;
+       gboolean ret = FALSE;
+       int exp_len;
+
+       path = g_file_get_path (file);
+       if (! path)
+               goto out;
+
+       /* TODO/FIXME - Actually use the GFile functions to write to file. */
+
+       if ((fp = fopen(path, "wb")) == NULL)
+               goto out;
+
+       if (self->gap_pos > self->buffer)
+       {
+               exp_len = MIN (self->payload_size, (size_t)(self->gap_pos - self->buffer));
+               ret = fwrite (self->buffer, 1, exp_len, fp);
+               ret = (ret == exp_len) ? TRUE : FALSE;
+       }
+
+       if (self->gap_pos < self->buffer + self->payload_size)
+       {
+               exp_len = self->payload_size - (self->gap_pos - self->buffer);
+               ret = fwrite (self->gap_pos + self->gap_size, 1, exp_len, fp);
+               ret = (ret == exp_len) ? TRUE : FALSE;
+       }
+
+out:
+       g_free (path);
+       if (fp) fclose(fp);
+       return ret;
+}
+
+static size_t
+hex_buffer_malloc_get_payload_size (HexBuffer *buf)
+{
+       HexBufferMalloc *self = HEX_BUFFER_MALLOC (buf);
+
+       return self->payload_size;
+}
+
+/* CONSTRUCTORS AND DESTRUCTORS */
+
+static void
+hex_buffer_malloc_init (HexBufferMalloc *self)
+{
+       self->gap_size = 100;
+       self->buffer_size = self->gap_size;
+       self->buffer = g_malloc (self->buffer_size);
+       self->gap_pos = self->buffer;
+}
+
+static void
+hex_buffer_malloc_dispose (GObject *gobject)
+{
+       HexBufferMalloc *self = HEX_BUFFER_MALLOC (gobject);
+
+       /* chain up */
+       G_OBJECT_CLASS(hex_buffer_malloc_parent_class)->dispose (gobject);
+}
+
+static void
+hex_buffer_malloc_finalize (GObject *gobject)
+{
+       HexBufferMalloc *self = HEX_BUFFER_MALLOC (gobject);
+
+       g_clear_pointer (&self->buffer, g_free);
+
+       /* chain up */
+       G_OBJECT_CLASS(hex_buffer_malloc_parent_class)->finalize (gobject);
+}
+
+static void
+hex_buffer_malloc_class_init (HexBufferMallocClass *klass)
+{
+       GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+       
+       gobject_class->finalize = hex_buffer_malloc_finalize;
+       gobject_class->dispose = hex_buffer_malloc_dispose;
+}
+
+
+/* PUBLIC FUNCTIONS */
+
+HexBufferMalloc *
+hex_buffer_malloc_new (GFile *file)
+{
+       HexBufferMalloc *self = g_object_new (HEX_TYPE_BUFFER_MALLOC, NULL);
+
+       if (file)
+       {
+               /* If a path is provided but it can't be set, nullify the object */
+               if (! hex_buffer_malloc_set_file (HEX_BUFFER(self), file))
+                       g_clear_object (&self);
+       }
+
+       return self;
+}
+
+
+/* INTERFACE IMPLEMENTATION FUNCTIONS */
+
+static void
+hex_buffer_malloc_iface_init (HexBufferInterface *iface)
+{
+       iface->get_data = hex_buffer_malloc_get_data;
+       iface->get_byte = hex_buffer_malloc_get_byte;
+       iface->set_data = hex_buffer_malloc_set_data;
+       iface->set_file = hex_buffer_malloc_set_file;
+       iface->read = hex_buffer_malloc_read;
+       iface->write_to_file = hex_buffer_malloc_write_to_file;
+       iface->get_payload_size = hex_buffer_malloc_get_payload_size;
+}
diff --git a/src/hex-buffer-malloc.h b/src/hex-buffer-malloc.h
new file mode 100644
index 0000000..ed63626
--- /dev/null
+++ b/src/hex-buffer-malloc.h
@@ -0,0 +1,36 @@
+/* vim: ts=4 sw=4 colorcolumn=80                                                
+ * -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- *
+ */
+/* hex-document-malloc.h - `malloc` implementation of the HexBuffer iface.
+ *
+ * Based on code from aoeui, Copyright © 2007, 2008 Peter Klausler,
+ * licensed by the author/copyright-holder under GPLv2 only.
+ *
+ * Source as adapted herein licensed for GHex to GPLv2+ with written
+ * permission from Peter Klausler dated December 13, 2021 (see
+ * associated git log).
+ *
+ * Copyright © 2021 Logan Rathbone
+ */
+
+#ifndef HEX_DOCUMENT_MALLOC_H
+#define HEX_DOCUMENT_MALLOC_H
+
+#include "hex-buffer-iface.h"
+
+#define _GNU_SOURCE
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+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);
+
+G_END_DECLS
+#endif
diff --git a/src/hex-buffer-mmap.c b/src/hex-buffer-mmap.c
new file mode 100644
index 0000000..b855d50
--- /dev/null
+++ b/src/hex-buffer-mmap.c
@@ -0,0 +1,452 @@
+/* vim: ts=4 sw=4 colorcolumn=80                                                
+ * -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- *
+ */
+/* hex-document-mmap.c - `mmap` implementation of the HexBuffer iface.
+ *
+ * Based on code from aoeui, Copyright © 2007, 2008 Peter Klausler,
+ * licensed by the author/copyright-holder under GPLv2 only.
+ *
+ * Source as adapted herein licensed for GHex to GPLv2+ with written
+ * permission from Peter Klausler dated December 13, 2021 (see
+ * associated git log).
+ *
+ * Copyright © 2021 Logan Rathbone
+ */
+
+// WIP -- NOT WORKING CODE!!
+
+#include "hex-buffer-mmap.h"
+
+#define HEX_BUFFER_MMAP_ERROR hex_buffer_mmap_error_quark ()
+GQuark
+hex_buffer_mmap_error_quark (void)
+{
+  return g_quark_from_static_string ("hex-buffer-mmap-error-quark");
+}
+
+struct _HexBufferMmap
+{
+       GObject parent_instance;
+
+       GError *error;          /* no custom codes; use codes from errno */
+       int last_errno;         /* cache in case we need to re-report errno error. */
+
+       char *data;
+       size_t payload;
+       size_t mapped;
+       size_t gap;
+       int fd;
+       char *path;
+       size_t pagesize;        /* is only fetched once and cached. */
+};
+
+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
+
+/* Helper wrapper for g_set_error and to cache errno */
+static void
+set_error (HexBufferMmap *self, const char *blurb)
+{
+       char *message = NULL;
+
+       if (errno) {
+       /* Translators:  the first '%s' is the blurb indicating some kind of an
+        * error has occurred (eg, 'An error has occurred', and the the 2nd '%s'
+        * is the standard error message that will be reported from the system
+        * (eg, 'No such file or directory').
+        */
+               message = g_strdup_printf (_("%s: %s"), blurb, g_strerror (errno));
+       }
+
+       g_set_error (&self->error,
+                       HEX_BUFFER_MMAP_ERROR,
+                       errno,
+                       message ? message : blurb,
+                       g_strerror (errno));
+
+       if (errno)
+               self->last_errno = errno;
+
+       g_free (message);
+}
+
+static inline
+size_t buffer_gap_bytes (HexBufferMmap *self)
+{
+       return self->mapped - self->payload;
+}
+
+/* CONSTRUCTORS AND DESTRUCTORS */
+
+static void
+hex_buffer_mmap_init (HexBufferMmap *self)
+{
+       self->pagesize = getpagesize ();
+}
+
+static void
+hex_buffer_mmap_dispose (GObject *gobject)
+{
+       HexBufferMmap *self = HEX_BUFFER_MMAP (gobject);
+
+       /* chain up */
+       G_OBJECT_CLASS(hex_buffer_mmap_parent_class)->dispose (gobject);
+}
+
+static void
+hex_buffer_mmap_finalize (GObject *gobject)
+{
+       HexBufferMmap *self = HEX_BUFFER_MMAP (gobject);
+
+       munmap (self->data, self->mapped);
+
+       if (self->fd >= 0)
+       {
+               close (self->fd);
+               unlink (self->path);
+       }
+
+       g_free (self->path);
+
+       /* chain up */
+       G_OBJECT_CLASS(hex_buffer_mmap_parent_class)->finalize (gobject);
+}
+
+static void
+hex_buffer_mmap_class_init (HexBufferMmapClass *klass)
+{
+       GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+       
+       gobject_class->finalize = hex_buffer_mmap_finalize;
+       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? */
+hex_buffer_mmap_place_gap (HexBufferMmap *self, size_t offset)
+{
+       g_return_if_fail (HEX_IS_BUFFER_MMAP (self));
+
+       size_t gapsize = buffer_gap_bytes (self);
+
+       if (offset > self->payload)
+               offset = self->payload;
+
+       if (offset <= self->gap)
+               memmove (self->data + offset + gapsize,
+                               self->data + offset,
+                               self->gap - offset);
+       else
+               memmove (self->data + self->gap,
+                       self->data + self->gap + gapsize,
+                       offset - self->gap);
+
+       self->gap = offset;
+
+       if (self->fd >= 0 && gapsize)
+               memset (self->data + self->gap, ' ', gapsize);
+}
+
+
+
+// was: resize
+void   /* FIXME - make static if possible? */
+hex_buffer_mmap_resize (HexBufferMmap *self, size_t payload_bytes)
+{
+       void *p;
+       char *old = self->data;
+       int fd;
+       int mapflags = 0;
+       size_t map_bytes = payload_bytes;
+
+       g_return_if_fail (HEX_IS_BUFFER_MMAP (self));
+
+       /* Whole pages, with extras as size increases */
+       map_bytes += self->pagesize - 1;
+       map_bytes /= self->pagesize;
+       map_bytes *= 11;
+       map_bytes /= 10;
+       map_bytes *= self->pagesize;
+
+       if (map_bytes < self->mapped)
+               munmap (old + map_bytes, self->mapped - map_bytes);
+
+       if (self->fd >= 0  &&  map_bytes != self->mapped)
+       {
+               errno = 0;
+               if (ftruncate (self->fd, map_bytes))
+               {
+                       char *errmsg = g_strdup_printf (
+                                       _("Could not adjust %s from %lu to %lu bytes"),
+                                               self->path, (long)self->mapped, (long)map_bytes);
+
+                       set_error (self, errmsg);
+                       g_free (errmsg);
+                       return;
+               }
+       }
+
+       if (map_bytes <= self->mapped)
+       {
+               self->mapped = map_bytes;
+               return;
+       }
+
+       if (old)
+       {
+               /* attempt extension */
+               errno = 0;
+               p = mremap (old, self->mapped, map_bytes, MREMAP_MAYMOVE);
+               if (p != MAP_FAILED)
+                       goto done;
+       }
+
+       /* new/replacement allocation */
+
+       if ((fd = self->fd) >= 0)
+       {
+               mapflags |= MAP_SHARED;
+               if (old) {
+                       munmap(old, self->mapped);
+                       old = NULL;
+               }
+       }
+       else
+       {
+#ifdef MAP_ANONYMOUS
+               mapflags |= MAP_ANONYMOUS;
+#else
+               mapflags |= MAP_ANON;
+#endif
+               mapflags |= MAP_PRIVATE;
+       }
+
+       errno = 0;
+       p = mmap (0, map_bytes, PROT_READ|PROT_WRITE, mapflags, fd, 0);
+       if (p == MAP_FAILED)
+       {
+               char *errmsg = g_strdup_printf (
+                       _("Fatal error: Memory mapping of file (%lu bytes, fd %d) failed"),
+                               (long)map_bytes, fd);
+
+               set_error (self, errmsg);
+               g_free (errmsg);
+               return;
+       }
+
+       if (old)
+       {
+               memcpy(p, old, self->payload);
+               munmap(old, self->mapped);
+       }
+
+done:
+       self->data = p;
+       self->mapped = map_bytes;
+}
+
+#define ADJUST_OFFSET_AND_BYTES                                \
+       if (offset >= self->payload)                    \
+               offset = self->payload;                         \
+       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
+
+       if (!bytes) {
+               *out = NULL;
+               return 0;
+       }
+
+       if (offset < self->gap  &&  offset + bytes > self->gap)
+               hex_buffer_mmap_place_gap (self, offset + bytes);
+
+       *out = self->data + offset;
+       if (offset >= self->gap)
+               *out += buffer_gap_bytes (self);
+
+       return bytes;
+}
+
+
+
+
+// was: buffer_get     - gets & copies data - need to allocate 1st!!!
+//
+size_t
+hex_buffer_mmap_get (HexBufferMmap *self,
+               void *out, size_t offset, size_t bytes)
+{
+       size_t left;
+
+       g_assert (HEX_IS_BUFFER_MMAP (self));
+
+       ADJUST_OFFSET_AND_BYTES
+
+       left = bytes;
+       if (offset < self->gap)
+       {
+               unsigned int before = self->gap - offset;
+
+               if (before > bytes)
+                       before = bytes;
+
+               memcpy (out, self->data + offset, before);
+
+               out = (char *)out + before;
+               offset += before;
+               left -= before;
+
+               if (!left)
+                       return bytes;
+       }
+       offset += buffer_gap_bytes (self);
+
+       memcpy (out, self->data + offset, left);
+
+       return bytes;
+}
+
+// was: buffer_delete
+size_t
+hex_buffer_mmap_delete (HexBufferMmap *self,
+                    size_t offset, size_t bytes)
+{
+       g_assert (HEX_IS_BUFFER_MMAP (self));
+
+       ADJUST_OFFSET_AND_BYTES
+
+       hex_buffer_mmap_place_gap (self, offset);
+       self->payload -= bytes;
+
+       return bytes;
+}
+#undef ADJUST_OFFSET_AND_BYTES
+
+// was: buffer_insert
+//
+size_t
+hex_buffer_mmap_insert (HexBufferMmap *self,
+               const void *in, size_t offset, size_t bytes)
+{
+       g_assert (HEX_IS_BUFFER_MMAP (self));
+
+       if (offset > self->payload)
+               offset = self->payload;
+
+       if (bytes > buffer_gap_bytes (self)) {
+               hex_buffer_mmap_place_gap (self, self->payload);
+               hex_buffer_mmap_resize (self, self->payload + bytes);
+       }
+
+       hex_buffer_mmap_place_gap (self, offset);
+
+       if (in)
+               memcpy (self->data + offset, in, bytes);
+       else
+               memset (self->data + offset, 0, bytes);
+
+       self->gap += bytes;
+       self->payload += bytes;
+
+       return bytes;
+}
+
+// was: buffer_move
+//
+size_t
+hex_buffer_mmap_move (HexBufferMmap *to,
+               size_t to_offset,
+               HexBufferMmap *from,
+               size_t from_offset,
+               size_t bytes)
+{
+       char *raw = NULL;
+
+       bytes = hex_buffer_mmap_raw (from, &raw, from_offset, bytes);
+       hex_buffer_mmap_insert (to, raw, to_offset, bytes);
+
+       return hex_buffer_mmap_delete (from, from_offset, bytes);
+}
+
+// was: buffer_snap
+//
+void 
+hex_buffer_mmap_snap (HexBufferMmap *self)
+{
+       g_return_if_fail (HEX_IS_BUFFER_MMAP (self));
+
+       if (self->fd >= 0)
+       {
+               hex_buffer_mmap_place_gap (self, self->payload);
+               if (ftruncate (self->fd, self->payload)) {
+                       /* don't care */
+               }
+       }
+}
+
+/* INTERFACE IMPLEMENTATION FUNCTIONS */
+
+static void
+hex_buffer_mmap_iface_init (HexBufferInterface *iface)
+{
+#if 0
+       iface->blah = blah_blah_function;
+#endif
+}
+
diff --git a/src/hex-buffer-mmap.h b/src/hex-buffer-mmap.h
new file mode 100644
index 0000000..f517cbf
--- /dev/null
+++ b/src/hex-buffer-mmap.h
@@ -0,0 +1,37 @@
+/* vim: ts=4 sw=4 colorcolumn=80                                                
+ * -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- *
+ */
+/* hex-document-mmap.h - `mmap` implementation of the HexBuffer iface.
+ *
+ * Based on code from aoeui, Copyright © 2007, 2008 Peter Klausler,
+ * licensed by the author/copyright-holder under GPLv2 only.
+ *
+ * Source as adapted herein licensed for GHex to GPLv2+ with written
+ * permission from Peter Klausler dated December 13, 2021 (see
+ * associated git log).
+ *
+ * Copyright © 2021 Logan Rathbone
+ */
+
+// WIP - NOT WORKING CODE!
+
+#ifndef HEX_DOCUMENT_MMAP_H
+#define HEX_DOCUMENT_MMAP_H
+
+#include "hex-buffer-iface.h"
+
+#define _GNU_SOURCE
+
+#include <errno.h>
+#include <unistd.h>
+#include <sys/mman.h>
+
+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);
+
+G_END_DECLS
+#endif
diff --git a/src/hex-document.c b/src/hex-document.c
index a2da0c3..78ab7ee 100644
--- a/src/hex-document.c
+++ b/src/hex-document.c
@@ -35,6 +35,11 @@
 
 #include <gtkhex.h>
 
+/* FIXME / TODO - Allow for swappability. Hardcoding for now for testing
+ * purposes.
+ */
+#include "hex-buffer-malloc.h"
+
 #include <stdio.h>
 #include <unistd.h>
 #include <sys/stat.h>
@@ -47,16 +52,12 @@ static void hex_document_real_changed   (HexDocument *doc,
                                                                                 gboolean undoable);
 static void hex_document_real_redo      (HexDocument *doc);
 static void hex_document_real_undo      (HexDocument *doc);
-static void move_gap_to                 (HexDocument *doc,
-                                                                                int offset,
-                                                                            int min_size);
 static void free_stack                  (GList *stack);
 static gint undo_stack_push             (HexDocument *doc,
                                                                             HexChangeData *change_data);
 static void undo_stack_descend          (HexDocument *doc);
 static void undo_stack_ascend           (HexDocument *doc);
 static void undo_stack_free             (HexDocument *doc);
-static gboolean get_document_attributes (HexDocument *doc);
 
 #define DEFAULT_UNDO_DEPTH 1024
 
@@ -79,18 +80,9 @@ struct _HexDocument
 {
        GObject object;
 
-       GList *views;      /* a list of GtkHex widgets showing this document */
-       
-       char *file_name;
-       char *basename;
-
-       char *buffer;    /* data buffer */
-       char *gap_pos;   /* pointer to the start of insertion gap */
-       int gap_size;     /* insertion gap size */
-       int buffer_size; /* buffer size = file size + gap size */
-       int file_size;   /* real file size */
-
+       GFile *file;
        gboolean changed;
+       HexBuffer *buffer;
 
        GList *undo_stack; /* stack base */
        GList *undo_top;   /* top of the stack (for redo) */
@@ -224,116 +216,24 @@ undo_stack_free(HexDocument *doc)
        g_signal_emit(G_OBJECT(doc), hex_signals[UNDO_STACK_FORGET], 0);
 }
 
-static gboolean
-get_document_attributes(HexDocument *doc)
-{
-       static struct stat stats;
-
-       if(doc->file_name == NULL)
-               return FALSE;
-
-       if(!stat(doc->file_name, &stats) &&
-          S_ISREG(stats.st_mode)) {
-               doc->file_size = stats.st_size;
-
-               return TRUE;
-       }
-
-       return FALSE;
-}
-
-
 static void
-move_gap_to(HexDocument *doc, int offset, int min_size)
+hex_document_dispose (GObject *obj)
 {
-       char *tmp, *buf_ptr, *tmp_ptr;
-
-       if(doc->gap_size < min_size) {
-               tmp = g_malloc(doc->file_size);
-               buf_ptr = doc->buffer;
-               tmp_ptr = tmp;
-               while(buf_ptr < doc->gap_pos)
-                       *tmp_ptr++ = *buf_ptr++;
-               buf_ptr += doc->gap_size;
-               while(buf_ptr < doc->buffer + doc->buffer_size)
-                       *tmp_ptr++ = *buf_ptr++;
-
-               doc->gap_size = MAX(min_size, 32);
-               doc->buffer_size = doc->file_size + doc->gap_size;
-               doc->buffer = g_realloc(doc->buffer, doc->buffer_size);
-               doc->gap_pos = doc->buffer + offset;
-
-               buf_ptr = doc->buffer;
-               tmp_ptr = tmp;
-               
-               while(buf_ptr < doc->gap_pos)
-                       *buf_ptr++ = *tmp_ptr++;
-               buf_ptr += doc->gap_size;
-               while(buf_ptr < doc->buffer + doc->buffer_size)
-                       *buf_ptr++ = *tmp_ptr++;
-
-               g_free(tmp);
-       }
-       else {
-               if(doc->buffer + offset < doc->gap_pos) {
-                       buf_ptr = doc->gap_pos + doc->gap_size - 1;
-                       while(doc->gap_pos > doc->buffer + offset)
-                               *buf_ptr-- = *(--doc->gap_pos);
-               }
-               else if(doc->buffer + offset > doc->gap_pos) {
-                       buf_ptr = doc->gap_pos + doc->gap_size;
-                       while(doc->gap_pos < doc->buffer + offset)
-                               *doc->gap_pos++ = *buf_ptr++;
-               }
-       }
-}
-
-GtkWidget *
-hex_document_add_view(HexDocument *doc)
-{
-       GtkWidget *new_view;
+       HexDocument *doc = HEX_DOCUMENT(obj);
        
-       new_view = gtk_hex_new(doc);
-
-       g_object_ref (new_view);
-
-       doc->views = g_list_append(doc->views, new_view);
-
-       return new_view;
-}
-
-void
-hex_document_remove_view(HexDocument *doc, GtkWidget *view)
-{
-       if(g_list_index(doc->views, view) == -1)
-               return;
-
-       doc->views = g_list_remove(doc->views, view);
+       if (doc->file)
+               g_object_unref (doc->file);
 
-       g_object_unref(view);
+       G_OBJECT_CLASS(hex_document_parent_class)->dispose (obj);
 }
 
 static void
 hex_document_finalize (GObject *obj)
 {
-       HexDocument *doc;
-       
-       doc = HEX_DOCUMENT(obj);
-       
-       if (doc->buffer)
-               g_free (doc->buffer);
+       HexDocument *doc = HEX_DOCUMENT (obj);
        
-       if (doc->file_name)
-               g_free (doc->file_name);
-
-       if (doc->basename)
-               g_free (doc->basename);
-
        undo_stack_free (doc);
 
-       while (doc->views)
-               hex_document_remove_view(doc, GTK_WIDGET(doc->views->data));
-
        G_OBJECT_CLASS(hex_document_parent_class)->finalize (obj);
 }
 
@@ -351,6 +251,7 @@ hex_document_class_init (HexDocumentClass *klass)
        GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
        
        gobject_class->finalize = hex_document_finalize;
+       gobject_class->dispose= hex_document_dispose;
        
        hex_signals[DOCUMENT_CHANGED] =
                g_signal_new_class_handler ("document-changed",
@@ -410,13 +311,8 @@ hex_document_class_init (HexDocumentClass *klass)
 static void
 hex_document_init (HexDocument *doc)
 {
+       doc->buffer = HEX_BUFFER(hex_buffer_malloc_new (NULL));
        doc->undo_max = DEFAULT_UNDO_DEPTH;
-       doc->file_name = NULL;
-       doc->gap_size = 100;
-       doc->file_size = 0;
-       doc->buffer_size = doc->file_size + doc->gap_size;
-       doc->gap_pos = doc->buffer = g_malloc(doc->buffer_size);
-       doc->basename = g_strdup(_("New document"));
 }
 
 /*-------- public API starts here --------*/
@@ -428,283 +324,210 @@ hex_document_new (void)
        return g_object_new (HEX_TYPE_DOCUMENT, NULL);
 }
 
-HexDocument *
-hex_document_new_from_file(const gchar *name)
+gboolean
+hex_document_set_file (HexDocument *doc, GFile *file)
 {
-       HexDocument *doc;
-       char *basename;
+       gboolean had_prev_file = FALSE;
 
-       doc = hex_document_new ();
-
-       g_return_val_if_fail (doc, NULL);
+       if (! hex_buffer_set_file (doc->buffer, file)) {
+               g_debug ("%s: Invalid file", __func__);
+               return FALSE;
+       }
 
-       doc->file_name = g_strdup (name);
+       if (G_IS_FILE (doc->file)) {
+               had_prev_file = TRUE;
+               g_object_unref (doc->file);
+       }
 
-       if (get_document_attributes (doc))
-       {
-               doc->gap_size = 100;
-               doc->buffer_size = doc->file_size + doc->gap_size;
-               doc->buffer = g_malloc(doc->buffer_size);
+       doc->file = g_object_ref (file);
 
-               /* find the start of the filename without path */
-               basename = g_path_get_basename (doc->file_name);
-               doc->basename = g_filename_to_utf8 (basename, -1, NULL, NULL, NULL);
-               g_free (basename);
+       if (had_prev_file)
+               g_signal_emit (G_OBJECT(doc), hex_signals[FILE_NAME_CHANGED], 0);
 
-               if (hex_document_read (doc))
-                       return doc;
+       if (! hex_document_read (doc))
+       {
+               g_debug ("%s: Unable to load/read file", __func__);
+               return FALSE;
        }
-       g_object_unref (doc);
-       
-       return NULL;
-}
 
-char
-hex_document_get_byte(HexDocument *doc, int offset)
-{
-       if(offset < doc->file_size) {
-               if(doc->gap_pos <= doc->buffer + offset)
-                       offset += doc->gap_size;
-               return doc->buffer[offset];
-       }
-       else
-               return 0;
+       return TRUE;
 }
 
-char *
-hex_document_get_data(HexDocument *doc, int offset, int len)
+HexDocument *
+hex_document_new_from_file (GFile *file)
 {
-       char *ptr, *data, *dptr;
-       int i;
-
-       ptr = doc->buffer + offset;
-
-       if (ptr >= doc->gap_pos)
-               ptr += doc->gap_size;
-
-       dptr = data = g_malloc(len);
+       HexDocument *doc;
 
-       for (i = 0; i < len; ++i) {
-               if (ptr >= doc->gap_pos && ptr < doc->gap_pos + doc->gap_size)
-                       ptr += doc->gap_size;
+       g_return_val_if_fail (G_IS_FILE (file), NULL);
+       
+       doc = hex_document_new ();
+       g_return_val_if_fail (doc, NULL);
 
-               *dptr++ = *ptr++;
+       if (! hex_document_set_file (doc, file))
+       {
+               g_object_unref (doc);
        }
 
-       return data;
+       return doc;
 }
 
 void
-hex_document_set_nibble(HexDocument *doc, char val, int offset,
+hex_document_set_nibble (HexDocument *doc, char val, size_t offset,
                                                gboolean lower_nibble, gboolean insert,
                                                gboolean undoable)
 {
+       static HexChangeData tmp_change_data;
        static HexChangeData change_data;
+       char tmp_data[2] = {0};         /* 1 char + NUL */
+
+       doc->changed = TRUE;
+       tmp_change_data.start = offset;
+       tmp_change_data.end = offset;
+       tmp_change_data.v_string = NULL;
+       tmp_change_data.type = HEX_CHANGE_BYTE;
+       tmp_change_data.lower_nibble = lower_nibble;
+       tmp_change_data.insert = insert;
+
+       tmp_change_data.v_byte = hex_buffer_get_byte (doc->buffer, offset);
+
+       /* If in insert mode and on lower nibble, let the user enter the 2nd
+        * nibble on the selected byte, and don't insert a new byte until the
+        * next keystroke. nb: This has the side effect of only letting you
+        * insert a new byte if you're on the upper nibble...
+        */
+       if (!lower_nibble && insert)
+               tmp_change_data.rep_len = 0;
+       else
+               tmp_change_data.rep_len = 1;
 
-       if(offset <= doc->file_size) {
-               if(!insert && offset == doc->file_size)
-                       return;
-
-               doc->changed = TRUE;
-               change_data.start = offset;
-               change_data.end = offset;
-               change_data.v_string = NULL;
-               change_data.type = HEX_CHANGE_BYTE;
-               change_data.lower_nibble = lower_nibble;
-               change_data.insert = insert;
-               if(!lower_nibble && insert) {
-                       move_gap_to(doc, offset, 1);
-                       doc->gap_size--;
-                       doc->gap_pos++;
-                       doc->file_size++;
-                       change_data.rep_len = 0;
-                       if(offset == doc->file_size)
-                               doc->buffer[offset] = 0;
-               }
-               else {
-                       if(doc->buffer + offset >= doc->gap_pos)
-                               offset += doc->gap_size;
-                       change_data.rep_len = 1;
-               }
-
-               change_data.v_byte = doc->buffer[offset];
-               doc->buffer[offset] = (doc->buffer[offset] & (lower_nibble?0xF0:0x0F)) | 
(lower_nibble?val:(val << 4));
+       /* some 80s C magic right here, folks */
+       snprintf (tmp_data, 2, "%c",
+                       (tmp_change_data.v_byte & (lower_nibble ? 0xF0 : 0x0F)) |
+                       (lower_nibble ? val : (val << 4)));
 
-               hex_document_changed(doc, &change_data, undoable);
+       if (hex_buffer_set_data (doc->buffer, offset, 1, tmp_change_data.rep_len,
+                               tmp_data))
+       {
+               change_data = tmp_change_data;
+               hex_document_changed (doc, &change_data, undoable);
        }
 }
 
 void
-hex_document_set_byte(HexDocument *doc, char val, int offset,
+hex_document_set_byte (HexDocument *doc, char val, size_t offset,
                                          gboolean insert, gboolean undoable)
 {
+       static HexChangeData tmp_change_data;
        static HexChangeData change_data;
+       char tmp_data[2] = {0};         /* 1 char + NUL */
 
-       if(offset <= doc->file_size) {
-               if(!insert && offset == doc->file_size)
-                       return;
-
-               doc->changed = TRUE;
-               change_data.start = offset;
-               change_data.end = offset;
-               change_data.rep_len = (insert?0:1);
-               change_data.v_string = NULL;
-               change_data.type = HEX_CHANGE_BYTE;
-               change_data.lower_nibble = FALSE;
-               change_data.insert = insert;
-               if(insert) {
-                       move_gap_to(doc, offset, 1);
-                       doc->gap_size--;
-                       doc->gap_pos++;
-                       doc->file_size++;
-               }
-               else if(doc->buffer + offset >= doc->gap_pos)
-                       offset += doc->gap_size;
-                       
-               change_data.v_byte = doc->buffer[offset];
-               doc->buffer[offset] = val;
+       doc->changed = TRUE;
+       tmp_change_data.start = offset;
+       tmp_change_data.end = offset;
+       tmp_change_data.rep_len = (insert ? 0 : 1);
+       tmp_change_data.v_string = NULL;
+       tmp_change_data.type = HEX_CHANGE_BYTE;
+       tmp_change_data.lower_nibble = FALSE;
+       tmp_change_data.insert = insert;
+
+       tmp_change_data.v_byte = hex_buffer_get_byte (doc->buffer, offset);
 
-               hex_document_changed(doc, &change_data, undoable);
+       snprintf (tmp_data, 2, "%c", val);
+
+       if (hex_buffer_set_data (doc->buffer, offset, 1, tmp_change_data.rep_len,
+                               tmp_data))
+       {
+               change_data = tmp_change_data;
+               hex_document_changed (doc, &change_data, undoable);
        }
 }
 
 void
-hex_document_set_data (HexDocument *doc, int offset, int len,
-                                         int rep_len, char *data, gboolean undoable)
+hex_document_set_data (HexDocument *doc, size_t offset, size_t len,
+                                         size_t rep_len, char *data, gboolean undoable)
 {
        int i;
        char *ptr;
+       static HexChangeData tmp_change_data;
        static HexChangeData change_data;
 
-       if(offset <= doc->file_size) {
-               if(doc->file_size - offset < rep_len)
-                       rep_len -= doc->file_size - offset;
+       doc->changed = TRUE;
 
-               doc->changed = TRUE;
-               
-               change_data.v_string = g_realloc(change_data.v_string, rep_len);
-               change_data.start = offset;
-               change_data.end = change_data.start + len - 1;
-               change_data.rep_len = rep_len;
-               change_data.type = HEX_CHANGE_STRING;
-               change_data.lower_nibble = FALSE;
-               
-               i = 0;
-               ptr = &doc->buffer[offset];
-               if(ptr >= doc->gap_pos)
-                       ptr += doc->gap_size;
-               while(offset + i < doc->file_size && i < rep_len) {
-                       if(ptr >= doc->gap_pos && ptr < doc->gap_pos + doc->gap_size)
-                               ptr += doc->gap_size;
-                       change_data.v_string[i] = *ptr++;
-                       i++;
-               }
-               
-               if(rep_len == len) {
-                       if(doc->buffer + offset >= doc->gap_pos)
-                               offset += doc->gap_size;
-               }
-               else {
-                       if(rep_len > len) {
-                               move_gap_to(doc, offset + rep_len, 1);
-                       }
-                       else if(rep_len < len) {
-                               move_gap_to(doc, offset + rep_len, len - rep_len);
-                       }
-                       doc->gap_pos -= (gint)rep_len - (gint)len;
-                       doc->gap_size += (gint)rep_len - (gint)len;
-                       doc->file_size += (gint)len - (gint)rep_len;
-               }
-               
-               ptr = &doc->buffer[offset];
-               i = 0;
-               while(offset + i < doc->buffer_size && i < len) {
-                       *ptr++ = *data++;
-                       i++;
-               }
-               
-               hex_document_changed(doc, &change_data, undoable);
+       tmp_change_data.start = offset;
+       tmp_change_data.end = tmp_change_data.start + len - 1;
+       tmp_change_data.rep_len = rep_len;
+       tmp_change_data.type = HEX_CHANGE_STRING;
+       tmp_change_data.lower_nibble = FALSE;
+
+       g_clear_pointer (&tmp_change_data.v_string, g_free);
+
+       tmp_change_data.v_string = hex_buffer_get_data (doc->buffer,
+                       tmp_change_data.start, tmp_change_data.rep_len);
+
+       if (hex_buffer_set_data (doc->buffer, offset, len, rep_len, data))
+       {
+               change_data = tmp_change_data;
+               hex_document_changed (doc, &change_data, undoable);
        }
 }
 
 void
 hex_document_delete_data(HexDocument *doc, guint offset, guint len, gboolean undoable)
 {
-       hex_document_set_data(doc, offset, 0, len, NULL, undoable);
+       hex_document_set_data (doc, offset, 0, len, NULL, undoable);
 }
 
-gint
-hex_document_read(HexDocument *doc)
+gboolean
+hex_document_read (HexDocument *doc)
 {
-       FILE *file;
        static HexChangeData change_data;
-       int fread_as_int;
-
-       if(doc->file_name == NULL)
-               return FALSE;
+       size_t payload;
 
-       if(!get_document_attributes(doc))
-               return FALSE;
+       g_return_val_if_fail (G_IS_FILE (doc->file), FALSE);
 
-       if((file = fopen(doc->file_name, "r")) == NULL)
+       /* Read the actual file on disk into the buffer */
+       if (!hex_buffer_read (doc->buffer))
                return FALSE;
 
-       doc->gap_size = doc->buffer_size - doc->file_size;
-
-       fread_as_int = fread(doc->buffer + doc->gap_size, 1, doc->file_size, file);
-       if (fread_as_int != doc->file_size)
-       {
-               g_return_val_if_reached(FALSE);
-       }
-
-       doc->gap_pos = doc->buffer;
-       fclose(file);
        undo_stack_free(doc);
 
+       payload = hex_buffer_get_payload_size (hex_document_get_buffer (doc));
+
        change_data.start = 0;
-       change_data.end = doc->file_size - 1;
+       change_data.end = payload - 1;
        doc->changed = FALSE;
-       hex_document_changed(doc, &change_data, FALSE);
+       hex_document_changed (doc, &change_data, FALSE);
 
        return TRUE;
 }
 
-gint
-hex_document_write_to_file(HexDocument *doc, FILE *file)
+gboolean
+hex_document_write_to_file (HexDocument *doc, GFile *file)
 {
-       int ret = TRUE;
-       int exp_len;
-
-       if(doc->gap_pos > doc->buffer) {
-               exp_len = MIN(doc->file_size, doc->gap_pos - doc->buffer);
-               ret = fwrite(doc->buffer, 1, exp_len, file);
-               ret = (ret == exp_len) ? TRUE : FALSE;
-       }
-       if(doc->gap_pos < doc->buffer + doc->file_size) {
-               exp_len = doc->file_size - (size_t)(doc->gap_pos - doc->buffer);
-               ret = fwrite(doc->gap_pos + doc->gap_size, 1, exp_len, file);
-               ret = (ret == exp_len)?TRUE:FALSE;
-       }
-       return ret;
+       return hex_buffer_write_to_file (doc->buffer, file);
 }
 
-gint
-hex_document_write(HexDocument *doc)
+gboolean
+hex_document_write (HexDocument *doc)
 {
-       FILE *file;
-       gint ret = FALSE;
+       gboolean ret = FALSE;
+       char *path = NULL;
 
-       if(doc->file_name == NULL)
-               return FALSE;
+       g_return_val_if_fail (G_IS_FILE (doc->file), FALSE);
 
-       if((file = fopen(doc->file_name, "wb")) != NULL) {
-               ret = hex_document_write_to_file(doc, file);
-               fclose(file);
-               if(ret) {
-                       doc->changed = FALSE;
-               }
+       path = g_file_get_path (doc->file);
+       if (! path)
+               goto out;
+
+       ret = hex_buffer_write_to_file (doc->buffer, doc->file);
+       if (ret)
+       {
+               doc->changed = FALSE;
+               g_signal_emit (G_OBJECT(doc), hex_signals[FILE_SAVED], 0);
        }
-       g_signal_emit (G_OBJECT(doc), hex_signals[FILE_SAVED], 0);
+
+out:
+       g_free (path);
        return ret;
 }
 
@@ -732,26 +555,21 @@ hex_document_set_max_undo(HexDocument *doc, int max_undo)
        }
 }
 
-static gboolean
-ignore_dialog_cb(GtkDialog *dialog, gpointer user_data)
-{
-       /* unused, as this function just ignores user input. */
-       (void)dialog, (void)user_data;
-
-       return TRUE;
-}
-
-int
+gboolean
 hex_document_export_html (HexDocument *doc, char *html_path, char *base_name,
-                                                int start, int end, int cpl, int lpp,
-                                                int cpw)
+                                                size_t start, size_t end, guint cpl, guint lpp,
+                                                guint cpw)
 {
-       GtkWidget *progress_dialog, *progress_bar;
        FILE *file;
-       int page, line, pos, lines, pages, c;
+       guint page, line, pos, lines, pages, c;
        gchar *page_name, b;
-       gint update_pages;
        gchar *progress_str;
+       size_t payload = hex_buffer_get_payload_size (hex_document_get_buffer (doc));
+       char *basename;
+
+       basename = g_file_get_basename (doc->file);
+       if (! basename)
+               basename = g_strdup (_("Untitled"));
 
        lines = (end - start)/cpl;
        if((end - start)%cpl != 0)
@@ -759,7 +577,6 @@ hex_document_export_html (HexDocument *doc, char *html_path, char *base_name,
        pages = lines/lpp;
        if(lines%lpp != 0)
                pages++;
-       update_pages = pages/1000 + 1;
 
        /* top page */
        page_name = g_strdup_printf("%s/%s.html", html_path, base_name);
@@ -776,13 +593,13 @@ hex_document_export_html (HexDocument *doc, char *html_path, char *base_name,
        fprintf(file, "<CENTER>");
        fprintf(file, "<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n");
        fprintf(file, "<TR>\n<TD COLSPAN=\"3\"><B>%s</B></TD>\n</TR>\n",
-                       doc->file_name?doc->file_name:doc->basename);
+                       basename);
        fprintf(file, "<TR>\n<TD COLSPAN=\"3\">&nbsp;</TD>\n</TR>\n");
        for(page = 0; page < pages; page++) {
                fprintf(file, "<TR>\n<TD>\n<A HREF=\"%s%08d.html\"><PRE>", base_name, page);
                fprintf(file, _("Page"));
                fprintf(file, " %d</PRE></A>\n</TD>\n<TD>&nbsp;</TD>\n<TD VALIGN=\"CENTER\"><PRE>%08x -", 
page+1, page*cpl*lpp);
-               fprintf(file, " %08x</PRE></TD>\n</TR>\n", MIN((page+1)*cpl*lpp-1, doc->file_size-1));
+               fprintf(file, " %08lx</PRE></TD>\n</TR>\n", MIN((page+1)*cpl*lpp-1, payload-1));
        }
        fprintf(file, "</TABLE>\n</CENTER>\n");
        fprintf(file, "<HR WIDTH=\"100%%\">");
@@ -791,38 +608,10 @@ hex_document_export_html (HexDocument *doc, char *html_path, char *base_name,
        fprintf(file, "</BODY>\n</HTML>\n");
        fclose(file);
 
-       progress_dialog = gtk_dialog_new();
-       gtk_window_set_resizable(GTK_WINDOW(progress_dialog), FALSE);
-       gtk_window_set_modal(GTK_WINDOW(progress_dialog), TRUE);
-
-       g_signal_connect(G_OBJECT(progress_dialog), "close",
-                                        G_CALLBACK(ignore_dialog_cb), NULL);
-       gtk_window_set_title(GTK_WINDOW(progress_dialog),
-                                                _("Saving to HTML..."));
-
-       progress_bar = gtk_progress_bar_new();
-       gtk_widget_show(progress_bar);
-
-       gtk_box_append (GTK_BOX (gtk_dialog_get_content_area(GTK_DIALOG (progress_dialog))),
-                       progress_bar);
-       gtk_widget_show(progress_dialog);
-
        pos = start;
        g_object_ref(G_OBJECT(doc));
-       for(page = 0; page < pages; page++) {
-               if((page%update_pages) == 0) {
-                       gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(progress_bar),
-                                       (gdouble)page/(gdouble)pages);
-                       progress_str = g_strdup_printf("%d/%d", page, pages);
-                       gtk_progress_bar_set_text(GTK_PROGRESS_BAR(progress_bar),
-                                                                         progress_str);
-                       g_free(progress_str);
-
-                       while (g_main_context_pending (NULL)) { /* GMainContext - NULL == default */
-                               g_main_context_iteration (NULL, /* " " */
-                                               FALSE);         /* gboolean may_block */
-                       }
-               }
+       for(page = 0; page < pages; page++)
+       {
                /* write page header */
                page_name = g_strdup_printf("%s/%s%08d.html",
                                                                        html_path, base_name, page);
@@ -848,7 +637,7 @@ hex_document_export_html (HexDocument *doc, char *html_path, char *base_name,
                fprintf(file, "\n</TD>\n");
                fprintf(file, "<TD WIDTH=\"33%%\" ALIGN=\"CENTER\">\n");
                fprintf(file, "<A HREF=\"%s.html\">", base_name);
-               fprintf(file, "%s:", doc->basename);
+               fprintf(file, "%s:", basename);
                fprintf(file, "</A>");
                fprintf(file, " %d/%d", page+1, pages);
                fprintf(file, "\n</TD>\n");
@@ -868,7 +657,7 @@ hex_document_export_html (HexDocument *doc, char *html_path, char *base_name,
                fprintf(file, "<TABLE BORDER=\"1\" CELLSPACING=\"2\" CELLPADDING=\"2\">\n");
                fprintf(file, "<TR>\n<TD>\n");
                fprintf(file, "<TABLE BORDER=\"0\" CELLSPACING=\"0\" CELLPADDING=\"0\">\n");
-               for(line = 0; line < lpp && pos + line*cpl < doc->file_size; line++) {
+               for(line = 0; line < lpp && pos + line*cpl < payload; line++) {
                /* offset of line*/
                        fprintf(file, "<TR>\n<TD>\n");
                        fprintf(file, "<PRE>%08x</PRE>\n", pos + line*cpl);
@@ -882,7 +671,7 @@ hex_document_export_html (HexDocument *doc, char *html_path, char *base_name,
                        /* hex data */
                        fprintf(file, "<TR>\n<TD>\n<PRE>");
                        while(pos + c < end) {
-                               fprintf(file, "%02x", hex_document_get_byte(doc, pos + c));
+                               fprintf(file, "%02x", hex_buffer_get_byte (doc->buffer, pos + c));
                                c++;
                                if(c%cpl == 0)
                                        break;
@@ -899,7 +688,7 @@ hex_document_export_html (HexDocument *doc, char *html_path, char *base_name,
                        /* ascii data */
                        fprintf(file, "<TR>\n<TD>\n<PRE>");
                        while(pos + c < end) {
-                               b = hex_document_get_byte(doc, pos + c);
+                               b = hex_buffer_get_byte (doc->buffer, pos + c);
                                if(b >= 0x20)
                                        fprintf(file, "%c", b);
                                else
@@ -922,8 +711,8 @@ hex_document_export_html (HexDocument *doc, char *html_path, char *base_name,
                fprintf(file, "</BODY>\n</HTML>\n");
                fclose(file);
        }
+       g_free (basename);
        g_object_unref(G_OBJECT(doc));
-       gtk_window_destroy(GTK_WINDOW (progress_dialog));
 
        return TRUE;
 }
@@ -935,7 +724,7 @@ hex_document_compare_data(HexDocument *doc, char *s2, int pos, int len)
 
        for (int i = 0; i < len; i++, s2++)
        {
-               c1 = hex_document_get_byte(doc, pos + i);
+               c1 = hex_buffer_get_byte (doc->buffer, pos + i);
 
                if(c1 != (*s2))
                        return (c1 - (*s2));
@@ -944,14 +733,16 @@ hex_document_compare_data(HexDocument *doc, char *s2, int pos, int len)
        return 0;
 }
 
-int
-hex_document_find_forward (HexDocument *doc, int start, char *what,
-                                                 int len, int *found)
+gboolean
+hex_document_find_forward (HexDocument *doc, size_t start, char *what,
+                                                 size_t len, size_t *found)
 {
-       int pos;
-       
+       size_t pos;
+       size_t payload = hex_buffer_get_payload_size (
+                       hex_document_get_buffer (doc));
+
        pos = start;
-       while (pos < doc->file_size)
+       while (pos < payload)
        {
                if (hex_document_compare_data(doc, what, pos, len) == 0)
                {
@@ -964,11 +755,11 @@ hex_document_find_forward (HexDocument *doc, int start, char *what,
        return FALSE;
 }
 
-int
-hex_document_find_backward (HexDocument *doc, int start, char *what,
-                                                  int len, int *found)
+gboolean
+hex_document_find_backward (HexDocument *doc, size_t start, char *what,
+                                                  size_t len, size_t *found)
 {
-       int pos;
+       size_t pos;
        
        pos = start;
 
@@ -1001,16 +792,22 @@ static void
 hex_document_real_undo (HexDocument *doc)
 {
        HexChangeData *cd;
-       int len;
+       size_t len;
        char *rep_data;
        char c_val;
 
        cd = doc->undo_top->data;
 
        switch(cd->type) {
+
        case HEX_CHANGE_BYTE:
-               if(cd->start >= 0 && cd->end < doc->file_size) {
-                       c_val = hex_document_get_byte(doc, cd->start);
+       {
+               size_t payload = hex_buffer_get_payload_size (
+                               hex_document_get_buffer (doc));
+
+               if (cd->end < payload)
+               {
+                       c_val = hex_buffer_get_byte (doc->buffer, cd->start);
                        if(cd->rep_len > 0)
                                hex_document_set_byte(doc, cd->v_byte, cd->start, FALSE, FALSE);
                        else if(cd->rep_len == 0)
@@ -1019,30 +816,25 @@ hex_document_real_undo (HexDocument *doc)
                                hex_document_set_byte(doc, cd->v_byte, cd->start, TRUE, FALSE);
                        cd->v_byte = c_val;
                }
+       }
                break;
+
        case HEX_CHANGE_STRING:
                len = cd->end - cd->start + 1;
-               rep_data = hex_document_get_data(doc, cd->start, len);
-               hex_document_set_data(doc, cd->start, cd->rep_len, len, cd->v_string, FALSE);
+               rep_data = hex_buffer_get_data (doc->buffer, cd->start, len);
+               hex_document_set_data (doc, cd->start, cd->rep_len, len, cd->v_string, FALSE);
                g_free(cd->v_string);
                cd->end = cd->start + cd->rep_len - 1;
                cd->rep_len = len;
                cd->v_string = rep_data;
                break;
-       }
+       }       /* switch */
 
        hex_document_changed(doc, cd, FALSE);
 
        undo_stack_descend(doc);
 }
 
-gboolean
-hex_document_is_writable(HexDocument *doc)
-{
-       return (doc->file_name != NULL &&
-                       access(doc->file_name, W_OK) == 0);
-}
-
 gboolean 
 hex_document_redo(HexDocument *doc)
 {
@@ -1067,9 +859,15 @@ hex_document_real_redo(HexDocument *doc)
        cd = (HexChangeData *)doc->undo_top->data;
 
        switch(cd->type) {
+
        case HEX_CHANGE_BYTE:
-               if(cd->start >= 0 && cd->end <= doc->file_size) {
-                       c_val = hex_document_get_byte(doc, cd->start);
+       {
+               size_t payload = hex_buffer_get_payload_size (
+                               hex_document_get_buffer (doc));
+
+               if (cd->end <= payload)
+               {
+                       c_val = hex_buffer_get_byte (doc->buffer, cd->start);
                        if(cd->rep_len > 0)
                                hex_document_set_byte(doc, cd->v_byte, cd->start, FALSE, FALSE);
                        else if(cd->rep_len == 0)
@@ -1081,11 +879,13 @@ hex_document_real_redo(HexDocument *doc)
                                hex_document_set_byte(doc, cd->v_byte, cd->start, TRUE, FALSE);
                        cd->v_byte = c_val;
                }
+       }
                break;
+
        case HEX_CHANGE_STRING:
                len = cd->end - cd->start + 1;
-               rep_data = hex_document_get_data(doc, cd->start, len);
-               hex_document_set_data(doc, cd->start, cd->rep_len, len, cd->v_string, FALSE);
+               rep_data = hex_buffer_get_data (doc->buffer, cd->start, len);
+               hex_document_set_data (doc, cd->start, cd->rep_len, len, cd->v_string, FALSE);
                g_free(cd->v_string);
                cd->end = cd->start + cd->rep_len - 1;
                cd->rep_len = len;
@@ -1096,33 +896,6 @@ hex_document_real_redo(HexDocument *doc)
        hex_document_changed(doc, cd, FALSE);
 }
 
-gboolean
-hex_document_change_file_name (HexDocument *doc, const char *new_file_name)
-{
-       char *new_path_end = NULL;
-
-       if(doc->file_name)
-               g_free(doc->file_name);
-       if(doc->basename)
-               g_free(doc->basename);
-
-       doc->file_name = g_strdup(new_file_name);
-       doc->changed = FALSE;
-
-       new_path_end = g_path_get_basename (doc->file_name);
-       doc->basename = g_filename_to_utf8 (new_path_end, -1, NULL, NULL, NULL);
-
-       if (new_path_end)
-               g_free (new_path_end);
-
-       if (doc->file_name && doc->basename) {
-               g_signal_emit (G_OBJECT(doc), hex_signals[FILE_NAME_CHANGED], 0);
-               return TRUE;
-       } else {
-               return FALSE;
-       }
-}
-
 gboolean
 hex_document_can_undo (HexDocument *doc)
 {
@@ -1145,26 +918,20 @@ hex_document_can_redo (HexDocument *doc)
                return FALSE;
 }
 
-const char *
-hex_document_get_file_name (HexDocument *doc)
-{
-       return doc->file_name;
-}
-
-const char *
-hex_document_get_basename (HexDocument *doc)
+HexChangeData *
+hex_document_get_undo_data (HexDocument *doc)
 {
-       return doc->basename;
+       return doc->undo_top->data;
 }
 
-int
-hex_document_get_file_size (HexDocument *doc)
+HexBuffer *
+hex_document_get_buffer (HexDocument *doc)
 {
-       return doc->file_size;
+       return doc->buffer;
 }
 
-HexChangeData *
-hex_document_get_undo_data (HexDocument *doc)
+GFile *
+hex_document_get_file (HexDocument *doc)
 {
-       return doc->undo_top->data;
+       return doc->file;
 }
diff --git a/src/hex-document.h b/src/hex-document.h
index 2b35169..2bdd5f4 100644
--- a/src/hex-document.h
+++ b/src/hex-document.h
@@ -38,6 +38,8 @@
 #include <glib-object.h>
 #include <gtk/gtk.h>
 
+#include "hex-buffer-iface.h"
+
 G_BEGIN_DECLS
 
 #define HEX_TYPE_DOCUMENT hex_document_get_type ()
@@ -51,9 +53,9 @@ typedef enum {
 typedef struct _HexChangeData HexChangeData;
 struct _HexChangeData
 {
-       int start, end;
+       size_t start, end;
        /* length to replace (overwrite); (0 to insert without overwriting) */
-       int rep_len;
+       size_t rep_len;
        gboolean lower_nibble;
        gboolean insert;
        HexChangeType type;
@@ -63,46 +65,33 @@ struct _HexChangeData
 
 
 HexDocument *hex_document_new(void);
-HexDocument *hex_document_new_from_file(const char *name);
-void        hex_document_set_data(HexDocument *doc, int offset,
-               int len, int rep_len, char *data, gboolean undoable);
-void        hex_document_set_byte(HexDocument *doc, char val, int offset,
-               gboolean insert, gboolean undoable);
-void        hex_document_set_nibble(HexDocument *doc, char val,
-               int offset, gboolean lower_nibble, gboolean insert, gboolean undoable);
-char        hex_document_get_byte(HexDocument *doc, int offset);
-char        *hex_document_get_data(HexDocument *doc, int offset, int len);
-void        hex_document_delete_data(HexDocument *doc, guint offset,
-               guint len, gboolean undoable);
-int        hex_document_read(HexDocument *doc);
-int        hex_document_write(HexDocument *doc);
-int        hex_document_write_to_file(HexDocument *doc, FILE *file);
-int        hex_document_export_html(HexDocument *doc, char *html_path,
-               char *base_name, int start, int end, int cpl, int lpp, int cpw);
+HexDocument *hex_document_new_from_file (GFile *file);
+void        hex_document_set_data(HexDocument *doc, size_t offset, size_t len, size_t rep_len, char *data, 
gboolean undoable);
+void        hex_document_set_byte(HexDocument *doc, char val, size_t offset, gboolean insert, gboolean 
undoable);
+void        hex_document_set_nibble(HexDocument *doc, char val, size_t offset, gboolean lower_nibble, 
gboolean insert, gboolean undoable);
+void        hex_document_delete_data(HexDocument *doc, guint offset, guint len, gboolean undoable);
+gboolean   hex_document_read (HexDocument *doc);
+gboolean   hex_document_write(HexDocument *doc);
+
+gboolean   hex_document_write_to_file (HexDocument *doc, GFile *file);
+gboolean    hex_document_export_html (HexDocument *doc, char *html_path, char *base_name, size_t start, 
size_t end, guint cpl, guint lpp, guint cpw);
 gboolean    hex_document_has_changed(HexDocument *doc);
-void        hex_document_changed(HexDocument *doc, gpointer change_data,
-               gboolean push_undo);
+void        hex_document_changed(HexDocument *doc, gpointer change_data, gboolean push_undo);
 void        hex_document_set_max_undo(HexDocument *doc, int max_undo);
 gboolean    hex_document_undo(HexDocument *doc);
 gboolean    hex_document_redo(HexDocument *doc);
-int        hex_document_compare_data(HexDocument *doc, char *s2,
-               int pos, int len);
-int        hex_document_find_forward(HexDocument *doc, int start,
-               char *what, int len, int *found);
-int        hex_document_find_backward(HexDocument *doc, int start,
-               char *what, int len, int *found);
-void        hex_document_remove_view(HexDocument *doc, GtkWidget *view);
-GtkWidget   *hex_document_add_view(HexDocument *doc);
-gboolean    hex_document_is_writable(HexDocument *doc);
-gboolean    hex_document_change_file_name (HexDocument *doc,
-               const char *new_file_name);
+int        hex_document_compare_data(HexDocument *doc, char *s2, int pos, int len);
+gboolean   hex_document_find_forward (HexDocument *doc, size_t start, char *what, size_t len, size_t *found);
+gboolean   hex_document_find_backward (HexDocument *doc, size_t start, char *what, size_t len, size_t 
*found);
 gboolean    hex_document_can_undo (HexDocument *doc);
 gboolean    hex_document_can_redo (HexDocument *doc);
-const char     *hex_document_get_file_name (HexDocument *doc);
-const char     *hex_document_get_basename (HexDocument *doc);
-int                    hex_document_get_file_size (HexDocument *doc);
+size_t         hex_document_get_file_size (HexDocument *doc);
 HexChangeData *hex_document_get_undo_data (HexDocument *doc);
 
+HexBuffer * hex_document_get_buffer (HexDocument *doc);
+GFile *     hex_document_get_file (HexDocument *doc);
+gboolean    hex_document_set_file (HexDocument *doc, GFile *file);
+
 G_END_DECLS
 
 #endif /* HEX_DOCUMENT_H */
diff --git a/src/main.c b/src/main.c
index 8efb42a..7757908 100644
--- a/src/main.c
+++ b/src/main.c
@@ -130,12 +130,14 @@ open (GApplication *application,
 {
        GHexApplicationWindow *app_win;
 
-       if (n_files > 1)
-               g_warning ("Can only open a single file");
+//     if (n_files > 1)
+//             g_warning ("Can only open a single file");
 
        activate (GTK_APPLICATION(application), NULL);
        app_win = GHEX_APPLICATION_WINDOW(window);
-       ghex_application_window_open_file (app_win, files[0]);
+
+       for (int i = 0; i < n_files; ++i)
+               ghex_application_window_open_file (app_win, files[i]);
 }
 
 int
diff --git a/src/meson.build b/src/meson.build
index 690ac43..e2c95a0 100644
--- a/src/meson.build
+++ b/src/meson.build
@@ -1,12 +1,15 @@
 libghex_sources = [
   'gtkhex.c',
-  'hex-document.c',
+  'hex-buffer-iface.c',              # TEST
+  'hex-buffer-malloc.c',      # TEST
   'gtkhex-layout-manager.c',
-  'gtkhex-paste-data.c'
+  'gtkhex-paste-data.c',
+  'hex-document.c'
 ]
 
 libghex_headers = [
   'hex-document.h',
+  'hex-buffer-iface.h',
   'gtkhex.h',
   'gtkhex-paste-data.h'
 ]
@@ -16,7 +19,7 @@ libghex_deps = [
 ]
 
 libghex_c_args = [
-  '-DG_LOG_DOMAIN="libgtkhex-3"'
+  '-DG_LOG_DOMAIN="libgtkhex-4"'
 ]
 
 libghex_link_args = cc.get_supported_link_arguments([
diff --git a/src/print.c b/src/print.c
index f10486a..945365c 100644
--- a/src/print.c
+++ b/src/print.c
@@ -45,10 +45,8 @@ static void print_header(GHexPrintJobInfo *pji, unsigned int page)
 {
        PangoLayout *layout;
 
-       const char *file_name = hex_document_get_file_name (pji->doc);
        cairo_t *cr = gtk_print_context_get_cairo_context (pji->pc);
-       char *text1 = g_filename_to_utf8 (file_name, -1, NULL,
-                                                                          NULL, NULL);
+       char *text1 = g_file_get_path (hex_document_get_file (pji->doc));
        char *text2 = g_strdup_printf (_("Page: %i/%i"), page, pji->pages);
        char *pagetext = g_strdup_printf ("%d", page);
        double x, y;
@@ -146,7 +144,7 @@ static void format_hex (HexDocument *doc, guint gt, char *out,
        guchar c;
 
        for (i = start + 1, j = 0; i <= end; i++) {
-               c = hex_document_get_byte(doc, i - 1);
+               c = hex_buffer_get_byte (hex_document_get_buffer (doc), i - 1);
                low = c & 0x0F;
                high = (c & 0xF0) >> 4;
 
@@ -166,7 +164,7 @@ static void format_ascii (HexDocument *doc,
        guchar c;
 
        for (i = start, j = 0; i < end; i++, j++) {
-               c = hex_document_get_byte(doc, i);
+               c = hex_buffer_get_byte (hex_document_get_buffer (doc), i);
                if (is_printable(c))
                        out[j] = c;
                else
@@ -290,7 +288,7 @@ begin_print (GtkPrintOperation *operation,
     pji->pc = context;
     int font_width, font_height;
     int printable_width, printable_height;
-       int file_size = hex_document_get_file_size (pji->doc);
+       size_t payload = hex_buffer_get_payload_size (hex_document_get_buffer (pji->doc));
 
     layout = gtk_print_context_create_pango_layout (context);
     pango_layout_set_text (layout, " ", -1);
@@ -319,7 +317,7 @@ begin_print (GtkPrintOperation *operation,
     pji->bytes_per_row *= pji->gt;
     pji->rows_per_page = (printable_height - pji->header_height) /
                           pji->font_char_height - 2;
-    pji->pages = (((file_size/pji->bytes_per_row) + 1)/
+    pji->pages = (((payload/pji->bytes_per_row) + 1)/
                    pji->rows_per_page) + 1;
     gtk_print_operation_set_n_pages (pji->master, pji->pages);
 }
@@ -330,7 +328,8 @@ print_page (GtkPrintOperation *operation,
             int               page_nr,
             gpointer           data)
 {
-       int j, max_row, file_size;
+       int j, max_row;
+       size_t payload;
 
        GHexPrintJobInfo *pji = (GHexPrintJobInfo *)data;
        g_return_if_fail(pji != NULL);
@@ -338,12 +337,12 @@ print_page (GtkPrintOperation *operation,
        pji->pc = context;
        g_return_if_fail(pji->pc != NULL);
 
-       file_size = hex_document_get_file_size (pji->doc);
+       payload = hex_buffer_get_payload_size (hex_document_get_buffer (pji->doc));
 
        print_header (pji, page_nr+1);
        max_row = (pji->bytes_per_row*pji->rows_per_page*(page_nr+1) >
-                       file_size ?
-                       (int)((file_size-1)-
+                       payload ?
+                       (int)((payload-1)-
                              (pji->bytes_per_row *
                               pji->rows_per_page*(page_nr))) /
                               pji->bytes_per_row + 1:
@@ -353,10 +352,10 @@ print_page (GtkPrintOperation *operation,
                int file_offset = pji->bytes_per_row*(j - 1) +
                        pji->bytes_per_row*pji->rows_per_page*(page_nr);
                int length = (file_offset + pji->bytes_per_row >
-                       file_size ?
-                       file_size - file_offset :
+                       payload ?
+                       payload - file_offset :
                        pji->bytes_per_row);
-               if (file_offset >= file_size)
+               if (file_offset >= payload)
                        break;
                print_row (pji, file_offset, length, j);
        }


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