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
21namespace LIBC_NAMESPACE_DECL {
22namespace printf_core {
23
24enum 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.
33template <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
41template <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
128template <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
158public:
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.
206Writer(WriteBuffer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>)
207 -> Writer<WriteMode::FILL_BUFF_AND_DROP_OVERFLOW>;
208Writer(WriteBuffer<WriteMode::RESIZE_AND_FILL_BUFF>)
209 -> Writer<WriteMode::RESIZE_AND_FILL_BUFF>;
210Writer(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.

source code of libc/src/stdio/printf_core/writer.h