(___printf_fp): Minor optimization.
[kopensolaris-gnu/glibc.git] / stdio-common / printf_fp.c
index 1f0334f..32df06a 100644 (file)
@@ -1,36 +1,33 @@
 /* Floating point output for `printf'.
-   Copyright (C) 1995-1999, 2000 Free Software Foundation, Inc.
+   Copyright (C) 1995-2003, 2006, 2007 Free Software Foundation, Inc.
+
    This file is part of the GNU C Library.
    Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995.
 
    The GNU C Library is free software; you can redistribute it and/or
-   modify it under the terms of the GNU Library General Public License as
-   published by the Free Software Foundation; either version 2 of the
-   License, or (at your option) any later version.
+   modify it under the terms of the GNU Lesser General Public
+   License as published by the Free Software Foundation; either
+   version 2.1 of the License, or (at your option) any later version.
 
    The GNU C Library is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
-   Library General Public License for more details.
+   Lesser General Public License for more details.
 
-   You should have received a copy of the GNU Library General Public
-   License along with the GNU C Library; see the file COPYING.LIB.  If not,
-   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-   Boston, MA 02111-1307, USA.  */
+   You should have received a copy of the GNU Lesser General Public
+   License along with the GNU C Library; if not, write to the Free
+   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
+   02111-1307 USA.  */
 
 /* The gmp headers need some configuration frobs.  */
 #define HAVE_ALLOCA 1
 
-#ifdef USE_IN_LIBIO
-#  include <libioP.h>
-#else
-#  include <stdio.h>
-#endif
+#include <libioP.h>
 #include <alloca.h>
 #include <ctype.h>
 #include <float.h>
 #include <gmp-mparam.h>
-#include <stdlib/gmp.h>
+#include <gmp.h>
 #include <stdlib/gmp-impl.h>
 #include <stdlib/longlong.h>
 #include <stdlib/fpioconst.h>
 #include <stdlib.h>
 #include <wchar.h>
 
+#ifdef COMPILE_WPRINTF
+# define CHAR_T        wchar_t
+#else
+# define CHAR_T        char
+#endif
+
+#include "_i18n_number.h"
+
 #ifndef NDEBUG
 # define NDEBUG                        /* Undefine this for debugging assertions.  */
 #endif
 
 /* This defines make it possible to use the same code for GNU C library and
    the GNU I/O library.         */
-#ifdef USE_IN_LIBIO
-# define PUT(f, s, n) _IO_sputn (f, s, n)
-# define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : _IO_padn (f, c, n))
+#define PUT(f, s, n) _IO_sputn (f, s, n)
+#define PAD(f, c, n) (wide ? _IO_wpadn (f, c, n) : INTUSE(_IO_padn) (f, c, n))
 /* We use this file GNU C library and GNU I/O library. So make
    names equal.         */
-# undef putc
-# define putc(c, f) (wide \
-                     ? _IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))
-# define size_t     _IO_size_t
-# define FILE       _IO_FILE
-#else  /* ! USE_IN_LIBIO */
-# define PUT(f, s, n) fwrite (s, 1, n, f)
-# define PAD(f, c, n) __printf_pad (f, c, n)
-ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c.  */
-#endif /* USE_IN_LIBIO */
+#undef putc
+#define putc(c, f) (wide \
+                   ? (int)_IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f))
+#define size_t     _IO_size_t
+#define FILE        _IO_FILE
 \f
 /* Macros for doing the actual output.  */
 
@@ -73,7 +72,11 @@ ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c.  */
     {                                                                        \
       register const int outc = (ch);                                        \
       if (putc (outc, fp) == EOF)                                            \
-       return -1;                                                            \
+       {                                                                     \
+         if (buffer_malloced)                                                \
+           free (wbuffer);                                                   \
+         return -1;                                                          \
+       }                                                                     \
       ++done;                                                                \
     } while (0)
 
@@ -84,7 +87,11 @@ ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c.  */
       if (len > 20)                                                          \
        {                                                                     \
          if (PUT (fp, wide ? (const char *) wptr : ptr, outlen) != outlen)   \
-           return -1;                                                        \
+           {                                                                 \
+             if (buffer_malloced)                                            \
+               free (wbuffer);                                               \
+             return -1;                                                      \
+           }                                                                 \
          ptr += outlen;                                                      \
          done += outlen;                                                     \
        }                                                                     \
