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