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