gnucash future: Multiple changes pushed

John Ralls jralls at code.gnucash.org
Thu Jun 25 15:32:26 EDT 2026


Updated	 via  https://github.com/Gnucash/gnucash/commit/994b04a1 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3b2daddb (commit)
	 via  https://github.com/Gnucash/gnucash/commit/c7d35bcd (commit)
	 via  https://github.com/Gnucash/gnucash/commit/00803ba0 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2d1859ff (commit)
	 via  https://github.com/Gnucash/gnucash/commit/29d74209 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/2a01ce23 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/b3e29a81 (commit)
	from  https://github.com/Gnucash/gnucash/commit/b4fd332d (commit)



commit 994b04a161d43e623eefad5e9d39537f3dc0a4ce
Merge: 3b2daddb47 c7d35bcdb2
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Jun 25 12:31:53 2026 -0700

    Merge Brent McBride's 'uri-format' into future.


commit 3b2daddb477c98a4d116e6cd1c9e3a62b6a0045a
Merge: b4fd332de3 00803ba018
Author: John Ralls <jralls at ceridwen.us>
Date:   Thu Jun 25 12:13:29 2026 -0700

    Merge branch 'use-boost-flat-map' of https://github.com/christopherlam/gnucash into future

diff --cc libgnucash/engine/qofid.cpp
index f649c281a6,7724106866..62d4ec46b0
--- a/libgnucash/engine/qofid.cpp
+++ b/libgnucash/engine/qofid.cpp
@@@ -43,8 -47,13 +47,14 @@@ struct QofCollection_
  
      QofCollection_s (QofIdType type) : e_type{static_cast<QofIdType>(CACHE_INSERT(type))}
                                       , is_dirty{FALSE}
-                                      , hash_of_entities{guid_hash_table_new()}
-                                      , data{NULL} {}
+                                      , data{NULL}
+     {
+         if (!g_strcmp0 (e_type, "Split"))
+             guid_entity_map.reserve (50000);
+         else if (!g_strcmp0 (e_type, "Trans"))
+             guid_entity_map.reserve (10000);
+     }
++
      ~QofCollection_s ()
      {
          CACHE_REMOVE (e_type);

commit c7d35bcdb2cbd8fb552e28b196cef862e92d8b39
Author: Brent McBride <mcbridebt at hotmail.com>
Date:   Tue Jun 23 19:33:40 2026 -0700

    gnc-uri: compose URIs with std::format

diff --git a/libgnucash/engine/gnc-uri.cpp b/libgnucash/engine/gnc-uri.cpp
index 3f30857a83..c85a7fe864 100755
--- a/libgnucash/engine/gnc-uri.cpp
+++ b/libgnucash/engine/gnc-uri.cpp
@@ -25,6 +25,7 @@
 
 #include <glib.h>
 #include <cstdint>
+#include <format>
 #include <optional>
 #include <stdexcept>
 #include <string>
@@ -239,8 +240,8 @@ GncUri::try_str (bool allow_password) const
          */
         bool absolute = !abs_path.empty() &&
                         (abs_path.front() == '/' || abs_path.front() == '\\');
-        return absolute ? scheme + "://" + abs_path
-                        : scheme + ":///" + abs_path; // extra "/" for windows
+        return absolute ? std::format ("{}://{}", scheme, abs_path)
+                        : std::format ("{}:///{}", scheme, abs_path); // extra "/" for windows
     }
 
     std::string userpass;
@@ -257,9 +258,9 @@ GncUri::try_str (bool allow_password) const
 
     std::string portstr;
     if (m_port != 0)
-        portstr = ":" + std::to_string (m_port);
+        portstr = std::format (":{}", m_port);
 
-    return *m_scheme + "://" + userpass + *m_hostname + portstr + "/" + path;
+    return std::format ("{}://{}{}{}/{}", *m_scheme, userpass, *m_hostname, portstr, path);
 }
 
 std::string

commit 00803ba018527f30ea3239fd60a8675009a878ef
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Wed Jun 17 11:07:55 2026 +0800

    [qofid.cpp] for Split and Trans reserve QofInstance* map

