2015-01-27 16:18:33 +01:00
|
|
|
/*
|
|
|
|
This file is part of TALER
|
2020-01-19 19:21:58 +01:00
|
|
|
Copyright (C) 2014 Taler Systems SA
|
2015-01-27 16:18:33 +01:00
|
|
|
|
|
|
|
TALER is free software; you can redistribute it and/or modify it under the
|
|
|
|
terms of the GNU General Public License as published by the Free Software
|
|
|
|
Foundation; either version 3, or (at your option) any later version.
|
|
|
|
|
|
|
|
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
|
|
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
|
|
|
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License along with
|
2016-07-07 17:55:25 +02:00
|
|
|
TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
|
2015-01-27 16:18:33 +01:00
|
|
|
*/
|
|
|
|
/**
|
2015-01-28 20:53:21 +01:00
|
|
|
* @file util/amount.c
|
2015-01-27 16:18:33 +01:00
|
|
|
* @brief Common utility functions to deal with units of currency
|
|
|
|
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
|
|
|
|
* @author Florian Dold
|
|
|
|
* @author Benedikt Mueller
|
2015-04-13 17:20:46 +02:00
|
|
|
* @author Christian Grothoff
|
2015-01-27 16:18:33 +01:00
|
|
|
*/
|
|
|
|
#include "platform.h"
|
|
|
|
#include "taler_util.h"
|
|
|
|
|
2017-11-02 17:40:14 +01:00
|
|
|
/**
|
|
|
|
* Maximum legal 'value' for an amount, based on IEEE double (for JavaScript compatibility).
|
|
|
|
*/
|
2017-11-02 18:42:12 +01:00
|
|
|
#define MAX_AMOUNT_VALUE (1LLU << 52)
|
2017-11-02 17:40:14 +01:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set @a a to "invalid".
|
|
|
|
*
|
2020-01-19 19:44:29 +01:00
|
|
|
* @param[out] a amount to set to invalid
|
2017-11-02 17:40:14 +01:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
invalidate (struct TALER_Amount *a)
|
|
|
|
{
|
|
|
|
memset (a,
|
|
|
|
0,
|
|
|
|
sizeof (struct TALER_Amount));
|
|
|
|
}
|
|
|
|
|
2015-01-27 16:18:33 +01:00
|
|
|
|
|
|
|
/**
|
2020-01-19 22:01:20 +01:00
|
|
|
* Parse monetary amount, in the format "T:V.F".
|
2015-01-27 16:18:33 +01:00
|
|
|
*
|
2020-01-19 22:01:20 +01:00
|
|
|
* @param str amount string
|
|
|
|
* @param[out] amount amount to write the result to
|
|
|
|
* @return #GNUNET_OK if the string is a valid monetary amount specification,
|
2015-01-27 16:18:33 +01:00
|
|
|
* #GNUNET_SYSERR if it is invalid.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
TALER_string_to_amount (const char *str,
|
2020-02-29 17:13:43 +01:00
|
|
|
struct TALER_Amount *amount)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
int n;
|
|
|
|
uint32_t b;
|
|
|
|
const char *colon;
|
|
|
|
const char *value;
|
2015-01-27 16:18:33 +01:00
|
|
|
|
2015-03-18 18:55:41 +01:00
|
|
|
/* skip leading whitespace */
|
2019-08-25 16:18:24 +02:00
|
|
|
while (isspace ( (unsigned char) str[0]))
|
2015-03-18 18:55:41 +01:00
|
|
|
str++;
|
|
|
|
if ('\0' == str[0])
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-17 11:37:21 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
2015-03-18 18:55:41 +01:00
|
|
|
"Null before currency\n");
|
2020-02-29 17:13:43 +01:00
|
|
|
invalidate (amount);
|
2015-01-27 16:18:33 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2020-01-19 19:39:14 +01:00
|
|
|
|
2015-03-18 18:55:41 +01:00
|
|
|
/* parse currency */
|
|
|
|
colon = strchr (str, (int) ':');
|
|
|
|
if ( (NULL == colon) ||
|
|
|
|
((colon - str) >= TALER_CURRENCY_LEN) )
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
2019-02-11 19:55:00 +01:00
|
|
|
"Invalid currency specified before colon: `%s'\n",
|
2015-03-18 18:55:41 +01:00
|
|
|
str);
|
2020-02-29 17:13:43 +01:00
|
|
|
invalidate (amount);
|
2020-01-19 19:39:14 +01:00
|
|
|
return GNUNET_SYSERR;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
2020-01-19 19:39:14 +01:00
|
|
|
|
|
|
|
GNUNET_assert (TALER_CURRENCY_LEN > (colon - str));
|
2020-02-29 17:13:43 +01:00
|
|
|
memcpy (amount->currency,
|
2015-03-18 18:55:41 +01:00
|
|
|
str,
|
|
|
|
colon - str);
|
2020-01-19 19:39:14 +01:00
|
|
|
/* 0-terminate *and* normalize buffer by setting everything to '\0' */
|
2020-02-29 17:13:43 +01:00
|
|
|
memset (&amount->currency [colon - str],
|
2020-01-19 19:39:14 +01:00
|
|
|
0,
|
|
|
|
TALER_CURRENCY_LEN - (colon - str));
|
|
|
|
|
2015-03-18 18:55:41 +01:00
|
|
|
/* skip colon */
|
|
|
|
value = colon + 1;
|
|
|
|
if ('\0' == value[0])
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-17 11:37:21 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
2020-01-19 19:39:14 +01:00
|
|
|
"Actual value missing in amount `%s'\n",
|
|
|
|
str);
|
2020-02-29 17:13:43 +01:00
|
|
|
invalidate (amount);
|
2020-01-19 19:39:14 +01:00
|
|
|
return GNUNET_SYSERR;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
|
|
|
|
2020-02-29 17:13:43 +01:00
|
|
|
amount->value = 0;
|
|
|
|
amount->fraction = 0;
|
2020-01-19 19:39:14 +01:00
|
|
|
|
2015-03-18 18:55:41 +01:00
|
|
|
/* parse value */
|
2020-01-19 19:39:14 +01:00
|
|
|
while ('.' != *value)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2020-01-19 19:39:14 +01:00
|
|
|
if ('\0' == *value)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2020-01-19 19:39:14 +01:00
|
|
|
/* we are done */
|
2015-01-27 16:18:33 +01:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
2020-01-19 19:39:14 +01:00
|
|
|
if ( (*value < '0') ||
|
|
|
|
(*value > '9') )
|
2015-03-18 18:55:41 +01:00
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
2020-01-19 19:39:14 +01:00
|
|
|
"Invalid character `%c' in amount `%s'\n",
|
|
|
|
(int) *value,
|
|
|
|
str);
|
2020-02-29 17:13:43 +01:00
|
|
|
invalidate (amount);
|
2020-01-19 19:39:14 +01:00
|
|
|
return GNUNET_SYSERR;
|
2015-03-18 18:55:41 +01:00
|
|
|
}
|
2020-01-19 19:39:14 +01:00
|
|
|
n = *value - '0';
|
2020-02-29 17:13:43 +01:00
|
|
|
if ( (amount->value * 10 + n < amount->value) ||
|
|
|
|
(amount->value > MAX_AMOUNT_VALUE) )
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-17 11:37:21 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
2020-01-19 19:39:14 +01:00
|
|
|
"Value specified in amount `%s' is too large\n",
|
|
|
|
str);
|
2020-02-29 17:13:43 +01:00
|
|
|
invalidate (amount);
|
2020-01-19 19:39:14 +01:00
|
|
|
return GNUNET_SYSERR;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
2020-02-29 17:13:43 +01:00
|
|
|
amount->value = (amount->value * 10) + n;
|
2020-01-19 19:39:14 +01:00
|
|
|
value++;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
|
|
|
|
2015-03-18 18:55:41 +01:00
|
|
|
/* skip the dot */
|
2020-01-19 19:39:14 +01:00
|
|
|
value++;
|
2015-01-27 16:18:33 +01:00
|
|
|
|
2015-03-18 18:55:41 +01:00
|
|
|
/* parse fraction */
|
2020-01-19 19:39:14 +01:00
|
|
|
if ('\0' == *value)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-17 11:37:21 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
2020-01-19 19:39:14 +01:00
|
|
|
"Amount `%s' ends abruptly after `.'\n",
|
|
|
|
str);
|
2020-02-29 17:13:43 +01:00
|
|
|
invalidate (amount);
|
2020-01-19 19:39:14 +01:00
|
|
|
return GNUNET_SYSERR;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
2015-03-18 18:55:41 +01:00
|
|
|
b = TALER_AMOUNT_FRAC_BASE / 10;
|
2020-01-19 19:39:14 +01:00
|
|
|
while ('\0' != *value)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
if (0 == b)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-17 11:37:21 +01:00
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
2020-01-19 19:39:14 +01:00
|
|
|
"Fractional value too small (only %u digits supported) in amount `%s'\n",
|
|
|
|
(unsigned int) TALER_AMOUNT_FRAC_LEN,
|
|
|
|
str);
|
2020-02-29 17:13:43 +01:00
|
|
|
invalidate (amount);
|
2020-01-19 19:39:14 +01:00
|
|
|
return GNUNET_SYSERR;
|
2015-03-18 18:55:41 +01:00
|
|
|
}
|
2020-01-19 19:39:14 +01:00
|
|
|
if ( (*value < '0') ||
|
|
|
|
(*value > '9') )
|
2015-03-18 18:55:41 +01:00
|
|
|
{
|
|
|
|
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
|
2015-04-13 17:20:46 +02:00
|
|
|
"Error after dot\n");
|
2020-02-29 17:13:43 +01:00
|
|
|
invalidate (amount);
|
2020-01-19 19:39:14 +01:00
|
|
|
return GNUNET_SYSERR;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
2020-01-19 19:39:14 +01:00
|
|
|
n = *value - '0';
|
2020-02-29 17:13:43 +01:00
|
|
|
amount->fraction += n * b;
|
2015-01-27 16:18:33 +01:00
|
|
|
b /= 10;
|
2020-01-19 19:39:14 +01:00
|
|
|
value++;
|
2017-11-02 17:40:14 +01:00
|
|
|
}
|
2015-01-27 16:18:33 +01:00
|
|
|
return GNUNET_OK;
|
2015-03-18 18:55:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-04-04 16:47:11 +02:00
|
|
|
/**
|
2020-01-19 22:01:20 +01:00
|
|
|
* Parse monetary amount, in the format "T:V.F".
|
|
|
|
* The result is stored in network byte order (NBO).
|
2016-04-04 16:47:11 +02:00
|
|
|
*
|
2020-01-19 22:01:20 +01:00
|
|
|
* @param str amount string
|
|
|
|
* @param[out] amount_nbo amount to write the result to
|
|
|
|
* @return #GNUNET_OK if the string is a valid amount specification,
|
2016-04-04 16:47:11 +02:00
|
|
|
* #GNUNET_SYSERR if it is invalid.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
TALER_string_to_amount_nbo (const char *str,
|
2020-01-19 22:01:20 +01:00
|
|
|
struct TALER_AmountNBO *amount_nbo)
|
2016-04-04 16:47:11 +02:00
|
|
|
{
|
|
|
|
struct TALER_Amount amount;
|
|
|
|
|
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_string_to_amount (str,
|
|
|
|
&amount))
|
|
|
|
return GNUNET_SYSERR;
|
2020-01-19 22:01:20 +01:00
|
|
|
TALER_amount_hton (amount_nbo,
|
2016-04-04 16:47:11 +02:00
|
|
|
&amount);
|
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-18 18:55:41 +01:00
|
|
|
/**
|
|
|
|
* Convert amount from host to network representation.
|
|
|
|
*
|
|
|
|
* @param res where to store amount in network representation
|
2020-01-19 20:11:32 +01:00
|
|
|
* @param[out] d amount in host representation
|
2015-03-18 18:55:41 +01:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
TALER_amount_hton (struct TALER_AmountNBO *res,
|
|
|
|
const struct TALER_Amount *d)
|
|
|
|
{
|
2018-01-01 23:07:36 +01:00
|
|
|
GNUNET_assert (GNUNET_YES ==
|
2019-08-25 16:18:24 +02:00
|
|
|
TALER_amount_is_valid (d));
|
2015-03-18 18:55:41 +01:00
|
|
|
res->value = GNUNET_htonll (d->value);
|
|
|
|
res->fraction = htonl (d->fraction);
|
|
|
|
memcpy (res->currency,
|
|
|
|
d->currency,
|
|
|
|
TALER_CURRENCY_LEN);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Convert amount from network to host representation.
|
|
|
|
*
|
2020-01-19 20:11:32 +01:00
|
|
|
* @param[out] res where to store amount in host representation
|
2015-03-28 15:42:07 +01:00
|
|
|
* @param dn amount in network representation
|
2015-03-18 18:55:41 +01:00
|
|
|
*/
|
|
|
|
void
|
|
|
|
TALER_amount_ntoh (struct TALER_Amount *res,
|
|
|
|
const struct TALER_AmountNBO *dn)
|
|
|
|
{
|
|
|
|
res->value = GNUNET_ntohll (dn->value);
|
|
|
|
res->fraction = ntohl (dn->fraction);
|
|
|
|
memcpy (res->currency,
|
|
|
|
dn->currency,
|
|
|
|
TALER_CURRENCY_LEN);
|
2018-01-01 23:07:36 +01:00
|
|
|
GNUNET_assert (GNUNET_YES ==
|
2019-08-25 16:18:24 +02:00
|
|
|
TALER_amount_is_valid (res));
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-03-18 18:55:41 +01:00
|
|
|
* Get the value of "zero" in a particular currency.
|
|
|
|
*
|
|
|
|
* @param cur currency description
|
2020-01-19 22:01:20 +01:00
|
|
|
* @param[out] amount amount to write the result to
|
2015-03-18 18:55:41 +01:00
|
|
|
* @return #GNUNET_OK if @a cur is a valid currency specification,
|
|
|
|
* #GNUNET_SYSERR if it is invalid.
|
2015-01-27 16:18:33 +01:00
|
|
|
*/
|
2015-03-18 18:55:41 +01:00
|
|
|
int
|
|
|
|
TALER_amount_get_zero (const char *cur,
|
2020-01-19 22:01:20 +01:00
|
|
|
struct TALER_Amount *amount)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
size_t slen;
|
2015-01-27 16:18:33 +01:00
|
|
|
|
2015-03-18 18:55:41 +01:00
|
|
|
slen = strlen (cur);
|
|
|
|
if (slen >= TALER_CURRENCY_LEN)
|
|
|
|
return GNUNET_SYSERR;
|
2020-01-19 22:01:20 +01:00
|
|
|
memset (amount,
|
2015-03-18 18:55:41 +01:00
|
|
|
0,
|
|
|
|
sizeof (struct TALER_Amount));
|
2020-01-19 22:01:20 +01:00
|
|
|
memcpy (amount->currency,
|
2015-03-18 18:55:41 +01:00
|
|
|
cur,
|
|
|
|
slen);
|
|
|
|
return GNUNET_OK;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-18 18:55:41 +01:00
|
|
|
/**
|
2017-03-15 12:01:06 +01:00
|
|
|
* Test if the given amount is valid.
|
2015-03-18 18:55:41 +01:00
|
|
|
*
|
2017-03-15 12:01:06 +01:00
|
|
|
* @param amount amount to check
|
|
|
|
* @return #GNUNET_OK if @a amount is valid
|
2015-03-18 18:55:41 +01:00
|
|
|
*/
|
2017-03-15 12:01:06 +01:00
|
|
|
int
|
|
|
|
TALER_amount_is_valid (const struct TALER_Amount *amount)
|
2015-03-18 18:55:41 +01:00
|
|
|
{
|
2017-03-15 12:01:06 +01:00
|
|
|
return ('\0' != amount->currency[0]);
|
2015-03-18 18:55:41 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-03 15:41:09 +02:00
|
|
|
/**
|
|
|
|
* Test if @a a is valid, NBO variant.
|
|
|
|
*
|
|
|
|
* @param a amount to test
|
|
|
|
* @return #GNUNET_YES if valid,
|
|
|
|
* #GNUNET_NO if invalid
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
test_valid_nbo (const struct TALER_AmountNBO *a)
|
|
|
|
{
|
|
|
|
return ('\0' != a->currency[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-03-18 18:55:41 +01:00
|
|
|
/**
|
|
|
|
* Test if @a a1 and @a a2 are the same currency.
|
|
|
|
*
|
|
|
|
* @param a1 amount to test
|
|
|
|
* @param a2 amount to test
|
|
|
|
* @return #GNUNET_YES if @a a1 and @a a2 are the same currency
|
|
|
|
* #GNUNET_NO if the currencies are different,
|
|
|
|
* #GNUNET_SYSERR if either amount is invalid
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
TALER_amount_cmp_currency (const struct TALER_Amount *a1,
|
|
|
|
const struct TALER_Amount *a2)
|
|
|
|
{
|
2017-03-15 12:01:06 +01:00
|
|
|
if ( (GNUNET_NO == TALER_amount_is_valid (a1)) ||
|
|
|
|
(GNUNET_NO == TALER_amount_is_valid (a2)) )
|
2015-03-18 18:55:41 +01:00
|
|
|
return GNUNET_SYSERR;
|
2015-04-13 17:20:46 +02:00
|
|
|
if (0 == strcasecmp (a1->currency,
|
2019-08-25 16:18:24 +02:00
|
|
|
a2->currency))
|
2015-03-18 18:55:41 +01:00
|
|
|
return GNUNET_YES;
|
|
|
|
return GNUNET_NO;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-06-03 15:41:09 +02:00
|
|
|
/**
|
|
|
|
* Test if @a a1 and @a a2 are the same currency, NBO variant.
|
|
|
|
*
|
|
|
|
* @param a1 amount to test
|
|
|
|
* @param a2 amount to test
|
|
|
|
* @return #GNUNET_YES if @a a1 and @a a2 are the same currency
|
|
|
|
* #GNUNET_NO if the currencies are different,
|
|
|
|
* #GNUNET_SYSERR if either amount is invalid
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
TALER_amount_cmp_currency_nbo (const struct TALER_AmountNBO *a1,
|
|
|
|
const struct TALER_AmountNBO *a2)
|
|
|
|
{
|
|
|
|
if ( (GNUNET_NO == test_valid_nbo (a1)) ||
|
|
|
|
(GNUNET_NO == test_valid_nbo (a2)) )
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
if (0 == strcasecmp (a1->currency,
|
2019-08-25 16:18:24 +02:00
|
|
|
a2->currency))
|
2015-06-03 15:41:09 +02:00
|
|
|
return GNUNET_YES;
|
|
|
|
return GNUNET_NO;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-27 16:18:33 +01:00
|
|
|
/**
|
2015-03-18 18:55:41 +01:00
|
|
|
* Compare the value/fraction of two amounts. Does not compare the currency.
|
|
|
|
* Comparing amounts of different currencies will cause the program to abort().
|
|
|
|
* If unsure, check with #TALER_amount_cmp_currency() first to be sure that
|
|
|
|
* the currencies of the two amounts are identical.
|
2015-01-27 16:18:33 +01:00
|
|
|
*
|
|
|
|
* @param a1 first amount
|
|
|
|
* @param a2 second amount
|
2015-07-05 13:18:49 +02:00
|
|
|
* @return result of the comparison,
|
|
|
|
* -1 if `a1 < a2`
|
|
|
|
* 1 if `a1 > a2`
|
|
|
|
* 0 if `a1 == a2`.
|
2015-01-27 16:18:33 +01:00
|
|
|
*/
|
|
|
|
int
|
2015-03-18 18:55:41 +01:00
|
|
|
TALER_amount_cmp (const struct TALER_Amount *a1,
|
|
|
|
const struct TALER_Amount *a2)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
struct TALER_Amount n1;
|
|
|
|
struct TALER_Amount n2;
|
|
|
|
|
|
|
|
GNUNET_assert (GNUNET_YES ==
|
2020-01-19 20:11:32 +01:00
|
|
|
TALER_amount_cmp_currency (a1,
|
|
|
|
a2));
|
2015-03-18 18:55:41 +01:00
|
|
|
n1 = *a1;
|
|
|
|
n2 = *a2;
|
2015-04-13 17:20:46 +02:00
|
|
|
GNUNET_assert (GNUNET_SYSERR !=
|
2019-08-25 16:18:24 +02:00
|
|
|
TALER_amount_normalize (&n1));
|
2015-04-13 17:20:46 +02:00
|
|
|
GNUNET_assert (GNUNET_SYSERR !=
|
2019-08-25 16:18:24 +02:00
|
|
|
TALER_amount_normalize (&n2));
|
2015-03-18 18:55:41 +01:00
|
|
|
if (n1.value == n2.value)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
if (n1.fraction < n2.fraction)
|
2015-01-27 16:18:33 +01:00
|
|
|
return -1;
|
2015-03-18 18:55:41 +01:00
|
|
|
if (n1.fraction > n2.fraction)
|
2015-01-27 16:18:33 +01:00
|
|
|
return 1;
|
|
|
|
return 0;
|
|
|
|
}
|
2015-03-18 18:55:41 +01:00
|
|
|
if (n1.value < n2.value)
|
2015-01-27 16:18:33 +01:00
|
|
|
return -1;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Perform saturating subtraction of amounts.
|
|
|
|
*
|
2020-01-19 20:11:32 +01:00
|
|
|
* @param[out] diff where to store (@a a1 - @a a2), or invalid if @a a2 > @a a1
|
2015-01-27 16:18:33 +01:00
|
|
|
* @param a1 amount to subtract from
|
|
|
|
* @param a2 amount to subtract
|
2015-03-18 18:55:41 +01:00
|
|
|
* @return #GNUNET_OK if the subtraction worked,
|
|
|
|
* #GNUNET_NO if @a a1 = @a a2
|
|
|
|
* #GNUNET_SYSERR if @a a2 > @a a1 or currencies are incompatible;
|
|
|
|
* @a diff is set to invalid
|
2015-01-27 16:18:33 +01:00
|
|
|
*/
|
2015-03-18 18:55:41 +01:00
|
|
|
int
|
|
|
|
TALER_amount_subtract (struct TALER_Amount *diff,
|
|
|
|
const struct TALER_Amount *a1,
|
|
|
|
const struct TALER_Amount *a2)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
struct TALER_Amount n1;
|
|
|
|
struct TALER_Amount n2;
|
2015-01-27 16:18:33 +01:00
|
|
|
|
2015-03-18 18:55:41 +01:00
|
|
|
if (GNUNET_YES !=
|
2020-01-19 20:11:32 +01:00
|
|
|
TALER_amount_cmp_currency (a1,
|
|
|
|
a2))
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
invalidate (diff);
|
|
|
|
return GNUNET_SYSERR;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
2020-01-19 20:11:32 +01:00
|
|
|
/* make local copies to avoid aliasing problems between
|
|
|
|
diff and a1/a2 */
|
2015-03-18 18:55:41 +01:00
|
|
|
n1 = *a1;
|
|
|
|
n2 = *a2;
|
2015-04-13 17:20:46 +02:00
|
|
|
if ( (GNUNET_SYSERR == TALER_amount_normalize (&n1)) ||
|
|
|
|
(GNUNET_SYSERR == TALER_amount_normalize (&n2)) )
|
|
|
|
{
|
|
|
|
invalidate (diff);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2015-01-27 16:18:33 +01:00
|
|
|
|
2015-03-18 18:55:41 +01:00
|
|
|
if (n1.fraction < n2.fraction)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
if (0 == n1.value)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
invalidate (diff);
|
|
|
|
return GNUNET_SYSERR;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
2015-03-18 18:55:41 +01:00
|
|
|
n1.fraction += TALER_AMOUNT_FRAC_BASE;
|
|
|
|
n1.value--;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
2015-03-18 18:55:41 +01:00
|
|
|
if (n1.value < n2.value)
|
|
|
|
{
|
|
|
|
invalidate (diff);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
2015-03-26 19:39:16 +01:00
|
|
|
TALER_amount_get_zero (n1.currency,
|
2015-03-18 18:55:41 +01:00
|
|
|
diff));
|
|
|
|
GNUNET_assert (n1.fraction >= n2.fraction);
|
|
|
|
diff->fraction = n1.fraction - n2.fraction;
|
|
|
|
GNUNET_assert (n1.value >= n2.value);
|
|
|
|
diff->value = n1.value - n2.value;
|
|
|
|
if ( (0 == diff->fraction) &&
|
|
|
|
(0 == diff->value) )
|
|
|
|
return GNUNET_NO;
|
|
|
|
return GNUNET_OK;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2015-03-18 18:55:41 +01:00
|
|
|
* Perform addition of amounts.
|
2015-01-27 16:18:33 +01:00
|
|
|
*
|
2020-01-19 20:11:32 +01:00
|
|
|
* @param[out] sum where to store @a a1 + @a a2, set to "invalid" on overflow
|
2015-01-27 16:18:33 +01:00
|
|
|
* @param a1 first amount to add
|
|
|
|
* @param a2 second amount to add
|
2015-03-18 18:55:41 +01:00
|
|
|
* @return #GNUNET_OK if the addition worked,
|
|
|
|
* #GNUNET_SYSERR on overflow
|
2015-01-27 16:18:33 +01:00
|
|
|
*/
|
2015-03-18 18:55:41 +01:00
|
|
|
int
|
|
|
|
TALER_amount_add (struct TALER_Amount *sum,
|
|
|
|
const struct TALER_Amount *a1,
|
|
|
|
const struct TALER_Amount *a2)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
struct TALER_Amount n1;
|
|
|
|
struct TALER_Amount n2;
|
2015-04-13 17:20:46 +02:00
|
|
|
struct TALER_Amount res;
|
2015-01-27 16:18:33 +01:00
|
|
|
|
2015-03-18 18:55:41 +01:00
|
|
|
if (GNUNET_YES !=
|
|
|
|
TALER_amount_cmp_currency (a1, a2))
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
invalidate (sum);
|
|
|
|
return GNUNET_SYSERR;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
2020-01-19 20:11:32 +01:00
|
|
|
/* make local copies to avoid aliasing problems between
|
|
|
|
diff and a1/a2 */
|
2015-03-18 18:55:41 +01:00
|
|
|
n1 = *a1;
|
|
|
|
n2 = *a2;
|
2015-04-13 17:20:46 +02:00
|
|
|
if ( (GNUNET_SYSERR == TALER_amount_normalize (&n1)) ||
|
|
|
|
(GNUNET_SYSERR == TALER_amount_normalize (&n2)) )
|
|
|
|
{
|
|
|
|
invalidate (sum);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2015-03-18 18:55:41 +01:00
|
|
|
|
|
|
|
GNUNET_assert (GNUNET_OK ==
|
|
|
|
TALER_amount_get_zero (a1->currency,
|
2015-04-13 17:20:46 +02:00
|
|
|
&res));
|
|
|
|
res.value = n1.value + n2.value;
|
|
|
|
if (res.value < n1.value)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
/* integer overflow */
|
|
|
|
invalidate (sum);
|
|
|
|
return GNUNET_SYSERR;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
2017-11-02 17:40:14 +01:00
|
|
|
if (res.value > MAX_AMOUNT_VALUE)
|
|
|
|
{
|
|
|
|
/* too large to be legal */
|
|
|
|
invalidate (sum);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2015-04-13 17:20:46 +02:00
|
|
|
res.fraction = n1.fraction + n2.fraction;
|
2015-03-18 18:55:41 +01:00
|
|
|
if (GNUNET_SYSERR ==
|
2015-04-13 17:20:46 +02:00
|
|
|
TALER_amount_normalize (&res))
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
/* integer overflow via carry from fraction */
|
|
|
|
invalidate (sum);
|
|
|
|
return GNUNET_SYSERR;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
2015-04-13 17:20:46 +02:00
|
|
|
*sum = res;
|
2015-03-18 18:55:41 +01:00
|
|
|
return GNUNET_OK;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Normalize the given amount.
|
|
|
|
*
|
2020-01-19 20:11:32 +01:00
|
|
|
* @param[in,out] amount amount to normalize
|
2015-03-18 18:55:41 +01:00
|
|
|
* @return #GNUNET_OK if normalization worked
|
|
|
|
* #GNUNET_NO if value was already normalized
|
|
|
|
* #GNUNET_SYSERR if value was invalid or could not be normalized
|
2015-01-27 16:18:33 +01:00
|
|
|
*/
|
2015-03-18 18:55:41 +01:00
|
|
|
int
|
|
|
|
TALER_amount_normalize (struct TALER_Amount *amount)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
int ret;
|
|
|
|
|
2017-03-15 12:01:06 +01:00
|
|
|
if (GNUNET_YES != TALER_amount_is_valid (amount))
|
2015-03-18 18:55:41 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
ret = GNUNET_NO;
|
|
|
|
while ( (amount->value != UINT64_MAX) &&
|
|
|
|
(amount->fraction >= TALER_AMOUNT_FRAC_BASE) )
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
amount->fraction -= TALER_AMOUNT_FRAC_BASE;
|
|
|
|
amount->value++;
|
|
|
|
ret = GNUNET_OK;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
2015-03-18 18:55:41 +01:00
|
|
|
if (amount->fraction >= TALER_AMOUNT_FRAC_BASE)
|
|
|
|
{
|
|
|
|
/* failed to normalize, adding up fractions caused
|
|
|
|
main value to overflow! */
|
2015-04-13 17:20:46 +02:00
|
|
|
invalidate (amount);
|
2015-03-18 18:55:41 +01:00
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
return ret;
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2019-06-18 13:20:43 +02:00
|
|
|
/**
|
2020-01-18 13:23:10 +01:00
|
|
|
* Convert the fraction of @a amount to a string in decimals.
|
2019-06-18 13:20:43 +02:00
|
|
|
*
|
|
|
|
* @param amount value to convert
|
2020-03-31 20:57:11 +02:00
|
|
|
* @param[out] tail where to write the result
|
2019-06-18 13:20:43 +02:00
|
|
|
*/
|
|
|
|
static void
|
|
|
|
amount_to_tail (const struct TALER_Amount *amount,
|
|
|
|
char tail[TALER_AMOUNT_FRAC_LEN + 1])
|
|
|
|
{
|
|
|
|
uint32_t n = amount->fraction;
|
2020-01-18 14:01:56 +01:00
|
|
|
unsigned int i;
|
2019-06-18 13:20:43 +02:00
|
|
|
|
2020-01-18 14:01:56 +01:00
|
|
|
for (i = 0; (i < TALER_AMOUNT_FRAC_LEN) && (0 != n); i++)
|
2019-06-18 13:20:43 +02:00
|
|
|
{
|
|
|
|
tail[i] = '0' + (n / (TALER_AMOUNT_FRAC_BASE / 10));
|
|
|
|
n = (n * 10) % (TALER_AMOUNT_FRAC_BASE);
|
|
|
|
}
|
|
|
|
tail[i] = '\0';
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-27 16:18:33 +01:00
|
|
|
/**
|
|
|
|
* Convert amount to string.
|
|
|
|
*
|
|
|
|
* @param amount amount to convert to string
|
|
|
|
* @return freshly allocated string representation
|
|
|
|
*/
|
|
|
|
char *
|
2015-03-18 18:55:41 +01:00
|
|
|
TALER_amount_to_string (const struct TALER_Amount *amount)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
char *result;
|
|
|
|
struct TALER_Amount norm;
|
|
|
|
|
2017-03-15 12:01:06 +01:00
|
|
|
if (GNUNET_YES != TALER_amount_is_valid (amount))
|
2015-03-18 18:55:41 +01:00
|
|
|
return NULL;
|
|
|
|
norm = *amount;
|
|
|
|
GNUNET_break (GNUNET_SYSERR !=
|
|
|
|
TALER_amount_normalize (&norm));
|
2019-06-18 13:20:43 +02:00
|
|
|
if (0 != norm.fraction)
|
2015-01-27 16:18:33 +01:00
|
|
|
{
|
2019-06-18 13:20:43 +02:00
|
|
|
char tail[TALER_AMOUNT_FRAC_LEN + 1];
|
|
|
|
|
|
|
|
amount_to_tail (&norm,
|
|
|
|
tail);
|
2015-03-18 18:55:41 +01:00
|
|
|
GNUNET_asprintf (&result,
|
|
|
|
"%s:%llu.%s",
|
|
|
|
norm.currency,
|
|
|
|
(unsigned long long) norm.value,
|
|
|
|
tail);
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-03-18 18:55:41 +01:00
|
|
|
GNUNET_asprintf (&result,
|
|
|
|
"%s:%llu",
|
|
|
|
norm.currency,
|
|
|
|
(unsigned long long) norm.value);
|
2015-01-27 16:18:33 +01:00
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-20 02:29:33 +01:00
|
|
|
/**
|
|
|
|
* Convert amount to string.
|
|
|
|
*
|
|
|
|
* @param amount amount to convert to string
|
|
|
|
* @return statically allocated buffer with string representation,
|
|
|
|
* NULL if the @a amount was invalid
|
|
|
|
*/
|
|
|
|
const char *
|
|
|
|
TALER_amount2s (const struct TALER_Amount *amount)
|
|
|
|
{
|
2020-01-19 20:11:32 +01:00
|
|
|
/* 12 is sufficient for a uint32_t value in decimal; 3 is for ":.\0" */
|
2017-03-20 02:29:33 +01:00
|
|
|
static char result[TALER_AMOUNT_FRAC_LEN + TALER_CURRENCY_LEN + 3 + 12];
|
|
|
|
struct TALER_Amount norm;
|
|
|
|
|
|
|
|
if (GNUNET_YES != TALER_amount_is_valid (amount))
|
|
|
|
return NULL;
|
|
|
|
norm = *amount;
|
|
|
|
GNUNET_break (GNUNET_SYSERR !=
|
|
|
|
TALER_amount_normalize (&norm));
|
2019-06-18 13:20:43 +02:00
|
|
|
if (0 != norm.fraction)
|
2017-03-20 02:29:33 +01:00
|
|
|
{
|
2019-06-18 13:20:43 +02:00
|
|
|
char tail[TALER_AMOUNT_FRAC_LEN + 1];
|
|
|
|
|
|
|
|
amount_to_tail (&norm,
|
|
|
|
tail);
|
2017-03-20 02:29:33 +01:00
|
|
|
GNUNET_snprintf (result,
|
|
|
|
sizeof (result),
|
|
|
|
"%s:%llu.%s",
|
|
|
|
norm.currency,
|
|
|
|
(unsigned long long) norm.value,
|
|
|
|
tail);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
GNUNET_snprintf (result,
|
|
|
|
sizeof (result),
|
|
|
|
"%s:%llu",
|
|
|
|
norm.currency,
|
|
|
|
(unsigned long long) norm.value);
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-03-06 16:35:30 +01:00
|
|
|
/**
|
2020-01-19 20:11:32 +01:00
|
|
|
* Divide an amount by a @a divisor. Note that this function
|
2017-03-06 16:35:30 +01:00
|
|
|
* may introduce a rounding error!
|
|
|
|
*
|
2020-01-19 20:11:32 +01:00
|
|
|
* @param[out] result where to store @a dividend / @a divisor
|
2017-03-06 16:35:30 +01:00
|
|
|
* @param dividend amount to divide
|
|
|
|
* @param divisor by what to divide, must be positive
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
TALER_amount_divide (struct TALER_Amount *result,
|
|
|
|
const struct TALER_Amount *dividend,
|
|
|
|
uint32_t divisor)
|
|
|
|
{
|
|
|
|
uint64_t modr;
|
|
|
|
|
2020-01-19 20:11:32 +01:00
|
|
|
GNUNET_assert (0 != divisor); /* division by zero is discouraged */
|
2017-03-06 16:35:30 +01:00
|
|
|
*result = *dividend;
|
2020-01-19 20:11:32 +01:00
|
|
|
/* in case @a dividend was not yet normalized */
|
|
|
|
GNUNET_assert (GNUNET_SYSERR !=
|
|
|
|
TALER_amount_normalize (result));
|
2017-03-06 16:35:30 +01:00
|
|
|
if (1 == divisor)
|
|
|
|
return;
|
|
|
|
modr = result->value % divisor;
|
|
|
|
result->value /= divisor;
|
2020-01-19 20:11:32 +01:00
|
|
|
/* modr fits into 32 bits, so we can safely multiply by (<32-bit) base and add fraction! */
|
2017-03-06 16:35:30 +01:00
|
|
|
modr = (modr * TALER_AMOUNT_FRAC_BASE) + result->fraction;
|
|
|
|
result->fraction = (uint32_t) (modr / divisor);
|
2020-01-19 20:11:32 +01:00
|
|
|
/* 'fraction' could now be larger than #TALER_AMOUNT_FRAC_BASE, so we must normalize */
|
2020-01-20 01:40:29 +01:00
|
|
|
GNUNET_assert (GNUNET_SYSERR !=
|
|
|
|
TALER_amount_normalize (result));
|
2017-03-06 16:35:30 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-01-13 18:16:01 +01:00
|
|
|
/**
|
2020-01-15 15:17:02 +01:00
|
|
|
* Round the amount to something that can be transferred on the wire.
|
2020-01-17 18:59:15 +01:00
|
|
|
* The rounding mode is specified via the smallest transferable unit,
|
2020-01-19 20:11:32 +01:00
|
|
|
* which must only have a fractional part *or* only a value (either
|
|
|
|
* of the two must be zero!).
|
|
|
|
*
|
|
|
|
* If the @a round_unit given is zero, we do nothing and return #GNUNET_NO.
|
2020-01-13 18:16:01 +01:00
|
|
|
*
|
|
|
|
* @param[in,out] amount amount to round down
|
2020-01-19 20:11:32 +01:00
|
|
|
* @param[in] round_unit unit that should be rounded down to, and
|
|
|
|
* either value part or the faction must be zero
|
2020-01-13 18:16:01 +01:00
|
|
|
* @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
|
2020-01-19 20:11:32 +01:00
|
|
|
* #GNUNET_SYSERR if the amount or currency or @a round_unit was invalid
|
2020-01-13 18:16:01 +01:00
|
|
|
*/
|
|
|
|
int
|
2020-01-15 15:17:02 +01:00
|
|
|
TALER_amount_round_down (struct TALER_Amount *amount,
|
2020-01-17 18:59:15 +01:00
|
|
|
const struct TALER_Amount *round_unit)
|
2020-01-13 18:16:01 +01:00
|
|
|
{
|
2020-03-15 20:42:47 +01:00
|
|
|
if (GNUNET_OK !=
|
|
|
|
TALER_amount_cmp_currency (amount,
|
|
|
|
round_unit))
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
2020-01-19 20:11:32 +01:00
|
|
|
if ( (0 != round_unit->fraction) &&
|
|
|
|
(0 != round_unit->value) )
|
|
|
|
{
|
|
|
|
GNUNET_break (0);
|
|
|
|
return GNUNET_SYSERR;
|
|
|
|
}
|
|
|
|
if ( (0 == round_unit->fraction) &&
|
|
|
|
(0 == round_unit->value) )
|
|
|
|
return GNUNET_NO; /* no rounding requested */
|
|
|
|
if (0 != round_unit->fraction)
|
|
|
|
{
|
|
|
|
uint32_t delta;
|
2020-01-15 15:17:02 +01:00
|
|
|
|
2020-01-19 20:11:32 +01:00
|
|
|
delta = amount->fraction % round_unit->fraction;
|
|
|
|
if (0 == delta)
|
|
|
|
return GNUNET_NO;
|
|
|
|
amount->fraction -= delta;
|
|
|
|
}
|
|
|
|
if (0 != round_unit->value)
|
|
|
|
{
|
|
|
|
uint64_t delta;
|
2020-01-17 18:59:15 +01:00
|
|
|
|
2020-01-19 20:11:32 +01:00
|
|
|
delta = amount->value % round_unit->value;
|
|
|
|
if (0 == delta)
|
|
|
|
return GNUNET_NO;
|
|
|
|
amount->value -= delta;
|
|
|
|
amount->fraction = 0;
|
|
|
|
}
|
2020-01-13 18:16:01 +01:00
|
|
|
return GNUNET_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2015-01-27 16:18:33 +01:00
|
|
|
/* end of amount.c */
|