[libc-commits] [libc] [libc] Template the writing mode for the writer class (PR #111559)
Joseph Huber via libc-commits
libc-commits at lists.llvm.org
Tue Mar 11 11:30:44 PDT 2025
https://github.com/jhuber6 updated https://github.com/llvm/llvm-project/pull/111559
>From aa832ddf4bf13f6bdbe3699fd65368054ef4df33 Mon Sep 17 00:00:00 2001
From: Joseph Huber <huberjn at outlook.com>
Date: Tue, 8 Oct 2024 12:04:13 -0500
Subject: [PATCH] [libc] Template the writing mode for the writer class
Summary:
Currently we dispatch the writing mode off of a runtime enum passed in
by the constructor. This causes very unfortunate codegen for the GPU
targets where we get worst-case codegen because of the unused function
pointer for `sprintf`. Instead, this patch moves all of this to a
template so it can be masked out. This results in no dynamic stack and
uses 60 VGPRs instead of 117. It also compiles about 5x as fast.
WIP
Update
---
libc/config/config.json | 4 +
libc/config/gpu/amdgpu/config.json | 3 +
libc/config/gpu/nvptx/config.json | 3 +
libc/docs/configure.rst | 1 +
libc/src/stdio/printf_core/CMakeLists.txt | 15 +-
libc/src/stdio/printf_core/char_converter.h | 4 +-
libc/src/stdio/printf_core/converter.cpp | 105 --------------
libc/src/stdio/printf_core/converter.h | 85 ++++++++++-
libc/src/stdio/printf_core/fixed_converter.h | 4 +-
.../stdio/printf_core/float_dec_converter.h | 60 +++++---
.../stdio/printf_core/float_hex_converter.h | 3 +-
.../printf_core/float_inf_nan_converter.h | 4 +-
libc/src/stdio/printf_core/int_converter.h | 4 +-
libc/src/stdio/printf_core/printf_main.cpp | 43 ------
libc/src/stdio/printf_core/printf_main.h | 24 +++-
libc/src/stdio/printf_core/ptr_converter.h | 4 +-
.../stdio/printf_core/strerror_converter.h | 4 +-
libc/src/stdio/printf_core/string_converter.h | 4 +-
.../stdio/printf_core/vasprintf_internal.h | 11 +-
.../src/stdio/printf_core/vfprintf_internal.h | 6 +-
.../stdio/printf_core/write_int_converter.h | 3 +-
libc/src/stdio/printf_core/writer.cpp | 46 ------
libc/src/stdio/printf_core/writer.h | 132 ++++++++++++------
libc/src/stdio/snprintf.cpp | 6 +-
libc/src/stdio/sprintf.cpp | 6 +-
libc/src/stdio/vsnprintf.cpp | 6 +-
libc/src/stdio/vsprintf.cpp | 6 +-
libc/src/stdlib/str_from_util.h | 4 +-
libc/src/stdlib/strfromd.cpp | 6 +-
libc/src/stdlib/strfromf.cpp | 6 +-
libc/src/stdlib/strfroml.cpp | 6 +-
libc/src/time/strftime.cpp | 6 +-
libc/src/time/strftime_core/CMakeLists.txt | 8 +-
.../time/strftime_core/composite_converter.h | 21 ++-
libc/src/time/strftime_core/converter.cpp | 96 -------------
libc/src/time/strftime_core/converter.h | 81 ++++++++++-
libc/src/time/strftime_core/num_converter.h | 6 +-
libc/src/time/strftime_core/str_converter.h | 3 +-
libc/src/time/strftime_core/strftime_main.cpp | 40 ------
libc/src/time/strftime_core/strftime_main.h | 24 +++-
libc/src/time/strftime_l.cpp | 6 +-
.../src/stdio/printf_core/converter_test.cpp | 12 +-
.../src/stdio/printf_core/writer_test.cpp | 80 ++++++-----
libc/utils/gpu/server/CMakeLists.txt | 6 +-
libc/utils/gpu/server/rpc_server.cpp | 13 +-
45 files changed, 510 insertions(+), 510 deletions(-)
delete mode 100644 libc/src/stdio/printf_core/converter.cpp
delete mode 100644 libc/src/stdio/printf_core/printf_main.cpp
delete mode 100644 libc/src/stdio/printf_core/writer.cpp
delete mode 100644 libc/src/time/strftime_core/converter.cpp
delete mode 100644 libc/src/time/strftime_core/strftime_main.cpp
diff --git a/libc/config/config.json b/libc/config/config.json
index c38d424229218..d738aade74427 100644
--- a/libc/config/config.json
+++ b/libc/config/config.json
@@ -41,6 +41,10 @@
"LIBC_CONF_PRINTF_DISABLE_STRERROR": {
"value": false,
"doc": "Disable handling of %m to print strerror in printf and friends."
+ },
+ "LIBC_CONF_PRINTF_RUNTIME_DISPATCH": {
+ "value": true,
+ "doc": "Use dynamic dispatch for the output mechanism to reduce code size."
}
},
"scanf": {
diff --git a/libc/config/gpu/amdgpu/config.json b/libc/config/gpu/amdgpu/config.json
index d99f48ecbede1..30ae10e2cfd61 100644
--- a/libc/config/gpu/amdgpu/config.json
+++ b/libc/config/gpu/amdgpu/config.json
@@ -19,6 +19,9 @@
},
"LIBC_CONF_PRINTF_DISABLE_STRERROR": {
"value": true
+ },
+ "LIBC_CONF_PRINTF_RUNTIME_DISPATCH": {
+ "value": false
}
},
"scanf": {
diff --git a/libc/config/gpu/nvptx/config.json b/libc/config/gpu/nvptx/config.json
index d99f48ecbede1..30ae10e2cfd61 100644
--- a/libc/config/gpu/nvptx/config.json
+++ b/libc/config/gpu/nvptx/config.json
@@ -19,6 +19,9 @@
},
"LIBC_CONF_PRINTF_DISABLE_STRERROR": {
"value": true
+ },
+ "LIBC_CONF_PRINTF_RUNTIME_DISPATCH": {
+ "value": false
}
},
"scanf": {
diff --git a/libc/docs/configure.rst b/libc/docs/configure.rst
index 940a07754c458..a4b6237ef1e05 100644
--- a/libc/docs/configure.rst
+++ b/libc/docs/configure.rst
@@ -45,6 +45,7 @@ to learn about the defaults for your platform and target.
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_DYADIC_FLOAT``: Use dyadic float for faster and smaller but less accurate printf doubles.
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_FLOAT320``: Use an alternative printf float implementation based on 320-bit floats
- ``LIBC_CONF_PRINTF_FLOAT_TO_STR_USE_MEGA_LONG_DOUBLE_TABLE``: Use large table for better printf long double performance.
+ - ``LIBC_CONF_PRINTF_RUNTIME_DISPATCH``: Use dynamic distpatch for conversion functions to reduce code size.
* **"pthread" options**
- ``LIBC_CONF_RAW_MUTEX_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a mutex is in contention (default to 100).
- ``LIBC_CONF_RWLOCK_DEFAULT_SPIN_COUNT``: Default number of spins before blocking if a rwlock is in contention (default to 100).
diff --git a/libc/src/stdio/printf_core/CMakeLists.txt b/libc/src/stdio/printf_core/CMakeLists.txt
index ea58067c7070a..c22f9858f3b1e 100644
--- a/libc/src/stdio/printf_core/CMakeLists.txt
+++ b/libc/src/stdio/printf_core/CMakeLists.txt
@@ -25,6 +25,9 @@ endif()
if(LIBC_CONF_PRINTF_DISABLE_STRERROR)
list(APPEND printf_config_copts "-DLIBC_COPT_PRINTF_DISABLE_STRERROR")
endif()
+if(LIBC_CONF_PRINTF_RUNTIME_DISPATCH)
+ list(APPEND printf_config_copts "-DLIBC_COPT_PRINTF_RUNTIME_DISPATCH")
+endif()
if(printf_config_copts)
list(PREPEND printf_config_copts "COMPILE_OPTIONS")
endif()
@@ -62,10 +65,8 @@ add_header_library(
libc.src.__support.common
)
-add_object_library(
+add_header_library(
writer
- SRCS
- writer.cpp
HDRS
writer.h
DEPENDS
@@ -76,10 +77,8 @@ add_object_library(
libc.src.string.memory_utils.inline_memset
)
-add_object_library(
+add_header_library(
converter
- SRCS
- converter.cpp
HDRS
converter.h
converter_atlas.h
@@ -113,10 +112,8 @@ add_object_library(
libc.src.__support.StringUtil.error_to_string
)
-add_object_library(
+add_header_library(
printf_main
- SRCS
- printf_main.cpp
HDRS
printf_main.h
DEPENDS
diff --git a/libc/src/stdio/printf_core/char_converter.h b/libc/src/stdio/printf_core/char_converter.h
index 2596cba813c2e..fd2eb2553887a 100644
--- a/libc/src/stdio/printf_core/char_converter.h
+++ b/libc/src/stdio/printf_core/char_converter.h
@@ -17,7 +17,9 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
-LIBC_INLINE int convert_char(Writer *writer, const FormatSection &to_conv) {
+template <WriteMode write_mode>
+LIBC_INLINE int convert_char(Writer<write_mode> *writer,
+ const FormatSection &to_conv) {
char c = static_cast<char>(to_conv.conv_val_raw);
constexpr int STRING_LEN = 1;
diff --git a/libc/src/stdio/printf_core/converter.cpp b/libc/src/stdio/printf_core/converter.cpp
deleted file mode 100644
index b1c66451f53f0..0000000000000
--- a/libc/src/stdio/printf_core/converter.cpp
+++ /dev/null
@@ -1,105 +0,0 @@
-//===-- Format specifier converter implmentation for printf -----*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "src/stdio/printf_core/converter.h"
-
-#include "src/__support/macros/config.h"
-#include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/printf_config.h"
-#include "src/stdio/printf_core/strerror_converter.h"
-#include "src/stdio/printf_core/writer.h"
-
-// This option allows for replacing all of the conversion functions with custom
-// replacements. This allows conversions to be replaced at compile time.
-#ifndef LIBC_COPT_PRINTF_CONV_ATLAS
-#include "src/stdio/printf_core/converter_atlas.h"
-#else
-#include LIBC_COPT_PRINTF_CONV_ATLAS
-#endif
-
-#include <stddef.h>
-
-namespace LIBC_NAMESPACE_DECL {
-namespace printf_core {
-
-int convert(Writer *writer, const FormatSection &to_conv) {
- if (!to_conv.has_conv)
- return writer->write(to_conv.raw_string);
-
-#if !defined(LIBC_COPT_PRINTF_DISABLE_FLOAT) && \
- defined(LIBC_COPT_PRINTF_HEX_LONG_DOUBLE)
- if (to_conv.length_modifier == LengthModifier::L) {
- switch (to_conv.conv_name) {
- case 'f':
- case 'F':
- case 'e':
- case 'E':
- case 'g':
- case 'G':
- return convert_float_hex_exp(writer, to_conv);
- default:
- break;
- }
- }
-#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
-
- switch (to_conv.conv_name) {
- case '%':
- return writer->write("%");
- case 'c':
- return convert_char(writer, to_conv);
- case 's':
- return convert_string(writer, to_conv);
- case 'd':
- case 'i':
- case 'u':
- case 'o':
- case 'x':
- case 'X':
- case 'b':
- case 'B':
- return convert_int(writer, to_conv);
-#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
- case 'f':
- case 'F':
- return convert_float_decimal(writer, to_conv);
- case 'e':
- case 'E':
- return convert_float_dec_exp(writer, to_conv);
- case 'a':
- case 'A':
- return convert_float_hex_exp(writer, to_conv);
- case 'g':
- case 'G':
- return convert_float_dec_auto(writer, to_conv);
-#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
-#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
- case 'r':
- case 'R':
- case 'k':
- case 'K':
- return convert_fixed(writer, to_conv);
-#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
-#ifndef LIBC_COPT_PRINTF_DISABLE_STRERROR
- case 'm':
- return convert_strerror(writer, to_conv);
-#endif // LIBC_COPT_PRINTF_DISABLE_STRERROR
-#ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT
- case 'n':
- return convert_write_int(writer, to_conv);
-#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
- case 'p':
- return convert_pointer(writer, to_conv);
- default:
- return writer->write(to_conv.raw_string);
- }
- return -1;
-}
-
-} // namespace printf_core
-} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/converter.h b/libc/src/stdio/printf_core/converter.h
index 2b3f06d0aa7a3..f26ed727f05f4 100644
--- a/libc/src/stdio/printf_core/converter.h
+++ b/libc/src/stdio/printf_core/converter.h
@@ -11,8 +11,18 @@
#include "src/__support/macros/config.h"
#include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/printf_config.h"
+#include "src/stdio/printf_core/strerror_converter.h"
#include "src/stdio/printf_core/writer.h"
+// This option allows for replacing all of the conversion functions with custom
+// replacements. This allows conversions to be replaced at compile time.
+#ifndef LIBC_COPT_PRINTF_CONV_ATLAS
+#include "src/stdio/printf_core/converter_atlas.h"
+#else
+#include LIBC_COPT_PRINTF_CONV_ATLAS
+#endif
+
#include <stddef.h>
namespace LIBC_NAMESPACE_DECL {
@@ -21,7 +31,80 @@ namespace printf_core {
// convert will call a conversion function to convert the FormatSection into
// its string representation, and then that will write the result to the
// writer.
-int convert(Writer *writer, const FormatSection &to_conv);
+template <WriteMode write_mode>
+int convert(Writer<write_mode> *writer, const FormatSection &to_conv) {
+ if (!to_conv.has_conv)
+ return writer->write(to_conv.raw_string);
+
+#if !defined(LIBC_COPT_PRINTF_DISABLE_FLOAT) && \
+ defined(LIBC_COPT_PRINTF_HEX_LONG_DOUBLE)
+ if (to_conv.length_modifier == LengthModifier::L) {
+ switch (to_conv.conv_name) {
+ case 'f':
+ case 'F':
+ case 'e':
+ case 'E':
+ case 'g':
+ case 'G':
+ return convert_float_hex_exp(writer, to_conv);
+ default:
+ break;
+ }
+ }
+#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
+
+ switch (to_conv.conv_name) {
+ case '%':
+ return writer->write("%");
+ case 'c':
+ return convert_char(writer, to_conv);
+ case 's':
+ return convert_string(writer, to_conv);
+ case 'd':
+ case 'i':
+ case 'u':
+ case 'o':
+ case 'x':
+ case 'X':
+ case 'b':
+ case 'B':
+ return convert_int(writer, to_conv);
+#ifndef LIBC_COPT_PRINTF_DISABLE_FLOAT
+ case 'f':
+ case 'F':
+ return convert_float_decimal(writer, to_conv);
+ case 'e':
+ case 'E':
+ return convert_float_dec_exp(writer, to_conv);
+ case 'a':
+ case 'A':
+ return convert_float_hex_exp(writer, to_conv);
+ case 'g':
+ case 'G':
+ return convert_float_dec_auto(writer, to_conv);
+#endif // LIBC_COPT_PRINTF_DISABLE_FLOAT
+#ifdef LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
+ case 'r':
+ case 'R':
+ case 'k':
+ case 'K':
+ return convert_fixed(writer, to_conv);
+#endif // LIBC_INTERNAL_PRINTF_HAS_FIXED_POINT
+#ifndef LIBC_COPT_PRINTF_DISABLE_STRERROR
+ case 'm':
+ return convert_strerror(writer, to_conv);
+#endif // LIBC_COPT_PRINTF_DISABLE_STRERROR
+#ifndef LIBC_COPT_PRINTF_DISABLE_WRITE_INT
+ case 'n':
+ return convert_write_int(writer, to_conv);
+#endif // LIBC_COPT_PRINTF_DISABLE_WRITE_INT
+ case 'p':
+ return convert_pointer(writer, to_conv);
+ default:
+ return writer->write(to_conv.raw_string);
+ }
+ return -1;
+}
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/fixed_converter.h b/libc/src/stdio/printf_core/fixed_converter.h
index ba0a62d9fcb87..e8a3314967562 100644
--- a/libc/src/stdio/printf_core/fixed_converter.h
+++ b/libc/src/stdio/printf_core/fixed_converter.h
@@ -63,7 +63,9 @@ LIBC_INLINE constexpr uint32_t const_ten_exp(uint32_t exponent) {
} \
} while (false)
-LIBC_INLINE int convert_fixed(Writer *writer, const FormatSection &to_conv) {
+template <WriteMode write_mode>
+LIBC_INLINE int convert_fixed(Writer<write_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.
using LARep = fixed_point::FXRep<unsigned long accum>;
diff --git a/libc/src/stdio/printf_core/float_dec_converter.h b/libc/src/stdio/printf_core/float_dec_converter.h
index d93457fcafd7f..ee5549825a6f2 100644
--- a/libc/src/stdio/printf_core/float_dec_converter.h
+++ b/libc/src/stdio/printf_core/float_dec_converter.h
@@ -93,7 +93,7 @@ zero_after_digits(int32_t base_2_exp, int32_t digits_after_point, T mantissa,
return has_trailing_zeros;
}
-class PaddingWriter {
+template <WriteMode write_mode> class PaddingWriter {
bool left_justified = false;
bool leading_zeroes = false;
char sign_char = 0;
@@ -107,7 +107,8 @@ 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 *writer, size_t total_digits) {
+ LIBC_INLINE int write_left_padding(Writer<write_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.
int padding_amount =
@@ -130,7 +131,8 @@ class PaddingWriter {
return 0;
}
- LIBC_INLINE int write_right_padding(Writer *writer, size_t total_digits) {
+ LIBC_INLINE int write_right_padding(Writer<write_mode> *writer,
+ size_t total_digits) {
// If and only if the conversion is left justified, there may be trailing
// spaces.
int padding_amount =
@@ -155,7 +157,7 @@ class PaddingWriter {
This FloatWriter class does the buffering and counting, and writes to the
output when necessary.
*/
-class FloatWriter {
+template <WriteMode write_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.
@@ -164,8 +166,9 @@ 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 *writer; // Writes to the final output.
- PaddingWriter padding_writer; // Handles prefixes/padding, uses total_digits.
+ Writer<write_mode> *writer; // Writes to the final output.
+ PaddingWriter<write_mode>
+ padding_writer; // Handles prefixes/padding, uses total_digits.
LIBC_INLINE int flush_buffer(bool round_up_max_blocks = false) {
const char MAX_BLOCK_DIGIT = (round_up_max_blocks ? '0' : '9');
@@ -245,8 +248,9 @@ class FloatWriter {
static_assert(fputil::FPBits<long double>::EXP_LEN < (sizeof(int) * 8));
public:
- LIBC_INLINE FloatWriter(Writer *init_writer, bool init_has_decimal_point,
- const PaddingWriter &init_padding_writer)
+ LIBC_INLINE FloatWriter(Writer<write_mode> *init_writer,
+ bool init_has_decimal_point,
+ const PaddingWriter<write_mode> &init_padding_writer)
: has_decimal_point(init_has_decimal_point), writer(init_writer),
padding_writer(init_padding_writer) {}
@@ -466,12 +470,24 @@ 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>;
+
// 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, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
-LIBC_INLINE int convert_float_decimal_typed(Writer *writer,
+template <typename T, WriteMode write_mode,
+ cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
+LIBC_INLINE int convert_float_decimal_typed(Writer<write_mode> *writer,
const FormatSection &to_conv,
fputil::FPBits<T> float_bits) {
// signed because later we use -FRACTION_LEN
@@ -498,7 +514,7 @@ LIBC_INLINE int convert_float_decimal_typed(Writer *writer,
// ignored.
bool nonzero = false;
- PaddingWriter padding_writer(to_conv, sign_char);
+ PaddingWriter<write_mode> padding_writer(to_conv, sign_char);
FloatWriter float_writer(writer, has_decimal_point, padding_writer);
FloatToString<T> float_converter(float_bits.get_val());
@@ -579,8 +595,9 @@ LIBC_INLINE int convert_float_decimal_typed(Writer *writer,
return WRITE_OK;
}
-template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
-LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer,
+template <typename T, WriteMode write_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,
const FormatSection &to_conv,
fputil::FPBits<T> float_bits) {
// signed because later we use -FRACTION_LEN
@@ -603,7 +620,7 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer,
bool has_decimal_point =
(precision > 0) || ((to_conv.flags & FormatFlags::ALTERNATE_FORM) != 0);
- PaddingWriter padding_writer(to_conv, sign_char);
+ PaddingWriter<write_mode> padding_writer(to_conv, sign_char);
FloatWriter float_writer(writer, has_decimal_point, padding_writer);
FloatToString<T> float_converter(float_bits.get_val());
@@ -740,8 +757,9 @@ LIBC_INLINE int convert_float_dec_exp_typed(Writer *writer,
return WRITE_OK;
}
-template <typename T, cpp::enable_if_t<cpp::is_floating_point_v<T>, int> = 0>
-LIBC_INLINE int convert_float_dec_auto_typed(Writer *writer,
+template <typename T, WriteMode write_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,
const FormatSection &to_conv,
fputil::FPBits<T> float_bits) {
// signed because later we use -FRACTION_LEN
@@ -1107,7 +1125,9 @@ LIBC_INLINE int convert_float_dec_auto_typed(Writer *writer,
}
// TODO: unify the float converters to remove the duplicated checks for inf/nan.
-LIBC_INLINE int convert_float_decimal(Writer *writer,
+
+template <WriteMode write_mode>
+LIBC_INLINE int convert_float_decimal(Writer<write_mode> *writer,
const FormatSection &to_conv) {
if (to_conv.length_modifier == LengthModifier::L) {
fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
@@ -1128,7 +1148,8 @@ LIBC_INLINE int convert_float_decimal(Writer *writer,
return convert_inf_nan(writer, to_conv);
}
-LIBC_INLINE int convert_float_dec_exp(Writer *writer,
+template <WriteMode write_mode>
+LIBC_INLINE int convert_float_dec_exp(Writer<write_mode> *writer,
const FormatSection &to_conv) {
if (to_conv.length_modifier == LengthModifier::L) {
fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
@@ -1149,7 +1170,8 @@ LIBC_INLINE int convert_float_dec_exp(Writer *writer,
return convert_inf_nan(writer, to_conv);
}
-LIBC_INLINE int convert_float_dec_auto(Writer *writer,
+template <WriteMode write_mode>
+LIBC_INLINE int convert_float_dec_auto(Writer<write_mode> *writer,
const FormatSection &to_conv) {
if (to_conv.length_modifier == LengthModifier::L) {
fputil::FPBits<long double>::StorageType float_raw = to_conv.conv_val_raw;
diff --git a/libc/src/stdio/printf_core/float_hex_converter.h b/libc/src/stdio/printf_core/float_hex_converter.h
index b264b5cf20728..16592e7bac932 100644
--- a/libc/src/stdio/printf_core/float_hex_converter.h
+++ b/libc/src/stdio/printf_core/float_hex_converter.h
@@ -25,7 +25,8 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
-LIBC_INLINE int convert_float_hex_exp(Writer *writer,
+template <WriteMode write_mode>
+LIBC_INLINE int convert_float_hex_exp(Writer<write_mode> *writer,
const FormatSection &to_conv) {
using LDBits = fputil::FPBits<long double>;
using StorageType = LDBits::StorageType;
diff --git a/libc/src/stdio/printf_core/float_inf_nan_converter.h b/libc/src/stdio/printf_core/float_inf_nan_converter.h
index 3e41612e21c9f..ce31d7ae55499 100644
--- a/libc/src/stdio/printf_core/float_inf_nan_converter.h
+++ b/libc/src/stdio/printf_core/float_inf_nan_converter.h
@@ -24,7 +24,9 @@ namespace printf_core {
using StorageType = fputil::FPBits<long double>::StorageType;
-LIBC_INLINE int convert_inf_nan(Writer *writer, const FormatSection &to_conv) {
+template <WriteMode write_mode>
+LIBC_INLINE int convert_inf_nan(Writer<write_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.
bool is_negative;
diff --git a/libc/src/stdio/printf_core/int_converter.h b/libc/src/stdio/printf_core/int_converter.h
index d0af229f89be5..11234c32ce997 100644
--- a/libc/src/stdio/printf_core/int_converter.h
+++ b/libc/src/stdio/printf_core/int_converter.h
@@ -61,7 +61,9 @@ num_to_strview(uintmax_t num, cpp::span<char> bufref, char conv_name) {
} // namespace details
-LIBC_INLINE int convert_int(Writer *writer, const FormatSection &to_conv) {
+template <WriteMode write_mode>
+LIBC_INLINE int convert_int(Writer<write_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/stdio/printf_core/printf_main.cpp b/libc/src/stdio/printf_core/printf_main.cpp
deleted file mode 100644
index bd4a5a168bd23..0000000000000
--- a/libc/src/stdio/printf_core/printf_main.cpp
+++ /dev/null
@@ -1,43 +0,0 @@
-//===-- Starting point for printf -------------------------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "src/stdio/printf_core/printf_main.h"
-
-#include "src/__support/arg_list.h"
-#include "src/__support/macros/config.h"
-#include "src/stdio/printf_core/converter.h"
-#include "src/stdio/printf_core/core_structs.h"
-#include "src/stdio/printf_core/parser.h"
-#include "src/stdio/printf_core/writer.h"
-
-#include <stddef.h>
-
-namespace LIBC_NAMESPACE_DECL {
-namespace printf_core {
-
-int printf_main(Writer *writer, const char *__restrict str,
- internal::ArgList &args) {
- Parser<internal::ArgList> parser(str, args);
- int result = 0;
- for (FormatSection cur_section = parser.get_next_section();
- !cur_section.raw_string.empty();
- cur_section = parser.get_next_section()) {
- if (cur_section.has_conv)
- result = convert(writer, cur_section);
- else
- result = writer->write(cur_section.raw_string);
-
- if (result < 0)
- return result;
- }
-
- return writer->get_chars_written();
-}
-
-} // namespace printf_core
-} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/printf_main.h b/libc/src/stdio/printf_core/printf_main.h
index 3e73bf36e0e30..57f29858d5298 100644
--- a/libc/src/stdio/printf_core/printf_main.h
+++ b/libc/src/stdio/printf_core/printf_main.h
@@ -11,6 +11,9 @@
#include "src/__support/arg_list.h"
#include "src/__support/macros/config.h"
+#include "src/stdio/printf_core/converter.h"
+#include "src/stdio/printf_core/core_structs.h"
+#include "src/stdio/printf_core/parser.h"
#include "src/stdio/printf_core/writer.h"
#include <stddef.h>
@@ -18,8 +21,25 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
-int printf_main(Writer *writer, const char *__restrict str,
- internal::ArgList &args);
+template <WriteMode write_mode>
+int printf_main(Writer<write_mode> *writer, const char *__restrict str,
+ internal::ArgList &args) {
+ Parser<internal::ArgList> parser(str, args);
+ int result = 0;
+ for (FormatSection cur_section = parser.get_next_section();
+ !cur_section.raw_string.empty();
+ cur_section = parser.get_next_section()) {
+ if (cur_section.has_conv)
+ result = convert(writer, cur_section);
+ else
+ result = writer->write(cur_section.raw_string);
+
+ if (result < 0)
+ return result;
+ }
+
+ return writer->get_chars_written();
+}
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/ptr_converter.h b/libc/src/stdio/printf_core/ptr_converter.h
index bf84718dfe6a8..c2a74e3043e6f 100644
--- a/libc/src/stdio/printf_core/ptr_converter.h
+++ b/libc/src/stdio/printf_core/ptr_converter.h
@@ -18,7 +18,9 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
-LIBC_INLINE int convert_pointer(Writer *writer, const FormatSection &to_conv) {
+template <WriteMode write_mode>
+LIBC_INLINE int convert_pointer(Writer<write_mode> *writer,
+ const FormatSection &to_conv) {
FormatSection new_conv = to_conv;
if (to_conv.conv_val_ptr == nullptr) {
diff --git a/libc/src/stdio/printf_core/strerror_converter.h b/libc/src/stdio/printf_core/strerror_converter.h
index 2902fd37c31ae..2cd6df0c01d45 100644
--- a/libc/src/stdio/printf_core/strerror_converter.h
+++ b/libc/src/stdio/printf_core/strerror_converter.h
@@ -19,7 +19,9 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
-LIBC_INLINE int convert_strerror(Writer *writer, const FormatSection &to_conv) {
+template <WriteMode write_mode>
+LIBC_INLINE int convert_strerror(Writer<write_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/stdio/printf_core/string_converter.h b/libc/src/stdio/printf_core/string_converter.h
index 1f36d51124107..74c9f598210f7 100644
--- a/libc/src/stdio/printf_core/string_converter.h
+++ b/libc/src/stdio/printf_core/string_converter.h
@@ -20,7 +20,9 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
-LIBC_INLINE int convert_string(Writer *writer, const FormatSection &to_conv) {
+template <WriteMode write_mode>
+LIBC_INLINE int convert_string(Writer<write_mode> *writer,
+ const FormatSection &to_conv) {
size_t string_len = 0;
const char *str_ptr = reinterpret_cast<const char *>(to_conv.conv_val_ptr);
diff --git a/libc/src/stdio/printf_core/vasprintf_internal.h b/libc/src/stdio/printf_core/vasprintf_internal.h
index 0e446f856e438..9d46617da7751 100644
--- a/libc/src/stdio/printf_core/vasprintf_internal.h
+++ b/libc/src/stdio/printf_core/vasprintf_internal.h
@@ -19,8 +19,9 @@ namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str, void *target) {
- printf_core::WriteBuffer *wb =
- reinterpret_cast<printf_core::WriteBuffer *>(target);
+ WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> *wb =
+ reinterpret_cast<
+ WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> *>(target);
size_t new_size = new_str.size() + wb->buff_cur;
const bool isBuffOnStack = (wb->buff == wb->init_buff);
char *new_buff = static_cast<char *>(
@@ -45,9 +46,9 @@ constexpr size_t DEFAULT_BUFFER_SIZE = 200;
LIBC_INLINE int vasprintf_internal(char **ret, const char *__restrict format,
internal::ArgList args) {
char init_buff_on_stack[DEFAULT_BUFFER_SIZE];
- printf_core::WriteBuffer wb(init_buff_on_stack, DEFAULT_BUFFER_SIZE,
- resize_overflow_hook);
- printf_core::Writer writer(&wb);
+ printf_core::WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> wb(
+ init_buff_on_stack, DEFAULT_BUFFER_SIZE, resize_overflow_hook);
+ printf_core::Writer writer(wb);
auto ret_val = printf_core::printf_main(&writer, format, args);
if (ret_val < 0) {
diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h
index 3becfee71dd27..630de9d9d43dd 100644
--- a/libc/src/stdio/printf_core/vfprintf_internal.h
+++ b/libc/src/stdio/printf_core/vfprintf_internal.h
@@ -72,9 +72,9 @@ LIBC_INLINE int vfprintf_internal(::FILE *__restrict stream,
internal::ArgList &args) {
constexpr size_t BUFF_SIZE = 1024;
char buffer[BUFF_SIZE];
- printf_core::WriteBuffer wb(buffer, BUFF_SIZE, &file_write_hook,
- reinterpret_cast<void *>(stream));
- Writer writer(&wb);
+ printf_core::WriteBuffer<Mode<WriteMode::FLUSH_TO_STREAM>::value> wb(
+ buffer, BUFF_SIZE, &file_write_hook, reinterpret_cast<void *>(stream));
+ Writer writer(wb);
internal::flockfile(stream);
int retval = printf_main(&writer, format, args);
int flushval = wb.overflow_write("");
diff --git a/libc/src/stdio/printf_core/write_int_converter.h b/libc/src/stdio/printf_core/write_int_converter.h
index a47cb41cb3287..efcff278bd284 100644
--- a/libc/src/stdio/printf_core/write_int_converter.h
+++ b/libc/src/stdio/printf_core/write_int_converter.h
@@ -19,7 +19,8 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
-LIBC_INLINE int convert_write_int(Writer *writer,
+template <WriteMode write_mode>
+LIBC_INLINE int convert_write_int(Writer<write_mode> *writer,
const FormatSection &to_conv) {
#ifndef LIBC_COPT_PRINTF_NO_NULLPTR_CHECKS
diff --git a/libc/src/stdio/printf_core/writer.cpp b/libc/src/stdio/printf_core/writer.cpp
deleted file mode 100644
index d1cf85df1c8f8..0000000000000
--- a/libc/src/stdio/printf_core/writer.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-//===-- Writer definition for printf ----------------------------*- C++ -*-===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "writer.h"
-#include "src/__support/CPP/string_view.h"
-#include "src/__support/macros/config.h"
-#include "src/stdio/printf_core/core_structs.h"
-#include "src/string/memory_utils/inline_memset.h"
-#include <stddef.h>
-
-namespace LIBC_NAMESPACE_DECL {
-namespace printf_core {
-
-int Writer::pad(char 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);
- 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);
- while (written + MINI_BUFF_SIZE < length) {
- int result = wb->overflow_write(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);
-}
-
-} // namespace printf_core
-} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/printf_core/writer.h b/libc/src/stdio/printf_core/writer.h
index 28c9a28270875..40aa0a07c6322 100644
--- a/libc/src/stdio/printf_core/writer.h
+++ b/libc/src/stdio/printf_core/writer.h
@@ -21,12 +21,24 @@
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
-struct WriteBuffer {
- enum class WriteMode {
- FILL_BUFF_AND_DROP_OVERFLOW,
- FLUSH_TO_STREAM,
- RESIZE_AND_FILL_BUFF,
- };
+enum class WriteMode {
+ FILL_BUFF_AND_DROP_OVERFLOW,
+ FLUSH_TO_STREAM,
+ RESIZE_AND_FILL_BUFF,
+ RUNTIME_DISPATCH,
+};
+
+// 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 {
+#ifdef LIBC_COPT_PRINTF_RUNTIME_DISPATCH
+ static constexpr WriteMode value = WriteMode::RUNTIME_DISPATCH;
+#else
+ static constexpr WriteMode value = write_mode;
+#endif
+};
+
+template <WriteMode write_mode> struct WriteBuffer {
using StreamWriter = int (*)(cpp::string_view, void *);
char *buff;
const char *init_buff; // for checking when resize.
@@ -35,23 +47,26 @@ struct WriteBuffer {
// The stream writer will be called when the buffer is full. It will be passed
// string_views to write to the stream.
- StreamWriter stream_writer;
+ const StreamWriter stream_writer;
void *output_target;
- WriteMode write_mode;
- LIBC_INLINE WriteBuffer(char *Buff, size_t Buff_len, StreamWriter hook,
+ // The current writing mode in case the user wants runtime dispatch of the
+ // stream writer with function pointers.
+ [[maybe_unused]] WriteMode write_mode_;
+
+ LIBC_INLINE WriteBuffer(char *buff, size_t buff_len, StreamWriter hook,
void *target)
- : buff(Buff), init_buff(Buff), buff_len(Buff_len), stream_writer(hook),
- output_target(target), write_mode(WriteMode::FLUSH_TO_STREAM) {}
+ : buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(hook),
+ output_target(target), write_mode_(WriteMode::FLUSH_TO_STREAM) {}
- LIBC_INLINE WriteBuffer(char *Buff, size_t Buff_len)
- : buff(Buff), init_buff(Buff), buff_len(Buff_len), stream_writer(nullptr),
+ LIBC_INLINE WriteBuffer(char *buff, size_t buff_len)
+ : buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(nullptr),
output_target(nullptr),
- write_mode(WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) {}
+ write_mode_(WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) {}
- LIBC_INLINE WriteBuffer(char *Buff, size_t Buff_len, StreamWriter hook)
- : buff(Buff), init_buff(Buff), buff_len(Buff_len), stream_writer(hook),
- output_target(this), write_mode(WriteMode::RESIZE_AND_FILL_BUFF) {}
+ LIBC_INLINE WriteBuffer(char *buff, size_t buff_len, StreamWriter hook)
+ : buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(hook),
+ output_target(this), write_mode_(WriteMode::RESIZE_AND_FILL_BUFF) {}
LIBC_INLINE int flush_to_stream(cpp::string_view new_str) {
if (buff_cur > 0) {
@@ -92,40 +107,69 @@ struct WriteBuffer {
// this with an empty string will flush the buffer if relevant.
LIBC_INLINE int overflow_write(cpp::string_view new_str) {
- switch (write_mode) {
- case WriteMode::FILL_BUFF_AND_DROP_OVERFLOW:
+#ifdef LIBC_COPT_PRINTF_RUNTIME_DISPATCH
+ if (write_mode_ == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW)
return fill_remaining_to_buff(new_str);
- case WriteMode::FLUSH_TO_STREAM:
+ else if (write_mode_ == WriteMode::FLUSH_TO_STREAM)
return flush_to_stream(new_str);
- case WriteMode::RESIZE_AND_FILL_BUFF:
+ else if (write_mode_ == WriteMode::RESIZE_AND_FILL_BUFF)
return resize_and_write(new_str);
- }
+#else
+ if constexpr (write_mode == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW)
+ return fill_remaining_to_buff(new_str);
+ else if constexpr (write_mode == WriteMode::FLUSH_TO_STREAM)
+ return flush_to_stream(new_str);
+ else if constexpr (write_mode == WriteMode::RESIZE_AND_FILL_BUFF)
+ return resize_and_write(new_str);
+#endif
__builtin_unreachable();
}
};
-class Writer final {
- WriteBuffer *wb;
+template <WriteMode write_mode> class Writer final {
+ WriteBuffer<write_mode> &wb;
int chars_written = 0;
- // This is a separate, non-inlined function so that the inlined part of the
- // write function is shorter.
- int pad(char new_char, size_t length);
+ LIBC_INLINE int pad(char 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);
+ 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);
+ while (written + MINI_BUFF_SIZE < length) {
+ int result = wb.overflow_write(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);
+ }
public:
- LIBC_INLINE Writer(WriteBuffer *WB) : wb(WB) {}
+ LIBC_INLINE Writer(WriteBuffer<write_mode> &wb) : wb(wb) {}
// 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) {
chars_written += static_cast<int>(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(),
+ 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());
- wb->buff_cur += new_string.size();
+ wb.buff_cur += new_string.size();
return WRITE_OK;
}
- return wb->overflow_write(new_string);
+ return wb.overflow_write(new_string);
}
// Takes a char and a length, memsets the next length characters of the buffer
@@ -134,10 +178,10 @@ class Writer final {
LIBC_INLINE int write(char new_char, size_t length) {
chars_written += static_cast<int>(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);
- wb->buff_cur += 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);
+ wb.buff_cur += length;
return WRITE_OK;
}
return pad(new_char, length);
@@ -147,18 +191,26 @@ class Writer final {
// to the overflow mechanism to be handled separately.
LIBC_INLINE int write(char 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;
+ 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 wb.overflow_write(char_string_view);
}
LIBC_INLINE int get_chars_written() { return chars_written; }
};
+// 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>;
+
} // namespace printf_core
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp
index 12ad3cd1f762b..c8940862f711f 100644
--- a/libc/src/stdio/snprintf.cpp
+++ b/libc/src/stdio/snprintf.cpp
@@ -27,8 +27,10 @@ LLVM_LIBC_FUNCTION(int, snprintf,
// and pointer semantics, as well as handling
// destruction automatically.
va_end(vlist);
- printf_core::WriteBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
- printf_core::Writer writer(&wb);
+ printf_core::WriteBuffer<printf_core::Mode<
+ printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
+ wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
+ printf_core::Writer writer(wb);
int ret_val = printf_core::printf_main(&writer, format, args);
if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp
index 1f59e6bae4723..7be97d3591aaf 100644
--- a/libc/src/stdio/sprintf.cpp
+++ b/libc/src/stdio/sprintf.cpp
@@ -28,8 +28,10 @@ LLVM_LIBC_FUNCTION(int, sprintf,
// destruction automatically.
va_end(vlist);
- printf_core::WriteBuffer wb(buffer, cpp::numeric_limits<size_t>::max());
- printf_core::Writer writer(&wb);
+ printf_core::WriteBuffer<
+ printf_core::Mode<printf_core::WriteMode::RESIZE_AND_FILL_BUFF>::value>
+ wb(buffer, cpp::numeric_limits<size_t>::max());
+ printf_core::Writer writer(wb);
int ret_val = printf_core::printf_main(&writer, format, args);
wb.buff[wb.buff_cur] = '\0';
diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp
index a584c76833a2d..b07a2499a0dd3 100644
--- a/libc/src/stdio/vsnprintf.cpp
+++ b/libc/src/stdio/vsnprintf.cpp
@@ -24,8 +24,10 @@ 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::WriteBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
- printf_core::Writer writer(&wb);
+ printf_core::WriteBuffer<printf_core::Mode<
+ printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
+ wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
+ printf_core::Writer writer(wb);
int ret_val = printf_core::printf_main(&writer, format, args);
if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer.
diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp
index b3978a09c671a..26d497be42125 100644
--- a/libc/src/stdio/vsprintf.cpp
+++ b/libc/src/stdio/vsprintf.cpp
@@ -25,8 +25,10 @@ LLVM_LIBC_FUNCTION(int, vsprintf,
// and pointer semantics, as well as handling
// destruction automatically.
- printf_core::WriteBuffer wb(buffer, cpp::numeric_limits<size_t>::max());
- printf_core::Writer writer(&wb);
+ printf_core::WriteBuffer<printf_core::Mode<
+ printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
+ wb(buffer, cpp::numeric_limits<size_t>::max());
+ printf_core::Writer writer(wb);
int ret_val = printf_core::printf_main(&writer, format, args);
wb.buff[wb.buff_cur] = '\0';
diff --git a/libc/src/stdlib/str_from_util.h b/libc/src/stdlib/str_from_util.h
index 7f54bdf71a018..61e6ba24b3817 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>
-int strfromfloat_convert(printf_core::Writer *writer,
+template <typename T, printf_core::WriteMode write_mode>
+int strfromfloat_convert(printf_core::Writer<write_mode> *writer,
const printf_core::FormatSection §ion) {
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 4c51e4c5c8a01..f51e6d4c7f1df 100644
--- a/libc/src/stdlib/strfromd.cpp
+++ b/libc/src/stdlib/strfromd.cpp
@@ -19,8 +19,10 @@ LLVM_LIBC_FUNCTION(int, strfromd,
printf_core::FormatSection section =
internal::parse_format_string(format, fp);
- printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0));
- printf_core::Writer writer(&wb);
+ printf_core::WriteBuffer<printf_core::Mode<
+ printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
+ wb(s, (n > 0 ? n - 1 : 0));
+ printf_core::Writer writer(wb);
int result = 0;
if (section.has_conv)
diff --git a/libc/src/stdlib/strfromf.cpp b/libc/src/stdlib/strfromf.cpp
index ea98a69ee4d60..14dbfdb25bab6 100644
--- a/libc/src/stdlib/strfromf.cpp
+++ b/libc/src/stdlib/strfromf.cpp
@@ -19,8 +19,10 @@ LLVM_LIBC_FUNCTION(int, strfromf,
printf_core::FormatSection section =
internal::parse_format_string(format, fp);
- printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0));
- printf_core::Writer writer(&wb);
+ printf_core::WriteBuffer<printf_core::Mode<
+ printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
+ wb(s, (n > 0 ? n - 1 : 0));
+ printf_core::Writer writer(wb);
int result = 0;
if (section.has_conv)
diff --git a/libc/src/stdlib/strfroml.cpp b/libc/src/stdlib/strfroml.cpp
index d5bee7609f69c..12f22a8a2fb65 100644
--- a/libc/src/stdlib/strfroml.cpp
+++ b/libc/src/stdlib/strfroml.cpp
@@ -24,8 +24,10 @@ LLVM_LIBC_FUNCTION(int, strfroml,
// the length modifier has to be set to LenghtModifier::L
section.length_modifier = printf_core::LengthModifier::L;
- printf_core::WriteBuffer wb(s, (n > 0 ? n - 1 : 0));
- printf_core::Writer writer(&wb);
+ printf_core::WriteBuffer<printf_core::Mode<
+ printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
+ wb(s, (n > 0 ? n - 1 : 0));
+ printf_core::Writer writer(wb);
int result = 0;
if (section.has_conv)
diff --git a/libc/src/time/strftime.cpp b/libc/src/time/strftime.cpp
index c19e58fbadf71..f36091bc9736e 100644
--- a/libc/src/time/strftime.cpp
+++ b/libc/src/time/strftime.cpp
@@ -19,8 +19,10 @@ namespace LIBC_NAMESPACE_DECL {
LLVM_LIBC_FUNCTION(size_t, strftime,
(char *__restrict buffer, size_t buffsz,
const char *__restrict format, const tm *timeptr)) {
- printf_core::WriteBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
- printf_core::Writer writer(&wb);
+ printf_core::WriteBuffer<printf_core::Mode<
+ printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
+ wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
+ printf_core::Writer writer(wb);
int 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/src/time/strftime_core/CMakeLists.txt b/libc/src/time/strftime_core/CMakeLists.txt
index a12a26b2aee0f..5e40e662ac791 100644
--- a/libc/src/time/strftime_core/CMakeLists.txt
+++ b/libc/src/time/strftime_core/CMakeLists.txt
@@ -18,10 +18,8 @@ add_header_library(
libc.src.__support.str_to_integer
)
-add_object_library(
+add_header_library(
converter
- SRCS
- converter.cpp
HDRS
converter.h
num_converter.h
@@ -36,10 +34,8 @@ add_object_library(
libc.src.__support.integer_to_string
)
-add_object_library(
+add_header_library(
strftime_main
- SRCS
- strftime_main.cpp
HDRS
strftime_main.h
DEPENDS
diff --git a/libc/src/time/strftime_core/composite_converter.h b/libc/src/time/strftime_core/composite_converter.h
index 3530075cfe9a9..53cb7e536a0e5 100644
--- a/libc/src/time/strftime_core/composite_converter.h
+++ b/libc/src/time/strftime_core/composite_converter.h
@@ -42,7 +42,8 @@ get_specific_int_format(const tm *timeptr, const FormatSection &base_to_conv,
return result;
}
-LIBC_INLINE int convert_date_us(printf_core::Writer *writer,
+template <printf_core::WriteMode write_mode>
+LIBC_INLINE int convert_date_us(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv,
const tm *timeptr) {
// format is %m/%d/%y (month/day/year)
@@ -66,7 +67,8 @@ LIBC_INLINE int convert_date_us(printf_core::Writer *writer,
return WRITE_OK;
}
-LIBC_INLINE int convert_date_iso(printf_core::Writer *writer,
+template <printf_core::WriteMode write_mode>
+LIBC_INLINE int convert_date_iso(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv,
const tm *timeptr) {
// format is "%Y-%m-%d" (year-month-day)
@@ -90,7 +92,8 @@ LIBC_INLINE int convert_date_iso(printf_core::Writer *writer,
return WRITE_OK;
}
-LIBC_INLINE int convert_time_am_pm(printf_core::Writer *writer,
+template <printf_core::WriteMode write_mode>
+LIBC_INLINE int convert_time_am_pm(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv,
const tm *timeptr) {
// format is "%I:%M:%S %p" (hour:minute:second AM/PM)
@@ -119,7 +122,8 @@ LIBC_INLINE int convert_time_am_pm(printf_core::Writer *writer,
return WRITE_OK;
}
-LIBC_INLINE int convert_time_minute(printf_core::Writer *writer,
+template <printf_core::WriteMode write_mode>
+LIBC_INLINE int convert_time_minute(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv,
const tm *timeptr) {
// format is "%H:%M" (hour:minute)
@@ -139,7 +143,8 @@ LIBC_INLINE int convert_time_minute(printf_core::Writer *writer,
return WRITE_OK;
}
-LIBC_INLINE int convert_time_second(printf_core::Writer *writer,
+template <printf_core::WriteMode write_mode>
+LIBC_INLINE int convert_time_second(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv,
const tm *timeptr) {
// format is "%H:%M:%S" (hour:minute:second)
@@ -163,7 +168,8 @@ LIBC_INLINE int convert_time_second(printf_core::Writer *writer,
return WRITE_OK;
}
-LIBC_INLINE int convert_full_date_time(printf_core::Writer *writer,
+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) {
const time_utils::TMReader time_reader(timeptr);
@@ -204,7 +210,8 @@ LIBC_INLINE int convert_full_date_time(printf_core::Writer *writer,
return WRITE_OK;
}
-LIBC_INLINE int convert_composite(printf_core::Writer *writer,
+template <printf_core::WriteMode write_mode>
+LIBC_INLINE int convert_composite(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv,
const tm *timeptr) {
switch (to_conv.conv_name) {
diff --git a/libc/src/time/strftime_core/converter.cpp b/libc/src/time/strftime_core/converter.cpp
deleted file mode 100644
index e9263af3d0e55..0000000000000
--- a/libc/src/time/strftime_core/converter.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-//===-- Format specifier converter implmentation for strftime -------------===//
-//
-// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
-// See htto_conv.times://llvm.org/LICENSE.txt for license information.
-// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
-//
-//===----------------------------------------------------------------------===//
-
-#include "hdr/types/struct_tm.h"
-#include "src/__support/macros/config.h"
-#include "src/stdio/printf_core/writer.h"
-#include "src/time/strftime_core/core_structs.h"
-
-#include "composite_converter.h"
-#include "num_converter.h"
-#include "str_converter.h"
-
-namespace LIBC_NAMESPACE_DECL {
-namespace strftime_core {
-
-int convert(printf_core::Writer *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
- // the default locale.
-
- if (!to_conv.has_conv)
- return writer->write(to_conv.raw_string);
- switch (to_conv.conv_name) {
- // The cases are grouped by type, then alphabetized with lowercase before
- // uppercase.
-
- // raw conversions
- case '%':
- return writer->write("%");
- case 'n':
- return writer->write("\n");
- case 't':
- return writer->write("\t");
-
- // numeric conversions
- case 'C': // Century [00-99]
- case 'd': // Day of the month [01-31]
- case 'e': // Day of the month [1-31]
- case 'g': // last 2 digits of ISO year [00-99]
- case 'G': // ISO year
- case 'H': // 24-hour format [00-23]
- case 'I': // 12-hour format [01-12]
- case 'j': // Day of the year [001-366]
- case 'm': // Month of the year [01-12]
- case 'M': // Minute of the hour [00-59]
- case 's': // Seconds since the epoch
- case 'S': // Second of the minute [00-60]
- case 'u': // ISO day of the week ([1-7] starting Monday)
- case 'U': // Week of the year ([00-53] week 1 starts on first *Sunday*)
- case 'V': // ISO week number ([01-53], 01 is first week majority in this year)
- case 'w': // Day of week ([0-6] starting Sunday)
- case 'W': // Week of the year ([00-53] week 1 starts on first *Monday*)
- case 'y': // Year of the Century [00-99]
- case 'Y': // Full year
- return convert_int(writer, to_conv, timeptr);
-
- // string conversions
- case 'a': // Abbreviated weekday name
- case 'A': // Full weekday name
- case 'b': // Abbreviated month name
- case 'B': // Full month name
- case 'h': // same as %b
- case 'p': // AM/PM designation
- return convert_str(writer, to_conv, timeptr);
-
- // composite conversions
- case 'c': // locale specified date and time
- case 'D': // %m/%d/%y (month/day/year)
- case 'F': // %Y-%m-%d (year-month-day)
- case 'r': // %I:%M:%S %p (hour:minute:second AM/PM)
- case 'R': // %H:%M (hour:minute)
- case 'T': // %H:%M:%S (hour:minute:second)
- case 'x': // locale specified date
- case 'X': // locale specified time
- return convert_composite(writer, to_conv, timeptr);
-
- // timezone conversions
- case 'z': // Timezone offset (+/-hhmm) (num conv)
- case 'Z': // Timezone name (string conv)
- // the standard says if no time zone is determinable, write no characters.
- // Leave this here until time zones are implemented.
- return 0;
- default:
- return writer->write(to_conv.raw_string);
- }
- return 0;
-}
-
-} // namespace strftime_core
-} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/time/strftime_core/converter.h b/libc/src/time/strftime_core/converter.h
index 154ee38d9f05a..ff0faf35bbd90 100644
--- a/libc/src/time/strftime_core/converter.h
+++ b/libc/src/time/strftime_core/converter.h
@@ -10,17 +10,94 @@
#define LLVM_LIBC_SRC_STDIO_STRFTIME_CORE_CONVERTER_H
#include "hdr/types/struct_tm.h"
+#include "src/__support/macros/config.h"
#include "src/stdio/printf_core/writer.h"
#include "src/time/strftime_core/core_structs.h"
+#include "composite_converter.h"
+#include "num_converter.h"
+#include "str_converter.h"
+
namespace LIBC_NAMESPACE_DECL {
namespace strftime_core {
// convert will call a conversion function to convert the FormatSection into
// its string representation, and then that will write the result to the
// writer.
-int convert(printf_core::Writer *writer, const FormatSection &to_conv,
- const tm *timeptr);
+template <printf_core::WriteMode write_mode>
+int convert(printf_core::Writer<write_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
+ // the default locale.
+
+ if (!to_conv.has_conv)
+ return writer->write(to_conv.raw_string);
+ switch (to_conv.conv_name) {
+ // The cases are grouped by type, then alphabetized with lowercase before
+ // uppercase.
+
+ // raw conversions
+ case '%':
+ return writer->write("%");
+ case 'n':
+ return writer->write("\n");
+ case 't':
+ return writer->write("\t");
+
+ // numeric conversions
+ case 'C': // Century [00-99]
+ case 'd': // Day of the month [01-31]
+ case 'e': // Day of the month [1-31]
+ case 'g': // last 2 digits of ISO year [00-99]
+ case 'G': // ISO year
+ case 'H': // 24-hour format [00-23]
+ case 'I': // 12-hour format [01-12]
+ case 'j': // Day of the year [001-366]
+ case 'm': // Month of the year [01-12]
+ case 'M': // Minute of the hour [00-59]
+ case 's': // Seconds since the epoch
+ case 'S': // Second of the minute [00-60]
+ case 'u': // ISO day of the week ([1-7] starting Monday)
+ case 'U': // Week of the year ([00-53] week 1 starts on first *Sunday*)
+ case 'V': // ISO week number ([01-53], 01 is first week majority in this year)
+ case 'w': // Day of week ([0-6] starting Sunday)
+ case 'W': // Week of the year ([00-53] week 1 starts on first *Monday*)
+ case 'y': // Year of the Century [00-99]
+ case 'Y': // Full year
+ return convert_int(writer, to_conv, timeptr);
+
+ // string conversions
+ case 'a': // Abbreviated weekday name
+ case 'A': // Full weekday name
+ case 'b': // Abbreviated month name
+ case 'B': // Full month name
+ case 'h': // same as %b
+ case 'p': // AM/PM designation
+ return convert_str(writer, to_conv, timeptr);
+
+ // composite conversions
+ case 'c': // locale specified date and time
+ case 'D': // %m/%d/%y (month/day/year)
+ case 'F': // %Y-%m-%d (year-month-day)
+ case 'r': // %I:%M:%S %p (hour:minute:second AM/PM)
+ case 'R': // %H:%M (hour:minute)
+ case 'T': // %H:%M:%S (hour:minute:second)
+ case 'x': // locale specified date
+ case 'X': // locale specified time
+ return convert_composite(writer, to_conv, timeptr);
+
+ // timezone conversions
+ case 'z': // Timezone offset (+/-hhmm) (num conv)
+ case 'Z': // Timezone name (string conv)
+ // the standard says if no time zone is determinable, write no characters.
+ // Leave this here until time zones are implemented.
+ return 0;
+ default:
+ return writer->write(to_conv.raw_string);
+ }
+ return 0;
+}
} // namespace strftime_core
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/time/strftime_core/num_converter.h b/libc/src/time/strftime_core/num_converter.h
index aef9ddbf791b2..7da9195aa4885 100644
--- a/libc/src/time/strftime_core/num_converter.h
+++ b/libc/src/time/strftime_core/num_converter.h
@@ -30,7 +30,8 @@ struct IntFormatSection {
char padding_char = '0';
};
-LIBC_INLINE int write_padded_int(printf_core::Writer *writer,
+template <printf_core::WriteMode write_mode>
+LIBC_INLINE int write_padded_int(printf_core::Writer<write_mode> *writer,
const IntFormatSection &num_info) {
DecFmt d(num_info.num);
@@ -187,7 +188,8 @@ LIBC_INLINE IntFormatSection get_int_format(const FormatSection &to_conv,
return result;
}
-LIBC_INLINE int convert_int(printf_core::Writer *writer,
+template <printf_core::WriteMode write_mode>
+LIBC_INLINE int convert_int(printf_core::Writer<write_mode> *writer,
const FormatSection &to_conv, const tm *timeptr) {
return write_padded_int(writer, get_int_format(to_conv, timeptr));
diff --git a/libc/src/time/strftime_core/str_converter.h b/libc/src/time/strftime_core/str_converter.h
index f0d5bf59102f3..13eccd3979ea6 100644
--- a/libc/src/time/strftime_core/str_converter.h
+++ b/libc/src/time/strftime_core/str_converter.h
@@ -27,7 +27,8 @@ unwrap_opt(cpp::optional<cpp::string_view> str_opt) {
return str_opt.has_value() ? *str_opt : OUT_OF_BOUNDS_STR;
}
-LIBC_INLINE int convert_str(printf_core::Writer *writer,
+template <printf_core::WriteMode write_mode>
+LIBC_INLINE int convert_str(printf_core::Writer<write_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.cpp b/libc/src/time/strftime_core/strftime_main.cpp
deleted file mode 100644
index 00839e5a3f4da..0000000000000
--- a/libc/src/time/strftime_core/strftime_main.cpp
+++ /dev/null
@@ -1,40 +0,0 @@
-//===-- Starting point for strftime ---------------------------------------===//
-//
-// 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
-//
-//===----------------------------------------------------------------------===//
-
-#include "src/time/strftime_core/strftime_main.h"
-
-#include "hdr/types/struct_tm.h"
-#include "src/stdio/printf_core/writer.h"
-#include "src/time/strftime_core/converter.h"
-#include "src/time/strftime_core/core_structs.h"
-#include "src/time/strftime_core/parser.h"
-
-namespace LIBC_NAMESPACE_DECL {
-namespace strftime_core {
-
-int strftime_main(printf_core::Writer *writer, const char *__restrict str,
- const tm *timeptr) {
- Parser parser(str);
- int result = 0;
- for (FormatSection cur_section = parser.get_next_section();
- !cur_section.raw_string.empty();
- cur_section = parser.get_next_section()) {
- if (cur_section.has_conv)
- result = convert(writer, cur_section, timeptr);
- else
- result = writer->write(cur_section.raw_string);
-
- if (result < 0)
- return result;
- }
-
- return writer->get_chars_written();
-}
-
-} // namespace strftime_core
-} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/time/strftime_core/strftime_main.h b/libc/src/time/strftime_core/strftime_main.h
index ae706828df44c..c7e590627094a 100644
--- a/libc/src/time/strftime_core/strftime_main.h
+++ b/libc/src/time/strftime_core/strftime_main.h
@@ -12,12 +12,32 @@
#include "hdr/types/struct_tm.h"
#include "src/__support/macros/config.h"
#include "src/stdio/printf_core/writer.h"
+#include "src/time/strftime_core/converter.h"
+#include "src/time/strftime_core/core_structs.h"
+#include "src/time/strftime_core/parser.h"
namespace LIBC_NAMESPACE_DECL {
namespace strftime_core {
-int strftime_main(printf_core::Writer *writer, const char *__restrict str,
- const tm *timeptr);
+template <printf_core::WriteMode write_mode>
+int strftime_main(printf_core::Writer<write_mode> *writer,
+ const char *__restrict str, const tm *timeptr) {
+ Parser parser(str);
+ int result = 0;
+ for (strftime_core::FormatSection cur_section = parser.get_next_section();
+ !cur_section.raw_string.empty();
+ cur_section = parser.get_next_section()) {
+ if (cur_section.has_conv)
+ result = convert(writer, cur_section, timeptr);
+ else
+ result = writer->write(cur_section.raw_string);
+
+ if (result < 0)
+ return result;
+ }
+
+ return writer->get_chars_written();
+}
} // namespace strftime_core
} // namespace LIBC_NAMESPACE_DECL
diff --git a/libc/src/time/strftime_l.cpp b/libc/src/time/strftime_l.cpp
index e19c7c605cd4a..201b85da39ee2 100644
--- a/libc/src/time/strftime_l.cpp
+++ b/libc/src/time/strftime_l.cpp
@@ -22,8 +22,10 @@ LLVM_LIBC_FUNCTION(size_t, strftime_l,
(char *__restrict buffer, size_t buffsz,
const char *__restrict format, const tm *timeptr,
locale_t)) {
- printf_core::WriteBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
- printf_core::Writer writer(&wb);
+ printf_core::WriteBuffer<printf_core::Mode<
+ printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value>
+ wb(buffer, (buffsz > 0 ? buffsz - 1 : 0));
+ printf_core::Writer writer(wb);
int 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/stdio/printf_core/converter_test.cpp b/libc/test/src/stdio/printf_core/converter_test.cpp
index 9da749f3b8ad1..96a00ae598ec2 100644
--- a/libc/test/src/stdio/printf_core/converter_test.cpp
+++ b/libc/test/src/stdio/printf_core/converter_test.cpp
@@ -18,10 +18,14 @@ class LlvmLibcPrintfConverterTest : public LIBC_NAMESPACE::testing::Test {
// void TearDown() override {}
char str[60];
- LIBC_NAMESPACE::printf_core::WriteBuffer wb =
- LIBC_NAMESPACE::printf_core::WriteBuffer(str, sizeof(str) - 1);
- LIBC_NAMESPACE::printf_core::Writer writer =
- LIBC_NAMESPACE::printf_core::Writer(&wb);
+ LIBC_NAMESPACE::printf_core::WriteBuffer<
+ LIBC_NAMESPACE::printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>
+ wb = LIBC_NAMESPACE::printf_core::WriteBuffer<
+ LIBC_NAMESPACE::printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>(
+ str, sizeof(str) - 1);
+ LIBC_NAMESPACE::printf_core::Writer<
+ LIBC_NAMESPACE::printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>
+ writer = LIBC_NAMESPACE::printf_core::Writer(wb);
};
TEST_F(LlvmLibcPrintfConverterTest, SimpleRawConversion) {
diff --git a/libc/test/src/stdio/printf_core/writer_test.cpp b/libc/test/src/stdio/printf_core/writer_test.cpp
index 4fe5ffb4aa989..8611caa2dfa58 100644
--- a/libc/test/src/stdio/printf_core/writer_test.cpp
+++ b/libc/test/src/stdio/printf_core/writer_test.cpp
@@ -15,19 +15,20 @@
using LIBC_NAMESPACE::cpp::string_view;
using LIBC_NAMESPACE::printf_core::WriteBuffer;
+using LIBC_NAMESPACE::printf_core::WriteMode;
using LIBC_NAMESPACE::printf_core::Writer;
TEST(LlvmLibcPrintfWriterTest, Constructor) {
char str[10];
- WriteBuffer wb(str, sizeof(str) - 1);
- Writer writer(&wb);
+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+ Writer writer(wb);
(void)writer;
}
TEST(LlvmLibcPrintfWriterTest, Write) {
char str[4] = {'D', 'E', 'F', 'G'};
- WriteBuffer wb(str, sizeof(str) - 1);
- Writer writer(&wb);
+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+ Writer writer(wb);
writer.write({"abc", 3});
EXPECT_EQ(str[3], 'G');
@@ -42,8 +43,8 @@ TEST(LlvmLibcPrintfWriterTest, Write) {
TEST(LlvmLibcPrintfWriterTest, WriteMultipleTimes) {
char str[10];
- WriteBuffer wb(str, sizeof(str) - 1);
- Writer writer(&wb);
+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+ Writer writer(wb);
writer.write({"abc", 3});
writer.write({"DEF", 3});
writer.write({"1234", 3});
@@ -56,8 +57,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteMultipleTimes) {
TEST(LlvmLibcPrintfWriterTest, WriteChars) {
char str[4] = {'D', 'E', 'F', 'G'};
- WriteBuffer wb(str, sizeof(str) - 1);
- Writer writer(&wb);
+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+ Writer writer(wb);
writer.write('a', 3);
EXPECT_EQ(str[3], 'G');
@@ -69,8 +70,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteChars) {
TEST(LlvmLibcPrintfWriterTest, WriteCharsMultipleTimes) {
char str[10];
- WriteBuffer wb(str, sizeof(str) - 1);
- Writer writer(&wb);
+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+ Writer writer(wb);
writer.write('a', 3);
writer.write('D', 3);
writer.write('1', 3);
@@ -83,8 +84,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsMultipleTimes) {
TEST(LlvmLibcPrintfWriterTest, WriteManyChars) {
char str[100];
- WriteBuffer wb(str, sizeof(str) - 1);
- Writer writer(&wb);
+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+ Writer writer(wb);
writer.write('Z', 99);
wb.buff[wb.buff_cur] = '\0';
@@ -105,8 +106,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteManyChars) {
TEST(LlvmLibcPrintfWriterTest, MixedWrites) {
char str[13];
- WriteBuffer wb(str, sizeof(str) - 1);
- Writer writer(&wb);
+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+ Writer writer(wb);
writer.write('a', 3);
writer.write({"DEF", 3});
writer.write('1', 3);
@@ -120,8 +121,8 @@ TEST(LlvmLibcPrintfWriterTest, MixedWrites) {
TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLength) {
char str[11];
- WriteBuffer wb(str, sizeof(str) - 1);
- Writer writer(&wb);
+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+ Writer writer(wb);
writer.write({"abcDEF123456", 12});
wb.buff[wb.buff_cur] = '\0';
@@ -132,8 +133,8 @@ TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLength) {
TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLength) {
char str[11];
- WriteBuffer wb(str, sizeof(str) - 1);
- Writer writer(&wb);
+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
+ Writer writer(wb);
writer.write('1', 15);
wb.buff[wb.buff_cur] = '\0';
@@ -144,9 +145,9 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLength) {
TEST(LlvmLibcPrintfWriterTest, MixedWriteWithMaxLength) {
char str[11];
- WriteBuffer wb(str, sizeof(str) - 1);
+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1);
- Writer writer(&wb);
+ Writer writer(wb);
writer.write('a', 3);
writer.write({"DEF", 3});
writer.write('1', 3);
@@ -162,9 +163,9 @@ 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.
- WriteBuffer wb(str, 0);
+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, 0);
- Writer writer(&wb);
+ Writer writer(wb);
writer.write('a', 3);
writer.write({"DEF", 3});
writer.write('1', 3);
@@ -177,9 +178,9 @@ TEST(LlvmLibcPrintfWriterTest, StringWithMaxLengthOne) {
}
TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLength) {
- WriteBuffer wb(nullptr, 0);
+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(nullptr, 0);
- Writer writer(&wb);
+ Writer writer(wb);
writer.write('a', 3);
writer.write({"DEF", 3});
writer.write('1', 3);
@@ -213,9 +214,10 @@ TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLengthWithCallback) {
OutBuff out_buff = {str, 0};
char wb_buff[8];
- WriteBuffer wb(wb_buff, sizeof(wb_buff), ©_to_out,
- reinterpret_cast<void *>(&out_buff));
- Writer writer(&wb);
+ WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
+ wb_buff, sizeof(wb_buff), ©_to_out,
+ reinterpret_cast<void *>(&out_buff));
+ Writer writer(wb);
writer.write({"abcDEF123456", 12});
// Flush the buffer
@@ -232,9 +234,10 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLengthWithCallback) {
OutBuff out_buff = {str, 0};
char wb_buff[8];
- WriteBuffer wb(wb_buff, sizeof(wb_buff), ©_to_out,
- reinterpret_cast<void *>(&out_buff));
- Writer writer(&wb);
+ WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
+ wb_buff, sizeof(wb_buff), ©_to_out,
+ reinterpret_cast<void *>(&out_buff));
+ Writer writer(wb);
writer.write('1', 15);
// Flush the buffer
@@ -251,9 +254,10 @@ TEST(LlvmLibcPrintfWriterTest, MixedWriteWithMaxLengthWithCallback) {
OutBuff out_buff = {str, 0};
char wb_buff[8];
- WriteBuffer wb(wb_buff, sizeof(wb_buff), ©_to_out,
- reinterpret_cast<void *>(&out_buff));
- Writer writer(&wb);
+ WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
+ wb_buff, sizeof(wb_buff), ©_to_out,
+ reinterpret_cast<void *>(&out_buff));
+ Writer writer(wb);
writer.write('a', 3);
writer.write({"DEF", 3});
writer.write('1', 3);
@@ -273,9 +277,10 @@ TEST(LlvmLibcPrintfWriterTest, ZeroLengthBufferWithCallback) {
OutBuff out_buff = {str, 0};
char wb_buff[1];
- WriteBuffer wb(wb_buff, 0, ©_to_out, reinterpret_cast<void *>(&out_buff));
+ WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
+ wb_buff, 0, ©_to_out, reinterpret_cast<void *>(&out_buff));
- Writer writer(&wb);
+ Writer writer(wb);
writer.write('a', 3);
writer.write({"DEF", 3});
writer.write('1', 3);
@@ -294,9 +299,10 @@ TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLengthWithCallback) {
OutBuff out_buff = {str, 0};
- WriteBuffer wb(nullptr, 0, ©_to_out, reinterpret_cast<void *>(&out_buff));
+ WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb(
+ nullptr, 0, ©_to_out, reinterpret_cast<void *>(&out_buff));
- Writer writer(&wb);
+ Writer writer(wb);
writer.write('a', 3);
writer.write({"DEF", 3});
writer.write('1', 3);
diff --git a/libc/utils/gpu/server/CMakeLists.txt b/libc/utils/gpu/server/CMakeLists.txt
index ae8a0d902f459..7ca101e42a0af 100644
--- a/libc/utils/gpu/server/CMakeLists.txt
+++ b/libc/utils/gpu/server/CMakeLists.txt
@@ -1,8 +1,4 @@
-add_library(llvmlibc_rpc_server STATIC
- ${LIBC_SOURCE_DIR}/src/stdio/printf_core/writer.cpp
- ${LIBC_SOURCE_DIR}/src/stdio/printf_core/converter.cpp
- rpc_server.cpp
-)
+add_library(llvmlibc_rpc_server STATIC rpc_server.cpp)
# Include the RPC implemenation from libc.
target_include_directories(llvmlibc_rpc_server PRIVATE ${LIBC_SOURCE_DIR})
diff --git a/libc/utils/gpu/server/rpc_server.cpp b/libc/utils/gpu/server/rpc_server.cpp
index 1faee531fa20d..6dc81a5ff805d 100644
--- a/libc/utils/gpu/server/rpc_server.cpp
+++ b/libc/utils/gpu/server/rpc_server.cpp
@@ -96,8 +96,8 @@ static void handle_printf(rpc::Server::Port &port, TempStorage &temp_storage) {
if (!format[lane])
continue;
- WriteBuffer wb(nullptr, 0);
- Writer writer(&wb);
+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(nullptr, 0);
+ Writer writer(wb);
internal::DummyArgList<packed> printf_args;
Parser<internal::DummyArgList<packed> &> parser(
@@ -123,8 +123,8 @@ static void handle_printf(rpc::Server::Port &port, TempStorage &temp_storage) {
if (!format[lane])
continue;
- WriteBuffer wb(nullptr, 0);
- Writer writer(&wb);
+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(nullptr, 0);
+ Writer writer(wb);
internal::StructArgList<packed> printf_args(args[lane], args_sizes[lane]);
Parser<internal::StructArgList<packed>> parser(
@@ -180,8 +180,9 @@ static void handle_printf(rpc::Server::Port &port, TempStorage &temp_storage) {
continue;
char *buffer = temp_storage.alloc(buffer_size[lane]);
- WriteBuffer wb(buffer, buffer_size[lane]);
- Writer writer(&wb);
+ WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(buffer,
+ buffer_size[lane]);
+ Writer writer(wb);
internal::StructArgList<packed> printf_args(args[lane], args_sizes[lane]);
Parser<internal::StructArgList<packed>> parser(
More information about the libc-commits
mailing list