LCOV - code coverage report
Current view: top level - util - amount_quantity.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 67.3 % 110 74
Test Date: 2026-01-01 16:44:56 Functions: 100.0 % 5 5

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2025 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU Lesser 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 amount_quantity.c
      18              :  * @brief Parsing quantities and other decimal fractions
      19              :  * @author Christian Grothoff
      20              :  */
      21              : #include "platform.h"
      22              : #include <gnunet/gnunet_util_lib.h>
      23              : #include <gnunet/gnunet_db_lib.h>
      24              : #include <taler/taler_json_lib.h>
      25              : #include "taler_merchant_util.h"
      26              : 
      27              : 
      28              : /**
      29              :  * Multiply two 64-bit values and store result as high/low 64-bit parts.
      30              :  */
      31              : static void
      32          105 : mul64_overflow (uint64_t a,
      33              :                 uint64_t b,
      34              :                 uint64_t *hi,
      35              :                 uint64_t *lo)
      36              : {
      37          105 :   uint64_t a_lo = a & 0xFFFFFFFF;
      38          105 :   uint64_t a_hi = a >> 32;
      39          105 :   uint64_t b_lo = b & 0xFFFFFFFF;
      40          105 :   uint64_t b_hi = b >> 32;
      41              : 
      42          105 :   uint64_t p0 = a_lo * b_lo;
      43          105 :   uint64_t p1 = a_lo * b_hi;
      44          105 :   uint64_t p2 = a_hi * b_lo;
      45          105 :   uint64_t p3 = a_hi * b_hi;
      46              : 
      47          105 :   uint64_t carry = ((p0 >> 32) + (p1 & 0xFFFFFFFF) + (p2 & 0xFFFFFFFF)) >> 32;
      48              : 
      49          105 :   *lo = p0 + (p1 << 32) + (p2 << 32);
      50          105 :   *hi = p3 + (p1 >> 32) + (p2 >> 32) + carry;
      51          105 : }
      52              : 
      53              : 
      54              : /**
      55              :  * Add two 128-bit numbers represented as hi/lo pairs.
      56              :  * Returns 1 on overflow, 0 otherwise.
      57              :  */
      58              : static int
      59           75 : add128 (uint64_t a_hi,
      60              :         uint64_t a_lo,
      61              :         uint64_t b_hi,
      62              :         uint64_t b_lo,
      63              :         uint64_t *r_hi,
      64              :         uint64_t *r_lo)
      65              : {
      66              :   uint64_t carry;
      67              : 
      68           75 :   *r_lo = a_lo + b_lo;
      69           75 :   carry = (*r_lo < a_lo) ? 1 : 0;
      70           75 :   *r_hi = a_hi + b_hi + carry;
      71              : 
      72           75 :   return (*r_hi < a_hi) || ((*r_hi == a_hi) && carry && (b_hi == UINT64_MAX));
      73              : }
      74              : 
      75              : 
      76              : /**
      77              :  * Subtract two 128-bit numbers represented as hi/lo pairs.
      78              :  * Returns 1 on underflow, 0 otherwise.
      79              :  */
      80              : static int
      81           15 : sub128 (uint64_t a_hi,
      82              :         uint64_t a_lo,
      83              :         uint64_t b_hi,
      84              :         uint64_t b_lo,
      85              :         uint64_t *r_hi,
      86              :         uint64_t *r_lo)
      87              : {
      88              :   uint64_t carry;
      89              : 
      90           15 :   carry = (a_lo < b_lo) ? 1 : 0;
      91           15 :   *r_lo = a_lo - b_lo;
      92           15 :   *r_hi = a_hi - b_hi - carry;
      93              : 
      94           15 :   return (a_hi < b_hi) || ((a_hi == b_hi) && carry);
      95              : }
      96              : 
      97              : 
      98              : /**
      99              :  * Divide a 128-bit number by a 64-bit number.
     100              :  * Returns quotient in q_hi/q_lo and remainder in r.
     101              :  */
     102              : static void
     103           45 : div128_64 (uint64_t n_hi,
     104              :            uint64_t n_lo,
     105              :            uint64_t d,
     106              :            uint64_t *q_hi,
     107              :            uint64_t *q_lo,
     108              :            uint64_t *r)
     109              : {
     110              :   uint64_t remainder;
     111              : 
     112           45 :   if (0 == n_hi)
     113              :   {
     114           45 :     *q_hi = 0;
     115           45 :     *q_lo = n_lo / d;
     116           45 :     *r = n_lo % d;
     117           45 :     return;
     118              :   }
     119              : 
     120              :   /* Note: very slow method, could be done faster, but
     121              :      in practice we expect the above short-cut to apply
     122              :      in virtually all cases, so we keep it simple here;
     123              :      also, if it mattered, we should use __uint128_t on
     124              :      systems that support it. */
     125            0 :   remainder = 0;
     126            0 :   *q_hi = 0;
     127            0 :   *q_lo = 0;
     128            0 :   for (int i = 127; i >= 0; i--)
     129              :   {
     130            0 :     remainder <<= 1;
     131            0 :     if (i >= 64)
     132            0 :       remainder |= (n_hi >> (i - 64)) & 1;
     133              :     else
     134            0 :       remainder |= (n_lo >> i) & 1;
     135              : 
     136            0 :     if (remainder >= d)
     137              :     {
     138            0 :       remainder -= d;
     139            0 :       if (i >= 64)
     140            0 :         *q_hi |= (1ULL << (i - 64));
     141              :       else
     142            0 :         *q_lo |= (1ULL << i);
     143              :     }
     144              :   }
     145            0 :   *r = remainder;
     146              : }
     147              : 
     148              : 
     149              : enum GNUNET_GenericReturnValue
     150           15 : TALER_MERCHANT_amount_multiply_by_quantity (
     151              :   struct TALER_Amount *result,
     152              :   const struct TALER_Amount *unit_price,
     153              :   const struct TALER_MERCHANT_ProductQuantity *factor,
     154              :   enum TALER_MERCHANT_RoundMode rm,
     155              :   const struct TALER_Amount *atomic_amount)
     156              : {
     157              :   uint64_t price_hi;
     158              :   uint64_t price_lo;
     159              :   uint64_t factor_hi;
     160              :   uint64_t factor_lo;
     161              :   uint64_t prod_hi;
     162              :   uint64_t prod_lo;
     163              :   uint64_t raw_hi;
     164              :   uint64_t raw_lo;
     165              :   uint64_t rem;
     166              :   uint64_t atomic_hi;
     167              :   uint64_t atomic_lo;
     168              :   uint64_t rounded_hi;
     169              :   uint64_t rounded_lo;
     170              :   uint64_t remainder;
     171              : 
     172           15 :   if (GNUNET_OK !=
     173           15 :       TALER_amount_cmp_currency (unit_price,
     174              :                                  atomic_amount))
     175              :   {
     176            0 :     GNUNET_break (0);
     177            0 :     return GNUNET_SYSERR;
     178              :   }
     179           15 :   GNUNET_assert (factor->fractional < TALER_MERCHANT_UNIT_FRAC_BASE);
     180           15 :   GNUNET_assert (unit_price->fraction < TALER_AMOUNT_FRAC_BASE);
     181           15 :   GNUNET_assert (atomic_amount->fraction < TALER_AMOUNT_FRAC_BASE);
     182           15 :   GNUNET_assert (GNUNET_OK ==
     183              :                  TALER_amount_set_zero (unit_price->currency,
     184              :                                         result));
     185              : 
     186           15 :   if (TALER_amount_is_zero (atomic_amount))
     187              :   {
     188            0 :     GNUNET_break (0);
     189            0 :     return GNUNET_SYSERR;
     190              :   }
     191              : 
     192              :   /* Convert unit_price to fractional units */
     193           15 :   mul64_overflow (unit_price->value,
     194              :                   TALER_AMOUNT_FRAC_BASE,
     195              :                   &price_hi,
     196              :                   &price_lo);
     197           15 :   if (add128 (price_hi,
     198              :               price_lo,
     199              :               0,
     200           15 :               unit_price->fraction,
     201              :               &price_hi,
     202              :               &price_lo))
     203            0 :     return GNUNET_NO;
     204              : 
     205              :   /* Convert factor to fractional units */
     206           15 :   mul64_overflow (factor->integer,
     207              :                   TALER_MERCHANT_UNIT_FRAC_BASE,
     208              :                   &factor_hi,
     209              :                   &factor_lo);
     210           15 :   if (add128 (factor_hi,
     211              :               factor_lo,
     212              :               0,
     213           15 :               factor->fractional,
     214              :               &factor_hi,
     215              :               &factor_lo))
     216            0 :     return GNUNET_NO;
     217              : 
     218              :   /* Multiply price by factor: (price_hi:price_lo) * (factor_hi:factor_lo) */
     219              :   {
     220              :     uint64_t p0_hi, p0_lo, p1_hi, p1_lo, p2_hi, p2_lo, p3_hi, p3_lo;
     221              : 
     222           15 :     mul64_overflow (price_lo,
     223              :                     factor_lo,
     224              :                     &p0_hi,
     225              :                     &p0_lo);
     226           15 :     mul64_overflow (price_lo,
     227              :                     factor_hi,
     228              :                     &p1_hi,
     229              :                     &p1_lo);
     230           15 :     mul64_overflow (price_hi,
     231              :                     factor_lo,
     232              :                     &p2_hi,
     233              :                     &p2_lo);
     234           15 :     mul64_overflow (price_hi,
     235              :                     factor_hi,
     236              :                     &p3_hi,
     237              :                     &p3_lo);
     238              :     /* Check for overflow in 128-bit result */
     239           15 :     if ( (0 != p3_hi) ||
     240           15 :          (0 != p3_lo) ||
     241           15 :          (0 != p2_hi) ||
     242           15 :          (0 != p1_hi) )
     243            0 :       return GNUNET_NO;
     244              : 
     245              :     /* Add all fractions together */
     246           15 :     prod_hi = p0_hi;
     247           15 :     prod_lo = p0_lo;
     248           15 :     if (add128 (prod_hi,
     249              :                 prod_lo,
     250              :                 p1_lo,
     251              :                 0,
     252              :                 &prod_hi,
     253              :                 &prod_lo))
     254            0 :       return GNUNET_NO;
     255           15 :     if (add128 (prod_hi,
     256              :                 prod_lo,
     257              :                 p2_lo,
     258              :                 0,
     259              :                 &prod_hi,
     260              :                 &prod_lo))
     261            0 :       return GNUNET_NO;
     262              :   }
     263              : 
     264              :   /* Divide by MERCHANT_UNIT_FRAC_BASE */
     265           15 :   div128_64 (prod_hi,
     266              :              prod_lo,
     267              :              TALER_MERCHANT_UNIT_FRAC_BASE,
     268              :              &raw_hi,
     269              :              &raw_lo,
     270              :              &rem);
     271              : 
     272              :   /* Convert atomic_amount to fractional units */
     273           15 :   mul64_overflow (atomic_amount->value,
     274              :                   TALER_AMOUNT_FRAC_BASE,
     275              :                   &atomic_hi,
     276              :                   &atomic_lo);
     277           15 :   if (add128 (atomic_hi,
     278              :               atomic_lo,
     279              :               0,
     280           15 :               atomic_amount->fraction,
     281              :               &atomic_hi,
     282              :               &atomic_lo))
     283              :   {
     284            0 :     GNUNET_break (0);
     285            0 :     return GNUNET_SYSERR;
     286              :   }
     287           15 :   if (atomic_hi > 0)
     288              :   {
     289              :     /* outside of supported range */
     290            0 :     GNUNET_break (0);
     291            0 :     return GNUNET_SYSERR;
     292              :   }
     293              : 
     294              :   /* Compute remainder when dividing by atomic_amount and round down */
     295              :   {
     296              :     uint64_t q_hi, q_lo;
     297           15 :     uint64_t half_atomic = atomic_lo >> 1;
     298           15 :     bool round_up = false;
     299              : 
     300           15 :     div128_64 (raw_hi,
     301              :                raw_lo,
     302              :                atomic_lo,
     303              :                &q_hi,
     304              :                &q_lo,
     305              :                &remainder);
     306           15 :     sub128 (raw_hi,
     307              :             raw_lo,
     308              :             0,
     309              :             remainder,
     310              :             &rounded_hi,
     311              :             &rounded_lo);
     312           15 :     switch (rm)
     313              :     {
     314            0 :     case TALER_MERCHANT_ROUND_NEAREST:
     315            0 :       round_up = (remainder > half_atomic) ||
     316            0 :                  (remainder == half_atomic && (q_lo & 1));
     317            0 :       break;
     318           15 :     case TALER_MERCHANT_ROUND_UP:
     319           15 :       round_up = (remainder > 0);
     320           15 :       break;
     321            0 :     case TALER_MERCHANT_ROUND_DOWN:
     322            0 :       break;
     323              :     }
     324           15 :     if ( (round_up) &&
     325            0 :          (add128 (rounded_hi,
     326              :                   rounded_lo,
     327              :                   atomic_hi,
     328              :                   atomic_lo,
     329              :                   &rounded_hi,
     330              :                   &rounded_lo)) )
     331            0 :       return GNUNET_NO;
     332              :   }
     333              : 
     334              :   /* Convert back to value and fraction */
     335              :   {
     336              :     uint64_t final_value;
     337              :     uint64_t final_fraction;
     338              :     uint64_t q_hi;
     339              : 
     340           15 :     div128_64 (rounded_hi,
     341              :                rounded_lo,
     342              :                TALER_AMOUNT_FRAC_BASE,
     343              :                &q_hi,
     344              :                &final_value,
     345              :                &final_fraction);
     346              : 
     347              :     /* Check for overflow */
     348           15 :     if (0 != q_hi)
     349            0 :       return GNUNET_NO;
     350              : 
     351           15 :     result->value = final_value;
     352           15 :     result->fraction = (uint32_t) final_fraction;
     353              :   }
     354           15 :   return GNUNET_OK;
     355              : }
        

Generated by: LCOV version 2.0-1