@@ -103,7 +110,11 @@ ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c.  */
   do                                                                         \
     {                                                                        \
       if (PAD (fp, ch, len) != len)                                          \
-       return -1;                                                            \
+       {                                                                     \
+         if (buffer_malloced)                                                \
+           free (wbuffer);                                                   \
+         return -1;                                                          \
+       }                                                                     \
       done += len;                                                           \
     }                                                                        \
   while (0)
@@ -120,7 +131,8 @@ ssize_t __printf_pad __P ((FILE *, char pad, int n)); /* In vfprintf.c.  */
 #define MPN_GE(u,v) \
   (u##size > v##size || (u##size == v##size && __mpn_cmp (u, v, u##size) >= 0))
 
-extern int __isinfl (long double), __isnanl (long double);
+extern int __isinfl_internal (long double) attribute_hidden;
+extern int __isnanl_internal (long double) attribute_hidden;
 
 extern mp_size_t __mpn_extract_double (mp_ptr res_ptr, mp_size_t size,
                                       int *expt, int *is_neg,
@@ -139,9 +151,9 @@ static wchar_t *group_number (wchar_t *buf, wchar_t *bufend,
 
 
 int
-__printf_fp (FILE *fp,
-            const struct printf_info *info,
-            const void *const *args)
+___printf_fp (FILE *fp,
+             const struct printf_info *info,
+             const void *const *args)
 {
   /* The floating-point value to output.  */
   union
@@ -200,6 +212,13 @@ __printf_fp (FILE *fp,
   /* Nonzero if this is output on a wide character stream.  */
   int wide = info->wide;
 
+  /* Buffer in which we produce the output.  */
+  wchar_t *wbuffer = NULL;
+  /* Flag whether wbuffer is malloc'ed or not.  */
+  int buffer_malloced = 0;
+
+  auto wchar_t hack_digit (void);
+
   wchar_t hack_digit (void)
     {
       mp_limb_t hi;
@@ -209,8 +228,7 @@ __printf_fp (FILE *fp,
       else if (scalesize == 0)
        {
          hi = frac[fracsize - 1];
-         cy = __mpn_mul_1 (frac, frac, fracsize - 1, 10);
-         frac[fracsize - 1] = cy;
+         frac[fracsize - 1] = __mpn_mul_1 (frac, frac, fracsize - 1, 10);
        }
       else
        {
@@ -234,9 +252,9 @@ __printf_fp (FILE *fp,
                }
            }
 
-         cy = __mpn_mul_1 (frac, frac, fracsize, 10);
-         if (cy != 0)
-           frac[fracsize++] = cy;
+         mp_limb_t _cy = __mpn_mul_1 (frac, frac, fracsize, 10);
+         if (_cy != 0)
+           frac[fracsize++] = _cy;
        }
 
       return L'0' + hi;
@@ -252,11 +270,17 @@ __printf_fp (FILE *fp,
   else
     {
       decimal = _NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT);
+      if (*decimal == '\0')
+       decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
       decimalwc = _NL_CURRENT_WORD (LC_MONETARY,
                                    _NL_MONETARY_DECIMAL_POINT_WC);
+      if (decimalwc == L'\0')
+       decimalwc = _NL_CURRENT_WORD (LC_NUMERIC,
+                                     _NL_NUMERIC_DECIMAL_POINT_WC);
     }
   /* The decimal point character must not be zero.  */
-  assert (*decimal != L'\0');
+  assert (*decimal != '\0');
+  assert (decimalwc != L'\0');
 
   if (info->group)
     {
@@ -355,6 +379,7 @@ __printf_fp (FILE *fp,
       /* Check for special values: not a number or infinity.  */
       if (__isnan (fpnum.dbl))
        {
+         is_neg = 0;
          if (isupper (info->spec))
            {
              special = "NAN";
@@ -365,10 +390,10 @@ __printf_fp (FILE *fp,
              special = "nan";
              wspecial = L"nan";
            }
-         is_neg = 0;
        }
       else if (__isinf (fpnum.dbl))
        {
+         is_neg = fpnum.dbl < 0;
          if (isupper (info->spec))
            {
              special = "INF";
@@ -379,7 +404,6 @@ __printf_fp (FILE *fp,
              special = "inf";
              wspecial = L"inf";
            }
-         is_neg = fpnum.dbl < 0;
        }
       else
        {
@@ -424,7 +448,9 @@ __printf_fp (FILE *fp,
      would be really big it could lead to memory problems.  */
   {
     mp_size_t bignum_size = ((ABS (exponent) + BITS_PER_MP_LIMB - 1)
-                            / BITS_PER_MP_LIMB + 4) * sizeof (mp_limb_t);
+                            / BITS_PER_MP_LIMB
+                            + (LDBL_MANT_DIG / BITS_PER_MP_LIMB > 2 ? 8 : 4))
+                           * sizeof (mp_limb_t);
     frac = (mp_limb_t *) alloca (bignum_size);
     tmp = (mp_limb_t *) alloca (bignum_size);
     scale = (mp_limb_t *) alloca (bignum_size);
@@ -485,6 +511,9 @@ __printf_fp (FILE *fp,
                              &__tens[powers->arrayoff],
                              tmpsize * sizeof (mp_limb_t));
                      MPN_ZERO (tmp, _FPIO_CONST_SHIFT);
+                     /* Adjust exponent, as scaleexpo will be this much
+                        bigger too.  */
+                     exponent += _FPIO_CONST_SHIFT * BITS_PER_MP_LIMB;
                    }
                  else
 #endif
@@ -778,17 +807,18 @@ __printf_fp (FILE *fp,
 
   {
     int width = info->width;
-    wchar_t *wbuffer, *wstartp, *wcp;
-    int buffer_malloced;
+    wchar_t *wstartp, *wcp;
     int chars_needed;
     int expscale;
     int intdig_max, intdig_no = 0;
-    int fracdig_min, fracdig_max, fracdig_no = 0;
+    int fracdig_min;
+    int fracdig_max;
     int dig_max;
     int significant;
     int ngroups = 0;
+    char spec = _tolower (info->spec);
 
-    if (_tolower (info->spec) == 'e')
+    if (spec == 'e')
       {
        type = info->spec;
        intdig_max = 1;
@@ -798,10 +828,12 @@ __printf_fp (FILE *fp,
        dig_max = INT_MAX;              /* Unlimited.  */
        significant = 1;                /* Does not matter here.  */
       }
-    else if (info->spec == 'f')
+    else if (spec == 'f')
       {
        type = 'f';
        fracdig_min = fracdig_max = info->prec < 0 ? 6 : info->prec;
+       dig_max = INT_MAX;              /* Unlimited.  */
+       significant = 1;                /* Does not matter here.  */
        if (expsign == 0)
          {
            intdig_max = exponent + 1;
@@ -813,8 +845,6 @@ __printf_fp (FILE *fp,
            intdig_max = 1;
            chars_needed = 1 + 1 + fracdig_max;
          }
-       dig_max = INT_MAX;              /* Unlimited.  */
-       significant = 1;                /* Does not matter here.  */
       }
     else
       {
@@ -835,9 +865,12 @@ __printf_fp (FILE *fp,
            type = 'f';
            intdig_max = expsign == 0 ? exponent + 1 : 0;
            fracdig_max = dig_max - intdig_max;
-           /* We need space for the significant digits and perhaps for
-              leading zeros when < 1.0.  Pessimistic guess: dig_max.  */
-           chars_needed = dig_max + dig_max + 1;
+           /* We need space for the significant digits and perhaps
+              for leading zeros when < 1.0.  The number of leading
+              zeros can be as many as would be required for
+              exponential notation with a negative two-digit
+              exponent, which is 4.  */
+           chars_needed = dig_max + 1 + 4;
          }
        fracdig_min = info->alt ? fracdig_max : 0;
        significant = 0;                /* We count significant digits.  */
@@ -855,8 +888,8 @@ __printf_fp (FILE *fp,
        it is possible that we need two more characters in front of all the
        other output.  If the amount of memory we have to allocate is too
        large use `malloc' instead of `alloca'.  */
-    buffer_malloced = chars_needed > 5000;
-    if (buffer_malloced)
+    buffer_malloced = ! __libc_use_alloca (chars_needed * 2 * sizeof (wchar_t));
+    if (__builtin_expect (buffer_malloced, 0))
       {
        wbuffer = (wchar_t *) malloc ((2 + chars_needed) * sizeof (wchar_t));
        if (wbuffer == NULL)
@@ -892,12 +925,13 @@ __printf_fp (FILE *fp,
       }
 
     /* Generate the needed number of fractional digits.         */
+    int fracdig_no = 0;
     while (fracdig_no < fracdig_min
           || (fracdig_no < fracdig_max && (fracsize > 1 || frac[0] != 0)))
       {
        ++fracdig_no;
        *wcp = hack_digit ();
-       if (*wcp != L'0')
+       if (*wcp++ != L'0')
          significant = 1;
        else if (significant == 0)
          {
@@ -905,7 +939,6 @@ __printf_fp (FILE *fp,
            if (fracdig_min > 0)
              ++fracdig_min;
          }
-       ++wcp;
       }
 
     /* Do rounding.  */
@@ -914,7 +947,9 @@ __printf_fp (FILE *fp,
       {
        wchar_t *wtp = wcp;
 
-       if (digit == L'5' && (*(wcp - 1) & 1) == 0)
+       if (digit == L'5'
+           && ((*(wcp - 1) != decimalwc && (*(wcp - 1) & 1) == 0)
+               || ((*(wcp - 1) == decimalwc && (*(wcp - 2) & 1) == 0))))
          {
            /* This is the critical case.        */
            if (fracsize == 1 && frac[0] == 0)
@@ -940,7 +975,7 @@ __printf_fp (FILE *fp,
            /* Process fractional digits.  Terminate if not rounded or
               radix character is reached.  */
            while (*--wtp != decimalwc && *wtp == L'9')
-             *wtp = '0';
+             *wtp = L'0';
            if (*wtp != decimalwc)
              /* Round up.  */
              (*wtp)++;
@@ -1072,19 +1107,15 @@ __printf_fp (FILE *fp,
     {
       char *buffer = NULL;
       char *cp = NULL;
+      char *tmpptr;
 
       if (! wide)
        {
          /* Create the single byte string.  */
-         const char *decimal;
          size_t decimal_len;
          size_t thousands_sep_len;
          wchar_t *copywc;
 
-         if (info->extra == 0)
-           decimal = _NL_CURRENT (LC_NUMERIC, DECIMAL_POINT);
-         else
-           decimal = _NL_CURRENT (LC_MONETARY, MON_DECIMAL_POINT);
          decimal_len = strlen (decimal);
 
          if (thousands_sep == NULL)
@@ -1092,13 +1123,16 @@ __printf_fp (FILE *fp,
          else
            thousands_sep_len = strlen (thousands_sep);
 
-         if (buffer_malloced)
+         if (__builtin_expect (buffer_malloced, 0))
            {
              buffer = (char *) malloc (2 + chars_needed + decimal_len
                                        + ngroups * thousands_sep_len);
              if (buffer == NULL)
-               /* Signal an error to the caller.  */
-               return -1;
+               {
+                 /* Signal an error to the caller.  */
+                 free (wbuffer);
+                 return -1;
+               }
            }
          else
            buffer = (char *) alloca (2 + chars_needed + decimal_len
@@ -1117,10 +1151,20 @@ __printf_fp (FILE *fp,
              *cp++ = (char) *copywc;
        }
 
-      PRINT (buffer, wstartp, wide ? wcp - wstartp : cp - buffer);
+      tmpptr = buffer;
+      if (__builtin_expect (info->i18n, 0))
+        {
+#ifdef COMPILE_WPRINTF
+         wstartp = _i18n_number_rewrite (wstartp, wcp);
+#else
+         tmpptr = _i18n_number_rewrite (tmpptr, cp);
+#endif
+        }
+
+      PRINT (tmpptr, wstartp, wide ? wcp - wstartp : cp - tmpptr);
 
       /* Free the memory if necessary.  */
-      if (buffer_malloced)
+      if (__builtin_expect (buffer_malloced, 0))
        {
          free (buffer);
          free (wbuffer);
@@ -1132,6 +1176,8 @@ __printf_fp (FILE *fp,
   }
   return done;
 }
+ldbl_hidden_def (___printf_fp, __printf_fp)
+ldbl_strong_alias (___printf_fp, __printf_fp)
 \f
 /* Return the number of extra grouping characters that will be inserted
    into a number with INTDIG_MAX integer digits.  */
@@ -1186,8 +1232,8 @@ group_number (wchar_t *buf, wchar_t *bufend, unsigned int intdig_no,
     return bufend;
 
   /* Move the fractional part down.  */
-  wmemmove (buf + intdig_no + ngroups, buf + intdig_no,
-           bufend - (buf + intdig_no));
+  __wmemmove (buf + intdig_no + ngroups, buf + intdig_no,
+             bufend - (buf + intdig_no));
 
   p = buf + intdig_no + ngroups - 1;
   do