LCOV - code coverage report
Current view: top level - src - coerce.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 285 285 100.0 %
Date: 2026-08-20 23:47:58 Functions: 17 17 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 260 263 98.9 %

           Branch data     Line data    Source code
       1                 :            : // Copyright 2019-2026 David Robillard <d@drobilla.net>
       2                 :            : // SPDX-License-Identifier: ISC
       3                 :            : 
       4                 :            : #include "big_decimal.h"
       5                 :            : #include "decimal_to_double.h"
       6                 :            : #include "double_to_decimal.h"
       7                 :            : #include "macros.h"
       8                 :            : #include "number_utils.h"
       9                 :            : #include "result.h"
      10                 :            : 
      11                 :            : #include <exess/exess.h>
      12                 :            : 
      13                 :            : #include <assert.h>
      14                 :            : #include <math.h>
      15                 :            : #include <stdbool.h>
      16                 :            : #include <stdint.h>
      17                 :            : #include <string.h>
      18                 :            : 
      19                 :            : /* Limits for the range of integers that can be exactly represented in floating
      20                 :            :    point types.  Note that these limits are one less than the largest value,
      21                 :            :    since values larger than that may round to it which causes problems with
      22                 :            :    perfect round-tripping.  For example, 16777217 when parsed as a float will
      23                 :            :    result in 1.6777216E7, which a "lossless" coercion would then convert to
      24                 :            :    16777216. */
      25                 :            : 
      26                 :            : #define MAX_FLOAT_INT 16777215
      27                 :            : #define MAX_DOUBLE_INT 9007199254740991L
      28                 :            : 
      29                 :            : #ifndef FLT_DECIMAL_DIG
      30                 :            : #  define FLT_DECIMAL_DIG 9U
      31                 :            : #endif
      32                 :            : #ifndef DBL_DECIMAL_DIG
      33                 :            : #  define DBL_DECIMAL_DIG 17U
      34                 :            : #endif
      35                 :            : 
      36                 :            : #define ENSURE(condition, return_status_otherwise)  \
      37                 :            :   do {                                              \
      38                 :            :     if (!(condition)) {                             \
      39                 :            :       return RESULT((return_status_otherwise), 0U); \
      40                 :            :     }                                               \
      41                 :            :   } while (0)
      42                 :            : 
      43                 :            : #define ENSURE_COERCION(coercions, coercion) \
      44                 :            :   ENSURE(((coercions) & (ExessCoercions)(coercion)), EXESS_LOSS)
      45                 :            : 
      46                 :            : #ifdef __cplusplus
      47                 :            : #  define UCONVERSION(s, v) (UConversion{s, v})
      48                 :            : #  define SCONVERSION(s, v) (SConversion{s, v})
      49                 :            : #else
      50                 :            : #  define UCONVERSION(s, v) ((UConversion){s, v})
      51                 :            : #  define SCONVERSION(s, v) ((SConversion){s, v})
      52                 :            : #endif
      53                 :            : 
      54                 :            : typedef struct {
      55                 :            :   ExessStatus status;
      56                 :            :   int64_t     value;
      57                 :            : } SConversion;
      58                 :            : 
      59                 :            : typedef struct {
      60                 :            :   ExessStatus status;
      61                 :            :   uint64_t    value;
      62                 :            : } UConversion;
      63                 :            : 
      64                 :            : static bool
      65                 :        209 : round_decimal_up(const BigDecimal* const d, const char* const digits)
      66                 :            : {
      67         [ +  + ]:        209 :   if (digits[d->expt] > '5') {
      68                 :          2 :     return true;
      69                 :            :   }
      70                 :            : 
      71         [ +  + ]:        207 :   if (digits[d->expt] < '5') {
      72                 :         39 :     return false;
      73                 :            :   }
      74                 :            : 
      75         [ -  + ]:        168 :   assert(d->n_digits > 0);
      76         [ +  + ]:        168 :   const char ones = (char)((d->expt > 0) ? digits[d->expt - 1] : '0');
      77                 :        168 :   return ((ones - '0') % 2);
      78                 :            : }
      79                 :            : 
      80                 :            : static UConversion
      81                 :        858 : convert_significand(const ExessCoercions coercions, const void* const in)
      82                 :            : {
      83                 :        858 :   BigDecimal        d      = *(const BigDecimal*)in;
      84                 :        858 :   const char* const digits = (const char*)in + sizeof(d);
      85                 :            : 
      86                 :        858 :   bool round_up = false;
      87         [ +  + ]:        858 :   if (d.expt < (int)d.n_digits) {
      88         [ +  + ]:        763 :     if (coercions & (ExessCoercions)EXESS_TRUNCATE) {
      89         [ +  + ]:        212 :       d.n_digits = (uint8_t)MAX(0, d.expt);
      90         [ +  + ]:        551 :     } else if (coercions & (ExessCoercions)EXESS_ROUND) {
      91         [ +  + ]:        224 :       if (d.expt <= 0) {
      92                 :         48 :         return UCONVERSION(EXESS_SUCCESS, 0U);
      93                 :            :       }
      94                 :            : 
      95                 :        176 :       round_up   = round_decimal_up(&d, digits);
      96                 :        176 :       d.n_digits = (uint8_t)d.expt;
      97                 :            :     } else {
      98                 :        327 :       return UCONVERSION(EXESS_LOSS, 0U);
      99                 :            :     }
     100                 :            :   }
     101                 :            : 
     102                 :        483 :   uint64_t magnitude = 0U;
     103                 :        483 :   size_t   i         = 0U;
     104         [ +  + ]:       2210 :   for (; i < d.n_digits; ++i) {
     105                 :       1730 :     const uint64_t next = (magnitude * 10U) + (uint64_t)(digits[i] - '0');
     106         [ +  + ]:       1730 :     if (next < magnitude) {
     107                 :          3 :       return UCONVERSION(EXESS_OUT_OF_RANGE, i);
     108                 :            :     }
     109                 :            : 
     110                 :       1727 :     magnitude = next;
     111                 :            :   }
     112                 :            : 
     113                 :        480 :   return UCONVERSION(EXESS_SUCCESS, magnitude + round_up);
     114                 :            : }
     115                 :            : 
     116                 :            : static SConversion
     117                 :        590 : convert_signed_decimal(const ExessCoercions coercions, const void* const in)
     118                 :            : {
     119                 :        590 :   BigDecimal        d         = *(const BigDecimal*)in;
     120                 :        590 :   const UConversion c         = convert_significand(coercions, in);
     121                 :        590 :   const uint64_t    magnitude = c.value;
     122                 :            : 
     123                 :        815 :   return c.status ? SCONVERSION(c.status, 0U)
     124         [ +  + ]:        602 :          : (d.expt == 19 &&
     125   [ +  +  +  + ]:         57 :             (d.kind == EXESS_NEGATIVE && magnitude == 9223372036854775808ULL))
     126                 :         20 :            ? SCONVERSION(c.status, INT64_MIN)
     127         [ +  + ]:        365 :          : (d.expt > 19 ||
     128   [ +  +  +  + ]:        342 :             (d.expt == 19 && (magnitude >= 9223372036854775808ULL)))
     129                 :            :            ? SCONVERSION(EXESS_OUT_OF_RANGE, 0)
     130         [ +  + ]:        345 :          : (d.kind == EXESS_NEGATIVE)
     131                 :        152 :            ? SCONVERSION(c.status, -(int64_t)magnitude)
     132         [ +  + ]:        333 :            : SCONVERSION(c.status, (int64_t)magnitude);
     133                 :            : }
     134                 :            : 
     135                 :            : static SConversion
     136                 :       6680 : convert_signed(const ExessCoercions coercions,
     137                 :            :                const ExessDatatype  in_datatype,
     138                 :            :                const void* const    in)
     139                 :            : {
     140                 :            : #define FP_CONVERSION(Type, f, max, trunc_func, round_func)           \
     141                 :            :   ((f) < (Type)(-(max)) || (f) > (Type)(max))                         \
     142                 :            :     ? SCONVERSION(EXESS_OUT_OF_RANGE, 0)                              \
     143                 :            :   : ((f) == trunc_func(f)) ? SCONVERSION(EXESS_SUCCESS, (int64_t)(f)) \
     144                 :            :   : (coercions & (ExessCoercions)EXESS_ROUND)                         \
     145                 :            :     ? SCONVERSION(EXESS_SUCCESS, round_func(f))                       \
     146                 :            :   : (coercions & (ExessCoercions)EXESS_TRUNCATE)                      \
     147                 :            :     ? SCONVERSION(EXESS_SUCCESS, (int64_t)(f))                        \
     148                 :            :     : SCONVERSION(EXESS_LOSS, 0)
     149                 :            : 
     150   [ +  +  +  +  :       6680 :   switch (in_datatype) {
          +  +  +  +  +  
          +  +  +  +  +  
                      - ]
     151                 :          6 :   case EXESS_NOTHING:
     152                 :         99 :     break;
     153                 :            : 
     154                 :         20 :   case EXESS_BOOLEAN:
     155                 :       6581 :     return SCONVERSION(EXESS_SUCCESS, *(const bool*)in);
     156                 :            : 
     157                 :        817 :   case EXESS_DOUBLE:
     158   [ +  +  +  +  :        817 :     return FP_CONVERSION(
          +  +  +  +  +  
                      + ]
     159                 :            :       double, *(const double*)in, MAX_DOUBLE_INT, trunc, llrint);
     160                 :        817 :   case EXESS_FLOAT:
     161   [ +  +  +  +  :        817 :     return FP_CONVERSION(
          +  +  +  +  +  
                      + ]
     162                 :            :       float, *(const float*)in, MAX_FLOAT_INT, truncf, llrintf);
     163                 :            : 
     164                 :       4234 :   case EXESS_LONG:
     165                 :       4234 :     return SCONVERSION(EXESS_SUCCESS, *(const int64_t*)in);
     166                 :         11 :   case EXESS_INT:
     167                 :         11 :     return SCONVERSION(EXESS_SUCCESS, *(const int32_t*)in);
     168                 :         11 :   case EXESS_SHORT:
     169                 :         11 :     return SCONVERSION(EXESS_SUCCESS, *(const int16_t*)in);
     170                 :         11 :   case EXESS_BYTE:
     171                 :         11 :     return SCONVERSION(EXESS_SUCCESS, *(const int8_t*)in);
     172                 :            : 
     173                 :         46 :   case EXESS_ULONG: {
     174                 :         46 :     const uint64_t u = *(const uint64_t*)in;
     175                 :         46 :     return (u > INT64_MAX) ? SCONVERSION(EXESS_OUT_OF_RANGE, 0)
     176         [ +  + ]:         46 :                            : SCONVERSION(EXESS_SUCCESS, (int64_t)u);
     177                 :            :   }
     178                 :            : 
     179                 :          8 :   case EXESS_UINT:
     180                 :          8 :     return SCONVERSION(EXESS_SUCCESS, *(const uint32_t*)in);
     181                 :          8 :   case EXESS_USHORT:
     182                 :          8 :     return SCONVERSION(EXESS_SUCCESS, *(const uint16_t*)in);
     183                 :          8 :   case EXESS_UBYTE:
     184                 :          8 :     return SCONVERSION(EXESS_SUCCESS, *(const uint8_t*)in);
     185                 :            : 
     186                 :        590 :   case EXESS_DECIMAL:
     187                 :            :   case EXESS_INTEGER:
     188                 :            :   case EXESS_NON_POSITIVE_INTEGER:
     189                 :            :   case EXESS_NEGATIVE_INTEGER:
     190                 :            :   case EXESS_NON_NEGATIVE_INTEGER:
     191                 :            :   case EXESS_POSITIVE_INTEGER:
     192                 :        590 :     return convert_signed_decimal(coercions, in);
     193                 :            : 
     194                 :         93 :   case EXESS_DURATION:
     195                 :            :   case EXESS_YEAR_MONTH_DURATION:
     196                 :            :   case EXESS_DAY_TIME_DURATION:
     197                 :            :   case EXESS_DATE_TIME:
     198                 :            :   case EXESS_DATE_TIME_STAMP:
     199                 :            :   case EXESS_TIME:
     200                 :            :   case EXESS_DATE:
     201                 :            :   case EXESS_YEAR_MONTH:
     202                 :            :   case EXESS_YEAR:
     203                 :            :   case EXESS_MONTH_DAY:
     204                 :            :   case EXESS_DAY:
     205                 :            :   case EXESS_MONTH:
     206                 :            :   case EXESS_HEX:
     207                 :            :   case EXESS_BASE64:
     208                 :         93 :     break;
     209                 :            :   }
     210                 :            : 
     211                 :         99 :   return SCONVERSION(EXESS_UNSUPPORTED, 0);
     212                 :            : }
     213                 :            : 
     214                 :            : static UConversion
     215                 :        945 : convert_unsigned(const ExessCoercions coercions,
     216                 :            :                  const ExessDatatype  in_datatype,
     217                 :            :                  const void* const    in)
     218                 :            : {
     219         [ +  + ]:        945 :   if (in_datatype == EXESS_ULONG) {
     220                 :         21 :     return UCONVERSION(EXESS_SUCCESS, *(const uint64_t*)in);
     221                 :            :   }
     222                 :            : 
     223   [ +  +  +  + ]:        924 :   if (in_datatype >= EXESS_DECIMAL && in_datatype <= EXESS_POSITIVE_INTEGER) {
     224                 :        268 :     BigDecimal        d = *(const BigDecimal*)in;
     225                 :        268 :     const UConversion c = convert_significand(coercions, in);
     226                 :            : 
     227                 :        373 :     return c.status        ? UCONVERSION(c.status, 0U)
     228         [ +  + ]:        431 :            : (d.expt > 20) ? UCONVERSION(EXESS_OUT_OF_RANGE, 0U)
     229         [ +  + ]:        163 :                            : c;
     230                 :            :   }
     231                 :            : 
     232                 :        656 :   const SConversion c = convert_signed(coercions, in_datatype, in);
     233                 :            : 
     234                 :        929 :   return c.status        ? UCONVERSION(c.status, 0U)
     235         [ +  + ]:        683 :          : (c.value < 0) ? UCONVERSION(EXESS_OUT_OF_RANGE, 0U)
     236         [ +  + ]:        383 :                          : UCONVERSION(EXESS_SUCCESS, (uint64_t)c.value);
     237                 :            : }
     238                 :            : 
     239                 :            : static SConversion
     240                 :       5997 : convert_signed_range(const ExessCoercions coercions,
     241                 :            :                      const ExessDatatype  in_datatype,
     242                 :            :                      const void* const    in,
     243                 :            :                      const int64_t        min,
     244                 :            :                      const int64_t        max)
     245                 :            : {
     246                 :       5997 :   const SConversion c = convert_signed(coercions, in_datatype, in);
     247                 :            : 
     248   [ +  +  +  + ]:       5239 :   return (!c.status && (c.value < min || c.value > max))
     249                 :            :            ? SCONVERSION(EXESS_OUT_OF_RANGE, 0U)
     250         [ +  + ]:      11236 :            : c;
     251                 :            : }
     252                 :            : 
     253                 :            : static UConversion
     254                 :        945 : convert_unsigned_range(const ExessCoercions coercions,
     255                 :            :                        const ExessDatatype  in_datatype,
     256                 :            :                        const void* const    in,
     257                 :            :                        const uint64_t       max)
     258                 :            : {
     259                 :        945 :   const UConversion c = convert_unsigned(coercions, in_datatype, in);
     260                 :            : 
     261   [ +  +  +  + ]:        945 :   return (!c.status && c.value > max) ? UCONVERSION(EXESS_OUT_OF_RANGE, 0U) : c;
     262                 :            : }
     263                 :            : 
     264                 :            : static ExessResult
     265                 :         27 : to_boolean(const ExessCoercions coercions,
     266                 :            :            const ExessDatatype  in_datatype,
     267                 :            :            const void* const    in,
     268                 :            :            bool* const          out)
     269                 :            : {
     270                 :         27 :   const SConversion c = convert_signed(coercions, in_datatype, in);
     271         [ +  + ]:         27 :   ENSURE(!c.status, c.status);
     272   [ +  +  +  +  :         23 :   ENSURE((coercions & (ExessCoercions)EXESS_TRUNCATE) || c.value == 0 ||
                   +  + ]
     273                 :            :            c.value == 1,
     274                 :            :          EXESS_LOSS);
     275                 :            : 
     276                 :         14 :   *out = !!c.value;
     277                 :         14 :   return RESULT(EXESS_SUCCESS, sizeof(bool));
     278                 :            : }
     279                 :            : 
     280                 :            : static ExessResult
     281                 :       8500 : to_decimal(const ExessCoercions coercions,
     282                 :            :            const ExessDatatype  in_datatype,
     283                 :            :            const void* const    in,
     284                 :            :            const size_t         out_size,
     285                 :            :            void* const          out)
     286                 :            : {
     287                 :       8500 :   BigDecimal* const head        = (BigDecimal*)out;
     288                 :       8500 :   const size_t      digits_size = out_size - sizeof(*head);
     289                 :       8500 :   char* const       digits      = (char*)(head + 1U);
     290                 :            : 
     291         [ +  + ]:       8500 :   if (in_datatype == EXESS_DECIMAL) {
     292                 :         68 :     const BigDecimal* const in_head = (const BigDecimal*)in;
     293                 :         68 :     const size_t            size = sizeof(BigDecimal) + in_head->n_digits + 1;
     294         [ +  + ]:         68 :     if (out_size < size) {
     295                 :         18 :       return RESULT(EXESS_NO_SPACE, size);
     296                 :            :     }
     297                 :            : 
     298                 :         50 :     memcpy(out, in, size);
     299                 :         50 :     return RESULT(EXESS_SUCCESS, size);
     300                 :            :   }
     301                 :            : 
     302   [ +  +  +  + ]:       8432 :   if (in_datatype == EXESS_DOUBLE || in_datatype == EXESS_FLOAT) {
     303                 :       4257 :     const double d = (in_datatype == EXESS_DOUBLE) ? *(const double*)in
     304         [ +  + ]:       4257 :                                                    : (double)*(const float*)in;
     305                 :            : 
     306                 :       4257 :     *head = double_to_decimal(d, digits_size, digits);
     307         [ +  + ]:       4257 :     if (head->n_digits == digits_size) { // No space for terminator
     308                 :         30 :       return RESULT(EXESS_NO_SPACE, sizeof(*head) + head->n_digits + 1U);
     309                 :            :     }
     310                 :            : 
     311                 :       4227 :     digits[head->n_digits] = '\0';
     312                 :       4227 :     return RESULT(EXESS_SUCCESS, sizeof(*head) + head->n_digits + 1U);
     313                 :            :   }
     314                 :            : 
     315         [ +  + ]:       4175 :   if (in_datatype == EXESS_ULONG) {
     316                 :         11 :     const uint64_t    u_in = *(const uint64_t*)in;
     317                 :         11 :     const ExessResult r    = write_digits(u_in, digits_size, digits, 0);
     318         [ +  + ]:         11 :     head->kind             = u_in ? EXESS_POSITIVE : EXESS_POSITIVE_ZERO;
     319                 :         11 :     head->n_digits         = (uint8_t)r.count;
     320                 :         11 :     head->expt             = (int16_t)r.count;
     321                 :         11 :     digits[head->n_digits] = '\0';
     322                 :         11 :     return RESULT(r.status, sizeof(*head) + r.count + 1U);
     323                 :            :   }
     324                 :            : 
     325                 :            :   const SConversion c =
     326                 :       4164 :     convert_signed_range(coercions, in_datatype, in, INT64_MIN, INT64_MAX);
     327                 :            : 
     328         [ +  + ]:       4164 :   ENSURE(!c.status, c.status);
     329         [ +  + ]:       4159 :   if (!c.value) {
     330                 :         18 :     head->kind     = EXESS_POSITIVE_ZERO;
     331                 :         18 :     head->n_digits = 0U;
     332                 :         18 :     head->expt     = 0;
     333                 :         18 :     digits[0]      = '\0';
     334                 :         18 :     return RESULT(EXESS_SUCCESS, sizeof(*head) + 1U);
     335                 :            :   }
     336                 :            : 
     337         [ +  + ]:       4141 :   if (c.value == INT64_MIN) {
     338         [ +  + ]:          7 :     if (out_size < sizeof(BigDecimal) + 20U) {
     339                 :          3 :       return RESULT(EXESS_NO_SPACE, sizeof(BigDecimal) + head->n_digits + 1);
     340                 :            :     }
     341                 :            : 
     342                 :          4 :     head->kind     = EXESS_NEGATIVE;
     343                 :          4 :     head->n_digits = 19U;
     344                 :          4 :     head->expt     = 19;
     345                 :          4 :     memcpy(digits, "9223372036854775808", 19U);
     346                 :          4 :     return RESULT(EXESS_SUCCESS, sizeof(*head) + 20U);
     347                 :            :   }
     348                 :            : 
     349                 :       4134 :   uint64_t abs_value   = c.value < 0 ? (uint64_t)-c.value : (uint64_t)c.value;
     350                 :       4134 :   int      expt_offset = 0;
     351   [ +  +  +  + ]:       4394 :   while (abs_value >= 10U && (abs_value % 10U) == 0U) {
     352                 :        260 :     abs_value /= 10U;
     353                 :        260 :     ++expt_offset;
     354                 :            :   }
     355                 :            : 
     356                 :       4134 :   const ExessResult r = write_digits(abs_value, digits_size - 1U, digits, 0);
     357                 :            : 
     358         [ +  + ]:       4134 :   head->kind             = c.value < 0 ? EXESS_NEGATIVE : EXESS_POSITIVE;
     359                 :       4134 :   head->n_digits         = (uint8_t)r.count;
     360                 :       4134 :   head->expt             = (int16_t)(expt_offset + (int)r.count);
     361                 :       4134 :   digits[head->n_digits] = '\0';
     362                 :            : 
     363                 :       4134 :   return RESULT(r.status, sizeof(*head) + r.count + 1U);
     364                 :            : }
     365                 :            : 
     366                 :            : static ExessResult
     367                 :       4115 : to_double(const ExessCoercions coercions,
     368                 :            :           const ExessDatatype  in_datatype,
     369                 :            :           const void* const    in,
     370                 :            :           double* const        out)
     371                 :            : {
     372         [ +  + ]:       4115 :   if (in_datatype == EXESS_DECIMAL) {
     373                 :       4103 :     const BigDecimal  d      = *(const BigDecimal*)in;
     374                 :       4103 :     const char* const digits = (const char*)in + sizeof(d);
     375   [ +  +  +  + ]:       4103 :     ENSURE((coercions & (ExessCoercions)EXESS_APPROXIMATE) ||
     376                 :            :              d.n_digits <= DBL_DECIMAL_DIG,
     377                 :            :            EXESS_LOSS);
     378                 :            : 
     379                 :       4102 :     *out = decimal_to_double(d, digits);
     380                 :       4102 :     return RESULT(EXESS_SUCCESS, sizeof(double));
     381                 :            :   }
     382                 :            : 
     383         [ +  + ]:         12 :   if (in_datatype == EXESS_FLOAT) {
     384                 :          1 :     *out = (double)*(const float*)in;
     385                 :          1 :     return RESULT(EXESS_SUCCESS, sizeof(double));
     386                 :            :   }
     387                 :            : 
     388                 :         11 :   const SConversion c = convert_signed_range(
     389                 :            :     coercions, in_datatype, in, -MAX_DOUBLE_INT, MAX_DOUBLE_INT);
     390                 :            : 
     391         [ +  + ]:         11 :   ENSURE(!c.status, c.status);
     392                 :          8 :   *out = (double)c.value;
     393                 :          8 :   return RESULT(EXESS_SUCCESS, sizeof(double));
     394                 :            : }
     395                 :            : 
     396                 :            : static ExessResult
     397                 :         19 : to_float(const ExessCoercions coercions,
     398                 :            :          const ExessDatatype  in_datatype,
     399                 :            :          const void* const    in,
     400                 :            :          float* const         out)
     401                 :            : {
     402         [ +  + ]:         19 :   if (in_datatype == EXESS_DECIMAL) {
     403                 :          3 :     const BigDecimal  d      = *(const BigDecimal*)in;
     404                 :          3 :     const char* const digits = (const char*)in + sizeof(d);
     405   [ +  +  +  + ]:          3 :     ENSURE((coercions & (ExessCoercions)EXESS_APPROXIMATE) ||
     406                 :            :              d.n_digits <= FLT_DECIMAL_DIG,
     407                 :            :            EXESS_LOSS);
     408                 :            : 
     409                 :          2 :     *out = (float)decimal_to_double(d, digits);
     410                 :          2 :     return RESULT(EXESS_SUCCESS, sizeof(float));
     411                 :            :   }
     412                 :            : 
     413         [ +  + ]:         16 :   if (in_datatype == EXESS_DOUBLE) {
     414         [ +  + ]:          5 :     ENSURE_COERCION(coercions, EXESS_APPROXIMATE);
     415                 :          2 :     *out = (float)*(const double*)in;
     416                 :          2 :     return RESULT(EXESS_SUCCESS, sizeof(float));
     417                 :            :   }
     418                 :            : 
     419                 :         11 :   const SConversion c = convert_signed_range(
     420                 :            :     coercions, in_datatype, in, -MAX_FLOAT_INT, MAX_FLOAT_INT);
     421                 :            : 
     422         [ +  + ]:         11 :   ENSURE(!c.status, c.status);
     423                 :          8 :   *out = (float)c.value;
     424                 :          8 :   return RESULT(EXESS_SUCCESS, sizeof(float));
     425                 :            : }
     426                 :            : 
     427                 :            : static ExessResult
     428                 :       4337 : to_integer(const ExessCoercions coercions,
     429                 :            :            const ExessDatatype  in_datatype,
     430                 :            :            const void* const    in,
     431                 :            :            const size_t         out_size,
     432                 :            :            void* const          out)
     433                 :            : {
     434                 :       4337 :   ExessResult r = to_decimal(coercions, in_datatype, in, out_size, out);
     435         [ +  + ]:       4337 :   if (r.status) {
     436                 :         58 :     return r;
     437                 :            :   }
     438                 :            : 
     439                 :       4279 :   BigDecimal* const d = (BigDecimal*)out;
     440         [ +  + ]:       4279 :   if (d->expt < (int)d->n_digits) {
     441                 :        121 :     char* const digits = (char*)(d + 1);
     442         [ +  + ]:        121 :     if (coercions & (ExessCoercions)EXESS_TRUNCATE) {
     443         [ +  + ]:         27 :       d->n_digits         = (uint8_t)MAX(0, d->expt);
     444                 :         27 :       digits[d->n_digits] = '\0';
     445         [ +  + ]:         27 :       if (!d->n_digits) {
     446                 :          7 :         d->kind = EXESS_POSITIVE_ZERO;
     447                 :          7 :         d->expt = 0;
     448                 :            :       }
     449         [ +  + ]:         94 :     } else if (coercions & (ExessCoercions)EXESS_ROUND) {
     450                 :         33 :       const bool up = round_decimal_up(d, digits);
     451         [ +  + ]:         33 :       d->n_digits   = (uint8_t)MAX(0, d->expt);
     452         [ +  + ]:         33 :       if (up) {
     453                 :         14 :         ++digits[d->n_digits - 1];
     454                 :            :       }
     455         [ +  + ]:         33 :       if (!d->n_digits) {
     456                 :          7 :         d->kind = EXESS_POSITIVE_ZERO;
     457                 :          7 :         d->expt = 0;
     458                 :            :       }
     459                 :            :     } else {
     460                 :         61 :       r.status = EXESS_LOSS;
     461                 :            :     }
     462                 :            :   }
     463                 :            : 
     464                 :       4279 :   return r;
     465                 :            : }
     466                 :            : 
     467                 :            : static ExessResult
     468                 :         29 : to_constrained_integer(const ExessCoercions coercions,
     469                 :            :                        const ExessDatatype  in_datatype,
     470                 :            :                        const void* const    in,
     471                 :            :                        const ExessDatatype  out_datatype,
     472                 :            :                        const size_t         out_size,
     473                 :            :                        void* const          out)
     474                 :            : {
     475                 :         29 :   const ExessResult r = to_integer(coercions, in_datatype, in, out_size, out);
     476         [ +  + ]:         29 :   ENSURE(!r.status, r.status);
     477                 :            : 
     478                 :         20 :   const BigDecimalKind kind = ((BigDecimal*)out)->kind;
     479                 :            : 
     480                 :         20 :   const bool bad_kind =
     481                 :            :     (out_datatype == EXESS_NON_POSITIVE_INTEGER)   ? (kind == EXESS_POSITIVE)
     482   [ +  +  +  +  :         20 :     : (out_datatype == EXESS_NEGATIVE_INTEGER)     ? (kind != EXESS_NEGATIVE)
                   +  + ]
     483                 :            :     : (out_datatype == EXESS_NON_NEGATIVE_INTEGER) ? (kind == EXESS_NEGATIVE)
     484                 :            :                                                    : (kind != EXESS_POSITIVE);
     485                 :            : 
     486         [ +  + ]:         20 :   return bad_kind ? RESULT(EXESS_OUT_OF_RANGE, 0U) : r;
     487                 :            : }
     488                 :            : 
     489                 :            : static ExessResult
     490                 :         17 : to_time(const ExessCoercions coercions,
     491                 :            :         const ExessDatatype  in_datatype,
     492                 :            :         const void* const    in,
     493                 :            :         ExessTime* const     out)
     494                 :            : {
     495         [ +  + ]:         17 :   ENSURE(in_datatype == EXESS_DATE_TIME, EXESS_UNSUPPORTED);
     496         [ +  + ]:          7 :   ENSURE_COERCION(coercions, EXESS_TRUNCATE);
     497                 :            : 
     498                 :          3 :   const ExessDateTime datetime = *(const ExessDateTime*)in;
     499                 :            : 
     500                 :          3 :   out->zone       = datetime.zone;
     501                 :          3 :   out->hour       = datetime.hour;
     502                 :          3 :   out->minute     = datetime.minute;
     503                 :          3 :   out->second     = datetime.second;
     504                 :          3 :   out->nanosecond = datetime.nanosecond;
     505                 :          3 :   return RESULT(EXESS_SUCCESS, sizeof(ExessTime));
     506                 :            : }
     507                 :            : 
     508                 :            : static ExessResult
     509                 :         16 : to_date(const ExessCoercions coercions,
     510                 :            :         const ExessDatatype  in_datatype,
     511                 :            :         const void* const    in,
     512                 :            :         ExessDate* const     out)
     513                 :            : {
     514         [ +  + ]:         16 :   ENSURE(in_datatype == EXESS_DATE_TIME, EXESS_UNSUPPORTED);
     515         [ +  + ]:          6 :   ENSURE_COERCION(coercions, EXESS_TRUNCATE);
     516                 :            : 
     517                 :          3 :   const ExessDateTime datetime = *(const ExessDateTime*)in;
     518                 :            : 
     519                 :          3 :   out->year  = datetime.year;
     520                 :          3 :   out->month = datetime.month;
     521                 :          3 :   out->day   = datetime.day;
     522                 :          3 :   out->zone  = datetime.zone;
     523                 :          3 :   return RESULT(EXESS_SUCCESS, sizeof(ExessDate));
     524                 :            : }
     525                 :            : 
     526                 :            : static uint8_t
     527                 :      13924 : min_value_size(const ExessDatatype datatype)
     528                 :            : {
     529         [ +  + ]:       8593 :   return (datatype >= EXESS_DECIMAL && datatype <= EXESS_POSITIVE_INTEGER)
     530                 :            :            ? 6U
     531         [ +  + ]:      22517 :            : exess_value_sizes[datatype];
     532                 :            : }
     533                 :            : 
     534                 :            : EXESS_NONBLOCKING ExessResult
     535                 :      13925 : exess_coerce_value(const ExessCoercions coercions,
     536                 :            :                    const ExessDatatype  in_datatype,
     537                 :            :                    const size_t         in_size,
     538                 :            :                    const void* const    in,
     539                 :            :                    const ExessDatatype  out_datatype,
     540                 :            :                    const size_t         out_size,
     541                 :            :                    void* const          out)
     542                 :            : {
     543                 :            :   // Ensure the input and output are large enough (for fixed types)
     544         [ +  + ]:      13925 :   ENSURE(in_size >= exess_value_sizes[in_datatype], EXESS_BAD_VALUE);
     545         [ +  + ]:      13924 :   ENSURE(out_size >= min_value_size(out_datatype), EXESS_NO_SPACE);
     546                 :            : 
     547                 :            :   // Copy value verbatim for trivial conversions
     548   [ +  +  +  + ]:      13516 :   if ((out_datatype == in_datatype) ||
     549   [ +  +  +  + ]:      13513 :       (out_datatype == EXESS_HEX && in_datatype == EXESS_BASE64) ||
     550         [ +  + ]:          5 :       (out_datatype == EXESS_BASE64 && in_datatype == EXESS_HEX)) {
     551         [ +  + ]:         10 :     ENSURE(out_size >= in_size, EXESS_NO_SPACE);
     552                 :          6 :     memcpy(out, in, in_size);
     553                 :          6 :     return RESULT(EXESS_SUCCESS, in_size);
     554                 :            :   }
     555                 :            : 
     556                 :            : #define FIXED_CASE(Type, c)                                    \
     557                 :            :   do {                                                         \
     558                 :            :     *(Type*)out = (c).status ? (Type)0 : (Type)(c).value;      \
     559                 :            :     return RESULT((c).status, (c).status ? 0U : sizeof(Type)); \
     560                 :            :   } while (0)
     561                 :            : 
     562   [ +  +  +  +  :      13506 :   switch (out_datatype) {
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
                      - ]
     563                 :          2 :   case EXESS_NOTHING:
     564                 :          2 :     break;
     565                 :            : 
     566                 :         27 :   case EXESS_BOOLEAN:
     567                 :         27 :     return to_boolean(coercions, in_datatype, in, (bool*)out);
     568                 :       4115 :   case EXESS_DOUBLE:
     569                 :       4115 :     return to_double(coercions, in_datatype, in, (double*)out);
     570                 :         19 :   case EXESS_FLOAT:
     571                 :         19 :     return to_float(coercions, in_datatype, in, (float*)out);
     572                 :            : 
     573                 :        169 :   case EXESS_LONG:
     574   [ +  +  +  + ]:        169 :     FIXED_CASE(
     575                 :            :       int64_t,
     576                 :            :       convert_signed_range(coercions, in_datatype, in, INT64_MIN, INT64_MAX));
     577                 :        116 :   case EXESS_INT:
     578   [ +  +  +  + ]:        116 :     FIXED_CASE(
     579                 :            :       int32_t,
     580                 :            :       convert_signed_range(coercions, in_datatype, in, INT32_MIN, INT32_MAX));
     581                 :        116 :   case EXESS_SHORT:
     582   [ +  +  +  + ]:        116 :     FIXED_CASE(
     583                 :            :       int16_t,
     584                 :            :       convert_signed_range(coercions, in_datatype, in, INT16_MIN, INT16_MAX));
     585                 :        116 :   case EXESS_BYTE:
     586         [ +  + ]:        116 :     FIXED_CASE(
     587                 :            :       int8_t,
     588                 :            :       convert_signed_range(coercions, in_datatype, in, INT8_MIN, INT8_MAX));
     589                 :            : 
     590                 :        103 :   case EXESS_ULONG:
     591   [ +  +  +  + ]:        103 :     FIXED_CASE(uint64_t,
     592                 :            :                convert_unsigned_range(coercions, in_datatype, in, UINT64_MAX));
     593                 :         56 :   case EXESS_UINT:
     594   [ +  +  +  + ]:         56 :     FIXED_CASE(uint32_t,
     595                 :            :                convert_unsigned_range(coercions, in_datatype, in, UINT32_MAX));
     596                 :         56 :   case EXESS_USHORT:
     597   [ +  +  +  + ]:         56 :     FIXED_CASE(uint16_t,
     598                 :            :                convert_unsigned_range(coercions, in_datatype, in, UINT16_MAX));
     599                 :         56 :   case EXESS_UBYTE:
     600         [ +  + ]:         56 :     FIXED_CASE(uint8_t,
     601                 :            :                convert_unsigned_range(coercions, in_datatype, in, UINT8_MAX));
     602                 :            : 
     603                 :       4163 :   case EXESS_DECIMAL:
     604                 :       4163 :     return to_decimal(coercions, in_datatype, in, out_size, out);
     605                 :       4308 :   case EXESS_INTEGER:
     606                 :       4308 :     return to_integer(coercions, in_datatype, in, out_size, out);
     607                 :         29 :   case EXESS_NON_POSITIVE_INTEGER:
     608                 :            :   case EXESS_NEGATIVE_INTEGER:
     609                 :            :   case EXESS_NON_NEGATIVE_INTEGER:
     610                 :            :   case EXESS_POSITIVE_INTEGER:
     611                 :         29 :     return to_constrained_integer(
     612                 :            :       coercions, in_datatype, in, out_datatype, out_size, out);
     613                 :            : 
     614                 :         20 :   case EXESS_DURATION:
     615                 :            :   case EXESS_YEAR_MONTH_DURATION:
     616                 :            :   case EXESS_DAY_TIME_DURATION:
     617                 :            :   case EXESS_DATE_TIME:
     618                 :            :   case EXESS_DATE_TIME_STAMP:
     619                 :         20 :     break;
     620                 :            : 
     621                 :         17 :   case EXESS_TIME:
     622                 :         17 :     return to_time(coercions, in_datatype, in, (ExessTime*)out);
     623                 :         16 :   case EXESS_DATE:
     624                 :         16 :     return to_date(coercions, in_datatype, in, (ExessDate*)out);
     625                 :            : 
     626                 :          2 :   case EXESS_YEAR_MONTH:
     627                 :            :   case EXESS_YEAR:
     628                 :            :   case EXESS_MONTH_DAY:
     629                 :            :   case EXESS_DAY:
     630                 :            :   case EXESS_MONTH:
     631                 :            :   case EXESS_HEX:
     632                 :            :   case EXESS_BASE64:
     633                 :          2 :     break;
     634                 :            :   }
     635                 :            : 
     636                 :            : #undef FIXED_CASE
     637                 :            : 
     638                 :         24 :   return RESULT(EXESS_UNSUPPORTED, 0U);
     639                 :            : }

Generated by: LCOV version 1.16