microfmt
Zero-allocation C++ formatting for constrained environments
Loading...
Searching...
No Matches
styled_sink.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2026 Jarosław Pelczar <jarek@jpelczar.com>
2//
3// SPDX-License-Identifier: BSD-2-Clause
4
5#pragma once
6
16#include "../microfmt.hpp"
17#include <cstddef>
18#include <cstdint>
19#include <string_view>
20#include <utility>
21
22namespace microfmt {
23
25enum class char_transform : uint8_t {
26 none = 0,
29 sanitize_ascii // Replaces non-printable control chars with '.'
30};
31
38class MICROFMT_API_CLASS RELOCO_POINTER transform_sink {
39public:
41 explicit constexpr transform_sink(sink target RELOCO_LIFETIME_CAPTURE_BY_THIS,
42 char_transform t = char_transform::none) noexcept
43 : target_(target), transform_(t) {}
44
45 transform_sink(const transform_sink &) = delete;
47 transform_sink(transform_sink &&) noexcept = default;
48 transform_sink &operator=(transform_sink &&) noexcept = default;
49
51 [[nodiscard]] sink as_sink() & noexcept RELOCO_LIFETIMEBOUND {
52 return sink{this,
53 [](void *ctx, microfmt::string_view sv) noexcept { static_cast<transform_sink *>(ctx)->write(sv); }};
54 }
55
57 void set_transform(char_transform t) noexcept { transform_ = t; }
58
59 void put(char c) noexcept { target_.put(apply_char(c)); }
60
61 void write(microfmt::string_view sv) noexcept {
62 for (char c : sv) {
63 target_.put(apply_char(c));
64 }
65 }
66
67private:
68 // bridges to a C/platform API (FILE*, syslog, android log, raw chunk storage) that takes raw pointers.
69 RELOCO_BEGIN_UNSAFE_CODE
70 [[nodiscard]] constexpr char apply_char(char c) const noexcept {
71 switch (transform_) {
72 case char_transform::to_upper:
73 if (c >= 'a' && c <= 'z')
74 return static_cast<char>(c - ('a' - 'A'));
75 return c;
76 case char_transform::to_lower:
77 if (c >= 'A' && c <= 'Z')
78 return static_cast<char>(c + ('a' - 'A'));
79 return c;
80 case char_transform::sanitize_ascii:
81 if ((static_cast<uint8_t>(c) < 0x20 || c == '\x7f') && c != '\n' && c != '\r' && c != '\t') {
82 return '.';
83 }
84 return c;
85 case char_transform::none:
86 default:
87 return c;
88 }
89 }
91
92 sink target_;
93 char_transform transform_{char_transform::none};
94};
95
102class MICROFMT_API_CLASS RELOCO_POINTER prefix_sink {
103public:
105 explicit constexpr prefix_sink(sink target RELOCO_LIFETIME_CAPTURE_BY_THIS,
106 microfmt::string_view prefix RELOCO_LIFETIME_CAPTURE_BY_THIS = " ") noexcept
107 : target_(target), prefix_(prefix) {}
108
109 prefix_sink(const prefix_sink &) = delete;
111 prefix_sink(prefix_sink &&) noexcept = default;
112 prefix_sink &operator=(prefix_sink &&) noexcept = default;
113
115 [[nodiscard]] sink as_sink() & noexcept RELOCO_LIFETIMEBOUND {
116 return sink{this, [](void *ctx, microfmt::string_view sv) noexcept { static_cast<prefix_sink *>(ctx)->write(sv); }};
117 }
118
120 void set_prefix(microfmt::string_view p RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept { prefix_ = p; }
121
122 void put(char c) noexcept {
123 if (at_line_start_) {
124 target_.write(prefix_);
125 at_line_start_ = false;
126 }
127
128 target_.put(c);
129
130 if (c == '\n') {
131 at_line_start_ = true;
132 }
133 }
134
135 void write(microfmt::string_view sv) noexcept {
136 for (char c : sv) {
137 put(c);
138 }
139 }
140
141private:
142 sink target_;
143 microfmt::string_view prefix_{" "};
144 bool at_line_start_{true};
145};
146
148class MICROFMT_API_CLASS RELOCO_POINTER limit_sink {
149public:
151 explicit constexpr limit_sink(sink target RELOCO_LIFETIME_CAPTURE_BY_THIS, size_t max_bytes) noexcept
152 : target_(target), remaining_(max_bytes) {}
153
154 limit_sink(const limit_sink &) = delete;
155 limit_sink &operator=(const limit_sink &) = delete;
156 limit_sink(limit_sink &&) noexcept = default;
157 limit_sink &operator=(limit_sink &&) noexcept = default;
158
160 [[nodiscard]] sink as_sink() & noexcept RELOCO_LIFETIMEBOUND {
161 return sink{this, [](void *ctx, microfmt::string_view sv) noexcept { static_cast<limit_sink *>(ctx)->write(sv); }};
162 }
163
164 void put(char c) noexcept {
165 if (remaining_ > 0) {
166 target_.put(c);
167 --remaining_;
168 }
169 }
170
171 void write(microfmt::string_view sv) noexcept {
172 if (remaining_ == 0 || sv.empty())
173 return;
174
175 const size_t to_write = (sv.size() <= remaining_) ? sv.size() : remaining_;
176 target_.write(sv.substr(0, to_write));
177 remaining_ -= to_write;
178 }
179
181 [[nodiscard]] constexpr size_t remaining() const noexcept { return remaining_; }
183 [[nodiscard]] constexpr bool capped() const noexcept { return remaining_ == 0; }
184
185private:
186 sink target_;
187 size_t remaining_{0};
188};
189
190} // namespace microfmt
Definition styled_sink.hpp:148
constexpr bool capped() const noexcept
Definition styled_sink.hpp:183
limit_sink & operator=(const limit_sink &)=delete
limit_sink(limit_sink &&) noexcept=default
void put(char c) noexcept
Definition styled_sink.hpp:164
constexpr limit_sink(sink target RELOCO_LIFETIME_CAPTURE_BY_THIS, size_t max_bytes) noexcept
Definition styled_sink.hpp:151
constexpr size_t remaining() const noexcept
Definition styled_sink.hpp:181
void write(microfmt::string_view sv) noexcept
Definition styled_sink.hpp:171
limit_sink(const limit_sink &)=delete
Definition styled_sink.hpp:102
void set_prefix(microfmt::string_view p RELOCO_LIFETIME_CAPTURE_BY_THIS) noexcept
Definition styled_sink.hpp:120
void write(microfmt::string_view sv) noexcept
Definition styled_sink.hpp:135
constexpr prefix_sink(sink target RELOCO_LIFETIME_CAPTURE_BY_THIS, microfmt::string_view prefix RELOCO_LIFETIME_CAPTURE_BY_THIS=" ") noexcept
Definition styled_sink.hpp:105
prefix_sink(const prefix_sink &)=delete
prefix_sink(prefix_sink &&) noexcept=default
void put(char c) noexcept
Definition styled_sink.hpp:122
prefix_sink & operator=(const prefix_sink &)=delete
Definition styled_sink.hpp:38
transform_sink(const transform_sink &)=delete
transform_sink(transform_sink &&) noexcept=default
void write(microfmt::string_view sv) noexcept
Definition styled_sink.hpp:61
void put(char c) noexcept
Definition styled_sink.hpp:59
transform_sink & operator=(const transform_sink &)=delete
constexpr transform_sink(sink target RELOCO_LIFETIME_CAPTURE_BY_THIS, char_transform t=char_transform::none) noexcept
Definition styled_sink.hpp:41
void set_transform(char_transform t) noexcept
Definition styled_sink.hpp:57
#define MICROFMT_API_CLASS
Definition compat.hpp:181
Definition assert.hpp:19
char_transform
Definition styled_sink.hpp:25
reloco::sink sink
Type-erased, zero-allocation output sink.
Definition microfmt.hpp:46
RELOCO_END_UNSAFE_CODE
Definition remote_object.hpp:132