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/optimization.h" |
14 | #include "src/stdio/printf_core/core_structs.h" |
15 | #include "src/string/memory_utils/inline_memcpy.h" |
16 | #include "src/string/memory_utils/inline_memset.h" |
17 | |
18 | #include <stddef.h> |
19 | |
20 | namespace LIBC_NAMESPACE { |
21 | namespace printf_core { |
22 | |
23 | struct WriteBuffer { |
24 | using StreamWriter = int (*)(cpp::string_view, void *); |
25 | char *buff; |
26 | const size_t buff_len; |
27 | size_t buff_cur = 0; |
28 | |
29 | // The stream writer will be called when the buffer is full. It will be passed |
30 | // string_views to write to the stream. |
31 | StreamWriter stream_writer; |
32 | void *output_target; |
33 | |
34 | LIBC_INLINE WriteBuffer(char *Buff, size_t Buff_len, StreamWriter hook, |
35 | void *target) |
36 | : buff(Buff), buff_len(Buff_len), stream_writer(hook), |
37 | output_target(target) {} |
38 | |
39 | LIBC_INLINE WriteBuffer(char *Buff, size_t Buff_len) |
40 | : buff(Buff), buff_len(Buff_len), stream_writer(nullptr), |
41 | output_target(nullptr) {} |
42 | |
43 | // The overflow_write method is intended to be called to write the contents of |
44 | // the buffer and new_str to the stream_writer if it exists, else it will |
45 | // write as much of new_str to the buffer as it can. The current position in |
46 | // the buffer will be reset iff stream_writer is called. Calling this with an |
47 | // empty string will flush the buffer if relevant. |
48 | LIBC_INLINE int overflow_write(cpp::string_view new_str) { |
49 | // If there is a stream_writer, write the contents of the buffer, then |
50 | // new_str, then clear the buffer. |
51 | if (stream_writer != nullptr) { |
52 | if (buff_cur > 0) { |
53 | int retval = stream_writer({buff, buff_cur}, output_target); |
54 | if (retval < 0) { |
55 | return retval; |
56 | } |
57 | } |
58 | if (new_str.size() > 0) { |
59 | int retval = stream_writer(new_str, output_target); |
60 | if (retval < 0) { |
61 | return retval; |
62 | } |
63 | } |
64 | buff_cur = 0; |
65 | return WRITE_OK; |
66 | } else { |
67 | // We can't flush to the stream, so fill the rest of the buffer, then drop |
68 | // the overflow. |
69 | if (buff_cur < buff_len) { |
70 | size_t bytes_to_write = buff_len - buff_cur; |
71 | if (bytes_to_write > new_str.size()) { |
72 | bytes_to_write = new_str.size(); |
73 | } |
74 | inline_memcpy(dst: buff + buff_cur, src: new_str.data(), count: bytes_to_write); |
75 | buff_cur += bytes_to_write; |
76 | } |
77 | return WRITE_OK; |
78 | } |
79 | } |
80 | }; |
81 | |
82 | class Writer final { |
83 | WriteBuffer *wb; |
84 | int chars_written = 0; |
85 | |
86 | // This is a separate, non-inlined function so that the inlined part of the |
87 | // write function is shorter. |
88 | int pad(char new_char, size_t length); |
89 | |
90 | public: |
91 | LIBC_INLINE Writer(WriteBuffer *WB) : wb(WB) {} |
92 | |
93 | // Takes a string, copies it into the buffer if there is space, else passes it |
94 | // to the overflow mechanism to be handled separately. |
95 | LIBC_INLINE int write(cpp::string_view new_string) { |
96 | chars_written += static_cast<int>(new_string.size()); |
97 | if (LIBC_LIKELY(wb->buff_cur + new_string.size() <= wb->buff_len)) { |
98 | inline_memcpy(dst: wb->buff + wb->buff_cur, src: new_string.data(), |
99 | count: new_string.size()); |
100 | wb->buff_cur += new_string.size(); |
101 | return WRITE_OK; |
102 | } |
103 | return wb->overflow_write(new_str: new_string); |
104 | } |
105 | |
106 | // Takes a char and a length, memsets the next length characters of the buffer |
107 | // if there is space, else calls pad which will loop and call the overflow |
108 | // mechanism on a secondary buffer. |
109 | LIBC_INLINE int write(char new_char, size_t length) { |
110 | chars_written += static_cast<int>(length); |
111 | |
112 | if (LIBC_LIKELY(wb->buff_cur + length <= wb->buff_len)) { |
113 | inline_memset(dst: wb->buff + wb->buff_cur, value: new_char, count: length); |
114 | wb->buff_cur += length; |
115 | return WRITE_OK; |
116 | } |
117 | return pad(new_char, length); |
118 | } |
119 | |
120 | // Takes a char, copies it into the buffer if there is space, else passes it |
121 | // to the overflow mechanism to be handled separately. |
122 | LIBC_INLINE int write(char new_char) { |
123 | chars_written += 1; |
124 | if (LIBC_LIKELY(wb->buff_cur + 1 <= wb->buff_len)) { |
125 | wb->buff[wb->buff_cur] = new_char; |
126 | wb->buff_cur += 1; |
127 | return WRITE_OK; |
128 | } |
129 | cpp::string_view char_string_view(&new_char, 1); |
130 | return wb->overflow_write(new_str: char_string_view); |
131 | } |
132 | |
133 | LIBC_INLINE int get_chars_written() { return chars_written; } |
134 | }; |
135 | |
136 | } // namespace printf_core |
137 | } // namespace LIBC_NAMESPACE |
138 | |
139 | #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITER_H |
140 | |