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-07-14 21:28:07 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_HEX:
     202                 :            :   case EXESS_BASE64:
     203                 :         93 :     break;
     204                 :            :   }
     205                 :            : 
     206                 :         99 :   return SCONVERSION(EXESS_UNSUPPORTED, 0);
     207                 :            : }
     208                 :            : 
     209                 :            : static UConversion
     210                 :        945 : convert_unsigned(const ExessCoercions coercions,
     211                 :            :                  const ExessDatatype  in_datatype,
     212                 :            :                  const void* const    in)
     213                 :            : {
     214         [ +  + ]:        945 :   if (in_datatype == EXESS_ULONG) {
     215                 :         21 :     return UCONVERSION(EXESS_SUCCESS, *(const uint64_t*)in);
     216                 :            :   }
     217                 :            : 
     218   [ +  +  +  + ]:        924 :   if (in_datatype >= EXESS_DECIMAL && in_datatype <= EXESS_POSITIVE_INTEGER) {
     219                 :        268 :     BigDecimal        d = *(const BigDecimal*)in;
     220                 :        268 :     const UConversion c = convert_significand(coercions, in);
     221                 :            : 
     222                 :        373 :     return c.status        ? UCONVERSION(c.status, 0U)
     223         [ +  + ]:        431 :            : (d.expt > 20) ? UCONVERSION(EXESS_OUT_OF_RANGE, 0U)
     224         [ +  + ]:        163 :                            : c;
     225                 :            :   }
     226                 :            : 
     227                 :        656 :   const SConversion c = convert_signed(coercions, in_datatype, in);
     228                 :            : 
     229                 :        929 :   return c.status        ? UCONVERSION(c.status, 0U)
     230         [ +  + ]:        683 :          : (c.value < 0) ? UCONVERSION(EXESS_OUT_OF_RANGE, 0U)
     231         [ +  + ]:        383 :                          : UCONVERSION(EXESS_SUCCESS, (uint64_t)c.value);
     232                 :            : }
     233                 :            : 
     234                 :            : static SConversion
     235                 :       5997 : convert_signed_range(const ExessCoercions coercions,
     236                 :            :                      const ExessDatatype  in_datatype,
     237                 :            :                      const void* const    in,
     238                 :            :                      const int64_t        min,
     239                 :            :                      const int64_t        max)
     240                 :            : {
     241                 :       5997 :   const SConversion c = convert_signed(coercions, in_datatype, in);
     242                 :            : 
     243   [ +  +  +  + ]:       5239 :   return (!c.status && (c.value < min || c.value > max))
     244                 :            :            ? SCONVERSION(EXESS_OUT_OF_RANGE, 0U)
     245         [ +  + ]:      11236 :            : c;
     246                 :            : }
     247                 :            : 
     248                 :            : static UConversion
     249                 :        945 : convert_unsigned_range(const ExessCoercions coercions,
     250                 :            :                        const ExessDatatype  in_datatype,
     251                 :            :                        const void* const    in,
     252                 :            :                        const uint64_t       max)
     253                 :            : {
     254                 :        945 :   const UConversion c = convert_unsigned(coercions, in_datatype, in);
     255                 :            : 
     256   [ +  +  +  + ]:        945 :   return (!c.status && c.value > max) ? UCONVERSION(EXESS_OUT_OF_RANGE, 0U) : c;
     257                 :            : }
     258                 :            : 
     259                 :            : static ExessResult
     260                 :         27 : to_boolean(const ExessCoercions coercions,
     261                 :            :            const ExessDatatype  in_datatype,
     262                 :            :            const void* const    in,
     263                 :            :            bool* const          out)
     264                 :            : {
     265                 :         27 :   const SConversion c = convert_signed(coercions, in_datatype, in);
     266         [ +  + ]:         27 :   ENSURE(!c.status, c.status);
     267   [ +  +  +  +  :         23 :   ENSURE((coercions & (ExessCoercions)EXESS_TRUNCATE) || c.value == 0 ||
                   +  + ]
     268                 :            :            c.value == 1,
     269                 :            :          EXESS_LOSS);
     270                 :            : 
     271                 :         14 :   *out = !!c.value;
     272                 :         14 :   return RESULT(EXESS_SUCCESS, sizeof(bool));
     273                 :            : }
     274                 :            : 
     275                 :            : static ExessResult
     276                 :       8500 : to_decimal(const ExessCoercions coercions,
     277                 :            :            const ExessDatatype  in_datatype,
     278                 :            :            const void* const    in,
     279                 :            :            const size_t         out_size,
     280                 :            :            void* const          out)
     281                 :            : {
     282                 :       8500 :   BigDecimal* const head        = (BigDecimal*)out;
     283                 :       8500 :   const size_t      digits_size = out_size - sizeof(*head);
     284                 :       8500 :   char* const       digits      = (char*)(head + 1U);
     285                 :            : 
     286         [ +  + ]:       8500 :   if (in_datatype == EXESS_DECIMAL) {
     287                 :         68 :     const BigDecimal* const in_head = (const BigDecimal*)in;
     288                 :         68 :     const size_t            size = sizeof(BigDecimal) + in_head->n_digits + 1;
     289         [ +  + ]:         68 :     if (out_size < size) {
     290                 :         18 :       return RESULT(EXESS_NO_SPACE, size);
     291                 :            :     }
     292                 :            : 
     293                 :         50 :     memcpy(out, in, size);
     294                 :         50 :     return RESULT(EXESS_SUCCESS, size);
     295                 :            :   }
     296                 :            : 
     297   [ +  +  +  + ]:       8432 :   if (in_datatype == EXESS_DOUBLE || in_datatype == EXESS_FLOAT) {
     298                 :       4257 :     const double d = (in_datatype == EXESS_DOUBLE) ? *(const double*)in
     299         [ +  + ]:       4257 :                                                    : (double)*(const float*)in;
     300                 :            : 
     301                 :       4257 :     *head = double_to_decimal(d, digits_size, digits);
     302         [ +  + ]:       4257 :     if (head->n_digits == digits_size) { // No space for terminator
     303                 :         30 :       return RESULT(EXESS_NO_SPACE, sizeof(*head) + head->n_digits + 1U);
     304                 :            :     }
     305                 :            : 
     306                 :       4227 :     digits[head->n_digits] = '\0';
     307                 :       4227 :     return RESULT(EXESS_SUCCESS, sizeof(*head) + head->n_digits + 1U);
     308                 :            :   }
     309                 :            : 
     310         [ +  + ]:       4175 :   if (in_datatype == EXESS_ULONG) {
     311                 :         11 :     const uint64_t    u_in = *(const uint64_t*)in;
     312                 :         11 :     const ExessResult r    = write_digits(u_in, digits_size, digits, 0);
     313         [ +  + ]:         11 :     head->kind             = u_in ? EXESS_POSITIVE : EXESS_POSITIVE_ZERO;
     314                 :         11 :     head->n_digits         = (uint8_t)r.count;
     315                 :         11 :     head->expt             = (int16_t)r.count;
     316                 :         11 :     digits[head->n_digits] = '\0';
     317                 :         11 :     return RESULT(r.status, sizeof(*head) + r.count + 1U);
     318                 :            :   }
     319                 :            : 
     320                 :            :   const SConversion c =
     321                 :       4164 :     convert_signed_range(coercions, in_datatype, in, INT64_MIN, INT64_MAX);
     322                 :            : 
     323         [ +  + ]:       4164 :   ENSURE(!c.status, c.status);
     324         [ +  + ]:       4159 :   if (!c.value) {
     325                 :         18 :     head->kind     = EXESS_POSITIVE_ZERO;
     326                 :         18 :     head->n_digits = 0U;
     327                 :         18 :     head->expt     = 0;
     328                 :         18 :     digits[0]      = '\0';
     329                 :         18 :     return RESULT(EXESS_SUCCESS, sizeof(*head) + 1U);
     330                 :            :   }
     331                 :            : 
     332         [ +  + ]:       4141 :   if (c.value == INT64_MIN) {
     333         [ +  + ]:          7 :     if (out_size < sizeof(BigDecimal) + 20U) {
     334                 :          3 :       return RESULT(EXESS_NO_SPACE, sizeof(BigDecimal) + head->n_digits + 1);
     335                 :            :     }
     336                 :            : 
     337                 :          4 :     head->kind     = EXESS_NEGATIVE;
     338                 :          4 :     head->n_digits = 19U;
     339                 :          4 :     head->expt     = 19;
     340                 :          4 :     memcpy(digits, "9223372036854775808", 19U);
     341                 :          4 :     return RESULT(EXESS_SUCCESS, sizeof(*head) + 20U);
     342                 :            :   }
     343                 :            : 
     344                 :       4134 :   uint64_t abs_value   = c.value < 0 ? (uint64_t)-c.value : (uint64_t)c.value;
     345                 :       4134 :   int      expt_offset = 0;
     346   [ +  +  +  + ]:       4380 :   while (abs_value >= 10U && (abs_value % 10U) == 0U) {
     347                 :        246 :     abs_value /= 10U;
     348                 :        246 :     ++expt_offset;
     349                 :            :   }
     350                 :            : 
     351                 :       4134 :   const ExessResult r = write_digits(abs_value, digits_size - 1U, digits, 0);
     352                 :            : 
     353         [ +  + ]:       4134 :   head->kind             = c.value < 0 ? EXESS_NEGATIVE : EXESS_POSITIVE;
     354                 :       4134 :   head->n_digits         = (uint8_t)r.count;
     355                 :       4134 :   head->expt             = (int16_t)(expt_offset + (int)r.count);
     356                 :       4134 :   digits[head->n_digits] = '\0';
     357                 :            : 
     358                 :       4134 :   return RESULT(r.status, sizeof(*head) + r.count + 1U);
     359                 :            : }
     360                 :            : 
     361                 :            : static ExessResult
     362                 :       4115 : to_double(const ExessCoercions coercions,
     363                 :            :           const ExessDatatype  in_datatype,
     364                 :            :           const void* const    in,
     365                 :            :           double* const        out)
     366                 :            : {
     367         [ +  + ]:       4115 :   if (in_datatype == EXESS_DECIMAL) {
     368                 :       4103 :     const BigDecimal  d      = *(const BigDecimal*)in;
     369                 :       4103 :     const char* const digits = (const char*)in + sizeof(d);
     370   [ +  +  +  + ]:       4103 :     ENSURE((coercions & (ExessCoercions)EXESS_APPROXIMATE) ||
     371                 :            :              d.n_digits <= DBL_DECIMAL_DIG,
     372                 :            :            EXESS_LOSS);
     373                 :            : 
     374                 :       4102 :     *out = decimal_to_double(d, digits);
     375                 :       4102 :     return RESULT(EXESS_SUCCESS, sizeof(double));
     376                 :            :   }
     377                 :            : 
     378         [ +  + ]:         12 :   if (in_datatype == EXESS_FLOAT) {
     379                 :          1 :     *out = (double)*(const float*)in;
     380                 :          1 :     return RESULT(EXESS_SUCCESS, sizeof(double));
     381                 :            :   }
     382                 :            : 
     383                 :         11 :   const SConversion c = convert_signed_range(
     384                 :            :     coercions, in_datatype, in, -MAX_DOUBLE_INT, MAX_DOUBLE_INT);
     385                 :            : 
     386         [ +  + ]:         11 :   ENSURE(!c.status, c.status);
     387                 :          8 :   *out = (double)c.value;
     388                 :          8 :   return RESULT(EXESS_SUCCESS, sizeof(double));
     389                 :            : }
     390                 :            : 
     391                 :            : static ExessResult
     392                 :         19 : to_float(const ExessCoercions coercions,
     393                 :            :          const ExessDatatype  in_datatype,
     394                 :            :          const void* const    in,
     395                 :            :          float* const         out)
     396                 :            : {
     397         [ +  + ]:         19 :   if (in_datatype == EXESS_DECIMAL) {
     398                 :          3 :     const BigDecimal  d      = *(const BigDecimal*)in;
     399                 :          3 :     const char* const digits = (const char*)in + sizeof(d);
     400   [ +  +  +  + ]:          3 :     ENSURE((coercions & (ExessCoercions)EXESS_APPROXIMATE) ||
     401                 :            :              d.n_digits <= FLT_DECIMAL_DIG,
     402                 :            :            EXESS_LOSS);
     403                 :            : 
     404                 :          2 :     *out = (float)decimal_to_double(d, digits);
     405                 :          2 :     return RESULT(EXESS_SUCCESS, sizeof(float));
     406                 :            :   }
     407                 :            : 
     408         [ +  + ]:         16 :   if (in_datatype == EXESS_DOUBLE) {
     409         [ +  + ]:          5 :     ENSURE_COERCION(coercions, EXESS_APPROXIMATE);
     410                 :          2 :     *out = (float)*(const double*)in;
     411                 :          2 :     return RESULT(EXESS_SUCCESS, sizeof(float));
     412                 :            :   }
     413                 :            : 
     414                 :         11 :   const SConversion c = convert_signed_range(
     415                 :            :     coercions, in_datatype, in, -MAX_FLOAT_INT, MAX_FLOAT_INT);
     416                 :            : 
     417         [ +  + ]:         11 :   ENSURE(!c.status, c.status);
     418                 :          8 :   *out = (float)c.value;
     419                 :          8 :   return RESULT(EXESS_SUCCESS, sizeof(float));
     420                 :            : }
     421                 :            : 
     422                 :            : static ExessResult
     423                 :       4337 : to_integer(const ExessCoercions coercions,
     424                 :            :            const ExessDatatype  in_datatype,
     425                 :            :            const void* const    in,
     426                 :            :            const size_t         out_size,
     427                 :            :            void* const          out)
     428                 :            : {
     429                 :       4337 :   ExessResult r = to_decimal(coercions, in_datatype, in, out_size, out);
     430         [ +  + ]:       4337 :   if (r.status) {
     431                 :         58 :     return r;
     432                 :            :   }
     433                 :            : 
     434                 :       4279 :   BigDecimal* const d = (BigDecimal*)out;
     435         [ +  + ]:       4279 :   if (d->expt < (int)d->n_digits) {
     436                 :        121 :     char* const digits = (char*)(d + 1);
     437         [ +  + ]:        121 :     if (coercions & (ExessCoercions)EXESS_TRUNCATE) {
     438         [ +  + ]:         27 :       d->n_digits         = (uint8_t)MAX(0, d->expt);
     439                 :         27 :       digits[d->n_digits] = '\0';
     440         [ +  + ]:         27 :       if (!d->n_digits) {
     441                 :          7 :         d->kind = EXESS_POSITIVE_ZERO;
     442                 :          7 :         d->expt = 0;
     443                 :            :       }
     444         [ +  + ]:         94 :     } else if (coercions & (ExessCoercions)EXESS_ROUND) {
     445                 :         33 :       const bool up = round_decimal_up(d, digits);
     446         [ +  + ]:         33 :       d->n_digits   = (uint8_t)MAX(0, d->expt);
     447         [ +  + ]:         33 :       if (up) {
     448                 :         14 :         ++digits[d->n_digits - 1];
     449                 :            :       }
     450         [ +  + ]:         33 :       if (!d->n_digits) {
     451                 :          7 :         d->kind = EXESS_POSITIVE_ZERO;
     452                 :          7 :         d->expt = 0;
     453                 :            :       }
     454                 :            :     } else {
     455                 :         61 :       r.status = EXESS_LOSS;
     456                 :            :     }
     457                 :            :   }
     458                 :            : 
     459                 :       4279 :   return r;
     460                 :            : }
     461                 :            : 
     462                 :            : static ExessResult
     463                 :         29 : to_constrained_integer(const ExessCoercions coercions,
     464                 :            :                        const ExessDatatype  in_datatype,
     465                 :            :                        const void* const    in,
     466                 :            :                        const ExessDatatype  out_datatype,
     467                 :            :                        const size_t         out_size,
     468                 :            :                        void* const          out)
     469                 :            : {
     470                 :         29 :   const ExessResult r = to_integer(coercions, in_datatype, in, out_size, out);
     471         [ +  + ]:         29 :   ENSURE(!r.status, r.status);
     472                 :            : 
     473                 :         20 :   const BigDecimalKind kind = ((BigDecimal*)out)->kind;
     474                 :            : 
     475                 :         20 :   const bool bad_kind =
     476                 :            :     (out_datatype == EXESS_NON_POSITIVE_INTEGER)   ? (kind == EXESS_POSITIVE)
     477   [ +  +  +  +  :         20 :     : (out_datatype == EXESS_NEGATIVE_INTEGER)     ? (kind != EXESS_NEGATIVE)
                   +  + ]
     478                 :            :     : (out_datatype == EXESS_NON_NEGATIVE_INTEGER) ? (kind == EXESS_NEGATIVE)
     479                 :            :                                                    : (kind != EXESS_POSITIVE);
     480                 :            : 
     481         [ +  + ]:         20 :   return bad_kind ? RESULT(EXESS_OUT_OF_RANGE, 0U) : r;
     482                 :            : }
     483                 :            : 
     484                 :            : static ExessResult
     485                 :         17 : to_time(const ExessCoercions coercions,
     486                 :            :         const ExessDatatype  in_datatype,
     487                 :            :         const void* const    in,
     488                 :            :         ExessTime* const     out)
     489                 :            : {
     490         [ +  + ]:         17 :   ENSURE(in_datatype == EXESS_DATE_TIME, EXESS_UNSUPPORTED);
     491         [ +  + ]:          7 :   ENSURE_COERCION(coercions, EXESS_TRUNCATE);
     492                 :            : 
     493                 :          3 :   const ExessDateTime datetime = *(const ExessDateTime*)in;
     494                 :            : 
     495                 :          3 :   out->zone       = datetime.zone;
     496                 :          3 :   out->hour       = datetime.hour;
     497                 :          3 :   out->minute     = datetime.minute;
     498                 :          3 :   out->second     = datetime.second;
     499                 :          3 :   out->nanosecond = datetime.nanosecond;
     500                 :          3 :   return RESULT(EXESS_SUCCESS, sizeof(ExessTime));
     501                 :            : }
     502                 :            : 
     503                 :            : static ExessResult
     504                 :         16 : to_date(const ExessCoercions coercions,
     505                 :            :         const ExessDatatype  in_datatype,
     506                 :            :         const void* const    in,
     507                 :            :         ExessDate* const     out)
     508                 :            : {
     509         [ +  + ]:         16 :   ENSURE(in_datatype == EXESS_DATE_TIME, EXESS_UNSUPPORTED);
     510         [ +  + ]:          6 :   ENSURE_COERCION(coercions, EXESS_TRUNCATE);
     511                 :            : 
     512                 :          3 :   const ExessDateTime datetime = *(const ExessDateTime*)in;
     513                 :            : 
     514                 :          3 :   out->year  = datetime.year;
     515                 :          3 :   out->month = datetime.month;
     516                 :          3 :   out->day   = datetime.day;
     517                 :          3 :   out->zone  = datetime.zone;
     518                 :          3 :   return RESULT(EXESS_SUCCESS, sizeof(ExessDate));
     519                 :            : }
     520                 :            : 
     521                 :            : static uint8_t
     522                 :      13924 : min_value_size(const ExessDatatype datatype)
     523                 :            : {
     524         [ +  + ]:       8593 :   return (datatype >= EXESS_DECIMAL && datatype <= EXESS_POSITIVE_INTEGER)
     525                 :            :            ? 6U
     526         [ +  + ]:      22517 :            : exess_value_sizes[datatype];
     527                 :            : }
     528                 :            : 
     529                 :            : EXESS_NONBLOCKING ExessResult
     530                 :      13925 : exess_coerce_value(const ExessCoercions coercions,
     531                 :            :                    const ExessDatatype  in_datatype,
     532                 :            :                    const size_t         in_size,
     533                 :            :                    const void* const    in,
     534                 :            :                    const ExessDatatype  out_datatype,
     535                 :            :                    const size_t         out_size,
     536                 :            :                    void* const          out)
     537                 :            : {
     538                 :            :   // Ensure the input and output are large enough (for fixed types)
     539         [ +  + ]:      13925 :   ENSURE(in_size >= exess_value_sizes[in_datatype], EXESS_BAD_VALUE);
     540         [ +  + ]:      13924 :   ENSURE(out_size >= min_value_size(out_datatype), EXESS_NO_SPACE);
     541                 :            : 
     542                 :            :   // Copy value verbatim for trivial conversions
     543   [ +  +  +  + ]:      13516 :   if ((out_datatype == in_datatype) ||
     544   [ +  +  +  + ]:      13513 :       (out_datatype == EXESS_HEX && in_datatype == EXESS_BASE64) ||
     545         [ +  + ]:          5 :       (out_datatype == EXESS_BASE64 && in_datatype == EXESS_HEX)) {
     546         [ +  + ]:         10 :     ENSURE(out_size >= in_size, EXESS_NO_SPACE);
     547                 :          6 :     memcpy(out, in, in_size);
     548                 :          6 :     return RESULT(EXESS_SUCCESS, in_size);
     549                 :            :   }
     550                 :            : 
     551                 :            : #define FIXED_CASE(Type, c)                                    \
     552                 :            :   do {                                                         \
     553                 :            :     *(Type*)out = (c).status ? (Type)0 : (Type)(c).value;      \
     554                 :            :     return RESULT((c).status, (c).status ? 0U : sizeof(Type)); \
     555                 :            :   } while (0)
     556                 :            : 
     557   [ +  +  +  +  :      13506 :   switch (out_datatype) {
          +  +  +  +  +  
          +  +  +  +  +  
          +  +  +  +  +  
                      - ]
     558                 :          2 :   case EXESS_NOTHING:
     559                 :          2 :     break;
     560                 :            : 
     561                 :         27 :   case EXESS_BOOLEAN:
     562                 :         27 :     return to_boolean(coercions, in_datatype, in, (bool*)out);
     563                 :       4115 :   case EXESS_DOUBLE:
     564                 :       4115 :     return to_double(coercions, in_datatype, in, (double*)out);
     565                 :         19 :   case EXESS_FLOAT:
     566                 :         19 :     return to_float(coercions, in_datatype, in, (float*)out);
     567                 :            : 
     568                 :        169 :   case EXESS_LONG:
     569   [ +  +  +  + ]:        169 :     FIXED_CASE(
     570                 :            :       int64_t,
     571                 :            :       convert_signed_range(coercions, in_datatype, in, INT64_MIN, INT64_MAX));
     572                 :        116 :   case EXESS_INT:
     573   [ +  +  +  + ]:        116 :     FIXED_CASE(
     574                 :            :       int32_t,
     575                 :            :       convert_signed_range(coercions, in_datatype, in, INT32_MIN, INT32_MAX));
     576                 :        116 :   case EXESS_SHORT:
     577   [ +  +  +  + ]:        116 :     FIXED_CASE(
     578                 :            :       int16_t,
     579                 :            :       convert_signed_range(coercions, in_datatype, in, INT16_MIN, INT16_MAX));
     580                 :        116 :   case EXESS_BYTE:
     581         [ +  + ]:        116 :     FIXED_CASE(
     582                 :            :       int8_t,
     583                 :            :       convert_signed_range(coercions, in_datatype, in, INT8_MIN, INT8_MAX));
     584                 :            : 
     585                 :        103 :   case EXESS_ULONG:
     586   [ +  +  +  + ]:        103 :     FIXED_CASE(uint64_t,
     587                 :            :                convert_unsigned_range(coercions, in_datatype, in, UINT64_MAX));
     588                 :         56 :   case EXESS_UINT:
     589   [ +  +  +  + ]:         56 :     FIXED_CASE(uint32_t,
     590                 :            :                convert_unsigned_range(coercions, in_datatype, in, UINT32_MAX));
     591                 :         56 :   case EXESS_USHORT:
     592   [ +  +  +  + ]:         56 :     FIXED_CASE(uint16_t,
     593                 :            :                convert_unsigned_range(coercions, in_datatype, in, UINT16_MAX));
     594                 :         56 :   case EXESS_UBYTE:
     595         [ +  + ]:         56 :     FIXED_CASE(uint8_t,
     596                 :            :                convert_unsigned_range(coercions, in_datatype, in, UINT8_MAX));
     597                 :            : 
     598                 :       4163 :   case EXESS_DECIMAL:
     599                 :       4163 :     return to_decimal(coercions, in_datatype, in, out_size, out);
     600                 :       4308 :   case EXESS_INTEGER:
     601                 :       4308 :     return to_integer(coercions, in_datatype, in, out_size, out);
     602                 :         29 :   case EXESS_NON_POSITIVE_INTEGER:
     603                 :            :   case EXESS_NEGATIVE_INTEGER:
     604                 :            :   case EXESS_NON_NEGATIVE_INTEGER:
     605                 :            :   case EXESS_POSITIVE_INTEGER:
     606                 :         29 :     return to_constrained_integer(
     607                 :            :       coercions, in_datatype, in, out_datatype, out_size, out);
     608                 :            : 
     609                 :         20 :   case EXESS_DURATION:
     610                 :            :   case EXESS_YEAR_MONTH_DURATION:
     611                 :            :   case EXESS_DAY_TIME_DURATION:
     612                 :            :   case EXESS_DATE_TIME:
     613                 :            :   case EXESS_DATE_TIME_STAMP:
     614                 :         20 :     break;
     615                 :            : 
     616                 :         17 :   case EXESS_TIME:
     617                 :         17 :     return to_time(coercions, in_datatype, in, (ExessTime*)out);
     618                 :         16 :   case EXESS_DATE:
     619                 :         16 :     return to_date(coercions, in_datatype, in, (ExessDate*)out);
     620                 :            : 
     621                 :          2 :   case EXESS_HEX:
     622                 :            :   case EXESS_BASE64:
     623                 :          2 :     break;
     624                 :            :   }
     625                 :            : 
     626                 :            : #undef FIXED_CASE
     627                 :            : 
     628                 :         24 :   return RESULT(EXESS_UNSUPPORTED, 0U);
     629                 :            : }

Generated by: LCOV version 1.16