LCOV - code coverage report
Current view: top level - src/tds/unittests - convert_bounds.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 87 95 91.6 %
Date: 2025-01-18 11:50:39 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
       2             :  * Copyright (C) 2022  Frediano Ziglio
       3             :  *
       4             :  * This library is free software; you can redistribute it and/or
       5             :  * modify it under the terms of the GNU Library General Public
       6             :  * License as published by the Free Software Foundation; either
       7             :  * version 2 of the License, or (at your option) any later version.
       8             :  *
       9             :  * This library is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             :  * Library General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU Library General Public
      15             :  * License along with this library; if not, write to the
      16             :  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
      17             :  * Boston, MA 02111-1307, USA.
      18             :  */
      19             : 
      20             : /* 
      21             :  * Purpose: test conversion bounds of integers and floating points.
      22             :  */
      23             : #include "common.h"
      24             : #include <assert.h>
      25             : #include <freetds/convert.h>
      26             : #include <freetds/utils/smp.h>
      27             : 
      28             : static TDS_INT convert_and_free(int srctype, const void *src, TDS_UINT srclen, int desttype, CONV_RESULT *cr);
      29             : static bool is_valid(const char *num, int type, CONV_RESULT *cr);
      30             : static double convert_to_float(smp n, int type);
      31             : static int64_t get_float_precision_factor(smp n, int type);
      32             : static void real_test(smp n, int type, bool is_integer);
      33             : static void double_to_string(char *out, double d);
      34             : 
      35             : static TDSCONTEXT *ctx;
      36             : 
      37             : typedef struct {
      38             :         int type;
      39             :         bool is_integer;
      40             : } type_desc;
      41             : 
      42             : /* list of integer and floating point types */
      43             : static const type_desc number_types[] = {
      44             :         { SYBINT1, true },
      45             :         { SYBSINT1, true },
      46             :         { SYBUINT1, true },
      47             :         { SYBINT2, true },
      48             :         { SYBUINT2, true },
      49             :         { SYBINT4, true },
      50             :         { SYBUINT4, true },
      51             :         { SYBINT8, true },
      52             :         { SYBUINT8, true },
      53             :         { SYBMONEY4, true },
      54             :         { SYBMONEY, true },
      55             :         { SYBREAL, false },
      56             :         { SYBFLT8, false },
      57             :         { SYBNUMERIC, true },
      58             :         { 0, false },
      59             : };
      60             : 
      61             : /* list of upper and lower bounds for numbers */
      62             : static const char *bounds[] = {
      63             :         "0",
      64             :         /* 8 bits */
      65             :         "128",
      66             :         "256",
      67             :         /* 16 bits */
      68             :         "0x8000",
      69             :         "0x1'0000",
      70             :         /* money */
      71             :         "214748",
      72             :         "922337203685477",
      73             :         /* 32 bits */
      74             :         "0x8000'0000",
      75             :         "0x1'0000'0000",
      76             :         /* 64 bits */
      77             :         "0x8000'0000'0000'0000",
      78             :         "0x1'0000'0000'0000'0000",
      79             :         NULL,
      80             : };
      81             : 
      82             : int
      83           8 : main(void)
      84             : {
      85             :         const char **bound;
      86             : 
      87           8 :         setbuf(stdout, NULL);
      88           8 :         setbuf(stderr, NULL);
      89             : 
      90           8 :         ctx = tds_alloc_context(NULL);
      91           8 :         assert(ctx);
      92           8 :         if (!ctx->locale->datetime_fmt) {
      93             :                 /* set default in case there's no locale file */
      94           0 :                 ctx->locale->datetime_fmt = strdup(STD_DATETIME_FMT);
      95             :         }
      96             : 
      97             :         /* test all bounds for all types */
      98          88 :         for (bound = bounds; *bound; ++bound) {
      99             :                 const type_desc *t;
     100          88 :                 smp n = smp_from_string(*bound);
     101             : 
     102        1320 :                 for (t = number_types; t->type != 0; ++t)
     103        1232 :                         real_test(n, t->type, t->is_integer);
     104             :         }
     105             : 
     106           8 :         tds_free_context(ctx);
     107             :         return 0;
     108             : }
     109             : 
     110             : static TDS_INT
     111     1414000 : convert_and_free(int srctype, const void *src, TDS_UINT srclen, int desttype, CONV_RESULT *cr)
     112             : {
     113             :         TDS_INT res;
     114             : 
     115     1414000 :         cr->n.precision = 20;
     116     1414000 :         cr->n.scale = 0;
     117             : 
     118     1414000 :         res = tds_convert(ctx, srctype, src, srclen, desttype, cr);
     119     1414000 :         if (res < 0)
     120             :                 return res;
     121             : 
     122      795904 :         switch (desttype) {
     123           0 :         case SYBCHAR: case SYBVARCHAR: case SYBTEXT: case XSYBCHAR: case XSYBVARCHAR:
     124             :         case SYBBINARY: case SYBVARBINARY: case SYBIMAGE: case XSYBBINARY: case XSYBVARBINARY:
     125             :         case SYBLONGBINARY:
     126           0 :                 free(cr->c);
     127           0 :                 break;
     128             :         }
     129             :         return res;
     130             : }
     131             : 
     132             : static void
     133        1232 : real_test(smp n, int srctype, bool is_integer)
     134             : {
     135             :         int i;
     136             : 
     137             :         /* test both positive and negative */
     138        3488 :         for (i = 0; i < 2; ++i) {
     139             :                 const type_desc *t;
     140        2360 :                 char *s_num = smp_to_string(n);
     141             :                 int diff;
     142        2360 :                 int64_t precision_factor = 1;
     143             : 
     144        2360 :                 printf("Testing conversions from %s for number %s\n", tds_prtype(srctype), s_num);
     145        2360 :                 free(s_num);
     146             : 
     147        2360 :                 if (!is_integer)
     148         336 :                         precision_factor = get_float_precision_factor(n, srctype);
     149             : 
     150             :                 /* test bound conversions to all number types */
     151       35400 :                 for (t = number_types; t->type != 0; ++t) {
     152      693840 :                         for (diff = -10; diff <= 10; ++diff) {
     153             :                                 bool valid_src, valid_dest;
     154      693840 :                                 int desttype = t->type;
     155             :                                 CONV_RESULT cr_src, cr_dest;
     156             :                                 int result;
     157      693840 :                                 smp num = smp_add(n, smp_from_int(diff * precision_factor));
     158             : 
     159             :                                 /* convert from char to check for validity */
     160      693840 :                                 s_num = smp_to_string(num);
     161      693840 :                                 valid_src = is_valid(s_num, srctype, &cr_src);
     162             : 
     163             :                                 /* if we were not able to get the source number do not check conversion */
     164      693840 :                                 if (!valid_src) {
     165      333760 :                                         TDS_ZERO_FREE(s_num);
     166      333760 :                                         continue;
     167             :                                 }
     168             : 
     169             :                                 /* NUMERIC has a special encoding for -0 number */
     170      360080 :                                 if (srctype == SYBNUMERIC && i > 0 && smp_is_zero(num))
     171         112 :                                         cr_src.n.array[0] = 1;
     172             : 
     173      360080 :                                 if (is_integer) {
     174      261296 :                                         valid_dest = is_valid(s_num, desttype, NULL);
     175             :                                 } else {
     176             :                                         /* in order to account for possible lost of precision convert from
     177             :                                          * conversion result */
     178       98784 :                                         double d = convert_to_float(smp_from_string(s_num), srctype);
     179             :                                         char out_n[128];
     180             : 
     181       98784 :                                         double_to_string(out_n, d);
     182       98784 :                                         valid_dest = is_valid(out_n, desttype, NULL);
     183             :                                 }
     184             : 
     185             :                                 /* try to convert */
     186      360080 :                                 result = convert_and_free(srctype, &cr_src, 8, desttype, &cr_dest);
     187             : 
     188             :                                 /* conversion should succeed if previously succeeded or viceversa */
     189      360080 :                                 if (valid_dest != (result >= 0)) {
     190           0 :                                         fprintf(stderr, "Unmatch results from %s to %s for %s\n"
     191             :                                                 "results %d (from string) %d (from source type)\n",
     192             :                                                 tds_prtype(srctype), tds_prtype(desttype), s_num,
     193             :                                                 valid_dest, (result >= 0));
     194           0 :                                         TDS_ZERO_FREE(s_num);
     195           0 :                                         exit(1);
     196             :                                 }
     197             : 
     198             :                                 /* if failed it should have been an overflow, types are compatible */
     199      360080 :                                 assert((result >= 0) || result == TDS_CONVERT_OVERFLOW);
     200      360080 :                                 TDS_ZERO_FREE(s_num);
     201             :                         }
     202             :                 }
     203             : 
     204        2360 :                 if (smp_is_zero(n) && srctype != SYBNUMERIC)
     205             :                         break;
     206        2256 :                 n = smp_negate(n);
     207             :         }
     208        1232 : }
     209             : 
     210             : /* check if a number is valid converted to some type */
     211             : static bool
     212     1053920 : is_valid(const char *num, int type, CONV_RESULT *cr)
     213             : {
     214             :         CONV_RESULT dummy_cr;
     215             : 
     216     1053920 :         if (!cr)
     217      360080 :                 cr = &dummy_cr;
     218             : 
     219     1053920 :         return convert_and_free(SYBVARCHAR, num, strlen(num), type, cr) >= 0;
     220             : }
     221             : 
     222             : /* convert multiple precision to a floating number of a specific type */
     223             : static double
     224      102208 : convert_to_float(smp n, int type)
     225             : {
     226      102208 :         double ret = smp_to_double(n);
     227             : 
     228      102208 :         switch (type) {
     229       51928 :         case SYBREAL:
     230       51928 :                 return (TDS_REAL) ret;
     231             :         case SYBFLT8:
     232             :                 return (TDS_FLOAT) ret;
     233             :         default:
     234           0 :                 assert(!"wrong type");
     235             :         }
     236             :         return 0;
     237             : }
     238             : 
     239             : /* Compute a factor in order to see the change to the original number expressed
     240             :  * with a given floating point number.
     241             :  * This is due to the limited precision of a floating point type.
     242             :  * If we use numbers with 20 digits but the precision is just 10 we need
     243             :  * increment in the order of 10 digits to see changes in the floating point
     244             :  * numbers if we increment them.
     245             :  */
     246             : static int64_t
     247         336 : get_float_precision_factor(smp n, int type)
     248             : {
     249             :         int shift;
     250         336 :         const double orig = convert_to_float(n, type);
     251        2688 :         for (shift = 0; ; ++shift) {
     252        2688 :                 smp diff = smp_from_int(((int64_t) 1) << shift);
     253             : 
     254             :                 /* returns only if both n+diff and n-diff change the original number */
     255        3088 :                 if (orig != convert_to_float(smp_add(n, diff), type) &&
     256         400 :                     orig != convert_to_float(smp_sub(n, diff), type))
     257             :                         break;
     258             :         }
     259         336 :         return ((int64_t) 1) << (shift ? shift - 1 : shift);
     260             : }
     261             : 
     262             : /* Convert a double to a string.
     263             :  * This is done to avoid a bug in some old versions of Visual Studio (maybe CRT).
     264             :  * It's assumed that the double contains a number with no digits after the unit.
     265             :  */
     266             : static void
     267       98784 : double_to_string(char *out, double d)
     268             : {
     269       98784 :         int bdigits = 0;
     270       98784 :         double exp = 1.f;
     271       98784 :         bool negative = false;
     272             :         char *s;
     273       98784 :         smp n = smp_zero;
     274             : 
     275       98784 :         if (d < 0) {
     276       49280 :                 negative = true;
     277       49280 :                 d = -d;
     278             :         }
     279     3011232 :         while (exp <= d) {
     280     2912448 :                 exp *= 2.f;
     281     2912448 :                 ++bdigits;
     282             :         }
     283     3011232 :         for (; bdigits >= 0; --bdigits) {
     284     3011232 :                 n = smp_add(n,n);
     285     3011232 :                 if (exp <= d) {
     286     1118656 :                         n = smp_add(n, smp_one);
     287     1118656 :                         d -= exp;
     288             :                 }
     289     3011232 :                 exp *= 0.5f;
     290             :         }
     291       98784 :         if (negative)
     292       49280 :                 n = smp_negate(n);
     293       98784 :         s = smp_to_string(n);
     294       98784 :         strcpy(out, s);
     295       98784 :         free(s);
     296       98784 : }

Generated by: LCOV version 1.13