amount.c (31506B)
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 GNUNET_assert (0 <= 765 TALER_amount_add (&nxt, 766 &tmp, 767 divisor)); 768 if (1 == 769 TALER_amount_cmp (&nxt, 770 dividend)) 771 break; /* nxt > dividend */ 772 ret++; 773 tmp = nxt; 774 } 775 return ret; 776 } 777 778 779 enum TALER_AmountArithmeticResult 780 TALER_amount_multiply (struct TALER_Amount *result, 781 const struct TALER_Amount *amount, 782 uint32_t factor) 783 { 784 struct TALER_Amount in = *amount; 785 786 if (GNUNET_SYSERR == 787 TALER_amount_normalize (&in)) 788 { 789 invalidate (result); 790 return TALER_AAR_INVALID_NORMALIZATION_FAILED; 791 } 792 GNUNET_memcpy (result->currency, 793 amount->currency, 794 TALER_CURRENCY_LEN); 795 if ( (0 == factor) || 796 ( (0 == in.value) && 797 (0 == in.fraction) ) ) 798 { 799 result->value = 0; 800 result->fraction = 0; 801 return TALER_AAR_RESULT_ZERO; 802 } 803 result->value = in.value * ((uint64_t) factor); 804 if (in.value != result->value / factor) 805 { 806 invalidate (result); 807 return TALER_AAR_INVALID_RESULT_OVERFLOW; 808 } 809 { 810 /* This multiplication cannot overflow since both inputs are 32-bit values */ 811 uint64_t tmp = ((uint64_t) factor) * ((uint64_t) in.fraction); 812 uint64_t res; 813 814 res = tmp / TALER_AMOUNT_FRAC_BASE; 815 /* check for overflow */ 816 if (result->value + res < result->value) 817 { 818 invalidate (result); 819 return TALER_AAR_INVALID_RESULT_OVERFLOW; 820 } 821 result->value += res; 822 result->fraction = tmp % TALER_AMOUNT_FRAC_BASE; 823 } 824 if (result->value > TALER_AMOUNT_MAX_VALUE) 825 { 826 invalidate (result); 827 return TALER_AAR_INVALID_RESULT_OVERFLOW; 828 } 829 /* This check should be redundant... */ 830 GNUNET_assert (GNUNET_SYSERR != 831 TALER_amount_normalize (result)); 832 return TALER_AAR_RESULT_POSITIVE; 833 } 834 835 836 enum GNUNET_GenericReturnValue 837 TALER_amount_round_down (struct TALER_Amount *amount, 838 const struct TALER_Amount *round_unit) 839 { 840 if (GNUNET_OK != 841 TALER_amount_cmp_currency (amount, 842 round_unit)) 843 { 844 GNUNET_break (0); 845 return GNUNET_SYSERR; 846 } 847 if ( (0 != round_unit->fraction) && 848 (0 != round_unit->value) ) 849 { 850 GNUNET_break (0); 851 return GNUNET_SYSERR; 852 } 853 if ( (0 == round_unit->fraction) && 854 (0 == round_unit->value) ) 855 return GNUNET_NO; /* no rounding requested */ 856 if (0 != round_unit->fraction) 857 { 858 uint32_t delta; 859 860 delta = amount->fraction % round_unit->fraction; 861 if (0 == delta) 862 return GNUNET_NO; 863 amount->fraction -= delta; 864 } 865 if (0 != round_unit->value) 866 { 867 uint64_t delta; 868 869 delta = amount->value % round_unit->value; 870 if ( (0 == delta) && 871 (0 == amount->fraction) ) 872 return GNUNET_NO; 873 amount->value -= delta; 874 amount->fraction = 0; 875 } 876 return GNUNET_OK; 877 } 878 879 880 void 881 TALER_amount_set_free (struct TALER_AmountSet *as) 882 { 883 GNUNET_array_grow (as->taa, 884 as->taa_size, 885 0); 886 } 887 888 889 enum TALER_AmountArithmeticResult 890 TALER_amount_set_add (struct TALER_AmountSet *as, 891 const struct TALER_Amount *val, 892 const struct TALER_Amount *cap) 893 { 894 for (unsigned int i = 0; i<as->taa_size; i++) 895 { 896 struct TALER_Amount *ai = &as->taa[i]; 897 enum TALER_AmountArithmeticResult aar; 898 899 if (GNUNET_OK != 900 TALER_amount_cmp_currency (ai, 901 val)) 902 continue; 903 aar = TALER_amount_add (ai, 904 ai, 905 val); 906 /* If we have a cap, we tolerate the overflow */ 907 if ( (aar < 0) && 908 ( (NULL == cap) || 909 (TALER_AAR_INVALID_RESULT_OVERFLOW != aar) ) ) 910 return aar; /* hard error */ 911 if (TALER_AAR_INVALID_RESULT_OVERFLOW == aar) 912 { 913 if (GNUNET_OK != 914 TALER_amount_cmp_currency (val, 915 cap)) 916 return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE; 917 *ai = *cap; 918 return (TALER_amount_is_zero (cap)) 919 ? TALER_AAR_RESULT_ZERO 920 : TALER_AAR_RESULT_POSITIVE; 921 } 922 GNUNET_assert (aar >= 0); 923 if (NULL != cap) 924 GNUNET_assert (GNUNET_OK == 925 TALER_amount_min (ai, 926 ai, 927 cap)); 928 return (TALER_amount_is_zero (ai)) 929 ? TALER_AAR_RESULT_ZERO 930 : TALER_AAR_RESULT_POSITIVE; 931 } 932 GNUNET_array_append (as->taa, 933 as->taa_size, 934 *val); 935 { 936 struct TALER_Amount *ai = &as->taa[as->taa_size - 1]; 937 938 if (NULL != cap) 939 { 940 if (GNUNET_OK != 941 TALER_amount_cmp_currency (val, 942 cap)) 943 return TALER_AAR_INVALID_CURRENCIES_INCOMPATIBLE; 944 GNUNET_assert (GNUNET_OK == 945 TALER_amount_min (ai, 946 ai, 947 cap)); 948 } 949 return (TALER_amount_is_zero (ai)) 950 ? TALER_AAR_RESULT_ZERO 951 : TALER_AAR_RESULT_POSITIVE; 952 } 953 } 954 955 956 bool 957 TALER_amount_set_test_above (const struct TALER_AmountSet *as, 958 const struct TALER_Amount *b) 959 { 960 for (unsigned int i = 0; i<as->taa_size; i++) 961 { 962 const struct TALER_Amount *asi = &as->taa[i]; 963 964 if (GNUNET_OK != 965 TALER_amount_cmp_currency (b, 966 asi)) 967 continue; 968 if (1 != 969 TALER_amount_cmp (b, 970 asi)) 971 return true; 972 } 973 return false; 974 } 975 976 977 const struct TALER_Amount * 978 TALER_amount_set_find (const char *currency, 979 const struct TALER_AmountSet *as) 980 { 981 static TALER_THREAD_LOCAL struct TALER_Amount z; 982 983 for (unsigned int i = 0; i<as->taa_size; i++) 984 { 985 const struct TALER_Amount *asi = &as->taa[i]; 986 987 if (0 == strcasecmp (currency, 988 asi->currency)) 989 return asi; 990 } 991 if (GNUNET_OK != 992 TALER_amount_set_zero (currency, 993 &z)) 994 { 995 GNUNET_break (0); 996 return NULL; 997 } 998 return &z; 999 } 1000 1001 1002 /** 1003 * Upper bound on the length of the string representation of a 1004 * single amount: the currency, the ':', the value, the '.', the 1005 * fraction and the '\0'. 24 is sufficient for a uint64_t value 1006 * in decimal. 1007 */ 1008 #define AMOUNT_STR_MAX (TALER_AMOUNT_FRAC_LEN \ 1009 + TALER_CURRENCY_LEN + 3 + 24) 1010 1011 1012 void 1013 TALER_amount_list_free (struct TALER_AmountList *al) 1014 { 1015 GNUNET_array_grow (al->tal, 1016 al->tal_len, 1017 0); 1018 } 1019 1020 1021 enum GNUNET_GenericReturnValue 1022 TALER_string_to_amount_list (const char *str, 1023 struct TALER_AmountList *al) 1024 { 1025 struct TALER_AmountList tmp = { 1026 .tal = NULL, 1027 .tal_len = 0 1028 }; 1029 const char *pos = str; 1030 1031 /* skip leading whitespace, so that an all-whitespace option 1032 value means "free" and not "malformed" */ 1033 while (isspace ( (unsigned char) pos[0])) 1034 pos++; 1035 if ('\0' == pos[0]) 1036 { 1037 al->tal = NULL; 1038 al->tal_len = 0; 1039 return GNUNET_OK; 1040 } 1041 while (1) 1042 { 1043 const char *end = strchr (pos, 1044 (int) ';'); 1045 size_t len = (NULL == end) 1046 ? strlen (pos) 1047 : (size_t) (end - pos); 1048 struct TALER_Amount a; 1049 char *component; 1050 1051 if (0 == len) 1052 { 1053 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1054 "Empty component in amount list `%s'\n", 1055 str); 1056 TALER_amount_list_free (&tmp); 1057 return GNUNET_SYSERR; 1058 } 1059 component = GNUNET_strndup (pos, 1060 len); 1061 if (GNUNET_OK != 1062 TALER_string_to_amount (component, 1063 &a)) 1064 { 1065 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1066 "Invalid amount `%s' in amount list `%s'\n", 1067 component, 1068 str); 1069 GNUNET_free (component); 1070 TALER_amount_list_free (&tmp); 1071 return GNUNET_SYSERR; 1072 } 1073 GNUNET_free (component); 1074 /* A repeated currency is a typo, not an accumulation: which of 1075 the two prices would apply is anyone's guess. */ 1076 if (NULL != 1077 TALER_amount_list_find (&tmp, 1078 a.currency)) 1079 { 1080 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1081 "Currency `%s' given more than once in amount list `%s'\n", 1082 a.currency, 1083 str); 1084 TALER_amount_list_free (&tmp); 1085 return GNUNET_SYSERR; 1086 } 1087 GNUNET_array_append (tmp.tal, 1088 tmp.tal_len, 1089 a); 1090 if (NULL == end) 1091 break; 1092 pos = end + 1; 1093 } 1094 *al = tmp; 1095 return GNUNET_OK; 1096 } 1097 1098 1099 const char * 1100 TALER_amount_list2s (const struct TALER_AmountList *al) 1101 { 1102 static TALER_THREAD_LOCAL char *result; 1103 static TALER_THREAD_LOCAL size_t result_size; 1104 size_t need; 1105 size_t off = 0; 1106 1107 /* one separator per entry is one too many, which covers the '\0' */ 1108 need = (al->tal_len + 1) * (AMOUNT_STR_MAX + 1); 1109 if (need > result_size) 1110 { 1111 GNUNET_free (result); 1112 result = GNUNET_malloc (need); 1113 result_size = need; 1114 } 1115 result[0] = '\0'; 1116 for (unsigned int i = 0; i<al->tal_len; i++) 1117 { 1118 const char *as = TALER_amount2s (&al->tal[i]); 1119 1120 if (NULL == as) 1121 { 1122 GNUNET_break (0); 1123 return NULL; 1124 } 1125 off += GNUNET_snprintf (&result[off], 1126 result_size - off, 1127 "%s%s", 1128 (0 == i) ? "" : ";", 1129 as); 1130 } 1131 return result; 1132 } 1133 1134 1135 const struct TALER_Amount * 1136 TALER_amount_list_find (const struct TALER_AmountList *al, 1137 const char *currency) 1138 { 1139 for (unsigned int i = 0; i<al->tal_len; i++) 1140 { 1141 const struct TALER_Amount *ali = &al->tal[i]; 1142 1143 if (0 == strcasecmp (currency, 1144 ali->currency)) 1145 return ali; 1146 } 1147 return NULL; 1148 } 1149 1150 1151 enum GNUNET_GenericReturnValue 1152 TALER_amount_list_check_uniform (const struct TALER_AmountList *al) 1153 { 1154 bool have_zero = false; 1155 bool have_price = false; 1156 1157 for (unsigned int i = 0; i<al->tal_len; i++) 1158 { 1159 if (TALER_amount_is_zero (&al->tal[i])) 1160 have_zero = true; 1161 else 1162 have_price = true; 1163 } 1164 if (have_zero && have_price) 1165 return GNUNET_SYSERR; 1166 if (have_price) 1167 return GNUNET_OK; 1168 return GNUNET_NO; /* all zero, or empty */ 1169 } 1170 1171 1172 bool 1173 TALER_amount_list_covers (const struct TALER_AmountList *al, 1174 const char *const *currencies, 1175 unsigned int currencies_len) 1176 { 1177 /* @a al has no duplicates, so equal length plus each currency 1178 being present is enough to conclude the two agree exactly */ 1179 if (al->tal_len != currencies_len) 1180 return false; 1181 for (unsigned int i = 0; i<currencies_len; i++) 1182 if (NULL == 1183 TALER_amount_list_find (al, 1184 currencies[i])) 1185 return false; 1186 return true; 1187 } 1188 1189 1190 enum GNUNET_GenericReturnValue 1191 TALER_amount_list_multiply (struct TALER_AmountList *al, 1192 uint32_t n) 1193 { 1194 struct TALER_Amount *tmp; 1195 1196 if (0 == n) 1197 { 1198 GNUNET_break (0); 1199 return GNUNET_SYSERR; 1200 } 1201 if ( (1 == n) || 1202 (0 == al->tal_len) ) 1203 return GNUNET_OK; 1204 /* compute into a scratch array first, so that an overflow in a 1205 late currency does not leave the early ones multiplied */ 1206 tmp = GNUNET_new_array (al->tal_len, 1207 struct TALER_Amount); 1208 for (unsigned int i = 0; i<al->tal_len; i++) 1209 { 1210 if (0 > 1211 TALER_amount_multiply (&tmp[i], 1212 &al->tal[i], 1213 n)) 1214 { 1215 GNUNET_free (tmp); 1216 return GNUNET_SYSERR; 1217 } 1218 } 1219 GNUNET_memcpy (al->tal, 1220 tmp, 1221 al->tal_len * sizeof (struct TALER_Amount)); 1222 GNUNET_free (tmp); 1223 return GNUNET_OK; 1224 } 1225 1226 1227 void 1228 TALER_amount_list_copy (struct TALER_AmountList *dst, 1229 const struct TALER_AmountList *src) 1230 { 1231 dst->tal_len = src->tal_len; 1232 if (0 == src->tal_len) 1233 { 1234 dst->tal = NULL; 1235 return; 1236 } 1237 dst->tal = GNUNET_new_array (src->tal_len, 1238 struct TALER_Amount); 1239 GNUNET_memcpy (dst->tal, 1240 src->tal, 1241 src->tal_len * sizeof (struct TALER_Amount)); 1242 } 1243 1244 1245 /* end of amount.c */