#include <stdbool.h>
#include <string.h>
+#include <sys/param.h>
#include "fpconv.h"
#include "powers.h"
return generate_digits (&w, &upper, &lower, digits, K);
}
-static int emit_digits (char *digits, int ndigits, char *dest, int K, bool neg,
- bool scientific) {
- int exp = absv(K + ndigits - 1);
-
- /* write plain integer */
- if (K >= 0 && (exp < (ndigits + 7))) {
- memcpy(dest, digits, ndigits);
- memset(dest + ndigits, '0', K);
-
- return ndigits + K;
- }
-
- /* write decimal w/o scientific notation */
- if (!scientific || (K < 0 && (K > -7 || exp < 4))) {
- int offset = ndigits - absv(K);
- /* fp < 1.0 -> write leading zero */
- if (offset <= 0) {
- offset = -offset;
- dest[0] = '0';
- dest[1] = '.';
-
- /* We have up to 21 characters in output available */
- if (offset + ndigits <= 21) {
- memset(dest + 2, '0', offset);
- memcpy(dest + offset + 2, digits, ndigits);
+static inline int emit_integer (char *digits, int ndigits,
+ char *dest, int K, bool neg,
+ unsigned precision)
+{
+ char *d = dest;
- return ndigits + 2 + offset;
- }
- else {
- goto scientific_fallback;
- }
+ memcpy (d, digits, ndigits);
+ d += ndigits;
+ memset (d, '0', K);
+ d += K;
- /* fp > 1.0 */
- }
- else {
- /* Overflow check */
- if (ndigits <= 23) {
- memcpy(dest, digits, offset);
- dest[offset] = '.';
- memcpy(dest + offset + 1, digits + offset, ndigits - offset);
- return ndigits + 1;
- }
+ precision = MIN(precision, FPCONV_BUFLEN - (ndigits + K + 1));
- goto scientific_fallback;
- }
+ if (precision) {
+ *d++ = '.';
+ memset (d, '0', precision);
+ d += precision;
}
- scientific_fallback:
+ return d - dest;
+}
+
+static inline int emit_scientific_digits (char *digits, int ndigits,
+ char *dest, int K, bool neg,
+ unsigned precision, int exp)
+{
/* write decimal w/ scientific notation */
ndigits = minv(ndigits, 18 - neg);
return idx;
}
-static int filter_special (double fp, char *dest) {
+static inline int emit_fixed_digits (char *digits, int ndigits,
+ char *dest, int K, bool neg,
+ unsigned precision, int exp)
+{
+ int offset = ndigits - absv(K), to_print;
+ /* fp < 1.0 -> write leading zero */
+ if (K < 0) {
+ if (offset <= 0) {
+ if (precision) {
+ if (-offset >= precision) {
+ /* Just print 0.[0]{precision} */
+ dest[0] = '0';
+ dest[1] = '.';
+ memset(dest + 2, '0', precision);
+
+ return precision + 2;
+ }
+
+ to_print = MAX(ndigits - offset, precision);
+ }
+ else {
+ to_print = ndigits - offset;
+ }
+
+ if (to_print <= FPCONV_BUFLEN - 3) {
+ offset = -offset;
+ dest[0] = '0';
+ dest[1] = '.';
+ memset(dest + 2, '0', offset);
+
+ if (precision) {
+ /* The case where offset > precision is covered previously */
+ precision -= offset;
+
+ if (precision <= ndigits) {
+ /* Truncate or leave as is */
+ memcpy(dest + offset + 2, digits, precision);
+
+ return precision + 2 + offset;
+ }
+ else {
+ /* Expand */
+ memcpy(dest + offset + 2, digits, ndigits);
+ precision -= ndigits;
+ memset(dest + offset + 2 + ndigits, '0', precision);
+
+ return ndigits + 2 + offset + precision;
+ }
+ }
+ else {
+ memcpy(dest + offset + 2, digits, ndigits);
+ }
+
+ return ndigits + 2 + offset;
+ }
+ else {
+ return emit_scientific_digits (digits, ndigits, dest, K, neg, precision, exp);
+ }
+ }
+ else {
+ /*
+ * fp > 1.0, if offset > 0 then we have less digits than
+ * fp exponent, so we need to switch to scientific notation to
+ * display number at least more or less precisely
+ */
+ if (offset > 0 && ndigits <= FPCONV_BUFLEN - 3) {
+ char *d = dest;
+ memcpy(d, digits, offset);
+ d += offset;
+ *d++ = '.';
+
+ ndigits -= offset;
+
+ if (precision) {
+ if (ndigits >= precision) {
+ /* Truncate or leave as is */
+ memcpy(d, digits + offset, precision);
+ d += precision;
+ }
+ else {
+ /* Expand */
+ memcpy(d, digits + offset, ndigits);
+ precision -= ndigits;
+ d += ndigits;
+
+ /* Check if we have enough bufspace */
+ if ((d - dest) + precision <= FPCONV_BUFLEN) {
+ memset (d, '0', precision);
+ d += precision;
+ }
+ else {
+ memset (d, '0', FPCONV_BUFLEN - (d - dest));
+ d += FPCONV_BUFLEN - (d - dest);
+ }
+ }
+ }
+ else {
+ memcpy(d, digits + offset, ndigits);
+ d += ndigits;
+ }
+
+ return d - dest;
+ }
+ }
+ }
+
+ return emit_scientific_digits (digits, ndigits, dest, K, neg, precision, exp);
+}
+
+static int emit_digits (char *digits, int ndigits, char *dest, int K, bool neg,
+ unsigned precision, bool scientific)
+{
+ int exp = absv(K + ndigits - 1);
+
+ /* write plain integer */
+ if (K >= 0 && (exp < (ndigits + 7))) {
+ return emit_integer (digits, ndigits, dest, K, neg, precision);
+ }
+
+ /* write decimal w/o scientific notation */
+ if (!scientific || (K < 0 && (K > -7 || exp < 4))) {
+ return emit_fixed_digits (digits, ndigits, dest, K, neg, precision, exp);
+ }
+
+ return emit_scientific_digits (digits, ndigits, dest, K, neg, precision, exp);
+}
+
+static int filter_special (double fp, char *dest, unsigned precision)
+{
int nchars = 3;
+ char *d = dest;
if (fp == 0.0) {
if (get_dbits (fp) & signmask) {
- dest[0] = '-';
- dest[1] = '0';
- return 2;
+ *d++ = '-';
+ *d++ = '0';
}
else {
- dest[0] = '0';
- return 1;
+ *d++ = '0';
}
+
+ if (precision) {
+ *d ++ = '.';
+ memset (d, '0', precision);
+ }
+
+ return d - dest + precision;
}
uint64_t bits = get_dbits (fp);
return nchars;
}
-int fpconv_dtoa (double d, char dest[24], bool scientific) {
+int
+fpconv_dtoa (double d, char dest[FPCONV_BUFLEN],
+ unsigned precision, bool scientific)
+{
char digits[18];
int str_len = 0;
bool neg = false;
+ if (precision > FPCONV_BUFLEN - 5) {
+ precision = FPCONV_BUFLEN - 5;
+ }
- int spec = filter_special (d, dest + str_len);
+ int spec = filter_special (d, dest, precision);
if (spec) {
- return str_len + spec;
+ return spec;
}
if (get_dbits (d) & signmask) {
int K = 0;
int ndigits = grisu2 (d, digits, &K);
- str_len += emit_digits (digits, ndigits, dest + str_len, K, neg, scientific);
+ str_len += emit_digits (digits, ndigits, dest + str_len, K, neg, precision,
+ scientific);
return str_len;
}
const gchar *fmt,
va_list args)
{
- gchar zero, numbuf[G_ASCII_DTOSTR_BUF_SIZE], dtoabuf[32], *p, *last, c;
+ gchar zero, numbuf[G_ASCII_DTOSTR_BUF_SIZE], dtoabuf[32], *p, *last;
+ guchar c;
const gchar *buf_start = fmt, *fmt_start = NULL;
gint d;
gdouble f;
}
while (slen) {
- hexbuf[0] = hex == 2 ? _HEX[(*p >> 4) & 0xf] :
- _hex[(*p >> 4) & 0xf];
- hexbuf[1] = hex == 2 ? _HEX[*p & 0xf] : _hex[*p & 0xf];
+ hexbuf[0] = hex == 2 ? _HEX[(*p >> 4u) & 0xfu] :
+ _hex[(*p >> 4u) & 0xfu];
+ hexbuf[1] = hex == 2 ? _HEX[*p & 0xfu] : _hex[*p & 0xfu];
RSPAMD_PRINTF_APPEND_BUF (hexbuf, 2);
p++;
slen--;
case 'f':
f = (gdouble) va_arg (args, double);
- slen = fpconv_dtoa (f, dtoabuf, false);
-
- if (frac_width != 0) {
- const gchar *dot_pos = memchr (dtoabuf, '.', slen);
-
- if (dot_pos) {
- if (frac_width < (slen - ((dot_pos - dtoabuf) + 1))) {
- /* Truncate */
- slen = (dot_pos - dtoabuf) + 1 + /* xxx. */
- frac_width; /* .yyy */
- }
- else if (frac_width + dot_pos + 1 < dtoabuf + sizeof (dtoabuf)) {
- /* Expand */
- frac_width -= slen - ((dot_pos - dtoabuf) + 1);
- memset (dtoabuf + slen, '0', frac_width);
- slen += frac_width;
- }
- }
- else {
- /* Expand */
- frac_width = MIN (frac_width, sizeof (dtoabuf) - slen - 1);
- dtoabuf[slen ++] = '.';
- memset (dtoabuf + slen, '0', frac_width);
- slen += frac_width;
- }
- }
+ slen = fpconv_dtoa (f, dtoabuf, frac_width, false);
RSPAMD_PRINTF_APPEND (dtoabuf, slen);
case 'g':
f = (gdouble) va_arg (args, double);
- slen = fpconv_dtoa (f, dtoabuf, true);
+ slen = fpconv_dtoa (f, dtoabuf, 0, true);
RSPAMD_PRINTF_APPEND (dtoabuf, slen);
continue;
case 'F':
f = (gdouble) va_arg (args, long double);
- slen = fpconv_dtoa (f, dtoabuf, false);
-
- if (frac_width != 0) {
- const gchar *dot_pos = memchr (dtoabuf, '.', slen);
-
- if (dot_pos) {
- if (frac_width < (slen - ((dot_pos - dtoabuf) + 1))) {
- /* Truncate */
- slen = (dot_pos - dtoabuf) + 1 + /* xxx. */
- frac_width; /* .yyy */
- }
- else if (frac_width + dot_pos + 1 < dtoabuf + sizeof (dtoabuf)) {
- /* Expand */
- frac_width -= slen - ((dot_pos - dtoabuf) + 1);
- memset (dtoabuf + slen, '0', frac_width);
- slen += frac_width;
- }
- }
- else {
- /* Expand */
- frac_width = MIN (frac_width, sizeof (dtoabuf) - slen - 1);
- dtoabuf[slen ++] = '.';
- memset (dtoabuf + slen, '0', frac_width);
- slen += frac_width;
- }
- }
+ slen = fpconv_dtoa (f, dtoabuf, frac_width, false);
RSPAMD_PRINTF_APPEND (dtoabuf, slen);
case 'G':
f = (gdouble) va_arg (args, long double);
- slen = fpconv_dtoa (f, dtoabuf, true);
+ slen = fpconv_dtoa (f, dtoabuf, 0, true);
RSPAMD_PRINTF_APPEND (dtoabuf, slen);
continue;
case 'c':
c = va_arg (args, gint);
- c &= 0xff;
+ c &= 0xffu;
if (G_UNLIKELY (hex)) {
gchar hexbuf[2];
- hexbuf[0] = hex == 2 ? _HEX[(c >> 4) & 0xf] :
- _hex[(c >> 4) & 0xf];
- hexbuf[1] = hex == 2 ? _HEX[c & 0xf] : _hex[c & 0xf];
+ hexbuf[0] = hex == 2 ? _HEX[(c >> 4u) & 0xfu] :
+ _hex[(c >> 4u) & 0xfu];
+ hexbuf[1] = hex == 2 ? _HEX[c & 0xfu] : _hex[c & 0xfu];
RSPAMD_PRINTF_APPEND (hexbuf, 2);
}