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