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