diff --git a/libgnucash/engine/qofid.cpp b/libgnucash/engine/qofid.cpp
index cde7fd524e..7724106866 100644
--- a/libgnucash/engine/qofid.cpp
+++ b/libgnucash/engine/qofid.cpp
@@ -47,7 +47,13 @@ struct QofCollection_s
 
     QofCollection_s (QofIdType type) : e_type{static_cast<QofIdType>(CACHE_INSERT(type))}
                                      , is_dirty{FALSE}
-                                     , data{NULL} {}
+                                     , data{NULL}
+    {
+        if (!g_strcmp0 (e_type, "Split"))
+            guid_entity_map.reserve (50000);
+        else if (!g_strcmp0 (e_type, "Trans"))
+            guid_entity_map.reserve (10000);
+    }
     ~QofCollection_s ()
     {
         CACHE_REMOVE (e_type);

commit 2d1859ff3e9e88c1fb723b246ed80407f479d95b
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat May 30 23:36:04 2026 +0800

    [qofid.cpp] use boost::unordered_flat_map
    
    to store guid->qof entities
    
    note this is appropriate if the map values are QofInstance* pointers,
    because unordered_flat_map does not guarantee stable iterators.

diff --git a/libgnucash/engine/qofid.cpp b/libgnucash/engine/qofid.cpp
index f649c281a6..cde7fd524e 100644
--- a/libgnucash/engine/qofid.cpp
+++ b/libgnucash/engine/qofid.cpp
@@ -27,10 +27,14 @@
 #include <config.h>
 #include <string.h>
 
+#include <guid.hpp>
 #include "qof.h"
 #include "qofid-p.h"
 #include "qofinstance-p.h"
 
+#include <boost/unordered/unordered_flat_map.hpp>
+using GuidEntityMap = boost::unordered_flat_map<GncGUID, QofInstance*, std::hash<GncGUID>>;
+
 static QofLogModule log_module = QOF_MOD_ENGINE;
 
 struct QofCollection_s
@@ -38,17 +42,15 @@ struct QofCollection_s
     QofIdType    e_type;
     gboolean     is_dirty;
 
-    GHashTable * hash_of_entities;
+    GuidEntityMap guid_entity_map;
     gpointer     data;       /* place where object class can hang arbitrary data */
 
     QofCollection_s (QofIdType type) : e_type{static_cast<QofIdType>(CACHE_INSERT(type))}
                                      , is_dirty{FALSE}
-                                     , hash_of_entities{guid_hash_table_new()}
                                      , data{NULL} {}
     ~QofCollection_s ()
     {
         CACHE_REMOVE (e_type);
-        g_hash_table_destroy (hash_of_entities);
     }
 };
 
@@ -87,48 +89,30 @@ qof_collection_remove_entity (QofInstance *ent)
     col = qof_instance_get_collection(ent);
     if (!col) return;
     guid = qof_instance_get_guid(ent);
-    g_hash_table_remove (col->hash_of_entities, guid);
+    col->guid_entity_map.erase(*guid);
     qof_instance_set_collection(ent, NULL);
 }
 
 void
 qof_collection_insert_entity (QofCollection *col, QofInstance *ent)
 {
-    const GncGUID *guid;
-
     if (!col || !ent) return;
-    guid = qof_instance_get_guid(ent);
+    const GncGUID *guid = qof_instance_get_guid(ent);
     if (guid_equal(guid, guid_null())) return;
     g_return_if_fail (col->e_type == ent->e_type);
     qof_collection_remove_entity (ent);
-    g_hash_table_insert (col->hash_of_entities, (gpointer)guid, ent);
+    col->guid_entity_map[*guid] = ent;
     qof_instance_set_collection(ent, col);
 }
 
 gboolean
 qof_collection_add_entity (QofCollection *coll, QofInstance *ent)
 {
-    QofInstance *e;
-    const GncGUID *guid;
-
-    e = NULL;
-    if (!coll || !ent)
-    {
-        return FALSE;
-    }
-    guid = qof_instance_get_guid(ent);
-    if (guid_equal(guid, guid_null()))
-    {
-        return FALSE;
-    }
+    if (!coll || !ent) return FALSE;
+    const GncGUID *guid = qof_instance_get_guid(ent);
+    if (guid_equal(guid, guid_null())) return FALSE;
     g_return_val_if_fail (coll->e_type == ent->e_type, FALSE);
-    e = qof_collection_lookup_entity(coll, guid);
-    if ( e != NULL )
-    {
-        return FALSE;
-    }
-    g_hash_table_insert (coll->hash_of_entities, (gpointer)guid, ent);
-    return TRUE;
+    return coll->guid_entity_map.try_emplace (*guid, ent).second;
 }
 
 
