about summary refs log tree commit diff
path: root/include
diff options
context:
space:
mode:
authorFlorian Weimer <fweimer@redhat.com>2022-12-19 18:56:54 +0100
committerFlorian Weimer <fweimer@redhat.com>2022-12-19 18:56:54 +0100
commite88b9f0e5cc50cab57a299dc7efe1a4eb385161d (patch)
tree2b733d221cc4247e16aef46150c2fc8153ad6db4 /include
parent46378560e056300623364669de2405a7182b064f (diff)
downloadglibc-e88b9f0e5cc50cab57a299dc7efe1a4eb385161d.tar.gz
glibc-e88b9f0e5cc50cab57a299dc7efe1a4eb385161d.tar.xz
glibc-e88b9f0e5cc50cab57a299dc7efe1a4eb385161d.zip
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 'include')
-rw-r--r--include/printf.h29
-rw-r--r--include/printf_buffer.h44
2 files changed, 64 insertions, 9 deletions
diff --git a/include/printf.h b/include/printf.h
index 5127a45f9b..2c998059d4 100644
--- a/include/printf.h
+++ b/include/printf.h
@@ -65,18 +65,31 @@ int __translated_number_width (locale_t loc,
 			       const char *first, const char *last)
   attribute_hidden;
 
-extern int __printf_fphex (FILE *, const struct printf_info *,
-			   const void *const *) attribute_hidden;
+
+struct __printf_buffer;
+void __printf_buffer (struct __printf_buffer *buf, const char *format,
+		      va_list ap, unsigned int mode_flags);
+struct __wprintf_buffer;
+void __wprintf_buffer (struct __wprintf_buffer *buf, const wchar_t *format,
+		       va_list ap, unsigned int mode_flags);
+
 extern int __printf_fp (FILE *, const struct printf_info *,
 			const void *const *);
 libc_hidden_proto (__printf_fp)
-extern int __printf_fp_l (FILE *, locale_t, const struct printf_info *,
-			  const void *const *);
-libc_hidden_proto (__printf_fp_l)
 
-extern unsigned int __guess_grouping (unsigned int intdig_max,
-				      const char *grouping)
-     attribute_hidden;
+void __printf_fphex_l_buffer (struct __printf_buffer *, locale_t,
+			      const struct printf_info *,
+			      const void *const *) attribute_hidden;
+void __printf_fp_l_buffer (struct __printf_buffer *, locale_t,
+			   const struct printf_info *,
+			   const void *const *) attribute_hidden;
+struct __wprintf_buffer;
+void __wprintf_fphex_l_buffer (struct __wprintf_buffer *, locale_t,
+			       const struct printf_info *,
+			       const void *const *) attribute_hidden;
+void __wprintf_fp_l_buffer (struct __wprintf_buffer *, locale_t,
+			    const struct printf_info *,
+			    const void *const *) attribute_hidden;
 
 # endif /* !_ISOMAC */
 #endif
diff --git a/include/printf_buffer.h b/include/printf_buffer.h
index e27f2a899c..39ef232587 100644
--- a/include/printf_buffer.h
+++ b/include/printf_buffer.h
@@ -45,7 +45,12 @@
 enum __printf_buffer_mode
   {
     __printf_buffer_mode_failed,
+    __printf_buffer_mode_snprintf,
     __printf_buffer_mode_to_file,
+    __printf_buffer_mode_strfmon,
+    __printf_buffer_mode_fp,         /* For __printf_fp_l_buffer.  */
+    __printf_buffer_mode_fp_to_wide, /* For __wprintf_fp_l_buffer.  */
+    __printf_buffer_mode_fphex_to_wide, /* For __wprintf_fphex_l_buffer.  */
   };
 
 /* Buffer for fast character writing with overflow handling.
@@ -268,13 +273,45 @@ bool __wprintf_buffer_flush (struct __wprintf_buffer *buf) attribute_hidden;
 #define Xprintf_buffer_puts Xprintf (buffer_puts)
 #define Xprintf_buffer_write Xprintf (buffer_write)
 
+/* Commonly used buffers.  */
+
+struct __printf_buffer_snprintf
+{
+  struct __printf_buffer base;
+#define PRINTF_BUFFER_SIZE_DISCARD 128
+  char discard[PRINTF_BUFFER_SIZE_DISCARD]; /* Used in counting mode.  */
+};
+
+/* Sets up [BUFFER, BUFFER + LENGTH) as the write target.  If LENGTH
+   is positive, also writes a NUL byte to *BUFFER.  */
+void __printf_buffer_snprintf_init (struct __printf_buffer_snprintf *,
+                                    char *buffer, size_t length)
+  attribute_hidden;
+
+/* Add the null terminator after everything has been written.  The
+   return value is the one expected by printf (see __printf_buffer_done).  */
+int __printf_buffer_snprintf_done (struct __printf_buffer_snprintf *)
+  attribute_hidden;
+
 /* Flush function implementations follow.  They are called from
    __printf_buffer_flush.  Generic code should not call these flush
    functions directly.  Some modes have inline implementations.  */
 
+void __printf_buffer_flush_snprintf (struct __printf_buffer_snprintf *)
+  attribute_hidden;
 struct __printf_buffer_to_file;
 void __printf_buffer_flush_to_file (struct __printf_buffer_to_file *)
   attribute_hidden;
+struct __printf_buffer_fp;
+void __printf_buffer_flush_fp (struct __printf_buffer_fp *)
+  attribute_hidden;
+struct __printf_buffer_fp_to_wide;
+void __printf_buffer_flush_fp_to_wide (struct __printf_buffer_fp_to_wide *)
+  attribute_hidden;
+struct __printf_buffer_fphex_to_wide;
+void __printf_buffer_flush_fphex_to_wide (struct
+                                          __printf_buffer_fphex_to_wide *)
+  attribute_hidden;
 
 struct __wprintf_buffer_to_file;
 void __wprintf_buffer_flush_to_file (struct __wprintf_buffer_to_file *)
@@ -282,10 +319,15 @@ void __wprintf_buffer_flush_to_file (struct __wprintf_buffer_to_file *)
 
 /* Buffer sizes.  These can be tuned as necessary.  There is a tension
    here between stack consumption, cache usage, and additional system
-   calls or heap allocations (if the buffer is too small).  */
+   calls or heap allocations (if the buffer is too small).
+
+   Also see PRINTF_BUFFER_SIZE_DISCARD above for snprintf.  */
 
 /* Fallback buffer if the underlying FILE * stream does not provide
    buffer space.  */
 #define PRINTF_BUFFER_SIZE_TO_FILE_STAGE 128
 
+/* Temporary buffer used during floating point digit translation.  */
+#define PRINTF_BUFFER_SIZE_DIGITS 64
+
 #endif /* PRINTF_BUFFER_H */