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