• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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