]> source.dussan.org Git - rspamd.git/commitdiff
[Fix] Further fixes to printing of the FP numbers
authorVsevolod Stakhov <vsevolod@highsecure.ru>
Sun, 19 May 2019 19:34:20 +0000 (20:34 +0100)
committerVsevolod Stakhov <vsevolod@highsecure.ru>
Wed, 22 May 2019 13:39:08 +0000 (14:39 +0100)
contrib/fpconv/fpconv.c
contrib/fpconv/fpconv.h
src/libutil/printf.c

index 71818828f370036591ccef6655e370e4874e4347..f8b601a55d9e0a5ae8a45d3eed4ffa78c47ce726 100644 (file)
@@ -1,5 +1,6 @@
 #include <stdbool.h>
 #include <string.h>
+#include <sys/param.h>
 
 #include "fpconv.h"
 #include "powers.h"
@@ -199,54 +200,32 @@ static int grisu2 (double d, char *digits, int *K) {
        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);
 
@@ -286,19 +265,153 @@ static int emit_digits (char *digits, int ndigits, char *dest, int K, bool 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);
@@ -332,17 +445,23 @@ static int filter_special (double fp, char *dest) {
        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) {
@@ -354,7 +473,8 @@ int fpconv_dtoa (double d, char dest[24], bool scientific) {
        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;
 }
index 0bc270b3155c30730b5226fe9d5eafc20e7f853c..8c07c1368cacc1619f6c3351ec8b101909dbb921 100644 (file)
@@ -1,6 +1,7 @@
 #ifndef FPCONV_H
 #define FPCONV_H
 
+#define FPCONV_BUFLEN 32
 /* Fast and accurate double to string conversion based on Florian Loitsch's
  * Grisu-algorithm[1].
  *
@@ -26,7 +27,8 @@
  *
  */
 
-int fpconv_dtoa(double fp, char dest[32], bool scientific);
+int fpconv_dtoa(double fp, char dest[FPCONV_BUFLEN], unsigned precision,
+               bool scientific);
 
 #endif
 
index 6739fe142122aa6faa262cd2db5f1a5b54870ee2..9db2bfe7d0ba6865a5b8590da20aa19caa42c813 100644 (file)
@@ -591,7 +591,8 @@ rspamd_vprintf_common (rspamd_printf_append_func func,
        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;
@@ -839,9 +840,9 @@ rspamd_vprintf_common (rspamd_printf_append_func func,
                                        }
 
                                        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--;
@@ -948,32 +949,7 @@ rspamd_vprintf_common (rspamd_printf_append_func func,
 
                        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);
 
@@ -981,39 +957,14 @@ rspamd_vprintf_common (rspamd_printf_append_func func,
 
                        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);
 
@@ -1021,7 +972,7 @@ rspamd_vprintf_common (rspamd_printf_append_func func,
 
                        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;
@@ -1036,12 +987,12 @@ rspamd_vprintf_common (rspamd_printf_append_func func,
 
                        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);
                                }