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