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-02-16 10:50:18 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        73175 : TALER_check_currency (const char *str)
      44              : {
      45        73175 :   size_t len = strlen (str);
      46        73175 :   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        73175 :   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       402804 :   for (unsigned int i = 0; '\0' != str[i]; i++)
      61              :   {
      62       329629 :     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        73175 :   return GNUNET_OK;
      71              : }
      72              : 
      73              : 
      74              : enum GNUNET_GenericReturnValue
      75        14403 : TALER_string_to_amount (const char *str,
      76              :                         struct TALER_Amount *amount)
      77              : {
      78              :   uint32_t b;
      79              :   const char *colon;
      80              :   const char *value;
      81              : 
      82              :   /* skip leading whitespace */
      83        14405 :   while (isspace ( (unsigned char) str[0]))
      84            2 :     str++;
      85        14403 :   if ('\0' == str[0])
      86              :   {
      87            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
      88              :                 "Null before currency\n");
      89            0 :     invalidate (amount);
      90            0 :     return GNUNET_SYSERR;
      91              :   }
      92              : 
      93              :   /* parse currency */
      94        14403 :   colon = strchr (str, (int) ':');
      95        14403 :   if ( (NULL == colon) ||
      96        14402 :        (colon == str) ||
      97        14402 :        ((colon - str) >= TALER_CURRENCY_LEN) )
      98              :   {
      99            1 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     100              :                 "Invalid currency specified before colon: `%s'\n",
     101              :                 str);
     102            1 :     invalidate (amount);
     103            1 :     return GNUNET_SYSERR;
     104              :   }
     105              : 
     106        14402 :   GNUNET_assert (TALER_CURRENCY_LEN > (colon - str));
     107        58524 :   for (unsigned int i = 0; i<colon - str; i++)
     108        44122 :     amount->currency[i] = str[i];
     109              :   /* 0-terminate *and* normalize buffer by setting everything to '\0' */
     110        14402 :   memset (&amount->currency [colon - str],
     111              :           0,
     112        14402 :           TALER_CURRENCY_LEN - (colon - str));
     113        14402 :   if (GNUNET_OK !=
     114        14402 :       TALER_check_currency (amount->currency))
     115            0 :     return GNUNET_SYSERR;
     116              :   /* skip colon */
     117        14402 :   value = colon + 1;
     118        14402 :   if ('\0' == value[0])
     119              :   {
     120            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     121              :                 "Actual value missing in amount `%s'\n",
     122              :                 str);
     123            0 :     invalidate (amount);
     124            0 :     return GNUNET_SYSERR;
     125              :   }
     126              : 
     127        14402 :   amount->value = 0;
     128        14402 :   amount->fraction = 0;
     129              : 
     130              :   /* parse value */
     131        30590 :   while ('.' != *value)
     132              :   {
     133              :     int n;
     134              : 
     135        19709 :     if ('\0' == *value)
     136              :     {
     137              :       /* we are done */
     138         3519 :       return GNUNET_OK;
     139              :     }
     140        16190 :     if ( (*value < '0') ||
     141        16190 :          (*value > '9') )
     142              :     {
     143            1 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     144              :                   "Invalid character `%c' in amount `%s'\n",
     145              :                   (int) *value,
     146              :                   str);
     147            1 :       invalidate (amount);
     148            1 :       return GNUNET_SYSERR;
     149              :     }
     150        16189 :     n = *value - '0';
     151        16189 :     if ( (amount->value * 10 < amount->value) ||
     152        16189 :          (amount->value * 10 + n < amount->value) ||
     153        16189 :          (amount->value > TALER_AMOUNT_MAX_VALUE) ||
     154        16189 :          (amount->value * 10 + n > TALER_AMOUNT_MAX_VALUE) )
     155              :     {
     156            1 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     157              :                   "Value specified in amount `%s' is too large\n",
     158              :                   str);
     159            1 :       invalidate (amount);
     160            1 :       return GNUNET_SYSERR;
     161              :     }
     162        16188 :     amount->value = (amount->value * 10) + n;
     163        16188 :     value++;
     164              :   }
     165              : 
     166              :   /* skip the dot */
     167        10881 :   value++;
     168              : 
     169              :   /* parse fraction */
     170        10881 :   if ('\0' == *value)
     171              :   {
     172            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     173              :                 "Amount `%s' ends abruptly after `.'\n",
     174              :                 str);
     175            0 :     invalidate (amount);
     176            0 :     return GNUNET_SYSERR;
     177              :   }
     178        10881 :   b = TALER_AMOUNT_FRAC_BASE / 10;
     179        32359 :   while ('\0' != *value)
     180              :   {
     181              :     int n;
     182              : 
     183        21481 :     if (0 == b)
     184              :     {
     185            1 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     186              :                   "Fractional value too small (only %u digits supported) in amount `%s'\n",
     187              :                   (unsigned int) TALER_AMOUNT_FRAC_LEN,
     188              :                   str);
     189            1 :       invalidate (amount);
     190            1 :       return GNUNET_SYSERR;
     191              :     }
     192        21480 :     if ( (*value < '0') ||
     193        21480 :          (*value > '9') )
     194              :     {
     195            2 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     196              :                   "Error after dot\n");
     197            2 :       invalidate (amount);
     198            2 :       return GNUNET_SYSERR;
     199              :     }
     200        21478 :     n = *value - '0';
     201        21478 :     amount->fraction += n * b;
     202        21478 :     b /= 10;
     203        21478 :     value++;
     204              :   }
     205        10878 :   return GNUNET_OK;
     206              : }
     207              : 
     208              : 
     209              : enum GNUNET_GenericReturnValue
     210            0 : TALER_string_to_amount_nbo (const char *str,
     211              :                             struct TALER_AmountNBO *amount_nbo)
     212              : {
     213              :   struct TALER_Amount amount;
     214              : 
     215            0 :   if (GNUNET_OK !=
     216            0 :       TALER_string_to_amount (str,
     217              :                               &amount))
     218            0 :     return GNUNET_SYSERR;
     219            0 :   TALER_amount_hton (amount_nbo,
     220              :                      &amount);
     221            0 :   return GNUNET_OK;
     222              : }
     223              : 
     224              : 
     225              : void
     226        22524 : TALER_amount_hton (struct TALER_AmountNBO *res,
     227              :                    const struct TALER_Amount *d)
     228              : {
     229        22524 :   GNUNET_assert (GNUNET_YES ==
     230              :                  TALER_amount_is_valid (d));
     231        22524 :   res->value = GNUNET_htonll (d->value);
     232        22524 :   res->fraction = htonl (d->fraction);
     233       292812 :   for (unsigned int i = 0; i<TALER_CURRENCY_LEN; i++)
     234       270288 :     res->currency[i] = d->currency[i];
     235        22524 : }
     236              : 
     237              : 
     238              : void
     239           57 : TALER_amount_ntoh (struct TALER_Amount *res,
     240              :                    const struct TALER_AmountNBO *dn)
     241              : {
     242           57 :   res->value = GNUNET_ntohll (dn->value);
     243           57 :   res->fraction = ntohl (dn->fraction);
     244           57 :   GNUNET_memcpy (res->currency,
     245              :                  dn->currency,
     246              :                  TALER_CURRENCY_LEN);
     247           57 :   GNUNET_assert (GNUNET_YES ==
     248              :                  TALER_amount_is_valid (res));
     249           57 : }
     250              : 
     251              : 
     252              : enum GNUNET_GenericReturnValue
     253        58641 : TALER_amount_set_zero (const char *cur,
     254              :                        struct TALER_Amount *amount)
     255              : {
     256              :   char tmp[TALER_CURRENCY_LEN];
     257              :   size_t slen;
     258              : 
     259        58641 :   if (GNUNET_OK !=
     260        58641 :       TALER_check_currency (cur))
     261            0 :     return GNUNET_SYSERR;
     262        58641 :   slen = strlen (cur);
     263              :   /* make a copy of 'cur' to 'tmp' as the memset may clobber cur
     264              :      if cur aliases &amount->currency! */
     265        58641 :   memcpy (tmp,
     266              :           cur,
     267              :           slen);
     268        58641 :   memset (amount,
     269              :           0,
     270              :           sizeof (struct TALER_Amount));
     271       343536 :   for (unsigned int i = 0; i<slen; i++)
     272       284895 :     amount->currency[i] = tmp[i];
     273        58641 :   return GNUNET_OK;
     274              : }
     275              : 
     276              : 
     277              : enum GNUNET_GenericReturnValue
     278       378178 : TALER_amount_is_valid (const struct TALER_Amount *amount)
     279              : {
     280       378178 :   if (amount->value > TALER_AMOUNT_MAX_VALUE)
     281              :   {
     282            2 :     GNUNET_break (0);
     283            2 :     return GNUNET_SYSERR;
     284              :   }
     285       378176 :   return ('\0' != amount->currency[0]) ? GNUNET_OK : GNUNET_NO;
     286              : }
     287              : 
     288              : 
     289              : enum GNUNET_GenericReturnValue
     290            0 : TALER_amount_max (struct TALER_Amount *ma,
     291              :                   const struct TALER_Amount *a1,
     292              :                   const struct TALER_Amount *a2)
     293              : {
     294            0 :   if (GNUNET_OK !=
     295            0 :       TALER_amount_cmp_currency (a1,
     296              :                                  a2))
     297              :   {
     298            0 :     memset (ma,
     299              :             0,
     300              :             sizeof (*ma));
     301            0 :     return GNUNET_SYSERR;
     302              :   }
     303            0 :   if (1 == TALER_amount_cmp (a1,
     304              :                              a2))
     305            0 :     *ma = *a1;
     306              :   else
     307            0 :     *ma = *a2;
     308            0 :   return GNUNET_OK;
     309              : }
     310              : 
     311              : 
     312              : enum GNUNET_GenericReturnValue
     313            0 : TALER_amount_min (struct TALER_Amount *mi,
     314              :                   const struct TALER_Amount *a1,
     315              :                   const struct TALER_Amount *a2)
     316              : {
     317            0 :   if (GNUNET_OK !=
     318            0 :       TALER_amount_cmp_currency (a1,
     319              :                                  a2))
     320              :   {
     321            0 :     memset (mi,
     322              :             0,
     323              :             sizeof (*mi));
     324            0 :     return GNUNET_SYSERR;
     325              :   }
     326            0 :   if (1 == TALER_amount_cmp (a1,
     327              :                              a2))
     328            0 :     *mi = *a2;
     329              :   else
     330            0 :     *mi = *a1;
     331            0 :   return GNUNET_OK;
     332              : }
     333              : 
     334              : 
     335              : bool
     336        65679 : TALER_amount_is_zero (const struct TALER_Amount *amount)
     337              : {
     338        65679 :   if (GNUNET_OK !=
     339        65679 :       TALER_amount_is_valid (amount))
     340            0 :     return false;
     341              :   return
     342        89920 :     (0 == amount->value) &&
     343        24241 :     (0 == amount->fraction);
     344              : }
     345              : 
     346              : 
     347              : enum GNUNET_GenericReturnValue
     348         5736 : TALER_amount_is_currency (const struct TALER_Amount *amount,
     349              :                           const char *currency)
     350              : {
     351         5736 :   if (GNUNET_OK !=
     352         5736 :       TALER_amount_is_valid (amount))
     353            0 :     return GNUNET_SYSERR;
     354         5736 :   return (0 == strcasecmp (currency,
     355         5736 :                            amount->currency))
     356              :          ? GNUNET_OK
     357         5736 :          : GNUNET_NO;
     358              : }
     359              : 
     360              : 
     361              : /**
     362              :  * Test if @a a is valid, NBO variant.
     363              :  *
     364              :  * @param a amount to test
     365              :  * @return #GNUNET_YES if valid,
     366              :  *         #GNUNET_NO if invalid
     367              :  */
     368              : static enum GNUNET_GenericReturnValue
     369            0 : test_valid_nbo (const struct TALER_AmountNBO *a)
     370              : {
     371            0 :   return ('\0' != a->currency[0]) ? GNUNET_YES : GNUNET_NO;
     372              : }
     373              : 
     374              : 
     375              : enum GNUNET_GenericReturnValue
     376        59160 : TALER_amount_cmp_currency (const struct TALER_Amount *a1,
     377              :                            const struct TALER_Amount *a2)
     378              : {
     379       118320 :   if ( (GNUNET_NO == TALER_amount_is_valid (a1)) ||
     380        59160 :        (GNUNET_NO == TALER_amount_is_valid (a2)) )
     381            0 :     return GNUNET_SYSERR;
     382        59160 :   if (0 == strcasecmp (a1->currency,
     383        59160 :                        a2->currency))
     384        59158 :     return GNUNET_YES;
     385            2 :   return GNUNET_NO;
     386              : }
     387              : 
     388              : 
     389              : enum GNUNET_GenericReturnValue
     390            0 : TALER_amount_cmp_currency_nbo (const struct TALER_AmountNBO *a1,
     391              :                                const struct TALER_AmountNBO *a2)
     392              : {
     393            0 :   if ( (GNUNET_NO == test_valid_nbo (a1)) ||
     394            0 :        (GNUNET_NO == test_valid_nbo (a2)) )
     395            0 :     return GNUNET_SYSERR;
     396            0 :   if (0 == strcasecmp (a1->currency,
     397            0 :                        a2->currency))
     398            0 :     return GNUNET_YES;
     399            0 :   return GNUNET_NO;
     400              : }
     401              : 
     402              : 
     403              : int
     404        39940 : TALER_amount_cmp (const struct TALER_Amount *a1,
     405              :                   const struct TALER_Amount *a2)
     406              : {
     407              :   struct TALER_Amount n1;
     408              :   struct TALER_Amount n2;
     409              : 
     410        39940 :   GNUNET_assert (GNUNET_YES ==
     411              :                  TALER_amount_cmp_currency (a1,
     412              :                                             a2));
     413        39940 :   n1 = *a1;
     414        39940 :   n2 = *a2;
     415        39940 :   GNUNET_assert (GNUNET_SYSERR !=
     416              :                  TALER_amount_normalize (&n1));
     417        39940 :   GNUNET_assert (GNUNET_SYSERR !=
     418              :                  TALER_amount_normalize (&n2));
     419        39940 :   if (n1.value == n2.value)
     420              :   {
     421        37050 :     if (n1.fraction < n2.fraction)
     422           99 :       return -1;
     423        36951 :     if (n1.fraction > n2.fraction)
     424        16618 :       return 1;
     425        20333 :     return 0;
     426              :   }
     427         2890 :   if (n1.value < n2.value)
     428         1505 :     return -1;
     429         1385 :   return 1;
     430              : }
     431              : 
     432              : 
     433              : int
     434            0 : TALER_amount_cmp_nbo (const struct TALER_AmountNBO *a1,
     435              :                       const struct TALER_AmountNBO *a2)
     436              : {
     437              :   struct TALER_Amount h1;
     438              :   struct TALER_Amount h2;
     439              : 
     440            0 :   TALER_amount_ntoh (&h1,
     441              :                      a1);
     442            0 :   TALER_amount_ntoh (&h2,
     443              :                      a2);
     444            0 :   return TALER_amount_cmp (&h1,
     445              :                            &h2);
     446              : }
     447              : 
     448              : 
     449              : enum TALER_AmountArithmeticResult
     450        17131 : TALER_amount_subtract (struct TALER_Amount *diff,
     451              :                        const struct TALER_Amount *a1,
     452              :                        const struct TALER_Amount *a2)
     453              : {
     454              :   struct TALER_Amount n1;
     455              :   struct TALER_Amount n2;
     456              : 
     457        17131 :   if (GNUNET_YES !=
     458        17131 :       TALER_amount_cmp_currency (a1,
     459              :                                  a2))
     460              :   {
     461            1 :     invalidate (diff);
     462            1 :     return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE;
     463              :   }
     464              :   /* make local copies to avoid aliasing problems between
     465              :      diff and a1/a2 */
     466        17130 :   n1 = *a1;
     467        17130 :   n2 = *a2;
     468        34260 :   if ( (GNUNET_SYSERR == TALER_amount_normalize (&n1)) ||
     469        17130 :        (GNUNET_SYSERR == TALER_amount_normalize (&n2)) )
     470              :   {
     471            0 :     invalidate (diff);
     472            0 :     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
     473              :   }
     474              : 
     475        17130 :   if (n1.fraction < n2.fraction)
     476              :   {
     477          287 :     if (0 == n1.value)
     478              :     {
     479            6 :       invalidate (diff);
     480            6 :       return TALER_AAR_INVALID_NEGATIVE_RESULT;
     481              :     }
     482          281 :     n1.fraction += TALER_AMOUNT_FRAC_BASE;
     483          281 :     n1.value--;
     484              :   }
     485        17124 :   if (n1.value < n2.value)
     486              :   {
     487            1 :     invalidate (diff);
     488            1 :     return TALER_AAR_INVALID_NEGATIVE_RESULT;
     489              :   }
     490        17123 :   GNUNET_assert (GNUNET_OK ==
     491              :                  TALER_amount_set_zero (n1.currency,
     492              :                                         diff));
     493        17123 :   GNUNET_assert (n1.fraction >= n2.fraction);
     494        17123 :   diff->fraction = n1.fraction - n2.fraction;
     495        17123 :   GNUNET_assert (n1.value >= n2.value);
     496        17123 :   diff->value = n1.value - n2.value;
     497        17123 :   if ( (0 == diff->fraction) &&
     498           89 :        (0 == diff->value) )
     499           18 :     return TALER_AAR_RESULT_ZERO;
     500        17105 :   return TALER_AAR_RESULT_POSITIVE;
     501              : }
     502              : 
     503              : 
     504              : enum TALER_AmountArithmeticResult
     505         1980 : TALER_amount_add (struct TALER_Amount *sum,
     506              :                   const struct TALER_Amount *a1,
     507              :                   const struct TALER_Amount *a2)
     508              : {
     509              :   struct TALER_Amount n1;
     510              :   struct TALER_Amount n2;
     511              :   struct TALER_Amount res;
     512              : 
     513         1980 :   if (GNUNET_YES !=
     514         1980 :       TALER_amount_cmp_currency (a1,
     515              :                                  a2))
     516              :   {
     517            0 :     invalidate (sum);
     518            0 :     return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE;
     519              :   }
     520              :   /* make local copies to avoid aliasing problems between
     521              :      diff and a1/a2 */
     522         1980 :   n1 = *a1;
     523         1980 :   n2 = *a2;
     524         1980 :   if ( (GNUNET_SYSERR ==
     525         3960 :         TALER_amount_normalize (&n1)) ||
     526              :        (GNUNET_SYSERR ==
     527         1980 :         TALER_amount_normalize (&n2)) )
     528              :   {
     529            0 :     invalidate (sum);
     530            0 :     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
     531              :   }
     532              : 
     533         1980 :   GNUNET_assert (GNUNET_OK ==
     534              :                  TALER_amount_set_zero (a1->currency,
     535              :                                         &res));
     536         1980 :   res.value = n1.value + n2.value;
     537         1980 :   if (res.value < n1.value)
     538              :   {
     539              :     /* integer overflow */
     540            0 :     invalidate (sum);
     541            0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     542              :   }
     543         1980 :   if (res.value > TALER_AMOUNT_MAX_VALUE)
     544              :   {
     545              :     /* too large to be legal */
     546            0 :     invalidate (sum);
     547            0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     548              :   }
     549         1980 :   res.fraction = n1.fraction + n2.fraction;
     550         1980 :   if (GNUNET_SYSERR ==
     551         1980 :       TALER_amount_normalize (&res))
     552              :   {
     553              :     /* integer overflow via carry from fraction */
     554            1 :     invalidate (sum);
     555            1 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     556              :   }
     557         1979 :   *sum = res;
     558         1979 :   if ( (0 == sum->fraction) &&
     559          340 :        (0 == sum->value) )
     560            8 :     return TALER_AAR_RESULT_ZERO;
     561         1971 :   return TALER_AAR_RESULT_POSITIVE;
     562              : }
     563              : 
     564              : 
     565              : enum GNUNET_GenericReturnValue
     566       142942 : TALER_amount_normalize (struct TALER_Amount *amount)
     567              : {
     568              :   uint32_t overflow;
     569              : 
     570       142942 :   if (GNUNET_YES != TALER_amount_is_valid (amount))
     571            3 :     return GNUNET_SYSERR;
     572       142939 :   if (amount->fraction < TALER_AMOUNT_FRAC_BASE)
     573       142872 :     return GNUNET_NO;
     574           67 :   overflow = amount->fraction / TALER_AMOUNT_FRAC_BASE;
     575           67 :   amount->fraction %= TALER_AMOUNT_FRAC_BASE;
     576           67 :   amount->value += overflow;
     577           67 :   if ( (amount->value < overflow) ||
     578           67 :        (amount->value > TALER_AMOUNT_MAX_VALUE) )
     579              :   {
     580            1 :     invalidate (amount);
     581            1 :     return GNUNET_SYSERR;
     582              :   }
     583           66 :   return GNUNET_OK;
     584              : }
     585              : 
     586              : 
     587              : /**
     588              :  * Convert the fraction of @a amount to a string in decimals.
     589              :  *
     590              :  * @param amount value to convert
     591              :  * @param[out] tail where to write the result
     592              :  */
     593              : static void
     594        20682 : amount_to_tail (const struct TALER_Amount *amount,
     595              :                 char tail[TALER_AMOUNT_FRAC_LEN + 1])
     596              : {
     597        20682 :   uint32_t n = amount->fraction;
     598              :   unsigned int i;
     599              : 
     600       161172 :   for (i = 0; (i < TALER_AMOUNT_FRAC_LEN) && (0 != n); i++)
     601              :   {
     602       140490 :     tail[i] = '0' + (n / (TALER_AMOUNT_FRAC_BASE / 10));
     603       140490 :     n = (n * 10) % (TALER_AMOUNT_FRAC_BASE);
     604              :   }
     605        20682 :   tail[i] = '\0';
     606        20682 : }
     607              : 
     608              : 
     609              : char *
     610         5286 : TALER_amount_to_string (const struct TALER_Amount *amount)
     611              : {
     612              :   char *result;
     613              :   struct TALER_Amount norm;
     614              : 
     615         5286 :   if (GNUNET_YES != TALER_amount_is_valid (amount))
     616            1 :     return NULL;
     617         5285 :   norm = *amount;
     618         5285 :   GNUNET_break (GNUNET_SYSERR !=
     619              :                 TALER_amount_normalize (&norm));
     620         5285 :   if (0 != norm.fraction)
     621              :   {
     622              :     char tail[TALER_AMOUNT_FRAC_LEN + 1];
     623              : 
     624         3576 :     amount_to_tail (&norm,
     625              :                     tail);
     626         3576 :     GNUNET_asprintf (&result,
     627              :                      "%s:%llu.%s",
     628              :                      norm.currency,
     629         3576 :                      (unsigned long long) norm.value,
     630              :                      tail);
     631              :   }
     632              :   else
     633              :   {
     634         1709 :     GNUNET_asprintf (&result,
     635              :                      "%s:%llu",
     636              :                      norm.currency,
     637         1709 :                      (unsigned long long) norm.value);
     638              :   }
     639         5285 :   return result;
     640              : }
     641              : 
     642              : 
     643              : const char *
     644        17565 : TALER_amount2s (const struct TALER_Amount *amount)
     645              : {
     646              :   /* 24 is sufficient for a uint64_t value in decimal; 3 is for ":.\0" */
     647              :   static TALER_THREAD_LOCAL char result[TALER_AMOUNT_FRAC_LEN
     648              :                                         + TALER_CURRENCY_LEN + 3 + 24];
     649              :   struct TALER_Amount norm;
     650              : 
     651        17565 :   if (GNUNET_YES != TALER_amount_is_valid (amount))
     652           12 :     return NULL;
     653        17553 :   norm = *amount;
     654        17553 :   GNUNET_break (GNUNET_SYSERR !=
     655              :                 TALER_amount_normalize (&norm));
     656        17553 :   if (0 != norm.fraction)
     657              :   {
     658              :     char tail[TALER_AMOUNT_FRAC_LEN + 1];
     659              : 
     660        17106 :     amount_to_tail (&norm,
     661              :                     tail);
     662        17106 :     GNUNET_snprintf (result,
     663              :                      sizeof (result),
     664              :                      "%s:%llu.%s",
     665              :                      norm.currency,
     666        17106 :                      (unsigned long long) norm.value,
     667              :                      tail);
     668              :   }
     669              :   else
     670              :   {
     671          447 :     GNUNET_snprintf (result,
     672              :                      sizeof (result),
     673              :                      "%s:%llu",
     674              :                      norm.currency,
     675          447 :                      (unsigned long long) norm.value);
     676              :   }
     677        17553 :   return result;
     678              : }
     679              : 
     680              : 
     681              : void
     682            4 : TALER_amount_divide (struct TALER_Amount *result,
     683              :                      const struct TALER_Amount *dividend,
     684              :                      uint32_t divisor)
     685              : {
     686              :   uint64_t modr;
     687              : 
     688            4 :   GNUNET_assert (0 != divisor); /* division by zero is discouraged */
     689            4 :   *result = *dividend;
     690              :   /* in case @a dividend was not yet normalized */
     691            4 :   GNUNET_assert (GNUNET_SYSERR !=
     692              :                  TALER_amount_normalize (result));
     693            4 :   if (1 == divisor)
     694            1 :     return;
     695            3 :   modr = result->value % divisor;
     696            3 :   result->value /= divisor;
     697              :   /* modr fits into 32 bits, so we can safely multiply by (<32-bit) base and add fraction! */
     698            3 :   modr = (modr * TALER_AMOUNT_FRAC_BASE) + result->fraction;
     699            3 :   result->fraction = (uint32_t) (modr / divisor);
     700              :   /* 'fraction' could now be larger than #TALER_AMOUNT_FRAC_BASE, so we must normalize */
     701            3 :   GNUNET_assert (GNUNET_SYSERR !=
     702              :                  TALER_amount_normalize (result));
     703              : }
     704              : 
     705              : 
     706              : int
     707            5 : TALER_amount_divide2 (const struct TALER_Amount *dividend,
     708              :                       const struct TALER_Amount *divisor)
     709              : {
     710              :   double approx;
     711              :   double d;
     712              :   double r;
     713              :   int ret;
     714              :   struct TALER_Amount tmp;
     715              :   struct TALER_Amount nxt;
     716              : 
     717            5 :   if (GNUNET_YES !=
     718            5 :       TALER_amount_cmp_currency (dividend,
     719              :                                  divisor))
     720              :   {
     721            0 :     GNUNET_break (0);
     722            0 :     return -1;
     723              :   }
     724            5 :   if ( (0 == divisor->fraction) &&
     725            2 :        (0 == divisor->value) )
     726            1 :     return INT_MAX;
     727              :   /* first, get rounded approximation */
     728            4 :   d = ((double) dividend->value) * ((double) TALER_AMOUNT_FRAC_BASE)
     729            4 :       + ( (double) dividend->fraction);
     730            4 :   r = ((double) divisor->value) * ((double) TALER_AMOUNT_FRAC_BASE)
     731            4 :       + ( (double) divisor->fraction);
     732            4 :   approx = d / r;
     733            4 :   if (approx > ((double) INT_MAX))
     734            0 :     return INT_MAX; /* 'infinity' */
     735              :   /* round down */
     736            4 :   if (approx < 2)
     737            1 :     ret = 0;
     738              :   else
     739            3 :     ret = (int) approx - 2;
     740              :   /* Now do *exact* calculation, using well rounded-down factor as starting
     741              :      point to avoid having to do too many steps. */
     742            4 :   GNUNET_assert (0 <=
     743              :                  TALER_amount_multiply (&tmp,
     744              :                                         divisor,
     745              :                                         ret));
     746              :   /* in practice, this loop will only run for one or two iterations */
     747              :   while (1)
     748              :   {
     749           10 :     GNUNET_assert (0 <=
     750              :                    TALER_amount_add (&nxt,
     751              :                                      &tmp,
     752              :                                      divisor));
     753           10 :     if (1 ==
     754           10 :         TALER_amount_cmp (&nxt,
     755              :                           dividend))
     756            4 :       break; /* nxt > dividend */
     757            6 :     ret++;
     758            6 :     tmp = nxt;
     759              :   }
     760            4 :   return ret;
     761              : }
     762              : 
     763              : 
     764              : enum TALER_AmountArithmeticResult
     765            6 : TALER_amount_multiply (struct TALER_Amount *result,
     766              :                        const struct TALER_Amount *amount,
     767              :                        uint32_t factor)
     768              : {
     769            6 :   struct TALER_Amount in = *amount;
     770              : 
     771            6 :   if (GNUNET_SYSERR ==
     772            6 :       TALER_amount_normalize (&in))
     773            0 :     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
     774            6 :   GNUNET_memcpy (result->currency,
     775              :                  amount->currency,
     776              :                  TALER_CURRENCY_LEN);
     777            6 :   if ( (0 == factor) ||
     778            5 :        ( (0 == in.value) &&
     779            1 :          (0 == in.fraction) ) )
     780              :   {
     781            2 :     result->value = 0;
     782            2 :     result->fraction = 0;
     783            2 :     return TALER_AAR_RESULT_ZERO;
     784              :   }
     785            4 :   result->value = in.value * ((uint64_t) factor);
     786            4 :   if (in.value != result->value / factor)
     787            0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     788              :   {
     789              :     /* This multiplication cannot overflow since both inputs are 32-bit values */
     790            4 :     uint64_t tmp = ((uint64_t) factor) * ((uint64_t) in.fraction);
     791              :     uint64_t res;
     792              : 
     793            4 :     res = tmp / TALER_AMOUNT_FRAC_BASE;
     794              :     /* check for overflow */
     795            4 :     if (result->value + res < result->value)
     796            0 :       return TALER_AAR_INVALID_RESULT_OVERFLOW;
     797            4 :     result->value += res;
     798            4 :     result->fraction = tmp % TALER_AMOUNT_FRAC_BASE;
     799              :   }
     800            4 :   if (result->value > TALER_AMOUNT_MAX_VALUE)
     801            0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     802              :   /* This check should be redundant... */
     803            4 :   GNUNET_assert (GNUNET_SYSERR !=
     804              :                  TALER_amount_normalize (result));
     805            4 :   return TALER_AAR_RESULT_POSITIVE;
     806              : }
     807              : 
     808              : 
     809              : enum GNUNET_GenericReturnValue
     810           63 : TALER_amount_round_down (struct TALER_Amount *amount,
     811              :                          const struct TALER_Amount *round_unit)
     812              : {
     813           63 :   if (GNUNET_OK !=
     814           63 :       TALER_amount_cmp_currency (amount,
     815              :                                  round_unit))
     816              :   {
     817            0 :     GNUNET_break (0);
     818            0 :     return GNUNET_SYSERR;
     819              :   }
     820           63 :   if ( (0 != round_unit->fraction) &&
     821           62 :        (0 != round_unit->value) )
     822              :   {
     823            0 :     GNUNET_break (0);
     824            0 :     return GNUNET_SYSERR;
     825              :   }
     826           63 :   if ( (0 == round_unit->fraction) &&
     827            1 :        (0 == round_unit->value) )
     828            0 :     return GNUNET_NO; /* no rounding requested */
     829           63 :   if (0 != round_unit->fraction)
     830              :   {
     831              :     uint32_t delta;
     832              : 
     833           62 :     delta = amount->fraction % round_unit->fraction;
     834           62 :     if (0 == delta)
     835           58 :       return GNUNET_NO;
     836            4 :     amount->fraction -= delta;
     837              :   }
     838            5 :   if (0 != round_unit->value)
     839              :   {
     840              :     uint64_t delta;
     841              : 
     842            1 :     delta = amount->value % round_unit->value;
     843            1 :     if (0 == delta)
     844            0 :       return GNUNET_NO;
     845            1 :     amount->value -= delta;
     846            1 :     amount->fraction = 0;
     847              :   }
     848            5 :   return GNUNET_OK;
     849              : }
     850              : 
     851              : 
     852              : /* end of amount.c */
        

Generated by: LCOV version 2.0-1