amount.c (31703B)
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/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 47 if (len >= TALER_CURRENCY_LEN) 48 { 49 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 50 "Currency code name `%s' is too long\n", 51 str); 52 return GNUNET_SYSERR; 53 } 54 if (len == 0) 55 { 56 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 57 "Currency code name must be set\n"); 58 return GNUNET_SYSERR; 59 } 60 /* validate str has only legal characters in it! */ 61 for (unsigned int i = 0; '\0' != str[i]; i++) 62 { 63 if ( ('A' > str[i]) || ('Z' < str[i]) ) 64 { 65 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 66 "Currency code name `%s' contains illegal characters (only A-Z allowed)\n", 67 str); 68 return GNUNET_SYSERR; 69 } 70 } 71 return GNUNET_OK; 72 } 73 74 75 enum GNUNET_GenericReturnValue 76 TALER_string_to_amount (const char *str, 77 struct TALER_Amount *amount) 78 { 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 memcpy (&amount->currency[0], 109 str, 110 colon - str); 111 /* 0-terminate *and* normalize buffer by setting everything to '\0' */ 112 memset (&amount->currency [colon - str], 113 0, 114 TALER_CURRENCY_LEN - (colon - str)); 115 if (GNUNET_OK != 116 TALER_check_currency (amount->currency)) 117 { 118 invalidate (amount); 119 return GNUNET_SYSERR; 120 } 121 /* skip colon */ 122 value = colon + 1; 123 if ('\0' == value[0]) 124 { 125 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 126 "Actual value missing in amount `%s'\n", 127 str); 128 invalidate (amount); 129 return GNUNET_SYSERR; 130 } 131 132 amount->value = 0; 133 amount->fraction = 0; 134 135 /* parse value */ 136 while ('.' != *value) 137 { 138 int n; 139 140 if ('\0' == *value) 141 { 142 /* we are done */ 143 return GNUNET_OK; 144 } 145 if ( (*value < '0') || 146 (*value > '9') ) 147 { 148 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 149 "Invalid character `%c' in amount `%s'\n", 150 (int) *value, 151 str); 152 invalidate (amount); 153 return GNUNET_SYSERR; 154 } 155 n = *value - '0'; 156 if ( (amount->value * 10 < amount->value) || 157 (amount->value * 10 + n < amount->value) || 158 (amount->value > TALER_AMOUNT_MAX_VALUE) || 159 (amount->value * 10 + n > TALER_AMOUNT_MAX_VALUE) ) 160 { 161 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 162 "Value specified in amount `%s' is too large\n", 163 str); 164 invalidate (amount); 165 return GNUNET_SYSERR; 166 } 167 amount->value = (amount->value * 10) + n; 168 value++; 169 } 170 171 /* skip the dot */ 172 value++; 173 174 /* parse fraction */ 175 if ('\0' == *value) 176 { 177 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 178 "Amount `%s' ends abruptly after `.'\n", 179 str); 180 invalidate (amount); 181 return GNUNET_SYSERR; 182 } 183 b = TALER_AMOUNT_FRAC_BASE / 10; 184 while ('\0' != *value) 185 { 186 int n; 187 188 if (0 == b) 189 { 190 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 191 "Fractional value too small (only %u digits supported) in amount `%s'\n", 192 (unsigned int) TALER_AMOUNT_FRAC_LEN, 193 str); 194 invalidate (amount); 195 return GNUNET_SYSERR; 196 } 197 if ( (*value < '0') || 198 (*value > '9') ) 199 { 200 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 201 "Error after dot\n"); 202 invalidate (amount); 203 return GNUNET_SYSERR; 204 } 205 n = *value - '0'; 206 amount->fraction += n * b; 207 b /= 10; 208 value++; 209 } 210 return GNUNET_OK; 211 } 212 213 214 enum GNUNET_GenericReturnValue 215 TALER_string_to_amount_nbo (const char *str, 216 struct TALER_AmountNBO *amount_nbo) 217 { 218 struct TALER_Amount amount; 219 220 if (GNUNET_OK != 221 TALER_string_to_amount (str, 222 &amount)) 223 return GNUNET_SYSERR; 224 TALER_amount_hton (amount_nbo, 225 &amount); 226 return GNUNET_OK; 227 } 228 229 230 void 231 TALER_amount_hton (struct TALER_AmountNBO *res, 232 const struct TALER_Amount *d) 233 { 234 GNUNET_assert (GNUNET_YES == 235 TALER_amount_is_valid (d)); 236 res->value = GNUNET_htonll (d->value); 237 res->fraction = htonl (d->fraction); 238 for (unsigned int i = 0; i<TALER_CURRENCY_LEN; i++) 239 res->currency[i] = d->currency[i]; 240 } 241 242 243 void 244 TALER_amount_ntoh (struct TALER_Amount *res, 245 const struct TALER_AmountNBO *dn) 246 { 247 res->value = GNUNET_ntohll (dn->value); 248 res->fraction = ntohl (dn->fraction); 249 GNUNET_memcpy (res->currency, 250 dn->currency, 251 TALER_CURRENCY_LEN); 252 GNUNET_assert (GNUNET_YES == 253 TALER_amount_is_valid (res)); 254 } 255 256 257 enum GNUNET_GenericReturnValue 258 TALER_amount_set_zero (const char *cur, 259 struct TALER_Amount *amount) 260 { 261 char tmp[TALER_CURRENCY_LEN]; 262 size_t slen; 263 264 if (GNUNET_OK != 265 TALER_check_currency (cur)) 266 return GNUNET_SYSERR; 267 slen = strlen (cur); 268 /* make a copy of 'cur' to 'tmp' as the memset may clobber cur 269 if cur aliases &amount->currency! */ 270 memcpy (tmp, 271 cur, 272 slen); 273 memset (amount, 274 0, 275 sizeof (struct TALER_Amount)); 276 for (unsigned int i = 0; i<slen; i++) 277 amount->currency[i] = tmp[i]; 278 return GNUNET_OK; 279 } 280 281 282 enum GNUNET_GenericReturnValue 283 TALER_amount_is_valid (const struct TALER_Amount *amount) 284 { 285 if (amount->value > TALER_AMOUNT_MAX_VALUE) 286 { 287 GNUNET_break (0); 288 return GNUNET_SYSERR; 289 } 290 return ('\0' != amount->currency[0]) ? GNUNET_OK : GNUNET_NO; 291 } 292 293 294 enum GNUNET_GenericReturnValue 295 TALER_amount_max (struct TALER_Amount *ma, 296 const struct TALER_Amount *a1, 297 const struct TALER_Amount *a2) 298 { 299 if (GNUNET_OK != 300 TALER_amount_cmp_currency (a1, 301 a2)) 302 { 303 memset (ma, 304 0, 305 sizeof (*ma)); 306 return GNUNET_SYSERR; 307 } 308 if (1 == TALER_amount_cmp (a1, 309 a2)) 310 *ma = *a1; 311 else 312 *ma = *a2; 313 return GNUNET_OK; 314 } 315 316 317 enum GNUNET_GenericReturnValue 318 TALER_amount_min (struct TALER_Amount *mi, 319 const struct TALER_Amount *a1, 320 const struct TALER_Amount *a2) 321 { 322 if (GNUNET_OK != 323 TALER_amount_cmp_currency (a1, 324 a2)) 325 { 326 memset (mi, 327 0, 328 sizeof (*mi)); 329 return GNUNET_SYSERR; 330 } 331 if (1 == TALER_amount_cmp (a1, 332 a2)) 333 *mi = *a2; 334 else 335 *mi = *a1; 336 return GNUNET_OK; 337 } 338 339 340 bool 341 TALER_amount_is_zero (const struct TALER_Amount *amount) 342 { 343 if (GNUNET_OK != 344 TALER_amount_is_valid (amount)) 345 return false; 346 return 347 (0 == amount->value) && 348 (0 == amount->fraction); 349 } 350 351 352 enum GNUNET_GenericReturnValue 353 TALER_amount_is_currency (const struct TALER_Amount *amount, 354 const char *currency) 355 { 356 if (GNUNET_OK != 357 TALER_amount_is_valid (amount)) 358 return GNUNET_SYSERR; 359 return (0 == strcasecmp (currency, 360 amount->currency)) 361 ? GNUNET_OK 362 : GNUNET_NO; 363 } 364 365 366 /** 367 * Test if @a a is valid, NBO variant. 368 * 369 * @param a amount to test 370 * @return #GNUNET_YES if valid, 371 * #GNUNET_NO if invalid 372 */ 373 static enum GNUNET_GenericReturnValue 374 test_valid_nbo (const struct TALER_AmountNBO *a) 375 { 376 return ('\0' != a->currency[0]) ? GNUNET_YES : GNUNET_NO; 377 } 378 379 380 enum GNUNET_GenericReturnValue 381 TALER_amount_cmp_currency (const struct TALER_Amount *a1, 382 const struct TALER_Amount *a2) 383 { 384 if ( (GNUNET_NO == TALER_amount_is_valid (a1)) || 385 (GNUNET_NO == TALER_amount_is_valid (a2)) ) 386 return GNUNET_SYSERR; 387 if (0 == strcasecmp (a1->currency, 388 a2->currency)) 389 return GNUNET_YES; 390 return GNUNET_NO; 391 } 392 393 394 enum GNUNET_GenericReturnValue 395 TALER_amount_cmp_currency_nbo (const struct TALER_AmountNBO *a1, 396 const struct TALER_AmountNBO *a2) 397 { 398 if ( (GNUNET_NO == test_valid_nbo (a1)) || 399 (GNUNET_NO == test_valid_nbo (a2)) ) 400 return GNUNET_SYSERR; 401 if (0 == strcasecmp (a1->currency, 402 a2->currency)) 403 return GNUNET_YES; 404 return GNUNET_NO; 405 } 406 407 408 int 409 TALER_amount_cmp (const struct TALER_Amount *a1, 410 const struct TALER_Amount *a2) 411 { 412 struct TALER_Amount n1; 413 struct TALER_Amount n2; 414 415 GNUNET_assert (GNUNET_YES == 416 TALER_amount_cmp_currency (a1, 417 a2)); 418 n1 = *a1; 419 n2 = *a2; 420 GNUNET_assert (GNUNET_SYSERR != 421 TALER_amount_normalize (&n1)); 422 GNUNET_assert (GNUNET_SYSERR != 423 TALER_amount_normalize (&n2)); 424 if (n1.value == n2.value) 425 { 426 if (n1.fraction < n2.fraction) 427 return -1; 428 if (n1.fraction > n2.fraction) 429 return 1; 430 return 0; 431 } 432 if (n1.value < n2.value) 433 return -1; 434 return 1; 435 } 436 437 438 int 439 TALER_amount_cmp_nbo (const struct TALER_AmountNBO *a1, 440 const struct TALER_AmountNBO *a2) 441 { 442 struct TALER_Amount h1; 443 struct TALER_Amount h2; 444 445 TALER_amount_ntoh (&h1, 446 a1); 447 TALER_amount_ntoh (&h2, 448 a2); 449 return TALER_amount_cmp (&h1, 450 &h2); 451 } 452 453 454 enum TALER_AmountArithmeticResult 455 TALER_amount_subtract (struct TALER_Amount *diff, 456 const struct TALER_Amount *a1, 457 const struct TALER_Amount *a2) 458 { 459 struct TALER_Amount n1; 460 struct TALER_Amount n2; 461 462 if (GNUNET_YES != 463 TALER_amount_cmp_currency (a1, 464 a2)) 465 { 466 invalidate (diff); 467 return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE; 468 } 469 /* make local copies to avoid aliasing problems between 470 diff and a1/a2 */ 471 n1 = *a1; 472 n2 = *a2; 473 if ( (GNUNET_SYSERR == TALER_amount_normalize (&n1)) || 474 (GNUNET_SYSERR == TALER_amount_normalize (&n2)) ) 475 { 476 invalidate (diff); 477 return TALER_AAR_INVALID_NORMALIZATION_FAILED; 478 } 479 480 if (n1.fraction < n2.fraction) 481 { 482 if (0 == n1.value) 483 { 484 invalidate (diff); 485 return TALER_AAR_INVALID_NEGATIVE_RESULT; 486 } 487 n1.fraction += TALER_AMOUNT_FRAC_BASE; 488 n1.value--; 489 } 490 if (n1.value < n2.value) 491 { 492 invalidate (diff); 493 return TALER_AAR_INVALID_NEGATIVE_RESULT; 494 } 495 GNUNET_assert (GNUNET_OK == 496 TALER_amount_set_zero (n1.currency, 497 diff)); 498 GNUNET_assert (n1.fraction >= n2.fraction); 499 diff->fraction = n1.fraction - n2.fraction; 500 GNUNET_assert (n1.value >= n2.value); 501 diff->value = n1.value - n2.value; 502 if ( (0 == diff->fraction) && 503 (0 == diff->value) ) 504 return TALER_AAR_RESULT_ZERO; 505 return TALER_AAR_RESULT_POSITIVE; 506 } 507 508 509 enum TALER_AmountArithmeticResult 510 TALER_amount_add (struct TALER_Amount *sum, 511 const struct TALER_Amount *a1, 512 const struct TALER_Amount *a2) 513 { 514 struct TALER_Amount n1; 515 struct TALER_Amount n2; 516 struct TALER_Amount res; 517 518 if (GNUNET_YES != 519 TALER_amount_cmp_currency (a1, 520 a2)) 521 { 522 invalidate (sum); 523 return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE; 524 } 525 /* make local copies to avoid aliasing problems between 526 diff and a1/a2 */ 527 n1 = *a1; 528 n2 = *a2; 529 if ( (GNUNET_SYSERR == 530 TALER_amount_normalize (&n1)) || 531 (GNUNET_SYSERR == 532 TALER_amount_normalize (&n2)) ) 533 { 534 invalidate (sum); 535 return TALER_AAR_INVALID_NORMALIZATION_FAILED; 536 } 537 538 GNUNET_assert (GNUNET_OK == 539 TALER_amount_set_zero (a1->currency, 540 &res)); 541 res.value = n1.value + n2.value; 542 if (res.value < n1.value) 543 { 544 /* integer overflow */ 545 invalidate (sum); 546 return TALER_AAR_INVALID_RESULT_OVERFLOW; 547 } 548 if (res.value > TALER_AMOUNT_MAX_VALUE) 549 { 550 /* too large to be legal */ 551 invalidate (sum); 552 return TALER_AAR_INVALID_RESULT_OVERFLOW; 553 } 554 res.fraction = n1.fraction + n2.fraction; 555 if (GNUNET_SYSERR == 556 TALER_amount_normalize (&res)) 557 { 558 /* integer overflow via carry from fraction */ 559 invalidate (sum); 560 return TALER_AAR_INVALID_RESULT_OVERFLOW; 561 } 562 *sum = res; 563 if ( (0 == sum->fraction) && 564 (0 == sum->value) ) 565 return TALER_AAR_RESULT_ZERO; 566 return TALER_AAR_RESULT_POSITIVE; 567 } 568 569 570 enum GNUNET_GenericReturnValue 571 TALER_amount_normalize (struct TALER_Amount *amount) 572 { 573 uint32_t overflow; 574 575 if (GNUNET_YES != TALER_amount_is_valid (amount)) 576 return GNUNET_SYSERR; 577 if (amount->fraction < TALER_AMOUNT_FRAC_BASE) 578 return GNUNET_NO; 579 overflow = amount->fraction / TALER_AMOUNT_FRAC_BASE; 580 amount->fraction %= TALER_AMOUNT_FRAC_BASE; 581 amount->value += overflow; 582 if ( (amount->value < overflow) || 583 (amount->value > TALER_AMOUNT_MAX_VALUE) ) 584 { 585 invalidate (amount); 586 return GNUNET_SYSERR; 587 } 588 return GNUNET_OK; 589 } 590 591 592 /** 593 * Convert the fraction of @a amount to a string in decimals. 594 * 595 * @param amount value to convert 596 * @param[out] tail where to write the result 597 */ 598 static void 599 amount_to_tail (const struct TALER_Amount *amount, 600 char tail[TALER_AMOUNT_FRAC_LEN + 1]) 601 { 602 uint32_t n = amount->fraction; 603 unsigned int i; 604 605 for (i = 0; (i < TALER_AMOUNT_FRAC_LEN) && (0 != n); i++) 606 { 607 tail[i] = '0' + (n / (TALER_AMOUNT_FRAC_BASE / 10)); 608 n = (n * 10) % (TALER_AMOUNT_FRAC_BASE); 609 } 610 tail[i] = '\0'; 611 } 612 613 614 char * 615 TALER_amount_to_string (const struct TALER_Amount *amount) 616 { 617 char *result; 618 struct TALER_Amount norm; 619 620 if (GNUNET_YES != 621 TALER_amount_is_valid (amount)) 622 return NULL; 623 norm = *amount; 624 if (GNUNET_SYSERR == 625 TALER_amount_normalize (&norm)) 626 { 627 GNUNET_break (0); 628 return NULL; 629 } 630 if (0 != norm.fraction) 631 { 632 char tail[TALER_AMOUNT_FRAC_LEN + 1]; 633 634 amount_to_tail (&norm, 635 tail); 636 GNUNET_asprintf (&result, 637 "%s:%llu.%s", 638 norm.currency, 639 (unsigned long long) norm.value, 640 tail); 641 } 642 else 643 { 644 GNUNET_asprintf (&result, 645 "%s:%llu", 646 norm.currency, 647 (unsigned long long) norm.value); 648 } 649 return result; 650 } 651 652 653 const char * 654 TALER_amount2s (const struct TALER_Amount *amount) 655 { 656 /* 24 is sufficient for a uint64_t value in decimal; 3 is for ":.\0" */ 657 static TALER_THREAD_LOCAL char result[TALER_AMOUNT_FRAC_LEN 658 + TALER_CURRENCY_LEN + 3 + 24]; 659 struct TALER_Amount norm; 660 661 if (GNUNET_YES != 662 TALER_amount_is_valid (amount)) 663 return NULL; 664 norm = *amount; 665 if (GNUNET_SYSERR == 666 TALER_amount_normalize (&norm)) 667 { 668 GNUNET_break (0); 669 return NULL; 670 } 671 if (0 != norm.fraction) 672 { 673 char tail[TALER_AMOUNT_FRAC_LEN + 1]; 674 675 amount_to_tail (&norm, 676 tail); 677 GNUNET_snprintf (result, 678 sizeof (result), 679 "%s:%llu.%s", 680 norm.currency, 681 (unsigned long long) norm.value, 682 tail); 683 } 684 else 685 { 686 GNUNET_snprintf (result, 687 sizeof (result), 688 "%s:%llu", 689 norm.currency, 690 (unsigned long long) norm.value); 691 } 692 return result; 693 } 694 695 696 void 697 TALER_amount_divide (struct TALER_Amount *result, 698 const struct TALER_Amount *dividend, 699 uint32_t divisor) 700 { 701 uint64_t modr; 702 703 GNUNET_assert (0 != divisor); /* division by zero is discouraged */ 704 *result = *dividend; 705 /* in case @a dividend was not yet normalized */ 706 GNUNET_assert (GNUNET_SYSERR != 707 TALER_amount_normalize (result)); 708 if (1 == divisor) 709 return; 710 modr = result->value % divisor; 711 result->value /= divisor; 712 /* modr fits into 32 bits, so we can safely multiply by (<32-bit) base and add fraction! */ 713 modr = (modr * TALER_AMOUNT_FRAC_BASE) + result->fraction; 714 result->fraction = (uint32_t) (modr / divisor); 715 /* 'fraction' could now be larger than #TALER_AMOUNT_FRAC_BASE, so we must normalize */ 716 GNUNET_assert (GNUNET_SYSERR != 717 TALER_amount_normalize (result)); 718 } 719 720 721 int 722 TALER_amount_divide2 (const struct TALER_Amount *dividend, 723 const struct TALER_Amount *divisor) 724 { 725 double approx; 726 double d; 727 double r; 728 int ret; 729 struct TALER_Amount tmp; 730 struct TALER_Amount nxt; 731 732 if (GNUNET_YES != 733 TALER_amount_cmp_currency (dividend, 734 divisor)) 735 { 736 GNUNET_break (0); 737 return -1; 738 } 739 if ( (0 == divisor->fraction) && 740 (0 == divisor->value) ) 741 return INT_MAX; 742 /* first, get rounded approximation */ 743 d = ((double) dividend->value) * ((double) TALER_AMOUNT_FRAC_BASE) 744 + ( (double) dividend->fraction); 745 r = ((double) divisor->value) * ((double) TALER_AMOUNT_FRAC_BASE) 746 + ( (double) divisor->fraction); 747 approx = d / r; 748 if (approx > ((double) INT_MAX)) 749 return INT_MAX; /* 'infinity' */ 750 /* round down */ 751 if (approx < 2) 752 ret = 0; 753 else 754 ret = (int) approx - 2; 755 /* Now do *exact* calculation, using well rounded-down factor as starting 756 point to avoid having to do too many steps. */ 757 GNUNET_assert (0 <= 758 TALER_amount_multiply (&tmp, 759 divisor, 760 ret)); 761 /* in practice, this loop will only run for one or two iterations */ 762 while (1) 763 { 764 enum TALER_AmountArithmeticResult aar; 765 766 aar = TALER_amount_add (&nxt, 767 &tmp, 768 divisor); 769 if (TALER_AAR_INVALID_RESULT_OVERFLOW == aar) 770 break; /* nxt would exceed TALER_AMOUNT_MAX_VALUE, and thus 771 certainly the (presumed valid) dividend */ 772 GNUNET_assert (0 <= aar); 773 if (1 == 774 TALER_amount_cmp (&nxt, 775 dividend)) 776 break; /* nxt > dividend */ 777 ret++; 778 tmp = nxt; 779 } 780 return ret; 781 } 782 783 784 enum TALER_AmountArithmeticResult 785 TALER_amount_multiply (struct TALER_Amount *result, 786 const struct TALER_Amount *amount, 787 uint32_t factor) 788 { 789 struct TALER_Amount in = *amount; 790 791 if (GNUNET_SYSERR == 792 TALER_amount_normalize (&in)) 793 { 794 invalidate (result); 795 return TALER_AAR_INVALID_NORMALIZATION_FAILED; 796 } 797 GNUNET_memcpy (result->currency, 798 amount->currency, 799 TALER_CURRENCY_LEN); 800 if ( (0 == factor) || 801 ( (0 == in.value) && 802 (0 == in.fraction) ) ) 803 { 804 result->value = 0; 805 result->fraction = 0; 806 return TALER_AAR_RESULT_ZERO; 807 } 808 result->value = in.value * ((uint64_t) factor); 809 if (in.value != result->value / factor) 810 { 811 invalidate (result); 812 return TALER_AAR_INVALID_RESULT_OVERFLOW; 813 } 814 { 815 /* This multiplication cannot overflow since both inputs are 32-bit values */ 816 uint64_t tmp = ((uint64_t) factor) * ((uint64_t) in.fraction); 817 uint64_t res; 818 819 res = tmp / TALER_AMOUNT_FRAC_BASE; 820 /* check for overflow */ 821 if (result->value + res < result->value) 822 { 823 invalidate (result); 824 return TALER_AAR_INVALID_RESULT_OVERFLOW; 825 } 826 result->value += res; 827 result->fraction = tmp % TALER_AMOUNT_FRAC_BASE; 828 } 829 if (result->value > TALER_AMOUNT_MAX_VALUE) 830 { 831 invalidate (result); 832 return TALER_AAR_INVALID_RESULT_OVERFLOW; 833 } 834 /* This check should be redundant... */ 835 GNUNET_assert (GNUNET_SYSERR != 836 TALER_amount_normalize (result)); 837 return TALER_AAR_RESULT_POSITIVE; 838 } 839 840 841 enum GNUNET_GenericReturnValue 842 TALER_amount_round_down (struct TALER_Amount *amount, 843 const struct TALER_Amount *round_unit) 844 { 845 if (GNUNET_OK != 846 TALER_amount_cmp_currency (amount, 847 round_unit)) 848 { 849 GNUNET_break (0); 850 return GNUNET_SYSERR; 851 } 852 if ( (0 != round_unit->fraction) && 853 (0 != round_unit->value) ) 854 { 855 GNUNET_break (0); 856 return GNUNET_SYSERR; 857 } 858 if ( (0 == round_unit->fraction) && 859 (0 == round_unit->value) ) 860 return GNUNET_NO; /* no rounding requested */ 861 if (0 != round_unit->fraction) 862 { 863 uint32_t delta; 864 865 delta = amount->fraction % round_unit->fraction; 866 if (0 == delta) 867 return GNUNET_NO; 868 amount->fraction -= delta; 869 } 870 if (0 != round_unit->value) 871 { 872 uint64_t delta; 873 874 delta = amount->value % round_unit->value; 875 if ( (0 == delta) && 876 (0 == amount->fraction) ) 877 return GNUNET_NO; 878 amount->value -= delta; 879 amount->fraction = 0; 880 } 881 return GNUNET_OK; 882 } 883 884 885 void 886 TALER_amount_set_free (struct TALER_AmountSet *as) 887 { 888 GNUNET_array_grow (as->taa, 889 as->taa_size, 890 0); 891 } 892 893 894 enum TALER_AmountArithmeticResult 895 TALER_amount_set_add (struct TALER_AmountSet *as, 896 const struct TALER_Amount *val, 897 const struct TALER_Amount *cap) 898 { 899 for (unsigned int i = 0; i<as->taa_size; i++) 900 { 901 struct TALER_Amount *ai = &as->taa[i]; 902 enum TALER_AmountArithmeticResult aar; 903 904 if (GNUNET_OK != 905 TALER_amount_cmp_currency (ai, 906 val)) 907 continue; 908 aar = TALER_amount_add (ai, 909 ai, 910 val); 911 /* If we have a cap, we tolerate the overflow */ 912 if ( (aar < 0) && 913 ( (NULL == cap) || 914 (TALER_AAR_INVALID_RESULT_OVERFLOW != aar) ) ) 915 return aar; /* hard error */ 916 if (TALER_AAR_INVALID_RESULT_OVERFLOW == aar) 917 { 918 if (GNUNET_OK != 919 TALER_amount_cmp_currency (val, 920 cap)) 921 return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE; 922 *ai = *cap; 923 return (TALER_amount_is_zero (cap)) 924 ? TALER_AAR_RESULT_ZERO 925 : TALER_AAR_RESULT_POSITIVE; 926 } 927 GNUNET_assert (aar >= 0); 928 if (NULL != cap) 929 GNUNET_assert (GNUNET_OK == 930 TALER_amount_min (ai, 931 ai, 932 cap)); 933 return (TALER_amount_is_zero (ai)) 934 ? TALER_AAR_RESULT_ZERO 935 : TALER_AAR_RESULT_POSITIVE; 936 } 937 GNUNET_array_append (as->taa, 938 as->taa_size, 939 *val); 940 { 941 struct TALER_Amount *ai = &as->taa[as->taa_size - 1]; 942 943 if (NULL != cap) 944 { 945 if (GNUNET_OK != 946 TALER_amount_cmp_currency (val, 947 cap)) 948 return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE; 949 GNUNET_assert (GNUNET_OK == 950 TALER_amount_min (ai, 951 ai, 952 cap)); 953 } 954 return (TALER_amount_is_zero (ai)) 955 ? TALER_AAR_RESULT_ZERO 956 : TALER_AAR_RESULT_POSITIVE; 957 } 958 } 959 960 961 bool 962 TALER_amount_set_test_above (const struct TALER_AmountSet *as, 963 const struct TALER_Amount *b) 964 { 965 for (unsigned int i = 0; i<as->taa_size; i++) 966 { 967 const struct TALER_Amount *asi = &as->taa[i]; 968 969 if (GNUNET_OK != 970 TALER_amount_cmp_currency (b, 971 asi)) 972 continue; 973 if (1 != 974 TALER_amount_cmp (b, 975 asi)) 976 return true; 977 } 978 return false; 979 } 980 981 982 const struct TALER_Amount * 983 TALER_amount_set_find (const char *currency, 984 const struct TALER_AmountSet *as) 985 { 986 static TALER_THREAD_LOCAL struct TALER_Amount z; 987 988 for (unsigned int i = 0; i<as->taa_size; i++) 989 { 990 const struct TALER_Amount *asi = &as->taa[i]; 991 992 if (0 == strcasecmp (currency, 993 asi->currency)) 994 return asi; 995 } 996 if (GNUNET_OK != 997 TALER_amount_set_zero (currency, 998 &z)) 999 { 1000 GNUNET_break (0); 1001 return NULL; 1002 } 1003 return &z; 1004 } 1005 1006 1007 /** 1008 * Upper bound on the length of the string representation of a 1009 * single amount: the currency, the ':', the value, the '.', the 1010 * fraction and the '\0'. 24 is sufficient for a uint64_t value 1011 * in decimal. 1012 */ 1013 #define AMOUNT_STR_MAX (TALER_AMOUNT_FRAC_LEN \ 1014 + TALER_CURRENCY_LEN + 3 + 24) 1015 1016 1017 void 1018 TALER_amount_list_free (struct TALER_AmountList *al) 1019 { 1020 GNUNET_array_grow (al->tal, 1021 al->tal_len, 1022 0); 1023 } 1024 1025 1026 enum GNUNET_GenericReturnValue 1027 TALER_string_to_amount_list (const char *str, 1028 struct TALER_AmountList *al) 1029 { 1030 struct TALER_AmountList tmp = { 1031 .tal = NULL, 1032 .tal_len = 0 1033 }; 1034 const char *pos = str; 1035 1036 /* skip leading whitespace, so that an all-whitespace option 1037 value means "free" and not "malformed" */ 1038 while (isspace ( (unsigned char) pos[0])) 1039 pos++; 1040 if ('\0' == pos[0]) 1041 { 1042 al->tal = NULL; 1043 al->tal_len = 0; 1044 return GNUNET_OK; 1045 } 1046 while (1) 1047 { 1048 const char *end = strchr (pos, 1049 (int) ';'); 1050 size_t len = (NULL == end) 1051 ? strlen (pos) 1052 : (size_t) (end - pos); 1053 struct TALER_Amount a; 1054 char *component; 1055 1056 if (0 == len) 1057 { 1058 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1059 "Empty component in amount list `%s'\n", 1060 str); 1061 TALER_amount_list_free (&tmp); 1062 return GNUNET_SYSERR; 1063 } 1064 component = GNUNET_strndup (pos, 1065 len); 1066 if (GNUNET_OK != 1067 TALER_string_to_amount (component, 1068 &a)) 1069 { 1070 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1071 "Invalid amount `%s' in amount list `%s'\n", 1072 component, 1073 str); 1074 GNUNET_free (component); 1075 TALER_amount_list_free (&tmp); 1076 return GNUNET_SYSERR; 1077 } 1078 GNUNET_free (component); 1079 /* A repeated currency is a typo, not an accumulation: which of 1080 the two prices would apply is anyone's guess. */ 1081 if (NULL != 1082 TALER_amount_list_find (&tmp, 1083 a.currency)) 1084 { 1085 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1086 "Currency `%s' given more than once in amount list `%s'\n", 1087 a.currency, 1088 str); 1089 TALER_amount_list_free (&tmp); 1090 return GNUNET_SYSERR; 1091 } 1092 GNUNET_array_append (tmp.tal, 1093 tmp.tal_len, 1094 a); 1095 if (NULL == end) 1096 break; 1097 pos = end + 1; 1098 } 1099 *al = tmp; 1100 return GNUNET_OK; 1101 } 1102 1103 1104 const char * 1105 TALER_amount_list2s (const struct TALER_AmountList *al) 1106 { 1107 static TALER_THREAD_LOCAL char *result; 1108 static TALER_THREAD_LOCAL size_t result_size; 1109 size_t need; 1110 size_t off = 0; 1111 1112 /* one separator per entry is one too many, which covers the '\0' */ 1113 need = (al->tal_len + 1) * (AMOUNT_STR_MAX + 1); 1114 if (need > result_size) 1115 { 1116 GNUNET_free (result); 1117 result = GNUNET_malloc (need); 1118 result_size = need; 1119 } 1120 result[0] = '\0'; 1121 for (unsigned int i = 0; i<al->tal_len; i++) 1122 { 1123 const char *as = TALER_amount2s (&al->tal[i]); 1124 1125 if (NULL == as) 1126 { 1127 GNUNET_break (0); 1128 return NULL; 1129 } 1130 off += GNUNET_snprintf (&result[off], 1131 result_size - off, 1132 "%s%s", 1133 (0 == i) ? "" : ";", 1134 as); 1135 } 1136 return result; 1137 } 1138 1139 1140 const struct TALER_Amount * 1141 TALER_amount_list_find (const struct TALER_AmountList *al, 1142 const char *currency) 1143 { 1144 for (unsigned int i = 0; i<al->tal_len; i++) 1145 { 1146 const struct TALER_Amount *ali = &al->tal[i]; 1147 1148 if (0 == strcasecmp (currency, 1149 ali->currency)) 1150 return ali; 1151 } 1152 return NULL; 1153 } 1154 1155 1156 enum GNUNET_GenericReturnValue 1157 TALER_amount_list_check_uniform (const struct TALER_AmountList *al) 1158 { 1159 bool have_zero = false; 1160 bool have_price = false; 1161 1162 for (unsigned int i = 0; i<al->tal_len; i++) 1163 { 1164 if (TALER_amount_is_zero (&al->tal[i])) 1165 have_zero = true; 1166 else 1167 have_price = true; 1168 } 1169 if (have_zero && have_price) 1170 return GNUNET_SYSERR; 1171 if (have_price) 1172 return GNUNET_OK; 1173 return GNUNET_NO; /* all zero, or empty */ 1174 } 1175 1176 1177 bool 1178 TALER_amount_list_covers (const struct TALER_AmountList *al, 1179 const char *const *currencies, 1180 unsigned int currencies_len) 1181 { 1182 /* @a al has no duplicates, so equal length plus each currency 1183 being present is enough to conclude the two agree exactly */ 1184 if (al->tal_len != currencies_len) 1185 return false; 1186 for (unsigned int i = 0; i<currencies_len; i++) 1187 if (NULL == 1188 TALER_amount_list_find (al, 1189 currencies[i])) 1190 return false; 1191 return true; 1192 } 1193 1194 1195 enum GNUNET_GenericReturnValue 1196 TALER_amount_list_multiply (struct TALER_AmountList *al, 1197 uint32_t n) 1198 { 1199 struct TALER_Amount *tmp; 1200 1201 if (0 == n) 1202 { 1203 GNUNET_break (0); 1204 return GNUNET_SYSERR; 1205 } 1206 if ( (1 == n) || 1207 (0 == al->tal_len) ) 1208 return GNUNET_OK; 1209 /* compute into a scratch array first, so that an overflow in a 1210 late currency does not leave the early ones multiplied */ 1211 tmp = GNUNET_new_array (al->tal_len, 1212 struct TALER_Amount); 1213 for (unsigned int i = 0; i<al->tal_len; i++) 1214 { 1215 if (0 > 1216 TALER_amount_multiply (&tmp[i], 1217 &al->tal[i], 1218 n)) 1219 { 1220 GNUNET_free (tmp); 1221 return GNUNET_SYSERR; 1222 } 1223 } 1224 GNUNET_memcpy (al->tal, 1225 tmp, 1226 al->tal_len * sizeof (struct TALER_Amount)); 1227 GNUNET_free (tmp); 1228 return GNUNET_OK; 1229 } 1230 1231 1232 void 1233 TALER_amount_list_copy (struct TALER_AmountList *dst, 1234 const struct TALER_AmountList *src) 1235 { 1236 dst->tal_len = src->tal_len; 1237 if (0 == src->tal_len) 1238 { 1239 dst->tal = NULL; 1240 return; 1241 } 1242 dst->tal = GNUNET_new_array (src->tal_len, 1243 struct TALER_Amount); 1244 GNUNET_memcpy (dst->tal, 1245 src->tal, 1246 src->tal_len * sizeof (struct TALER_Amount)); 1247 } 1248 1249 1250 /* end of amount.c */