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
20namespace LIBC_NAMESPACE {
21namespace printf_core {
22
23struct 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
82class 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
90public:
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

source code of libc/src/stdio/printf_core/writer.h