Warning: This file is not a C or C++ file. It does not have highlighting.
| 1 | //===-- Writer definition for printf ----------------------------*- C++ -*-===// |
|---|---|
| 2 | // |
| 3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| 4 | // See https://llvm.org/LICENSE.txt for license information. |
| 5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| 6 | // |
| 7 | //===----------------------------------------------------------------------===// |
| 8 | |
| 9 | #ifndef LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITER_H |
| 10 | #define LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITER_H |
| 11 | |
| 12 | #include "src/__support/CPP/string_view.h" |
| 13 | #include "src/__support/macros/config.h" |
| 14 | #include "src/__support/macros/optimization.h" |
| 15 | #include "src/stdio/printf_core/core_structs.h" |
| 16 | #include "src/string/memory_utils/inline_memcpy.h" |
| 17 | #include "src/string/memory_utils/inline_memset.h" |
| 18 | |
| 19 | #include <stddef.h> |
| 20 | |
| 21 | namespace LIBC_NAMESPACE_DECL { |
| 22 | namespace printf_core { |
| 23 | |
| 24 | enum class WriteMode { |
| 25 | FILL_BUFF_AND_DROP_OVERFLOW, |
| 26 | FLUSH_TO_STREAM, |
| 27 | RESIZE_AND_FILL_BUFF, |
| 28 | RUNTIME_DISPATCH, |
| 29 | }; |
| 30 | |
| 31 | // Helper to omit the template argument if we are using runtime dispatch and |
| 32 | // avoid multiple copies of the converter functions. |
| 33 | template <WriteMode write_mode> struct Mode { |
| 34 | #ifdef LIBC_COPT_PRINTF_RUNTIME_DISPATCH |
| 35 | static constexpr WriteMode value = WriteMode::RUNTIME_DISPATCH; |
| 36 | #else |
| 37 | static constexpr WriteMode value = write_mode; |
| 38 | #endif |
| 39 | }; |
| 40 | |
| 41 | template <WriteMode write_mode> struct WriteBuffer { |
| 42 | using StreamWriter = int (*)(cpp::string_view, void *); |
| 43 | char *buff; |
| 44 | const char *init_buff; // for checking when resize. |
| 45 | size_t buff_len; |
| 46 | size_t buff_cur = 0; |
| 47 | |
| 48 | // The stream writer will be called when the buffer is full. It will be passed |
| 49 | // string_views to write to the stream. |
| 50 | const StreamWriter stream_writer; |
| 51 | void *output_target; |
| 52 | |
| 53 | // The current writing mode in case the user wants runtime dispatch of the |
| 54 | // stream writer with function pointers. |
| 55 | [[maybe_unused]] WriteMode write_mode_; |
| 56 | |
| 57 | LIBC_INLINE WriteBuffer(char *buff, size_t buff_len, StreamWriter hook, |
| 58 | void *target) |
| 59 | : buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(hook), |
| 60 | output_target(target), write_mode_(WriteMode::FLUSH_TO_STREAM) {} |
| 61 | |
| 62 | LIBC_INLINE WriteBuffer(char *buff, size_t buff_len) |
| 63 | : buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(nullptr), |
| 64 | output_target(nullptr), |
| 65 | write_mode_(WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) {} |
| 66 | |
| 67 | LIBC_INLINE WriteBuffer(char *buff, size_t buff_len, StreamWriter hook) |
| 68 | : buff(buff), init_buff(buff), buff_len(buff_len), stream_writer(hook), |
| 69 | output_target(this), write_mode_(WriteMode::RESIZE_AND_FILL_BUFF) {} |
| 70 | |
| 71 | LIBC_INLINE int flush_to_stream(cpp::string_view new_str) { |
| 72 | if (buff_cur > 0) { |
| 73 | int retval = stream_writer({buff, buff_cur}, output_target); |
| 74 | if (retval < 0) |
| 75 | return retval; |
| 76 | } |
| 77 | if (new_str.size() > 0) { |
| 78 | int retval = stream_writer(new_str, output_target); |
| 79 | if (retval < 0) |
| 80 | return retval; |
| 81 | } |
| 82 | buff_cur = 0; |
| 83 | return WRITE_OK; |
| 84 | } |
| 85 | |
| 86 | LIBC_INLINE int fill_remaining_to_buff(cpp::string_view new_str) { |
| 87 | if (buff_cur < buff_len) { |
| 88 | size_t bytes_to_write = buff_len - buff_cur; |
| 89 | if (bytes_to_write > new_str.size()) { |
| 90 | bytes_to_write = new_str.size(); |
| 91 | } |
| 92 | inline_memcpy(buff + buff_cur, new_str.data(), bytes_to_write); |
| 93 | buff_cur += bytes_to_write; |
| 94 | } |
| 95 | return WRITE_OK; |
| 96 | } |
| 97 | |
| 98 | LIBC_INLINE int resize_and_write(cpp::string_view new_str) { |
| 99 | return stream_writer(new_str, output_target); |
| 100 | } |
| 101 | |
| 102 | // The overflow_write method is intended to be called to write the contents of |
| 103 | // the buffer and new_str to the stream_writer if it exists. If a resizing |
| 104 | // hook is provided, it will resize the buffer and write the contents. If |
| 105 | // neither a stream_writer nor a resizing hook is provided, it will fill the |
| 106 | // remaining space in the buffer with new_str and drop the overflow. Calling |
| 107 | // this with an empty string will flush the buffer if relevant. |
| 108 | |
| 109 | LIBC_INLINE int overflow_write(cpp::string_view new_str) { |
| 110 | if constexpr (write_mode == WriteMode::RUNTIME_DISPATCH) { |
| 111 | if (write_mode_ == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) |
| 112 | return fill_remaining_to_buff(new_str); |
| 113 | else if (write_mode_ == WriteMode::FLUSH_TO_STREAM) |
| 114 | return flush_to_stream(new_str); |
| 115 | else if (write_mode_ == WriteMode::RESIZE_AND_FILL_BUFF) |
| 116 | return resize_and_write(new_str); |
| 117 | } else if constexpr (write_mode == WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) { |
| 118 | return fill_remaining_to_buff(new_str); |
| 119 | } else if constexpr (write_mode == WriteMode::FLUSH_TO_STREAM) { |
| 120 | return flush_to_stream(new_str); |
| 121 | } else if constexpr (write_mode == WriteMode::RESIZE_AND_FILL_BUFF) { |
| 122 | return resize_and_write(new_str); |
| 123 | } |
| 124 | __builtin_unreachable(); |
| 125 | } |
| 126 | }; |
| 127 | |
| 128 | template <WriteMode write_mode> class Writer final { |
| 129 | WriteBuffer<write_mode> &wb; |
| 130 | int chars_written = 0; |
| 131 | |
| 132 | LIBC_INLINE int pad(char new_char, size_t length) { |
| 133 | // First, fill as much of the buffer as possible with the padding char. |
| 134 | size_t written = 0; |
| 135 | const size_t buff_space = wb.buff_len - wb.buff_cur; |
| 136 | // ASSERT: length > buff_space |
| 137 | if (buff_space > 0) { |
| 138 | inline_memset(wb.buff + wb.buff_cur, new_char, buff_space); |
| 139 | wb.buff_cur += buff_space; |
| 140 | written = buff_space; |
| 141 | } |
| 142 | |
| 143 | // Next, overflow write the rest of length using the mini_buff. |
| 144 | constexpr size_t MINI_BUFF_SIZE = 64; |
| 145 | char mini_buff[MINI_BUFF_SIZE]; |
| 146 | inline_memset(mini_buff, new_char, MINI_BUFF_SIZE); |
| 147 | cpp::string_view mb_string_view(mini_buff, MINI_BUFF_SIZE); |
| 148 | while (written + MINI_BUFF_SIZE < length) { |
| 149 | int result = wb.overflow_write(mb_string_view); |
| 150 | if (result != WRITE_OK) |
| 151 | return result; |
| 152 | written += MINI_BUFF_SIZE; |
| 153 | } |
| 154 | cpp::string_view mb_substr = mb_string_view.substr(0, length - written); |
| 155 | return wb.overflow_write(mb_substr); |
| 156 | } |
| 157 | |
| 158 | public: |
| 159 | LIBC_INLINE Writer(WriteBuffer<write_mode> &wb) : wb(wb) {} |
| 160 | |
| 161 | // Takes a string, copies it into the buffer if there is space, else passes it |
| 162 | // to the overflow mechanism to be handled separately. |
| 163 | LIBC_INLINE int write(cpp::string_view new_string) { |
| 164 | chars_written += static_cast<int>(new_string.size()); |
| 165 | if (LIBC_LIKELY(wb.buff_cur + new_string.size() <= wb.buff_len)) { |
| 166 | inline_memcpy(wb.buff + wb.buff_cur, new_string.data(), |
| 167 | new_string.size()); |
| 168 | wb.buff_cur += new_string.size(); |
| 169 | return WRITE_OK; |
| 170 | } |
| 171 | return wb.overflow_write(new_string); |
| 172 | } |
| 173 | |
| 174 | // Takes a char and a length, memsets the next length characters of the buffer |
| 175 | // if there is space, else calls pad which will loop and call the overflow |
| 176 | // mechanism on a secondary buffer. |
| 177 | LIBC_INLINE int write(char new_char, size_t length) { |
| 178 | chars_written += static_cast<int>(length); |
| 179 | |
| 180 | if (LIBC_LIKELY(wb.buff_cur + length <= wb.buff_len)) { |
| 181 | inline_memset(wb.buff + wb.buff_cur, static_cast<unsigned char>(new_char), |
| 182 | length); |
| 183 | wb.buff_cur += length; |
| 184 | return WRITE_OK; |
| 185 | } |
| 186 | return pad(new_char, length); |
| 187 | } |
| 188 | |
| 189 | // Takes a char, copies it into the buffer if there is space, else passes it |
| 190 | // to the overflow mechanism to be handled separately. |
| 191 | LIBC_INLINE int write(char new_char) { |
| 192 | chars_written += 1; |
| 193 | if (LIBC_LIKELY(wb.buff_cur + 1 <= wb.buff_len)) { |
| 194 | wb.buff[wb.buff_cur] = new_char; |
| 195 | wb.buff_cur += 1; |
| 196 | return WRITE_OK; |
| 197 | } |
| 198 | cpp::string_view char_string_view(&new_char, 1); |
| 199 | return wb.overflow_write(char_string_view); |
| 200 | } |
| 201 | |
| 202 | LIBC_INLINE int get_chars_written() { return chars_written; } |
| 203 | }; |
| 204 | |
| 205 | // Class-template auto deduction helpers. |
| 206 | Writer(WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>) |
| 207 | -> Writer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>; |
| 208 | Writer(WriteBuffer<WriteMode::RESIZE_AND_FILL_BUFF>) |
| 209 | -> Writer<WriteMode::RESIZE_AND_FILL_BUFF>; |
| 210 | Writer(WriteBuffer<WriteMode::FLUSH_TO_STREAM>) |
| 211 | -> Writer<WriteMode::FLUSH_TO_STREAM>; |
| 212 | |
| 213 | } // namespace printf_core |
| 214 | } // namespace LIBC_NAMESPACE_DECL |
| 215 | |
| 216 | #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITER_H |
| 217 |
Warning: This file is not a C or C++ file. It does not have highlighting.
