LCOV - code coverage report
Current view: top level - util - amount.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 300 375 80.0 %
Date: 2025-06-05 21:03:14 Functions: 22 27 81.5 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2021 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file util/amount.c
      18             :  * @brief Common utility functions to deal with units of currency
      19             :  * @author Sree Harsha Totakura <sreeharsha@totakura.in>
      20             :  * @author Florian Dold
      21             :  * @author Benedikt Mueller
      22             :  * @author Christian Grothoff
      23             :  */
      24             : #include "platform.h"
      25             : #include "taler_util.h"
      26             : 
      27             : 
      28             : /**
      29             :  * Set @a a to "invalid".
      30             :  *
      31             :  * @param[out] a amount to set to invalid
      32             :  */
      33             : static void
      34          24 : invalidate (struct TALER_Amount *a)
      35             : {
      36          24 :   memset (a,
      37             :           0,
      38             :           sizeof (struct TALER_Amount));
      39          24 : }
      40             : 
      41             : 
      42             : enum GNUNET_GenericReturnValue
      43      249986 : TALER_check_currency (const char *str)
      44             : {
      45      249986 :   size_t len = strlen (str);
      46      249986 :   if (len >= TALER_CURRENCY_LEN)
      47             :   {
      48           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      49             :                 "Currency code name `%s' is too long\n",
      50             :                 str);
      51           0 :     return GNUNET_SYSERR;
      52             :   }
      53      249986 :   if (len == 0)
      54             :   {
      55           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      56             :                 "Currency code name must be set\n");
      57           0 :     return GNUNET_SYSERR;
      58             :   }
      59             :   /* validate str has only legal characters in it! */
      60     1420644 :   for (unsigned int i = 0; '\0' != str[i]; i++)
      61             :   {
      62     1170658 :     if ( ('A' > str[i]) || ('Z' < str[i]) )
      63             :     {
      64           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      65             :                   "Currency code name `%s' contains illegal characters (only A-Z allowed)\n",
      66             :                   str);
      67           0 :       return GNUNET_SYSERR;
      68             :     }
      69             :   }
      70      249986 :   return GNUNET_OK;
      71             : }
      72             : 
      73             : 
      74             : enum GNUNET_GenericReturnValue
      75      161951 : TALER_string_to_amount (const char *str,
      76             :                         struct TALER_Amount *amount)
      77             : {
      78             :   int n;
      79             :   uint32_t b;
      80             :   const char *colon;
      81             :   const char *value;
      82             : 
      83             :   /* skip leading whitespace */
      84      161953 :   while (isspace ( (unsigned char) str[0]))
      85           2 :     str++;
      86      161951 :   if ('\0' == str[0])
      87             :   {
      88           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
      89             :                 "Null before currency\n");
      90           0 :     invalidate (amount);
      91           0 :     return GNUNET_SYSERR;
      92             :   }
      93             : 
      94             :   /* parse currency */
      95      161951 :   colon = strchr (str, (int) ':');
      96      161951 :   if ( (NULL == colon) ||
      97      161950 :        (colon == str) ||
      98      161950 :        ((colon - str) >= TALER_CURRENCY_LEN) )
      99             :   {
     100           1 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     101             :                 "Invalid currency specified before colon: `%s'\n",
     102             :                 str);
     103           1 :     invalidate (amount);
     104           1 :     return GNUNET_SYSERR;
     105             :   }
     106             : 
     107      161950 :   GNUNET_assert (TALER_CURRENCY_LEN > (colon - str));
     108      783794 :   for (unsigned int i = 0; i<colon - str; i++)
     109      621844 :     amount->currency[i] = str[i];
     110             :   /* 0-terminate *and* normalize buffer by setting everything to '\0' */
     111      161950 :   memset (&amount->currency [colon - str],
     112             :           0,
     113      161950 :           TALER_CURRENCY_LEN - (colon - str));
     114      161950 :   if (GNUNET_OK !=
     115      161950 :       TALER_check_currency (amount->currency))
     116           0 :     return GNUNET_SYSERR;
     117             :   /* skip colon */
     118      161950 :   value = colon + 1;
     119      161950 :   if ('\0' == value[0])
     120             :   {
     121           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     122             :                 "Actual value missing in amount `%s'\n",
     123             :                 str);
     124           0 :     invalidate (amount);
     125           0 :     return GNUNET_SYSERR;
     126             :   }
     127             : 
     128      161950 :   amount->value = 0;
     129      161950 :   amount->fraction = 0;
     130             : 
     131             :   /* parse value */
     132      331088 :   while ('.' != *value)
     133             :   {
     134      195308 :     if ('\0' == *value)
     135             :     {
     136             :       /* we are done */
     137       26168 :       return GNUNET_OK;
     138             :     }
     139      169140 :     if ( (*value < '0') ||
     140      169140 :          (*value > '9') )
     141             :     {
     142           1 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     143             :                   "Invalid character `%c' in amount `%s'\n",
     144             :                   (int) *value,
     145             :                   str);
     146           1 :       invalidate (amount);
     147           1 :       return GNUNET_SYSERR;
     148             :     }
     149      169139 :     n = *value - '0';
     150      169139 :     if ( (amount->value * 10 < amount->value) ||
     151      169139 :          (amount->value * 10 + n < amount->value) ||
     152      169139 :          (amount->value > TALER_AMOUNT_MAX_VALUE) ||
     153      169139 :          (amount->value * 10 + n > TALER_AMOUNT_MAX_VALUE) )
     154             :     {
     155           1 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     156             :                   "Value specified in amount `%s' is too large\n",
     157             :                   str);
     158           1 :       invalidate (amount);
     159           1 :       return GNUNET_SYSERR;
     160             :     }
     161      169138 :     amount->value = (amount->value * 10) + n;
     162      169138 :     value++;
     163             :   }
     164             : 
     165             :   /* skip the dot */
     166      135780 :   value++;
     167             : 
     168             :   /* parse fraction */
     169      135780 :   if ('\0' == *value)
     170             :   {
     171           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     172             :                 "Amount `%s' ends abruptly after `.'\n",
     173             :                 str);
     174           0 :     invalidate (amount);
     175           0 :     return GNUNET_SYSERR;
     176             :   }
     177      135780 :   b = TALER_AMOUNT_FRAC_BASE / 10;
     178      404908 :   while ('\0' != *value)
     179             :   {
     180      269131 :     if (0 == b)
     181             :     {
     182           1 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     183             :                   "Fractional value too small (only %u digits supported) in amount `%s'\n",
     184             :                   (unsigned int) TALER_AMOUNT_FRAC_LEN,
     185             :                   str);
     186           1 :       invalidate (amount);
     187           1 :       return GNUNET_SYSERR;
     188             :     }
     189      269130 :     if ( (*value < '0') ||
     190      269130 :          (*value > '9') )
     191             :     {
     192           2 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     193             :                   "Error after dot\n");
     194           2 :       invalidate (amount);
     195           2 :       return GNUNET_SYSERR;
     196             :     }
     197      269128 :     n = *value - '0';
     198      269128 :     amount->fraction += n * b;
     199      269128 :     b /= 10;
     200      269128 :     value++;
     201             :   }
     202      135777 :   return GNUNET_OK;
     203             : }
     204             : 
     205             : 
     206             : enum GNUNET_GenericReturnValue
     207           0 : TALER_string_to_amount_nbo (const char *str,
     208             :                             struct TALER_AmountNBO *amount_nbo)
     209             : {
     210             :   struct TALER_Amount amount;
     211             : 
     212           0 :   if (GNUNET_OK !=
     213           0 :       TALER_string_to_amount (str,
     214             :                               &amount))
     215           0 :     return GNUNET_SYSERR;
     216           0 :   TALER_amount_hton (amount_nbo,
     217             :                      &amount);
     218           0 :   return GNUNET_OK;
     219             : }
     220             : 
     221             : 
     222             : void
     223      468352 : TALER_amount_hton (struct TALER_AmountNBO *res,
     224             :                    const struct TALER_Amount *d)
     225             : {
     226      468352 :   GNUNET_assert (GNUNET_YES ==
     227             :                  TALER_amount_is_valid (d));
     228      468352 :   res->value = GNUNET_htonll (d->value);
     229      468352 :   res->fraction = htonl (d->fraction);
     230     6088576 :   for (unsigned int i = 0; i<TALER_CURRENCY_LEN; i++)
     231     5620224 :     res->currency[i] = d->currency[i];
     232      468352 : }
     233             : 
     234             : 
     235             : void
     236          72 : TALER_amount_ntoh (struct TALER_Amount *res,
     237             :                    const struct TALER_AmountNBO *dn)
     238             : {
     239          72 :   res->value = GNUNET_ntohll (dn->value);
     240          72 :   res->fraction = ntohl (dn->fraction);
     241          72 :   GNUNET_memcpy (res->currency,
     242             :                  dn->currency,
     243             :                  TALER_CURRENCY_LEN);
     244          72 :   GNUNET_assert (GNUNET_YES ==
     245             :                  TALER_amount_is_valid (res));
     246          72 : }
     247             : 
     248             : 
     249             : enum GNUNET_GenericReturnValue
     250       87859 : TALER_amount_set_zero (const char *cur,
     251             :                        struct TALER_Amount *amount)
     252             : {
     253             :   char tmp[TALER_CURRENCY_LEN];
     254             :   size_t slen;
     255             : 
     256       87859 :   if (GNUNET_OK !=
     257       87859 :       TALER_check_currency (cur))
     258           0 :     return GNUNET_SYSERR;
     259       87859 :   slen = strlen (cur);
     260             :   /* make a copy of 'cur' to 'tmp' as the memset may clobber cur
     261             :      if cur aliases &amount->currency! */
     262       87859 :   memcpy (tmp,
     263             :           cur,
     264             :           slen);
     265       87859 :   memset (amount,
     266             :           0,
     267             :           sizeof (struct TALER_Amount));
     268      635806 :   for (unsigned int i = 0; i<slen; i++)
     269      547947 :     amount->currency[i] = tmp[i];
     270       87859 :   return GNUNET_OK;
     271             : }
     272             : 
     273             : 
     274             : enum GNUNET_GenericReturnValue
     275     1476835 : TALER_amount_is_valid (const struct TALER_Amount *amount)
     276             : {
     277     1476835 :   if (amount->value > TALER_AMOUNT_MAX_VALUE)
     278             :   {
     279           2 :     GNUNET_break (0);
     280           2 :     return GNUNET_SYSERR;
     281             :   }
     282     1476833 :   return ('\0' != amount->currency[0]) ? GNUNET_OK : GNUNET_NO;
     283             : }
     284             : 
     285             : 
     286             : enum GNUNET_GenericReturnValue
     287           0 : TALER_amount_max (struct TALER_Amount *ma,
     288             :                   const struct TALER_Amount *a1,
     289             :                   const struct TALER_Amount *a2)
     290             : {
     291           0 :   if (GNUNET_OK !=
     292           0 :       TALER_amount_cmp_currency (a1,
     293             :                                  a2))
     294             :   {
     295           0 :     memset (ma,
     296             :             0,
     297             :             sizeof (*ma));
     298           0 :     return GNUNET_SYSERR;
     299             :   }
     300           0 :   if (1 == TALER_amount_cmp (a1,
     301             :                              a2))
     302           0 :     *ma = *a1;
     303             :   else
     304           0 :     *ma = *a2;
     305           0 :   return GNUNET_OK;
     306             : }
     307             : 
     308             : 
     309             : enum GNUNET_GenericReturnValue
     310           8 : TALER_amount_min (struct TALER_Amount *mi,
     311             :                   const struct TALER_Amount *a1,
     312             :                   const struct TALER_Amount *a2)
     313             : {
     314           8 :   if (GNUNET_OK !=
     315           8 :       TALER_amount_cmp_currency (a1,
     316             :                                  a2))
     317             :   {
     318           0 :     memset (mi,
     319             :             0,
     320             :             sizeof (*mi));
     321           0 :     return GNUNET_SYSERR;
     322             :   }
     323           8 :   if (1 == TALER_amount_cmp (a1,
     324             :                              a2))
     325           0 :     *mi = *a2;
     326             :   else
     327           8 :     *mi = *a1;
     328           8 :   return GNUNET_OK;
     329             : }
     330             : 
     331             : 
     332             : bool
     333       66690 : TALER_amount_is_zero (const struct TALER_Amount *amount)
     334             : {
     335       66690 :   if (GNUNET_OK !=
     336       66690 :       TALER_amount_is_valid (amount))
     337           0 :     return false;
     338             :   return
     339       91894 :     (0 == amount->value) &&
     340       25204 :     (0 == amount->fraction);
     341             : }
     342             : 
     343             : 
     344             : enum GNUNET_GenericReturnValue
     345       99616 : TALER_amount_is_currency (const struct TALER_Amount *amount,
     346             :                           const char *currency)
     347             : {
     348       99616 :   if (GNUNET_OK !=
     349       99616 :       TALER_amount_is_valid (amount))
     350           0 :     return GNUNET_SYSERR;
     351       99616 :   return (0 == strcasecmp (currency,
     352       99616 :                            amount->currency))
     353             :          ? GNUNET_OK
     354       99616 :          : GNUNET_NO;
     355             : }
     356             : 
     357             : 
     358             : /**
     359             :  * Test if @a a is valid, NBO variant.
     360             :  *
     361             :  * @param a amount to test
     362             :  * @return #GNUNET_YES if valid,
     363             :  *         #GNUNET_NO if invalid
     364             :  */
     365             : static enum GNUNET_GenericReturnValue
     366           0 : test_valid_nbo (const struct TALER_AmountNBO *a)
     367             : {
     368           0 :   return ('\0' != a->currency[0]) ? GNUNET_YES : GNUNET_NO;
     369             : }
     370             : 
     371             : 
     372             : enum GNUNET_GenericReturnValue
     373      124150 : TALER_amount_cmp_currency (const struct TALER_Amount *a1,
     374             :                            const struct TALER_Amount *a2)
     375             : {
     376      248300 :   if ( (GNUNET_NO == TALER_amount_is_valid (a1)) ||
     377      124150 :        (GNUNET_NO == TALER_amount_is_valid (a2)) )
     378           0 :     return GNUNET_SYSERR;
     379      124150 :   if (0 == strcasecmp (a1->currency,
     380      124150 :                        a2->currency))
     381      124148 :     return GNUNET_YES;
     382           2 :   return GNUNET_NO;
     383             : }
     384             : 
     385             : 
     386             : enum GNUNET_GenericReturnValue
     387           0 : TALER_amount_cmp_currency_nbo (const struct TALER_AmountNBO *a1,
     388             :                                const struct TALER_AmountNBO *a2)
     389             : {
     390           0 :   if ( (GNUNET_NO == test_valid_nbo (a1)) ||
     391           0 :        (GNUNET_NO == test_valid_nbo (a2)) )
     392           0 :     return GNUNET_SYSERR;
     393           0 :   if (0 == strcasecmp (a1->currency,
     394           0 :                        a2->currency))
     395           0 :     return GNUNET_YES;
     396           0 :   return GNUNET_NO;
     397             : }
     398             : 
     399             : 
     400             : int
     401       85132 : TALER_amount_cmp (const struct TALER_Amount *a1,
     402             :                   const struct TALER_Amount *a2)
     403             : {
     404             :   struct TALER_Amount n1;
     405             :   struct TALER_Amount n2;
     406             : 
     407       85132 :   GNUNET_assert (GNUNET_YES ==
     408             :                  TALER_amount_cmp_currency (a1,
     409             :                                             a2));
     410       85132 :   n1 = *a1;
     411       85132 :   n2 = *a2;
     412       85132 :   GNUNET_assert (GNUNET_SYSERR !=
     413             :                  TALER_amount_normalize (&n1));
     414       85132 :   GNUNET_assert (GNUNET_SYSERR !=
     415             :                  TALER_amount_normalize (&n2));
     416       85132 :   if (n1.value == n2.value)
     417             :   {
     418       49456 :     if (n1.fraction < n2.fraction)
     419         116 :       return -1;
     420       49340 :     if (n1.fraction > n2.fraction)
     421       17082 :       return 1;
     422       32258 :     return 0;
     423             :   }
     424       35676 :   if (n1.value < n2.value)
     425       16042 :     return -1;
     426       19634 :   return 1;
     427             : }
     428             : 
     429             : 
     430             : int
     431           0 : TALER_amount_cmp_nbo (const struct TALER_AmountNBO *a1,
     432             :                       const struct TALER_AmountNBO *a2)
     433             : {
     434             :   struct TALER_Amount h1;
     435             :   struct TALER_Amount h2;
     436             : 
     437           0 :   TALER_amount_ntoh (&h1,
     438             :                      a1);
     439           0 :   TALER_amount_ntoh (&h2,
     440             :                      a2);
     441           0 :   return TALER_amount_cmp (&h1,
     442             :                            &h2);
     443             : }
     444             : 
     445             : 
     446             : enum TALER_AmountArithmeticResult
     447       18247 : TALER_amount_subtract (struct TALER_Amount *diff,
     448             :                        const struct TALER_Amount *a1,
     449             :                        const struct TALER_Amount *a2)
     450             : {
     451             :   struct TALER_Amount n1;
     452             :   struct TALER_Amount n2;
     453             : 
     454       18247 :   if (GNUNET_YES !=
     455       18247 :       TALER_amount_cmp_currency (a1,
     456             :                                  a2))
     457             :   {
     458           1 :     invalidate (diff);
     459           1 :     return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE;
     460             :   }
     461             :   /* make local copies to avoid aliasing problems between
     462             :      diff and a1/a2 */
     463       18246 :   n1 = *a1;
     464       18246 :   n2 = *a2;
     465       36492 :   if ( (GNUNET_SYSERR == TALER_amount_normalize (&n1)) ||
     466       18246 :        (GNUNET_SYSERR == TALER_amount_normalize (&n2)) )
     467             :   {
     468           0 :     invalidate (diff);
     469           0 :     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
     470             :   }
     471             : 
     472       18246 :   if (n1.fraction < n2.fraction)
     473             :   {
     474         730 :     if (0 == n1.value)
     475             :     {
     476          11 :       invalidate (diff);
     477          11 :       return TALER_AAR_INVALID_NEGATIVE_RESULT;
     478             :     }
     479         719 :     n1.fraction += TALER_AMOUNT_FRAC_BASE;
     480         719 :     n1.value--;
     481             :   }
     482       18235 :   if (n1.value < n2.value)
     483             :   {
     484           4 :     invalidate (diff);
     485           4 :     return TALER_AAR_INVALID_NEGATIVE_RESULT;
     486             :   }
     487       18231 :   GNUNET_assert (GNUNET_OK ==
     488             :                  TALER_amount_set_zero (n1.currency,
     489             :                                         diff));
     490       18231 :   GNUNET_assert (n1.fraction >= n2.fraction);
     491       18231 :   diff->fraction = n1.fraction - n2.fraction;
     492       18231 :   GNUNET_assert (n1.value >= n2.value);
     493       18231 :   diff->value = n1.value - n2.value;
     494       18231 :   if ( (0 == diff->fraction) &&
     495         143 :        (0 == diff->value) )
     496          41 :     return TALER_AAR_RESULT_ZERO;
     497       18190 :   return TALER_AAR_RESULT_POSITIVE;
     498             : }
     499             : 
     500             : 
     501             : enum TALER_AmountArithmeticResult
     502       20548 : TALER_amount_add (struct TALER_Amount *sum,
     503             :                   const struct TALER_Amount *a1,
     504             :                   const struct TALER_Amount *a2)
     505             : {
     506             :   struct TALER_Amount n1;
     507             :   struct TALER_Amount n2;
     508             :   struct TALER_Amount res;
     509             : 
     510       20548 :   if (GNUNET_YES !=
     511       20548 :       TALER_amount_cmp_currency (a1,
     512             :                                  a2))
     513             :   {
     514           0 :     invalidate (sum);
     515           0 :     return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE;
     516             :   }
     517             :   /* make local copies to avoid aliasing problems between
     518             :      diff and a1/a2 */
     519       20548 :   n1 = *a1;
     520       20548 :   n2 = *a2;
     521       20548 :   if ( (GNUNET_SYSERR ==
     522       41096 :         TALER_amount_normalize (&n1)) ||
     523             :        (GNUNET_SYSERR ==
     524       20548 :         TALER_amount_normalize (&n2)) )
     525             :   {
     526           0 :     invalidate (sum);
     527           0 :     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
     528             :   }
     529             : 
     530       20548 :   GNUNET_assert (GNUNET_OK ==
     531             :                  TALER_amount_set_zero (a1->currency,
     532             :                                         &res));
     533       20548 :   res.value = n1.value + n2.value;
     534       20548 :   if (res.value < n1.value)
     535             :   {
     536             :     /* integer overflow */
     537           0 :     invalidate (sum);
     538           0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     539             :   }
     540       20548 :   if (res.value > TALER_AMOUNT_MAX_VALUE)
     541             :   {
     542             :     /* too large to be legal */
     543           0 :     invalidate (sum);
     544           0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     545             :   }
     546       20548 :   res.fraction = n1.fraction + n2.fraction;
     547       20548 :   if (GNUNET_SYSERR ==
     548       20548 :       TALER_amount_normalize (&res))
     549             :   {
     550             :     /* integer overflow via carry from fraction */
     551           1 :     invalidate (sum);
     552           1 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     553             :   }
     554       20547 :   *sum = res;
     555       20547 :   if ( (0 == sum->fraction) &&
     556        2385 :        (0 == sum->value) )
     557         418 :     return TALER_AAR_RESULT_ZERO;
     558       20129 :   return TALER_AAR_RESULT_POSITIVE;
     559             : }
     560             : 
     561             : 
     562             : enum GNUNET_GenericReturnValue
     563      431067 : TALER_amount_normalize (struct TALER_Amount *amount)
     564             : {
     565             :   uint32_t overflow;
     566             : 
     567      431067 :   if (GNUNET_YES != TALER_amount_is_valid (amount))
     568           3 :     return GNUNET_SYSERR;
     569      431064 :   if (amount->fraction < TALER_AMOUNT_FRAC_BASE)
     570      430306 :     return GNUNET_NO;
     571         758 :   overflow = amount->fraction / TALER_AMOUNT_FRAC_BASE;
     572         758 :   amount->fraction %= TALER_AMOUNT_FRAC_BASE;
     573         758 :   amount->value += overflow;
     574         758 :   if ( (amount->value < overflow) ||
     575         758 :        (amount->value > TALER_AMOUNT_MAX_VALUE) )
     576             :   {
     577           1 :     invalidate (amount);
     578           1 :     return GNUNET_SYSERR;
     579             :   }
     580         757 :   return GNUNET_OK;
     581             : }
     582             : 
     583             : 
     584             : /**
     585             :  * Convert the fraction of @a amount to a string in decimals.
     586             :  *
     587             :  * @param amount value to convert
     588             :  * @param[out] tail where to write the result
     589             :  */
     590             : static void
     591      120064 : amount_to_tail (const struct TALER_Amount *amount,
     592             :                 char tail[TALER_AMOUNT_FRAC_LEN + 1])
     593             : {
     594      120064 :   uint32_t n = amount->fraction;
     595             :   unsigned int i;
     596             : 
     597      449642 :   for (i = 0; (i < TALER_AMOUNT_FRAC_LEN) && (0 != n); i++)
     598             :   {
     599      329578 :     tail[i] = '0' + (n / (TALER_AMOUNT_FRAC_BASE / 10));
     600      329578 :     n = (n * 10) % (TALER_AMOUNT_FRAC_BASE);
     601             :   }
     602      120064 :   tail[i] = '\0';
     603      120064 : }
     604             : 
     605             : 
     606             : char *
     607       68112 : TALER_amount_to_string (const struct TALER_Amount *amount)
     608             : {
     609             :   char *result;
     610             :   struct TALER_Amount norm;
     611             : 
     612       68112 :   if (GNUNET_YES != TALER_amount_is_valid (amount))
     613           1 :     return NULL;
     614       68111 :   norm = *amount;
     615       68111 :   GNUNET_break (GNUNET_SYSERR !=
     616             :                 TALER_amount_normalize (&norm));
     617       68111 :   if (0 != norm.fraction)
     618             :   {
     619             :     char tail[TALER_AMOUNT_FRAC_LEN + 1];
     620             : 
     621       53451 :     amount_to_tail (&norm,
     622             :                     tail);
     623       53451 :     GNUNET_asprintf (&result,
     624             :                      "%s:%llu.%s",
     625             :                      norm.currency,
     626       53451 :                      (unsigned long long) norm.value,
     627             :                      tail);
     628             :   }
     629             :   else
     630             :   {
     631       14660 :     GNUNET_asprintf (&result,
     632             :                      "%s:%llu",
     633             :                      norm.currency,
     634       14660 :                      (unsigned long long) norm.value);
     635             :   }
     636       68111 :   return result;
     637             : }
     638             : 
     639             : 
     640             : const char *
     641       94544 : TALER_amount2s (const struct TALER_Amount *amount)
     642             : {
     643             :   /* 24 is sufficient for a uint64_t value in decimal; 3 is for ":.\0" */
     644             :   static TALER_THREAD_LOCAL char result[TALER_AMOUNT_FRAC_LEN
     645             :                                         + TALER_CURRENCY_LEN + 3 + 24];
     646             :   struct TALER_Amount norm;
     647             : 
     648       94544 :   if (GNUNET_YES != TALER_amount_is_valid (amount))
     649          12 :     return NULL;
     650       94532 :   norm = *amount;
     651       94532 :   GNUNET_break (GNUNET_SYSERR !=
     652             :                 TALER_amount_normalize (&norm));
     653       94532 :   if (0 != norm.fraction)
     654             :   {
     655             :     char tail[TALER_AMOUNT_FRAC_LEN + 1];
     656             : 
     657       66613 :     amount_to_tail (&norm,
     658             :                     tail);
     659       66613 :     GNUNET_snprintf (result,
     660             :                      sizeof (result),
     661             :                      "%s:%llu.%s",
     662             :                      norm.currency,
     663       66613 :                      (unsigned long long) norm.value,
     664             :                      tail);
     665             :   }
     666             :   else
     667             :   {
     668       27919 :     GNUNET_snprintf (result,
     669             :                      sizeof (result),
     670             :                      "%s:%llu",
     671             :                      norm.currency,
     672       27919 :                      (unsigned long long) norm.value);
     673             :   }
     674       94532 :   return result;
     675             : }
     676             : 
     677             : 
     678             : void
     679           4 : TALER_amount_divide (struct TALER_Amount *result,
     680             :                      const struct TALER_Amount *dividend,
     681             :                      uint32_t divisor)
     682             : {
     683             :   uint64_t modr;
     684             : 
     685           4 :   GNUNET_assert (0 != divisor); /* division by zero is discouraged */
     686           4 :   *result = *dividend;
     687             :   /* in case @a dividend was not yet normalized */
     688           4 :   GNUNET_assert (GNUNET_SYSERR !=
     689             :                  TALER_amount_normalize (result));
     690           4 :   if (1 == divisor)
     691           1 :     return;
     692           3 :   modr = result->value % divisor;
     693           3 :   result->value /= divisor;
     694             :   /* modr fits into 32 bits, so we can safely multiply by (<32-bit) base and add fraction! */
     695           3 :   modr = (modr * TALER_AMOUNT_FRAC_BASE) + result->fraction;
     696           3 :   result->fraction = (uint32_t) (modr / divisor);
     697             :   /* 'fraction' could now be larger than #TALER_AMOUNT_FRAC_BASE, so we must normalize */
     698           3 :   GNUNET_assert (GNUNET_SYSERR !=
     699             :                  TALER_amount_normalize (result));
     700             : }
     701             : 
     702             : 
     703             : int
     704           5 : TALER_amount_divide2 (const struct TALER_Amount *dividend,
     705             :                       const struct TALER_Amount *divisor)
     706             : {
     707             :   double approx;
     708             :   double d;
     709             :   double r;
     710             :   int ret;
     711             :   struct TALER_Amount tmp;
     712             :   struct TALER_Amount nxt;
     713             : 
     714           5 :   if (GNUNET_YES !=
     715           5 :       TALER_amount_cmp_currency (dividend,
     716             :                                  divisor))
     717             :   {
     718           0 :     GNUNET_break (0);
     719           0 :     return -1;
     720             :   }
     721           5 :   if ( (0 == divisor->fraction) &&
     722           2 :        (0 == divisor->value) )
     723           1 :     return INT_MAX;
     724             :   /* first, get rounded approximation */
     725           4 :   d = ((double) dividend->value) * ((double) TALER_AMOUNT_FRAC_BASE)
     726           4 :       + ( (double) dividend->fraction);
     727           4 :   r = ((double) divisor->value) * ((double) TALER_AMOUNT_FRAC_BASE)
     728           4 :       + ( (double) divisor->fraction);
     729           4 :   approx = d / r;
     730           4 :   if (approx > ((double) INT_MAX))
     731           0 :     return INT_MAX; /* 'infinity' */
     732             :   /* round down */
     733           4 :   if (approx < 2)
     734           1 :     ret = 0;
     735             :   else
     736           3 :     ret = (int) approx - 2;
     737             :   /* Now do *exact* calculation, using well rounded-down factor as starting
     738             :      point to avoid having to do too many steps. */
     739           4 :   GNUNET_assert (0 <=
     740             :                  TALER_amount_multiply (&tmp,
     741             :                                         divisor,
     742             :                                         ret));
     743             :   /* in practice, this loop will only run for one or two iterations */
     744             :   while (1)
     745             :   {
     746          10 :     GNUNET_assert (0 <=
     747             :                    TALER_amount_add (&nxt,
     748             :                                      &tmp,
     749             :                                      divisor));
     750          10 :     if (1 ==
     751          10 :         TALER_amount_cmp (&nxt,
     752             :                           dividend))
     753           4 :       break; /* nxt > dividend */
     754           6 :     ret++;
     755           6 :     tmp = nxt;
     756             :   }
     757           4 :   return ret;
     758             : }
     759             : 
     760             : 
     761             : enum TALER_AmountArithmeticResult
     762           6 : TALER_amount_multiply (struct TALER_Amount *result,
     763             :                        const struct TALER_Amount *amount,
     764             :                        uint32_t factor)
     765             : {
     766           6 :   struct TALER_Amount in = *amount;
     767             : 
     768           6 :   if (GNUNET_SYSERR ==
     769           6 :       TALER_amount_normalize (&in))
     770           0 :     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
     771           6 :   GNUNET_memcpy (result->currency,
     772             :                  amount->currency,
     773             :                  TALER_CURRENCY_LEN);
     774           6 :   if ( (0 == factor) ||
     775           5 :        ( (0 == in.value) &&
     776           1 :          (0 == in.fraction) ) )
     777             :   {
     778           2 :     result->value = 0;
     779           2 :     result->fraction = 0;
     780           2 :     return TALER_AAR_RESULT_ZERO;
     781             :   }
     782           4 :   result->value = in.value * ((uint64_t) factor);
     783           4 :   if (in.value != result->value / factor)
     784           0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     785             :   {
     786             :     /* This multiplication cannot overflow since both inputs are 32-bit values */
     787           4 :     uint64_t tmp = ((uint64_t) factor) * ((uint64_t) in.fraction);
     788             :     uint64_t res;
     789             : 
     790           4 :     res = tmp / TALER_AMOUNT_FRAC_BASE;
     791             :     /* check for overflow */
     792           4 :     if (result->value + res < result->value)
     793           0 :       return TALER_AAR_INVALID_RESULT_OVERFLOW;
     794           4 :     result->value += res;
     795           4 :     result->fraction = tmp % TALER_AMOUNT_FRAC_BASE;
     796             :   }
     797           4 :   if (result->value > TALER_AMOUNT_MAX_VALUE)
     798           0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     799             :   /* This check should be redundant... */
     800           4 :   GNUNET_assert (GNUNET_SYSERR !=
     801             :                  TALER_amount_normalize (result));
     802           4 :   return TALER_AAR_RESULT_POSITIVE;
     803             : }
     804             : 
     805             : 
     806             : enum GNUNET_GenericReturnValue
     807          91 : TALER_amount_round_down (struct TALER_Amount *amount,
     808             :                          const struct TALER_Amount *round_unit)
     809             : {
     810          91 :   if (GNUNET_OK !=
     811          91 :       TALER_amount_cmp_currency (amount,
     812             :                                  round_unit))
     813             :   {
     814           0 :     GNUNET_break (0);
     815           0 :     return GNUNET_SYSERR;
     816             :   }
     817          91 :   if ( (0 != round_unit->fraction) &&
     818          90 :        (0 != round_unit->value) )
     819             :   {
     820           0 :     GNUNET_break (0);
     821           0 :     return GNUNET_SYSERR;
     822             :   }
     823          91 :   if ( (0 == round_unit->fraction) &&
     824           1 :        (0 == round_unit->value) )
     825           0 :     return GNUNET_NO; /* no rounding requested */
     826          91 :   if (0 != round_unit->fraction)
     827             :   {
     828             :     uint32_t delta;
     829             : 
     830          90 :     delta = amount->fraction % round_unit->fraction;
     831          90 :     if (0 == delta)
     832          85 :       return GNUNET_NO;
     833           5 :     amount->fraction -= delta;
     834             :   }
     835           6 :   if (0 != round_unit->value)
     836             :   {
     837             :     uint64_t delta;
     838             : 
     839           1 :     delta = amount->value % round_unit->value;
     840           1 :     if (0 == delta)
     841           0 :       return GNUNET_NO;
     842           1 :     amount->value -= delta;
     843           1 :     amount->fraction = 0;
     844             :   }
     845           6 :   return GNUNET_OK;
     846             : }
     847             : 
     848             : 
     849             : /* end of amount.c */

Generated by: LCOV version 1.16