@@ -208,22 +192,18 @@ qof_collection_compare (QofCollection *target, QofCollection *merge)
 QofInstance *
 qof_collection_lookup_entity (const QofCollection *col, const GncGUID * guid)
 {
-    QofInstance *ent;
     g_return_val_if_fail (col, NULL);
     if (guid == NULL) return NULL;
-    ent = static_cast<QofInstance*>(g_hash_table_lookup (col->hash_of_entities,
-							 guid));
-    if (ent != NULL && qof_instance_get_destroying(ent)) return NULL;	
-    return ent;
+    auto it = col->guid_entity_map.find(*guid);
+    if (it == col->guid_entity_map.end() || qof_instance_get_destroying(it->second))
+        return nullptr;
+    return it->second;
 }
 
 guint
 qof_collection_count (const QofCollection *col)
 {
-    guint c;
-
-    c = g_hash_table_size(col->hash_of_entities);
-    return c;
+    return col->guid_entity_map.size();
 }
 
 /* =============================================================== */
@@ -283,20 +263,21 @@ void
 qof_collection_foreach_sorted (const QofCollection *col, QofInstanceForeachCB cb_func,
                                gpointer user_data, GCompareFunc sort_fn)
 {
-    GList *entries;
-
     g_return_if_fail (col);
     g_return_if_fail (cb_func);
 
-    PINFO("Hash Table size of %s before is %d", col->e_type, g_hash_table_size(col->hash_of_entities));
+    PINFO("Hash Table size of %s before is %ld", col->e_type, col->guid_entity_map.size());
 
-    entries = g_hash_table_get_values (col->hash_of_entities);
+    std::vector<QofInstance*> entries (col->guid_entity_map.size());
+    std::transform (col->guid_entity_map.cbegin(), col->guid_entity_map.cend(),
+                    entries.begin(), [](const auto& kv) { return kv.second; });
     if (sort_fn)
-        entries = g_list_sort (entries, sort_fn);
-    g_list_foreach (entries, (GFunc)cb_func, user_data);
-    g_list_free (entries);
+        std::sort (entries.begin(), entries.end(),
+                   [sort_fn](auto a, auto b) { return sort_fn (a, b) < 0; });
+    std::for_each (entries.cbegin(), entries.cend(),
+                   [&](auto ent) { cb_func (ent, user_data); });
 
-    PINFO("Hash Table size of %s after is %d", col->e_type, g_hash_table_size(col->hash_of_entities));
+    PINFO("Hash Table size of %s after is %ld", col->e_type, col->guid_entity_map.size());
 }
 
 void

commit 29d74209a7b6edbe00abf9501b7ffe59256dccfd
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Jun 14 12:05:47 2026 +0800

    [guid.cpp] create templated guid_hash function
    
    for arbitrary sized hashes

diff --git a/libgnucash/engine/guid.cpp b/libgnucash/engine/guid.cpp
index 0bd359a450..cb083dc913 100644
--- a/libgnucash/engine/guid.cpp
+++ b/libgnucash/engine/guid.cpp
@@ -254,15 +254,8 @@ guid_compare (const GncGUID *guid_1, const GncGUID *guid_2)
 guint
 guid_hash_to_guint (gconstpointer ptr)
 {
-    if (!ptr)
-    {
-        PERR ("received nullptr guid pointer.");
-        return 0;
-    }
-    const GncGUID* g = static_cast<const GncGUID*>(ptr);
-    guint rv;
-    memcpy (&rv, &g->reserved[12], sizeof (guint));
-    return rv;
+    g_return_val_if_fail (ptr, 0);
+    return gnc_guid_hash<guint> (*static_cast<const GncGUID*>(ptr));
 }
 
 gint
diff --git a/libgnucash/engine/guid.hpp b/libgnucash/engine/guid.hpp
index 7a883ecd9b..4f493ffd71 100644
--- a/libgnucash/engine/guid.hpp
+++ b/libgnucash/engine/guid.hpp
@@ -22,6 +22,7 @@
 #ifndef GUID_HPP_HEADER
 #define GUID_HPP_HEADER
 
+#include <boost/functional/hash.hpp>
 #include <boost/uuid/uuid.hpp>
 #include <stdexcept>
 #include <string>
