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