https://github.com/vonosmas created https://github.com/llvm/llvm-project/pull/170959
This change doesn't yet modify Writer::write(string_view) public API, which has a lot of uses through printf_core. For wide-char write functions (like swprintf) we'd need to templatize WriteBuffer and Writer to support wide characrters. We'd need to either support helper cpp::string_view class to support wide-character strings, or replace them with pure pointer/size pairs. The latter option seems to be a more straightforward one, given that majority of printf_core operates on buffers anyway (and only constructs string_views right before invoking the buffer), and we don't have a use case internal wide-character string-views. >From 008397fc8a6aa17b27aecd965c3fc2be433708c0 Mon Sep 17 00:00:00 2001 From: Alexey Samsonov <[email protected]> Date: Fri, 21 Nov 2025 19:45:34 +0000 Subject: [PATCH 1/2] [libc] Refactor static polymorphism in WriteBuffer (NFC). There are three flavors of WriteBuffer currently, all of which could be passed into printf_core::Writer class. It's a tricky class, since it chooses a flavor-specific logic either based on runtime dispatch (to save code size and prevent generating three versions of the entirety of printf_core), or based on template arguments (to avoid dealing with function pointers in codegen for FILL_BUFF_AND_DROP_OVERFLOW path. Refactor this somewhat convoluted logic to have three concrete subclasses inheriting from the templated base class, and use static polymorphism with reinterpret_cast to implement dispatching above. Now we can actually have flavor-specific fields, constructors, and methods (e.g. "flush_to_stream" is now a method of FlushingBuffer), and the code on the user side is cleaner: the complexity of enabling/disabling runtime-dispatch and using proper template arguments is now localized in writer.h. This code will need to be further templatized to support buffers of type wchar_t to implement swprintf() and friends. This change would make it (ever so slightly) easier. --- libc/src/__support/RPC/rpc_server.h | 12 +- libc/src/stdio/baremetal/printf.cpp | 8 +- libc/src/stdio/baremetal/vprintf.cpp | 8 +- .../stdio/printf_core/vasprintf_internal.h | 10 +- .../src/stdio/printf_core/vfprintf_internal.h | 6 +- libc/src/stdio/printf_core/writer.h | 154 +++++++++++------- libc/src/stdio/snprintf.cpp | 4 +- libc/src/stdio/sprintf.cpp | 5 +- libc/src/stdio/vsnprintf.cpp | 4 +- libc/src/stdio/vsprintf.cpp | 5 +- libc/src/stdlib/strfromd.cpp | 4 +- libc/src/stdlib/strfromf.cpp | 4 +- libc/src/stdlib/strfroml.cpp | 4 +- libc/src/time/strftime.cpp | 4 +- libc/src/time/strftime_l.cpp | 4 +- .../src/stdio/printf_core/converter_test.cpp | 16 +- .../src/stdio/printf_core/writer_test.cpp | 64 ++++---- 17 files changed, 162 insertions(+), 154 deletions(-) diff --git a/libc/src/__support/RPC/rpc_server.h b/libc/src/__support/RPC/rpc_server.h index abd604ae48146..98dc40ecd0a89 100644 --- a/libc/src/__support/RPC/rpc_server.h +++ b/libc/src/__support/RPC/rpc_server.h @@ -169,9 +169,7 @@ LIBC_INLINE static void handle_printf(rpc::Server::Port &port, if (!format[lane]) continue; - printf_core::WriteBuffer< - printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> - wb(nullptr, 0); + printf_core::DropOverflowBuffer wb(nullptr, 0); printf_core::Writer writer(wb); internal::DummyArgList<packed> printf_args; @@ -198,9 +196,7 @@ LIBC_INLINE static void handle_printf(rpc::Server::Port &port, if (!format[lane]) continue; - printf_core::WriteBuffer< - printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> - wb(nullptr, 0); + printf_core::DropOverflowBuffer wb(nullptr, 0); printf_core::Writer writer(wb); internal::StructArgList<packed> printf_args(args[lane], args_sizes[lane]); @@ -262,9 +258,7 @@ LIBC_INLINE static void handle_printf(rpc::Server::Port &port, continue; char *buffer = temp_storage.alloc(buffer_size[lane]); - printf_core::WriteBuffer< - printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> - wb(buffer, buffer_size[lane]); + printf_core::DropOverflowBuffer wb(buffer, buffer_size[lane]); printf_core::Writer writer(wb); internal::StructArgList<packed> printf_args(args[lane], args_sizes[lane]); diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp index 5a9b19ff20471..2fa9cf7c9f3cd 100644 --- a/libc/src/stdio/baremetal/printf.cpp +++ b/libc/src/stdio/baremetal/printf.cpp @@ -41,9 +41,9 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) { static constexpr size_t BUFF_SIZE = 1024; char buffer[BUFF_SIZE]; - printf_core::WriteBuffer<printf_core::WriteMode::FLUSH_TO_STREAM> wb( - buffer, BUFF_SIZE, &stdout_write_hook, nullptr); - printf_core::Writer<printf_core::WriteMode::FLUSH_TO_STREAM> writer(wb); + printf_core::FlushingBuffer wb(buffer, BUFF_SIZE, &stdout_write_hook, + nullptr); + printf_core::Writer writer(wb); auto retval = printf_core::printf_main(&writer, format, args); if (!retval.has_value()) { @@ -51,7 +51,7 @@ LLVM_LIBC_FUNCTION(int, printf, (const char *__restrict format, ...)) { return -1; } - int flushval = wb.overflow_write(""); + int flushval = wb.flush_to_stream(); if (flushval != printf_core::WRITE_OK) { libc_errno = printf_core::internal_error_to_errno(-flushval); return -1; diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp index c172b368d15f3..d89f26cd72b0a 100644 --- a/libc/src/stdio/baremetal/vprintf.cpp +++ b/libc/src/stdio/baremetal/vprintf.cpp @@ -39,9 +39,9 @@ LLVM_LIBC_FUNCTION(int, vprintf, static constexpr size_t BUFF_SIZE = 1024; char buffer[BUFF_SIZE]; - printf_core::WriteBuffer<printf_core::WriteMode::FLUSH_TO_STREAM> wb( - buffer, BUFF_SIZE, &stdout_write_hook, nullptr); - printf_core::Writer<printf_core::WriteMode::FLUSH_TO_STREAM> writer(wb); + printf_core::FlushingBuffer wb(buffer, BUFF_SIZE, &stdout_write_hook, + nullptr); + printf_core::Writer writer(wb); auto retval = printf_core::printf_main(&writer, format, args); if (!retval.has_value()) { @@ -49,7 +49,7 @@ LLVM_LIBC_FUNCTION(int, vprintf, return -1; } - int flushval = wb.overflow_write(""); + int flushval = wb.flush_to_stream(); if (flushval != printf_core::WRITE_OK) { libc_errno = printf_core::internal_error_to_errno(-flushval); return -1; diff --git a/libc/src/stdio/printf_core/vasprintf_internal.h b/libc/src/stdio/printf_core/vasprintf_internal.h index 41df17b67f35b..db6b95d49aaca 100644 --- a/libc/src/stdio/printf_core/vasprintf_internal.h +++ b/libc/src/stdio/printf_core/vasprintf_internal.h @@ -18,10 +18,8 @@ namespace LIBC_NAMESPACE_DECL { namespace printf_core { -LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str, void *target) { - WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> *wb = - reinterpret_cast< - WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> *>(target); +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 *>( @@ -47,8 +45,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::WriteBuffer<Mode<WriteMode::RESIZE_AND_FILL_BUFF>::value> wb( - init_buff_on_stack, DEFAULT_BUFFER_SIZE, resize_overflow_hook); + printf_core::ResizingBuffer 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); diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h index c47a03d741f98..321b0693ad339 100644 --- a/libc/src/stdio/printf_core/vfprintf_internal.h +++ b/libc/src/stdio/printf_core/vfprintf_internal.h @@ -86,8 +86,8 @@ LIBC_INLINE ErrorOr<size_t> vfprintf_internal(::FILE *__restrict stream, internal::ArgList &args) { constexpr size_t BUFF_SIZE = 1024; char buffer[BUFF_SIZE]; - printf_core::WriteBuffer<Mode<WriteMode::FLUSH_TO_STREAM>::value> wb( - buffer, BUFF_SIZE, &file_write_hook, reinterpret_cast<void *>(stream)); + printf_core::FlushingBuffer wb(buffer, BUFF_SIZE, &file_write_hook, + reinterpret_cast<void *>(stream)); Writer writer(wb); internal::flockfile(stream); auto retval = printf_main(&writer, format, args); @@ -95,7 +95,7 @@ LIBC_INLINE ErrorOr<size_t> vfprintf_internal(::FILE *__restrict stream, internal::funlockfile(stream); return retval; } - int flushval = wb.overflow_write(""); + int flushval = wb.flush_to_stream(); if (flushval != WRITE_OK) retval = Error(-flushval); internal::funlockfile(stream); diff --git a/libc/src/stdio/printf_core/writer.h b/libc/src/stdio/printf_core/writer.h index 9de108ece510f..cb45b105597d1 100644 --- a/libc/src/stdio/printf_core/writer.h +++ b/libc/src/stdio/printf_core/writer.h @@ -38,36 +38,65 @@ template <WriteMode write_mode> struct Mode { #endif }; +template <WriteMode write_mode> class Writer; + template <WriteMode write_mode> struct WriteBuffer { - using StreamWriter = int (*)(cpp::string_view, void *); char *buff; - const char *init_buff; // for checking when resize. size_t buff_len; size_t buff_cur = 0; - - // The stream writer will be called when the buffer is full. It will be passed - // string_views to write to the stream. - const StreamWriter stream_writer; - void *output_target; - // 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) {} +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; + } +}; - 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) {} +// 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 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 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); @@ -83,48 +112,61 @@ template <WriteMode write_mode> struct WriteBuffer { return WRITE_OK; } - 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; - } + LIBC_INLINE int flush_to_stream() { return flush_to_stream({}); } +}; - LIBC_INLINE int resize_and_write(cpp::string_view new_str) { - return stream_writer(new_str, output_target); - } +// 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. - // The overflow_write method is intended to be called to write the contents of - // the buffer and new_str to the stream_writer if it exists. If a resizing - // hook is provided, it will resize the buffer and write the contents. If - // neither a stream_writer nor a resizing hook is provided, it will fill the - // remaining space in the buffer with new_str and drop the overflow. Calling - // this with an empty string will flush the buffer if relevant. - - LIBC_INLINE int overflow_write(cpp::string_view new_str) { - if constexpr (write_mode == WriteMode::RUNTIME_DISPATCH) { - if (write_mode_ == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) - return fill_remaining_to_buff(new_str); - else if (write_mode_ == WriteMode::FLUSH_TO_STREAM) - return flush_to_stream(new_str); - 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); - } - __builtin_unreachable(); + 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 <> +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 <> +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); +} + +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); +} + +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); +} + template <WriteMode write_mode> class Writer final { WriteBuffer<write_mode> &wb; size_t chars_written = 0; diff --git a/libc/src/stdio/snprintf.cpp b/libc/src/stdio/snprintf.cpp index d95195f6f485f..8364e8d59b278 100644 --- a/libc/src/stdio/snprintf.cpp +++ b/libc/src/stdio/snprintf.cpp @@ -31,9 +31,7 @@ LLVM_LIBC_FUNCTION(int, snprintf, // and pointer semantics, as well as handling // destruction automatically. va_end(vlist); - printf_core::WriteBuffer<printf_core::Mode< - printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value> - wb(buffer, (buffsz > 0 ? buffsz - 1 : 0)); + printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0)); printf_core::Writer writer(wb); auto ret_val = printf_core::printf_main(&writer, format, args); diff --git a/libc/src/stdio/sprintf.cpp b/libc/src/stdio/sprintf.cpp index 2a9b6ea7c5e50..d340096bb6d2b 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::WriteBuffer< - printf_core::Mode<printf_core::WriteMode::RESIZE_AND_FILL_BUFF>::value> - wb(buffer, cpp::numeric_limits<size_t>::max()); + printf_core::DropOverflowBuffer wb(buffer, + cpp::numeric_limits<size_t>::max()); printf_core::Writer writer(wb); auto ret_val = printf_core::printf_main(&writer, format, args); diff --git a/libc/src/stdio/vsnprintf.cpp b/libc/src/stdio/vsnprintf.cpp index 5d936360c0857..b65343dfefc75 100644 --- a/libc/src/stdio/vsnprintf.cpp +++ b/libc/src/stdio/vsnprintf.cpp @@ -28,9 +28,7 @@ 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<printf_core::Mode< - printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value> - wb(buffer, (buffsz > 0 ? buffsz - 1 : 0)); + printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0)); printf_core::Writer writer(wb); auto ret_val = printf_core::printf_main(&writer, format, args); diff --git a/libc/src/stdio/vsprintf.cpp b/libc/src/stdio/vsprintf.cpp index f9cf8118534f6..8affb88d2b807 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::WriteBuffer<printf_core::Mode< - printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value> - wb(buffer, cpp::numeric_limits<size_t>::max()); + printf_core::DropOverflowBuffer wb(buffer, + cpp::numeric_limits<size_t>::max()); printf_core::Writer writer(wb); auto ret_val = printf_core::printf_main(&writer, format, args); diff --git a/libc/src/stdlib/strfromd.cpp b/libc/src/stdlib/strfromd.cpp index 71e257f08645b..5409a57c722d4 100644 --- a/libc/src/stdlib/strfromd.cpp +++ b/libc/src/stdlib/strfromd.cpp @@ -22,9 +22,7 @@ LLVM_LIBC_FUNCTION(int, strfromd, printf_core::FormatSection section = internal::parse_format_string(format, fp); - printf_core::WriteBuffer<printf_core::Mode< - printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value> - wb(s, (n > 0 ? n - 1 : 0)); + printf_core::DropOverflowBuffer wb(s, (n > 0 ? n - 1 : 0)); printf_core::Writer writer(wb); int result = 0; diff --git a/libc/src/stdlib/strfromf.cpp b/libc/src/stdlib/strfromf.cpp index 65f242b200f18..e68e50210dd4d 100644 --- a/libc/src/stdlib/strfromf.cpp +++ b/libc/src/stdlib/strfromf.cpp @@ -22,9 +22,7 @@ LLVM_LIBC_FUNCTION(int, strfromf, printf_core::FormatSection section = internal::parse_format_string(format, fp); - printf_core::WriteBuffer<printf_core::Mode< - printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value> - wb(s, (n > 0 ? n - 1 : 0)); + printf_core::DropOverflowBuffer wb(s, (n > 0 ? n - 1 : 0)); printf_core::Writer writer(wb); int result = 0; diff --git a/libc/src/stdlib/strfroml.cpp b/libc/src/stdlib/strfroml.cpp index 31668a0323c93..4be21677f7f13 100644 --- a/libc/src/stdlib/strfroml.cpp +++ b/libc/src/stdlib/strfroml.cpp @@ -27,9 +27,7 @@ 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<printf_core::Mode< - printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value> - wb(s, (n > 0 ? n - 1 : 0)); + printf_core::DropOverflowBuffer wb(s, (n > 0 ? n - 1 : 0)); printf_core::Writer writer(wb); int result = 0; diff --git a/libc/src/time/strftime.cpp b/libc/src/time/strftime.cpp index ff8c05a0b07da..af0f438d22cab 100644 --- a/libc/src/time/strftime.cpp +++ b/libc/src/time/strftime.cpp @@ -19,9 +19,7 @@ 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<printf_core::Mode< - printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value> - wb(buffer, (buffsz > 0 ? buffsz - 1 : 0)); + printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0)); printf_core::Writer writer(wb); auto ret = strftime_core::strftime_main(&writer, format, timeptr); if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer. diff --git a/libc/src/time/strftime_l.cpp b/libc/src/time/strftime_l.cpp index 2ec90634ea347..7a70d2c5418ee 100644 --- a/libc/src/time/strftime_l.cpp +++ b/libc/src/time/strftime_l.cpp @@ -22,9 +22,7 @@ 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<printf_core::Mode< - printf_core::WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::value> - wb(buffer, (buffsz > 0 ? buffsz - 1 : 0)); + printf_core::DropOverflowBuffer wb(buffer, (buffsz > 0 ? buffsz - 1 : 0)); printf_core::Writer writer(wb); auto ret = strftime_core::strftime_main(&writer, format, timeptr); if (buffsz > 0) // if the buffsz is 0 the buffer may be a null pointer. diff --git a/libc/test/src/stdio/printf_core/converter_test.cpp b/libc/test/src/stdio/printf_core/converter_test.cpp index 2dae2a22c864c..68e2fdac89d47 100644 --- a/libc/test/src/stdio/printf_core/converter_test.cpp +++ b/libc/test/src/stdio/printf_core/converter_test.cpp @@ -14,18 +14,14 @@ class LlvmLibcPrintfConverterTest : public LIBC_NAMESPACE::testing::Test { protected: - // void SetUp() override {} - // void TearDown() override {} + LlvmLibcPrintfConverterTest() : wb(str, sizeof(str) - 1), writer(wb) {} char str[60]; - 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); + 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> + writer; }; 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 d263cf55aa474..e0128505b2766 100644 --- a/libc/test/src/stdio/printf_core/writer_test.cpp +++ b/libc/test/src/stdio/printf_core/writer_test.cpp @@ -15,20 +15,21 @@ namespace { using LIBC_NAMESPACE::cpp::string_view; -using LIBC_NAMESPACE::printf_core::WriteBuffer; +using LIBC_NAMESPACE::printf_core::DropOverflowBuffer; +using LIBC_NAMESPACE::printf_core::FlushingBuffer; using LIBC_NAMESPACE::printf_core::WriteMode; using LIBC_NAMESPACE::printf_core::Writer; TEST(LlvmLibcPrintfWriterTest, Constructor) { char str[10]; - WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1); + DropOverflowBuffer wb(str, sizeof(str) - 1); Writer writer(wb); (void)writer; } TEST(LlvmLibcPrintfWriterTest, Write) { char str[4] = {'D', 'E', 'F', 'G'}; - WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1); + DropOverflowBuffer wb(str, sizeof(str) - 1); Writer writer(wb); writer.write({"abc", 3}); @@ -44,7 +45,7 @@ TEST(LlvmLibcPrintfWriterTest, Write) { TEST(LlvmLibcPrintfWriterTest, WriteMultipleTimes) { char str[10]; - WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1); + DropOverflowBuffer wb(str, sizeof(str) - 1); Writer writer(wb); writer.write({"abc", 3}); writer.write({"DEF", 3}); @@ -58,7 +59,7 @@ TEST(LlvmLibcPrintfWriterTest, WriteMultipleTimes) { TEST(LlvmLibcPrintfWriterTest, WriteChars) { char str[4] = {'D', 'E', 'F', 'G'}; - WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1); + DropOverflowBuffer wb(str, sizeof(str) - 1); Writer writer(wb); writer.write('a', 3); @@ -71,7 +72,7 @@ TEST(LlvmLibcPrintfWriterTest, WriteChars) { TEST(LlvmLibcPrintfWriterTest, WriteCharsMultipleTimes) { char str[10]; - WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1); + DropOverflowBuffer wb(str, sizeof(str) - 1); Writer writer(wb); writer.write('a', 3); writer.write('D', 3); @@ -85,7 +86,7 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsMultipleTimes) { TEST(LlvmLibcPrintfWriterTest, WriteManyChars) { char str[100]; - WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1); + DropOverflowBuffer wb(str, sizeof(str) - 1); Writer writer(wb); writer.write('Z', 99); @@ -107,7 +108,7 @@ TEST(LlvmLibcPrintfWriterTest, WriteManyChars) { TEST(LlvmLibcPrintfWriterTest, MixedWrites) { char str[13]; - WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1); + DropOverflowBuffer wb(str, sizeof(str) - 1); Writer writer(wb); writer.write('a', 3); writer.write({"DEF", 3}); @@ -122,7 +123,7 @@ TEST(LlvmLibcPrintfWriterTest, MixedWrites) { TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLength) { char str[11]; - WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1); + DropOverflowBuffer wb(str, sizeof(str) - 1); Writer writer(wb); writer.write({"abcDEF123456", 12}); @@ -134,7 +135,7 @@ TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLength) { TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLength) { char str[11]; - WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1); + DropOverflowBuffer wb(str, sizeof(str) - 1); Writer writer(wb); writer.write('1', 15); @@ -146,7 +147,7 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLength) { TEST(LlvmLibcPrintfWriterTest, MixedWriteWithMaxLength) { char str[11]; - WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, sizeof(str) - 1); + DropOverflowBuffer wb(str, sizeof(str) - 1); Writer writer(wb); writer.write('a', 3); @@ -164,7 +165,7 @@ 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<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(str, 0); + DropOverflowBuffer wb(str, 0); Writer writer(wb); writer.write('a', 3); @@ -179,7 +180,7 @@ TEST(LlvmLibcPrintfWriterTest, StringWithMaxLengthOne) { } TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLength) { - WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW> wb(nullptr, 0); + DropOverflowBuffer wb(nullptr, 0); Writer writer(wb); writer.write('a', 3); @@ -215,14 +216,12 @@ TEST(LlvmLibcPrintfWriterTest, WriteWithMaxLengthWithCallback) { OutBuff out_buff = {str, 0}; char wb_buff[8]; - WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb( - wb_buff, sizeof(wb_buff), ©_to_out, - reinterpret_cast<void *>(&out_buff)); + FlushingBuffer wb(wb_buff, sizeof(wb_buff), ©_to_out, + reinterpret_cast<void *>(&out_buff)); Writer writer(wb); writer.write({"abcDEF123456", 12}); - // Flush the buffer - wb.overflow_write(""); + wb.flush_to_stream(); str[out_buff.cur_pos] = '\0'; ASSERT_STREQ("abcDEF123456", str); @@ -235,14 +234,12 @@ TEST(LlvmLibcPrintfWriterTest, WriteCharsWithMaxLengthWithCallback) { OutBuff out_buff = {str, 0}; char wb_buff[8]; - WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb( - wb_buff, sizeof(wb_buff), ©_to_out, - reinterpret_cast<void *>(&out_buff)); + FlushingBuffer wb(wb_buff, sizeof(wb_buff), ©_to_out, + reinterpret_cast<void *>(&out_buff)); Writer writer(wb); writer.write('1', 15); - // Flush the buffer - wb.overflow_write(""); + wb.flush_to_stream(); str[out_buff.cur_pos] = '\0'; ASSERT_STREQ("111111111111111", str); @@ -255,17 +252,15 @@ TEST(LlvmLibcPrintfWriterTest, MixedWriteWithMaxLengthWithCallback) { OutBuff out_buff = {str, 0}; char wb_buff[8]; - WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb( - wb_buff, sizeof(wb_buff), ©_to_out, - reinterpret_cast<void *>(&out_buff)); + FlushingBuffer 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); writer.write({"456", 3}); - // Flush the buffer - wb.overflow_write(""); + wb.flush_to_stream(); str[out_buff.cur_pos] = '\0'; ASSERT_STREQ("aaaDEF111456", str); @@ -278,8 +273,8 @@ TEST(LlvmLibcPrintfWriterTest, ZeroLengthBufferWithCallback) { OutBuff out_buff = {str, 0}; char wb_buff[1]; - WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb( - wb_buff, 0, ©_to_out, reinterpret_cast<void *>(&out_buff)); + FlushingBuffer wb(wb_buff, 0, ©_to_out, + reinterpret_cast<void *>(&out_buff)); Writer writer(wb); writer.write('a', 3); @@ -287,8 +282,7 @@ TEST(LlvmLibcPrintfWriterTest, ZeroLengthBufferWithCallback) { writer.write('1', 3); writer.write({"456", 3}); - // Flush the buffer - wb.overflow_write(""); + wb.flush_to_stream(); str[out_buff.cur_pos] = '\0'; ASSERT_STREQ("aaaDEF111456", str); @@ -300,8 +294,8 @@ TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLengthWithCallback) { OutBuff out_buff = {str, 0}; - WriteBuffer<WriteMode::FLUSH_TO_STREAM> wb( - nullptr, 0, ©_to_out, reinterpret_cast<void *>(&out_buff)); + FlushingBuffer wb(nullptr, 0, ©_to_out, + reinterpret_cast<void *>(&out_buff)); Writer writer(wb); writer.write('a', 3); @@ -309,7 +303,7 @@ TEST(LlvmLibcPrintfWriterTest, NullStringWithZeroMaxLengthWithCallback) { writer.write('1', 3); writer.write({"456", 3}); - wb.overflow_write(""); + wb.flush_to_stream(); str[out_buff.cur_pos] = '\0'; ASSERT_EQ(writer.get_chars_written(), size_t{12}); >From 43b690df58735c2b0284d946937cba8b512133c8 Mon Sep 17 00:00:00 2001 From: Alexey Samsonov <[email protected]> Date: Sat, 6 Dec 2025 00:18:16 +0000 Subject: [PATCH 2/2] [libc][printf] De-string-viewify writer internals. This change doesn't yet modify Writer::write(string_view) public API, which has a lot of uses through printf_core. For wide-char write functions (like swprintf) we'd need to templatize WriteBuffer and Writer to support wide characrters. We'd need to either support helper cpp::string_view class to support wide-character strings, or replace them with pure pointer/size pairs. The latter option seems to be a more straightforward one, given that majority of printf_core operates on buffers anyway (and only constructs string_views right before invoking the buffer), and we don't have a use case internal wide-character string-views. --- libc/src/stdio/baremetal/printf.cpp | 5 +- libc/src/stdio/baremetal/vprintf.cpp | 5 +- .../stdio/printf_core/vasprintf_internal.h | 6 +- .../src/stdio/printf_core/vfprintf_internal.h | 9 +- libc/src/stdio/printf_core/writer.h | 105 ++++++++++-------- .../src/stdio/printf_core/writer_test.cpp | 12 +- 6 files changed, 75 insertions(+), 67 deletions(-) diff --git a/libc/src/stdio/baremetal/printf.cpp b/libc/src/stdio/baremetal/printf.cpp index 2fa9cf7c9f3cd..3329d4fecdad5 100644 --- a/libc/src/stdio/baremetal/printf.cpp +++ b/libc/src/stdio/baremetal/printf.cpp @@ -24,8 +24,9 @@ namespace LIBC_NAMESPACE_DECL { namespace { -LIBC_INLINE int stdout_write_hook(cpp::string_view new_str, void *) { - write_to_stdout(new_str); +LIBC_INLINE int stdout_write_hook(const char *new_str, size_t new_str_len, + void *) { + write_to_stdout({new_str, new_str_len}); return printf_core::WRITE_OK; } diff --git a/libc/src/stdio/baremetal/vprintf.cpp b/libc/src/stdio/baremetal/vprintf.cpp index d89f26cd72b0a..30faa9ad54b8b 100644 --- a/libc/src/stdio/baremetal/vprintf.cpp +++ b/libc/src/stdio/baremetal/vprintf.cpp @@ -24,8 +24,9 @@ namespace LIBC_NAMESPACE_DECL { namespace { -LIBC_INLINE int stdout_write_hook(cpp::string_view new_str, void *) { - write_to_stdout(new_str); +LIBC_INLINE int stdout_write_hook(const char *new_str, size_t new_str_len, + void *) { + write_to_stdout({new_str, new_str_len}); return printf_core::WRITE_OK; } diff --git a/libc/src/stdio/printf_core/vasprintf_internal.h b/libc/src/stdio/printf_core/vasprintf_internal.h index db6b95d49aaca..a36e9ca749305 100644 --- a/libc/src/stdio/printf_core/vasprintf_internal.h +++ b/libc/src/stdio/printf_core/vasprintf_internal.h @@ -18,9 +18,9 @@ namespace LIBC_NAMESPACE_DECL { namespace printf_core { -LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str, +LIBC_INLINE int resize_overflow_hook(const char *new_str, size_t new_str_len, ResizingBuffer *wb) { - size_t new_size = new_str.size() + wb->buff_cur; + size_t new_size = new_str_len + wb->buff_cur; const bool isBuffOnStack = (wb->buff == wb->init_buff); char *new_buff = static_cast<char *>( isBuffOnStack ? malloc(new_size + 1) @@ -33,7 +33,7 @@ LIBC_INLINE int resize_overflow_hook(cpp::string_view new_str, 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()); + inline_memcpy(wb->buff + wb->buff_cur, new_str, new_str_len); wb->buff_cur = new_size; wb->buff_len = new_size; return printf_core::WRITE_OK; diff --git a/libc/src/stdio/printf_core/vfprintf_internal.h b/libc/src/stdio/printf_core/vfprintf_internal.h index 321b0693ad339..b2b282fbf7b78 100644 --- a/libc/src/stdio/printf_core/vfprintf_internal.h +++ b/libc/src/stdio/printf_core/vfprintf_internal.h @@ -62,19 +62,20 @@ LIBC_INLINE FileIOResult fwrite_unlocked(const void *ptr, size_t size, namespace printf_core { -LIBC_INLINE int file_write_hook(cpp::string_view new_str, void *fp) { +LIBC_INLINE int file_write_hook(const char *new_str, size_t new_str_len, + 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); + auto write_result = internal::fwrite_unlocked(new_str, sizeof(char), + new_str_len, 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() || + if (write_result.value != new_str_len || internal::ferror_unlocked(target_file)) return FILE_WRITE_ERROR; diff --git a/libc/src/stdio/printf_core/writer.h b/libc/src/stdio/printf_core/writer.h index cb45b105597d1..1eeffee2f5dde 100644 --- a/libc/src/stdio/printf_core/writer.h +++ b/libc/src/stdio/printf_core/writer.h @@ -41,7 +41,8 @@ template <WriteMode write_mode> struct Mode { template <WriteMode write_mode> class Writer; template <WriteMode write_mode> struct WriteBuffer { - char *buff; + using CharType = char; + CharType *buff; size_t buff_len; size_t buff_cur = 0; // The current writing mode in case the user wants runtime dispatch of the @@ -49,7 +50,7 @@ template <WriteMode write_mode> struct WriteBuffer { [[maybe_unused]] WriteMode write_mode_; protected: - LIBC_INLINE WriteBuffer(char *buff, size_t buff_len, WriteMode mode) + LIBC_INLINE WriteBuffer(CharType *buff, size_t buff_len, WriteMode mode) : buff(buff), buff_len(buff_len), write_mode_(mode) {} private: @@ -57,24 +58,26 @@ template <WriteMode write_mode> struct WriteBuffer { // 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); + LIBC_INLINE int overflow_write(const CharType *new_str, size_t new_str_len); }; // 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) + LIBC_INLINE DropOverflowBuffer(CharType *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) { + LIBC_INLINE int fill_remaining_to_buff(const CharType *new_str, + size_t new_str_len) { 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(); + size_t chars_to_write = buff_len - buff_cur; + if (chars_to_write > new_str_len) { + chars_to_write = new_str_len; } - inline_memcpy(buff + buff_cur, new_str.data(), bytes_to_write); - buff_cur += bytes_to_write; + inline_memcpy(buff + buff_cur, new_str, + chars_to_write * sizeof(CharType)); + buff_cur += chars_to_write; } return WRITE_OK; } @@ -84,27 +87,27 @@ struct DropOverflowBuffer 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 *); + // buffers to write, and the target provided at construction time. + using StreamWriter = int (*)(const CharType *, size_t, void *); const StreamWriter stream_writer; void *output_target; - LIBC_INLINE FlushingBuffer(char *buff, size_t buff_len, StreamWriter hook, + LIBC_INLINE FlushingBuffer(CharType *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) { + // Flushes the entire current buffer to stream, followed by the new_str + // buffer. + LIBC_INLINE int flush_to_stream(const CharType *new_str, size_t new_str_len) { if (buff_cur > 0) { - int retval = stream_writer({buff, buff_cur}, output_target); + 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 (new_str_len > 0) { + int retval = stream_writer(new_str, new_str_len, output_target); if (retval < 0) return retval; } @@ -112,66 +115,73 @@ struct FlushingBuffer return WRITE_OK; } - LIBC_INLINE int flush_to_stream() { return flush_to_stream({}); } + LIBC_INLINE int flush_to_stream() { return flush_to_stream(nullptr, 0); } }; // 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 *); + using ResizeWriter = int (*)(const CharType *, size_t, ResizingBuffer *); const ResizeWriter resize_writer; - const char *init_buff; // for checking when resize. + const CharType *init_buff; // for checking when resize. - LIBC_INLINE ResizingBuffer(char *buff, size_t buff_len, ResizeWriter hook) + LIBC_INLINE ResizingBuffer(CharType *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); + LIBC_INLINE int resize_and_write(const CharType *new_str, + size_t new_str_len) { + return resize_writer(new_str, new_str_len, this); } }; template <> LIBC_INLINE int WriteBuffer<WriteMode::RUNTIME_DISPATCH>::overflow_write( - cpp::string_view new_str) { + const CharType *new_str, size_t new_str_len) { if (write_mode_ == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) return reinterpret_cast<DropOverflowBuffer *>(this)->fill_remaining_to_buff( - new_str); + new_str, new_str_len); else if (write_mode_ == WriteMode::FLUSH_TO_STREAM) - return reinterpret_cast<FlushingBuffer *>(this)->flush_to_stream(new_str); + return reinterpret_cast<FlushingBuffer *>(this)->flush_to_stream( + new_str, new_str_len); else if (write_mode_ == WriteMode::RESIZE_AND_FILL_BUFF) - return reinterpret_cast<ResizingBuffer *>(this)->resize_and_write(new_str); + return reinterpret_cast<ResizingBuffer *>(this)->resize_and_write( + new_str, new_str_len); __builtin_unreachable(); } template <> LIBC_INLINE int WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>::overflow_write( - cpp::string_view new_str) { + const CharType *new_str, size_t new_str_len) { return reinterpret_cast<DropOverflowBuffer *>(this)->fill_remaining_to_buff( - new_str); + new_str, new_str_len); } 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); +LIBC_INLINE int +WriteBuffer<WriteMode::FLUSH_TO_STREAM>::overflow_write(const CharType *new_str, + size_t new_str_len) { + return reinterpret_cast<FlushingBuffer *>(this)->flush_to_stream(new_str, + new_str_len); } 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); + const CharType *new_str, size_t new_str_len) { + return reinterpret_cast<ResizingBuffer *>(this)->resize_and_write( + new_str, new_str_len); } template <WriteMode write_mode> class Writer final { + using CharType = char; WriteBuffer<write_mode> &wb; size_t chars_written = 0; - LIBC_INLINE int pad(char new_char, size_t length) { + LIBC_INLINE int pad(CharType 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; @@ -184,17 +194,15 @@ template <WriteMode write_mode> class Writer final { // Next, overflow write the rest of length using the mini_buff. constexpr size_t MINI_BUFF_SIZE = 64; - char mini_buff[MINI_BUFF_SIZE]; + CharType 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); + int result = wb.overflow_write(mini_buff, MINI_BUFF_SIZE); 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); + return wb.overflow_write(mini_buff, length - written); } public: @@ -202,6 +210,7 @@ template <WriteMode write_mode> class Writer final { // Takes a string, copies it into the buffer if there is space, else passes it // to the overflow mechanism to be handled separately. + // TODO: Migrate all callers away from string_view to CharType*/size_t pair. LIBC_INLINE int write(cpp::string_view new_string) { chars_written += new_string.size(); if (LIBC_LIKELY(wb.buff_cur + new_string.size() <= wb.buff_len)) { @@ -210,18 +219,17 @@ template <WriteMode write_mode> class Writer final { wb.buff_cur += new_string.size(); return WRITE_OK; } - return wb.overflow_write(new_string); + return wb.overflow_write(new_string.data(), new_string.size()); } // 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(CharType 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); + inline_memset(wb.buff + wb.buff_cur, new_char, length); wb.buff_cur += length; return WRITE_OK; } @@ -230,15 +238,14 @@ 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(CharType 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 wb.overflow_write(&new_char, 1); } LIBC_INLINE size_t get_chars_written() { return chars_written; } diff --git a/libc/test/src/stdio/printf_core/writer_test.cpp b/libc/test/src/stdio/printf_core/writer_test.cpp index e0128505b2766..8f14e38aab922 100644 --- a/libc/test/src/stdio/printf_core/writer_test.cpp +++ b/libc/test/src/stdio/printf_core/writer_test.cpp @@ -8,13 +8,11 @@ #include "src/stdio/printf_core/writer.h" -#include "src/__support/CPP/string_view.h" #include "src/string/memory_utils/inline_memcpy.h" #include "test/UnitTest/Test.h" 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; @@ -196,17 +194,17 @@ struct OutBuff { size_t cur_pos = 0; }; -int copy_to_out(string_view new_str, void *raw_out_buff) { - if (new_str.size() == 0) { +int copy_to_out(const char *new_str, size_t new_str_len, void *raw_out_buff) { + if (new_str_len == 0) { return 0; } 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()); + LIBC_NAMESPACE::inline_memcpy(out_buff->out_str + out_buff->cur_pos, new_str, + new_str_len); - out_buff->cur_pos += new_str.size(); + out_buff->cur_pos += new_str_len; return 0; } _______________________________________________ llvm-branch-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits
