LCOV - code coverage report
Current view: top level - util - amount.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 78.4 % 375 294
Test Date: 2026-01-09 13:26:54 Functions: 77.8 % 27 21

            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 "taler/platform.h"
      25              : #include "taler/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           16 : invalidate (struct TALER_Amount *a)
      35              : {
      36           16 :   memset (a,
      37              :           0,
      38              :           sizeof (struct TALER_Amount));
      39           16 : }
      40              : 
      41              : 
      42              : enum GNUNET_GenericReturnValue
      43       171279 : TALER_check_currency (const char *str)
      44              : {
      45       171279 :   size_t len = strlen (str);
      46       171279 :   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       171279 :   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       795220 :   for (unsigned int i = 0; '\0' != str[i]; i++)
      61              :   {
      62       623941 :     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       171279 :   return GNUNET_OK;
      71              : }
      72              : 
      73              : 
      74              : enum GNUNET_GenericReturnValue
      75       112507 : 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       112509 :   while (isspace ( (unsigned char) str[0]))
      85            2 :     str++;
      86       112507 :   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       112507 :   colon = strchr (str, (int) ':');
      96       112507 :   if ( (NULL == colon) ||
      97       112506 :        (colon == str) ||
      98       112506 :        ((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       112506 :   GNUNET_assert (TALER_CURRENCY_LEN > (colon - str));
     108       450940 :   for (unsigned int i = 0; i<colon - str; i++)
     109       338434 :     amount->currency[i] = str[i];
     110              :   /* 0-terminate *and* normalize buffer by setting everything to '\0' */
     111       112506 :   memset (&amount->currency [colon - str],
     112              :           0,
     113       112506 :           TALER_CURRENCY_LEN - (colon - str));
     114       112506 :   if (GNUNET_OK !=
     115       112506 :       TALER_check_currency (amount->currency))
     116            0 :     return GNUNET_SYSERR;
     117              :   /* skip colon */
     118       112506 :   value = colon + 1;
     119       112506 :   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       112506 :   amount->value = 0;
     129       112506 :   amount->fraction = 0;
     130              : 
     131              :   /* parse value */
     132       230718 :   while ('.' != *value)
     133              :   {
     134       137434 :     if ('\0' == *value)
     135              :     {
     136              :       /* we are done */
     137        19220 :       return GNUNET_OK;
     138              :     }
     139       118214 :     if ( (*value < '0') ||
     140       118214 :          (*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       118213 :     n = *value - '0';
     150       118213 :     if ( (amount->value * 10 < amount->value) ||
     151       118213 :          (amount->value * 10 + n < amount->value) ||
     152       118213 :          (amount->value > TALER_AMOUNT_MAX_VALUE) ||
     153       118213 :          (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       118212 :     amount->value = (amount->value * 10) + n;
     162       118212 :     value++;
     163              :   }
     164              : 
     165              :   /* skip the dot */
     166        93284 :   value++;
     167              : 
     168              :   /* parse fraction */
     169        93284 :   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        93284 :   b = TALER_AMOUNT_FRAC_BASE / 10;
     178       277608 :   while ('\0' != *value)
     179              :   {
     180       184327 :     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       184326 :     if ( (*value < '0') ||
     190       184326 :          (*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       184324 :     n = *value - '0';
     198       184324 :     amount->fraction += n * b;
     199       184324 :     b /= 10;
     200       184324 :     value++;
     201              :   }
     202        93281 :   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       238350 : TALER_amount_hton (struct TALER_AmountNBO *res,
     224              :                    const struct TALER_Amount *d)
     225              : {
     226       238350 :   GNUNET_assert (GNUNET_YES ==
     227              :                  TALER_amount_is_valid (d));
     228       238350 :   res->value = GNUNET_htonll (d->value);
     229       238350 :   res->fraction = htonl (d->fraction);
     230      3098550 :   for (unsigned int i = 0; i<TALER_CURRENCY_LEN; i++)
     231      2860200 :     res->currency[i] = d->currency[i];
     232       238350 : }
     233              : 
     234              : 
     235              : void
     236           57 : TALER_amount_ntoh (struct TALER_Amount *res,
     237              :                    const struct TALER_AmountNBO *dn)
     238              : {
     239           57 :   res->value = GNUNET_ntohll (dn->value);
     240           57 :   res->fraction = ntohl (dn->fraction);
     241           57 :   GNUNET_memcpy (res->currency,
     242              :                  dn->currency,
     243              :                  TALER_CURRENCY_LEN);
     244           57 :   GNUNET_assert (GNUNET_YES ==
     245              :                  TALER_amount_is_valid (res));
     246           57 : }
     247              : 
     248              : 
     249              : enum GNUNET_GenericReturnValue
     250        58641 : 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        58641 :   if (GNUNET_OK !=
     257        58641 :       TALER_check_currency (cur))
     258            0 :     return GNUNET_SYSERR;
     259        58641 :   slen = strlen (cur);
     260              :   /* make a copy of 'cur' to 'tmp' as the memset may clobber cur
     261              :      if cur aliases &amount->currency! */
     262        58641 :   memcpy (tmp,
     263              :           cur,
     264              :           slen);
     265        58641 :   memset (amount,
     266              :           0,
     267              :           sizeof (struct TALER_Amount));
     268       343536 :   for (unsigned int i = 0; i<slen; i++)
     269       284895 :     amount->currency[i] = tmp[i];
     270        58641 :   return GNUNET_OK;
     271              : }
     272              : 
     273              : 
     274              : enum GNUNET_GenericReturnValue
     275       828157 : TALER_amount_is_valid (const struct TALER_Amount *amount)
     276              : {
     277       828157 :   if (amount->value > TALER_AMOUNT_MAX_VALUE)
     278              :   {
     279            2 :     GNUNET_break (0);
     280            2 :     return GNUNET_SYSERR;
     281              :   }
     282       828155 :   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            0 : TALER_amount_min (struct TALER_Amount *mi,
     311              :                   const struct TALER_Amount *a1,
     312              :                   const struct TALER_Amount *a2)
     313              : {
     314            0 :   if (GNUNET_OK !=
     315            0 :       TALER_amount_cmp_currency (a1,
     316              :                                  a2))
     317              :   {
     318            0 :     memset (mi,
     319              :             0,
     320              :             sizeof (*mi));
     321            0 :     return GNUNET_SYSERR;
     322              :   }
     323            0 :   if (1 == TALER_amount_cmp (a1,
     324              :                              a2))
     325            0 :     *mi = *a2;
     326              :   else
     327            0 :     *mi = *a1;
     328            0 :   return GNUNET_OK;
     329              : }
     330              : 
     331              : 
     332              : bool
     333        66274 : TALER_amount_is_zero (const struct TALER_Amount *amount)
     334              : {
     335        66274 :   if (GNUNET_OK !=
     336        66274 :       TALER_amount_is_valid (amount))
     337            0 :     return false;
     338              :   return
     339        91110 :     (0 == amount->value) &&
     340        24836 :     (0 == amount->fraction);
     341              : }
     342              : 
     343              : 
     344              : enum GNUNET_GenericReturnValue
     345        64600 : TALER_amount_is_currency (const struct TALER_Amount *amount,
     346              :                           const char *currency)
     347              : {
     348        64600 :   if (GNUNET_OK !=
     349        64600 :       TALER_amount_is_valid (amount))
     350            0 :     return GNUNET_SYSERR;
     351        64600 :   return (0 == strcasecmp (currency,
     352        64600 :                            amount->currency))
     353              :          ? GNUNET_OK
     354        64600 :          : 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        88449 : TALER_amount_cmp_currency (const struct TALER_Amount *a1,
     374              :                            const struct TALER_Amount *a2)
     375              : {
     376       176898 :   if ( (GNUNET_NO == TALER_amount_is_valid (a1)) ||
     377        88449 :        (GNUNET_NO == TALER_amount_is_valid (a2)) )
     378            0 :     return GNUNET_SYSERR;
     379        88449 :   if (0 == strcasecmp (a1->currency,
     380        88449 :                        a2->currency))
     381        88447 :     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        69229 : 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        69229 :   GNUNET_assert (GNUNET_YES ==
     408              :                  TALER_amount_cmp_currency (a1,
     409              :                                             a2));
     410        69229 :   n1 = *a1;
     411        69229 :   n2 = *a2;
     412        69229 :   GNUNET_assert (GNUNET_SYSERR !=
     413              :                  TALER_amount_normalize (&n1));
     414        69229 :   GNUNET_assert (GNUNET_SYSERR !=
     415              :                  TALER_amount_normalize (&n2));
     416        69229 :   if (n1.value == n2.value)
     417              :   {
     418        48033 :     if (n1.fraction < n2.fraction)
     419           93 :       return -1;
     420        47940 :     if (n1.fraction > n2.fraction)
     421        17565 :       return 1;
     422        30375 :     return 0;
     423              :   }
     424        21196 :   if (n1.value < n2.value)
     425        11120 :     return -1;
     426        10076 :   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        17131 : 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        17131 :   if (GNUNET_YES !=
     455        17131 :       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        17130 :   n1 = *a1;
     464        17130 :   n2 = *a2;
     465        34260 :   if ( (GNUNET_SYSERR == TALER_amount_normalize (&n1)) ||
     466        17130 :        (GNUNET_SYSERR == TALER_amount_normalize (&n2)) )
     467              :   {
     468            0 :     invalidate (diff);
     469            0 :     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
     470              :   }
     471              : 
     472        17130 :   if (n1.fraction < n2.fraction)
     473              :   {
     474          287 :     if (0 == n1.value)
     475              :     {
     476            6 :       invalidate (diff);
     477            6 :       return TALER_AAR_INVALID_NEGATIVE_RESULT;
     478              :     }
     479          281 :     n1.fraction += TALER_AMOUNT_FRAC_BASE;
     480          281 :     n1.value--;
     481              :   }
     482        17124 :   if (n1.value < n2.value)
     483              :   {
     484            1 :     invalidate (diff);
     485            1 :     return TALER_AAR_INVALID_NEGATIVE_RESULT;
     486              :   }
     487        17123 :   GNUNET_assert (GNUNET_OK ==
     488              :                  TALER_amount_set_zero (n1.currency,
     489              :                                         diff));
     490        17123 :   GNUNET_assert (n1.fraction >= n2.fraction);
     491        17123 :   diff->fraction = n1.fraction - n2.fraction;
     492        17123 :   GNUNET_assert (n1.value >= n2.value);
     493        17123 :   diff->value = n1.value - n2.value;
     494        17123 :   if ( (0 == diff->fraction) &&
     495           89 :        (0 == diff->value) )
     496           18 :     return TALER_AAR_RESULT_ZERO;
     497        17105 :   return TALER_AAR_RESULT_POSITIVE;
     498              : }
     499              : 
     500              : 
     501              : enum TALER_AmountArithmeticResult
     502         1980 : 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         1980 :   if (GNUNET_YES !=
     511         1980 :       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         1980 :   n1 = *a1;
     520         1980 :   n2 = *a2;
     521         1980 :   if ( (GNUNET_SYSERR ==
     522         3960 :         TALER_amount_normalize (&n1)) ||
     523              :        (GNUNET_SYSERR ==
     524         1980 :         TALER_amount_normalize (&n2)) )
     525              :   {
     526            0 :     invalidate (sum);
     527            0 :     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
     528              :   }
     529              : 
     530         1980 :   GNUNET_assert (GNUNET_OK ==
     531              :                  TALER_amount_set_zero (a1->currency,
     532              :                                         &res));
     533         1980 :   res.value = n1.value + n2.value;
     534         1980 :   if (res.value < n1.value)
     535              :   {
     536              :     /* integer overflow */
     537            0 :     invalidate (sum);
     538            0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     539              :   }
     540         1980 :   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         1980 :   res.fraction = n1.fraction + n2.fraction;
     547         1980 :   if (GNUNET_SYSERR ==
     548         1980 :       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         1979 :   *sum = res;
     555         1979 :   if ( (0 == sum->fraction) &&
     556          339 :        (0 == sum->value) )
     557            8 :     return TALER_AAR_RESULT_ZERO;
     558         1971 :   return TALER_AAR_RESULT_POSITIVE;
     559              : }
     560              : 
     561              : 
     562              : enum GNUNET_GenericReturnValue
     563       230289 : TALER_amount_normalize (struct TALER_Amount *amount)
     564              : {
     565              :   uint32_t overflow;
     566              : 
     567       230289 :   if (GNUNET_YES != TALER_amount_is_valid (amount))
     568            3 :     return GNUNET_SYSERR;
     569       230286 :   if (amount->fraction < TALER_AMOUNT_FRAC_BASE)
     570       230219 :     return GNUNET_NO;
     571           67 :   overflow = amount->fraction / TALER_AMOUNT_FRAC_BASE;
     572           67 :   amount->fraction %= TALER_AMOUNT_FRAC_BASE;
     573           67 :   amount->value += overflow;
     574           67 :   if ( (amount->value < overflow) ||
     575           67 :        (amount->value > TALER_AMOUNT_MAX_VALUE) )
     576              :   {
     577            1 :     invalidate (amount);
     578            1 :     return GNUNET_SYSERR;
     579              :   }
     580           66 :   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        41454 : amount_to_tail (const struct TALER_Amount *amount,
     592              :                 char tail[TALER_AMOUNT_FRAC_LEN + 1])
     593              : {
     594        41454 :   uint32_t n = amount->fraction;
     595              :   unsigned int i;
     596              : 
     597       223047 :   for (i = 0; (i < TALER_AMOUNT_FRAC_LEN) && (0 != n); i++)
     598              :   {
     599       181593 :     tail[i] = '0' + (n / (TALER_AMOUNT_FRAC_BASE / 10));
     600       181593 :     n = (n * 10) % (TALER_AMOUNT_FRAC_BASE);
     601              :   }
     602        41454 :   tail[i] = '\0';
     603        41454 : }
     604              : 
     605              : 
     606              : char *
     607        34055 : TALER_amount_to_string (const struct TALER_Amount *amount)
     608              : {
     609              :   char *result;
     610              :   struct TALER_Amount norm;
     611              : 
     612        34055 :   if (GNUNET_YES != TALER_amount_is_valid (amount))
     613            1 :     return NULL;
     614        34054 :   norm = *amount;
     615        34054 :   GNUNET_break (GNUNET_SYSERR !=
     616              :                 TALER_amount_normalize (&norm));
     617        34054 :   if (0 != norm.fraction)
     618              :   {
     619              :     char tail[TALER_AMOUNT_FRAC_LEN + 1];
     620              : 
     621        24348 :     amount_to_tail (&norm,
     622              :                     tail);
     623        24348 :     GNUNET_asprintf (&result,
     624              :                      "%s:%llu.%s",
     625              :                      norm.currency,
     626        24348 :                      (unsigned long long) norm.value,
     627              :                      tail);
     628              :   }
     629              :   else
     630              :   {
     631         9706 :     GNUNET_asprintf (&result,
     632              :                      "%s:%llu",
     633              :                      norm.currency,
     634         9706 :                      (unsigned long long) norm.value);
     635              :   }
     636        34054 :   return result;
     637              : }
     638              : 
     639              : 
     640              : const char *
     641        17565 : 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        17565 :   if (GNUNET_YES != TALER_amount_is_valid (amount))
     649           12 :     return NULL;
     650        17553 :   norm = *amount;
     651        17553 :   GNUNET_break (GNUNET_SYSERR !=
     652              :                 TALER_amount_normalize (&norm));
     653        17553 :   if (0 != norm.fraction)
     654              :   {
     655              :     char tail[TALER_AMOUNT_FRAC_LEN + 1];
     656              : 
     657        17106 :     amount_to_tail (&norm,
     658              :                     tail);
     659        17106 :     GNUNET_snprintf (result,
     660              :                      sizeof (result),
     661              :                      "%s:%llu.%s",
     662              :                      norm.currency,
     663        17106 :                      (unsigned long long) norm.value,
     664              :                      tail);
     665              :   }
     666              :   else
     667              :   {
     668          447 :     GNUNET_snprintf (result,
     669              :                      sizeof (result),
     670              :                      "%s:%llu",
     671              :                      norm.currency,
     672          447 :                      (unsigned long long) norm.value);
     673              :   }
     674        17553 :   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           63 : TALER_amount_round_down (struct TALER_Amount *amount,
     808              :                          const struct TALER_Amount *round_unit)
     809              : {
     810           63 :   if (GNUNET_OK !=
     811           63 :       TALER_amount_cmp_currency (amount,
     812              :                                  round_unit))
     813              :   {
     814            0 :     GNUNET_break (0);
     815            0 :     return GNUNET_SYSERR;
     816              :   }
     817           63 :   if ( (0 != round_unit->fraction) &&
     818           62 :        (0 != round_unit->value) )
     819              :   {
     820            0 :     GNUNET_break (0);
     821            0 :     return GNUNET_SYSERR;
     822              :   }
     823           63 :   if ( (0 == round_unit->fraction) &&
     824            1 :        (0 == round_unit->value) )
     825            0 :     return GNUNET_NO; /* no rounding requested */
     826           63 :   if (0 != round_unit->fraction)
     827              :   {
     828              :     uint32_t delta;
     829              : 
     830           62 :     delta = amount->fraction % round_unit->fraction;
     831           62 :     if (0 == delta)
     832           58 :       return GNUNET_NO;
     833            4 :     amount->fraction -= delta;
     834              :   }
     835            5 :   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            5 :   return GNUNET_OK;
     846              : }
     847              : 
     848              : 
     849              : /* end of amount.c */
        

Generated by: LCOV version 2.0-1