//===-- 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 // //===----------------------------------------------------------------------===// #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITER_H #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITER_H #include "src/__support/CPP/string_view.h" #include "src/__support/macros/config.h" #include "src/__support/macros/optimization.h" #include "src/stdio/printf_core/core_structs.h" #include "src/string/memory_utils/inline_memcpy.h" #include "src/string/memory_utils/inline_memset.h" #include namespace LIBC_NAMESPACE_DECL { namespace printf_core { struct WriteBuffer { enum class WriteMode { FILL_BUFF_AND_DROP_OVERFLOW, FLUSH_TO_STREAM, RESIZE_AND_FILL_BUFF, }; 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. StreamWriter stream_writer; void *output_target; 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) {} 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) {} 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) { int retval = stream_writer({buff, buff_cur}, output_target); if (retval < 0) return retval; } if (new_str.size() > 0) { int retval = stream_writer(new_str, output_target); if (retval < 0) return retval; } buff_cur = 0; return WRITE_OK; } 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 resize_and_write(cpp::string_view new_str) { return stream_writer(new_str, output_target); } // 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) { switch (write_mode) { case WriteMode::FILL_BUFF_AND_DROP_OVERFLOW: return fill_remaining_to_buff(new_str); case WriteMode::FLUSH_TO_STREAM: return flush_to_stream(new_str); case WriteMode::RESIZE_AND_FILL_BUFF: return resize_and_write(new_str); } __builtin_unreachable(); } }; class Writer final { WriteBuffer *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); public: LIBC_INLINE Writer(WriteBuffer *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(new_string.size()); if (LIBC_LIKELY(wb->buff_cur + new_string.size() <= wb->buff_len)) { inline_memcpy(wb->buff + wb->buff_cur, new_string.data(), new_string.size()); wb->buff_cur += new_string.size(); return WRITE_OK; } return wb->overflow_write(new_string); } // Takes a char and a length, memsets the next length characters of the buffer // if there is space, else calls pad which will loop and call the overflow // mechanism on a secondary buffer. LIBC_INLINE int write(char new_char, size_t length) { chars_written += static_cast(length); if (LIBC_LIKELY(wb->buff_cur + length <= wb->buff_len)) { inline_memset(wb->buff + wb->buff_cur, new_char, length); wb->buff_cur += length; return WRITE_OK; } return pad(new_char, length); } // 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) { 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); } LIBC_INLINE int get_chars_written() { return chars_written; } }; } // namespace printf_core } // namespace LIBC_NAMESPACE_DECL #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITER_H