LCOV - code coverage report
Current view: top level - util - value_kinds.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 67.9 % 131 89
Test Date: 2026-01-01 16:44:56 Functions: 100.0 % 3 3

            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 value_kinds.c
      18              :  * @brief Parsing quantities and other decimal fractions
      19              :  * @author Christian Grothoff
      20              :  * @author Bohdan
      21              :  */
      22              : #include "platform.h"
      23              : #include <gnunet/gnunet_util_lib.h>
      24              : #include <gnunet/gnunet_db_lib.h>
      25              : #include <taler/taler_json_lib.h>
      26              : #include "taler_merchant_util.h"
      27              : 
      28              : 
      29              : enum GNUNET_GenericReturnValue
      30           63 : TALER_MERCHANT_vk_parse_fractional_string (
      31              :   const char *value,
      32              :   int64_t *integer_part,
      33              :   uint32_t *fractional_part)
      34              : {
      35              :   const char *ptr;
      36           63 :   uint64_t integer = 0;
      37           63 :   uint32_t frac = 0;
      38           63 :   unsigned int digits = 0;
      39              : 
      40           63 :   GNUNET_assert (NULL != integer_part);
      41           63 :   GNUNET_assert (NULL != fractional_part);
      42              : 
      43           63 :   if (NULL == value)
      44              :   {
      45            0 :     GNUNET_break_op (0);
      46            0 :     return GNUNET_SYSERR;
      47              :   }
      48           63 :   ptr = value;
      49           63 :   if ('\0' == *ptr)
      50              :   {
      51            0 :     GNUNET_break_op (0);
      52            0 :     return GNUNET_SYSERR;
      53              :   }
      54           63 :   if ('-' == *ptr)
      55              :   {
      56            0 :     GNUNET_break_op (0);
      57            0 :     return GNUNET_SYSERR;
      58              :   }
      59           63 :   if (! isdigit ((unsigned char) *ptr))
      60              :   {
      61            0 :     GNUNET_break_op (0);
      62            0 :     return GNUNET_SYSERR;
      63              :   }
      64          135 :   while (isdigit ((unsigned char) *ptr))
      65              :   {
      66           72 :     unsigned int digit = (unsigned int) (*ptr - '0');
      67              : 
      68           72 :     if (integer > (UINT64_MAX - digit) / 10)
      69              :     {
      70            0 :       GNUNET_break_op (0);
      71            0 :       return GNUNET_SYSERR;
      72              :     }
      73           72 :     integer = integer * 10 + digit;
      74           72 :     ptr++;
      75              :   }
      76           63 :   if ('.' == *ptr)
      77              :   {
      78           12 :     ptr++;
      79           12 :     if ('\0' == *ptr)
      80              :     {
      81            0 :       GNUNET_break_op (0);
      82            0 :       return GNUNET_SYSERR;
      83              :     }
      84           28 :     while (isdigit ((unsigned char) *ptr))
      85              :     {
      86           16 :       unsigned int digit = (unsigned int) (*ptr - '0');
      87              : 
      88           16 :       if (digits >= TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS)
      89              :       {
      90            0 :         GNUNET_break_op (0);
      91            0 :         return GNUNET_SYSERR;
      92              :       }
      93           16 :       frac = (uint32_t) (frac * 10 + digit);
      94           16 :       digits++;
      95           16 :       ptr++;
      96              :     }
      97           68 :     while (digits < TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS)
      98              :     {
      99           56 :       frac *= 10;
     100           56 :       digits++;
     101              :     }
     102              :   }
     103           63 :   if ('\0' != *ptr)
     104              :   {
     105            0 :     GNUNET_break_op (0);
     106            0 :     return GNUNET_SYSERR;
     107              :   }
     108           63 :   if (integer > (uint64_t) INT64_MAX)
     109              :   {
     110            0 :     GNUNET_break_op (0);
     111            0 :     return GNUNET_SYSERR;
     112              :   }
     113           63 :   *integer_part = integer;
     114           63 :   *fractional_part = frac;
     115           63 :   return GNUNET_OK;
     116              : }
     117              : 
     118              : 
     119              : enum GNUNET_GenericReturnValue
     120           65 : TALER_MERCHANT_vk_process_quantity_inputs (enum TALER_MERCHANT_ValueKind kind,
     121              :                                            bool allow_fractional,
     122              :                                            bool int_missing,
     123              :                                            int64_t int_raw,
     124              :                                            bool str_missing,
     125              :                                            const char *str_raw,
     126              :                                            uint64_t *int_out,
     127              :                                            uint32_t *frac_out,
     128              :                                            const char **error_param)
     129              : {
     130              :   static char errbuf[128];
     131           65 :   int64_t parsed_int = 0;
     132           65 :   uint32_t parsed_frac = 0;
     133           65 :   const char *int_field = (TALER_MERCHANT_VK_STOCK == kind)
     134              :                           ? "total_stock"
     135           65 :                           : "quantity";
     136           65 :   const char *str_field = (TALER_MERCHANT_VK_STOCK == kind)
     137              :                           ? "unit_total_stock"
     138           65 :                           : "unit_quantity";
     139              : 
     140           65 :   *error_param = NULL;
     141              : 
     142           65 :   if (int_missing && str_missing)
     143              :   {
     144            0 :     GNUNET_snprintf (errbuf,
     145              :                      sizeof (errbuf),
     146              :                      "missing %s and %s",
     147              :                      int_field,
     148              :                      str_field);
     149            0 :     *error_param = errbuf;
     150            0 :     GNUNET_break_op (0);
     151            0 :     return GNUNET_SYSERR;
     152              :   }
     153              : 
     154           65 :   if (! str_missing)
     155              :   {
     156           65 :     if ( (TALER_MERCHANT_VK_STOCK == kind) &&
     157           36 :          (0 == strcmp ("-1",
     158              :                        str_raw)) )
     159              :     {
     160            2 :       parsed_int = -1;
     161            2 :       parsed_frac = 0;
     162              :     }
     163              :     else
     164              :     {
     165           63 :       if (GNUNET_OK !=
     166           63 :           TALER_MERCHANT_vk_parse_fractional_string (str_raw,
     167              :                                                      &parsed_int,
     168              :                                                      &parsed_frac))
     169              :       {
     170            0 :         GNUNET_snprintf (errbuf,
     171              :                          sizeof (errbuf),
     172              :                          "malformed %s",
     173              :                          str_field);
     174            0 :         *error_param = errbuf;
     175            0 :         GNUNET_break_op (0);
     176            0 :         return GNUNET_SYSERR;
     177              :       }
     178              :     }
     179              :   }
     180              : 
     181           65 :   if ( (! int_missing) && (! str_missing) )
     182              :   {
     183            1 :     if ( (parsed_int != int_raw) || (0 != parsed_frac) )
     184              :     {
     185            0 :       GNUNET_snprintf (errbuf,
     186              :                        sizeof (errbuf),
     187              :                        "%s/%s mismatch",
     188              :                        int_field,
     189              :                        str_field);
     190            0 :       *error_param = errbuf;
     191            0 :       GNUNET_break_op (0);
     192            0 :       return GNUNET_SYSERR;
     193              :     }
     194              :   }
     195           64 :   else if (int_missing)
     196              :   {
     197           64 :     int_raw = parsed_int;
     198              :   }
     199              : 
     200           65 :   if ( (TALER_MERCHANT_VK_STOCK == kind) && (-1 == int_raw) )
     201              :   {
     202            2 :     if ( (! str_missing) && (0 != parsed_frac) )
     203              :     {
     204            0 :       GNUNET_snprintf (errbuf,
     205              :                        sizeof (errbuf),
     206              :                        "fractional part forbidden with %s='-1'",
     207              :                        str_field);
     208            0 :       *error_param = errbuf;
     209            0 :       GNUNET_break_op (0);
     210            0 :       return GNUNET_SYSERR;
     211              :     }
     212            2 :     *int_out = INT64_MAX;
     213            2 :     *frac_out = INT32_MAX;
     214            2 :     return GNUNET_OK;
     215              :   }
     216              : 
     217           63 :   if (int_raw < 0)
     218              :   {
     219            0 :     GNUNET_snprintf (errbuf,
     220              :                      sizeof (errbuf),
     221              :                      "%s must be non-negative",
     222              :                      int_field);
     223            0 :     *error_param = errbuf;
     224            0 :     GNUNET_break_op (0);
     225            0 :     return GNUNET_SYSERR;
     226              :   }
     227              : 
     228           63 :   if (! allow_fractional)
     229              :   {
     230           51 :     if ( (! str_missing) && (0 != parsed_frac) )
     231              :     {
     232            4 :       GNUNET_snprintf (errbuf,
     233              :                        sizeof (errbuf),
     234              :                        "fractional part not allowed for %s",
     235              :                        str_field);
     236            4 :       *error_param = errbuf;
     237            4 :       GNUNET_break_op (0);
     238            4 :       return GNUNET_SYSERR;
     239              :     }
     240           47 :     parsed_frac = 0;
     241              :   }
     242           12 :   else if (! str_missing)
     243              :   {
     244           12 :     if (parsed_frac >= TALER_MERCHANT_UNIT_FRAC_BASE)
     245              :     {
     246            0 :       GNUNET_snprintf (errbuf,
     247              :                        sizeof (errbuf),
     248              :                        "%s fractional part exceeds base %u",
     249              :                        str_field,
     250              :                        TALER_MERCHANT_UNIT_FRAC_BASE);
     251            0 :       *error_param = errbuf;
     252            0 :       GNUNET_break_op (0);
     253            0 :       return GNUNET_SYSERR;
     254              :     }
     255              :   }
     256              : 
     257           59 :   *int_out = (uint64_t) int_raw;
     258           59 :   *frac_out = parsed_frac;
     259           59 :   return GNUNET_OK;
     260              : }
     261              : 
     262              : 
     263              : void
     264           54 : TALER_MERCHANT_vk_format_fractional_string (
     265              :   enum TALER_MERCHANT_ValueKind kind,
     266              :   uint64_t integer,
     267              :   uint32_t fractional,
     268              :   size_t buffer_length,
     269              :   char buffer[static buffer_length])
     270           54 : {
     271           54 :   GNUNET_assert (0 < buffer_length);
     272              : 
     273           54 :   if ( (TALER_MERCHANT_VK_STOCK == kind) &&
     274            1 :        (INT64_MAX == (int64_t) integer) &&
     275              :        (INT32_MAX == (int32_t) fractional) )
     276              :   {
     277            1 :     GNUNET_snprintf (buffer,
     278              :                      buffer_length,
     279              :                      "-1");
     280            1 :     return;
     281              :   }
     282              : 
     283           53 :   GNUNET_assert ( (TALER_MERCHANT_VK_QUANTITY != kind) ||
     284              :                   ((INT64_MAX != (int64_t) integer) &&
     285              :                    (INT32_MAX != (int32_t) fractional)) );
     286           53 :   GNUNET_assert (fractional < TALER_MERCHANT_UNIT_FRAC_BASE);
     287              : 
     288           53 :   if (0 == fractional)
     289              :   {
     290           43 :     GNUNET_snprintf (buffer,
     291              :                      buffer_length,
     292              :                      "%lu",
     293              :                      integer);
     294           43 :     return;
     295              :   }
     296              :   {
     297              :     char frac_buf[TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS + 1];
     298              :     size_t idx;
     299              : 
     300           10 :     GNUNET_snprintf (frac_buf,
     301              :                      sizeof (frac_buf),
     302              :                      "%0*u",
     303              :                      TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS,
     304              :                      (unsigned int) fractional);
     305           56 :     for (idx = strlen (frac_buf); idx > 0; idx--)
     306              :     {
     307           56 :       if ('0' != frac_buf[idx - 1])
     308           10 :         break;
     309           46 :       frac_buf[idx - 1] = '\0';
     310              :     }
     311           10 :     GNUNET_snprintf (buffer,
     312              :                      buffer_length,
     313              :                      "%lu.%s",
     314              :                      integer,
     315              :                      frac_buf);
     316              :   }
     317              : }
        

Generated by: LCOV version 2.0-1