[libc-commits] [libc] [libc] Update printf_core::Writer to template on character type. (PR #220669)

Alex Strelnikov via libc-commits libc-commits at lists.llvm.org
Thu Sep 3 08:01:17 PDT 2026


https://github.com/strel-12 updated https://github.com/llvm/llvm-project/pull/220669

>From 0f2bdc1743bf41b739d64b8998bcd9775444bea8 Mon Sep 17 00:00:00 2001
From: Alex Strelnikov <strel at google.com>
Date: Wed, 2 Sep 2026 16:26:21 +0000
Subject: [PATCH 1/3] Update printf_core::Writer to template on character type.

---
 libc/src/__support/printf_core/CMakeLists.txt |  45 ++-
 .../__support/printf_core/char_converter.h    |   4 +-
 libc/src/__support/printf_core/converter.h    |  18 +-
 .../__support/printf_core/fixed_converter.h   |   4 +-
 .../printf_core/float_dec_converter.h         |  55 ++-
 .../printf_core/float_dec_converter_limited.h |  36 +-
 .../printf_core/float_hex_converter.h         |   4 +-
 .../printf_core/float_inf_nan_converter.h     |   4 +-
 .../src/__support/printf_core/int_converter.h |   4 +-
 .../__support/printf_core/make_file_writer.h  | 120 +++++++
 .../printf_core/make_resizing_writer.h        |  63 ++++
 .../printf_core/make_stderr_writer.h          |  56 +++
 .../{write_modes.def => overflow_modes.def}   |   9 +-
 libc/src/__support/printf_core/printf_main.h  |   9 +-
 .../src/__support/printf_core/ptr_converter.h |   4 +-
 .../printf_core/strerror_converter.h          |   4 +-
 .../__support/printf_core/string_converter.h  |  12 +-
 .../printf_core/vasprintf_internal.h          |  37 +-
 .../__support/printf_core/vfprintf_internal.h |  67 +---
 .../printf_core/write_int_converter.h         |   4 +-
 libc/src/__support/printf_core/writer.h       | 327 ++++++++++--------
 libc/src/err/report.cpp                       |  17 +-
 libc/src/stdio/snprintf.cpp                   |   8 +-
 libc/src/stdio/sprintf.cpp                    |   6 +-
 libc/src/stdio/vsnprintf.cpp                  |   8 +-
 libc/src/stdio/vsprintf.cpp                   |   6 +-
 libc/src/stdlib/str_from_util.h               |   4 +-
 libc/src/stdlib/strfromd.cpp                  |   8 +-
 libc/src/stdlib/strfromf.cpp                  |   8 +-
 libc/src/stdlib/strfroml.cpp                  |   8 +-
 libc/src/time/asctime_utils.h                 |   5 +-
 libc/src/time/strftime.cpp                    |   8 +-
 .../time/strftime_core/composite_converter.h  |  32 +-
 libc/src/time/strftime_core/converter.h       |   6 +-
 libc/src/time/strftime_core/num_converter.h   |   8 +-
 libc/src/time/strftime_core/str_converter.h   |   6 +-
 libc/src/time/strftime_core/strftime_main.h   |   4 +-
 .../__support/printf_core/converter_test.cpp  |  10 +-
 .../src/__support/printf_core/writer_test.cpp | 117 +++----
 39 files changed, 695 insertions(+), 460 deletions(-)
 create mode 100644 libc/src/__support/printf_core/make_file_writer.h
 create mode 100644 libc/src/__support/printf_core/make_resizing_writer.h
 create mode 100644 libc/src/__support/printf_core/make_stderr_writer.h
 rename libc/src/__support/printf_core/{write_modes.def => overflow_modes.def} (66%)

diff --git a/libc/src/__support/printf_core/CMakeLists.txt b/libc/src/__support/printf_core/CMakeLists.txt
index 3617bb7ac1ea2..12826b55d98c9 100644
--- a/libc/src/__support/printf_core/CMakeLists.txt
+++ b/libc/src/__support/printf_core/CMakeLists.txt
@@ -118,6 +118,30 @@ add_header_library(
     libc.src.string.memory_utils.inline_memset
 )
 
+add_header_library(
+  make_resizing_writer
+  HDRS
+    make_resizing_writer.h
+  DEPENDS
+    .writer
+    libc.hdr.func.malloc
+    libc.hdr.func.free
+    libc.hdr.func.realloc
+    libc.src.__support.CPP.string_view
+    libc.src.__support.printf_core.writer
+)
+
+add_header_library(
+  make_stderr_writer
+  HDRS
+    make_stderr_writer.h
+  DEPENDS
+    .writer
+    libc.src.__support.CPP.string_view
+    libc.src.__support.OSUtil.io
+    libc.src.__support.macros.config
+)
+
 add_header_library(
   converter
   HDRS
@@ -179,12 +203,10 @@ add_header_library(
     vasprintf_internal.h
   DEPENDS
     libc.hdr.func.malloc
-    libc.hdr.func.free
-    libc.hdr.func.realloc
     libc.src.__support.arg_list
     libc.src.__support.error_or
+    libc.src.__support.printf_core.make_resizing_writer
     libc.src.__support.printf_core.printf_main
-    libc.src.__support.printf_core.writer
 )
 
 add_header_library(
@@ -202,15 +224,28 @@ if(NOT (TARGET libc.src.__support.File.file) AND LLVM_LIBC_FULL_BUILD)
   return()
 endif()
 
+add_header_library(
+  make_file_writer
+  HDRS
+    make_file_writer.h
+  DEPENDS
+    .writer
+    libc.src.__support.CPP.string_view
+    libc.src.__support.File.file
+    libc.src.__support.macros.attributes
+    libc.src.__support.macros.config
+    ${use_system_file}
+)
+
 add_header_library(
   vfprintf_internal
   HDRS
     vfprintf_internal.h
   DEPENDS
+    .make_file_writer
+    .printf_main
     libc.src.__support.File.file
     libc.src.__support.error_or
     libc.src.__support.arg_list
-    libc.src.__support.printf_core.printf_main
-    libc.src.__support.printf_core.writer
     ${use_system_file}
 )
diff --git a/libc/src/__support/printf_core/char_converter.h b/libc/src/__support/printf_core/char_converter.h
index 59f3e22f7ce50..7bad28d18d1be 100644
--- a/libc/src/__support/printf_core/char_converter.h
+++ b/libc/src/__support/printf_core/char_converter.h
@@ -26,8 +26,8 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_char(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_char(Writer<mode> *writer,
                              const FormatSection &to_conv) {
 
   char buffer[MB_LEN_MAX];
diff --git a/libc/src/__support/printf_core/converter.h b/libc/src/__support/printf_core/converter.h
index 67d0ee3d42b61..05ef6829b3a5e 100644
--- a/libc/src/__support/printf_core/converter.h
+++ b/libc/src/__support/printf_core/converter.h
@@ -29,8 +29,8 @@ namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
 #ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
-LIBC_PRINTF_MODULE((template <WriteMode write_mode>
-                    int convert_float(Writer<write_mode> *writer,
+LIBC_PRINTF_MODULE((template <OverflowMode mode>
+                    int convert_float(Writer<mode> *writer,
                                       const FormatSection &to_conv)),
                    {
                      switch (to_conv.conv_name) {
@@ -52,18 +52,18 @@ LIBC_PRINTF_MODULE((template <WriteMode write_mode>
 #endif // not LIBC_COPT_PRINTF_DISABLE_FLOAT
 
 #ifdef LIBC_PRINTF_DEFINE_MODULES
-#define HANDLE_WRITE_MODE(MODE)                                                \
-  template int convert_float<WriteMode::MODE>(                                 \
-      Writer<WriteMode::MODE> * writer, const FormatSection &to_conv);
-#include "src/__support/printf_core/write_modes.def"
-#undef HANDLE_WRITE_MODE
+#define HANDLE_OVERFLOW_MODE(MODE)                                             \
+  template int convert_float<OverflowMode::MODE>(                              \
+      Writer<OverflowMode::MODE> * writer, const FormatSection &to_conv);
+#include "src/__support/printf_core/overflow_modes.def"
+#undef HANDLE_OVERFLOW_MODE
 #endif // LIBC_PRINTF_DEFINE_MODULES
 
 // convert will call a conversion function to convert the FormatSection into
 // its string representation, and then that will write the result to the
 // writer.
-template <WriteMode write_mode>
-int convert(Writer<write_mode> *writer, const FormatSection &to_conv) {
+template <OverflowMode mode>
+int convert(Writer<mode> *writer, const FormatSection &to_conv) {
   if (!to_conv.has_conv)
     return writer->write(to_conv.raw_string);
 
diff --git a/libc/src/__support/printf_core/fixed_converter.h b/libc/src/__support/printf_core/fixed_converter.h
index 76b2d0d63644f..04994a7729bc8 100644
--- a/libc/src/__support/printf_core/fixed_converter.h
+++ b/libc/src/__support/printf_core/fixed_converter.h
@@ -63,8 +63,8 @@ LIBC_INLINE constexpr uint32_t const_ten_exp(uint32_t exponent) {
     }                                                                          \
   } while (false)
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_fixed(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_fixed(Writer<mode> *writer,
                               const FormatSection &to_conv) {
   // Long accum should be the largest type, so we can store all the smaller
   // numbers in things sized for it.
diff --git a/libc/src/__support/printf_core/float_dec_converter.h b/libc/src/__support/printf_core/float_dec_converter.h
index b678cd29cdb81..89ea766e72f0a 100644
--- a/libc/src/__support/printf_core/float_dec_converter.h
+++ b/libc/src/__support/printf_core/float_dec_converter.h
@@ -99,7 +99,7 @@ zero_after_digits(int32_t base_2_exp, int32_t digits_after_point, T mantissa,
   return has_trailing_zeros;
 }
 
-template <WriteMode write_mode> class PaddingWriter {
+template <OverflowMode mode> class PaddingWriter {
   bool left_justified = false;
   bool leading_zeroes = false;
   char sign_char = 0;
@@ -113,7 +113,7 @@ template <WriteMode write_mode> class PaddingWriter {
         sign_char(init_sign_char),
         min_width(to_conv.min_width > 0 ? to_conv.min_width : 0) {}
 
-  LIBC_INLINE int write_left_padding(Writer<write_mode> *writer,
+  LIBC_INLINE int write_left_padding(Writer<mode> *writer,
                                      size_t total_digits) {
     // The pattern is (spaces) (sign) (zeroes), but only one of spaces and
     // zeroes can be written, and only if the padding amount is positive.
@@ -137,7 +137,7 @@ template <WriteMode write_mode> class PaddingWriter {
     return 0;
   }
 
-  LIBC_INLINE int write_right_padding(Writer<write_mode> *writer,
+  LIBC_INLINE int write_right_padding(Writer<mode> *writer,
                                       size_t total_digits) {
     // If and only if the conversion is left justified, there may be trailing
     // spaces.
@@ -163,7 +163,7 @@ template <WriteMode write_mode> class PaddingWriter {
   This FloatWriter class does the buffering and counting, and writes to the
   output when necessary.
 */
-template <WriteMode write_mode> class FloatWriter {
+template <OverflowMode mode> class FloatWriter {
   char block_buffer[BLOCK_SIZE]; // The buffer that holds a block.
   size_t buffered_digits = 0;    // The number of digits held in the buffer.
   bool has_written = false;      // True once any digits have been output.
@@ -172,8 +172,8 @@ template <WriteMode write_mode> class FloatWriter {
   size_t digits_before_decimal = 0; // The # of digits to write before the '.'
   size_t total_digits_written = 0;  // The # of digits that have been output.
   bool has_decimal_point;           // True if the number has a decimal point.
-  Writer<write_mode> *writer;       // Writes to the final output.
-  PaddingWriter<write_mode>
+  Writer<mode> *writer;             // Writes to the final output.
+  PaddingWriter<mode>
       padding_writer; // Handles prefixes/padding, uses total_digits.
 
   LIBC_INLINE int flush_buffer(bool round_up_max_blocks = false) {
@@ -255,9 +255,9 @@ template <WriteMode write_mode> class FloatWriter {
 #endif // LIBC_TYPES_LONG_DOUBLE_IS_DOUBLE_DOUBLE
 
 public:
-  LIBC_INLINE FloatWriter(Writer<write_mode> *init_writer,
+  LIBC_INLINE FloatWriter(Writer<mode> *init_writer,
                           bool init_has_decimal_point,
-                          const PaddingWriter<write_mode> &init_padding_writer)
+                          const PaddingWriter<mode> &init_padding_writer)
       : has_decimal_point(init_has_decimal_point), writer(init_writer),
         padding_writer(init_padding_writer) {}
 
@@ -480,23 +480,16 @@ template <WriteMode write_mode> class FloatWriter {
 };
 
 // Class-template auto deduction helpers, add more if needed.
-FloatWriter(Writer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>, bool,
-            const PaddingWriter<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>)
-    -> FloatWriter<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>;
-FloatWriter(Writer<WriteMode::RESIZE_AND_FILL_BUFF>, bool,
-            const PaddingWriter<WriteMode::RESIZE_AND_FILL_BUFF>)
-    -> FloatWriter<WriteMode::RESIZE_AND_FILL_BUFF>;
-FloatWriter(Writer<WriteMode::FLUSH_TO_STREAM>, bool,
-            const PaddingWriter<WriteMode::FLUSH_TO_STREAM>)
-    -> FloatWriter<WriteMode::FLUSH_TO_STREAM>;
+template <OverflowMode mode>
+FloatWriter(Writer<mode>, bool, const PaddingWriter<mode>) -> FloatWriter<mode>;
 
 // This implementation is based on the Ryu Printf algorithm by Ulf Adams:
 // Ulf Adams. 2019. Ryƫ revisited: printf floating point conversion.
 // Proc. ACM Program. Lang. 3, OOPSLA, Article 169 (October 2019), 23 pages.
 // https://doi.org/10.1145/3360595
-template <typename T, WriteMode write_mode,
+template <typename T, OverflowMode mode,
           cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
-LIBC_INLINE int convert_float_decimal_typed(Writer<write_mode> *writer,
+LIBC_INLINE int convert_float_decimal_typed(Writer<mode> *writer,
                                             const FormatSection &to_conv,
                                             fputil::FPBits<T> float_bits) {
   // signed because later we use -FRACTION_LEN
@@ -523,7 +516,7 @@ LIBC_INLINE int convert_float_decimal_typed(Writer<write_mode> *writer,
   // ignored.
   bool nonzero = false;
 
-  PaddingWriter<write_mode> padding_writer(to_conv, sign_char);
+  PaddingWriter<mode> padding_writer(to_conv, sign_char);
   FloatWriter float_writer(writer, has_decimal_point, padding_writer);
   FloatToString<T> float_converter(float_bits.get_val());
 
@@ -604,9 +597,9 @@ LIBC_INLINE int convert_float_decimal_typed(Writer<write_mode> *writer,
   return WRITE_OK;
 }
 
-template <typename T, WriteMode write_mode,
+template <typename T, OverflowMode mode,
           cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
-LIBC_INLINE int convert_float_dec_exp_typed(Writer<write_mode> *writer,
+LIBC_INLINE int convert_float_dec_exp_typed(Writer<mode> *writer,
                                             const FormatSection &to_conv,
                                             fputil::FPBits<T> float_bits) {
   // signed because later we use -FRACTION_LEN
@@ -628,7 +621,7 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer<write_mode> *writer,
   bool has_decimal_point =
       (precision > 0) || ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0);
 
-  PaddingWriter<write_mode> padding_writer(to_conv, sign_char);
+  PaddingWriter<mode> padding_writer(to_conv, sign_char);
   FloatWriter float_writer(writer, has_decimal_point, padding_writer);
   FloatToString<T> float_converter(float_bits.get_val());
 
@@ -765,9 +758,9 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer<write_mode> *writer,
   return WRITE_OK;
 }
 
-template <typename T, WriteMode write_mode,
+template <typename T, OverflowMode mode,
           cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
-LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
+LIBC_INLINE int convert_float_dec_auto_typed(Writer<mode> *writer,
                                              const FormatSection &to_conv,
                                              fputil::FPBits<T> float_bits) {
   // signed because later we use -FRACTION_LEN
@@ -1133,8 +1126,8 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
 
 // TODO: unify the float converters to remove the duplicated checks for inf/nan.
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_float_decimal(Writer<mode> *writer,
                                       const FormatSection &to_conv) {
 #if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
   if (to_conv.length_modifier == LengthModifier::Q) {
@@ -1169,8 +1162,8 @@ LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
   return convert_inf_nan(writer, to_conv);
 }
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_dec_exp(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_float_dec_exp(Writer<mode> *writer,
                                       const FormatSection &to_conv) {
 #if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
   if (to_conv.length_modifier == LengthModifier::Q) {
@@ -1205,8 +1198,8 @@ LIBC_INLINE int convert_float_dec_exp(Writer<write_mode> *writer,
   return convert_inf_nan(writer, to_conv);
 }
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_float_dec_auto(Writer<mode> *writer,
                                        const FormatSection &to_conv) {
 #if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
   if (to_conv.length_modifier == LengthModifier::Q) {
diff --git a/libc/src/__support/printf_core/float_dec_converter_limited.h b/libc/src/__support/printf_core/float_dec_converter_limited.h
index 2e34004ce92ca..7aa886db37a3d 100644
--- a/libc/src/__support/printf_core/float_dec_converter_limited.h
+++ b/libc/src/__support/printf_core/float_dec_converter_limited.h
@@ -374,8 +374,8 @@ DigitsOutput decimal_digits(DigitsInput input, int precision, bool e_mode) {
   return output;
 }
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_inner(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_float_inner(Writer<mode> *writer,
                                     const FormatSection &to_conv,
                                     int32_t fraction_len, int exponent,
                                     AnyFloatStorageType mantissa, Sign sign,
@@ -618,10 +618,10 @@ LIBC_INLINE int convert_float_inner(Writer<write_mode> *writer,
   return WRITE_OK;
 }
 
-template <typename T, WriteMode write_mode,
+template <typename T, OverflowMode mode,
           cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
 LIBC_INLINE int
-convert_float_typed(Writer<write_mode> *writer, const FormatSection &to_conv,
+convert_float_typed(Writer<mode> *writer, const FormatSection &to_conv,
                     fputil::FPBits<T> float_bits, ConversionType ctype) {
   return convert_float_inner(writer, to_conv, float_bits.FRACTION_LEN,
                              float_bits.get_explicit_exponent(),
@@ -629,8 +629,8 @@ convert_float_typed(Writer<write_mode> *writer, const FormatSection &to_conv,
                              float_bits.sign(), ctype);
 }
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_outer(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_float_outer(Writer<mode> *writer,
                                     const FormatSection &to_conv,
                                     ConversionType ctype) {
 #if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
@@ -666,44 +666,44 @@ LIBC_INLINE int convert_float_outer(Writer<write_mode> *writer,
   return convert_inf_nan(writer, to_conv);
 }
 
-template <typename T, WriteMode write_mode,
+template <typename T, OverflowMode mode,
           cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
-LIBC_INLINE int convert_float_decimal_typed(Writer<write_mode> *writer,
+LIBC_INLINE int convert_float_decimal_typed(Writer<mode> *writer,
                                             const FormatSection &to_conv,
                                             fputil::FPBits<T> float_bits) {
   return convert_float_typed<T>(writer, to_conv, float_bits, ConversionType::F);
 }
 
-template <typename T, WriteMode write_mode,
+template <typename T, OverflowMode mode,
           cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
-LIBC_INLINE int convert_float_dec_exp_typed(Writer<write_mode> *writer,
+LIBC_INLINE int convert_float_dec_exp_typed(Writer<mode> *writer,
                                             const FormatSection &to_conv,
                                             fputil::FPBits<T> float_bits) {
   return convert_float_typed<T>(writer, to_conv, float_bits, ConversionType::E);
 }
 
-template <typename T, WriteMode write_mode,
+template <typename T, OverflowMode mode,
           cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
-LIBC_INLINE int convert_float_dec_auto_typed(Writer<write_mode> *writer,
+LIBC_INLINE int convert_float_dec_auto_typed(Writer<mode> *writer,
                                              const FormatSection &to_conv,
                                              fputil::FPBits<T> float_bits) {
   return convert_float_typed<T>(writer, to_conv, float_bits, ConversionType::G);
 }
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_float_decimal(Writer<mode> *writer,
                                       const FormatSection &to_conv) {
   return convert_float_outer(writer, to_conv, ConversionType::F);
 }
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_dec_exp(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_float_dec_exp(Writer<mode> *writer,
                                       const FormatSection &to_conv) {
   return convert_float_outer(writer, to_conv, ConversionType::E);
 }
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_float_dec_auto(Writer<mode> *writer,
                                        const FormatSection &to_conv) {
   return convert_float_outer(writer, to_conv, ConversionType::G);
 }
diff --git a/libc/src/__support/printf_core/float_hex_converter.h b/libc/src/__support/printf_core/float_hex_converter.h
index 640db7be2c0f4..15f3d9f05a127 100644
--- a/libc/src/__support/printf_core/float_hex_converter.h
+++ b/libc/src/__support/printf_core/float_hex_converter.h
@@ -47,8 +47,8 @@ get_float_hex_exp_fp_bits_properties(AnyFloatStorageType float_raw) {
   };
 }
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_float_hex_exp(Writer<mode> *writer,
                                       const FormatSection &to_conv) {
 #if defined(LIBC_INTERNAL_PRINTF_CONVERT_FLOAT128)
   static constexpr uint32_t MAX_POSSIBLE_FRACTION_LEN =
diff --git a/libc/src/__support/printf_core/float_inf_nan_converter.h b/libc/src/__support/printf_core/float_inf_nan_converter.h
index 81769ae4b4913..40740c701d99f 100644
--- a/libc/src/__support/printf_core/float_inf_nan_converter.h
+++ b/libc/src/__support/printf_core/float_inf_nan_converter.h
@@ -38,8 +38,8 @@ get_inf_nan_fp_bits_properties(AnyFloatStorageType float_raw) {
   };
 }
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_inf_nan(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_inf_nan(Writer<mode> *writer,
                                 const FormatSection &to_conv) {
   // All of the letters will be defined relative to variable a, which will be
   // the appropriate case based on the case of the conversion.
diff --git a/libc/src/__support/printf_core/int_converter.h b/libc/src/__support/printf_core/int_converter.h
index c40c742e41c35..786d9ff2268ff 100644
--- a/libc/src/__support/printf_core/int_converter.h
+++ b/libc/src/__support/printf_core/int_converter.h
@@ -61,8 +61,8 @@ num_to_strview(uintmax_t num, cpp::span<char> bufref, char conv_name) {
 
 } // namespace details
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_int(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_int(Writer<mode> *writer,
                             const FormatSection &to_conv) {
   static constexpr size_t BITS_IN_BYTE = 8;
   static constexpr size_t BITS_IN_NUM = sizeof(uintmax_t) * BITS_IN_BYTE;
diff --git a/libc/src/__support/printf_core/make_file_writer.h b/libc/src/__support/printf_core/make_file_writer.h
new file mode 100644
index 0000000000000..e0639581ee5e0
--- /dev/null
+++ b/libc/src/__support/printf_core/make_file_writer.h
@@ -0,0 +1,120 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation helpers for Writer<OverflowMode::FLUSH_TO_FILE, CharT>.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_FILE_WRITER_H
+#define LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_FILE_WRITER_H
+
+#include "hdr/types/FILE.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/File/file.h"
+#include "src/__support/macros/attributes.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/printf_core/writer.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace printf_core {
+
+template <typename CharT>
+LIBC_INLINE Writer<Mode<OverflowMode::FLUSH_TO_FILE>::value, CharT>
+make_file_writer(CharT *buffer, size_t buffer_len, ::FILE *fp) {
+  return Writer(buffer, buffer_len,
+                make_overflow_writer<OverflowMode::FLUSH_TO_FILE, CharT>(fp));
+}
+
+} // namespace printf_core
+
+namespace internal {
+#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
+LIBC_INLINE int ferror_unlocked(FILE *f) {
+  return reinterpret_cast<LIBC_NAMESPACE::File *>(f)->error_unlocked();
+}
+
+LIBC_INLINE void flockfile(FILE *f) {
+  reinterpret_cast<LIBC_NAMESPACE::File *>(f)->lock();
+}
+
+LIBC_INLINE void funlockfile(FILE *f) {
+  reinterpret_cast<LIBC_NAMESPACE::File *>(f)->unlock();
+}
+
+LIBC_INLINE FileIOResult fwrite_unlocked(const void *ptr, size_t size,
+                                         size_t nmemb, FILE *f) {
+  return reinterpret_cast<LIBC_NAMESPACE::File *>(f)->write_unlocked(
+      ptr, size * nmemb);
+}
+#else  // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
+LIBC_INLINE int ferror_unlocked(::FILE *f) { return ::ferror_unlocked(f); }
+
+LIBC_INLINE void flockfile(::FILE *f) { ::flockfile(f); }
+
+LIBC_INLINE void funlockfile(::FILE *f) { ::funlockfile(f); }
+
+LIBC_INLINE FileIOResult fwrite_unlocked(const void *ptr, size_t size,
+                                         size_t nmemb, ::FILE *f) {
+  // Need to use system errno in this case, as system write will set this errno
+  // which we need to propagate back into our code. fwrite only modifies errno
+  // if there was an error, and errno may have previously been nonzero. Only
+  // return errno if there was an error.
+  size_t members_written = ::fwrite_unlocked(ptr, size, nmemb, f);
+  return {members_written, members_written == nmemb ? 0 : errno};
+}
+#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
+} // namespace internal
+
+namespace printf_core {
+
+LIBC_INLINE int write_to_file_unlocked(cpp::string_view str,
+                                       ::FILE *target_file) {
+  if (str.size() == 0)
+    return WRITE_OK;
+
+  auto write_result = internal::fwrite_unlocked(str.data(), sizeof(char),
+                                                str.size(), target_file);
+  // Propagate actual system error in FileIOResult.
+  if (write_result.has_error())
+    return -write_result.error;
+
+  // In case short write occured or error was not set on FileIOResult for some
+  // reason.
+  if (write_result.value != str.size() ||
+      internal::ferror_unlocked(target_file))
+    return FILE_WRITE_ERROR;
+
+  return WRITE_OK;
+}
+
+// Handles overflow by flushing the current contents of `wb` and `new_str` to
+// the `FILE` pointed to by `fp`.
+template <typename CharT>
+LIBC_INLINE int
+overflow_write_flush_to_file(WriteBuffer<CharT> &wb,
+                             cpp::basic_string_view<CharT> new_str, void *fp) {
+  ::FILE *target_file = reinterpret_cast<::FILE *>(fp);
+  int retval;
+
+  retval = write_to_file_unlocked({wb.buff, wb.buff_cur}, target_file);
+  if (retval < 0)
+    return retval;
+  wb.buff_cur = 0;
+
+  retval = write_to_file_unlocked(new_str, target_file);
+  if (retval < 0)
+    return retval;
+
+  return WRITE_OK;
+}
+
+} // namespace printf_core
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_FILE_WRITER_H
diff --git a/libc/src/__support/printf_core/make_resizing_writer.h b/libc/src/__support/printf_core/make_resizing_writer.h
new file mode 100644
index 0000000000000..31dcd0d510d81
--- /dev/null
+++ b/libc/src/__support/printf_core/make_resizing_writer.h
@@ -0,0 +1,63 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation helpers for Writer<OverflowMode::RESIZE_BUFFER, CharT>.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_RESIZING_WRITER_H
+#define LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_RESIZING_WRITER_H
+
+#include "hdr/func/free.h"
+#include "hdr/func/malloc.h"
+#include "hdr/func/realloc.h"
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/printf_core/writer.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace printf_core {
+
+template <typename CharT>
+LIBC_INLINE Writer<Mode<OverflowMode::RESIZE_BUFFER>::value, CharT>
+make_resizing_writer(CharT *buffer, size_t buffer_len, bool buffer_on_stack) {
+  return Writer(buffer, buffer_len,
+                make_overflow_writer<OverflowMode::RESIZE_BUFFER, CharT>(
+                    buffer_on_stack ? buffer : nullptr));
+}
+
+// Handles overflow by resizing `wb` to have enough capacity for `new_str`, and
+// then copying the `new_str` contents into the buffer.
+template <typename CharT>
+LIBC_INLINE int
+overflow_write_resize_buffer(WriteBuffer<CharT> &wb,
+                             cpp::basic_string_view<CharT> new_str,
+                             void *initial_stack_buffer) {
+  size_t new_size = new_str.size() + wb.buff_cur;
+  const bool is_on_stack = (wb.buff == initial_stack_buffer);
+  char *new_buff = static_cast<char *>(
+      is_on_stack ? malloc(new_size + 1)
+                  : realloc(wb.buff, new_size + 1)); // +1 for null
+  if (new_buff == nullptr) {
+    if (!is_on_stack)
+      free(wb.buff);
+    return ALLOCATION_ERROR;
+  }
+  if (is_on_stack)
+    inline_memcpy(new_buff, wb.buff, wb.buff_cur);
+  wb.buff = new_buff;
+  inline_memcpy(wb.buff + wb.buff_cur, new_str.data(), new_str.size());
+  wb.buff_cur = new_size;
+  wb.buff_len = new_size;
+  return WRITE_OK;
+}
+
+} // namespace printf_core
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_RESIZING_WRITER_H
diff --git a/libc/src/__support/printf_core/make_stderr_writer.h b/libc/src/__support/printf_core/make_stderr_writer.h
new file mode 100644
index 0000000000000..0d2322ed0c74d
--- /dev/null
+++ b/libc/src/__support/printf_core/make_stderr_writer.h
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+///
+/// \file
+/// Implementation helpers for Writer<OverflowMode::FLUSH_TO_STDERR, CharT>.
+///
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_STDERR_WRITER_H
+#define LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_STDERR_WRITER_H
+
+#include "src/__support/CPP/string_view.h"
+#include "src/__support/OSUtil/io.h"
+#include "src/__support/macros/config.h"
+#include "src/__support/printf_core/writer.h"
+
+namespace LIBC_NAMESPACE_DECL {
+namespace printf_core {
+
+template <typename CharT>
+LIBC_INLINE Writer<Mode<OverflowMode::FLUSH_TO_STDERR>::value, CharT>
+make_stderr_writer(CharT *buffer, size_t buffer_len) {
+  return Writer(
+      buffer, buffer_len,
+      make_overflow_writer<OverflowMode::FLUSH_TO_STDERR, CharT>(nullptr));
+}
+
+LIBC_INLINE void flush_to_stderr(WriteBuffer<char> &wb) {
+  if (wb.buff_cur == 0)
+    return;
+
+  write_to_stderr({wb.buff, wb.buff_cur});
+  wb.buff_cur = 0;
+}
+
+// Handles overflow by flushing the current contents of `wb` and `new_str` to
+// stderr.
+template <typename CharT>
+LIBC_INLINE int
+overflow_write_flush_to_stderr(WriteBuffer<CharT> &wb,
+                               cpp::basic_string_view<CharT> new_str, void *) {
+  flush_to_stderr(wb);
+  if (new_str.size() > 0)
+    write_to_stderr(new_str);
+  return WRITE_OK;
+}
+
+} // namespace printf_core
+} // namespace LIBC_NAMESPACE_DECL
+
+#endif // LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_STDERR_WRITER_H
diff --git a/libc/src/__support/printf_core/write_modes.def b/libc/src/__support/printf_core/overflow_modes.def
similarity index 66%
rename from libc/src/__support/printf_core/write_modes.def
rename to libc/src/__support/printf_core/overflow_modes.def
index fdce4d0241f99..a18301e4ebf66 100644
--- a/libc/src/__support/printf_core/write_modes.def
+++ b/libc/src/__support/printf_core/overflow_modes.def
@@ -6,7 +6,8 @@
 //
 //===----------------------------------------------------------------------===//
 
-HANDLE_WRITE_MODE(FILL_BUFF_AND_DROP_OVERFLOW)
-HANDLE_WRITE_MODE(FLUSH_TO_STREAM)
-HANDLE_WRITE_MODE(RESIZE_AND_FILL_BUFF)
-HANDLE_WRITE_MODE(RUNTIME_DISPATCH)
+HANDLE_OVERFLOW_MODE(DROP_OVERFLOW)
+HANDLE_OVERFLOW_MODE(FLUSH_TO_FILE)
+HANDLE_OVERFLOW_MODE(FLUSH_TO_STDERR)
+HANDLE_OVERFLOW_MODE(RESIZE_BUFFER)
+HANDLE_OVERFLOW_MODE(RUNTIME_DISPATCH)
diff --git a/libc/src/__support/printf_core/printf_main.h b/libc/src/__support/printf_core/printf_main.h
index b08e05be25303..9800c33460602 100644
--- a/libc/src/__support/printf_core/printf_main.h
+++ b/libc/src/__support/printf_core/printf_main.h
@@ -22,8 +22,8 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-template <WriteMode write_mode>
-ErrorOr<size_t> printf_main_modular(Writer<write_mode> *writer,
+template <OverflowMode mode>
+ErrorOr<size_t> printf_main_modular(Writer<mode> *writer,
                                     const char *__restrict str,
                                     internal::ArgList &args) {
   Parser<internal::ArgList> parser(str, args);
@@ -42,9 +42,8 @@ ErrorOr<size_t> printf_main_modular(Writer<write_mode> *writer,
   return writer->get_chars_written();
 }
 
-template <WriteMode write_mode>
-ErrorOr<size_t> printf_main(Writer<write_mode> *writer,
-                            const char *__restrict str,
+template <OverflowMode mode>
+ErrorOr<size_t> printf_main(Writer<mode> *writer, const char *__restrict str,
                             internal::ArgList &args) {
 #ifdef LIBC_COPT_PRINTF_MODULAR
   LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
diff --git a/libc/src/__support/printf_core/ptr_converter.h b/libc/src/__support/printf_core/ptr_converter.h
index 692933a63f38b..6415439bbb2a5 100644
--- a/libc/src/__support/printf_core/ptr_converter.h
+++ b/libc/src/__support/printf_core/ptr_converter.h
@@ -18,8 +18,8 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_pointer(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_pointer(Writer<mode> *writer,
                                 const FormatSection &to_conv) {
   FormatSection new_conv = to_conv;
 
diff --git a/libc/src/__support/printf_core/strerror_converter.h b/libc/src/__support/printf_core/strerror_converter.h
index ec02a9a52f99a..4e4e61295b533 100644
--- a/libc/src/__support/printf_core/strerror_converter.h
+++ b/libc/src/__support/printf_core/strerror_converter.h
@@ -19,8 +19,8 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_strerror(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_strerror(Writer<mode> *writer,
                                  const FormatSection &to_conv) {
   FormatSection new_conv = to_conv;
   const int error_num = static_cast<int>(to_conv.conv_val_raw);
diff --git a/libc/src/__support/printf_core/string_converter.h b/libc/src/__support/printf_core/string_converter.h
index 4cd1d52a2bab5..5352bd8306ba6 100644
--- a/libc/src/__support/printf_core/string_converter.h
+++ b/libc/src/__support/printf_core/string_converter.h
@@ -27,8 +27,8 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-template <WriteMode write_mode>
-LIBC_INLINE int char_writer(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int char_writer(Writer<mode> *writer,
                             const FormatSection &to_conv) {
   const char *str_ptr = reinterpret_cast<const char *>(to_conv.conv_val_ptr);
   size_t string_len = 0;
@@ -66,8 +66,8 @@ LIBC_INLINE int char_writer(Writer<write_mode> *writer,
 }
 
 #ifndef LIBC_COPT_PRINTF_DISABLE_WIDE
-template <WriteMode write_mode>
-LIBC_INLINE int wchar_writer(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int wchar_writer(Writer<mode> *writer,
                              const FormatSection &to_conv) {
   size_t string_len = 0;
   const char32_t *wstr_ptr =
@@ -121,8 +121,8 @@ LIBC_INLINE int wchar_writer(Writer<write_mode> *writer,
 }
 #endif // LIBC_COPT_PRINTF_DISABLE_WIDE
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_string(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_string(Writer<mode> *writer,
                                const FormatSection &to_conv) {
   int ret = 0;
   if (to_conv.length_modifier == LengthModifier::l) {
diff --git a/libc/src/__support/printf_core/vasprintf_internal.h b/libc/src/__support/printf_core/vasprintf_internal.h
index 969da6ebc46bb..5396161c74115 100644
--- a/libc/src/__support/printf_core/vasprintf_internal.h
+++ b/libc/src/__support/printf_core/vasprintf_internal.h
@@ -9,39 +9,16 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_VASPRINTF_INTERNAL_H
 #define LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_VASPRINTF_INTERNAL_H
 
-#include "hdr/func/free.h"
 #include "hdr/func/malloc.h"
-#include "hdr/func/realloc.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/error_or.h"
 #include "src/__support/printf_core/core_structs.h"
+#include "src/__support/printf_core/make_resizing_writer.h"
 #include "src/__support/printf_core/printf_main.h"
-#include "src/__support/printf_core/writer.h"
 
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str,
-                                     ResizingBuffer *wb) {
-  size_t new_size = new_str.size() + wb->buff_cur;
-  const bool isBuffOnStack = (wb->buff == wb->init_buff);
-  char *new_buff = static_cast<char *>(
-      isBuffOnStack ? malloc(new_size + 1)
-                    : realloc(wb->buff, new_size + 1)); // +1 for null
-  if (new_buff == nullptr) {
-    if (wb->buff != wb->init_buff)
-      free(wb->buff);
-    return ALLOCATION_ERROR;
-  }
-  if (isBuffOnStack)
-    inline_memcpy(new_buff, wb->buff, wb->buff_cur);
-  wb->buff = new_buff;
-  inline_memcpy(wb->buff + wb->buff_cur, new_str.data(), new_str.size());
-  wb->buff_cur = new_size;
-  wb->buff_len = new_size;
-  return printf_core::WRITE_OK;
-}
-
 constexpr size_t DEFAULT_BUFFER_SIZE = 200;
 
 template <bool use_modular = false>
@@ -49,9 +26,8 @@ LIBC_INLINE ErrorOr<size_t> vasprintf_internal(char **ret,
                                                const char *__restrict format,
                                                internal::ArgList args) {
   char init_buff_on_stack[DEFAULT_BUFFER_SIZE];
-  printf_core::ResizingBuffer wb(init_buff_on_stack, DEFAULT_BUFFER_SIZE,
-                                 resize_overflow_hook);
-  printf_core::Writer writer(wb);
+  Writer writer = make_resizing_writer(init_buff_on_stack, DEFAULT_BUFFER_SIZE,
+                                       /* buffer_on_stack = */ true);
 
   auto ret_val = [&] {
     if constexpr (use_modular)
@@ -63,13 +39,14 @@ LIBC_INLINE ErrorOr<size_t> vasprintf_internal(char **ret,
     *ret = nullptr;
     return ret_val;
   }
-  if (wb.buff == init_buff_on_stack) {
+  char *final_buff = writer.get_write_buffer().buff;
+  if (final_buff == init_buff_on_stack) {
     *ret = static_cast<char *>(malloc(ret_val.value() + 1));
     if (ret == nullptr)
       return Error(ALLOCATION_ERROR);
-    inline_memcpy(*ret, wb.buff, ret_val.value());
+    inline_memcpy(*ret, final_buff, ret_val.value());
   } else {
-    *ret = wb.buff;
+    *ret = final_buff;
   }
   (*ret)[ret_val.value()] = '\0';
   return ret_val;
diff --git a/libc/src/__support/printf_core/vfprintf_internal.h b/libc/src/__support/printf_core/vfprintf_internal.h
index 6a62e966f7ff6..1f2ea5960f4c4 100644
--- a/libc/src/__support/printf_core/vfprintf_internal.h
+++ b/libc/src/__support/printf_core/vfprintf_internal.h
@@ -15,87 +15,28 @@
 #include "src/__support/macros/attributes.h" // For LIBC_INLINE
 #include "src/__support/macros/config.h"
 #include "src/__support/printf_core/core_structs.h"
+#include "src/__support/printf_core/make_file_writer.h"
 #include "src/__support/printf_core/printf_main.h"
-#include "src/__support/printf_core/writer.h"
 
 #include "hdr/types/FILE.h"
 
 namespace LIBC_NAMESPACE_DECL {
-
-namespace internal {
-#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
-LIBC_INLINE int ferror_unlocked(FILE *f) {
-  return reinterpret_cast<LIBC_NAMESPACE::File *>(f)->error_unlocked();
-}
-
-LIBC_INLINE void flockfile(FILE *f) {
-  reinterpret_cast<LIBC_NAMESPACE::File *>(f)->lock();
-}
-
-LIBC_INLINE void funlockfile(FILE *f) {
-  reinterpret_cast<LIBC_NAMESPACE::File *>(f)->unlock();
-}
-
-LIBC_INLINE FileIOResult fwrite_unlocked(const void *ptr, size_t size,
-                                         size_t nmemb, FILE *f) {
-  return reinterpret_cast<LIBC_NAMESPACE::File *>(f)->write_unlocked(
-      ptr, size * nmemb);
-}
-#else  // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
-LIBC_INLINE int ferror_unlocked(::FILE *f) { return ::ferror_unlocked(f); }
-
-LIBC_INLINE void flockfile(::FILE *f) { ::flockfile(f); }
-
-LIBC_INLINE void funlockfile(::FILE *f) { ::funlockfile(f); }
-
-LIBC_INLINE FileIOResult fwrite_unlocked(const void *ptr, size_t size,
-                                         size_t nmemb, ::FILE *f) {
-  // Need to use system errno in this case, as system write will set this errno
-  // which we need to propagate back into our code. fwrite only modifies errno
-  // if there was an error, and errno may have previously been nonzero. Only
-  // return errno if there was an error.
-  size_t members_written = ::fwrite_unlocked(ptr, size, nmemb, f);
-  return {members_written, members_written == nmemb ? 0 : errno};
-}
-#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
-} // namespace internal
-
 namespace printf_core {
 
-LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) {
-  ::FILE *target_file = reinterpret_cast<::FILE *>(fp);
-  // Write new_str to the target file. The logic preventing a zero-length write
-  // is in the writer, so we don't check here.
-  auto write_result = internal::fwrite_unlocked(new_str.data(), sizeof(char),
-                                                new_str.size(), target_file);
-  // Propagate actual system error in FileIOResult.
-  if (write_result.has_error())
-    return -write_result.error;
-
-  // In case short write occured or error was not set on FileIOResult for some
-  // reason.
-  if (write_result.value != new_str.size() ||
-      internal::ferror_unlocked(target_file))
-    return FILE_WRITE_ERROR;
-
-  return WRITE_OK;
-}
-
 LIBC_INLINE ErrorOr<size_t> vfprintf_internal(::FILE *__restrict stream,
                                               const char *__restrict format,
                                               internal::ArgList &args) {
   constexpr size_t BUFF_SIZE = 1024;
   char buffer[BUFF_SIZE];
-  printf_core::FlushingBuffer wb(buffer, BUFF_SIZE, &file_write_hook,
-                                 reinterpret_cast<void *>(stream));
-  Writer writer(wb);
+  Writer writer = make_file_writer(buffer, BUFF_SIZE, stream);
   internal::flockfile(stream);
   auto retval = printf_main(&writer, format, args);
   if (!retval.has_value()) {
     internal::funlockfile(stream);
     return retval;
   }
-  int flushval = wb.flush_to_stream();
+  WriteBuffer<char> &wb = writer.get_write_buffer();
+  int flushval = write_to_file_unlocked({wb.buff, wb.buff_cur}, stream);
   if (flushval != WRITE_OK)
     retval = Error(-flushval);
   internal::funlockfile(stream);
diff --git a/libc/src/__support/printf_core/write_int_converter.h b/libc/src/__support/printf_core/write_int_converter.h
index 906af324d10b4..882963c1c44e2 100644
--- a/libc/src/__support/printf_core/write_int_converter.h
+++ b/libc/src/__support/printf_core/write_int_converter.h
@@ -19,8 +19,8 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-template <WriteMode write_mode>
-LIBC_INLINE int convert_write_int(Writer<write_mode> *writer,
+template <OverflowMode mode>
+LIBC_INLINE int convert_write_int(Writer<mode> *writer,
                                   const FormatSection &to_conv) {
 
 #ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
diff --git a/libc/src/__support/printf_core/writer.h b/libc/src/__support/printf_core/writer.h
index e7d3fe530d7b0..31f1557ab09e4 100644
--- a/libc/src/__support/printf_core/writer.h
+++ b/libc/src/__support/printf_core/writer.h
@@ -21,206 +21,242 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
-#define HANDLE_WRITE_MODE(MODE) MODE,
-enum class WriteMode {
-#include "src/__support/printf_core/write_modes.def"
+#define HANDLE_OVERFLOW_MODE(MODE) MODE,
+enum class OverflowMode {
+#include "src/__support/printf_core/overflow_modes.def"
 };
-#undef HANDLE_WRITE_MODE
+#undef HANDLE_OVERFLOW_MODE
 
 // Helper to omit the template argument if we are using runtime dispatch and
 // avoid multiple copies of the converter functions.
-template <WriteMode write_mode> struct Mode {
+template <OverflowMode mode> struct Mode {
 #ifdef LIBC_COPT_PRINTF_RUNTIME_DISPATCH
-  static constexpr WriteMode value = WriteMode::RUNTIME_DISPATCH;
+  static constexpr OverflowMode value = OverflowMode::RUNTIME_DISPATCH;
 #else
-  static constexpr WriteMode value = write_mode;
+  static constexpr OverflowMode value = mode;
 #endif
 };
 
-template <WriteMode write_mode> class Writer;
-
-template <WriteMode write_mode> struct WriteBuffer {
-  char *buff;
+template <typename CharT> struct WriteBuffer {
+  CharT *buff;
   size_t buff_len;
   size_t buff_cur = 0;
-  // The current writing mode in case the user wants runtime dispatch of the
-  // stream writer with function pointers.
-  [[maybe_unused]] WriteMode write_mode_;
-
-protected:
-  LIBC_INLINE WriteBuffer(char *buff, size_t buff_len, WriteMode mode)
-      : buff(buff), buff_len(buff_len), write_mode_(mode) {}
-
-private:
-  friend class Writer<write_mode>;
-  // The overflow_write method will handle the case when adding new_str to
-  // the buffer would overflow it. Specific actions will depend on the buffer
-  // type / write_mode.
-  LIBC_INLINE int overflow_write(cpp::string_view new_str);
 };
 
-// Buffer variant that discards characters that don't fit into the buffer.
-struct DropOverflowBuffer
-    : public WriteBuffer<Mode<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value> {
-  LIBC_INLINE DropOverflowBuffer(char *buff, size_t buff_len)
-      : WriteBuffer<Mode<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>(
-            buff, buff_len, WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) {}
-
-  LIBC_INLINE int fill_remaining_to_buff(cpp::string_view new_str) {
-    if (buff_cur < buff_len) {
-      size_t bytes_to_write = buff_len - buff_cur;
-      if (bytes_to_write > new_str.size()) {
-        bytes_to_write = new_str.size();
-      }
-      inline_memcpy(buff + buff_cur, new_str.data(), bytes_to_write);
-      buff_cur += bytes_to_write;
-    }
-    return WRITE_OK;
+// Function type for handling the slow path in `Writer` when the `WriteBuffer`
+// may not have enough remaining capacity for the new content.
+template <typename CharT>
+using OverflowWriteFn = int (*)(WriteBuffer<CharT> & /* wb */,
+                                cpp::basic_string_view<CharT> /* new_str */,
+                                void * /* state */);
+
+// Handles overflow by filling any remaining space in `wb` with the start of
+// `new_str`, and dropping any excess bytes.
+template <typename CharT>
+LIBC_INLINE int
+overflow_write_drop_overflow(WriteBuffer<CharT> &wb,
+                             cpp::basic_string_view<CharT> new_str, void *) {
+  if (wb.buff_cur < wb.buff_len) {
+    size_t chars_to_write = wb.buff_len - wb.buff_cur;
+    if (chars_to_write > new_str.size())
+      chars_to_write = new_str.size();
+    inline_memcpy(wb.buff + wb.buff_cur, new_str.data(),
+                  chars_to_write * sizeof(CharT));
+    wb.buff_cur += chars_to_write;
   }
+  return WRITE_OK;
+}
+
+// Defined in make_file_writer.h
+template <typename CharT>
+int overflow_write_flush_to_file(WriteBuffer<CharT> &wb,
+                                 cpp::basic_string_view<CharT> new_str,
+                                 void *fp);
+
+// Defined in make_stderr_writer.h
+template <typename CharT>
+int overflow_write_flush_to_stderr(WriteBuffer<CharT> &wb,
+                                   cpp::basic_string_view<CharT> new_str,
+                                   void *);
+
+// Defined in make_resizing_writer.h
+template <typename CharT>
+int overflow_write_resize_buffer(WriteBuffer<CharT> &wb,
+                                 cpp::basic_string_view<CharT> new_str,
+                                 void *initial_stack_buffer);
+
+// Trait type helper mapping `OverflowMode` to the corresponding function.
+template <OverflowMode mode, typename CharT> struct OverflowModeToWriteFn {};
+
+template <typename CharT>
+struct OverflowModeToWriteFn<OverflowMode::DROP_OVERFLOW, CharT> {
+  static constexpr OverflowWriteFn<CharT> value =
+      overflow_write_drop_overflow<CharT>;
 };
 
-// Buffer variant that flushes to stream when it gets full.
-struct FlushingBuffer
-    : public WriteBuffer<Mode<WriteMode::FLUSH_TO_STREAM>::value> {
-  // The stream writer will be called when the buffer is full. It will be passed
-  // string_views to write to the stream.
-  using StreamWriter = int (*)(cpp::string_view, void *);
-  const StreamWriter stream_writer;
-  void *output_target;
-
-  LIBC_INLINE FlushingBuffer(char *buff, size_t buff_len, StreamWriter hook,
-                             void *target)
-      : WriteBuffer<Mode<WriteMode::FLUSH_TO_STREAM>::value>(
-            buff, buff_len, WriteMode::FLUSH_TO_STREAM),
-        stream_writer(hook), output_target(target) {}
-
-  // Flushes the entire current buffer to stream, followed by the new_str (if
-  // non-empty).
-  LIBC_INLINE int flush_to_stream(cpp::string_view new_str) {
-    if (buff_cur > 0) {
-      int retval = stream_writer({buff, buff_cur}, output_target);
-      if (retval < 0)
-        return retval;
-    }
-    if (new_str.size() > 0) {
-      int retval = stream_writer(new_str, output_target);
-      if (retval < 0)
-        return retval;
-    }
-    buff_cur = 0;
-    return WRITE_OK;
+template <typename CharT>
+struct OverflowModeToWriteFn<OverflowMode::FLUSH_TO_FILE, CharT> {
+  static constexpr OverflowWriteFn<CharT> value =
+      overflow_write_flush_to_file<CharT>;
+};
+
+template <typename CharT>
+struct OverflowModeToWriteFn<OverflowMode::FLUSH_TO_STDERR, CharT> {
+  static constexpr OverflowWriteFn<CharT> value =
+      overflow_write_flush_to_stderr<CharT>;
+};
+
+template <typename CharT>
+struct OverflowModeToWriteFn<OverflowMode::RESIZE_BUFFER, CharT> {
+  static constexpr OverflowWriteFn<CharT> value =
+      overflow_write_resize_buffer<CharT>;
+};
+
+// Helper template used by `Writer` to dispatch to the appropriate
+// `OverflowWriteFn`.
+template <OverflowMode mode, typename CharT> struct OverflowWriter;
+
+template <typename CharT>
+struct OverflowWriter<OverflowMode::DROP_OVERFLOW, CharT> {
+  LIBC_INLINE OverflowWriter(OverflowWriteFn<CharT>, void *) {}
+
+  LIBC_INLINE int write(WriteBuffer<CharT> &wb,
+                        cpp::basic_string_view<CharT> new_str) {
+    return overflow_write_drop_overflow<CharT>(wb, new_str, nullptr);
+  }
+};
+
+template <typename CharT>
+struct OverflowWriter<OverflowMode::FLUSH_TO_FILE, CharT> {
+  void *file;
+
+  LIBC_INLINE OverflowWriter(OverflowWriteFn<CharT>, void *file) : file(file) {}
+
+  LIBC_INLINE int write(WriteBuffer<CharT> &wb,
+                        cpp::basic_string_view<CharT> new_str) {
+    return overflow_write_flush_to_file(wb, new_str, file);
   }
+};
+
+template <typename CharT>
+struct OverflowWriter<OverflowMode::FLUSH_TO_STDERR, CharT> {
+  LIBC_INLINE OverflowWriter(OverflowWriteFn<CharT>, void *) {}
 
-  LIBC_INLINE int flush_to_stream() { return flush_to_stream({}); }
+  LIBC_INLINE int write(WriteBuffer<CharT> &wb,
+                        cpp::basic_string_view<CharT> new_str) {
+    return overflow_write_flush_to_stderr(wb, new_str, nullptr);
+  }
 };
 
-// Buffer variant that calls a resizing callback when it gets full.
-struct ResizingBuffer
-    : public WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> {
-  using ResizeWriter = int (*)(cpp::string_view, ResizingBuffer *);
-  const ResizeWriter resize_writer;
-  const char *init_buff; // for checking when resize.
-
-  LIBC_INLINE ResizingBuffer(char *buff, size_t buff_len, ResizeWriter hook)
-      : WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value>(
-            buff, buff_len, WriteMode::RESIZE_AND_FILL_BUFF),
-        resize_writer(hook), init_buff(buff) {}
-
-  // Invokes the callback that is supposed to resize the buffer and make
-  // it large enough to fit the new_str addition.
-  LIBC_INLINE int resize_and_write(cpp::string_view new_str) {
-    return resize_writer(new_str, this);
+template <typename CharT>
+struct OverflowWriter<OverflowMode::RESIZE_BUFFER, CharT> {
+  void *initial_stack_buffer;
+
+  LIBC_INLINE OverflowWriter(OverflowWriteFn<CharT>, void *initial_stack_buffer)
+      : initial_stack_buffer(initial_stack_buffer) {}
+
+  LIBC_INLINE int write(WriteBuffer<CharT> &wb,
+                        cpp::basic_string_view<CharT> new_str) {
+    return overflow_write_resize_buffer(wb, new_str, initial_stack_buffer);
   }
 };
 
-template <>
-LIBC_INLINE int WriteBuffer<WriteMode::RUNTIME_DISPATCH>::overflow_write(
-    cpp::string_view new_str) {
-  if (write_mode_ == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW)
-    return reinterpret_cast<DropOverflowBuffer *>(this)->fill_remaining_to_buff(
-        new_str);
-  else if (write_mode_ == WriteMode::FLUSH_TO_STREAM)
-    return reinterpret_cast<FlushingBuffer *>(this)->flush_to_stream(new_str);
-  else if (write_mode_ == WriteMode::RESIZE_AND_FILL_BUFF)
-    return reinterpret_cast<ResizingBuffer *>(this)->resize_and_write(new_str);
-  __builtin_unreachable();
-}
+template <typename CharT>
+struct OverflowWriter<OverflowMode::RUNTIME_DISPATCH, CharT> {
+  OverflowWriteFn<CharT> runtime_fn;
+  void *state;
 
-template <>
-LIBC_INLINE int
-WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::overflow_write(
-    cpp::string_view new_str) {
-  return reinterpret_cast<DropOverflowBuffer *>(this)->fill_remaining_to_buff(
-      new_str);
-}
+  LIBC_INLINE OverflowWriter(OverflowWriteFn<CharT> runtime_fn, void *state)
+      : runtime_fn(runtime_fn), state(state) {}
+
+  LIBC_INLINE int write(WriteBuffer<CharT> &wb,
+                        cpp::basic_string_view<CharT> new_str) {
+    return runtime_fn(wb, new_str, state);
+  }
+};
 
-template <>
-LIBC_INLINE int WriteBuffer<WriteMode::FLUSH_TO_STREAM>::overflow_write(
-    cpp::string_view new_str) {
-  return reinterpret_cast<FlushingBuffer *>(this)->flush_to_stream(new_str);
+// Constructs an `OverflowWriter` that dispatches according to
+// `LIBC_COPT_PRINTF_RUNTIME_DISPATCH`.
+template <OverflowMode mode, typename CharT>
+LIBC_INLINE OverflowWriter<Mode<mode>::value, CharT>
+make_overflow_writer(void *state) {
+  return OverflowWriter<Mode<mode>::value, CharT>(
+      OverflowModeToWriteFn<mode, CharT>::value, state);
 }
 
-template <>
-LIBC_INLINE int WriteBuffer<WriteMode::RESIZE_AND_FILL_BUFF>::overflow_write(
-    cpp::string_view new_str) {
-  return reinterpret_cast<ResizingBuffer *>(this)->resize_and_write(new_str);
+// Fills the `dest` buffer with `count` copies of `value`.
+template <typename CharT>
+LIBC_INLINE void fill_buffer(CharT *dest, CharT value, size_t count) {
+  if constexpr (sizeof(CharT) == sizeof(unsigned char)) {
+    inline_memset(dest, static_cast<unsigned char>(value), count);
+  } else {
+    for (size_t i = 0; i < count; ++i)
+      dest[i] = value;
+  }
 }
 
-template <WriteMode write_mode> class Writer final {
-  WriteBuffer<write_mode> &wb;
+template <OverflowMode mode, typename CharT = char> class Writer final {
+  WriteBuffer<CharT> wb;
   size_t chars_written = 0;
+  OverflowWriter<mode, CharT> overflow_writer;
 
-  LIBC_INLINE int pad(char new_char, size_t length) {
+  LIBC_INLINE int pad(CharT new_char, size_t length) {
     // First, fill as much of the buffer as possible with the padding char.
     size_t written = 0;
     const size_t buff_space = wb.buff_len - wb.buff_cur;
     // ASSERT: length > buff_space
     if (buff_space > 0) {
-      inline_memset(wb.buff + wb.buff_cur, new_char, buff_space);
+      fill_buffer(wb.buff + wb.buff_cur, new_char, buff_space);
       wb.buff_cur += buff_space;
       written = buff_space;
     }
 
     // Next, overflow write the rest of length using the mini_buff.
     constexpr size_t MINI_BUFF_SIZE = 64;
-    char mini_buff[MINI_BUFF_SIZE];
-    inline_memset(mini_buff, new_char, MINI_BUFF_SIZE);
-    cpp::string_view mb_string_view(mini_buff, MINI_BUFF_SIZE);
+    CharT mini_buff[MINI_BUFF_SIZE];
+    fill_buffer(mini_buff, new_char, MINI_BUFF_SIZE);
+    cpp::basic_string_view<CharT> mb_string_view(mini_buff, MINI_BUFF_SIZE);
     while (written + MINI_BUFF_SIZE < length) {
-      int result = wb.overflow_write(mb_string_view);
+      int result = overflow_writer.write(wb, mb_string_view);
       if (result != WRITE_OK)
         return result;
       written += MINI_BUFF_SIZE;
     }
-    cpp::string_view mb_substr = mb_string_view.substr(0, length - written);
-    return wb.overflow_write(mb_substr);
+    cpp::basic_string_view<CharT> mb_substr =
+        mb_string_view.substr(0, length - written);
+    return overflow_writer.write(wb, mb_substr);
   }
 
 public:
-  LIBC_INLINE Writer(WriteBuffer<write_mode> &wb) : wb(wb) {}
+  // Prefer using the factory functions in make_*_writer.h or
+  // `make_drop_overflow_writer` below, instead of directly calling this
+  // constructor.
+  LIBC_INLINE Writer(CharT *buffer, size_t buffer_len,
+                     OverflowWriter<mode, CharT> overflow_writer)
+      : wb{.buff = buffer, .buff_len = buffer_len},
+        overflow_writer(overflow_writer) {}
 
   // Takes a string, copies it into the buffer if there is space, else passes it
   // to the overflow mechanism to be handled separately.
-  LIBC_INLINE int write(cpp::string_view new_string) {
+  LIBC_INLINE int write(cpp::basic_string_view<CharT> new_string) {
     chars_written += new_string.size();
     if (LIBC_LIKELY(wb.buff_cur + new_string.size() <= wb.buff_len)) {
       inline_memcpy(wb.buff + wb.buff_cur, new_string.data(),
-                    new_string.size());
+                    new_string.size() * sizeof(CharT));
       wb.buff_cur += new_string.size();
       return WRITE_OK;
     }
-    return wb.overflow_write(new_string);
+    return overflow_writer.write(wb, new_string);
   }
 
   // Takes a char and a length, memsets the next length characters of the buffer
   // if there is space, else calls pad which will loop and call the overflow
   // mechanism on a secondary buffer.
-  LIBC_INLINE int write(char new_char, size_t length) {
+  LIBC_INLINE int write(CharT new_char, size_t length) {
     chars_written += length;
 
     if (LIBC_LIKELY(wb.buff_cur + length <= wb.buff_len)) {
-      inline_memset(wb.buff + wb.buff_cur, static_cast<unsigned char>(new_char),
-                    length);
+      fill_buffer(wb.buff + wb.buff_cur, new_char, length);
       wb.buff_cur += length;
       return WRITE_OK;
     }
@@ -229,27 +265,32 @@ template <WriteMode write_mode> class Writer final {
 
   // Takes a char, copies it into the buffer if there is space, else passes it
   // to the overflow mechanism to be handled separately.
-  LIBC_INLINE int write(char new_char) {
+  LIBC_INLINE int write(CharT new_char) {
     chars_written += 1;
     if (LIBC_LIKELY(wb.buff_cur + 1 <= wb.buff_len)) {
       wb.buff[wb.buff_cur] = new_char;
       wb.buff_cur += 1;
       return WRITE_OK;
     }
-    cpp::string_view char_string_view(&new_char, 1);
-    return wb.overflow_write(char_string_view);
+    return overflow_writer.write(wb, {&new_char, 1});
   }
 
   LIBC_INLINE size_t get_chars_written() { return chars_written; }
+
+  LIBC_INLINE WriteBuffer<CharT> &get_write_buffer() { return wb; }
 };
 
-// Class-template auto deduction helpers.
-Writer(WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>)
-    -> Writer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>;
-Writer(WriteBuffer<WriteMode::RESIZE_AND_FILL_BUFF>)
-    -> Writer<WriteMode::RESIZE_AND_FILL_BUFF>;
-Writer(WriteBuffer<WriteMode::FLUSH_TO_STREAM>)
-    -> Writer<WriteMode::FLUSH_TO_STREAM>;
+// Class-template auto deduction helper.
+template <OverflowMode mode, typename CharT>
+Writer(CharT *, size_t, OverflowWriter<mode, CharT>) -> Writer<mode, CharT>;
+
+template <typename CharT>
+LIBC_INLINE Writer<Mode<OverflowMode::DROP_OVERFLOW>::value, CharT>
+make_drop_overflow_writer(CharT *buffer, size_t buffer_len) {
+  return Writer(
+      buffer, buffer_len,
+      make_overflow_writer<OverflowMode::DROP_OVERFLOW, CharT>(nullptr));
+}
 
 } // namespace printf_core
 } // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/err/report.cpp b/libc/src/err/report.cpp
index e3ca01a9cbd5c..9c02b0ea8abdf 100644
--- a/libc/src/err/report.cpp
+++ b/libc/src/err/report.cpp
@@ -20,8 +20,8 @@
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
+#include "src/__support/printf_core/make_stderr_writer.h"
 #include "src/__support/printf_core/printf_main.h"
-#include "src/__support/printf_core/writer.h"
 
 #ifdef LIBC_FULL_BUILD
 #include "src/errno/program_invocation_short_name.h"
@@ -41,14 +41,9 @@ void report(bool show_err, int err_num, const char *fmt,
   if (!progname)
     progname = "";
   char buffer[1024];
-  printf_core::FlushingBuffer wb(
-      buffer, sizeof(buffer),
-      [](cpp::string_view str, [[maybe_unused]] void *raw_stream) -> int {
-        write_to_stderr(str);
-        return static_cast<int>(str.size());
-      },
-      nullptr);
-  printf_core::Writer writer(wb);
+  printf_core::Writer writer =
+      printf_core::make_stderr_writer(buffer, sizeof(buffer));
+  printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
 
   writer.write(progname);
   if (fmt != nullptr || show_err)
@@ -56,7 +51,7 @@ void report(bool show_err, int err_num, const char *fmt,
 
   if (fmt != nullptr) {
     if (!printf_core::printf_main(&writer, fmt, args)) {
-      wb.flush_to_stream();
+      printf_core::flush_to_stderr(wb);
       return;
     }
     if (show_err)
@@ -67,7 +62,7 @@ void report(bool show_err, int err_num, const char *fmt,
     writer.write(get_error_string(err_num));
 
   writer.write("\n");
-  wb.flush_to_stream();
+  printf_core::flush_to_stderr(wb);
 }
 
 } // namespace err_reporting
diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp
index ec1364ca6efc2..cf500c044f9ac 100644
--- a/libc/src/stdio/snprintf.cpp
+++ b/libc/src/stdio/snprintf.cpp
@@ -31,8 +31,8 @@ LLVM_LIBC_FUNCTION(int, snprintf,
                                  // and pointer semantics, as well as handling
                                  // destruction automatically.
   va_end(vlist);
-  printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
-  printf_core::Writer writer(wb);
+  printf_core::Writer writer = printf_core::make_drop_overflow_writer(
+      buffer, (buffsz > 0 ? buffsz - 1 : 0));
 
 #ifdef LIBC_COPT_PRINTF_MODULAR
   LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
@@ -44,8 +44,10 @@ LLVM_LIBC_FUNCTION(int, snprintf,
     libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
-  if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
+  if (buffsz > 0) { // if the buffsz is 0 the buffer may be a null pointer.
+    printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
     wb.buff[wb.buff_cur] = '\0';
+  }
 
   if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp
index 702ea5d43c69a..bbb452b286167 100644
--- a/libc/src/stdio/sprintf.cpp
+++ b/libc/src/stdio/sprintf.cpp
@@ -31,9 +31,8 @@ LLVM_LIBC_FUNCTION(int, sprintf,
                                  // destruction automatically.
   va_end(vlist);
 
-  printf_core::DropOverflowBuffer wb(buffer,
-                                     cpp::numeric_limits<size_t>::max());
-  printf_core::Writer writer(wb);
+  printf_core::Writer writer = printf_core::make_drop_overflow_writer(
+      buffer, cpp::numeric_limits<size_t>::max());
 
 #ifdef LIBC_COPT_PRINTF_MODULAR
   LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
@@ -45,6 +44,7 @@ LLVM_LIBC_FUNCTION(int, sprintf,
     libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
+  printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
   wb.buff[wb.buff_cur] = '\0';
 
   if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp
index b25daed931fce..5174152669f38 100644
--- a/libc/src/stdio/vsnprintf.cpp
+++ b/libc/src/stdio/vsnprintf.cpp
@@ -28,8 +28,8 @@ LLVM_LIBC_FUNCTION(int, vsnprintf,
   internal::ArgList args(vlist); // This holder class allows for easier copying
                                  // and pointer semantics, as well as handling
                                  // destruction automatically.
-  printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
-  printf_core::Writer writer(wb);
+  printf_core::Writer writer = printf_core::make_drop_overflow_writer(
+      buffer, (buffsz > 0 ? buffsz - 1 : 0));
 
 #ifdef LIBC_COPT_PRINTF_MODULAR
   LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
@@ -41,8 +41,10 @@ LLVM_LIBC_FUNCTION(int, vsnprintf,
     libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
-  if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
+  if (buffsz > 0) { // if the buffsz is 0 the buffer may be a null pointer.
+    printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
     wb.buff[wb.buff_cur] = '\0';
+  }
 
   if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
     libc_errno =
diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp
index f1af2f6143ad2..2a82045060cf3 100644
--- a/libc/src/stdio/vsprintf.cpp
+++ b/libc/src/stdio/vsprintf.cpp
@@ -28,9 +28,8 @@ LLVM_LIBC_FUNCTION(int, vsprintf,
                                  // and pointer semantics, as well as handling
                                  // destruction automatically.
 
-  printf_core::DropOverflowBuffer wb(buffer,
-                                     cpp::numeric_limits<size_t>::max());
-  printf_core::Writer writer(wb);
+  printf_core::Writer writer = printf_core::make_drop_overflow_writer(
+      buffer, cpp::numeric_limits<size_t>::max());
 
 #ifdef LIBC_COPT_PRINTF_MODULAR
   LIBC_INLINE_ASM(".reloc ., BFD_RELOC_NONE, __printf_float");
@@ -42,6 +41,7 @@ LLVM_LIBC_FUNCTION(int, vsprintf,
     libc_errno = printf_core::internal_error_to_errno(ret_val.error());
     return -1;
   }
+  printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
   wb.buff[wb.buff_cur] = '\0';
 
   if (ret_val.value() > static_cast<size_t>(cpp::numeric_limits<int>::max())) {
diff --git a/libc/src/stdlib/str_from_util.h b/libc/src/stdlib/str_from_util.h
index 8cd147580f719..205adcff17094 100644
--- a/libc/src/stdlib/str_from_util.h
+++ b/libc/src/stdlib/str_from_util.h
@@ -104,8 +104,8 @@ printf_core::FormatSection parse_format_string(const char *__restrict format,
   return section;
 }
 
-template <typename T, printf_core::WriteMode write_mode>
-int strfromfloat_convert(printf_core::Writer<write_mode> *writer,
+template <typename T, printf_core::OverflowMode overflow_mode>
+int strfromfloat_convert(printf_core::Writer<overflow_mode> *writer,
                          const printf_core::FormatSection &section) {
   if (!section.has_conv)
     return writer->write(section.raw_string);
diff --git a/libc/src/stdlib/strfromd.cpp b/libc/src/stdlib/strfromd.cpp
index dbf46b7c0534b..8983a0f5787d8 100644
--- a/libc/src/stdlib/strfromd.cpp
+++ b/libc/src/stdlib/strfromd.cpp
@@ -22,8 +22,8 @@ LLVM_LIBC_FUNCTION(int, strfromd,
 
   printf_core::FormatSection section =
       internal::parse_format_string(format, fp);
-  printf_core::DropOverflowBuffer wb(s, (n > 0 ? n - 1 : 0));
-  printf_core::Writer writer(wb);
+  printf_core::Writer writer =
+      printf_core::make_drop_overflow_writer(s, (n > 0 ? n - 1 : 0));
 
   int result = 0;
   if (section.has_conv)
@@ -34,8 +34,10 @@ LLVM_LIBC_FUNCTION(int, strfromd,
   if (result < 0)
     return result;
 
-  if (n > 0)
+  if (n > 0) {
+    printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
     wb.buff[wb.buff_cur] = '\0';
+  }
 
   if (writer.get_chars_written() >
       static_cast<size_t>(cpp::numeric_limits<int>::max())) {
diff --git a/libc/src/stdlib/strfromf.cpp b/libc/src/stdlib/strfromf.cpp
index d1ea47d55a250..417372d576af6 100644
--- a/libc/src/stdlib/strfromf.cpp
+++ b/libc/src/stdlib/strfromf.cpp
@@ -22,8 +22,8 @@ LLVM_LIBC_FUNCTION(int, strfromf,
 
   printf_core::FormatSection section =
       internal::parse_format_string(format, fp);
-  printf_core::DropOverflowBuffer wb(s, (n > 0 ? n - 1 : 0));
-  printf_core::Writer writer(wb);
+  printf_core::Writer writer =
+      printf_core::make_drop_overflow_writer(s, (n > 0 ? n - 1 : 0));
 
   int result = 0;
   if (section.has_conv)
@@ -34,8 +34,10 @@ LLVM_LIBC_FUNCTION(int, strfromf,
   if (result < 0)
     return result;
 
-  if (n > 0)
+  if (n > 0) {
+    printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
     wb.buff[wb.buff_cur] = '\0';
+  }
 
   if (writer.get_chars_written() >
       static_cast<size_t>(cpp::numeric_limits<int>::max())) {
diff --git a/libc/src/stdlib/strfroml.cpp b/libc/src/stdlib/strfroml.cpp
index c23c12fc91c27..2f32db0966b60 100644
--- a/libc/src/stdlib/strfroml.cpp
+++ b/libc/src/stdlib/strfroml.cpp
@@ -27,8 +27,8 @@ LLVM_LIBC_FUNCTION(int, strfroml,
   // the length modifier has to be set to LengthModifier::L
   section.length_modifier = printf_core::LengthModifier::L;
 
-  printf_core::DropOverflowBuffer wb(s, (n > 0 ? n - 1 : 0));
-  printf_core::Writer writer(wb);
+  printf_core::Writer writer =
+      printf_core::make_drop_overflow_writer(s, (n > 0 ? n - 1 : 0));
 
   int result = 0;
   if (section.has_conv)
@@ -39,8 +39,10 @@ LLVM_LIBC_FUNCTION(int, strfroml,
   if (result < 0)
     return result;
 
-  if (n > 0)
+  if (n > 0) {
+    printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
     wb.buff[wb.buff_cur] = '\0';
+  }
 
   if (writer.get_chars_written() >
       static_cast<size_t>(cpp::numeric_limits<int>::max())) {
diff --git a/libc/src/time/asctime_utils.h b/libc/src/time/asctime_utils.h
index f81965a6a2045..95df9de4682cf 100644
--- a/libc/src/time/asctime_utils.h
+++ b/libc/src/time/asctime_utils.h
@@ -38,9 +38,8 @@ LIBC_INLINE ErrorOr<char *> asctime(const tm *timeptr, char *buffer,
       timeptr->tm_mon > (time_constants::MONTHS_PER_YEAR - 1))
     return cpp::unexpected(EINVAL);
 
-  printf_core::DropOverflowBuffer wb(buffer,
-                                     buffer_length > 0 ? buffer_length - 1 : 0);
-  printf_core::Writer writer(wb);
+  printf_core::Writer writer = printf_core::make_drop_overflow_writer(
+      buffer, (buffer_length > 0 ? buffer_length - 1 : 0));
 
   auto res = strftime_core::strftime_main(&writer, "%a %b %e %T %Y\n", timeptr);
   if (!res.has_value())
diff --git a/libc/src/time/strftime.cpp b/libc/src/time/strftime.cpp
index 5522a2c1d2e4b..a6a202455d0d2 100644
--- a/libc/src/time/strftime.cpp
+++ b/libc/src/time/strftime.cpp
@@ -25,11 +25,13 @@ LLVM_LIBC_FUNCTION(size_t, strftime,
   LIBC_CRASH_ON_NULLPTR(format);
   LIBC_CRASH_ON_NULLPTR(timeptr);
 
-  printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
-  printf_core::Writer writer(wb);
+  printf_core::Writer writer = printf_core::make_drop_overflow_writer(
+      buffer, (buffsz > 0 ? buffsz - 1 : 0));
   auto ret = strftime_core::strftime_main(&writer, format, timeptr);
-  if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
+  if (buffsz > 0) { // if the buffsz is 0 the buffer may be a null pointer.
+    const printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
     wb.buff[wb.buff_cur] = '\0';
+  }
   return (!ret.has_value() || ret.value() >= buffsz) ? 0 : ret.value();
 }
 
diff --git a/libc/src/time/strftime_core/composite_converter.h b/libc/src/time/strftime_core/composite_converter.h
index ec279615806e9..fdc52828a49e1 100644
--- a/libc/src/time/strftime_core/composite_converter.h
+++ b/libc/src/time/strftime_core/composite_converter.h
@@ -44,8 +44,8 @@ get_specific_int_format(const tm *timeptr, const FormatSection &base_to_conv,
   return result;
 }
 
-template <printf_core::WriteMode write_mode>
-LIBC_INLINE int convert_date_us(printf_core::Writer<write_mode> *writer,
+template <printf_core::OverflowMode overflow_mode>
+LIBC_INLINE int convert_date_us(printf_core::Writer<overflow_mode> *writer,
                                 const FormatSection &to_conv,
                                 const tm *timeptr) {
   // format is %m/%d/%y (month/day/year)
@@ -67,8 +67,8 @@ LIBC_INLINE int convert_date_us(printf_core::Writer<write_mode> *writer,
   return WRITE_OK;
 }
 
-template <printf_core::WriteMode write_mode>
-LIBC_INLINE int convert_date_iso(printf_core::Writer<write_mode> *writer,
+template <printf_core::OverflowMode overflow_mode>
+LIBC_INLINE int convert_date_iso(printf_core::Writer<overflow_mode> *writer,
                                  const FormatSection &to_conv,
                                  const tm *timeptr) {
   // format is "%Y-%m-%d" (year-month-day)
@@ -90,8 +90,8 @@ LIBC_INLINE int convert_date_iso(printf_core::Writer<write_mode> *writer,
   return WRITE_OK;
 }
 
-template <printf_core::WriteMode write_mode>
-LIBC_INLINE int convert_time_am_pm(printf_core::Writer<write_mode> *writer,
+template <printf_core::OverflowMode overflow_mode>
+LIBC_INLINE int convert_time_am_pm(printf_core::Writer<overflow_mode> *writer,
                                    const FormatSection &to_conv,
                                    const tm *timeptr) {
   // format is "%I:%M:%S %p" (hour:minute:second AM/PM)
@@ -118,8 +118,8 @@ LIBC_INLINE int convert_time_am_pm(printf_core::Writer<write_mode> *writer,
   return WRITE_OK;
 }
 
-template <printf_core::WriteMode write_mode>
-LIBC_INLINE int convert_time_minute(printf_core::Writer<write_mode> *writer,
+template <printf_core::OverflowMode overflow_mode>
+LIBC_INLINE int convert_time_minute(printf_core::Writer<overflow_mode> *writer,
                                     const FormatSection &to_conv,
                                     const tm *timeptr) {
   // format is "%H:%M" (hour:minute)
@@ -138,8 +138,8 @@ LIBC_INLINE int convert_time_minute(printf_core::Writer<write_mode> *writer,
   return WRITE_OK;
 }
 
-template <printf_core::WriteMode write_mode>
-LIBC_INLINE int convert_time_second(printf_core::Writer<write_mode> *writer,
+template <printf_core::OverflowMode overflow_mode>
+LIBC_INLINE int convert_time_second(printf_core::Writer<overflow_mode> *writer,
                                     const FormatSection &to_conv,
                                     const tm *timeptr) {
   // format is "%H:%M:%S" (hour:minute:second)
@@ -161,10 +161,10 @@ LIBC_INLINE int convert_time_second(printf_core::Writer<write_mode> *writer,
   return WRITE_OK;
 }
 
-template <printf_core::WriteMode write_mode>
-LIBC_INLINE int convert_full_date_time(printf_core::Writer<write_mode> *writer,
-                                       const FormatSection &to_conv,
-                                       const tm *timeptr) {
+template <printf_core::OverflowMode overflow_mode>
+LIBC_INLINE int
+convert_full_date_time(printf_core::Writer<overflow_mode> *writer,
+                       const FormatSection &to_conv, const tm *timeptr) {
   const time_utils::TMReader time_reader(timeptr);
   // format is "%a %b %e %T %Y" (weekday month mday [time] year)
   // we only pad the first conversion, and we assume all the other values are in
@@ -200,8 +200,8 @@ LIBC_INLINE int convert_full_date_time(printf_core::Writer<write_mode> *writer,
   return WRITE_OK;
 }
 
-template <printf_core::WriteMode write_mode>
-LIBC_INLINE int convert_composite(printf_core::Writer<write_mode> *writer,
+template <printf_core::OverflowMode overflow_mode>
+LIBC_INLINE int convert_composite(printf_core::Writer<overflow_mode> *writer,
                                   const FormatSection &to_conv,
                                   const tm *timeptr) {
   switch (to_conv.conv_name) {
diff --git a/libc/src/time/strftime_core/converter.h b/libc/src/time/strftime_core/converter.h
index b6203ae8b72d0..3e6bab6df2b60 100644
--- a/libc/src/time/strftime_core/converter.h
+++ b/libc/src/time/strftime_core/converter.h
@@ -27,14 +27,14 @@ namespace strftime_core {
 
 /// Converts a format section and writes it to the writer.
 ///
-/// \tparam write_mode The write mode for the writer.
+/// \tparam overflow_mode The write overflow mode for the writer.
 /// \param writer The writer to write the output to.
 /// \param to_conv The format section to convert.
 /// \param timeptr Pointer to the tm structure.
 /// \return Number of characters written on success, or a negative error code on
 /// failure.
-template <printf_core::WriteMode write_mode>
-int convert(printf_core::Writer<write_mode> *writer,
+template <printf_core::OverflowMode overflow_mode>
+int convert(printf_core::Writer<overflow_mode> *writer,
             const FormatSection &to_conv, const tm *timeptr) {
   // TODO: Implement the locale support.
   // Currently locale flags are ignored, as described by the posix standard for
diff --git a/libc/src/time/strftime_core/num_converter.h b/libc/src/time/strftime_core/num_converter.h
index 6bcc166fecf93..65c3406a73ea2 100644
--- a/libc/src/time/strftime_core/num_converter.h
+++ b/libc/src/time/strftime_core/num_converter.h
@@ -31,8 +31,8 @@ struct IntFormatSection {
   char padding_char = '0';
 };
 
-template <printf_core::WriteMode write_mode>
-LIBC_INLINE int write_padded_int(printf_core::Writer<write_mode> *writer,
+template <printf_core::OverflowMode overflow_mode>
+LIBC_INLINE int write_padded_int(printf_core::Writer<overflow_mode> *writer,
                                  const IntFormatSection &num_info) {
 
   DecFmt d(num_info.num);
@@ -193,8 +193,8 @@ get_int_format(const FormatSection &to_conv, const tm *timeptr) {
   return result;
 }
 
-template <printf_core::WriteMode write_mode>
-LIBC_INLINE int convert_int(printf_core::Writer<write_mode> *writer,
+template <printf_core::OverflowMode overflow_mode>
+LIBC_INLINE int convert_int(printf_core::Writer<overflow_mode> *writer,
                             const FormatSection &to_conv, const tm *timeptr) {
   auto num_info_or = get_int_format(to_conv, timeptr);
   if (!num_info_or)
diff --git a/libc/src/time/strftime_core/str_converter.h b/libc/src/time/strftime_core/str_converter.h
index 4a636679c4a18..8941abac35d5d 100644
--- a/libc/src/time/strftime_core/str_converter.h
+++ b/libc/src/time/strftime_core/str_converter.h
@@ -35,13 +35,13 @@ unwrap_opt(cpp::optional<cpp::string_view> str_opt) {
 
 /// Converts string-based format specifiers (like %a, %Z) and writes to writer.
 ///
-/// \tparam write_mode The write mode for the writer.
+/// \tparam overflow_mode The write overflow mode for the writer.
 /// \param writer The writer to write the output to.
 /// \param to_conv The format section to convert.
 /// \param timeptr Pointer to the tm structure.
 /// \return WRITE_OK on success, or negative value on error.
-template <printf_core::WriteMode write_mode>
-LIBC_INLINE int convert_str(printf_core::Writer<write_mode> *writer,
+template <printf_core::OverflowMode overflow_mode>
+LIBC_INLINE int convert_str(printf_core::Writer<overflow_mode> *writer,
                             const FormatSection &to_conv, const tm *timeptr) {
   cpp::string_view str;
   cpp::optional<cpp::string_view> str_opt;
diff --git a/libc/src/time/strftime_core/strftime_main.h b/libc/src/time/strftime_core/strftime_main.h
index 4f958fb5bb294..75a734f176918 100644
--- a/libc/src/time/strftime_core/strftime_main.h
+++ b/libc/src/time/strftime_core/strftime_main.h
@@ -20,8 +20,8 @@
 namespace LIBC_NAMESPACE_DECL {
 namespace strftime_core {
 
-template <printf_core::WriteMode write_mode>
-ErrorOr<size_t> strftime_main(printf_core::Writer<write_mode> *writer,
+template <printf_core::OverflowMode overflow_mode>
+ErrorOr<size_t> strftime_main(printf_core::Writer<overflow_mode> *writer,
                               const char *__restrict str, const tm *timeptr) {
   Parser parser(str);
   int result = 0;
diff --git a/libc/test/src/__support/printf_core/converter_test.cpp b/libc/test/src/__support/printf_core/converter_test.cpp
index 36037ab2ed4b0..a09873c128eab 100644
--- a/libc/test/src/__support/printf_core/converter_test.cpp
+++ b/libc/test/src/__support/printf_core/converter_test.cpp
@@ -14,14 +14,16 @@
 
 class LlvmLibcPrintfConverterTest : public LIBC_NAMESPACE::testing::Test {
 protected:
-  LlvmLibcPrintfConverterTest() : wb(str, sizeof(str) - 1), writer(wb) {}
+  LlvmLibcPrintfConverterTest()
+      : writer(LIBC_NAMESPACE::printf_core::make_drop_overflow_writer(
+            str, sizeof(str) - 1)),
+        wb(writer.get_write_buffer()) {}
 
   char str[60];
-  LIBC_NAMESPACE::printf_core::DropOverflowBuffer wb;
   LIBC_NAMESPACE::printf_core::Writer<LIBC_NAMESPACE::printf_core::Mode<
-      LIBC_NAMESPACE::printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::
-                                          value>
+      LIBC_NAMESPACE::printf_core::OverflowMode::DROP_OVERFLOW>::value>
       writer;
+  LIBC_NAMESPACE::printf_core::WriteBuffer<char> &wb;
 };
 
 TEST_F(LlvmLibcPrintfConverterTest, SimpleRawConversion) {
diff --git a/libc/test/src/__support/printf_core/writer_test.cpp b/libc/test/src/__support/printf_core/writer_test.cpp
index b1873d729bb3f..5d34cdae1a18d 100644
--- a/libc/test/src/__support/printf_core/writer_test.cpp
+++ b/libc/test/src/__support/printf_core/writer_test.cpp
@@ -15,22 +15,23 @@
 namespace {
 
 using LIBC_NAMESPACE::cpp::string_view;
-using LIBC_NAMESPACE::printf_core::DropOverflowBuffer;
-using LIBC_NAMESPACE::printf_core::FlushingBuffer;
-using LIBC_NAMESPACE::printf_core::WriteMode;
+using LIBC_NAMESPACE::printf_core::make_drop_overflow_writer;
+using LIBC_NAMESPACE::printf_core::OverflowMode;
+using LIBC_NAMESPACE::printf_core::OverflowWriter;
+using LIBC_NAMESPACE::printf_core::WRITE_OK;
+using LIBC_NAMESPACE::printf_core::WriteBuffer;
 using LIBC_NAMESPACE::printf_core::Writer;
 
 TEST(LlvmLibcPrintfWriterTest, Constructor) {
   char str[10];
-  DropOverflowBuffer wb(str, sizeof(str) - 1);
-  Writer writer(wb);
-  (void)writer;
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  EXPECT_EQ(writer.get_write_buffer().buff, str);
 }
 
 TEST(LlvmLibcPrintfWriterTest, Write) {
   char str[4] = {'D', 'E', 'F', 'G'};
-  DropOverflowBuffer wb(str, sizeof(str) - 1);
-  Writer writer(wb);
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<char> &wb = writer.get_write_buffer();
   writer.write({"abc", 3});
 
   EXPECT_EQ(str[3], 'G');
@@ -45,8 +46,8 @@ TEST(LlvmLibcPrintfWriterTest, Write) {
 
 TEST(LlvmLibcPrintfWriterTest, WriteMultipleTimes) {
   char str[10];
-  DropOverflowBuffer wb(str, sizeof(str) - 1);
-  Writer writer(wb);
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<char> &wb = writer.get_write_buffer();
   writer.write({"abc", 3});
   writer.write({"DEF", 3});
   writer.write({"1234", 3});
@@ -59,8 +60,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteMultipleTimes) {
 
 TEST(LlvmLibcPrintfWriterTest, WriteChars) {
   char str[4] = {'D', 'E', 'F', 'G'};
-  DropOverflowBuffer wb(str, sizeof(str) - 1);
-  Writer writer(wb);
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<char> &wb = writer.get_write_buffer();
   writer.write('a', 3);
 
   EXPECT_EQ(str[3], 'G');
@@ -72,8 +73,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteChars) {
 
 TEST(LlvmLibcPrintfWriterTest, WriteCharsMultipleTimes) {
   char str[10];
-  DropOverflowBuffer wb(str, sizeof(str) - 1);
-  Writer writer(wb);
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<char> &wb = writer.get_write_buffer();
   writer.write('a', 3);
   writer.write('D', 3);
   writer.write('1', 3);
@@ -86,8 +87,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsMultipleTimes) {
 
 TEST(LlvmLibcPrintfWriterTest, WriteManyChars) {
   char str[100];
-  DropOverflowBuffer wb(str, sizeof(str) - 1);
-  Writer writer(wb);
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<char> &wb = writer.get_write_buffer();
   writer.write('Z', 99);
 
   wb.buff[wb.buff_cur] = '\0';
@@ -108,8 +109,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteManyChars) {
 
 TEST(LlvmLibcPrintfWriterTest, MixedWrites) {
   char str[13];
-  DropOverflowBuffer wb(str, sizeof(str) - 1);
-  Writer writer(wb);
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<char> &wb = writer.get_write_buffer();
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
@@ -123,8 +124,8 @@ TEST(LlvmLibcPrintfWriterTest, MixedWrites) {
 
 TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLength) {
   char str[11];
-  DropOverflowBuffer wb(str, sizeof(str) - 1);
-  Writer writer(wb);
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<char> &wb = writer.get_write_buffer();
   writer.write({"abcDEF123456", 12});
 
   wb.buff[wb.buff_cur] = '\0';
@@ -135,8 +136,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLength) {
 
 TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLength) {
   char str[11];
-  DropOverflowBuffer wb(str, sizeof(str) - 1);
-  Writer writer(wb);
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<char> &wb = writer.get_write_buffer();
   writer.write('1', 15);
 
   wb.buff[wb.buff_cur] = '\0';
@@ -147,9 +148,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLength) {
 
 TEST(LlvmLibcPrintfWriterTest, MixedWriteWithMaxLength) {
   char str[11];
-  DropOverflowBuffer wb(str, sizeof(str) - 1);
-
-  Writer writer(wb);
+  Writer writer = make_drop_overflow_writer(str, sizeof(str) - 1);
+  WriteBuffer<char> &wb = writer.get_write_buffer();
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
@@ -165,9 +165,8 @@ TEST(LlvmLibcPrintfWriterTest, StringWithMaxLengthOne) {
   char str[1];
   // This is because the max length should be at most 1 less than the size of
   // the buffer it's writing to.
-  DropOverflowBuffer wb(str, 0);
-
-  Writer writer(wb);
+  Writer writer = make_drop_overflow_writer(str, 0);
+  WriteBuffer<char> &wb = writer.get_write_buffer();
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
@@ -180,9 +179,7 @@ TEST(LlvmLibcPrintfWriterTest, StringWithMaxLengthOne) {
 }
 
 TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLength) {
-  DropOverflowBuffer wb(nullptr, 0);
-
-  Writer writer(wb);
+  Writer writer = make_drop_overflow_writer<char>(nullptr, 0);
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
@@ -196,10 +193,9 @@ struct OutBuff {
   size_t cur_pos = 0;
 };
 
-int copy_to_out(string_view new_str, void *raw_out_buff) {
-  if (new_str.size() == 0) {
-    return 0;
-  }
+void copy_to_out(string_view new_str, void *raw_out_buff) {
+  if (new_str.size() == 0)
+    return;
 
   OutBuff *out_buff = reinterpret_cast<OutBuff *>(raw_out_buff);
 
@@ -207,7 +203,21 @@ int copy_to_out(string_view new_str, void *raw_out_buff) {
                                 new_str.data(), new_str.size());
 
   out_buff->cur_pos += new_str.size();
-  return 0;
+}
+
+int overflow_write_copy_to_out(WriteBuffer<char> &wb, string_view new_str,
+                               void *raw_out_buff) {
+  copy_to_out({wb.buff, wb.buff_cur}, raw_out_buff);
+  wb.buff_cur = 0;
+  copy_to_out(new_str, raw_out_buff);
+  return WRITE_OK;
+}
+
+Writer<OverflowMode::RUNTIME_DISPATCH>
+make_test_writer(char *buffer, size_t buffer_length, OutBuff *out_buff) {
+  return Writer(buffer, buffer_length,
+                OverflowWriter<OverflowMode::RUNTIME_DISPATCH, char>(
+                    overflow_write_copy_to_out, out_buff));
 }
 
 TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLengthWithCallback) {
@@ -216,12 +226,11 @@ TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLengthWithCallback) {
   OutBuff out_buff = {str, 0};
 
   char wb_buff[8];
-  FlushingBuffer wb(wb_buff, sizeof(wb_buff), &copy_to_out,
-                    reinterpret_cast<void *>(&out_buff));
-  Writer writer(wb);
+  Writer writer = make_test_writer(wb_buff, sizeof(wb_buff) - 1, &out_buff);
   writer.write({"abcDEF123456", 12});
 
-  wb.flush_to_stream();
+  WriteBuffer<char> &wb = writer.get_write_buffer();
+  copy_to_out({wb.buff, wb.buff_cur}, &out_buff);
   str[out_buff.cur_pos] = '\0';
 
   ASSERT_STREQ("abcDEF123456", str);
@@ -234,12 +243,11 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLengthWithCallback) {
   OutBuff out_buff = {str, 0};
 
   char wb_buff[8];
-  FlushingBuffer wb(wb_buff, sizeof(wb_buff), &copy_to_out,
-                    reinterpret_cast<void *>(&out_buff));
-  Writer writer(wb);
+  Writer writer = make_test_writer(wb_buff, sizeof(wb_buff) - 1, &out_buff);
   writer.write('1', 15);
 
-  wb.flush_to_stream();
+  WriteBuffer<char> &wb = writer.get_write_buffer();
+  copy_to_out({wb.buff, wb.buff_cur}, &out_buff);
   str[out_buff.cur_pos] = '\0';
 
   ASSERT_STREQ("111111111111111", str);
@@ -252,15 +260,14 @@ TEST(LlvmLibcPrintfWriterTest, MixedWriteWithMaxLengthWithCallback) {
   OutBuff out_buff = {str, 0};
 
   char wb_buff[8];
-  FlushingBuffer wb(wb_buff, sizeof(wb_buff), &copy_to_out,
-                    reinterpret_cast<void *>(&out_buff));
-  Writer writer(wb);
+  Writer writer = make_test_writer(wb_buff, sizeof(wb_buff) - 1, &out_buff);
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
   writer.write({"456", 3});
 
-  wb.flush_to_stream();
+  WriteBuffer<char> &wb = writer.get_write_buffer();
+  copy_to_out({wb.buff, wb.buff_cur}, &out_buff);
   str[out_buff.cur_pos] = '\0';
 
   ASSERT_STREQ("aaaDEF111456", str);
@@ -273,16 +280,14 @@ TEST(LlvmLibcPrintfWriterTest, ZeroLengthBufferWithCallback) {
   OutBuff out_buff = {str, 0};
 
   char wb_buff[1];
-  FlushingBuffer wb(wb_buff, 0, &copy_to_out,
-                    reinterpret_cast<void *>(&out_buff));
-
-  Writer writer(wb);
+  Writer writer = make_test_writer(wb_buff, 0, &out_buff);
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
   writer.write({"456", 3});
 
-  wb.flush_to_stream();
+  WriteBuffer<char> &wb = writer.get_write_buffer();
+  copy_to_out({wb.buff, wb.buff_cur}, &out_buff);
   str[out_buff.cur_pos] = '\0';
 
   ASSERT_STREQ("aaaDEF111456", str);
@@ -294,16 +299,12 @@ TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLengthWithCallback) {
 
   OutBuff out_buff = {str, 0};
 
-  FlushingBuffer wb(nullptr, 0, &copy_to_out,
-                    reinterpret_cast<void *>(&out_buff));
-
-  Writer writer(wb);
+  Writer writer = make_test_writer(nullptr, 0, &out_buff);
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
   writer.write({"456", 3});
 
-  wb.flush_to_stream();
   str[out_buff.cur_pos] = '\0';
 
   ASSERT_EQ(writer.get_chars_written(), size_t{12});

>From 447413c8acc46e5553797b7acd704f7ab0db0dcb Mon Sep 17 00:00:00 2001
From: Alex Strelnikov <strel at google.com>
Date: Thu, 3 Sep 2026 14:48:33 +0000
Subject: [PATCH 2/3] Rework down to two overflow modes, drop and callback only

---
 libc/src/__support/printf_core/CMakeLists.txt |  45 +----
 .../__support/printf_core/make_file_writer.h  | 120 -------------
 .../printf_core/make_resizing_writer.h        |  63 -------
 .../printf_core/make_stderr_writer.h          |  56 ------
 .../__support/printf_core/overflow_modes.def  |   5 +-
 .../printf_core/vasprintf_internal.h          |  31 +++-
 .../__support/printf_core/vfprintf_internal.h |  68 +++++++-
 libc/src/__support/printf_core/writer.h       | 160 +++++++-----------
 libc/src/err/report.cpp                       |  17 +-
 libc/src/stdio/baremetal/vfprintf_internal.h  |  11 +-
 libc/src/stdio/snprintf_modular.cpp           |   5 +-
 libc/src/stdio/sprintf_modular.cpp            |   6 +-
 libc/src/stdio/vsnprintf_modular.cpp          |   5 +-
 libc/src/stdio/vsprintf_modular.cpp           |   6 +-
 libc/src/time/strftime_l.cpp                  |   5 +-
 .../src/__support/printf_core/writer_test.cpp |  55 +++---
 16 files changed, 215 insertions(+), 443 deletions(-)
 delete mode 100644 libc/src/__support/printf_core/make_file_writer.h
 delete mode 100644 libc/src/__support/printf_core/make_resizing_writer.h
 delete mode 100644 libc/src/__support/printf_core/make_stderr_writer.h

diff --git a/libc/src/__support/printf_core/CMakeLists.txt b/libc/src/__support/printf_core/CMakeLists.txt
index 12826b55d98c9..3617bb7ac1ea2 100644
--- a/libc/src/__support/printf_core/CMakeLists.txt
+++ b/libc/src/__support/printf_core/CMakeLists.txt
@@ -118,30 +118,6 @@ add_header_library(
     libc.src.string.memory_utils.inline_memset
 )
 
-add_header_library(
-  make_resizing_writer
-  HDRS
-    make_resizing_writer.h
-  DEPENDS
-    .writer
-    libc.hdr.func.malloc
-    libc.hdr.func.free
-    libc.hdr.func.realloc
-    libc.src.__support.CPP.string_view
-    libc.src.__support.printf_core.writer
-)
-
-add_header_library(
-  make_stderr_writer
-  HDRS
-    make_stderr_writer.h
-  DEPENDS
-    .writer
-    libc.src.__support.CPP.string_view
-    libc.src.__support.OSUtil.io
-    libc.src.__support.macros.config
-)
-
 add_header_library(
   converter
   HDRS
@@ -203,10 +179,12 @@ add_header_library(
     vasprintf_internal.h
   DEPENDS
     libc.hdr.func.malloc
+    libc.hdr.func.free
+    libc.hdr.func.realloc
     libc.src.__support.arg_list
     libc.src.__support.error_or
-    libc.src.__support.printf_core.make_resizing_writer
     libc.src.__support.printf_core.printf_main
+    libc.src.__support.printf_core.writer
 )
 
 add_header_library(
@@ -224,28 +202,15 @@ if(NOT (TARGET libc.src.__support.File.file) AND LLVM_LIBC_FULL_BUILD)
   return()
 endif()
 
-add_header_library(
-  make_file_writer
-  HDRS
-    make_file_writer.h
-  DEPENDS
-    .writer
-    libc.src.__support.CPP.string_view
-    libc.src.__support.File.file
-    libc.src.__support.macros.attributes
-    libc.src.__support.macros.config
-    ${use_system_file}
-)
-
 add_header_library(
   vfprintf_internal
   HDRS
     vfprintf_internal.h
   DEPENDS
-    .make_file_writer
-    .printf_main
     libc.src.__support.File.file
     libc.src.__support.error_or
     libc.src.__support.arg_list
+    libc.src.__support.printf_core.printf_main
+    libc.src.__support.printf_core.writer
     ${use_system_file}
 )
diff --git a/libc/src/__support/printf_core/make_file_writer.h b/libc/src/__support/printf_core/make_file_writer.h
deleted file mode 100644
index e0639581ee5e0..0000000000000
--- a/libc/src/__support/printf_core/make_file_writer.h
+++ /dev/null
@@ -1,120 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Implementation helpers for Writer<OverflowMode::FLUSH_TO_FILE, CharT>.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_FILE_WRITER_H
-#define LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_FILE_WRITER_H
-
-#include "hdr/types/FILE.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/File/file.h"
-#include "src/__support/macros/attributes.h"
-#include "src/__support/macros/config.h"
-#include "src/__support/printf_core/writer.h"
-
-namespace LIBC_NAMESPACE_DECL {
-namespace printf_core {
-
-template <typename CharT>
-LIBC_INLINE Writer<Mode<OverflowMode::FLUSH_TO_FILE>::value, CharT>
-make_file_writer(CharT *buffer, size_t buffer_len, ::FILE *fp) {
-  return Writer(buffer, buffer_len,
-                make_overflow_writer<OverflowMode::FLUSH_TO_FILE, CharT>(fp));
-}
-
-} // namespace printf_core
-
-namespace internal {
-#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
-LIBC_INLINE int ferror_unlocked(FILE *f) {
-  return reinterpret_cast<LIBC_NAMESPACE::File *>(f)->error_unlocked();
-}
-
-LIBC_INLINE void flockfile(FILE *f) {
-  reinterpret_cast<LIBC_NAMESPACE::File *>(f)->lock();
-}
-
-LIBC_INLINE void funlockfile(FILE *f) {
-  reinterpret_cast<LIBC_NAMESPACE::File *>(f)->unlock();
-}
-
-LIBC_INLINE FileIOResult fwrite_unlocked(const void *ptr, size_t size,
-                                         size_t nmemb, FILE *f) {
-  return reinterpret_cast<LIBC_NAMESPACE::File *>(f)->write_unlocked(
-      ptr, size * nmemb);
-}
-#else  // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
-LIBC_INLINE int ferror_unlocked(::FILE *f) { return ::ferror_unlocked(f); }
-
-LIBC_INLINE void flockfile(::FILE *f) { ::flockfile(f); }
-
-LIBC_INLINE void funlockfile(::FILE *f) { ::funlockfile(f); }
-
-LIBC_INLINE FileIOResult fwrite_unlocked(const void *ptr, size_t size,
-                                         size_t nmemb, ::FILE *f) {
-  // Need to use system errno in this case, as system write will set this errno
-  // which we need to propagate back into our code. fwrite only modifies errno
-  // if there was an error, and errno may have previously been nonzero. Only
-  // return errno if there was an error.
-  size_t members_written = ::fwrite_unlocked(ptr, size, nmemb, f);
-  return {members_written, members_written == nmemb ? 0 : errno};
-}
-#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
-} // namespace internal
-
-namespace printf_core {
-
-LIBC_INLINE int write_to_file_unlocked(cpp::string_view str,
-                                       ::FILE *target_file) {
-  if (str.size() == 0)
-    return WRITE_OK;
-
-  auto write_result = internal::fwrite_unlocked(str.data(), sizeof(char),
-                                                str.size(), target_file);
-  // Propagate actual system error in FileIOResult.
-  if (write_result.has_error())
-    return -write_result.error;
-
-  // In case short write occured or error was not set on FileIOResult for some
-  // reason.
-  if (write_result.value != str.size() ||
-      internal::ferror_unlocked(target_file))
-    return FILE_WRITE_ERROR;
-
-  return WRITE_OK;
-}
-
-// Handles overflow by flushing the current contents of `wb` and `new_str` to
-// the `FILE` pointed to by `fp`.
-template <typename CharT>
-LIBC_INLINE int
-overflow_write_flush_to_file(WriteBuffer<CharT> &wb,
-                             cpp::basic_string_view<CharT> new_str, void *fp) {
-  ::FILE *target_file = reinterpret_cast<::FILE *>(fp);
-  int retval;
-
-  retval = write_to_file_unlocked({wb.buff, wb.buff_cur}, target_file);
-  if (retval < 0)
-    return retval;
-  wb.buff_cur = 0;
-
-  retval = write_to_file_unlocked(new_str, target_file);
-  if (retval < 0)
-    return retval;
-
-  return WRITE_OK;
-}
-
-} // namespace printf_core
-} // namespace LIBC_NAMESPACE_DECL
-
-#endif // LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_FILE_WRITER_H
diff --git a/libc/src/__support/printf_core/make_resizing_writer.h b/libc/src/__support/printf_core/make_resizing_writer.h
deleted file mode 100644
index 31dcd0d510d81..0000000000000
--- a/libc/src/__support/printf_core/make_resizing_writer.h
+++ /dev/null
@@ -1,63 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Implementation helpers for Writer<OverflowMode::RESIZE_BUFFER, CharT>.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_RESIZING_WRITER_H
-#define LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_RESIZING_WRITER_H
-
-#include "hdr/func/free.h"
-#include "hdr/func/malloc.h"
-#include "hdr/func/realloc.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/printf_core/writer.h"
-
-namespace LIBC_NAMESPACE_DECL {
-namespace printf_core {
-
-template <typename CharT>
-LIBC_INLINE Writer<Mode<OverflowMode::RESIZE_BUFFER>::value, CharT>
-make_resizing_writer(CharT *buffer, size_t buffer_len, bool buffer_on_stack) {
-  return Writer(buffer, buffer_len,
-                make_overflow_writer<OverflowMode::RESIZE_BUFFER, CharT>(
-                    buffer_on_stack ? buffer : nullptr));
-}
-
-// Handles overflow by resizing `wb` to have enough capacity for `new_str`, and
-// then copying the `new_str` contents into the buffer.
-template <typename CharT>
-LIBC_INLINE int
-overflow_write_resize_buffer(WriteBuffer<CharT> &wb,
-                             cpp::basic_string_view<CharT> new_str,
-                             void *initial_stack_buffer) {
-  size_t new_size = new_str.size() + wb.buff_cur;
-  const bool is_on_stack = (wb.buff == initial_stack_buffer);
-  char *new_buff = static_cast<char *>(
-      is_on_stack ? malloc(new_size + 1)
-                  : realloc(wb.buff, new_size + 1)); // +1 for null
-  if (new_buff == nullptr) {
-    if (!is_on_stack)
-      free(wb.buff);
-    return ALLOCATION_ERROR;
-  }
-  if (is_on_stack)
-    inline_memcpy(new_buff, wb.buff, wb.buff_cur);
-  wb.buff = new_buff;
-  inline_memcpy(wb.buff + wb.buff_cur, new_str.data(), new_str.size());
-  wb.buff_cur = new_size;
-  wb.buff_len = new_size;
-  return WRITE_OK;
-}
-
-} // namespace printf_core
-} // namespace LIBC_NAMESPACE_DECL
-
-#endif // LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_RESIZING_WRITER_H
diff --git a/libc/src/__support/printf_core/make_stderr_writer.h b/libc/src/__support/printf_core/make_stderr_writer.h
deleted file mode 100644
index 0d2322ed0c74d..0000000000000
--- a/libc/src/__support/printf_core/make_stderr_writer.h
+++ /dev/null
@@ -1,56 +0,0 @@
-//===----------------------------------------------------------------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See https://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-///
-/// \file
-/// Implementation helpers for Writer<OverflowMode::FLUSH_TO_STDERR, CharT>.
-///
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_STDERR_WRITER_H
-#define LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_STDERR_WRITER_H
-
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/OSUtil/io.h"
-#include "src/__support/macros/config.h"
-#include "src/__support/printf_core/writer.h"
-
-namespace LIBC_NAMESPACE_DECL {
-namespace printf_core {
-
-template <typename CharT>
-LIBC_INLINE Writer<Mode<OverflowMode::FLUSH_TO_STDERR>::value, CharT>
-make_stderr_writer(CharT *buffer, size_t buffer_len) {
-  return Writer(
-      buffer, buffer_len,
-      make_overflow_writer<OverflowMode::FLUSH_TO_STDERR, CharT>(nullptr));
-}
-
-LIBC_INLINE void flush_to_stderr(WriteBuffer<char> &wb) {
-  if (wb.buff_cur == 0)
-    return;
-
-  write_to_stderr({wb.buff, wb.buff_cur});
-  wb.buff_cur = 0;
-}
-
-// Handles overflow by flushing the current contents of `wb` and `new_str` to
-// stderr.
-template <typename CharT>
-LIBC_INLINE int
-overflow_write_flush_to_stderr(WriteBuffer<CharT> &wb,
-                               cpp::basic_string_view<CharT> new_str, void *) {
-  flush_to_stderr(wb);
-  if (new_str.size() > 0)
-    write_to_stderr(new_str);
-  return WRITE_OK;
-}
-
-} // namespace printf_core
-} // namespace LIBC_NAMESPACE_DECL
-
-#endif // LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_MAKE_STDERR_WRITER_H
diff --git a/libc/src/__support/printf_core/overflow_modes.def b/libc/src/__support/printf_core/overflow_modes.def
index a18301e4ebf66..8fc1e620ed75d 100644
--- a/libc/src/__support/printf_core/overflow_modes.def
+++ b/libc/src/__support/printf_core/overflow_modes.def
@@ -7,7 +7,4 @@
 //===----------------------------------------------------------------------===//
 
 HANDLE_OVERFLOW_MODE(DROP_OVERFLOW)
-HANDLE_OVERFLOW_MODE(FLUSH_TO_FILE)
-HANDLE_OVERFLOW_MODE(FLUSH_TO_STDERR)
-HANDLE_OVERFLOW_MODE(RESIZE_BUFFER)
-HANDLE_OVERFLOW_MODE(RUNTIME_DISPATCH)
+HANDLE_OVERFLOW_MODE(CALLBACK)
diff --git a/libc/src/__support/printf_core/vasprintf_internal.h b/libc/src/__support/printf_core/vasprintf_internal.h
index 5396161c74115..5421336bc339d 100644
--- a/libc/src/__support/printf_core/vasprintf_internal.h
+++ b/libc/src/__support/printf_core/vasprintf_internal.h
@@ -9,16 +9,40 @@
 #ifndef LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_VASPRINTF_INTERNAL_H
 #define LLVM_LIBC_SRC___SUPPORT_PRINTF_CORE_VASPRINTF_INTERNAL_H
 
+#include "hdr/func/free.h"
 #include "hdr/func/malloc.h"
+#include "hdr/func/realloc.h"
 #include "src/__support/arg_list.h"
 #include "src/__support/error_or.h"
 #include "src/__support/printf_core/core_structs.h"
-#include "src/__support/printf_core/make_resizing_writer.h"
 #include "src/__support/printf_core/printf_main.h"
+#include "src/__support/printf_core/writer.h"
 
 namespace LIBC_NAMESPACE_DECL {
 namespace printf_core {
 
+LIBC_INLINE int overflow_write_resize_buffer(WriteBuffer<char> &wb,
+                                             cpp::string_view new_str,
+                                             void *initial_stack_buffer) {
+  size_t new_size = new_str.size() + wb.buff_cur;
+  const bool is_buff_on_stack = (wb.buff == initial_stack_buffer);
+  char *new_buff = static_cast<char *>(
+      is_buff_on_stack ? malloc(new_size + 1)
+                       : realloc(wb.buff, new_size + 1)); // +1 for null
+  if (new_buff == nullptr) {
+    if (!is_buff_on_stack)
+      free(wb.buff);
+    return ALLOCATION_ERROR;
+  }
+  if (is_buff_on_stack)
+    inline_memcpy(new_buff, wb.buff, wb.buff_cur);
+  wb.buff = new_buff;
+  inline_memcpy(wb.buff + wb.buff_cur, new_str.data(), new_str.size());
+  wb.buff_cur = new_size;
+  wb.buff_len = new_size;
+  return printf_core::WRITE_OK;
+}
+
 constexpr size_t DEFAULT_BUFFER_SIZE = 200;
 
 template <bool use_modular = false>
@@ -26,8 +50,9 @@ LIBC_INLINE ErrorOr<size_t> vasprintf_internal(char **ret,
                                                const char *__restrict format,
                                                internal::ArgList args) {
   char init_buff_on_stack[DEFAULT_BUFFER_SIZE];
-  Writer writer = make_resizing_writer(init_buff_on_stack, DEFAULT_BUFFER_SIZE,
-                                       /* buffer_on_stack = */ true);
+  printf_core::Writer writer = printf_core::make_writer(
+      init_buff_on_stack, DEFAULT_BUFFER_SIZE, &overflow_write_resize_buffer,
+      init_buff_on_stack);
 
   auto ret_val = [&] {
     if constexpr (use_modular)
diff --git a/libc/src/__support/printf_core/vfprintf_internal.h b/libc/src/__support/printf_core/vfprintf_internal.h
index 1f2ea5960f4c4..523eea30787cc 100644
--- a/libc/src/__support/printf_core/vfprintf_internal.h
+++ b/libc/src/__support/printf_core/vfprintf_internal.h
@@ -15,28 +15,88 @@
 #include "src/__support/macros/attributes.h" // For LIBC_INLINE
 #include "src/__support/macros/config.h"
 #include "src/__support/printf_core/core_structs.h"
-#include "src/__support/printf_core/make_file_writer.h"
 #include "src/__support/printf_core/printf_main.h"
+#include "src/__support/printf_core/writer.h"
 
 #include "hdr/types/FILE.h"
 
 namespace LIBC_NAMESPACE_DECL {
+
+namespace internal {
+#ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE
+LIBC_INLINE int ferror_unlocked(FILE *f) {
+  return reinterpret_cast<LIBC_NAMESPACE::File *>(f)->error_unlocked();
+}
+
+LIBC_INLINE void flockfile(FILE *f) {
+  reinterpret_cast<LIBC_NAMESPACE::File *>(f)->lock();
+}
+
+LIBC_INLINE void funlockfile(FILE *f) {
+  reinterpret_cast<LIBC_NAMESPACE::File *>(f)->unlock();
+}
+
+LIBC_INLINE FileIOResult fwrite_unlocked(const void *ptr, size_t size,
+                                         size_t nmemb, FILE *f) {
+  return reinterpret_cast<LIBC_NAMESPACE::File *>(f)->write_unlocked(
+      ptr, size * nmemb);
+}
+#else  // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE)
+LIBC_INLINE int ferror_unlocked(::FILE *f) { return ::ferror_unlocked(f); }
+
+LIBC_INLINE void flockfile(::FILE *f) { ::flockfile(f); }
+
+LIBC_INLINE void funlockfile(::FILE *f) { ::funlockfile(f); }
+
+LIBC_INLINE FileIOResult fwrite_unlocked(const void *ptr, size_t size,
+                                         size_t nmemb, ::FILE *f) {
+  // Need to use system errno in this case, as system write will set this errno
+  // which we need to propagate back into our code. fwrite only modifies errno
+  // if there was an error, and errno may have previously been nonzero. Only
+  // return errno if there was an error.
+  size_t members_written = ::fwrite_unlocked(ptr, size, nmemb, f);
+  return {members_written, members_written == nmemb ? 0 : errno};
+}
+#endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE
+} // namespace internal
+
 namespace printf_core {
 
+LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) {
+  ::FILE *target_file = reinterpret_cast<::FILE *>(fp);
+  // Write new_str to the target file. The logic preventing a zero-length write
+  // is in the writer, so we don't check here.
+  auto write_result = internal::fwrite_unlocked(new_str.data(), sizeof(char),
+                                                new_str.size(), target_file);
+  // Propagate actual system error in FileIOResult.
+  if (write_result.has_error())
+    return -write_result.error;
+
+  // In case short write occured or error was not set on FileIOResult for some
+  // reason.
+  if (write_result.value != new_str.size() ||
+      internal::ferror_unlocked(target_file))
+    return FILE_WRITE_ERROR;
+
+  return WRITE_OK;
+}
+
 LIBC_INLINE ErrorOr<size_t> vfprintf_internal(::FILE *__restrict stream,
                                               const char *__restrict format,
                                               internal::ArgList &args) {
   constexpr size_t BUFF_SIZE = 1024;
   char buffer[BUFF_SIZE];
-  Writer writer = make_file_writer(buffer, BUFF_SIZE, stream);
+  Writer writer =
+      make_writer(buffer, BUFF_SIZE,
+                  &overflow_write_flush_to_sink<char, file_write_hook>, stream);
   internal::flockfile(stream);
   auto retval = printf_main(&writer, format, args);
   if (!retval.has_value()) {
     internal::funlockfile(stream);
     return retval;
   }
-  WriteBuffer<char> &wb = writer.get_write_buffer();
-  int flushval = write_to_file_unlocked({wb.buff, wb.buff_cur}, stream);
+  int flushval =
+      writer.get_write_buffer().flush_to_sink<file_write_hook>(stream);
   if (flushval != WRITE_OK)
     retval = Error(-flushval);
   internal::funlockfile(stream);
diff --git a/libc/src/__support/printf_core/writer.h b/libc/src/__support/printf_core/writer.h
index 31f1557ab09e4..031c48b3a353d 100644
--- a/libc/src/__support/printf_core/writer.h
+++ b/libc/src/__support/printf_core/writer.h
@@ -31,27 +31,47 @@ enum class OverflowMode {
 // avoid multiple copies of the converter functions.
 template <OverflowMode mode> struct Mode {
 #ifdef LIBC_COPT_PRINTF_RUNTIME_DISPATCH
-  static constexpr OverflowMode value = OverflowMode::RUNTIME_DISPATCH;
+  static constexpr OverflowMode value = OverflowMode::CALLBACK;
 #else
   static constexpr OverflowMode value = mode;
 #endif
 };
 
+// Function type for an optionally stateful write sink to be used in a
+// `Writer` "overflow write" callback.
+//
+// Should not be expected to handle an empty string input.
+template <typename CharT>
+using WriteSink = int (*)(cpp::basic_string_view<CharT> /* str */,
+                          void * /* state */);
+
 template <typename CharT> struct WriteBuffer {
   CharT *buff;
   size_t buff_len;
   size_t buff_cur = 0;
+
+  // Flushes the current contents of the buffer to `write_sink`, if non-empty.
+  template <WriteSink<CharT> write_sink>
+  LIBC_INLINE int flush_to_sink(void *sink_state = nullptr) {
+    if (buff_cur == 0)
+      return WRITE_OK;
+
+    int retval = write_sink({buff, buff_cur}, sink_state);
+    if (retval >= 0)
+      buff_cur = 0;
+    return retval;
+  }
 };
 
-// Function type for handling the slow path in `Writer` when the `WriteBuffer`
-// may not have enough remaining capacity for the new content.
+// Function type for handling the "overflow write" slow path in `Writer` when
+// the `WriteBuffer` may not have enough remaining capacity for the new content.
 template <typename CharT>
 using OverflowWriteFn = int (*)(WriteBuffer<CharT> & /* wb */,
                                 cpp::basic_string_view<CharT> /* new_str */,
                                 void * /* state */);
 
 // Handles overflow by filling any remaining space in `wb` with the start of
-// `new_str`, and dropping any excess bytes.
+// `new_str`, and dropping any excess characters.
 template <typename CharT>
 LIBC_INLINE int
 overflow_write_drop_overflow(WriteBuffer<CharT> &wb,
@@ -67,50 +87,22 @@ overflow_write_drop_overflow(WriteBuffer<CharT> &wb,
   return WRITE_OK;
 }
 
-// Defined in make_file_writer.h
-template <typename CharT>
-int overflow_write_flush_to_file(WriteBuffer<CharT> &wb,
-                                 cpp::basic_string_view<CharT> new_str,
-                                 void *fp);
-
-// Defined in make_stderr_writer.h
-template <typename CharT>
-int overflow_write_flush_to_stderr(WriteBuffer<CharT> &wb,
-                                   cpp::basic_string_view<CharT> new_str,
-                                   void *);
-
-// Defined in make_resizing_writer.h
-template <typename CharT>
-int overflow_write_resize_buffer(WriteBuffer<CharT> &wb,
-                                 cpp::basic_string_view<CharT> new_str,
-                                 void *initial_stack_buffer);
-
-// Trait type helper mapping `OverflowMode` to the corresponding function.
-template <OverflowMode mode, typename CharT> struct OverflowModeToWriteFn {};
-
-template <typename CharT>
-struct OverflowModeToWriteFn<OverflowMode::DROP_OVERFLOW, CharT> {
-  static constexpr OverflowWriteFn<CharT> value =
-      overflow_write_drop_overflow<CharT>;
-};
-
-template <typename CharT>
-struct OverflowModeToWriteFn<OverflowMode::FLUSH_TO_FILE, CharT> {
-  static constexpr OverflowWriteFn<CharT> value =
-      overflow_write_flush_to_file<CharT>;
-};
-
-template <typename CharT>
-struct OverflowModeToWriteFn<OverflowMode::FLUSH_TO_STDERR, CharT> {
-  static constexpr OverflowWriteFn<CharT> value =
-      overflow_write_flush_to_stderr<CharT>;
-};
-
-template <typename CharT>
-struct OverflowModeToWriteFn<OverflowMode::RESIZE_BUFFER, CharT> {
-  static constexpr OverflowWriteFn<CharT> value =
-      overflow_write_resize_buffer<CharT>;
-};
+// Flushes the current contents of `wb` to `write_sink`, followed by `new_str`.
+template <typename CharT, WriteSink<CharT> write_sink>
+LIBC_INLINE int
+overflow_write_flush_to_sink(WriteBuffer<CharT> &wb,
+                             cpp::basic_string_view<CharT> new_str,
+                             void *sink_state) {
+  int retval = wb.template flush_to_sink<write_sink>(sink_state);
+  if (retval < 0)
+    return retval;
+  if (new_str.size() > 0) {
+    retval = write_sink(new_str, sink_state);
+    if (retval < 0)
+      return retval;
+  }
+  return WRITE_OK;
+}
 
 // Helper template used by `Writer` to dispatch to the appropriate
 // `OverflowWriteFn`.
@@ -126,43 +118,7 @@ struct OverflowWriter<OverflowMode::DROP_OVERFLOW, CharT> {
   }
 };
 
-template <typename CharT>
-struct OverflowWriter<OverflowMode::FLUSH_TO_FILE, CharT> {
-  void *file;
-
-  LIBC_INLINE OverflowWriter(OverflowWriteFn<CharT>, void *file) : file(file) {}
-
-  LIBC_INLINE int write(WriteBuffer<CharT> &wb,
-                        cpp::basic_string_view<CharT> new_str) {
-    return overflow_write_flush_to_file(wb, new_str, file);
-  }
-};
-
-template <typename CharT>
-struct OverflowWriter<OverflowMode::FLUSH_TO_STDERR, CharT> {
-  LIBC_INLINE OverflowWriter(OverflowWriteFn<CharT>, void *) {}
-
-  LIBC_INLINE int write(WriteBuffer<CharT> &wb,
-                        cpp::basic_string_view<CharT> new_str) {
-    return overflow_write_flush_to_stderr(wb, new_str, nullptr);
-  }
-};
-
-template <typename CharT>
-struct OverflowWriter<OverflowMode::RESIZE_BUFFER, CharT> {
-  void *initial_stack_buffer;
-
-  LIBC_INLINE OverflowWriter(OverflowWriteFn<CharT>, void *initial_stack_buffer)
-      : initial_stack_buffer(initial_stack_buffer) {}
-
-  LIBC_INLINE int write(WriteBuffer<CharT> &wb,
-                        cpp::basic_string_view<CharT> new_str) {
-    return overflow_write_resize_buffer(wb, new_str, initial_stack_buffer);
-  }
-};
-
-template <typename CharT>
-struct OverflowWriter<OverflowMode::RUNTIME_DISPATCH, CharT> {
+template <typename CharT> struct OverflowWriter<OverflowMode::CALLBACK, CharT> {
   OverflowWriteFn<CharT> runtime_fn;
   void *state;
 
@@ -175,15 +131,6 @@ struct OverflowWriter<OverflowMode::RUNTIME_DISPATCH, CharT> {
   }
 };
 
-// Constructs an `OverflowWriter` that dispatches according to
-// `LIBC_COPT_PRINTF_RUNTIME_DISPATCH`.
-template <OverflowMode mode, typename CharT>
-LIBC_INLINE OverflowWriter<Mode<mode>::value, CharT>
-make_overflow_writer(void *state) {
-  return OverflowWriter<Mode<mode>::value, CharT>(
-      OverflowModeToWriteFn<mode, CharT>::value, state);
-}
-
 // Fills the `dest` buffer with `count` copies of `value`.
 template <typename CharT>
 LIBC_INLINE void fill_buffer(CharT *dest, CharT value, size_t count) {
@@ -227,15 +174,21 @@ template <OverflowMode mode, typename CharT = char> class Writer final {
     return overflow_writer.write(wb, mb_substr);
   }
 
-public:
-  // Prefer using the factory functions in make_*_writer.h or
-  // `make_drop_overflow_writer` below, instead of directly calling this
-  // constructor.
   LIBC_INLINE Writer(CharT *buffer, size_t buffer_len,
                      OverflowWriter<mode, CharT> overflow_writer)
       : wb{.buff = buffer, .buff_len = buffer_len},
         overflow_writer(overflow_writer) {}
 
+public:
+  template <typename CharType>
+  friend Writer<Mode<OverflowMode::DROP_OVERFLOW>::value, CharType>
+  make_drop_overflow_writer(CharType *buffer, size_t buffer_len);
+
+  template <typename CharType>
+  friend Writer<Mode<OverflowMode::CALLBACK>::value, CharType>
+  make_writer(CharType *buffer, size_t buffer_len,
+              OverflowWriteFn<CharType> callback, void *callback_state);
+
   // Takes a string, copies it into the buffer if there is space, else passes it
   // to the overflow mechanism to be handled separately.
   LIBC_INLINE int write(cpp::basic_string_view<CharT> new_string) {
@@ -287,9 +240,18 @@ Writer(CharT *, size_t, OverflowWriter<mode, CharT>) -> Writer<mode, CharT>;
 template <typename CharT>
 LIBC_INLINE Writer<Mode<OverflowMode::DROP_OVERFLOW>::value, CharT>
 make_drop_overflow_writer(CharT *buffer, size_t buffer_len) {
+  return Writer(buffer, buffer_len,
+                OverflowWriter<Mode<OverflowMode::DROP_OVERFLOW>::value, CharT>(
+                    overflow_write_drop_overflow<CharT>, nullptr));
+}
+
+template <typename CharT>
+LIBC_INLINE Writer<OverflowMode::CALLBACK, CharT>
+make_writer(CharT *buffer, size_t buffer_len, OverflowWriteFn<CharT> callback,
+            void *callback_state = nullptr) {
   return Writer(
       buffer, buffer_len,
-      make_overflow_writer<OverflowMode::DROP_OVERFLOW, CharT>(nullptr));
+      OverflowWriter<OverflowMode::CALLBACK, CharT>(callback, callback_state));
 }
 
 } // namespace printf_core
diff --git a/libc/src/err/report.cpp b/libc/src/err/report.cpp
index 9c02b0ea8abdf..3b35043201100 100644
--- a/libc/src/err/report.cpp
+++ b/libc/src/err/report.cpp
@@ -20,8 +20,8 @@
 #include "src/__support/arg_list.h"
 #include "src/__support/libc_errno.h"
 #include "src/__support/macros/config.h"
-#include "src/__support/printf_core/make_stderr_writer.h"
 #include "src/__support/printf_core/printf_main.h"
+#include "src/__support/printf_core/writer.h"
 
 #ifdef LIBC_FULL_BUILD
 #include "src/errno/program_invocation_short_name.h"
@@ -35,15 +35,20 @@ extern "C" char *program_invocation_short_name;
 namespace LIBC_NAMESPACE_DECL {
 namespace err_reporting {
 
+LIBC_INLINE int stderr_write_hook(cpp::string_view str, void *) {
+  write_to_stderr(str);
+  return printf_core::WRITE_OK;
+}
+
 void report(bool show_err, int err_num, const char *fmt,
             internal::ArgList &args) {
   const char *progname = PROGRAM_INVOCATION_SHORT_NAME;
   if (!progname)
     progname = "";
   char buffer[1024];
-  printf_core::Writer writer =
-      printf_core::make_stderr_writer(buffer, sizeof(buffer));
-  printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
+  printf_core::Writer writer = printf_core::make_writer(
+      buffer, sizeof(buffer),
+      &printf_core::overflow_write_flush_to_sink<char, stderr_write_hook>);
 
   writer.write(progname);
   if (fmt != nullptr || show_err)
@@ -51,7 +56,7 @@ void report(bool show_err, int err_num, const char *fmt,
 
   if (fmt != nullptr) {
     if (!printf_core::printf_main(&writer, fmt, args)) {
-      printf_core::flush_to_stderr(wb);
+      writer.get_write_buffer().flush_to_sink<stderr_write_hook>();
       return;
     }
     if (show_err)
@@ -62,7 +67,7 @@ void report(bool show_err, int err_num, const char *fmt,
     writer.write(get_error_string(err_num));
 
   writer.write("\n");
-  printf_core::flush_to_stderr(wb);
+  writer.get_write_buffer().flush_to_sink<stderr_write_hook>();
 }
 
 } // namespace err_reporting
diff --git a/libc/src/stdio/baremetal/vfprintf_internal.h b/libc/src/stdio/baremetal/vfprintf_internal.h
index c86c40a7cca0c..715124df7a3b3 100644
--- a/libc/src/stdio/baremetal/vfprintf_internal.h
+++ b/libc/src/stdio/baremetal/vfprintf_internal.h
@@ -45,9 +45,11 @@ LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
   static constexpr size_t BUFF_SIZE = 1024;
   char buffer[BUFF_SIZE];
 
-  printf_core::FlushingBuffer wb(buffer, BUFF_SIZE, &internal::write_hook,
-                                 stream);
-  printf_core::Writer writer(wb);
+  // TODO:
+  printf_core::Writer writer = print_core::make_writer(
+      buffer, BUFF_SIZE,
+      &printf_core::overflow_write_flush_to_sink<char, internal::write_hook>,
+      stream);
 
   auto retval = [&] {
     if constexpr (use_modular)
@@ -60,7 +62,8 @@ LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
     return -1;
   }
 
-  int flushval = wb.flush_to_stream();
+  int flushval =
+      writer.get_write_buffer().flush_to_sink<internal::write_hook>(stream);
   if (flushval != printf_core::WRITE_OK) {
     libc_errno = printf_core::internal_error_to_errno(-flushval);
     return -1;
diff --git a/libc/src/stdio/snprintf_modular.cpp b/libc/src/stdio/snprintf_modular.cpp
index 86afdb9c5828e..485bf6b41693c 100644
--- a/libc/src/stdio/snprintf_modular.cpp
+++ b/libc/src/stdio/snprintf_modular.cpp
@@ -31,8 +31,9 @@ LLVM_LIBC_FUNCTION(int, __snprintf_modular,
                                  // and pointer semantics, as well as handling
                                  // destruction automatically.
   va_end(vlist);
-  printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
-  printf_core::Writer writer(wb);
+  printf_core::Writer writer = printf_core::make_drop_overflow_writer(
+      buffer, (buffsz > 0 ? buffsz - 1 : 0));
+  printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
 
   auto ret_val = printf_core::printf_main_modular(&writer, format, args);
   if (!ret_val.has_value()) {
diff --git a/libc/src/stdio/sprintf_modular.cpp b/libc/src/stdio/sprintf_modular.cpp
index 9003e2440d535..ce2a3b6a257df 100644
--- a/libc/src/stdio/sprintf_modular.cpp
+++ b/libc/src/stdio/sprintf_modular.cpp
@@ -31,9 +31,9 @@ LLVM_LIBC_FUNCTION(int, __sprintf_modular,
                                  // destruction automatically.
   va_end(vlist);
 
-  printf_core::DropOverflowBuffer wb(buffer,
-                                     cpp::numeric_limits<size_t>::max());
-  printf_core::Writer writer(wb);
+  printf_core::Writer writer = printf_core::make_drop_overflow_writer(
+      buffer, cpp::numeric_limits<size_t>::max());
+  printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
 
   auto ret_val = printf_core::printf_main_modular(&writer, format, args);
   if (!ret_val.has_value()) {
diff --git a/libc/src/stdio/vsnprintf_modular.cpp b/libc/src/stdio/vsnprintf_modular.cpp
index 96f020839c3cc..366f5af8c9884 100644
--- a/libc/src/stdio/vsnprintf_modular.cpp
+++ b/libc/src/stdio/vsnprintf_modular.cpp
@@ -28,8 +28,9 @@ LLVM_LIBC_FUNCTION(int, __vsnprintf_modular,
   internal::ArgList args(vlist); // This holder class allows for easier copying
                                  // and pointer semantics, as well as handling
                                  // destruction automatically.
-  printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
-  printf_core::Writer writer(wb);
+  printf_core::Writer writer = printf_core::make_drop_overflow_writer(
+      buffer, (buffsz > 0 ? buffsz - 1 : 0));
+  printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
 
   auto ret_val = printf_core::printf_main_modular(&writer, format, args);
   if (!ret_val.has_value()) {
diff --git a/libc/src/stdio/vsprintf_modular.cpp b/libc/src/stdio/vsprintf_modular.cpp
index 34deab1bfe412..40af67f313c43 100644
--- a/libc/src/stdio/vsprintf_modular.cpp
+++ b/libc/src/stdio/vsprintf_modular.cpp
@@ -28,9 +28,9 @@ LLVM_LIBC_FUNCTION(int, __vsprintf_modular,
                                  // and pointer semantics, as well as handling
                                  // destruction automatically.
 
-  printf_core::DropOverflowBuffer wb(buffer,
-                                     cpp::numeric_limits<size_t>::max());
-  printf_core::Writer writer(wb);
+  printf_core::Writer writer = printf_core::make_drop_overflow_writer(
+      buffer, cpp::numeric_limits<size_t>::max());
+  printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
 
   auto ret_val = printf_core::printf_main_modular(&writer, format, args);
   if (!ret_val.has_value()) {
diff --git a/libc/src/time/strftime_l.cpp b/libc/src/time/strftime_l.cpp
index faf3883c476b2..db72ca747c6fb 100644
--- a/libc/src/time/strftime_l.cpp
+++ b/libc/src/time/strftime_l.cpp
@@ -28,8 +28,9 @@ LLVM_LIBC_FUNCTION(size_t, strftime_l,
   LIBC_CRASH_ON_NULLPTR(format);
   LIBC_CRASH_ON_NULLPTR(timeptr);
 
-  printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
-  printf_core::Writer writer(wb);
+  printf_core::Writer writer = printf_core::make_drop_overflow_writer(
+      buffer, (buffsz > 0 ? buffsz - 1 : 0));
+  printf_core::WriteBuffer<char> &wb = writer.get_write_buffer();
   auto ret = strftime_core::strftime_main(&writer, format, timeptr);
   if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
     wb.buff[wb.buff_cur] = '\0';
diff --git a/libc/test/src/__support/printf_core/writer_test.cpp b/libc/test/src/__support/printf_core/writer_test.cpp
index 5d34cdae1a18d..5fc226bdeff62 100644
--- a/libc/test/src/__support/printf_core/writer_test.cpp
+++ b/libc/test/src/__support/printf_core/writer_test.cpp
@@ -16,8 +16,9 @@ namespace {
 
 using LIBC_NAMESPACE::cpp::string_view;
 using LIBC_NAMESPACE::printf_core::make_drop_overflow_writer;
+using LIBC_NAMESPACE::printf_core::make_writer;
+using LIBC_NAMESPACE::printf_core::overflow_write_flush_to_sink;
 using LIBC_NAMESPACE::printf_core::OverflowMode;
-using LIBC_NAMESPACE::printf_core::OverflowWriter;
 using LIBC_NAMESPACE::printf_core::WRITE_OK;
 using LIBC_NAMESPACE::printf_core::WriteBuffer;
 using LIBC_NAMESPACE::printf_core::Writer;
@@ -193,44 +194,28 @@ struct OutBuff {
   size_t cur_pos = 0;
 };
 
-void copy_to_out(string_view new_str, void *raw_out_buff) {
-  if (new_str.size() == 0)
-    return;
-
+int copy_to_out(string_view new_str, void *raw_out_buff) {
   OutBuff *out_buff = reinterpret_cast<OutBuff *>(raw_out_buff);
 
   LIBC_NAMESPACE::inline_memcpy(out_buff->out_str + out_buff->cur_pos,
                                 new_str.data(), new_str.size());
 
   out_buff->cur_pos += new_str.size();
-}
-
-int overflow_write_copy_to_out(WriteBuffer<char> &wb, string_view new_str,
-                               void *raw_out_buff) {
-  copy_to_out({wb.buff, wb.buff_cur}, raw_out_buff);
-  wb.buff_cur = 0;
-  copy_to_out(new_str, raw_out_buff);
   return WRITE_OK;
 }
 
-Writer<OverflowMode::RUNTIME_DISPATCH>
-make_test_writer(char *buffer, size_t buffer_length, OutBuff *out_buff) {
-  return Writer(buffer, buffer_length,
-                OverflowWriter<OverflowMode::RUNTIME_DISPATCH, char>(
-                    overflow_write_copy_to_out, out_buff));
-}
-
 TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLengthWithCallback) {
   char str[16];
 
   OutBuff out_buff = {str, 0};
 
   char wb_buff[8];
-  Writer writer = make_test_writer(wb_buff, sizeof(wb_buff) - 1, &out_buff);
+  Writer writer =
+      make_writer(wb_buff, sizeof(wb_buff) - 1,
+                  &overflow_write_flush_to_sink<char, copy_to_out>, &out_buff);
   writer.write({"abcDEF123456", 12});
 
-  WriteBuffer<char> &wb = writer.get_write_buffer();
-  copy_to_out({wb.buff, wb.buff_cur}, &out_buff);
+  writer.get_write_buffer().flush_to_sink<copy_to_out>(&out_buff);
   str[out_buff.cur_pos] = '\0';
 
   ASSERT_STREQ("abcDEF123456", str);
@@ -243,11 +228,12 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLengthWithCallback) {
   OutBuff out_buff = {str, 0};
 
   char wb_buff[8];
-  Writer writer = make_test_writer(wb_buff, sizeof(wb_buff) - 1, &out_buff);
+  Writer writer =
+      make_writer(wb_buff, sizeof(wb_buff) - 1,
+                  &overflow_write_flush_to_sink<char, copy_to_out>, &out_buff);
   writer.write('1', 15);
 
-  WriteBuffer<char> &wb = writer.get_write_buffer();
-  copy_to_out({wb.buff, wb.buff_cur}, &out_buff);
+  writer.get_write_buffer().flush_to_sink<copy_to_out>(&out_buff);
   str[out_buff.cur_pos] = '\0';
 
   ASSERT_STREQ("111111111111111", str);
@@ -260,14 +246,15 @@ TEST(LlvmLibcPrintfWriterTest, MixedWriteWithMaxLengthWithCallback) {
   OutBuff out_buff = {str, 0};
 
   char wb_buff[8];
-  Writer writer = make_test_writer(wb_buff, sizeof(wb_buff) - 1, &out_buff);
+  Writer writer =
+      make_writer(wb_buff, sizeof(wb_buff) - 1,
+                  &overflow_write_flush_to_sink<char, copy_to_out>, &out_buff);
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
   writer.write({"456", 3});
 
-  WriteBuffer<char> &wb = writer.get_write_buffer();
-  copy_to_out({wb.buff, wb.buff_cur}, &out_buff);
+  writer.get_write_buffer().flush_to_sink<copy_to_out>(&out_buff);
   str[out_buff.cur_pos] = '\0';
 
   ASSERT_STREQ("aaaDEF111456", str);
@@ -280,14 +267,15 @@ TEST(LlvmLibcPrintfWriterTest, ZeroLengthBufferWithCallback) {
   OutBuff out_buff = {str, 0};
 
   char wb_buff[1];
-  Writer writer = make_test_writer(wb_buff, 0, &out_buff);
+  Writer writer =
+      make_writer(wb_buff, sizeof(wb_buff) - 1,
+                  &overflow_write_flush_to_sink<char, copy_to_out>, &out_buff);
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
   writer.write({"456", 3});
 
-  WriteBuffer<char> &wb = writer.get_write_buffer();
-  copy_to_out({wb.buff, wb.buff_cur}, &out_buff);
+  writer.get_write_buffer().flush_to_sink<copy_to_out>(&out_buff);
   str[out_buff.cur_pos] = '\0';
 
   ASSERT_STREQ("aaaDEF111456", str);
@@ -299,12 +287,15 @@ TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLengthWithCallback) {
 
   OutBuff out_buff = {str, 0};
 
-  Writer writer = make_test_writer(nullptr, 0, &out_buff);
+  Writer writer =
+      make_writer(static_cast<char *>(nullptr), 0,
+                  &overflow_write_flush_to_sink<char, copy_to_out>, &out_buff);
   writer.write('a', 3);
   writer.write({"DEF", 3});
   writer.write('1', 3);
   writer.write({"456", 3});
 
+  writer.get_write_buffer().flush_to_sink<copy_to_out>(&out_buff);
   str[out_buff.cur_pos] = '\0';
 
   ASSERT_EQ(writer.get_chars_written(), size_t{12});

>From 83e9ee3ca088b474ccb8e38a716414da208a2f31 Mon Sep 17 00:00:00 2001
From: Alex Strelnikov <strel at google.com>
Date: Thu, 3 Sep 2026 15:00:51 +0000
Subject: [PATCH 3/3] Remove wip todo comment

---
 libc/src/stdio/baremetal/vfprintf_internal.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/libc/src/stdio/baremetal/vfprintf_internal.h b/libc/src/stdio/baremetal/vfprintf_internal.h
index 715124df7a3b3..e13a259cb6de8 100644
--- a/libc/src/stdio/baremetal/vfprintf_internal.h
+++ b/libc/src/stdio/baremetal/vfprintf_internal.h
@@ -45,7 +45,6 @@ LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
   static constexpr size_t BUFF_SIZE = 1024;
   char buffer[BUFF_SIZE];
 
-  // TODO:
   printf_core::Writer writer = print_core::make_writer(
       buffer, BUFF_SIZE,
       &printf_core::overflow_write_flush_to_sink<char, internal::write_hook>,



More information about the libc-commits mailing list