LCOV - code coverage report
Current view: top level - util - amount.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 280 325 86.2 %
Date: 2021-08-30 06:43:37 Functions: 21 22 95.5 %
Legend: Lines: hit not hit

          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        4743 : TALER_string_to_amount (const char *str,
      44             :                         struct TALER_Amount *amount)
      45             : {
      46             :   int n;
      47             :   uint32_t b;
      48             :   const char *colon;
      49             :   const char *value;
      50             : 
      51             :   /* skip leading whitespace */
      52        4745 :   while (isspace ( (unsigned char) str[0]))
      53           2 :     str++;
      54        4743 :   if ('\0' == str[0])
      55             :   {
      56           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
      57             :                 "Null before currency\n");
      58           0 :     invalidate (amount);
      59           0 :     return GNUNET_SYSERR;
      60             :   }
      61             : 
      62             :   /* parse currency */
      63        4743 :   colon = strchr (str, (int) ':');
      64        4743 :   if ( (NULL == colon) ||
      65        4743 :        ((colon - str) >= TALER_CURRENCY_LEN) )
      66             :   {
      67           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
      68             :                 "Invalid currency specified before colon: `%s'\n",
      69             :                 str);
      70           0 :     invalidate (amount);
      71           0 :     return GNUNET_SYSERR;
      72             :   }
      73             : 
      74        4743 :   GNUNET_assert (TALER_CURRENCY_LEN > (colon - str));
      75        4743 :   memcpy (amount->currency,
      76             :           str,
      77        4743 :           colon - str);
      78             :   /* 0-terminate *and* normalize buffer by setting everything to '\0' */
      79        4743 :   memset (&amount->currency [colon - str],
      80             :           0,
      81        4743 :           TALER_CURRENCY_LEN - (colon - str));
      82             : 
      83             :   /* skip colon */
      84        4743 :   value = colon + 1;
      85        4743 :   if ('\0' == value[0])
      86             :   {
      87           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
      88             :                 "Actual value missing in amount `%s'\n",
      89             :                 str);
      90           0 :     invalidate (amount);
      91           0 :     return GNUNET_SYSERR;
      92             :   }
      93             : 
      94        4743 :   amount->value = 0;
      95        4743 :   amount->fraction = 0;
      96             : 
      97             :   /* parse value */
      98        9752 :   while ('.' != *value)
      99             :   {
     100        5869 :     if ('\0' == *value)
     101             :     {
     102             :       /* we are done */
     103         858 :       return GNUNET_OK;
     104             :     }
     105        5011 :     if ( (*value < '0') ||
     106        5011 :          (*value > '9') )
     107             :     {
     108           1 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     109             :                   "Invalid character `%c' in amount `%s'\n",
     110             :                   (int) *value,
     111             :                   str);
     112           1 :       invalidate (amount);
     113           1 :       return GNUNET_SYSERR;
     114             :     }
     115        5010 :     n = *value - '0';
     116        5010 :     if ( (amount->value * 10 < amount->value) ||
     117        5010 :          (amount->value * 10 + n < amount->value) ||
     118        5010 :          (amount->value > TALER_AMOUNT_MAX_VALUE) ||
     119        5010 :          (amount->value * 10 + n > TALER_AMOUNT_MAX_VALUE) )
     120             :     {
     121           1 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     122             :                   "Value specified in amount `%s' is too large\n",
     123             :                   str);
     124           1 :       invalidate (amount);
     125           1 :       return GNUNET_SYSERR;
     126             :     }
     127        5009 :     amount->value = (amount->value * 10) + n;
     128        5009 :     value++;
     129             :   }
     130             : 
     131             :   /* skip the dot */
     132        3883 :   value++;
     133             : 
     134             :   /* parse fraction */
     135        3883 :   if ('\0' == *value)
     136             :   {
     137           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     138             :                 "Amount `%s' ends abruptly after `.'\n",
     139             :                 str);
     140           0 :     invalidate (amount);
     141           0 :     return GNUNET_SYSERR;
     142             :   }
     143        3883 :   b = TALER_AMOUNT_FRAC_BASE / 10;
     144       11546 :   while ('\0' != *value)
     145             :   {
     146        7667 :     if (0 == b)
     147             :     {
     148           1 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     149             :                   "Fractional value too small (only %u digits supported) in amount `%s'\n",
     150             :                   (unsigned int) TALER_AMOUNT_FRAC_LEN,
     151             :                   str);
     152           1 :       invalidate (amount);
     153           1 :       return GNUNET_SYSERR;
     154             :     }
     155        7666 :     if ( (*value < '0') ||
     156        7666 :          (*value > '9') )
     157             :     {
     158           3 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     159             :                   "Error after dot\n");
     160           3 :       invalidate (amount);
     161           3 :       return GNUNET_SYSERR;
     162             :     }
     163        7663 :     n = *value - '0';
     164        7663 :     amount->fraction += n * b;
     165        7663 :     b /= 10;
     166        7663 :     value++;
     167             :   }
     168        3879 :   return GNUNET_OK;
     169             : }
     170             : 
     171             : 
     172             : enum GNUNET_GenericReturnValue
     173         150 : TALER_string_to_amount_nbo (const char *str,
     174             :                             struct TALER_AmountNBO *amount_nbo)
     175             : {
     176             :   struct TALER_Amount amount;
     177             : 
     178         150 :   if (GNUNET_OK !=
     179         150 :       TALER_string_to_amount (str,
     180             :                               &amount))
     181           0 :     return GNUNET_SYSERR;
     182         150 :   TALER_amount_hton (amount_nbo,
     183             :                      &amount);
     184         150 :   return GNUNET_OK;
     185             : }
     186             : 
     187             : 
     188             : void
     189        9656 : TALER_amount_hton (struct TALER_AmountNBO *res,
     190             :                    const struct TALER_Amount *d)
     191             : {
     192        9656 :   GNUNET_assert (GNUNET_YES ==
     193             :                  TALER_amount_is_valid (d));
     194        9656 :   res->value = GNUNET_htonll (d->value);
     195        9656 :   res->fraction = htonl (d->fraction);
     196        9656 :   memcpy (res->currency,
     197        9656 :           d->currency,
     198             :           TALER_CURRENCY_LEN);
     199        9656 : }
     200             : 
     201             : 
     202             : void
     203      100150 : TALER_amount_ntoh (struct TALER_Amount *res,
     204             :                    const struct TALER_AmountNBO *dn)
     205             : {
     206      100150 :   res->value = GNUNET_ntohll (dn->value);
     207      100150 :   res->fraction = ntohl (dn->fraction);
     208      100150 :   memcpy (res->currency,
     209      100150 :           dn->currency,
     210             :           TALER_CURRENCY_LEN);
     211      100150 :   GNUNET_assert (GNUNET_YES ==
     212             :                  TALER_amount_is_valid (res));
     213      100150 : }
     214             : 
     215             : 
     216             : enum GNUNET_GenericReturnValue
     217       29374 : TALER_amount_set_zero (const char *cur,
     218             :                        struct TALER_Amount *amount)
     219             : {
     220             :   size_t slen;
     221             : 
     222       29374 :   slen = strlen (cur);
     223       29374 :   if (slen >= TALER_CURRENCY_LEN)
     224           0 :     return GNUNET_SYSERR;
     225       29374 :   memset (amount,
     226             :           0,
     227             :           sizeof (struct TALER_Amount));
     228       29374 :   memcpy (amount->currency,
     229             :           cur,
     230             :           slen);
     231       29374 :   return GNUNET_OK;
     232             : }
     233             : 
     234             : 
     235             : enum GNUNET_GenericReturnValue
     236      436380 : TALER_amount_is_valid (const struct TALER_Amount *amount)
     237             : {
     238      436380 :   if (amount->value > TALER_AMOUNT_MAX_VALUE)
     239           2 :     return GNUNET_SYSERR;
     240      436378 :   return ('\0' != amount->currency[0]) ? GNUNET_OK : GNUNET_NO;
     241             : }
     242             : 
     243             : 
     244             : /**
     245             :  * Test if @a a is valid, NBO variant.
     246             :  *
     247             :  * @param a amount to test
     248             :  * @return #GNUNET_YES if valid,
     249             :  *         #GNUNET_NO if invalid
     250             :  */
     251             : static enum GNUNET_GenericReturnValue
     252         256 : test_valid_nbo (const struct TALER_AmountNBO *a)
     253             : {
     254         256 :   return ('\0' != a->currency[0]) ? GNUNET_YES : GNUNET_NO;
     255             : }
     256             : 
     257             : 
     258             : enum GNUNET_GenericReturnValue
     259       28347 : TALER_amount_cmp_currency (const struct TALER_Amount *a1,
     260             :                            const struct TALER_Amount *a2)
     261             : {
     262       56694 :   if ( (GNUNET_NO == TALER_amount_is_valid (a1)) ||
     263       28347 :        (GNUNET_NO == TALER_amount_is_valid (a2)) )
     264           0 :     return GNUNET_SYSERR;
     265       28347 :   if (0 == strcasecmp (a1->currency,
     266       28347 :                        a2->currency))
     267       28345 :     return GNUNET_YES;
     268           2 :   return GNUNET_NO;
     269             : }
     270             : 
     271             : 
     272             : enum GNUNET_GenericReturnValue
     273         128 : TALER_amount_cmp_currency_nbo (const struct TALER_AmountNBO *a1,
     274             :                                const struct TALER_AmountNBO *a2)
     275             : {
     276         256 :   if ( (GNUNET_NO == test_valid_nbo (a1)) ||
     277         128 :        (GNUNET_NO == test_valid_nbo (a2)) )
     278           0 :     return GNUNET_SYSERR;
     279         128 :   if (0 == strcasecmp (a1->currency,
     280         128 :                        a2->currency))
     281         128 :     return GNUNET_YES;
     282           0 :   return GNUNET_NO;
     283             : }
     284             : 
     285             : 
     286             : int
     287        4232 : TALER_amount_cmp (const struct TALER_Amount *a1,
     288             :                   const struct TALER_Amount *a2)
     289             : {
     290             :   struct TALER_Amount n1;
     291             :   struct TALER_Amount n2;
     292             : 
     293        4232 :   GNUNET_assert (GNUNET_YES ==
     294             :                  TALER_amount_cmp_currency (a1,
     295             :                                             a2));
     296        4232 :   n1 = *a1;
     297        4232 :   n2 = *a2;
     298        4232 :   GNUNET_assert (GNUNET_SYSERR !=
     299             :                  TALER_amount_normalize (&n1));
     300        4232 :   GNUNET_assert (GNUNET_SYSERR !=
     301             :                  TALER_amount_normalize (&n2));
     302        4232 :   if (n1.value == n2.value)
     303             :   {
     304        2459 :     if (n1.fraction < n2.fraction)
     305          88 :       return -1;
     306        2371 :     if (n1.fraction > n2.fraction)
     307          40 :       return 1;
     308        2331 :     return 0;
     309             :   }
     310        1773 :   if (n1.value < n2.value)
     311         775 :     return -1;
     312         998 :   return 1;
     313             : }
     314             : 
     315             : 
     316             : int
     317           0 : TALER_amount_cmp_nbo (const struct TALER_AmountNBO *a1,
     318             :                       const struct TALER_AmountNBO *a2)
     319             : {
     320             :   struct TALER_Amount h1;
     321             :   struct TALER_Amount h2;
     322             : 
     323           0 :   TALER_amount_ntoh (&h1,
     324             :                      a1);
     325           0 :   TALER_amount_ntoh (&h2,
     326             :                      a2);
     327           0 :   return TALER_amount_cmp (&h1,
     328             :                            &h2);
     329             : }
     330             : 
     331             : 
     332             : enum TALER_AmountArithmeticResult
     333        1770 : TALER_amount_subtract (struct TALER_Amount *diff,
     334             :                        const struct TALER_Amount *a1,
     335             :                        const struct TALER_Amount *a2)
     336             : {
     337             :   struct TALER_Amount n1;
     338             :   struct TALER_Amount n2;
     339             : 
     340        1770 :   if (GNUNET_YES !=
     341        1770 :       TALER_amount_cmp_currency (a1,
     342             :                                  a2))
     343             :   {
     344           1 :     invalidate (diff);
     345           1 :     return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE;
     346             :   }
     347             :   /* make local copies to avoid aliasing problems between
     348             :      diff and a1/a2 */
     349        1769 :   n1 = *a1;
     350        1769 :   n2 = *a2;
     351        3538 :   if ( (GNUNET_SYSERR == TALER_amount_normalize (&n1)) ||
     352        1769 :        (GNUNET_SYSERR == TALER_amount_normalize (&n2)) )
     353             :   {
     354           0 :     invalidate (diff);
     355           0 :     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
     356             :   }
     357             : 
     358        1769 :   if (n1.fraction < n2.fraction)
     359             :   {
     360         582 :     if (0 == n1.value)
     361             :     {
     362           5 :       invalidate (diff);
     363           5 :       return TALER_AAR_INVALID_NEGATIVE_RESULT;
     364             :     }
     365         577 :     n1.fraction += TALER_AMOUNT_FRAC_BASE;
     366         577 :     n1.value--;
     367             :   }
     368        1764 :   if (n1.value < n2.value)
     369             :   {
     370          10 :     invalidate (diff);
     371          10 :     return TALER_AAR_INVALID_NEGATIVE_RESULT;
     372             :   }
     373        1754 :   GNUNET_assert (GNUNET_OK ==
     374             :                  TALER_amount_set_zero (n1.currency,
     375             :                                         diff));
     376        1754 :   GNUNET_assert (n1.fraction >= n2.fraction);
     377        1754 :   diff->fraction = n1.fraction - n2.fraction;
     378        1754 :   GNUNET_assert (n1.value >= n2.value);
     379        1754 :   diff->value = n1.value - n2.value;
     380        1754 :   if ( (0 == diff->fraction) &&
     381         463 :        (0 == diff->value) )
     382         270 :     return TALER_AAR_RESULT_ZERO;
     383        1484 :   return TALER_AAR_RESULT_POSITIVE;
     384             : }
     385             : 
     386             : 
     387             : enum TALER_AmountArithmeticResult
     388       21407 : TALER_amount_add (struct TALER_Amount *sum,
     389             :                   const struct TALER_Amount *a1,
     390             :                   const struct TALER_Amount *a2)
     391             : {
     392             :   struct TALER_Amount n1;
     393             :   struct TALER_Amount n2;
     394             :   struct TALER_Amount res;
     395             : 
     396       21407 :   if (GNUNET_YES !=
     397       21407 :       TALER_amount_cmp_currency (a1,
     398             :                                  a2))
     399             :   {
     400           0 :     invalidate (sum);
     401           0 :     return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE;
     402             :   }
     403             :   /* make local copies to avoid aliasing problems between
     404             :      diff and a1/a2 */
     405       21407 :   n1 = *a1;
     406       21407 :   n2 = *a2;
     407       21407 :   if ( (GNUNET_SYSERR ==
     408       42814 :         TALER_amount_normalize (&n1)) ||
     409             :        (GNUNET_SYSERR ==
     410       21407 :         TALER_amount_normalize (&n2)) )
     411             :   {
     412           0 :     invalidate (sum);
     413           0 :     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
     414             :   }
     415             : 
     416       21407 :   GNUNET_assert (GNUNET_OK ==
     417             :                  TALER_amount_set_zero (a1->currency,
     418             :                                         &res));
     419       21407 :   res.value = n1.value + n2.value;
     420       21407 :   if (res.value < n1.value)
     421             :   {
     422             :     /* integer overflow */
     423           0 :     invalidate (sum);
     424           0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     425             :   }
     426       21407 :   if (res.value > TALER_AMOUNT_MAX_VALUE)
     427             :   {
     428             :     /* too large to be legal */
     429           0 :     invalidate (sum);
     430           0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     431             :   }
     432       21407 :   res.fraction = n1.fraction + n2.fraction;
     433       21407 :   if (GNUNET_SYSERR ==
     434       21407 :       TALER_amount_normalize (&res))
     435             :   {
     436             :     /* integer overflow via carry from fraction */
     437           1 :     invalidate (sum);
     438           1 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     439             :   }
     440       21406 :   *sum = res;
     441       21406 :   if ( (0 == sum->fraction) &&
     442        1980 :        (0 == sum->value) )
     443          61 :     return TALER_AAR_RESULT_ZERO;
     444       21345 :   return TALER_AAR_RESULT_POSITIVE;
     445             : }
     446             : 
     447             : 
     448             : enum GNUNET_GenericReturnValue
     449      173059 : TALER_amount_normalize (struct TALER_Amount *amount)
     450             : {
     451             :   uint32_t overflow;
     452             : 
     453      173059 :   if (GNUNET_YES != TALER_amount_is_valid (amount))
     454           3 :     return GNUNET_SYSERR;
     455      173056 :   if (amount->fraction < TALER_AMOUNT_FRAC_BASE)
     456      172286 :     return GNUNET_NO;
     457         770 :   overflow = amount->fraction / TALER_AMOUNT_FRAC_BASE;
     458         770 :   amount->fraction %= TALER_AMOUNT_FRAC_BASE;
     459         770 :   amount->value += overflow;
     460         770 :   if ( (amount->value < overflow) ||
     461         770 :        (amount->value > TALER_AMOUNT_MAX_VALUE) )
     462             :   {
     463           1 :     invalidate (amount);
     464           1 :     return GNUNET_SYSERR;
     465             :   }
     466         769 :   return GNUNET_OK;
     467             : }
     468             : 
     469             : 
     470             : /**
     471             :  * Convert the fraction of @a amount to a string in decimals.
     472             :  *
     473             :  * @param amount value to convert
     474             :  * @param[out] tail where to write the result
     475             :  */
     476             : static void
     477       61003 : amount_to_tail (const struct TALER_Amount *amount,
     478             :                 char tail[TALER_AMOUNT_FRAC_LEN + 1])
     479             : {
     480       61003 :   uint32_t n = amount->fraction;
     481             :   unsigned int i;
     482             : 
     483      174281 :   for (i = 0; (i < TALER_AMOUNT_FRAC_LEN) && (0 != n); i++)
     484             :   {
     485      113278 :     tail[i] = '0' + (n / (TALER_AMOUNT_FRAC_BASE / 10));
     486      113278 :     n = (n * 10) % (TALER_AMOUNT_FRAC_BASE);
     487             :   }
     488       61003 :   tail[i] = '\0';
     489       61003 : }
     490             : 
     491             : 
     492             : char *
     493        6092 : TALER_amount_to_string (const struct TALER_Amount *amount)
     494             : {
     495             :   char *result;
     496             :   struct TALER_Amount norm;
     497             : 
     498        6092 :   if (GNUNET_YES != TALER_amount_is_valid (amount))
     499           1 :     return NULL;
     500        6091 :   norm = *amount;
     501        6091 :   GNUNET_break (GNUNET_SYSERR !=
     502             :                 TALER_amount_normalize (&norm));
     503        6091 :   if (0 != norm.fraction)
     504             :   {
     505             :     char tail[TALER_AMOUNT_FRAC_LEN + 1];
     506             : 
     507        2571 :     amount_to_tail (&norm,
     508             :                     tail);
     509        2571 :     GNUNET_asprintf (&result,
     510             :                      "%s:%llu.%s",
     511             :                      norm.currency,
     512        2571 :                      (unsigned long long) norm.value,
     513             :                      tail);
     514             :   }
     515             :   else
     516             :   {
     517        3520 :     GNUNET_asprintf (&result,
     518             :                      "%s:%llu",
     519             :                      norm.currency,
     520        3520 :                      (unsigned long long) norm.value);
     521             :   }
     522        6091 :   return result;
     523             : }
     524             : 
     525             : 
     526             : const char *
     527       90721 : TALER_amount2s (const struct TALER_Amount *amount)
     528             : {
     529             :   /* 24 is sufficient for a uint64_t value in decimal; 3 is for ":.\0" */
     530             :   static GNUNET_THREAD_LOCAL char result[TALER_AMOUNT_FRAC_LEN
     531             :                                          + TALER_CURRENCY_LEN + 3 + 24];
     532             :   struct TALER_Amount norm;
     533             : 
     534       90721 :   if (GNUNET_YES != TALER_amount_is_valid (amount))
     535           0 :     return NULL;
     536       90721 :   norm = *amount;
     537       90721 :   GNUNET_break (GNUNET_SYSERR !=
     538             :                 TALER_amount_normalize (&norm));
     539       90721 :   if (0 != norm.fraction)
     540             :   {
     541             :     char tail[TALER_AMOUNT_FRAC_LEN + 1];
     542             : 
     543       58432 :     amount_to_tail (&norm,
     544             :                     tail);
     545       58432 :     GNUNET_snprintf (result,
     546             :                      sizeof (result),
     547             :                      "%s:%llu.%s",
     548             :                      norm.currency,
     549       58432 :                      (unsigned long long) norm.value,
     550             :                      tail);
     551             :   }
     552             :   else
     553             :   {
     554       32289 :     GNUNET_snprintf (result,
     555             :                      sizeof (result),
     556             :                      "%s:%llu",
     557             :                      norm.currency,
     558       32289 :                      (unsigned long long) norm.value);
     559             :   }
     560       90721 :   return result;
     561             : }
     562             : 
     563             : 
     564             : void
     565           4 : TALER_amount_divide (struct TALER_Amount *result,
     566             :                      const struct TALER_Amount *dividend,
     567             :                      uint32_t divisor)
     568             : {
     569             :   uint64_t modr;
     570             : 
     571           4 :   GNUNET_assert (0 != divisor); /* division by zero is discouraged */
     572           4 :   *result = *dividend;
     573             :   /* in case @a dividend was not yet normalized */
     574           4 :   GNUNET_assert (GNUNET_SYSERR !=
     575             :                  TALER_amount_normalize (result));
     576           4 :   if (1 == divisor)
     577           1 :     return;
     578           3 :   modr = result->value % divisor;
     579           3 :   result->value /= divisor;
     580             :   /* modr fits into 32 bits, so we can safely multiply by (<32-bit) base and add fraction! */
     581           3 :   modr = (modr * TALER_AMOUNT_FRAC_BASE) + result->fraction;
     582           3 :   result->fraction = (uint32_t) (modr / divisor);
     583             :   /* 'fraction' could now be larger than #TALER_AMOUNT_FRAC_BASE, so we must normalize */
     584           3 :   GNUNET_assert (GNUNET_SYSERR !=
     585             :                  TALER_amount_normalize (result));
     586             : }
     587             : 
     588             : 
     589             : int
     590           5 : TALER_amount_divide2 (const struct TALER_Amount *dividend,
     591             :                       const struct TALER_Amount *divisor)
     592             : {
     593             :   double approx;
     594             :   double d;
     595             :   double r;
     596             :   int ret;
     597             :   struct TALER_Amount tmp;
     598             :   struct TALER_Amount nxt;
     599             : 
     600           5 :   if (GNUNET_YES !=
     601           5 :       TALER_amount_cmp_currency (dividend,
     602             :                                  divisor))
     603             :   {
     604           0 :     GNUNET_break (0);
     605           0 :     return -1;
     606             :   }
     607           5 :   if ( (0 == divisor->fraction) &&
     608           2 :        (0 == divisor->value) )
     609           1 :     return INT_MAX;
     610             :   /* first, get rounded approximation */
     611           4 :   d = ((double) dividend->value) * ((double) TALER_AMOUNT_FRAC_BASE)
     612           4 :       + ( (double) dividend->fraction);
     613           4 :   r = ((double) divisor->value) * ((double) TALER_AMOUNT_FRAC_BASE)
     614           4 :       + ( (double) divisor->fraction);
     615           4 :   approx = d / r;
     616           4 :   if (approx > ((double) INT_MAX))
     617           0 :     return INT_MAX; /* 'infinity' */
     618             :   /* round down */
     619           4 :   if (approx < 2)
     620           1 :     ret = 0;
     621             :   else
     622           3 :     ret = (int) approx - 2;
     623             :   /* Now do *exact* calculation, using well rounded-down factor as starting
     624             :      point to avoid having to do too many steps. */
     625           4 :   GNUNET_assert (0 <=
     626             :                  TALER_amount_multiply (&tmp,
     627             :                                         divisor,
     628             :                                         ret));
     629             :   /* in practice, this loop will only run for one or two iterations */
     630             :   while (1)
     631             :   {
     632          10 :     GNUNET_assert (0 <=
     633             :                    TALER_amount_add (&nxt,
     634             :                                      &tmp,
     635             :                                      divisor));
     636          10 :     if (1 ==
     637          10 :         TALER_amount_cmp (&nxt,
     638             :                           dividend))
     639           4 :       break; /* nxt > dividend */
     640           6 :     ret++;
     641           6 :     tmp = nxt;
     642             :   }
     643           4 :   return ret;
     644             : }
     645             : 
     646             : 
     647             : enum TALER_AmountArithmeticResult
     648           6 : TALER_amount_multiply (struct TALER_Amount *result,
     649             :                        const struct TALER_Amount *amount,
     650             :                        uint32_t factor)
     651             : {
     652           6 :   struct TALER_Amount in = *amount;
     653             : 
     654           6 :   if (GNUNET_SYSERR ==
     655           6 :       TALER_amount_normalize (&in))
     656           0 :     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
     657           6 :   memcpy (result->currency,
     658           6 :           amount->currency,
     659             :           TALER_CURRENCY_LEN);
     660           6 :   if ( (0 == factor) ||
     661           5 :        ( (0 == in.value) &&
     662           1 :          (0 == in.fraction) ) )
     663             :   {
     664           2 :     result->value = 0;
     665           2 :     result->fraction = 0;
     666           2 :     return TALER_AAR_RESULT_ZERO;
     667             :   }
     668           4 :   result->value = in.value * ((uint64_t) factor);
     669           4 :   if (in.value != result->value / factor)
     670           0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     671             :   {
     672             :     /* This multiplication cannot overflow since both inputs are 32-bit values */
     673           4 :     uint64_t tmp = ((uint64_t) factor) * ((uint64_t) in.fraction);
     674             :     uint64_t res;
     675             : 
     676           4 :     res = tmp / TALER_AMOUNT_FRAC_BASE;
     677             :     /* check for overflow */
     678           4 :     if (result->value + res < result->value)
     679           0 :       return TALER_AAR_INVALID_RESULT_OVERFLOW;
     680           4 :     result->value += res;
     681           4 :     result->fraction = tmp % TALER_AMOUNT_FRAC_BASE;
     682             :   }
     683           4 :   if (result->value > TALER_AMOUNT_MAX_VALUE)
     684           0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     685             :   /* This check should be redundant... */
     686           4 :   GNUNET_assert (GNUNET_SYSERR !=
     687             :                  TALER_amount_normalize (result));
     688           4 :   return TALER_AAR_RESULT_POSITIVE;
     689             : }
     690             : 
     691             : 
     692             : enum GNUNET_GenericReturnValue
     693         108 : TALER_amount_round_down (struct TALER_Amount *amount,
     694             :                          const struct TALER_Amount *round_unit)
     695             : {
     696         108 :   if (GNUNET_OK !=
     697         108 :       TALER_amount_cmp_currency (amount,
     698             :                                  round_unit))
     699             :   {
     700           0 :     GNUNET_break (0);
     701           0 :     return GNUNET_SYSERR;
     702             :   }
     703         108 :   if ( (0 != round_unit->fraction) &&
     704         107 :        (0 != round_unit->value) )
     705             :   {
     706           0 :     GNUNET_break (0);
     707           0 :     return GNUNET_SYSERR;
     708             :   }
     709         108 :   if ( (0 == round_unit->fraction) &&
     710           1 :        (0 == round_unit->value) )
     711           0 :     return GNUNET_NO; /* no rounding requested */
     712         108 :   if (0 != round_unit->fraction)
     713             :   {
     714             :     uint32_t delta;
     715             : 
     716         107 :     delta = amount->fraction % round_unit->fraction;
     717         107 :     if (0 == delta)
     718         102 :       return GNUNET_NO;
     719           5 :     amount->fraction -= delta;
     720             :   }
     721           6 :   if (0 != round_unit->value)
     722             :   {
     723             :     uint64_t delta;
     724             : 
     725           1 :     delta = amount->value % round_unit->value;
     726           1 :     if (0 == delta)
     727           0 :       return GNUNET_NO;
     728           1 :     amount->value -= delta;
     729           1 :     amount->fraction = 0;
     730             :   }
     731           6 :   return GNUNET_OK;
     732             : }
     733             : 
     734             : 
     735             : /* end of amount.c */

Generated by: LCOV version 1.14