LCOV - code coverage report
Current view: top level - libgnucash/engine - guid.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 75.8 % 165 125
Test Date: 2026-11-02 18:19:43 Functions: 82.9 % 41 34
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /********************************************************************\
       2              :  * guid.c -- globally unique ID implementation                      *
       3              :  * Copyright (C) 2000 Dave Peticolas <peticola@cs.ucdavis.edu>      *
       4              :  * Copyright (C) 2014 Aaron Laws <dartmetrash@gmail.com>            *
       5              :  *                                                                  *
       6              :  * This program is free software; you can redistribute it and/or    *
       7              :  * modify it under the terms of the GNU General Public License as   *
       8              :  * published by the Free Software Foundation; either version 2 of   *
       9              :  * the License, or (at your option) any later version.              *
      10              :  *                                                                  *
      11              :  * This program is distributed in the hope that it will be useful,  *
      12              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      13              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      14              :  * GNU General Public License for more details.                     *
      15              :  *                                                                  *
      16              :  * You should have received a copy of the GNU General Public License*
      17              :  * along with this program; if not, contact:                        *
      18              :  *                                                                  *
      19              :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      20              :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      21              :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      22              :  *                                                                  *
      23              : \********************************************************************/
      24              : 
      25              : #include "guid.hpp"
      26              : #include "guid.h"
      27              : 
      28              : #ifdef HAVE_CONFIG_H
      29              : # include <config.h>
      30              : #endif
      31              : 
      32              : #ifdef HAVE_SYS_TYPES_H
      33              : # include <sys/types.h>
      34              : #endif
      35              : #include <ctype.h>
      36              : #include <stdint.h>
      37              : #ifdef HAVE_DIRENT_H
      38              : # include <dirent.h>
      39              : #endif
      40              : #include <glib.h>
      41              : #include <glib/gstdio.h>
      42              : #include <stdlib.h>
      43              : #include <string.h>
      44              : #include <sys/stat.h>
      45              : #ifdef HAVE_SYS_TIMES_H
      46              : # include <sys/times.h>
      47              : #endif
      48              : #include <time.h>
      49              : #ifdef HAVE_UNISTD_H
      50              : # include <unistd.h>
      51              : #endif
      52              : #include "qof.h"
      53              : 
      54              : #include <boost/uuid/uuid.hpp>
      55              : #include <boost/uuid/uuid_generators.hpp>
      56              : #include <boost/uuid/uuid_io.hpp>
      57              : #include <sstream>
      58              : #include <string>
      59              : #include <algorithm>
      60              : 
      61              : /* This static indicates the debugging module that this .o belongs to.  */
      62              : static QofLogModule log_module = QOF_MOD_ENGINE;
      63              : 
      64              : /**
      65              :  * gnc_value_get_guid
      66              :  *
      67              :  * @param value a @c GValue whose value we want to get.
      68              :  *
      69              :  * @return the value stored in @a value
      70              :  */
      71              : const GncGUID*
      72            0 : gnc_value_get_guid (const GValue *value)
      73              : {
      74            0 :     if (!value) return nullptr;
      75              :     GncGUID *val;
      76              : 
      77            0 :     g_return_val_if_fail (value && G_IS_VALUE (value), nullptr);
      78            0 :     g_return_val_if_fail (GNC_VALUE_HOLDS_GUID (value), nullptr);
      79              : 
      80            0 :     val = (GncGUID*) g_value_get_boxed (value);
      81              : 
      82            0 :     return val;
      83              : }
      84              : 
      85              : GncGUID * guid_convert_create (gnc::GUID const &);
      86              : 
      87              : static gnc::GUID s_null_guid {boost::uuids::uuid { {0}}};
      88              : static GncGUID * s_null_gncguid {guid_convert_create (s_null_guid)};
      89              : 
      90              : static inline int
      91       334080 : char_to_num (unsigned char c) noexcept
      92              : {
      93       334080 :     unsigned int digit = c - '0';
      94       334080 :     unsigned int alpha = (c | 0x20) - 'a';
      95       334080 :     return digit <= 9 ? digit : alpha <= 5 ? alpha + 10 : -1;
      96              : }
      97              : 
      98              : static inline bool
      99        10446 : fast_string_to_guid (const char* s, uint8_t* out) noexcept
     100              : {
     101        10446 :     if (strnlen (s, GUID_ENCODING_LENGTH + 1) != GUID_ENCODING_LENGTH) return false;
     102        10440 :     bool all_ok = true;
     103       177480 :     for (int i = 0; i < GUID_DATA_SIZE; i++)
     104              :     {
     105       167040 :         int hi = char_to_num (*s++);
     106       167040 :         int lo = char_to_num (*s++);
     107       167040 :         all_ok &= (hi >= 0 && lo >= 0);
     108       167040 :         out[i] = (unsigned char)(((unsigned)hi << 4) | (unsigned)lo);
     109              :     }
     110        10440 :     return all_ok;
     111              : }
     112              : 
     113              : static inline void
     114        33553 : fast_guid_to_string (const uint8_t* src, char* dest) noexcept
     115              : {
     116              :     static constexpr char hex[] = "0123456789abcdef";
     117       570401 :     for (size_t i = 0; i < 16; i++)
     118              :     {
     119       536848 :         uint8_t b = src[i];
     120       536848 :         *dest++ = hex[b >> 4];
     121       536848 :         *dest++ = hex[b & 0x0F];
     122              :     }
     123        33553 : }
     124              : 
     125              : /* Memory management routines ***************************************/
     126              : 
     127              : /**
     128              :  * Allocates and returns a new GncGUID containing the same value as the
     129              :  * gnc::GUID passed in.
     130              :  */
     131              : GncGUID *
     132          118 : guid_convert_create (gnc::GUID const & guid)
     133              : {
     134          118 :     GncGUID temp = guid;
     135          236 :     return guid_copy (&temp);
     136              : }
     137              : 
     138              : GncGUID *
     139        41744 : guid_malloc (void)
     140              : {
     141        41744 :     return new GncGUID;
     142              : }
     143              : 
     144              : void
     145        41705 : guid_free (GncGUID *guid)
     146              : {
     147        41705 :     if (!guid) return;
     148        40500 :     if (guid == s_null_gncguid)
     149              :         /* Don't delete that! */
     150            0 :         return;
     151        40500 :     delete guid;
     152              : }
     153              : 
     154              : GncGUID *
     155         9211 : guid_copy (const GncGUID *guid)
     156              : {
     157         9211 :     if (!guid) return nullptr;
     158         9211 :     auto ret = guid_malloc ();
     159         9211 :     *ret = *guid;
     160         9211 :     return ret;
     161              : }
     162              : 
     163              : /*It looks like we are expected to provide the same pointer every time from this function*/
     164              : const GncGUID *
     165       155793 : guid_null (void)
     166              : {
     167       155793 :     return s_null_gncguid;
     168              : }
     169              : 
     170              : static void
     171       151185 : guid_assign (GncGUID & target, gnc::GUID const & source)
     172              : {
     173       151185 :     std::copy (source.begin(), source.end(), target.reserved);
     174       151185 : }
     175              : 
     176              : /*Takes an allocated guid pointer and constructs it in place*/
     177              : void
     178       145997 : guid_replace (GncGUID *guid)
     179              : {
     180       145997 :     if (!guid) return;
     181       145997 :     gnc::GUID temp_random {gnc::GUID::create_random ()};
     182       145997 :     guid_assign (*guid, temp_random);
     183              : }
     184              : 
     185              : GncGUID *
     186         4991 : guid_new (void)
     187              : {
     188         4991 :     auto ret = guid_new_return ();
     189         9982 :     return guid_copy (&ret);
     190              : }
     191              : 
     192              : GncGUID
     193         5056 : guid_new_return (void)
     194              : {
     195         5056 :     return gnc::GUID::create_random ();
     196              : }
     197              : 
     198              : gchar *
     199          450 : guid_to_string (const GncGUID * guid)
     200              : {
     201          450 :     if (!guid) return nullptr;
     202          450 :     char* buffer = g_new (char, GUID_ENCODING_LENGTH + 1);
     203          450 :     guid_to_string_buff (guid, buffer);
     204          450 :     return buffer;
     205              : }
     206              : 
     207              : gchar *
     208        32747 : guid_to_string_buff (const GncGUID * guid, gchar *str)
     209              : {
     210        32747 :     if (!str || !guid) return nullptr;
     211        32747 :     fast_guid_to_string (guid->reserved, str);
     212        32747 :     str[GUID_ENCODING_LENGTH] = '\0';
     213        32747 :     return str;
     214              : }
     215              : 
     216              : gboolean
     217        10554 : string_to_guid (const char * str, GncGUID * guid)
     218              : {
     219        10554 :     if (!guid || !str || !*str) return false;
     220              : 
     221        10425 :     if (fast_string_to_guid (str, guid->reserved))
     222        10425 :         return true;
     223              : 
     224              :     try
     225              :     {
     226            0 :         guid_assign (*guid, gnc::GUID::from_string (str));
     227              :     }
     228            0 :     catch (...)
     229              :     {
     230            0 :         PINFO("Failed to construct a GUID from %s", str);
     231            0 :         return false;
     232            0 :     }
     233            0 :     return true;
     234              : }
     235              : 
     236              : gboolean
     237       445429 : guid_equal (const GncGUID *guid_1, const GncGUID *guid_2)
     238              : {
     239       445429 :     return guid_compare (guid_1, guid_2) == 0;
     240              : }
     241              : 
     242              : gint
     243       453384 : guid_compare (const GncGUID *guid_1, const GncGUID *guid_2)
     244              : {
     245       453384 :     if (guid_1 == guid_2) return 0;
     246       342052 :     if (!guid_1) return -1;
     247       342052 :     if (!guid_2) return 1;
     248       341981 :     return std::memcmp (guid_1->reserved, guid_2->reserved, GUID_DATA_SIZE);
     249              : }
     250              : 
     251              : // returns a 32-bit hash from 32-byte guid. since guid are generated
     252              : // randomly, this is not expected to cause hash collisions. use memcpy
     253              : // to avoid alignment issues; memcpy likely to be optimised away.
     254              : guint
     255       560249 : guid_hash_to_guint (gconstpointer ptr)
     256              : {
     257       560249 :     if (!ptr)
     258              :     {
     259            0 :         PERR ("received nullptr guid pointer.");
     260            0 :         return 0;
     261              :     }
     262       560249 :     const GncGUID* g = static_cast<const GncGUID*>(ptr);
     263              :     guint rv;
     264       560249 :     memcpy (&rv, &g->reserved[12], sizeof (guint));
     265       560249 :     return rv;
     266              : }
     267              : 
     268              : gint
     269       167605 : guid_g_hash_table_equal (gconstpointer guid_a, gconstpointer guid_b)
     270              : {
     271       167605 :     return guid_equal (reinterpret_cast<const GncGUID*> (guid_a),
     272       167605 :                        reinterpret_cast<const GncGUID*> (guid_b));
     273              : }
     274              : 
     275              : GHashTable *
     276         6729 : guid_hash_table_new (void)
     277              : {
     278         6729 :     return g_hash_table_new (guid_hash_to_guint, guid_g_hash_table_equal);
     279              : }
     280              : 
     281              : /***************************/
     282              : static void
     283            0 : gnc_string_to_guid (const GValue *src, GValue *dest)
     284              : {
     285              :     /* FIXME: add more checks*/
     286              :     GncGUID *guid;
     287              :     const gchar *as_string;
     288              : 
     289            0 :     g_return_if_fail (G_VALUE_HOLDS_STRING (src) &&
     290              :                       GNC_VALUE_HOLDS_GUID (dest));
     291              : 
     292            0 :     as_string = g_value_get_string (src);
     293              : 
     294            0 :     guid = g_new0 (GncGUID, 1);
     295            0 :     string_to_guid (as_string, guid);
     296              : 
     297            0 :     g_value_take_boxed (dest, guid);
     298              : }
     299              : 
     300              : static void
     301            0 : gnc_guid_to_string (const GValue *src, GValue *dest)
     302              : {
     303              :     gchar *str;
     304              : 
     305            0 :     g_return_if_fail (G_VALUE_HOLDS_STRING (dest) &&
     306              :                       GNC_VALUE_HOLDS_GUID (src));
     307              : 
     308            0 :     str = guid_to_string (gnc_value_get_guid (src));
     309              : 
     310            0 :     g_value_take_string (dest, str);
     311              : }
     312              : 
     313         1241 : G_DEFINE_BOXED_TYPE_WITH_CODE (GncGUID, gnc_guid, guid_copy, guid_free,
     314              :         g_value_register_transform_func (G_TYPE_STRING,
     315              :                                          g_define_type_id,
     316              :                                          gnc_string_to_guid);
     317              : 
     318              :         g_value_register_transform_func (g_define_type_id,
     319              :                                          G_TYPE_STRING,
     320              :                                          gnc_guid_to_string);
     321              :     )
     322              : 
     323              : namespace gnc
     324              : {
     325              : 
     326              : GUID
     327       151053 : GUID::create_random () noexcept
     328              : {
     329       151053 :     static boost::uuids::random_generator gen;
     330       151053 :     return {gen ()};
     331              : }
     332              : 
     333       151185 : GUID::GUID (boost::uuids::uuid const & other) noexcept
     334       151185 :     : implementation (other)
     335              : {
     336       151185 : }
     337              : 
     338              : GUID const &
     339            0 : GUID::null_guid () noexcept
     340              : {
     341            0 :     return s_null_guid;
     342              : }
     343              : 
     344              : std::string
     345          806 : GUID::to_string () const noexcept
     346              : {
     347          806 :     std::string out;
     348          806 :     out.resize (implementation.size() * 2);
     349          806 :     fast_guid_to_string (implementation.data, out.data());
     350          806 :     return out;
     351              : }
     352              : 
     353              : GUID
     354           14 : GUID::from_string (const char* str)
     355              : {
     356           14 :     if (!str)
     357            0 :         throw guid_syntax_exception {};
     358              : 
     359           14 :     if (boost::uuids::uuid u; fast_string_to_guid(str, u.data))
     360           14 :         return u;
     361              :     try
     362              :     {
     363              :         static boost::uuids::string_generator strgen;
     364            0 :         return strgen (str);
     365              :     }
     366            0 :     catch (...)
     367              :     {
     368            0 :         throw guid_syntax_exception {};
     369            0 :     }
     370              : }
     371              : 
     372              : bool
     373            7 : GUID::is_valid_guid (const char* str)
     374              : {
     375              :     uint8_t bytes[16];
     376            7 :     if (fast_string_to_guid(str, bytes))
     377            1 :         return true;
     378              :     try
     379              :     {
     380              :         static boost::uuids::string_generator strgen;
     381            6 :         strgen (str);
     382            0 :         return true;
     383              :     }
     384            6 :     catch (...)
     385              :     {
     386            6 :         return false;
     387            6 :     }
     388              : }
     389              : 
     390            0 : guid_syntax_exception::guid_syntax_exception () noexcept
     391            0 :     : invalid_argument {"Invalid syntax for guid."}
     392              : {
     393            0 : }
     394              : 
     395          830 : GUID::GUID (GncGUID const & other) noexcept
     396         1660 : : implementation {{other.reserved[0] , other.reserved[1]
     397          830 :             , other.reserved[2], other.reserved[3]
     398          830 :             , other.reserved[4], other.reserved[5]
     399          830 :             , other.reserved[6], other.reserved[7]
     400          830 :             , other.reserved[8], other.reserved[9]
     401          830 :             , other.reserved[10], other.reserved[11]
     402          830 :             , other.reserved[12], other.reserved[13]
     403          830 :             , other.reserved[14], other.reserved[15]}
     404              :     }
     405              : {
     406              : 
     407          830 : }
     408              : 
     409              : auto
     410       151185 : GUID::end () const noexcept -> decltype (implementation.end ())
     411              : {
     412       151185 :     return implementation.end ();
     413              : }
     414              : 
     415              : auto
     416       151185 : GUID::begin () const noexcept -> decltype (implementation.begin ())
     417              : {
     418       151185 :     return implementation.begin ();
     419              : }
     420              : 
     421              : bool
     422            0 : GUID::operator < (GUID const & other) noexcept
     423              : {
     424            0 :     return implementation < other.implementation;
     425              : }
     426              : 
     427            8 : bool operator == (GUID const & lhs, GncGUID const & rhs) noexcept
     428              : {
     429            8 :     return lhs.implementation == GUID(rhs).implementation;
     430              : }
     431              : 
     432              : bool
     433            0 : operator != (GUID const & one, GUID const & two) noexcept
     434              : {
     435            0 :     return one.implementation != two.implementation;
     436              : }
     437              : 
     438            8 : GUID & GUID::operator = (GUID && other) noexcept
     439              : {
     440            8 :     boost::uuids::swap (other.implementation, implementation);
     441            8 :     return *this;
     442              : }
     443              : 
     444         5188 : GUID::operator GncGUID () const noexcept
     445              : {
     446              :     GncGUID ret;
     447         5188 :     guid_assign (ret, *this);
     448         5188 :     return ret;
     449              : }
     450              : 
     451              : } // namespace gnc
     452              : 
     453              : bool
     454            8 : operator==(const GncGUID& lhs, const GncGUID& rhs)
     455              : {
     456            8 :     return gnc::GUID{lhs} == gnc::GUID{rhs};
     457              : }
        

Generated by: LCOV version 2.0-1