diff options
author | Florian Weimer <fweimer@redhat.com> | 2022-12-19 18:56:54 +0100 |
---|---|---|
committer | Florian Weimer <fweimer@redhat.com> | 2022-12-19 18:56:54 +0100 |
commit | e88b9f0e5cc50cab57a299dc7efe1a4eb385161d (patch) | |
tree | 2b733d221cc4247e16aef46150c2fc8153ad6db4 /stdio-common/printf_fp.c | |
parent | 46378560e056300623364669de2405a7182b064f (diff) | |
download | glibc-e88b9f0e5cc50cab57a299dc7efe1a4eb385161d.zip glibc-e88b9f0e5cc50cab57a299dc7efe1a4eb385161d.tar.gz glibc-e88b9f0e5cc50cab57a299dc7efe1a4eb385161d.tar.bz2 |
stdio-common: Convert vfprintf and related functions to buffers
vfprintf is entangled with vfwprintf (of course), __printf_fp,
__printf_fphex, __vstrfmon_l_internal, and the strfrom family of
functions. The latter use the internal snprintf functionality,
so vsnprintf is converted as well.
The simples conversion is __printf_fphex, followed by
__vstrfmon_l_internal and __printf_fp, and finally
__vfprintf_internal and __vfwprintf_internal. __vsnprintf_internal
and strfrom* are mostly consuming the new interfaces, so they
are comparatively simple.
__printf_fp is a public symbol, so the FILE *-based interface
had to preserved.
The __printf_fp rewrite does not change the actual binary-to-decimal
conversion algorithm, and digits are still not emitted directly to
the target buffer. However, the staging buffer now uses bytes
instead of wide characters, and one buffer copy is eliminated.
The changes are at least performance-neutral in my testing.
Floating point printing and snprintf improved measurably, so that
this Lua script
for i=1,5000000 do
print(i, i * math.pi)
end
runs about 5% faster for me. To preserve fprintf performance for
a simple "%d" format, this commit has some logic changes under
LABEL (unsigned_number) to avoid additional function calls. There
are certainly some very easy performance improvements here: binary,
octal and hexadecimal formatting can easily avoid the temporary work
buffer (the number of digits can be computed ahead-of-time using one
of the __builtin_clz* built-ins). Decimal formatting can use a
specialized version of _itoa_word for base 10.
The existing (inconsistent) width handling between strfmon and printf
is preserved here. __print_fp_buffer_1 would have to use
__translated_number_width to achieve ISO conformance for printf.
Test expectations in libio/tst-vtables-common.c are adjusted because
the internal staging buffer merges all virtual function calls into
one.
In general, stack buffer usage is greatly reduced, particularly for
unbuffered input streams. __printf_fp can still use a large buffer
in binary128 mode for %g, though.
Reviewed-by: Adhemerval Zanella <adhemerval.zanella@linaro.org>
Diffstat (limited to 'stdio-common/printf_fp.c')
-rw-r--r-- | stdio-common/printf_fp.c | 736 |
1 files changed, 312 insertions, 424 deletions
diff --git a/stdio-common/printf_fp.c b/stdio-common/printf_fp.c index 3a5560f..e1abb1f 100644 --- a/stdio-common/printf_fp.c +++ b/stdio-common/printf_fp.c @@ -41,90 +41,12 @@ #include <wchar.h> #include <stdbool.h> #include <rounding-mode.h> +#include <printf_buffer.h> +#include <printf_buffer_to_file.h> +#include <grouping_iterator.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 #include <assert.h> -#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)) -#undef putc -#define putc(c, f) (wide \ - ? (int)_IO_putwc_unlocked (c, f) : _IO_putc_unlocked (c, f)) - - -/* Macros for doing the actual output. */ - -#define outchar(ch) \ - do \ - { \ - const int outc = (ch); \ - if (putc (outc, fp) == EOF) \ - { \ - if (buffer_malloced) \ - { \ - free (buffer); \ - free (wbuffer); \ - } \ - return -1; \ - } \ - ++done; \ - } while (0) - -#define PRINT(ptr, wptr, len) \ - do \ - { \ - size_t outlen = (len); \ - if (len > 20) \ - { \ - if (PUT (fp, wide ? (const char *) wptr : ptr, outlen) != outlen) \ - { \ - if (buffer_malloced) \ - { \ - free (buffer); \ - free (wbuffer); \ - } \ - return -1; \ - } \ - ptr += outlen; \ - done += outlen; \ - } \ - else \ - { \ - if (wide) \ - while (outlen-- > 0) \ - outchar (*wptr++); \ - else \ - while (outlen-- > 0) \ - outchar (*ptr++); \ - } \ - } while (0) - -#define PADN(ch, len) \ - do \ - { \ - if (PAD (fp, ch, len) != len) \ - { \ - if (buffer_malloced) \ - { \ - free (buffer); \ - free (wbuffer); \ - } \ - return -1; \ - } \ - done += len; \ - } \ - while (0) - /* We use the GNU MP library to handle large numbers. An MP variable occupies a varying number of entries in its array. We keep @@ -145,10 +67,6 @@ extern mp_size_t __mpn_extract_long_double (mp_ptr res_ptr, mp_size_t size, long double value); -static wchar_t *group_number (wchar_t *buf, wchar_t *bufend, - unsigned int intdig_no, const char *grouping, - wchar_t thousands_sep, int ngroups); - struct hack_digit_param { /* Sign of the exponent. */ @@ -165,7 +83,7 @@ struct hack_digit_param MPN_VAR(tmp); }; -static wchar_t +static char hack_digit (struct hack_digit_param *p) { mp_limb_t hi; @@ -197,7 +115,7 @@ hack_digit (struct hack_digit_param *p) /* We're not prepared for an mpn variable with zero limbs. */ p->fracsize = 1; - return L'0' + hi; + return '0' + hi; } } @@ -206,13 +124,22 @@ hack_digit (struct hack_digit_param *p) p->frac[p->fracsize++] = _cy; } - return L'0' + hi; + return '0' + hi; } -int -__printf_fp_l (FILE *fp, locale_t loc, - const struct printf_info *info, - const void *const *args) +/* Version that performs grouping (if INFO->group && THOUSANDS_SEP != 0), + but not i18n digit translation. + + The output buffer is always multibyte (not wide) at this stage. + Wide conversion and i18n digit translation happen later, with a + temporary buffer. To prepare for that, THOUSANDS_SEP_LENGTH is the + final length of the thousands separator. */ +static void +__printf_fp_buffer_1 (struct __printf_buffer *buf, locale_t loc, + char thousands_sep, char decimal, + unsigned int thousands_sep_length, + const struct printf_info *info, + const void *const *args) { /* The floating-point value to output. */ union @@ -225,18 +152,11 @@ __printf_fp_l (FILE *fp, locale_t loc, } fpnum; - /* Locale-dependent representation of decimal point. */ - const char *decimal; - wchar_t decimalwc; - - /* Locale-dependent thousands separator and grouping specification. */ - const char *thousands_sep = NULL; - wchar_t thousands_sepwc = 0; - const char *grouping; - /* "NaN" or "Inf" for the special cases. */ const char *special = NULL; - const wchar_t *wspecial = NULL; + + /* Used to determine grouping rules. */ + int lc_category = info->extra ? LC_MONETARY : LC_NUMERIC; /* When _Float128 is enabled in the library and ABI-distinct from long double, we need mp_limbs enough for any of them. */ @@ -256,90 +176,16 @@ __printf_fp_l (FILE *fp, locale_t loc, /* Sign of float number. */ int is_neg = 0; - /* Counter for number of written characters. */ - int done = 0; - /* General helper (carry limb). */ mp_limb_t cy; - /* 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; - char *buffer = NULL; + char *wbuffer = NULL; /* Flag whether wbuffer and buffer are malloc'ed or not. */ int buffer_malloced = 0; p.expsign = 0; - /* Figure out the decimal point character. */ - if (info->extra == 0) - { - decimal = _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT); - decimalwc = _nl_lookup_word - (loc, LC_NUMERIC, _NL_NUMERIC_DECIMAL_POINT_WC); - } - else - { - decimal = _nl_lookup (loc, LC_MONETARY, MON_DECIMAL_POINT); - if (*decimal == '\0') - decimal = _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT); - decimalwc = _nl_lookup_word (loc, LC_MONETARY, - _NL_MONETARY_DECIMAL_POINT_WC); - if (decimalwc == L'\0') - decimalwc = _nl_lookup_word (loc, LC_NUMERIC, - _NL_NUMERIC_DECIMAL_POINT_WC); - } - /* The decimal point character must not be zero. */ - assert (*decimal != '\0'); - assert (decimalwc != L'\0'); - - if (info->group) - { - if (info->extra == 0) - grouping = _nl_lookup (loc, LC_NUMERIC, GROUPING); - else - grouping = _nl_lookup (loc, LC_MONETARY, MON_GROUPING); - - if (*grouping <= 0 || *grouping == CHAR_MAX) - grouping = NULL; - else - { - /* Figure out the thousands separator character. */ - if (wide) - { - if (info->extra == 0) - thousands_sepwc = _nl_lookup_word - (loc, LC_NUMERIC, _NL_NUMERIC_THOUSANDS_SEP_WC); - else - thousands_sepwc = - _nl_lookup_word (loc, LC_MONETARY, - _NL_MONETARY_THOUSANDS_SEP_WC); - } - else - { - if (info->extra == 0) - thousands_sep = _nl_lookup (loc, LC_NUMERIC, THOUSANDS_SEP); - else - thousands_sep = _nl_lookup - (loc, LC_MONETARY, MON_THOUSANDS_SEP); - } - - if ((wide && thousands_sepwc == L'\0') - || (! wide && *thousands_sep == '\0')) - grouping = NULL; - else if (thousands_sepwc == L'\0') - /* If we are printing multibyte characters and there is a - multibyte representation for the thousands separator, - we must ensure the wide character thousands separator - is available, even if it is fake. */ - thousands_sepwc = 0xfffffffe; - } - } - else - grouping = NULL; - #define PRINTF_FP_FETCH(FLOAT, VAR, SUFFIX, MANT_DIG) \ { \ (VAR) = *(const FLOAT *) args[0]; \ @@ -349,29 +195,17 @@ __printf_fp_l (FILE *fp, locale_t loc, { \ is_neg = signbit (VAR); \ if (isupper (info->spec)) \ - { \ - special = "NAN"; \ - wspecial = L"NAN"; \ - } \ + special = "NAN"; \ else \ - { \ - special = "nan"; \ - wspecial = L"nan"; \ - } \ + special = "nan"; \ } \ else if (isinf (VAR)) \ { \ is_neg = signbit (VAR); \ if (isupper (info->spec)) \ - { \ - special = "INF"; \ - wspecial = L"INF"; \ - } \ + special = "INF"; \ else \ - { \ - special = "inf"; \ - wspecial = L"inf"; \ - } \ + special = "inf"; \ } \ else \ { \ @@ -405,22 +239,22 @@ __printf_fp_l (FILE *fp, locale_t loc, --width; width -= 3; - if (!info->left && width > 0) - PADN (' ', width); + if (!info->left) + __printf_buffer_pad (buf, ' ', width); if (is_neg) - outchar ('-'); + __printf_buffer_putc (buf, '-'); else if (info->showsign) - outchar ('+'); + __printf_buffer_putc (buf, '+'); else if (info->space) - outchar (' '); + __printf_buffer_putc (buf, ' '); - PRINT (special, wspecial, 3); + __printf_buffer_puts (buf, special); - if (info->left && width > 0) - PADN (' ', width); + if (info->left) + __printf_buffer_pad (buf, ' ', width); - return done; + return; } @@ -829,7 +663,7 @@ __printf_fp_l (FILE *fp, locale_t loc, { int width = info->width; - wchar_t *wstartp, *wcp; + char *wstartp, *wcp; size_t chars_needed; int expscale; int intdig_max, intdig_no = 0; @@ -837,7 +671,6 @@ __printf_fp_l (FILE *fp, locale_t loc, int fracdig_max; int dig_max; int significant; - int ngroups = 0; char spec = _tolower (info->spec); if (spec == 'e') @@ -898,38 +731,32 @@ __printf_fp_l (FILE *fp, locale_t loc, significant = 0; /* We count significant digits. */ } - if (grouping) - { - /* Guess the number of groups we will make, and thus how - many spaces we need for separator characters. */ - ngroups = __guess_grouping (intdig_max, grouping); - /* Allocate one more character in case rounding increases the - number of groups. */ - chars_needed += ngroups + 1; - } - /* Allocate buffer for output. We need two more because while rounding 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'. */ - if (__builtin_expect (chars_needed >= (size_t) -1 / sizeof (wchar_t) - 2 - || chars_needed < fracdig_max, 0)) + if (__glibc_unlikely (chars_needed >= (size_t) -1 - 2 + || chars_needed < fracdig_max)) { /* Some overflow occurred. */ __set_errno (ERANGE); - return -1; + __printf_buffer_mark_failed (buf); + return; } - size_t wbuffer_to_alloc = (2 + chars_needed) * sizeof (wchar_t); + size_t wbuffer_to_alloc = 2 + chars_needed; buffer_malloced = ! __libc_use_alloca (wbuffer_to_alloc); if (__builtin_expect (buffer_malloced, 0)) { - wbuffer = (wchar_t *) malloc (wbuffer_to_alloc); + wbuffer = malloc (wbuffer_to_alloc); if (wbuffer == NULL) - /* Signal an error to the caller. */ - return -1; + { + /* Signal an error to the caller. */ + __printf_buffer_mark_failed (buf); + return; + } } else - wbuffer = (wchar_t *) alloca (wbuffer_to_alloc); + wbuffer = alloca (wbuffer_to_alloc); wcp = wstartp = wbuffer + 2; /* Let room for rounding. */ /* Do the real work: put digits in allocated buffer. */ @@ -945,15 +772,15 @@ __printf_fp_l (FILE *fp, locale_t loc, if (info->alt || fracdig_min > 0 || (fracdig_max > 0 && (p.fracsize > 1 || p.frac[0] != 0))) - *wcp++ = decimalwc; + *wcp++ = decimal; } else { /* |fp| < 1.0 and the selected p.type is 'f', so put "0." in the buffer. */ - *wcp++ = L'0'; + *wcp++ = '0'; --p.exponent; - *wcp++ = decimalwc; + *wcp++ = decimal; } /* Generate the needed number of fractional digits. */ @@ -964,7 +791,7 @@ __printf_fp_l (FILE *fp, locale_t loc, { ++fracdig_no; *wcp = hack_digit (&p); - if (*wcp++ != L'0') + if (*wcp++ != '0') significant = 1; else if (significant == 0) { @@ -975,10 +802,10 @@ __printf_fp_l (FILE *fp, locale_t loc, } /* Do rounding. */ - wchar_t last_digit = wcp[-1] != decimalwc ? wcp[-1] : wcp[-2]; - wchar_t next_digit = hack_digit (&p); + char last_digit = wcp[-1] != decimal ? wcp[-1] : wcp[-2]; + char next_digit = hack_digit (&p); bool more_bits; - if (next_digit != L'0' && next_digit != L'5') + if (next_digit != '0' && next_digit != '5') more_bits = true; else if (p.fracsize == 1 && p.frac[0] == 0) /* Rest of the number is zero. */ @@ -995,29 +822,29 @@ __printf_fp_l (FILE *fp, locale_t loc, else more_bits = true; int rounding_mode = get_rounding_mode (); - if (round_away (is_neg, (last_digit - L'0') & 1, next_digit >= L'5', + if (round_away (is_neg, (last_digit - '0') & 1, next_digit >= '5', more_bits, rounding_mode)) { - wchar_t *wtp = wcp; + char *wtp = wcp; if (fracdig_no > 0) { /* Process fractional digits. Terminate if not rounded or radix character is reached. */ int removed = 0; - while (*--wtp != decimalwc && *wtp == L'9') + while (*--wtp != decimal && *wtp == '9') { - *wtp = L'0'; + *wtp = '0'; ++removed; } if (removed == fracdig_min && added_zeros > 0) --added_zeros; - if (*wtp != decimalwc) + if (*wtp != decimal) /* Round up. */ (*wtp)++; else if (__builtin_expect (spec == 'g' && p.type == 'f' && info->alt && wtp == wstartp + 1 - && wstartp[0] == L'0', + && wstartp[0] == '0', 0)) /* This is a special case: the rounded number is 1.0, the format is 'g' or 'G', and the alternative format @@ -1025,14 +852,14 @@ __printf_fp_l (FILE *fp, locale_t loc, --added_zeros; } - if (fracdig_no == 0 || *wtp == decimalwc) + if (fracdig_no == 0 || *wtp == decimal) { /* Round the integer digits. */ - if (*(wtp - 1) == decimalwc) + if (*(wtp - 1) == decimal) --wtp; - while (--wtp >= wstartp && *wtp == L'9') - *wtp = L'0'; + while (--wtp >= wstartp && *wtp == '9') + *wtp = '0'; if (wtp >= wstartp) /* Round up. */ @@ -1056,13 +883,13 @@ __printf_fp_l (FILE *fp, locale_t loc, /* This is the case where for p.type %g the number fits really in the range for %f output but after rounding the number of digits is too big. */ - *--wstartp = decimalwc; - *--wstartp = L'1'; + *--wstartp = decimal; + *--wstartp = '1'; if (info->alt || fracdig_no > 0) { /* Overwrite the old radix character. */ - wstartp[intdig_no + 2] = L'0'; + wstartp[intdig_no + 2] = '0'; ++fracdig_no; } @@ -1077,7 +904,7 @@ __printf_fp_l (FILE *fp, locale_t loc, { /* We can simply add another another digit before the radix. */ - *--wstartp = L'1'; + *--wstartp = '1'; ++intdig_no; } @@ -1094,28 +921,16 @@ __printf_fp_l (FILE *fp, locale_t loc, } /* Now remove unnecessary '0' at the end of the string. */ - while (fracdig_no > fracdig_min + added_zeros && *(wcp - 1) == L'0') + while (fracdig_no > fracdig_min + added_zeros && *(wcp - 1) == '0') { --wcp; --fracdig_no; } /* If we eliminate all fractional digits we perhaps also can remove the radix character. */ - if (fracdig_no == 0 && !info->alt && *(wcp - 1) == decimalwc) + if (fracdig_no == 0 && !info->alt && *(wcp - 1) == decimal) --wcp; - if (grouping) - { - /* Rounding might have changed the number of groups. We allocated - enough memory but we need here the correct number of groups. */ - if (intdig_no != intdig_max) - ngroups = __guess_grouping (intdig_no, grouping); - - /* Add in separator characters, overwriting the same buffer. */ - wcp = group_number (wstartp, wcp, intdig_no, grouping, thousands_sepwc, - ngroups); - } - /* Write the p.exponent if it is needed. */ if (p.type != 'f') { @@ -1125,12 +940,12 @@ __printf_fp_l (FILE *fp, locale_t loc, really smaller than -4, which requires the 'e'/'E' format. But after rounding the number has an p.exponent of -4. */ assert (wcp >= wstartp + 1); - assert (wstartp[0] == L'1'); - __wmemcpy (wstartp, L"0.0001", 6); - wstartp[1] = decimalwc; + assert (wstartp[0] == '1'); + memcpy (wstartp, "0.0001", 6); + wstartp[1] = decimal; if (wcp >= wstartp + 2) { - __wmemset (wstartp + 6, L'0', wcp - (wstartp + 2)); + memset (wstartp + 6, '0', wcp - (wstartp + 2)); wcp += 4; } else @@ -1138,8 +953,8 @@ __printf_fp_l (FILE *fp, locale_t loc, } else { - *wcp++ = (wchar_t) p.type; - *wcp++ = p.expsign ? L'-' : L'+'; + *wcp++ = p.type; + *wcp++ = p.expsign ? '-' : '+'; /* Find the magnitude of the p.exponent. */ expscale = 10; @@ -1148,220 +963,293 @@ __printf_fp_l (FILE *fp, locale_t loc, if (p.exponent < 10) /* Exponent always has at least two digits. */ - *wcp++ = L'0'; + *wcp++ = '0'; else do { expscale /= 10; - *wcp++ = L'0' + (p.exponent / expscale); + *wcp++ = '0' + (p.exponent / expscale); p.exponent %= expscale; } while (expscale > 10); - *wcp++ = L'0' + p.exponent; + *wcp++ = '0' + p.exponent; } } + struct grouping_iterator iter; + if (thousands_sep != '\0' && info->group) + __grouping_iterator_init (&iter, lc_category, loc, intdig_no); + else + iter.separators = 0; + /* Compute number of characters which must be filled with the padding character. */ if (is_neg || info->showsign || info->space) --width; + /* To count bytes, we would have to use __translated_number_width + for info->i18n && !info->wide. See bug 28943. */ width -= wcp - wstartp; + /* For counting bytes, we would have to multiply by + thousands_sep_length. */ + width -= iter.separators; - if (!info->left && info->pad != '0' && width > 0) - PADN (info->pad, width); + if (!info->left && info->pad != '0') + __printf_buffer_pad (buf, info->pad, width); if (is_neg) - outchar ('-'); + __printf_buffer_putc (buf, '-'); else if (info->showsign) - outchar ('+'); + __printf_buffer_putc (buf, '+'); else if (info->space) - outchar (' '); + __printf_buffer_putc (buf, ' '); - if (!info->left && info->pad == '0' && width > 0) - PADN ('0', width); + if (!info->left && info->pad == '0') + __printf_buffer_pad (buf, '0', width); - { - char *buffer_end = NULL; - char *cp = NULL; - char *tmpptr; + if (iter.separators > 0) + { + char *cp = wstartp; + for (int i = 0; i < intdig_no; ++i) + { + if (__grouping_iterator_next (&iter)) + __printf_buffer_putc (buf, thousands_sep); + __printf_buffer_putc (buf, *cp); + ++cp; + } + __printf_buffer_write (buf, cp, wcp - cp); + } + else + __printf_buffer_write (buf, wstartp, wcp - wstartp); - if (! wide) - { - /* Create the single byte string. */ - size_t decimal_len; - size_t thousands_sep_len; - wchar_t *copywc; - size_t factor; - if (info->i18n) - factor = _nl_lookup_word (loc, LC_CTYPE, _NL_CTYPE_MB_CUR_MAX); - else - factor = 1; + if (info->left) + __printf_buffer_pad (buf, info->pad, width); + } - decimal_len = strlen (decimal); + if (buffer_malloced) + free (wbuffer); +} - if (thousands_sep == NULL) - thousands_sep_len = 0; - else - thousands_sep_len = strlen (thousands_sep); +/* ASCII to localization translation. Multibyte version. */ +struct __printf_buffer_fp +{ + struct __printf_buffer base; - size_t nbuffer = (2 + chars_needed * factor + decimal_len - + ngroups * thousands_sep_len); - if (__glibc_unlikely (buffer_malloced)) - { - buffer = (char *) malloc (nbuffer); - if (buffer == NULL) - { - /* Signal an error to the caller. */ - free (wbuffer); - return -1; - } - } - else - buffer = (char *) alloca (nbuffer); - buffer_end = buffer + nbuffer; - - /* Now copy the wide character string. Since the character - (except for the decimal point and thousands separator) must - be coming from the ASCII range we can esily convert the - string without mapping tables. */ - for (cp = buffer, copywc = wstartp; copywc < wcp; ++copywc) - if (*copywc == decimalwc) - cp = (char *) __mempcpy (cp, decimal, decimal_len); - else if (*copywc == thousands_sepwc) - cp = (char *) __mempcpy (cp, thousands_sep, thousands_sep_len); - else - *cp++ = (char) *copywc; - } + /* Replacement for ',' and '.'. */ + const char *thousands_sep; + const char *decimal; + unsigned char decimal_point_bytes; + unsigned char thousands_sep_length; - tmpptr = buffer; - if (__glibc_unlikely (info->i18n)) - { -#ifdef COMPILE_WPRINTF - wstartp = _i18n_number_rewrite (wstartp, wcp, - wbuffer + wbuffer_to_alloc); - wcp = wbuffer + wbuffer_to_alloc; - assert ((uintptr_t) wbuffer <= (uintptr_t) wstartp); - assert ((uintptr_t) wstartp - < (uintptr_t) wbuffer + wbuffer_to_alloc); -#else - tmpptr = _i18n_number_rewrite (tmpptr, cp, buffer_end); - cp = buffer_end; - assert ((uintptr_t) buffer <= (uintptr_t) tmpptr); - assert ((uintptr_t) tmpptr < (uintptr_t) buffer_end); -#endif - } + /* Buffer to write to. */ + struct __printf_buffer *next; + + /* Activates outdigit translation if not NULL. */ + struct __locale_data *ctype; - PRINT (tmpptr, wstartp, wide ? wcp - wstartp : cp - tmpptr); + /* Buffer to which the untranslated ASCII digits are written. */ + char untranslated[PRINTF_BUFFER_SIZE_DIGITS]; +}; - /* Free the memory if necessary. */ - if (__glibc_unlikely (buffer_malloced)) +/* Multibyte buffer-to-buffer flush function with full translation. */ +void +__printf_buffer_flush_fp (struct __printf_buffer_fp *buf) +{ + /* No need to update buf->base.written; the actual count is + maintained in buf->next->written. */ + for (char *p = buf->untranslated; p < buf->base.write_ptr; ++p) + { + char ch = *p; + const char *replacement = NULL; + unsigned int replacement_bytes; + if (ch == ',') + { + replacement = buf->thousands_sep; + replacement_bytes = buf->thousands_sep_length; + } + else if (ch == '.') + { + replacement = buf->decimal; + replacement_bytes = buf->decimal_point_bytes; + } + else if (buf->ctype != NULL && '0' <= ch && ch <= '9') { - free (buffer); - free (wbuffer); - /* Avoid a double free if the subsequent PADN encounters an - I/O error. */ - buffer = NULL; - wbuffer = NULL; + int digit = ch - '0'; + replacement + = buf->ctype->values[_NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT0_MB) + + digit].string; + struct lc_ctype_data *ctype = buf->ctype->private; + replacement_bytes = ctype->outdigit_bytes[digit]; } + if (replacement == NULL) + __printf_buffer_putc (buf->next, ch); + else + __printf_buffer_write (buf->next, replacement, replacement_bytes); } - if (info->left && width > 0) - PADN (info->pad, width); - } - return done; + if (!__printf_buffer_has_failed (buf->next)) + buf->base.write_ptr = buf->untranslated; + else + __printf_buffer_mark_failed (&buf->base); } -libc_hidden_def (__printf_fp_l) -int -___printf_fp (FILE *fp, const struct printf_info *info, - const void *const *args) +void +__printf_fp_l_buffer (struct __printf_buffer *buf, locale_t loc, + const struct printf_info *info, + const void *const *args) { - return __printf_fp_l (fp, _NL_CURRENT_LOCALE, info, args); + struct __printf_buffer_fp tmp; + + if (info->extra) + { + tmp.thousands_sep = _nl_lookup (loc, LC_MONETARY, MON_THOUSANDS_SEP); + tmp.decimal = _nl_lookup (loc, LC_MONETARY, MON_DECIMAL_POINT); + if (tmp.decimal[0] == '\0') + tmp.decimal = _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT); + } + else + { + tmp.thousands_sep = _nl_lookup (loc, LC_NUMERIC, THOUSANDS_SEP); + tmp.decimal = _nl_lookup (loc, LC_NUMERIC, DECIMAL_POINT); + } + + tmp.thousands_sep_length = strlen (tmp.thousands_sep); + if (tmp.decimal[1] == '\0' && tmp.thousands_sep_length <= 1 + && !info->i18n) + { + /* Emit the the characters directly. This is only possible if the + separators have length 1 (or 0 in case of thousands_sep). i18n + digit translation still needs the full conversion. */ + __printf_fp_buffer_1 (buf, loc, + tmp.thousands_sep[0], tmp.decimal[0], + tmp.thousands_sep_length, + info, args); + return; + } + + tmp.decimal_point_bytes = strlen (tmp.decimal); + + if (info->i18n) + tmp.ctype = loc->__locales[LC_CTYPE]; + else + tmp.ctype = NULL; + tmp.next = buf; + + __printf_buffer_init (&tmp.base, tmp.untranslated, sizeof (tmp.untranslated), + __printf_buffer_mode_fp); + __printf_fp_buffer_1 (&tmp.base, loc, ',', '.', + tmp.thousands_sep_length, info, args); + if (__printf_buffer_has_failed (&tmp.base)) + { + __printf_buffer_mark_failed (tmp.next); + return; + } + __printf_buffer_flush_fp (&tmp); } -ldbl_hidden_def (___printf_fp, __printf_fp) -ldbl_strong_alias (___printf_fp, __printf_fp) - -/* Return the number of extra grouping characters that will be inserted - into a number with INTDIG_MAX integer digits. */ +/* The wide version is implemented on top of the multibyte version using + translation. */ -unsigned int -__guess_grouping (unsigned int intdig_max, const char *grouping) +struct __printf_buffer_fp_to_wide { - unsigned int groups; + struct __printf_buffer base; + wchar_t thousands_sep_wc; + wchar_t decimalwc; + struct __wprintf_buffer *next; - /* We treat all negative values like CHAR_MAX. */ + /* Activates outdigit translation if not NULL. */ + struct __locale_data *ctype; - if (*grouping == CHAR_MAX || *grouping <= 0) - /* No grouping should be done. */ - return 0; + char untranslated[PRINTF_BUFFER_SIZE_DIGITS]; +}; - groups = 0; - while (intdig_max > (unsigned int) *grouping) +void +__printf_buffer_flush_fp_to_wide (struct __printf_buffer_fp_to_wide *buf) +{ + /* No need to update buf->base.written; the actual count is + maintained in buf->next->written. */ + for (char *p = buf->untranslated; p < buf->base.write_ptr; ++p) { - ++groups; - intdig_max -= *grouping++; - - if (*grouping == CHAR_MAX -#if CHAR_MIN < 0 - || *grouping < 0 -#endif - ) - /* No more grouping should be done. */ - break; - else if (*grouping == 0) + /* wchar_t overlaps with char in the ASCII range. */ + wchar_t ch = *p; + if (ch == L',') { - /* Same grouping repeats. */ - groups += (intdig_max - 1) / grouping[-1]; - break; + ch = buf->thousands_sep_wc; + if (ch == 0) + continue; } + else if (ch == L'.') + ch = buf->decimalwc; + else if (buf->ctype != NULL && L'0' <= ch && ch <= L'9') + ch = buf->ctype->values[_NL_ITEM_INDEX (_NL_CTYPE_OUTDIGIT0_WC) + + ch - L'0'].word; + __wprintf_buffer_putc (buf->next, ch); } - return groups; + if (!__wprintf_buffer_has_failed (buf->next)) + buf->base.write_ptr = buf->untranslated; + else + __printf_buffer_mark_failed (&buf->base); } -/* Group the INTDIG_NO integer digits of the number in [BUF,BUFEND). - There is guaranteed enough space past BUFEND to extend it. - Return the new end of buffer. */ - -static wchar_t * -group_number (wchar_t *buf, wchar_t *bufend, unsigned int intdig_no, - const char *grouping, wchar_t thousands_sep, int ngroups) +void +__wprintf_fp_l_buffer (struct __wprintf_buffer *buf, locale_t loc, + const struct printf_info *info, + const void *const *args) { - wchar_t *p; - - if (ngroups == 0) - return bufend; + struct __printf_buffer_fp_to_wide tmp; + if (info->extra) + { + tmp.decimalwc = _nl_lookup_word (loc, LC_MONETARY, + _NL_MONETARY_DECIMAL_POINT_WC); + tmp.thousands_sep_wc = _nl_lookup_word (loc, LC_MONETARY, + _NL_MONETARY_THOUSANDS_SEP_WC); + if (tmp.decimalwc == 0) + tmp.decimalwc = _nl_lookup_word (loc, LC_NUMERIC, + _NL_NUMERIC_DECIMAL_POINT_WC); + } + else + { + tmp.decimalwc = _nl_lookup_word (loc, LC_NUMERIC, + _NL_NUMERIC_DECIMAL_POINT_WC); + tmp.thousands_sep_wc = _nl_lookup_word (loc, LC_NUMERIC, + _NL_NUMERIC_THOUSANDS_SEP_WC); + } - /* Move the fractional part down. */ - __wmemmove (buf + intdig_no + ngroups, buf + intdig_no, - bufend - (buf + intdig_no)); + if (info->i18n) + tmp.ctype = loc->__locales[LC_CTYPE]; + else + tmp.ctype = NULL; + tmp.next = buf; - p = buf + intdig_no + ngroups - 1; - do + __printf_buffer_init (&tmp.base, tmp.untranslated, sizeof (tmp.untranslated), + __printf_buffer_mode_fp_to_wide); + __printf_fp_buffer_1 (&tmp.base, loc, ',', '.', 1, info, args); + if (__printf_buffer_has_failed (&tmp.base)) { - unsigned int len = *grouping++; - do - *p-- = buf[--intdig_no]; - while (--len > 0); - *p-- = thousands_sep; + __wprintf_buffer_mark_failed (tmp.next); + return; + } + __printf_buffer_flush (&tmp.base); +} - if (*grouping == CHAR_MAX -#if CHAR_MIN < 0 - || *grouping < 0 -#endif - ) - /* No more grouping should be done. */ - break; - else if (*grouping == 0) - /* Same grouping repeats. */ - --grouping; - } while (intdig_no > (unsigned int) *grouping); - - /* Copy the remaining ungrouped digits. */ - do - *p-- = buf[--intdig_no]; - while (p > buf); - - return bufend + ngroups; +int +___printf_fp (FILE *fp, const struct printf_info *info, + const void *const *args) +{ + if (info->wide) + { + struct __wprintf_buffer_to_file buf; + __wprintf_buffer_to_file_init (&buf, fp); + __wprintf_fp_l_buffer (&buf.base, _NL_CURRENT_LOCALE, info, args); + return __wprintf_buffer_to_file_done (&buf); + } + else + { + struct __printf_buffer_to_file buf; + __printf_buffer_to_file_init (&buf, fp); + __printf_fp_l_buffer (&buf.base, _NL_CURRENT_LOCALE, info, args); + return __printf_buffer_to_file_done (&buf); + } } +ldbl_hidden_def (___printf_fp, __printf_fp) +ldbl_strong_alias (___printf_fp, __printf_fp) |