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 WriteBufferWriteBuffer34 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 WriteBufferWriteBuffer39 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. overflow_writeWriteBuffer48 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(buff + buff_cur, new_str.data(), 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: Writer(WriteBuffer * WB)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. write(cpp::string_view new_string)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(wb->buff + wb->buff_cur, new_string.data(), 99 new_string.size()); 100 wb->buff_cur += new_string.size(); 101 return WRITE_OK; 102 } 103 return wb->overflow_write(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. write(char new_char,size_t length)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(wb->buff + wb->buff_cur, new_char, 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. write(char new_char)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(char_string_view); 131 } 132 get_chars_written()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