LCOV - code coverage report
Current view: top level - libgnucash/engine - kvp-value.cpp (source / functions) Coverage Total Hit
Test: gnucash.info Lines: 77.2 % 193 149
Test Date: 2026-11-02 18:19:43 Functions: 30.4 % 115 35
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /********************************************************************\
       2              :  * kvp-value.cpp -- Implements a key-value frame system             *
       3              :  * Copyright (C) 2014 Aaron Laws                                    *
       4              :  *                                                                  *
       5              :  * This program is free software; you can redistribute it and/or    *
       6              :  * modify it under the terms of the GNU General Public License as   *
       7              :  * published by the Free Software Foundation; either version 2 of   *
       8              :  * the License, or (at your option) any later version.              *
       9              :  *                                                                  *
      10              :  * This program is distributed in the hope that it will be useful,  *
      11              :  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
      12              :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
      13              :  * GNU General Public License for more details.                     *
      14              :  *                                                                  *
      15              :  * You should have received a copy of the GNU General Public License*
      16              :  * along with this program; if not, contact:                        *
      17              :  *                                                                  *
      18              :  * Free Software Foundation           Voice:  +1-617-542-5942       *
      19              :  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
      20              :  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
      21              :  *                                                                  *
      22              : \********************************************************************/
      23              : 
      24              : #include "kvp-value.hpp"
      25              : #include "kvp-frame.hpp"
      26              : #include <cmath>
      27              : 
      28              : #include <sstream>
      29              : #include <iomanip>
      30              : #include <stdexcept>
      31              : 
      32              : using boost::typeindex::type_id;
      33              : 
      34        14866 : KvpValueImpl::KvpValueImpl(KvpValueImpl const & other) noexcept
      35              : {
      36        14866 :     duplicate(other);
      37        14866 : }
      38              : 
      39              : KvpValueImpl&
      40            0 : KvpValueImpl::operator=(KvpValueImpl const & other) noexcept
      41              : {
      42            0 :     duplicate(other);
      43            0 :     return *this;
      44              : }
      45              : 
      46            0 : KvpValueImpl::KvpValueImpl(KvpValueImpl && b) noexcept
      47              : {
      48            0 :     datastore = b.datastore;
      49            0 :     b.datastore = INT64_C(0);
      50            0 : }
      51              : 
      52              : KvpValueImpl&
      53            0 : KvpValueImpl::operator=(KvpValueImpl && b) noexcept
      54              : {
      55            0 :     std::swap (datastore, b.datastore);
      56            0 :     return *this;
      57              : }
      58              : 
      59              : KvpValueImpl *
      60            0 : KvpValueImpl::add(KvpValueImpl * val) noexcept
      61              : {
      62              :     /* If already a glist here, just append */
      63            0 :     if (this->datastore.type() == type_id<GList*>())
      64              :     {
      65            0 :         GList * list = boost::get<GList*>(datastore);
      66            0 :         datastore = g_list_append (list, val);
      67            0 :         return this;
      68              :     }
      69              :     /* If some other value, convert it to a glist */
      70            0 :     GList *list = nullptr;
      71              : 
      72            0 :     list = g_list_append (list, this);
      73            0 :     list = g_list_append (list, val);
      74            0 :     return new KvpValueImpl(list);
      75              : }
      76              : 
      77              : KvpValue::Type
      78        18487 : KvpValueImpl::get_type() const noexcept
      79              : {
      80        18487 :     if (datastore.type() == type_id<int64_t>())
      81         5800 :         return KvpValue::Type::INT64;
      82        12687 :     else if (datastore.type() == type_id<double>())
      83           89 :         return KvpValue::Type::DOUBLE;
      84        12598 :     else if (datastore.type() == type_id<gnc_numeric>())
      85         2547 :         return KvpValue::Type::NUMERIC;
      86        10051 :     else if (datastore.type() == type_id<const gchar *>())
      87         3280 :         return KvpValue::Type::STRING;
      88         6771 :     else if (datastore.type() == type_id<GncGUID *>())
      89         3248 :         return KvpValue::Type::GUID;
      90         3523 :     else if (datastore.type() == type_id<Time64>())
      91           46 :         return KvpValue::Type::TIME64;
      92         3477 :     else if (datastore.type() == type_id<GList *>())
      93         1526 :         return KvpValue::Type::GLIST;
      94         1951 :     else if (datastore.type() == type_id<KvpFrameImpl *>())
      95         1947 :         return KvpValue::Type::FRAME;
      96            4 :     else if (datastore.type() == type_id<GDate>())
      97            4 :         return KvpValue::Type::GDATE;
      98              : 
      99            0 :     return KvpValue::Type::INVALID;
     100              : }
     101              : 
     102              : struct to_string_visitor : boost::static_visitor<void>
     103              : {
     104              :     std::ostringstream & output;
     105              : 
     106          207 :     to_string_visitor(std::ostringstream & val) : output(val){}
     107              : 
     108           69 :     void operator()(int64_t val)
     109              :     {
     110           69 :         output << val << " (64-bit int)";
     111           69 :     }
     112              : 
     113            0 :     void operator()(KvpFrame* val)
     114              :     {
     115            0 :         output << val->to_string();
     116            0 :     }
     117              : 
     118            0 :     void operator()(GDate val)
     119              :     {
     120            0 :         output << std::setw(4) << g_date_get_year(&val) << '-';
     121            0 :         output << std::setw(2) << g_date_get_month(&val) << '-';
     122            0 :         output << std::setw(2) << g_date_get_day(&val);
     123            0 :         output << " (gdate)";
     124            0 :     }
     125              : 
     126           26 :     void operator()(GList * val)
     127              :     {
     128           26 :         output << "KVP_VALUE_GLIST(";
     129           26 :         output << "[ ";
     130              :         /*Since val is passed by value, we can modify it*/
     131          109 :         for (;val; val = val->next)
     132              :         {
     133           83 :             auto realvalue = static_cast<const KvpValue *>(val->data);
     134           83 :             output << ' ' << realvalue->to_string() << ',';
     135              :         }
     136           26 :         output << " ]";
     137           26 :         output << ")";
     138           26 :     }
     139              : 
     140            0 :     void operator()(Time64 val)
     141              :     {
     142            0 :         char tmp1[MAX_DATE_LENGTH + 1] {};
     143            0 :         gnc_time64_to_iso8601_buff (val.t, tmp1);
     144            0 :         output << tmp1 << " (time64)";
     145            0 :     }
     146              : 
     147           35 :     void operator()(gnc_numeric val)
     148              :     {
     149           35 :         auto tmp1 = gnc_numeric_to_string(val);
     150           35 :         if (tmp1)
     151              :         {
     152           35 :             output << tmp1;
     153           35 :             g_free(tmp1);
     154              :         }
     155              :         else
     156              :         {
     157            0 :             output << "(null)";
     158              :         }
     159           35 :         output << " (gnc_numeric)";
     160           35 :     }
     161              : 
     162           31 :     void operator()(GncGUID * val)
     163              :     {
     164              :         char guidstr[GUID_ENCODING_LENGTH+1];
     165           31 :         if (val)
     166              :         {
     167           31 :             guid_to_string_buff(val,guidstr);
     168           31 :             output << guidstr;
     169              :         }
     170              :         else
     171              :         {
     172            0 :             output << "(null)";
     173              :         }
     174           31 :         output << " (guid)";
     175           31 :     }
     176              : 
     177           44 :     void operator()(const char * val)
     178              :     {
     179           44 :         output << val << " (char *)";
     180           44 :     }
     181              : 
     182            2 :     void operator()(double val)
     183              :     {
     184            2 :         output << val << " (double)";
     185            2 :     }
     186              : };
     187              : 
     188              : std::string
     189          224 : KvpValueImpl::to_string(std::string const & prefix) const noexcept
     190              : {
     191          224 :     if (this->datastore.type() == type_id<KvpFrame*>())
     192           17 :         return this->get<KvpFrame*>()->to_string(prefix);
     193          207 :     std::ostringstream ret;
     194          207 :     to_string_visitor visitor {ret};
     195          207 :     boost::apply_visitor(visitor, datastore);
     196              :     /*We still use g_strdup since the return value will be freed by g_free*/
     197          207 :     return prefix + ret.str();
     198          207 : }
     199              : 
     200              : std::string
     201           83 : KvpValueImpl::to_string() const noexcept
     202              : {
     203          166 :     return to_string("");
     204              : }
     205              : 
     206              : static int
     207          509 : kvp_glist_compare(const GList * list1, const GList * list2)
     208              : {
     209              :     const GList *lp1;
     210              :     const GList *lp2;
     211              : 
     212          509 :     if (list1 == list2) return 0;
     213              : 
     214              :     /* Nothing is always less than something */
     215          509 :     if (!list1 && list2) return -1;
     216          509 :     if (list1 && !list2) return 1;
     217              : 
     218          509 :     lp1 = list1;
     219          509 :     lp2 = list2;
     220         2068 :     while (lp1 && lp2)
     221              :     {
     222         1559 :         KvpValue *v1 = (KvpValue *) lp1->data;
     223         1559 :         KvpValue *v2 = (KvpValue *) lp2->data;
     224         1559 :         gint vcmp = compare(v1, v2);
     225         1559 :         if (vcmp != 0) return vcmp;
     226         1559 :         lp1 = lp1->next;
     227         1559 :         lp2 = lp2->next;
     228              :     }
     229          509 :     if (!lp1 && lp2) return -1;
     230          509 :     if (!lp2 && lp1) return 1;
     231          509 :     return 0;
     232              : }
     233              : 
     234              : static GList *
     235         1362 : kvp_glist_copy(const GList * list)
     236              : {
     237         1362 :     GList * retval = NULL;
     238              :     GList * lptr;
     239              : 
     240         1362 :     if (!list) return retval;
     241              : 
     242              :     /* Duplicate the backbone of the list (this duplicates the POINTERS
     243              :      * to the values; we need to deep-copy the values separately) */
     244         1362 :     retval = g_list_copy((GList *) list);
     245              : 
     246              :     /* This step deep-copies the values */
     247         5463 :     for (lptr = retval; lptr; lptr = lptr->next)
     248              :     {
     249         4101 :         lptr->data = new KvpValue(*static_cast<KvpValue *>(lptr->data));
     250              :     }
     251              : 
     252         1362 :     return retval;
     253              : }
     254              : 
     255              : struct compare_visitor : boost::static_visitor<int>
     256              : {
     257              :     template <typename T, typename U>
     258            0 :     int operator()( T& one, U& two) const
     259              :     {
     260            0 :         throw std::invalid_argument{"You may not compare objects of different type."};
     261              :     }
     262              : 
     263              :     template <typename T>
     264         1765 :     int operator()( T & one, T & two) const
     265              :     {
     266              :         /*This will work for any type that implements < and ==.*/
     267         1765 :         if (one < two)
     268            0 :             return -1;
     269         1765 :         if (two < one)
     270            0 :             return 1;
     271         1765 :         return 0;
     272              :     }
     273              : };
     274          987 : template <> int compare_visitor::operator()(const char * const & one, const char * const & two) const
     275              : {
     276          987 :     return strcmp(one, two);
     277              : }
     278          803 : template <> int compare_visitor::operator()(gnc_numeric const & one, gnc_numeric const & two) const
     279              : {
     280          803 :     return gnc_numeric_compare(one, two);
     281              : }
     282          904 : template <> int compare_visitor::operator()(GncGUID * const & one, GncGUID * const & two) const
     283              : {
     284          904 :     return guid_compare(one, two);
     285              : }
     286            2 : template <> int compare_visitor::operator()(Time64 const & one, Time64 const & two) const
     287              : {
     288            2 :     return one.t < two.t ? -1 : one.t > two.t ? 1 : 0;
     289              : }
     290            0 : template <> int compare_visitor::operator()(GDate const & one, GDate const & two) const
     291              : {
     292            0 :     return g_date_compare(&one,&two);
     293              : }
     294          509 : template <> int compare_visitor::operator()(GList * const & one, GList * const & two) const
     295              : {
     296          509 :     return kvp_glist_compare(one, two);
     297              : }
     298          539 : template <> int compare_visitor::operator()(KvpFrame * const & one, KvpFrame * const & two) const
     299              : {
     300          539 :     return compare(one, two);
     301              : }
     302           29 : template <> int compare_visitor::operator()(double const & one, double const & two) const
     303              : {
     304           29 :     if (std::isnan(one) && std::isnan(two))
     305            0 :         return 0;
     306           29 :     if (one < two) return -1;
     307           29 :     if (two < one) return 1;
     308           28 :     return 0;
     309              : }
     310              : 
     311         5538 : int compare(const KvpValueImpl & one, const KvpValueImpl & two) noexcept
     312              : {
     313         5538 :     auto type1 = one.get_type();
     314         5538 :     auto type2 = two.get_type();
     315              : 
     316         5538 :     if (type1 != type2)
     317            0 :         return type1 < type2 ? -1 : 1;
     318              : 
     319              :     compare_visitor comparer;
     320         5538 :     return boost::apply_visitor(comparer, one.datastore, two.datastore);
     321              : }
     322              : 
     323              : int
     324         5565 : compare(const KvpValueImpl * one, const KvpValueImpl * two) noexcept
     325              : {
     326         5565 :     if (one == two) return 0;
     327         5538 :     if (one && !two) return 1;
     328         5538 :     if (!one && two) return -1;
     329         5538 :     assert (one && two);  /* Silence a static analysis warning. */
     330         5538 :     return compare(*one, *two);
     331              : }
     332              : 
     333              : struct delete_visitor : boost::static_visitor<void>
     334              : {
     335              :     template <typename T> void 
     336        16899 :     operator()(T &) { /*do nothing*/ }
     337              : };
     338              : 
     339              : static void
     340         9162 : destroy_value(void* item)
     341              : {
     342         9162 :     delete static_cast<KvpValue*>(item);
     343         9162 : }
     344              : 
     345              : template <> void
     346         3046 : delete_visitor::operator()(GList * & value)
     347              : {
     348         3046 :     g_list_free_full(value, destroy_value);
     349         3046 : }
     350              : template <> void
     351         6023 : delete_visitor::operator()(const gchar * & value)
     352              : {
     353         6023 :     g_free(const_cast<gchar *> (value));
     354         6023 : }
     355              : template <> void
     356         5295 : delete_visitor::operator()(GncGUID * & value)
     357              : {
     358         5295 :     guid_free(value);
     359         5295 : }
     360              : template <> void
     361         3066 : delete_visitor::operator()(KvpFrame * & value)
     362              : {
     363         3066 :     delete value;
     364         3066 : }
     365              : 
     366        34329 : KvpValueImpl::~KvpValueImpl() noexcept
     367              : {
     368              :     delete_visitor d;
     369        34329 :     boost::apply_visitor(d, datastore);
     370        34329 : }
     371              : 
     372              : void
     373        14866 : KvpValueImpl::duplicate(const KvpValueImpl& other) noexcept
     374              : {
     375        14866 :     if (other.datastore.type() == type_id<const gchar *>())
     376         5056 :         this->datastore = const_cast<const gchar *>(g_strdup(other.get<const gchar *>()));
     377        12338 :     else if (other.datastore.type() == type_id<GncGUID*>())
     378         2412 :         this->datastore = guid_copy(other.get<GncGUID *>());
     379         9926 :     else if (other.datastore.type() == type_id<GList*>())
     380         1362 :         this->datastore = kvp_glist_copy(other.get<GList *>());
     381         8564 :     else if (other.datastore.type() == type_id<KvpFrame*>())
     382         1451 :         this->datastore = new KvpFrame(*other.get<KvpFrame *>());
     383              :     else
     384         7113 :         this->datastore = other.datastore;
     385        14866 : }
     386              : 
        

Generated by: LCOV version 2.0-1