gnucash stable: Multiple changes pushed

Christopher Lam clam at code.gnucash.org
Fri Nov 28 18:58:38 EST 2025


Updated	 via  https://github.com/Gnucash/gnucash/commit/534bea74 (commit)
	 via  https://github.com/Gnucash/gnucash/commit/3c5a1801 (commit)
	from  https://github.com/Gnucash/gnucash/commit/7e5f5597 (commit)



commit 534bea748f556097c45695180d4adb71c359f9bf
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Sat Nov 29 07:57:14 2025 +0800

    [guid.cpp] Specialise guid<->32-byte string
    
    - string->guid has branch-lite unrollable loop, fallback to slow
      boost parser
    - guid->string uses lookup table
    - avoids creating several temporary heap-allocated std::strings

diff --git a/libgnucash/engine/guid.cpp b/libgnucash/engine/guid.cpp
index c65ae7261a..0fd36fd3d7 100644
--- a/libgnucash/engine/guid.cpp
+++ b/libgnucash/engine/guid.cpp
@@ -87,6 +87,41 @@ GncGUID * guid_convert_create (gnc::GUID const &);
 static gnc::GUID s_null_guid {boost::uuids::uuid { {0}}};
 static GncGUID * s_null_gncguid {guid_convert_create (s_null_guid)};
 
+static inline int
+char_to_num (unsigned char c) noexcept
+{
+    unsigned int digit = c - '0';
+    unsigned int alpha = (c | 0x20) - 'a';
+    return digit <= 9 ? digit : alpha <= 6 ? alpha + 10 : -1;
+}
+
+static inline bool
+fast_string_to_guid (const char* s, uint8_t* out) noexcept
+{
+    if (strnlen (s, GUID_ENCODING_LENGTH + 1) != GUID_ENCODING_LENGTH) return false;
+    bool all_ok = true;
+    for (int i = 0; i < GUID_DATA_SIZE; i++)
+    {
+        int hi = char_to_num (*s++);
+        int lo = char_to_num (*s++);
+        all_ok &= (hi >= 0 && lo >= 0);
+        out[i] = (unsigned char)((hi << 4) | lo);
+    }
+    return all_ok;
+}
+
+static inline void
+fast_guid_to_string (const uint8_t* src, char* dest) noexcept
+{
+    static constexpr char hex[] = "0123456789abcdef";
+    for (size_t i = 0; i < 16; i++)
+    {
+        uint8_t b = src[i];
+        *dest++ = hex[b >> 4];
+        *dest++ = hex[b & 0x0F];
+    }
+}
+
 /* Memory management routines ***************************************/
 
 /**
@@ -164,23 +199,18 @@ gchar *
 guid_to_string (const GncGUID * guid)
 {
     if (!guid) return nullptr;
-    gnc::GUID temp {*guid};
-    auto temp_str = temp.to_string ();
-    return g_strdup (temp_str.c_str ());
+    char* buffer = g_new (char, GUID_ENCODING_LENGTH + 1);
+    guid_to_string_buff (guid, buffer);
+    return buffer;
 }
 
 gchar *
 guid_to_string_buff (const GncGUID * guid, gchar *str)
 {
     if (!str || !guid) return nullptr;
-
-    gnc::GUID temp {*guid};
-    auto val = temp.to_string ();
-    /*We need to be sure to copy the terminating null character.
-     * The standard guarantees that std::basic_string::c_str ()
-     * returns with a terminating null character, too.*/
-    std::copy (val.c_str (), val.c_str () + val.size () + 1, str);
-    return str + val.size ();
+    fast_guid_to_string (guid->reserved, str);
+    str[GUID_ENCODING_LENGTH] = '\0';
+    return str;
 }
 
 gboolean
@@ -188,6 +218,9 @@ string_to_guid (const char * str, GncGUID * guid)
 {
     if (!guid || !str || !*str) return false;
 
+    if (fast_string_to_guid (str, guid->reserved))
+        return true;
+
     try
     {
         guid_assign (*guid, gnc::GUID::from_string (str));
@@ -323,12 +356,10 @@ GUID::null_guid () noexcept
 std::string
 GUID::to_string () const noexcept
 {
-    auto const & val = boost::uuids::to_string (implementation);
-    std::string ret;
-    std::for_each (val.begin (), val.end (), [&ret] (char a) {
-        if (a != '-') ret.push_back (a);
-    });
-    return ret;
+    std::string out;
+    out.resize (implementation.size() * 2);
+    fast_guid_to_string (implementation.data, out.data());
+    return out;
 }
 
 GUID
@@ -336,6 +367,14 @@ GUID::from_string (const char* str)
 {
     if (!str)
         throw guid_syntax_exception {};
+
+    uint8_t bytes[16];
+    if (fast_string_to_guid(str, bytes))
+    {
+        boost::uuids::uuid u;
+        std::memcpy(u.data, bytes, 16);
+        return GUID{u};
+    }
     try
     {
         static boost::uuids::string_generator strgen;
@@ -350,6 +389,9 @@ GUID::from_string (const char* str)
 bool
 GUID::is_valid_guid (const char* str)
 {
+    uint8_t bytes[16];
+    if (fast_string_to_guid(str, bytes))
+        return true;
     try
     {
         static boost::uuids::string_generator strgen;

commit 3c5a1801e29ed824287e95fdca57084c965e757a
Author: Christopher Lam <christopher.lck at gmail.com>
Date:   Fri Nov 28 00:31:12 2025 +0800

    [guid.cpp] fix guid==nullptr comparison retvals
    
    - if guid_1 guid_2 are both null they should return int(0) or bool(true)

diff --git a/libgnucash/engine/guid.cpp b/libgnucash/engine/guid.cpp
index 9526a06778..c65ae7261a 100644
--- a/libgnucash/engine/guid.cpp
+++ b/libgnucash/engine/guid.cpp
@@ -203,8 +203,8 @@ string_to_guid (const char * str, GncGUID * guid)
 gboolean
 guid_equal (const GncGUID *guid_1, const GncGUID *guid_2)
 {
-    if (!guid_1 || !guid_2)
-        return !guid_1 && !guid_2;
+    if (guid_1 == guid_2) return true;
+    if (!guid_1 || !guid_2) return false;
     gnc::GUID temp1 {*guid_1};
     gnc::GUID temp2 {*guid_2};
     return temp1 == temp2;
@@ -213,8 +213,9 @@ guid_equal (const GncGUID *guid_1, const GncGUID *guid_2)
 gint
 guid_compare (const GncGUID *guid_1, const GncGUID *guid_2)
 {
-    if (!guid_1 || !guid_2)
-        return !guid_1 && !guid_2;
+    if (guid_1 == guid_2) return 0;
+    if (!guid_1) return -1;
+    if (!guid_2) return 1;
     gnc::GUID temp1 {*guid_1};
     gnc::GUID temp2 {*guid_2};
     if (temp1 < temp2)



Summary of changes:
 libgnucash/engine/guid.cpp | 85 ++++++++++++++++++++++++++++++++++------------
 1 file changed, 64 insertions(+), 21 deletions(-)



More information about the gnucash-changes mailing list