@@ -68,4 +69,19 @@ bool operator == (GUID const &, GncGUID const &) noexcept;
 } // namespace gnc
 
 bool operator== (const GncGUID&, const GncGUID&);
+
+template <typename T>
+T
+gnc_guid_hash (const GncGUID& g)
+{
+    static_assert(std::is_integral_v<T>);
+    return static_cast<T>(boost::hash_range(g.reserved, g.reserved + sizeof (g.reserved)));
+};
+
+
+template<> struct std::hash<GncGUID>
+{
+    std::size_t operator()(const GncGUID& g) const{ return gnc_guid_hash<size_t> (g); }
+};
+
 #endif

commit 2a01ce23763756d7c3241a76bf08415193bd2d3b
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Tue Jun 16 13:57:56 2026 +0800

    [guid.cpp] plug char* guid_to_string leak
    
    it seems unused however

diff --git a/libgnucash/engine/guid.cpp b/libgnucash/engine/guid.cpp
index 946f3c1d03..0bd359a450 100644
--- a/libgnucash/engine/guid.cpp
+++ b/libgnucash/engine/guid.cpp
@@ -300,14 +300,14 @@ gnc_string_to_guid (const GValue *src, GValue *dest)
 static void
 gnc_guid_to_string (const GValue *src, GValue *dest)
 {
-    const gchar *str;
+    gchar *str;
 
     g_return_if_fail (G_VALUE_HOLDS_STRING (dest) &&
                       GNC_VALUE_HOLDS_GUID (src));
 
     str = guid_to_string (gnc_value_get_guid (src));
 
-    g_value_set_string (dest, str);
+    g_value_take_string (dest, str);
 }
 
 G_DEFINE_BOXED_TYPE_WITH_CODE (GncGUID, gnc_guid, guid_copy, guid_free,

commit b3e29a81fdfd784b1dc9229577fa289ff2c13afa
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sun Jun 14 10:30:02 2026 +0800

    [qofid.cpp] use ctor/dtor for QofCollection
    
    which will make it easier to construct in-place in book->hash_of_collections

diff --git a/libgnucash/engine/qofid.cpp b/libgnucash/engine/qofid.cpp
index 3e47ff9158..f649c281a6 100644
--- a/libgnucash/engine/qofid.cpp
+++ b/libgnucash/engine/qofid.cpp
@@ -40,6 +40,16 @@ struct QofCollection_s
 
     GHashTable * hash_of_entities;
     gpointer     data;       /* place where object class can hang arbitrary data */
+
+    QofCollection_s (QofIdType type) : e_type{static_cast<QofIdType>(CACHE_INSERT(type))}
+                                     , is_dirty{FALSE}
+                                     , hash_of_entities{guid_hash_table_new()}
+                                     , data{NULL} {}
+    ~QofCollection_s ()
+    {
+        CACHE_REMOVE (e_type);
+        g_hash_table_destroy (hash_of_entities);
+    }
 };
 
 /* =============================================================== */
@@ -47,24 +57,13 @@ struct QofCollection_s
 QofCollection *
 qof_collection_new (QofIdType type)
 {
-    QofCollection *col;
-    col = g_new0(QofCollection, 1);
-    col->e_type = static_cast<QofIdType>(CACHE_INSERT (type));
-    col->is_dirty = FALSE;
-    col->hash_of_entities = guid_hash_table_new();
-    col->data = NULL;
-    return col;
+    return new QofCollection (type);
 }
 
 void
 qof_collection_destroy (QofCollection *col)
 {
-    CACHE_REMOVE (col->e_type);
-    g_hash_table_destroy(col->hash_of_entities);
-    col->e_type = NULL;
-    col->hash_of_entities = NULL;
-    col->data = NULL;   /** XXX there should be a destroy notifier for this */
-    g_free (col);
+    delete col;
 }
 
 /* =============================================================== */



Summary of changes:
 libgnucash/engine/gnc-uri.cpp |  9 ++---
 libgnucash/engine/guid.cpp    | 11 ++----
 libgnucash/engine/guid.hpp    | 16 +++++++++
 libgnucash/engine/qofid.cpp   | 80 ++++++++++++++++++-------------------------
 4 files changed, 57 insertions(+), 59 deletions(-)



More information about the gnucash-changes mailing list