1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
|
//===-- 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 <stddef.h>
namespace LIBC_NAMESPACE_DECL {
namespace printf_core {
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.
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) {}
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) {
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();
}
};
template <WriteMode write_mode> class Writer final {
WriteBuffer<write_mode> &wb;
size_t chars_written = 0;
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<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 += 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 += 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);
}
// 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 size_t 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
#endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITER_H
|