LCOV - code coverage report
Current view: top level - src - decimal.c (source / functions) Hit Total Coverage
Test: Code coverage Lines: 60 60 100.0 %
Date: 2026-07-14 21:28:07 Functions: 3 3 100.0 %
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed Branches: 30 32 93.8 %

           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 "number_utils.h"
       6                 :            : #include "parse_decimal.h"
       7                 :            : #include "read_utils.h"
       8                 :            : #include "result.h"
       9                 :            : #include "write_utils.h"
      10                 :            : 
      11                 :            : #include <exess/exess.h>
      12                 :            : 
      13                 :            : #include <assert.h>
      14                 :            : #include <stddef.h>
      15                 :            : 
      16                 :            : typedef enum {
      17                 :            :   EXESS_POINT_AFTER,   ///< Decimal point is after all significant digits
      18                 :            :   EXESS_POINT_BEFORE,  ///< Decimal point is before all significant digits
      19                 :            :   EXESS_POINT_BETWEEN, ///< Decimal point is between significant digits
      20                 :            : } ExessPointLocation;
      21                 :            : 
      22                 :            : typedef struct {
      23                 :            :   ExessPointLocation point_loc;      ///< Location of decimal point
      24                 :            :   unsigned           n_zeros_before; ///< Number of extra zeros before point
      25                 :            :   unsigned           n_zeros_after;  ///< Number of extra zeros after point
      26                 :            : } DecimalMetrics;
      27                 :            : 
      28                 :            : static DecimalMetrics
      29                 :       4773 : decimal_metrics(const BigDecimal count)
      30                 :            : {
      31                 :       4773 :   DecimalMetrics metrics = {EXESS_POINT_AFTER, 0U, 0U};
      32                 :            : 
      33         [ +  + ]:       4773 :   if (count.expt >= (int)count.n_digits) {
      34                 :       2441 :     metrics.point_loc      = EXESS_POINT_AFTER;
      35                 :       2441 :     metrics.n_zeros_before = (unsigned)count.expt - count.n_digits;
      36                 :       2441 :     metrics.n_zeros_after  = 1U;
      37         [ +  + ]:       2332 :   } else if (count.expt < 0) {
      38                 :       2247 :     metrics.point_loc      = EXESS_POINT_BEFORE;
      39                 :       2247 :     metrics.n_zeros_before = 1U;
      40                 :       2247 :     metrics.n_zeros_after  = (unsigned)(-count.expt);
      41                 :            :   } else {
      42                 :         85 :     metrics.point_loc = EXESS_POINT_BETWEEN;
      43                 :            :   }
      44                 :            : 
      45                 :       4773 :   return metrics;
      46                 :            : }
      47                 :            : 
      48                 :            : EXESS_NONBLOCKING ExessVariableResult
      49                 :       4311 : exess_read_decimal(const char* const str,
      50                 :            :                    const size_t      out_size,
      51                 :            :                    void* const       out)
      52                 :            : {
      53         [ +  + ]:       4311 :   if (out_size < sizeof(BigDecimal) + 2U) {
      54                 :          5 :     return VRESULT(EXESS_NO_SPACE, 0U, sizeof(BigDecimal) + 2U);
      55                 :            :   }
      56                 :            : 
      57                 :       4306 :   char* const  digits      = (char*)out + sizeof(BigDecimal);
      58                 :       4306 :   const size_t digits_size = out_size - sizeof(BigDecimal) - 1U;
      59                 :            : 
      60                 :       4306 :   const size_t      i = skip_whitespace(str);
      61                 :       4306 :   BigDecimal        d = {EXESS_NAN, 0U, 0};
      62                 :       4306 :   const ExessResult r = parse_decimal(str + i, &d, digits_size, digits);
      63                 :            : 
      64                 :            :   // Trim trailing zeros
      65         [ +  + ]:       4306 :   if (r.status <= EXESS_LOSS) {
      66   [ +  +  +  + ]:       5740 :     while (d.n_digits > 1 && digits[d.n_digits - 1] == '0') {
      67                 :       1442 :       --d.n_digits;
      68                 :            :     }
      69                 :            :   }
      70                 :            : 
      71                 :       4306 :   *(BigDecimal*)out  = d;    // Set output header
      72                 :       4306 :   digits[d.n_digits] = '\0'; // Terminate digits for safety/convenience
      73                 :            : 
      74                 :       4306 :   return VRESULT(r.status, i + r.count, sizeof(d) + d.n_digits + 1U);
      75                 :            : }
      76                 :            : 
      77                 :            : EXESS_NONBLOCKING ExessResult
      78                 :       4795 : exess_write_decimal(const size_t      value_size,
      79                 :            :                     const void* const value,
      80                 :            :                     const size_t      buf_size,
      81                 :            :                     char* const       buf)
      82                 :            : {
      83                 :       4795 :   const BigDecimal  decimal = *(const BigDecimal*)value;
      84                 :       4795 :   const char* const digits  = (const char*)value + sizeof(decimal);
      85                 :            : 
      86         [ +  + ]:       4795 :   if (decimal.kind < EXESS_NEGATIVE_ZERO ||
      87         [ +  + ]:       4789 :       value_size < sizeof(decimal) + decimal.n_digits) {
      88                 :          7 :     return end_write(EXESS_BAD_VALUE, buf_size, buf, 0);
      89                 :            :   }
      90                 :            : 
      91         [ +  + ]:       4788 :   if (decimal.kind == EXESS_NEGATIVE_ZERO) {
      92                 :          8 :     return write_special(4, "-0.0", buf_size, buf);
      93                 :            :   }
      94                 :            : 
      95         [ +  + ]:       4780 :   if (decimal.kind == EXESS_POSITIVE_ZERO) {
      96                 :          7 :     return write_special(3, "0.0", buf_size, buf);
      97                 :            :   }
      98                 :            : 
      99                 :       4773 :   size_t i = 0;
     100         [ +  + ]:       4773 :   if (decimal.kind == EXESS_NEGATIVE) {
     101                 :         12 :     i += write_char('-', buf_size, buf, i);
     102                 :            :   }
     103                 :            : 
     104                 :       4773 :   const DecimalMetrics metrics = decimal_metrics(decimal);
     105                 :       4773 :   const unsigned       n_zeros = metrics.n_zeros_before + metrics.n_zeros_after;
     106                 :       4773 :   const unsigned       n_total = decimal.n_digits + 1U + n_zeros;
     107         [ +  + ]:       4773 :   if (i + n_total >= buf_size) {
     108                 :        660 :     return end_write(EXESS_NO_SPACE, buf_size, buf, i + n_total);
     109                 :            :   }
     110                 :            : 
     111         [ +  + ]:       4113 :   if (metrics.point_loc == EXESS_POINT_AFTER) {
     112                 :       2128 :     i += write_string(decimal.n_digits, digits, buf_size, buf, i);
     113                 :       2128 :     i += write_zeros(metrics.n_zeros_before, buf_size, buf, i);
     114                 :       2128 :     i += write_string(2, ".0", buf_size, buf, i);
     115         [ +  + ]:       1985 :   } else if (metrics.point_loc == EXESS_POINT_BEFORE) {
     116                 :       1919 :     i += write_string(2, "0.", buf_size, buf, i);
     117                 :       1919 :     i += write_zeros(metrics.n_zeros_after, buf_size, buf, i);
     118                 :       1919 :     i += write_string(decimal.n_digits, digits, buf_size, buf, i);
     119                 :            :   } else {
     120         [ -  + ]:         66 :     assert(metrics.point_loc == EXESS_POINT_BETWEEN);
     121         [ -  + ]:         66 :     assert(decimal.expt >= 0);
     122                 :         66 :     const size_t n_before = (size_t)decimal.expt;
     123                 :         66 :     const size_t n_after  = decimal.n_digits - n_before;
     124                 :         66 :     i += write_string(n_before, digits, buf_size, buf, i);
     125                 :         66 :     i += write_char('.', buf_size, buf, i);
     126                 :         66 :     i += write_string(n_after, digits + n_before, buf_size, buf, i);
     127                 :            :   }
     128                 :            : 
     129                 :       4113 :   return end_write(EXESS_SUCCESS, buf_size, buf, i);
     130                 :            : }

Generated by: LCOV version 1.16