xref: /aosp_15_r20/external/llvm-libc/src/stdio/printf_core/writer.h (revision 71db0c75aadcf003ffe3238005f61d7618a3fead)
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 struct WriteBuffer {
25   enum class WriteMode {
26     FILL_BUFF_AND_DROP_OVERFLOW,
27     FLUSH_TO_STREAM,
28     RESIZE_AND_FILL_BUFF,
29   };
30   using StreamWriter = int (*)(cpp::string_view, void *);
31   char *buff;
32   const char *init_buff; // for checking when resize.
33   size_t buff_len;
34   size_t buff_cur = 0;
35 
36   // The stream writer will be called when the buffer is full. It will be passed
37   // string_views to write to the stream.
38   StreamWriter stream_writer;
39   void *output_target;
40   WriteMode write_mode;
41 
WriteBufferWriteBuffer42   LIBC_INLINE WriteBuffer(char *Buff, size_t Buff_len, StreamWriter hook,
43                           void *target)
44       : buff(Buff), init_buff(Buff), buff_len(Buff_len), stream_writer(hook),
45         output_target(target), write_mode(WriteMode::FLUSH_TO_STREAM) {}
46 
WriteBufferWriteBuffer47   LIBC_INLINE WriteBuffer(char *Buff, size_t Buff_len)
48       : buff(Buff), init_buff(Buff), buff_len(Buff_len), stream_writer(nullptr),
49         output_target(nullptr),
50         write_mode(WriteMode::FILL_BUFF_AND_DROP_OVERFLOW) {}
51 
WriteBufferWriteBuffer52   LIBC_INLINE WriteBuffer(char *Buff, size_t Buff_len, StreamWriter hook)
53       : buff(Buff), init_buff(Buff), buff_len(Buff_len), stream_writer(hook),
54         output_target(this), write_mode(WriteMode::RESIZE_AND_FILL_BUFF) {}
55 
flush_to_streamWriteBuffer56   LIBC_INLINE int flush_to_stream(cpp::string_view new_str) {
57     if (buff_cur > 0) {
58       int retval = stream_writer({buff, buff_cur}, output_target);
59       if (retval < 0)
60         return retval;
61     }
62     if (new_str.size() > 0) {
63       int retval = stream_writer(new_str, output_target);
64       if (retval < 0)
65         return retval;
66     }
67     buff_cur = 0;
68     return WRITE_OK;
69   }
70 
fill_remaining_to_buffWriteBuffer71   LIBC_INLINE int fill_remaining_to_buff(cpp::string_view new_str) {
72     if (buff_cur < buff_len) {
73       size_t bytes_to_write = buff_len - buff_cur;
74       if (bytes_to_write > new_str.size()) {
75         bytes_to_write = new_str.size();
76       }
77       inline_memcpy(buff + buff_cur, new_str.data(), bytes_to_write);
78       buff_cur += bytes_to_write;
79     }
80     return WRITE_OK;
81   }
82 
resize_and_writeWriteBuffer83   LIBC_INLINE int resize_and_write(cpp::string_view new_str) {
84     return stream_writer(new_str, output_target);
85   }
86 
87   // The overflow_write method is intended to be called to write the contents of
88   // the buffer and new_str to the stream_writer if it exists. If a resizing
89   // hook is provided, it will resize the buffer and write the contents. If
90   // neither a stream_writer nor a resizing hook is provided, it will fill the
91   // remaining space in the buffer with new_str and drop the overflow. Calling
92   // this with an empty string will flush the buffer if relevant.
93 
overflow_writeWriteBuffer94   LIBC_INLINE int overflow_write(cpp::string_view new_str) {
95     switch (write_mode) {
96     case WriteMode::FILL_BUFF_AND_DROP_OVERFLOW:
97       return fill_remaining_to_buff(new_str);
98     case WriteMode::FLUSH_TO_STREAM:
99       return flush_to_stream(new_str);
100     case WriteMode::RESIZE_AND_FILL_BUFF:
101       return resize_and_write(new_str);
102     }
103     __builtin_unreachable();
104   }
105 };
106 
107 class Writer final {
108   WriteBuffer *wb;
109   int chars_written = 0;
110 
111   // This is a separate, non-inlined function so that the inlined part of the
112   // write function is shorter.
113   int pad(char new_char, size_t length);
114 
115 public:
Writer(WriteBuffer * WB)116   LIBC_INLINE Writer(WriteBuffer *WB) : wb(WB) {}
117 
118   // Takes a string, copies it into the buffer if there is space, else passes it
119   // to the overflow mechanism to be handled separately.
write(cpp::string_view new_string)120   LIBC_INLINE int write(cpp::string_view new_string) {
121     chars_written += static_cast<int>(new_string.size());
122     if (LIBC_LIKELY(wb->buff_cur + new_string.size() <= wb->buff_len)) {
123       inline_memcpy(wb->buff + wb->buff_cur, new_string.data(),
124                     new_string.size());
125       wb->buff_cur += new_string.size();
126       return WRITE_OK;
127     }
128     return wb->overflow_write(new_string);
129   }
130 
131   // Takes a char and a length, memsets the next length characters of the buffer
132   // if there is space, else calls pad which will loop and call the overflow
133   // mechanism on a secondary buffer.
write(char new_char,size_t length)134   LIBC_INLINE int write(char new_char, size_t length) {
135     chars_written += static_cast<int>(length);
136 
137     if (LIBC_LIKELY(wb->buff_cur + length <= wb->buff_len)) {
138       inline_memset(wb->buff + wb->buff_cur, new_char, length);
139       wb->buff_cur += length;
140       return WRITE_OK;
141     }
142     return pad(new_char, length);
143   }
144 
145   // Takes a char, copies it into the buffer if there is space, else passes it
146   // to the overflow mechanism to be handled separately.
write(char new_char)147   LIBC_INLINE int write(char new_char) {
148     chars_written += 1;
149     if (LIBC_LIKELY(wb->buff_cur + 1 <= wb->buff_len)) {
150       wb->buff[wb->buff_cur] = new_char;
151       wb->buff_cur += 1;
152       return WRITE_OK;
153     }
154     cpp::string_view char_string_view(&new_char, 1);
155     return wb->overflow_write(char_string_view);
156   }
157 
get_chars_written()158   LIBC_INLINE int get_chars_written() { return chars_written; }
159 };
160 
161 } // namespace printf_core
162 } // namespace LIBC_NAMESPACE_DECL
163 
164 #endif // LLVM_LIBC_SRC_STDIO_PRINTF_CORE_WRITER_H
165