/** * @return <0 if error */ TDS_INT tds_numeric_to_string(const TDS_NUMERIC * numeric, char *s) { const unsigned char *number; unsigned int packet[sizeof(numeric->array) / 2]; unsigned int *pnum, *packet_start; unsigned int *const packet_end = packet + TDS_VECTOR_SIZE(packet); unsigned int packet10k[(MAXPRECISION + 3) / 4]; unsigned int *p; int num_bytes; unsigned int remainder, n, i, m; /* a bit of debug */ #if ENABLE_EXTRA_CHECKS memset(packet, 0x55, sizeof(packet)); memset(packet10k, 0x55, sizeof(packet10k)); #endif if (numeric->precision < 1 || numeric->precision > MAXPRECISION || numeric->scale > numeric->precision) return TDS_CONVERT_FAIL; /* set sign */ if (numeric->array[0] == 1) *s++ = '-'; /* put number in a 16bit array */ number = numeric->array; num_bytes = tds_numeric_bytes_per_prec[numeric->precision]; n = num_bytes - 1; pnum = packet_end; for (; n > 1; n -= 2) *--pnum = TDS_GET_UA2BE(&number[n - 1]); if (n == 1) *--pnum = number[n]; while (!*pnum) { ++pnum; if (pnum == packet_end) { *s++ = '0'; if (numeric->scale) { *s++ = '.'; i = numeric->scale; do { *s++ = '0'; } while (--i); } *s = 0; return 1; } } packet_start = pnum; /* transform 2^16 base number in 10^4 base number */ for (p = packet10k + TDS_VECTOR_SIZE(packet10k); packet_start != packet_end;) { pnum = packet_start; n = *pnum; remainder = n % 10000u; if (!(*pnum++ = (n / 10000u))) packet_start = pnum; for (; pnum != packet_end; ++pnum) { n = remainder * (256u * 256u) + *pnum; remainder = n % 10000u; *pnum = n / 10000u; } *--p = remainder; } /* transform to 10 base number and output */ i = 4 * (unsigned int)((packet10k + TDS_VECTOR_SIZE(packet10k)) - p); /* current digit */ /* skip leading zeroes */ n = 1000; remainder = *p; while (remainder < n) n /= 10, --i; if (i <= numeric->scale) { *s++ = '0'; *s++ = '.'; m = i; while (m < numeric->scale) *s++ = '0', ++m; } for (;;) { *s++ = (remainder / n) + '0'; --i; remainder %= n; n /= 10; if (!n) { n = 1000; if (++p == packet10k + TDS_VECTOR_SIZE(packet10k)) break; remainder = *p; } if (i == numeric->scale) *s++ = '.'; } *s = 0; return 1; }
TDS_INT tds_numeric_change_prec_scale(TDS_NUMERIC * numeric, unsigned char new_prec, unsigned char new_scale) { static const TDS_WORD factors[] = { 1, 10, 100, 1000, 10000, #ifdef HAVE_INT64 100000, 1000000, 10000000, 100000000, 1000000000 #endif }; TDS_WORD packet[(sizeof(numeric->array) - 1) / sizeof(TDS_WORD)]; unsigned int i, packet_len; int scale_diff, bytes; if (numeric->precision < 1 || numeric->precision > 77 || numeric->scale > numeric->precision) return TDS_CONVERT_FAIL; if (new_prec < 1 || new_prec > 77 || new_scale > new_prec) return TDS_CONVERT_FAIL; scale_diff = new_scale - numeric->scale; if (scale_diff == 0 && new_prec >= numeric->precision) { i = tds_numeric_bytes_per_prec[new_prec] - tds_numeric_bytes_per_prec[numeric->precision]; if (i > 0) { memmove(numeric->array + 1 + i, numeric->array + 1, sizeof(numeric->array) - 1 - i); memset(numeric->array + 1, 0, i); } numeric->precision = new_prec; return sizeof(TDS_NUMERIC); } /* package number */ bytes = tds_numeric_bytes_per_prec[numeric->precision] - 1; i = 0; do { /* * note that if bytes are smaller we have a small buffer * overflow in numeric->array however is not a problem * cause overflow occurs in numeric and number is fixed below */ #ifndef HAVE_INT64 packet[i] = TDS_GET_UA2BE(&numeric->array[bytes-1]); #else packet[i] = TDS_GET_UA4BE(&numeric->array[bytes-3]); #endif ++i; } while ( (bytes -= sizeof(TDS_WORD)) > 0); /* fix last packet */ if (bytes < 0) packet[i-1] &= 0xffffffffu >> (8 * -bytes); while (i > 1 && packet[i-1] == 0) --i; packet_len = i; if (scale_diff >= 0) { /* check overflow before multiply */ if (tds_packet_check_overflow(packet, packet_len, new_prec - scale_diff)) return TDS_CONVERT_OVERFLOW; if (scale_diff == 0) { i = tds_numeric_bytes_per_prec[numeric->precision] - tds_numeric_bytes_per_prec[new_prec]; if (i > 0) memmove(numeric->array + 1, numeric->array + 1 + i, sizeof(numeric->array) - 1 - i); numeric->precision = new_prec; return sizeof(TDS_NUMERIC); } /* multiply */ do { /* multiply by at maximun TDS_WORD_DDIGIT */ unsigned int n = scale_diff > TDS_WORD_DDIGIT ? TDS_WORD_DDIGIT : scale_diff; TDS_WORD factor = factors[n]; TDS_WORD carry = 0; scale_diff -= n; for (i = 0; i < packet_len; ++i) { TDS_DWORD n = packet[i] * ((TDS_DWORD) factor) + carry; packet[i] = (TDS_WORD) n; carry = n >> (8 * sizeof(TDS_WORD)); } /* here we can expand number safely cause we know that it can't overflow */ if (carry) packet[packet_len++] = carry; } while (scale_diff > 0); } else { /* check overflow */ if (new_prec - scale_diff < numeric->precision)