Line data Source code
1 : /* 2 : * SPDX-License-Identifier: MPL-2.0 3 : * 4 : * This Source Code Form is subject to the terms of the Mozilla Public 5 : * License, v. 2.0. If a copy of the MPL was not distributed with this 6 : * file, You can obtain one at http://mozilla.org/MPL/2.0/. 7 : * 8 : * Copyright 2024 MonetDB Foundation; 9 : * Copyright August 2008 - 2023 MonetDB B.V.; 10 : * Copyright 1997 - July 2008 CWI. 11 : */ 12 : 13 : #include "monetdb_config.h" 14 : 15 : #include "sql_decimal.h" 16 : 17 : 18 : DEC_TPE 19 9510 : decimal_from_str(const char *dec, int* digits, int* scale, int* has_errors) 20 : { 21 : 22 : #ifdef HAVE_HGE 23 9510 : const hge max0 = GDK_hge_max / 10, max1 = GDK_hge_max % 10; 24 : #else 25 : const lng max0 = GDK_lng_max / 10, max1 = GDK_lng_max % 10; 26 : #endif 27 : 28 9510 : assert(digits); 29 9510 : assert(scale); 30 9510 : assert(has_errors); 31 : 32 9510 : DEC_TPE res = 0; 33 9510 : *has_errors = 0; 34 : 35 9510 : int _digits = 0; 36 9510 : int _scale = 0; 37 : 38 : // preceding whitespace: 39 9510 : int neg = 0; 40 9552 : while(isspace((unsigned char) *dec)) 41 42 : dec++; 42 : 43 : // optional sign: 44 9510 : if (*dec == '-') { 45 175 : neg = 1; 46 175 : dec++; 47 9335 : } else if (*dec == '+') { 48 8 : dec++; 49 : } 50 : 51 : // optional fractional separator first opportunity 52 9510 : if (*dec == '.') { // case: (+|-).456 53 46 : fractional_sep_first_opp: 54 3630 : dec++; 55 3630 : goto trailing_digits; 56 : } 57 : 58 : // preceding_digits: 59 9464 : if (!isdigit((unsigned char) *dec)) { 60 66 : *has_errors = 1; 61 66 : goto end_state; 62 : } 63 9435 : while (*dec == '0'){ 64 : // skip leading zeros in preceding digits, e.g. '0004563.1234' => '4563.1234' 65 3621 : dec++; 66 3621 : if (*dec == '.') { 67 3584 : _digits = 1; // case: 0.xyz the zero. the single preceding zero counts for one digit by convention. 68 3584 : goto fractional_sep_first_opp; 69 : } 70 : } 71 24851 : for (; *dec && (isdigit((unsigned char) *dec)); dec++) { 72 19037 : if (res > max0 || (res == max0 && *dec - '0' > max1)) { 73 0 : *has_errors = 1; 74 0 : return 0; 75 : } 76 19037 : res *= 10; 77 19037 : res += *dec - '0'; 78 19037 : _digits++; 79 : } 80 : 81 : // optional fractional separator second opportunity 82 5814 : if (*dec == '.') // case: (+|-)123.(456) 83 5696 : dec++; 84 : else // case: (+|-)123 85 118 : goto trailing_whitespace; 86 : 87 9326 : trailing_digits: 88 9326 : if (!isdigit((unsigned char) *dec)) 89 7 : goto trailing_whitespace; 90 40471 : for (; *dec && (isdigit((unsigned char) *dec)); dec++) { 91 31295 : if (res > max0 || (res == max0 && *dec - '0' > max1)) { 92 143 : *has_errors = 1; 93 143 : return 0; 94 : } 95 31152 : res *= 10; 96 31152 : res += *dec - '0'; 97 31152 : _scale++; 98 : } 99 9176 : _digits += _scale; 100 : 101 9301 : trailing_whitespace: 102 9315 : while(isspace((unsigned char) *dec)) 103 14 : dec++; 104 : 105 9301 : end_state: 106 : /* When the string cannot be parsed up to and including the null terminator, 107 : * the string is an invalid decimal representation. */ 108 9367 : if (*dec != 0) 109 73 : *has_errors = 1; 110 : 111 9367 : *digits = _digits; 112 9367 : *scale = _scale; 113 : 114 9367 : if (neg) 115 175 : return -res; 116 : else 117 : return res; 118 : } 119 : 120 : char * 121 : #ifdef HAVE_HGE 122 24 : decimal_to_str(sql_allocator *sa, hge v, sql_subtype *t) 123 : #else 124 : decimal_to_str(sql_allocator *sa, lng v, sql_subtype *t) 125 : #endif 126 : { 127 24 : char buf[64]; 128 24 : unsigned int scale = t->scale, i; 129 24 : int cur = 63, neg = (v<0), done = 0; 130 : 131 24 : if (v<0) v = -v; 132 : 133 24 : buf[cur--] = 0; 134 24 : if (scale){ 135 138 : for (i=0; i<scale; i++) { 136 114 : buf[cur--] = (char) (v%10 + '0'); 137 114 : v /= 10; 138 : } 139 24 : buf[cur--] = '.'; 140 : } 141 44 : while (v) { 142 20 : buf[cur--] = (char ) (v%10 + '0'); 143 20 : v /= 10; 144 20 : done = 1; 145 : } 146 24 : if (!done) 147 10 : buf[cur--] = '0'; 148 24 : if (neg) 149 0 : buf[cur--] = '-'; 150 24 : assert(cur >= -1); 151 24 : return sa_strdup(sa, buf+cur+1); 152 : } 153 : 154 : #ifdef HAVE_HGE 155 : extern hge 156 : #else 157 : extern lng 158 : #endif 159 22 : scale2value(int scale) 160 : { 161 : #ifdef HAVE_HGE 162 22 : hge val = 1; 163 : #else 164 : lng val = 1; 165 : #endif 166 : 167 22 : if (scale < 0) 168 : scale = -scale; 169 62 : for (; scale; scale--) { 170 40 : val = val * 10; 171 : } 172 22 : return val; 173 : }