LCOV - code coverage report
Current view: top level - util - amount.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 263 338 77.8 %
Date: 2022-08-25 06:15:09 Functions: 18 24 75.0 %
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          10 : invalidate (struct TALER_Amount *a)
      35             : {
      36          10 :   memset (a,
      37             :           0,
      38             :           sizeof (struct TALER_Amount));
      39          10 : }
      40             : 
      41             : 
      42             : enum GNUNET_GenericReturnValue
      43          54 : 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          56 :   while (isspace ( (unsigned char) str[0]))
      53           2 :     str++;
      54          54 :   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          54 :   colon = strchr (str, (int) ':');
      64          54 :   if ( (NULL == colon) ||
      65          54 :        ((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          54 :   GNUNET_assert (TALER_CURRENCY_LEN > (colon - str));
      75          54 :   memcpy (amount->currency,
      76             :           str,
      77          54 :           colon - str);
      78             :   /* 0-terminate *and* normalize buffer by setting everything to '\0' */
      79          54 :   memset (&amount->currency [colon - str],
      80             :           0,
      81          54 :           TALER_CURRENCY_LEN - (colon - str));
      82             : 
      83             :   /* skip colon */
      84          54 :   value = colon + 1;
      85          54 :   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          54 :   amount->value = 0;
      95          54 :   amount->fraction = 0;
      96             : 
      97             :   /* parse value */
      98         137 :   while ('.' != *value)
      99             :   {
     100          98 :     if ('\0' == *value)
     101             :     {
     102             :       /* we are done */
     103          13 :       return GNUNET_OK;
     104             :     }
     105          85 :     if ( (*value < '0') ||
     106          85 :          (*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          84 :     n = *value - '0';
     116          84 :     if ( (amount->value * 10 < amount->value) ||
     117          84 :          (amount->value * 10 + n < amount->value) ||
     118          84 :          (amount->value > TALER_AMOUNT_MAX_VALUE) ||
     119          84 :          (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          83 :     amount->value = (amount->value * 10) + n;
     128          83 :     value++;
     129             :   }
     130             : 
     131             :   /* skip the dot */
     132          39 :   value++;
     133             : 
     134             :   /* parse fraction */
     135          39 :   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          39 :   b = TALER_AMOUNT_FRAC_BASE / 10;
     144         134 :   while ('\0' != *value)
     145             :   {
     146          99 :     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          98 :     if ( (*value < '0') ||
     156          98 :          (*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          95 :     n = *value - '0';
     164          95 :     amount->fraction += n * b;
     165          95 :     b /= 10;
     166          95 :     value++;
     167             :   }
     168          35 :   return GNUNET_OK;
     169             : }
     170             : 
     171             : 
     172             : enum GNUNET_GenericReturnValue
     173           0 : TALER_string_to_amount_nbo (const char *str,
     174             :                             struct TALER_AmountNBO *amount_nbo)
     175             : {
     176             :   struct TALER_Amount amount;
     177             : 
     178           0 :   if (GNUNET_OK !=
     179           0 :       TALER_string_to_amount (str,
     180             :                               &amount))
     181           0 :     return GNUNET_SYSERR;
     182           0 :   TALER_amount_hton (amount_nbo,
     183             :                      &amount);
     184           0 :   return GNUNET_OK;
     185             : }
     186             : 
     187             : 
     188             : void
     189           7 : TALER_amount_hton (struct TALER_AmountNBO *res,
     190             :                    const struct TALER_Amount *d)
     191             : {
     192           7 :   GNUNET_assert (GNUNET_YES ==
     193             :                  TALER_amount_is_valid (d));
     194           7 :   res->value = GNUNET_htonll (d->value);
     195           7 :   res->fraction = htonl (d->fraction);
     196           7 :   memcpy (res->currency,
     197           7 :           d->currency,
     198             :           TALER_CURRENCY_LEN);
     199           7 : }
     200             : 
     201             : 
     202             : void
     203           7 : TALER_amount_ntoh (struct TALER_Amount *res,
     204             :                    const struct TALER_AmountNBO *dn)
     205             : {
     206           7 :   res->value = GNUNET_ntohll (dn->value);
     207           7 :   res->fraction = ntohl (dn->fraction);
     208           7 :   memcpy (res->currency,
     209           7 :           dn->currency,
     210             :           TALER_CURRENCY_LEN);
     211           7 :   GNUNET_assert (GNUNET_YES ==
     212             :                  TALER_amount_is_valid (res));
     213           7 : }
     214             : 
     215             : 
     216             : enum GNUNET_GenericReturnValue
     217          36 : TALER_amount_set_zero (const char *cur,
     218             :                        struct TALER_Amount *amount)
     219             : {
     220             :   size_t slen;
     221             : 
     222          36 :   slen = strlen (cur);
     223          36 :   if (slen >= TALER_CURRENCY_LEN)
     224           0 :     return GNUNET_SYSERR;
     225          36 :   memset (amount,
     226             :           0,
     227             :           sizeof (struct TALER_Amount));
     228          36 :   memcpy (amount->currency,
     229             :           cur,
     230             :           slen);
     231          36 :   return GNUNET_OK;
     232             : }
     233             : 
     234             : 
     235             : enum GNUNET_GenericReturnValue
     236         401 : TALER_amount_is_valid (const struct TALER_Amount *amount)
     237             : {
     238         401 :   if (amount->value > TALER_AMOUNT_MAX_VALUE)
     239           2 :     return GNUNET_SYSERR;
     240         399 :   return ('\0' != amount->currency[0]) ? GNUNET_OK : GNUNET_NO;
     241             : }
     242             : 
     243             : 
     244             : bool
     245           0 : TALER_amount_is_zero (const struct TALER_Amount *amount)
     246             : {
     247           0 :   if (GNUNET_OK !=
     248           0 :       TALER_amount_is_valid (amount))
     249           0 :     return false;
     250             :   return
     251           0 :     (0 == amount->value) &&
     252           0 :     (0 == amount->fraction);
     253             : }
     254             : 
     255             : 
     256             : enum GNUNET_GenericReturnValue
     257           0 : TALER_amount_is_currency (const struct TALER_Amount *amount,
     258             :                           const char *currency)
     259             : {
     260           0 :   if (GNUNET_OK !=
     261           0 :       TALER_amount_is_valid (amount))
     262           0 :     return GNUNET_SYSERR;
     263           0 :   return (0 == strcasecmp (currency,
     264           0 :                            amount->currency))
     265             :          ? GNUNET_OK
     266           0 :          : GNUNET_NO;
     267             : }
     268             : 
     269             : 
     270             : /**
     271             :  * Test if @a a is valid, NBO variant.
     272             :  *
     273             :  * @param a amount to test
     274             :  * @return #GNUNET_YES if valid,
     275             :  *         #GNUNET_NO if invalid
     276             :  */
     277             : static enum GNUNET_GenericReturnValue
     278           0 : test_valid_nbo (const struct TALER_AmountNBO *a)
     279             : {
     280           0 :   return ('\0' != a->currency[0]) ? GNUNET_YES : GNUNET_NO;
     281             : }
     282             : 
     283             : 
     284             : enum GNUNET_GenericReturnValue
     285          72 : TALER_amount_cmp_currency (const struct TALER_Amount *a1,
     286             :                            const struct TALER_Amount *a2)
     287             : {
     288         144 :   if ( (GNUNET_NO == TALER_amount_is_valid (a1)) ||
     289          72 :        (GNUNET_NO == TALER_amount_is_valid (a2)) )
     290           0 :     return GNUNET_SYSERR;
     291          72 :   if (0 == strcasecmp (a1->currency,
     292          72 :                        a2->currency))
     293          70 :     return GNUNET_YES;
     294           2 :   return GNUNET_NO;
     295             : }
     296             : 
     297             : 
     298             : enum GNUNET_GenericReturnValue
     299           0 : TALER_amount_cmp_currency_nbo (const struct TALER_AmountNBO *a1,
     300             :                                const struct TALER_AmountNBO *a2)
     301             : {
     302           0 :   if ( (GNUNET_NO == test_valid_nbo (a1)) ||
     303           0 :        (GNUNET_NO == test_valid_nbo (a2)) )
     304           0 :     return GNUNET_SYSERR;
     305           0 :   if (0 == strcasecmp (a1->currency,
     306           0 :                        a2->currency))
     307           0 :     return GNUNET_YES;
     308           0 :   return GNUNET_NO;
     309             : }
     310             : 
     311             : 
     312             : int
     313          28 : TALER_amount_cmp (const struct TALER_Amount *a1,
     314             :                   const struct TALER_Amount *a2)
     315             : {
     316             :   struct TALER_Amount n1;
     317             :   struct TALER_Amount n2;
     318             : 
     319          28 :   GNUNET_assert (GNUNET_YES ==
     320             :                  TALER_amount_cmp_currency (a1,
     321             :                                             a2));
     322          28 :   n1 = *a1;
     323          28 :   n2 = *a2;
     324          28 :   GNUNET_assert (GNUNET_SYSERR !=
     325             :                  TALER_amount_normalize (&n1));
     326          28 :   GNUNET_assert (GNUNET_SYSERR !=
     327             :                  TALER_amount_normalize (&n2));
     328          28 :   if (n1.value == n2.value)
     329             :   {
     330          14 :     if (n1.fraction < n2.fraction)
     331           0 :       return -1;
     332          14 :     if (n1.fraction > n2.fraction)
     333           1 :       return 1;
     334          13 :     return 0;
     335             :   }
     336          14 :   if (n1.value < n2.value)
     337           8 :     return -1;
     338           6 :   return 1;
     339             : }
     340             : 
     341             : 
     342             : int
     343           0 : TALER_amount_cmp_nbo (const struct TALER_AmountNBO *a1,
     344             :                       const struct TALER_AmountNBO *a2)
     345             : {
     346             :   struct TALER_Amount h1;
     347             :   struct TALER_Amount h2;
     348             : 
     349           0 :   TALER_amount_ntoh (&h1,
     350             :                      a1);
     351           0 :   TALER_amount_ntoh (&h2,
     352             :                      a2);
     353           0 :   return TALER_amount_cmp (&h1,
     354             :                            &h2);
     355             : }
     356             : 
     357             : 
     358             : enum TALER_AmountArithmeticResult
     359          11 : TALER_amount_subtract (struct TALER_Amount *diff,
     360             :                        const struct TALER_Amount *a1,
     361             :                        const struct TALER_Amount *a2)
     362             : {
     363             :   struct TALER_Amount n1;
     364             :   struct TALER_Amount n2;
     365             : 
     366          11 :   if (GNUNET_YES !=
     367          11 :       TALER_amount_cmp_currency (a1,
     368             :                                  a2))
     369             :   {
     370           1 :     invalidate (diff);
     371           1 :     return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE;
     372             :   }
     373             :   /* make local copies to avoid aliasing problems between
     374             :      diff and a1/a2 */
     375          10 :   n1 = *a1;
     376          10 :   n2 = *a2;
     377          20 :   if ( (GNUNET_SYSERR == TALER_amount_normalize (&n1)) ||
     378          10 :        (GNUNET_SYSERR == TALER_amount_normalize (&n2)) )
     379             :   {
     380           0 :     invalidate (diff);
     381           0 :     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
     382             :   }
     383             : 
     384          10 :   if (n1.fraction < n2.fraction)
     385             :   {
     386           2 :     if (0 == n1.value)
     387             :     {
     388           0 :       invalidate (diff);
     389           0 :       return TALER_AAR_INVALID_NEGATIVE_RESULT;
     390             :     }
     391           2 :     n1.fraction += TALER_AMOUNT_FRAC_BASE;
     392           2 :     n1.value--;
     393             :   }
     394          10 :   if (n1.value < n2.value)
     395             :   {
     396           1 :     invalidate (diff);
     397           1 :     return TALER_AAR_INVALID_NEGATIVE_RESULT;
     398             :   }
     399           9 :   GNUNET_assert (GNUNET_OK ==
     400             :                  TALER_amount_set_zero (n1.currency,
     401             :                                         diff));
     402           9 :   GNUNET_assert (n1.fraction >= n2.fraction);
     403           9 :   diff->fraction = n1.fraction - n2.fraction;
     404           9 :   GNUNET_assert (n1.value >= n2.value);
     405           9 :   diff->value = n1.value - n2.value;
     406           9 :   if ( (0 == diff->fraction) &&
     407           5 :        (0 == diff->value) )
     408           1 :     return TALER_AAR_RESULT_ZERO;
     409           8 :   return TALER_AAR_RESULT_POSITIVE;
     410             : }
     411             : 
     412             : 
     413             : enum TALER_AmountArithmeticResult
     414          20 : TALER_amount_add (struct TALER_Amount *sum,
     415             :                   const struct TALER_Amount *a1,
     416             :                   const struct TALER_Amount *a2)
     417             : {
     418             :   struct TALER_Amount n1;
     419             :   struct TALER_Amount n2;
     420             :   struct TALER_Amount res;
     421             : 
     422          20 :   if (GNUNET_YES !=
     423          20 :       TALER_amount_cmp_currency (a1,
     424             :                                  a2))
     425             :   {
     426           0 :     invalidate (sum);
     427           0 :     return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE;
     428             :   }
     429             :   /* make local copies to avoid aliasing problems between
     430             :      diff and a1/a2 */
     431          20 :   n1 = *a1;
     432          20 :   n2 = *a2;
     433          20 :   if ( (GNUNET_SYSERR ==
     434          40 :         TALER_amount_normalize (&n1)) ||
     435             :        (GNUNET_SYSERR ==
     436          20 :         TALER_amount_normalize (&n2)) )
     437             :   {
     438           0 :     invalidate (sum);
     439           0 :     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
     440             :   }
     441             : 
     442          20 :   GNUNET_assert (GNUNET_OK ==
     443             :                  TALER_amount_set_zero (a1->currency,
     444             :                                         &res));
     445          20 :   res.value = n1.value + n2.value;
     446          20 :   if (res.value < n1.value)
     447             :   {
     448             :     /* integer overflow */
     449           0 :     invalidate (sum);
     450           0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     451             :   }
     452          20 :   if (res.value > TALER_AMOUNT_MAX_VALUE)
     453             :   {
     454             :     /* too large to be legal */
     455           0 :     invalidate (sum);
     456           0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     457             :   }
     458          20 :   res.fraction = n1.fraction + n2.fraction;
     459          20 :   if (GNUNET_SYSERR ==
     460          20 :       TALER_amount_normalize (&res))
     461             :   {
     462             :     /* integer overflow via carry from fraction */
     463           1 :     invalidate (sum);
     464           1 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     465             :   }
     466          19 :   *sum = res;
     467          19 :   if ( (0 == sum->fraction) &&
     468           6 :        (0 == sum->value) )
     469           0 :     return TALER_AAR_RESULT_ZERO;
     470          19 :   return TALER_AAR_RESULT_POSITIVE;
     471             : }
     472             : 
     473             : 
     474             : enum GNUNET_GenericReturnValue
     475         201 : TALER_amount_normalize (struct TALER_Amount *amount)
     476             : {
     477             :   uint32_t overflow;
     478             : 
     479         201 :   if (GNUNET_YES != TALER_amount_is_valid (amount))
     480           3 :     return GNUNET_SYSERR;
     481         198 :   if (amount->fraction < TALER_AMOUNT_FRAC_BASE)
     482         192 :     return GNUNET_NO;
     483           6 :   overflow = amount->fraction / TALER_AMOUNT_FRAC_BASE;
     484           6 :   amount->fraction %= TALER_AMOUNT_FRAC_BASE;
     485           6 :   amount->value += overflow;
     486           6 :   if ( (amount->value < overflow) ||
     487           6 :        (amount->value > TALER_AMOUNT_MAX_VALUE) )
     488             :   {
     489           1 :     invalidate (amount);
     490           1 :     return GNUNET_SYSERR;
     491             :   }
     492           5 :   return GNUNET_OK;
     493             : }
     494             : 
     495             : 
     496             : /**
     497             :  * Convert the fraction of @a amount to a string in decimals.
     498             :  *
     499             :  * @param amount value to convert
     500             :  * @param[out] tail where to write the result
     501             :  */
     502             : static void
     503          30 : amount_to_tail (const struct TALER_Amount *amount,
     504             :                 char tail[TALER_AMOUNT_FRAC_LEN + 1])
     505             : {
     506          30 :   uint32_t n = amount->fraction;
     507             :   unsigned int i;
     508             : 
     509          95 :   for (i = 0; (i < TALER_AMOUNT_FRAC_LEN) && (0 != n); i++)
     510             :   {
     511          65 :     tail[i] = '0' + (n / (TALER_AMOUNT_FRAC_BASE / 10));
     512          65 :     n = (n * 10) % (TALER_AMOUNT_FRAC_BASE);
     513             :   }
     514          30 :   tail[i] = '\0';
     515          30 : }
     516             : 
     517             : 
     518             : char *
     519          21 : TALER_amount_to_string (const struct TALER_Amount *amount)
     520             : {
     521             :   char *result;
     522             :   struct TALER_Amount norm;
     523             : 
     524          21 :   if (GNUNET_YES != TALER_amount_is_valid (amount))
     525           1 :     return NULL;
     526          20 :   norm = *amount;
     527          20 :   GNUNET_break (GNUNET_SYSERR !=
     528             :                 TALER_amount_normalize (&norm));
     529          20 :   if (0 != norm.fraction)
     530             :   {
     531             :     char tail[TALER_AMOUNT_FRAC_LEN + 1];
     532             : 
     533          15 :     amount_to_tail (&norm,
     534             :                     tail);
     535          15 :     GNUNET_asprintf (&result,
     536             :                      "%s:%llu.%s",
     537             :                      norm.currency,
     538          15 :                      (unsigned long long) norm.value,
     539             :                      tail);
     540             :   }
     541             :   else
     542             :   {
     543           5 :     GNUNET_asprintf (&result,
     544             :                      "%s:%llu",
     545             :                      norm.currency,
     546           5 :                      (unsigned long long) norm.value);
     547             :   }
     548          20 :   return result;
     549             : }
     550             : 
     551             : 
     552             : const char *
     553          21 : TALER_amount2s (const struct TALER_Amount *amount)
     554             : {
     555             :   /* 24 is sufficient for a uint64_t value in decimal; 3 is for ":.\0" */
     556             :   static GNUNET_THREAD_LOCAL char result[TALER_AMOUNT_FRAC_LEN
     557             :                                          + TALER_CURRENCY_LEN + 3 + 24];
     558             :   struct TALER_Amount norm;
     559             : 
     560          21 :   if (GNUNET_YES != TALER_amount_is_valid (amount))
     561           0 :     return NULL;
     562          21 :   norm = *amount;
     563          21 :   GNUNET_break (GNUNET_SYSERR !=
     564             :                 TALER_amount_normalize (&norm));
     565          21 :   if (0 != norm.fraction)
     566             :   {
     567             :     char tail[TALER_AMOUNT_FRAC_LEN + 1];
     568             : 
     569          15 :     amount_to_tail (&norm,
     570             :                     tail);
     571          15 :     GNUNET_snprintf (result,
     572             :                      sizeof (result),
     573             :                      "%s:%llu.%s",
     574             :                      norm.currency,
     575          15 :                      (unsigned long long) norm.value,
     576             :                      tail);
     577             :   }
     578             :   else
     579             :   {
     580           6 :     GNUNET_snprintf (result,
     581             :                      sizeof (result),
     582             :                      "%s:%llu",
     583             :                      norm.currency,
     584           6 :                      (unsigned long long) norm.value);
     585             :   }
     586          21 :   return result;
     587             : }
     588             : 
     589             : 
     590             : void
     591           4 : TALER_amount_divide (struct TALER_Amount *result,
     592             :                      const struct TALER_Amount *dividend,
     593             :                      uint32_t divisor)
     594             : {
     595             :   uint64_t modr;
     596             : 
     597           4 :   GNUNET_assert (0 != divisor); /* division by zero is discouraged */
     598           4 :   *result = *dividend;
     599             :   /* in case @a dividend was not yet normalized */
     600           4 :   GNUNET_assert (GNUNET_SYSERR !=
     601             :                  TALER_amount_normalize (result));
     602           4 :   if (1 == divisor)
     603           1 :     return;
     604           3 :   modr = result->value % divisor;
     605           3 :   result->value /= divisor;
     606             :   /* modr fits into 32 bits, so we can safely multiply by (<32-bit) base and add fraction! */
     607           3 :   modr = (modr * TALER_AMOUNT_FRAC_BASE) + result->fraction;
     608           3 :   result->fraction = (uint32_t) (modr / divisor);
     609             :   /* 'fraction' could now be larger than #TALER_AMOUNT_FRAC_BASE, so we must normalize */
     610           3 :   GNUNET_assert (GNUNET_SYSERR !=
     611             :                  TALER_amount_normalize (result));
     612             : }
     613             : 
     614             : 
     615             : int
     616           5 : TALER_amount_divide2 (const struct TALER_Amount *dividend,
     617             :                       const struct TALER_Amount *divisor)
     618             : {
     619             :   double approx;
     620             :   double d;
     621             :   double r;
     622             :   int ret;
     623             :   struct TALER_Amount tmp;
     624             :   struct TALER_Amount nxt;
     625             : 
     626           5 :   if (GNUNET_YES !=
     627           5 :       TALER_amount_cmp_currency (dividend,
     628             :                                  divisor))
     629             :   {
     630           0 :     GNUNET_break (0);
     631           0 :     return -1;
     632             :   }
     633           5 :   if ( (0 == divisor->fraction) &&
     634           2 :        (0 == divisor->value) )
     635           1 :     return INT_MAX;
     636             :   /* first, get rounded approximation */
     637           4 :   d = ((double) dividend->value) * ((double) TALER_AMOUNT_FRAC_BASE)
     638           4 :       + ( (double) dividend->fraction);
     639           4 :   r = ((double) divisor->value) * ((double) TALER_AMOUNT_FRAC_BASE)
     640           4 :       + ( (double) divisor->fraction);
     641           4 :   approx = d / r;
     642           4 :   if (approx > ((double) INT_MAX))
     643           0 :     return INT_MAX; /* 'infinity' */
     644             :   /* round down */
     645           4 :   if (approx < 2)
     646           1 :     ret = 0;
     647             :   else
     648           3 :     ret = (int) approx - 2;
     649             :   /* Now do *exact* calculation, using well rounded-down factor as starting
     650             :      point to avoid having to do too many steps. */
     651           4 :   GNUNET_assert (0 <=
     652             :                  TALER_amount_multiply (&tmp,
     653             :                                         divisor,
     654             :                                         ret));
     655             :   /* in practice, this loop will only run for one or two iterations */
     656             :   while (1)
     657             :   {
     658          10 :     GNUNET_assert (0 <=
     659             :                    TALER_amount_add (&nxt,
     660             :                                      &tmp,
     661             :                                      divisor));
     662          10 :     if (1 ==
     663          10 :         TALER_amount_cmp (&nxt,
     664             :                           dividend))
     665           4 :       break; /* nxt > dividend */
     666           6 :     ret++;
     667           6 :     tmp = nxt;
     668             :   }
     669           4 :   return ret;
     670             : }
     671             : 
     672             : 
     673             : enum TALER_AmountArithmeticResult
     674           6 : TALER_amount_multiply (struct TALER_Amount *result,
     675             :                        const struct TALER_Amount *amount,
     676             :                        uint32_t factor)
     677             : {
     678           6 :   struct TALER_Amount in = *amount;
     679             : 
     680           6 :   if (GNUNET_SYSERR ==
     681           6 :       TALER_amount_normalize (&in))
     682           0 :     return TALER_AAR_INVALID_NORMALIZATION_FAILED;
     683           6 :   memcpy (result->currency,
     684           6 :           amount->currency,
     685             :           TALER_CURRENCY_LEN);
     686           6 :   if ( (0 == factor) ||
     687           5 :        ( (0 == in.value) &&
     688           1 :          (0 == in.fraction) ) )
     689             :   {
     690           2 :     result->value = 0;
     691           2 :     result->fraction = 0;
     692           2 :     return TALER_AAR_RESULT_ZERO;
     693             :   }
     694           4 :   result->value = in.value * ((uint64_t) factor);
     695           4 :   if (in.value != result->value / factor)
     696           0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     697             :   {
     698             :     /* This multiplication cannot overflow since both inputs are 32-bit values */
     699           4 :     uint64_t tmp = ((uint64_t) factor) * ((uint64_t) in.fraction);
     700             :     uint64_t res;
     701             : 
     702           4 :     res = tmp / TALER_AMOUNT_FRAC_BASE;
     703             :     /* check for overflow */
     704           4 :     if (result->value + res < result->value)
     705           0 :       return TALER_AAR_INVALID_RESULT_OVERFLOW;
     706           4 :     result->value += res;
     707           4 :     result->fraction = tmp % TALER_AMOUNT_FRAC_BASE;
     708             :   }
     709           4 :   if (result->value > TALER_AMOUNT_MAX_VALUE)
     710           0 :     return TALER_AAR_INVALID_RESULT_OVERFLOW;
     711             :   /* This check should be redundant... */
     712           4 :   GNUNET_assert (GNUNET_SYSERR !=
     713             :                  TALER_amount_normalize (result));
     714           4 :   return TALER_AAR_RESULT_POSITIVE;
     715             : }
     716             : 
     717             : 
     718             : enum GNUNET_GenericReturnValue
     719           4 : TALER_amount_round_down (struct TALER_Amount *amount,
     720             :                          const struct TALER_Amount *round_unit)
     721             : {
     722           4 :   if (GNUNET_OK !=
     723           4 :       TALER_amount_cmp_currency (amount,
     724             :                                  round_unit))
     725             :   {
     726           0 :     GNUNET_break (0);
     727           0 :     return GNUNET_SYSERR;
     728             :   }
     729           4 :   if ( (0 != round_unit->fraction) &&
     730           3 :        (0 != round_unit->value) )
     731             :   {
     732           0 :     GNUNET_break (0);
     733           0 :     return GNUNET_SYSERR;
     734             :   }
     735           4 :   if ( (0 == round_unit->fraction) &&
     736           1 :        (0 == round_unit->value) )
     737           0 :     return GNUNET_NO; /* no rounding requested */
     738           4 :   if (0 != round_unit->fraction)
     739             :   {
     740             :     uint32_t delta;
     741             : 
     742           3 :     delta = amount->fraction % round_unit->fraction;
     743           3 :     if (0 == delta)
     744           2 :       return GNUNET_NO;
     745           1 :     amount->fraction -= delta;
     746             :   }
     747           2 :   if (0 != round_unit->value)
     748             :   {
     749             :     uint64_t delta;
     750             : 
     751           1 :     delta = amount->value % round_unit->value;
     752           1 :     if (0 == delta)
     753           0 :       return GNUNET_NO;
     754           1 :     amount->value -= delta;
     755           1 :     amount->fraction = 0;
     756             :   }
     757           2 :   return GNUNET_OK;
     758             : }
     759             : 
     760             : 
     761             : /* end of amount.c */

Generated by: LCOV version 1.14