FastLED 3.10.6
Loading...
Searching...
No Matches
stdio.cpp.hpp
Go to the documentation of this file.
1#include "fl/stl/stdio.h"
2#include "fl/stl/charconv.h" // For fl::detail::hex, HexIntWidth
3
4namespace fl { namespace printf_detail {
5
7 if (value != value) {
8 return false;
9 }
10 // FLT_MAX, spelled out. The old bound, 3.5e38f, overflows float to
11 // infinity, so the check only worked because of that overflow.
12 return value >= -3.40282347e38f && value <= 3.40282347e38f;
13}
14
16 const bool negative = value < 0.0f;
17 float magnitude = negative ? -value : value;
18 int exponent = 0;
19 while (magnitude >= 10.0f) {
20 magnitude /= 10.0f;
21 ++exponent;
22 }
23 while (magnitude > 0.0f && magnitude < 1.0f) {
24 magnitude *= 10.0f;
25 --exponent;
26 }
27
28 // Normalising put the mantissa in [1, 10), but *rounding* it to the
29 // requested precision can carry it back out: 9.999e18 at precision 2
30 // rounds to 10.00, and "10.00e+18" is not scientific notation. Checked
31 // before formatting rather than patched after, so there is one place
32 // where the exponent is decided.
33 float half_step = 0.5f;
34 for (int digit = 0; digit < precision; ++digit) {
35 half_step /= 10.0f;
36 }
37 if (magnitude >= 10.0f - half_step) {
38 magnitude /= 10.0f;
39 ++exponent;
40 }
41
42 sstream stream;
43 if (negative) {
44 stream << "-";
45 }
46 // The mantissa is in [1, 10) now, so the ordinary path renders it.
47 stream << format_float(magnitude, precision);
48 stream << "e";
49 if (exponent < 0) {
50 stream << "-";
51 exponent = -exponent;
52 } else {
53 stream << "+";
54 }
55 if (exponent < 10) {
56 stream << "0";
57 }
58 stream << exponent;
59 return stream.str();
60}
61
62namespace {
63
64// Streams an integral value exactly as `sstream << original_type` would:
65// char as a character, bool as true/false, integers in decimal.
67 switch (a.kind) {
68 case ScalarArg::kUnsigned: temp << a.u; break;
69 case ScalarArg::kBool: temp << (a.u != 0); break;
70 case ScalarArg::kChar: temp << static_cast<char>(a.u); break;
71 default: temp << a.s; break;
72 }
73}
74
75// Same for float/double, which go through their own sstream overloads.
77 if (a.kind == ScalarArg::kFloat) {
78 temp << a.f;
79 } else {
80 temp << a.d;
81 }
82}
83
84void emit_padded(sstream& stream, const FormatSpec& spec, const fl::string& result,
85 bool is_numeric) FL_NO_EXCEPT {
86 stream << apply_width(result, spec, is_numeric);
87}
88
89} // namespace
90
91// Integral kinds. Must not reference float/double code: integer-only
92// programs link only this half (FastLED#4671).
93void format_integral(sstream& stream, const FormatSpec& spec, const ScalarArg& a) FL_NO_EXCEPT {
95 bool is_numeric = false;
96
97 switch (spec.type) {
98 case 'd':
99 case 'i': {
100 is_numeric = true;
101 sstream temp;
102 stream_integral(temp, a);
103 result = temp.str();
104
105 // Handle sign flags
106 bool is_negative = !result.empty() && result[0] == '-';
107 if (!is_negative) {
108 if (spec.show_sign) {
109 result = fl::string("+") + result;
110 } else if (spec.space_sign) {
111 result = fl::string(" ") + result;
112 }
113 }
114 break;
115 }
116
117 case 'u': {
118 is_numeric = true;
119 sstream temp;
120 stream_integral(temp, a);
121 result = temp.str();
122 break;
123 }
124
125 case 'o': {
126 is_numeric = true;
127 result = to_octal(a.u);
128 // Alternate form: prefix with 0 (but not for zero itself)
129 if (spec.alt_form && a.u != 0) {
130 result = fl::string("0") + result;
131 }
132 break;
133 }
134
135 case 'x': {
136 is_numeric = true;
137 // Mirrors fl::to_hex<T> for the original type.
138 const bool negative = a.s < 0;
139 const fl::u64 magnitude =
140 negative ? static_cast<fl::u64>(0) - a.u : a.u; // no INT64_MIN UB
142 magnitude,
143 static_cast<fl::detail::HexIntWidth>(a.size * 8),
144 negative, spec.uppercase, false);
145 if (spec.alt_form && a.u != 0) {
146 result = fl::string(spec.uppercase ? "0X" : "0x") + result;
147 }
148 break;
149 }
150
151 case 'f':
152 result = "<type_error>";
153 break;
154
155 case 'c': {
156 char temp_str[2] = {static_cast<char>(a.u), '\0'};
157 result = temp_str;
158 break;
159 }
160
161 case 's': {
162 sstream temp;
163 stream_integral(temp, a);
164 result = temp.str();
165 break;
166 }
167
168 default:
169 result = "<unknown_format>";
170 break;
171 }
172 emit_padded(stream, spec, result, is_numeric);
173}
174
175// Float/double kinds. Only floating format_arg instantiations reference this.
176void format_floating(sstream& stream, const FormatSpec& spec, const ScalarArg& a) FL_NO_EXCEPT {
178 bool is_numeric = false;
179
180 switch (spec.type) {
181 case 'd':
182 case 'i':
183 case 'u':
184 case 'o':
185 case 'c':
186 result = "<type_error>";
187 break;
188
189 case 'x': {
190 is_numeric = true;
191 const bool is_float = a.kind == ScalarArg::kFloat;
192 result = is_float ? fl::to_hex(a.f, spec.uppercase)
193 : fl::to_hex(a.d, spec.uppercase);
194 const bool nonzero = is_float ? a.f != 0.0f : a.d != 0.0;
195 if (spec.alt_form && nonzero) {
196 result = fl::string(spec.uppercase ? "0X" : "0x") + result;
197 }
198 break;
199 }
200
201 case 'f': {
202 is_numeric = true;
203 const float value =
204 a.kind == ScalarArg::kFloat ? a.f : static_cast<float>(a.d);
205 if (spec.precision >= 0) {
206 result = format_float(value, spec.precision);
207 } else {
208 sstream temp;
209 temp << value;
210 result = temp.str();
211 }
212 break;
213 }
214
215 case 's': {
216 sstream temp;
217 stream_floating(temp, a);
218 result = temp.str();
219 break;
220 }
221
222 default:
223 result = "<unknown_format>";
224 break;
225 }
226 emit_padded(stream, spec, result, is_numeric);
227}
228
229void format_arg(sstream& stream, const FormatSpec& spec, const fl::string& arg) FL_NO_EXCEPT {
230 format_arg(stream, spec, arg.c_str());
231}
232
233void format_arg(sstream& stream, const FormatSpec& spec, const fl::string_view& arg) FL_NO_EXCEPT {
234 format_arg(stream, spec, fl::string(arg).c_str());
235}
236
237} } // namespace fl::printf_detail
238
239namespace fl {
240
241void printf(const char* format) FL_NO_EXCEPT {
242 char output[64];
243 fl::size used = 0;
244 const auto flush_buffer = [&]() {
245 if (used == 0) {
246 return;
247 }
248 output[used] = '\0';
249 fl::print(output);
250 // `append` resumes writing only after this shared cursor is reset.
251 used = 0;
252 };
253 const auto append = [&](const char* text) {
254 while (*text) {
255 if (used == sizeof(output) - 1) {
256 flush_buffer();
257 }
258 output[used++] = *text++;
259 }
260 };
261
262 while (*format) {
263 if (*format == '%') {
266 append(spec.type == '%' ? "%" : "<missing_arg>");
267 continue;
268 }
269 if (format[0] == '{' && format[1] == '{') {
270 append("{");
271 format += 2;
272 continue;
273 }
274 if (format[0] == '}' && format[1] == '}') {
275 append("}");
276 format += 2;
277 continue;
278 }
279 if (format[0] == '{' && format[1] == '}') {
280 append("<missing_arg>");
281 format += 2;
282 continue;
283 }
284 if (used == sizeof(output) - 1) {
285 flush_buffer();
286 }
287 output[used++] = *format++;
288 }
289 flush_buffer();
290}
291
292int snprintf(char* buffer, fl::size size, const char* format) FL_NO_EXCEPT {
293 if (!buffer || size == 0) {
294 return 0;
295 }
296
297 fl::size written = 0;
298 const auto append_char = [&](char value) {
299 if (written < size - 1) {
300 buffer[written++] = value;
301 }
302 };
303 const auto append_text = [&](const char* text) {
304 while (*text && written < size - 1) {
305 buffer[written++] = *text++;
306 }
307 };
308
309 while (*format && written < size - 1) {
310 if (*format == '%') {
313 append_text(spec.type == '%' ? "%" : "<missing_arg>");
314 continue;
315 }
316 if (format[0] == '{' && format[1] == '{') {
317 append_char('{');
318 format += 2;
319 continue;
320 }
321 if (format[0] == '}' && format[1] == '}') {
322 append_char('}');
323 format += 2;
324 continue;
325 }
326 if (format[0] == '{' && format[1] == '}') {
327 append_text("<missing_arg>");
328 format += 2;
329 continue;
330 }
331 append_char(*format++);
332 }
333 buffer[written] = '\0';
334 return static_cast<int>(written);
335}
336
337} // namespace fl
string str() const FL_NO_EXCEPT
Definition strstream.h:43
fl::string hex(u64 value, HexIntWidth width, bool is_negative, bool uppercase, bool pad_to_width)
Internal hex conversion function (implementation in charconv.cpp)
HexIntWidth
Integer width classification for hex conversion.
Definition charconv.h:42
void stream_integral(sstream &temp, const ScalarArg &a) FL_NO_EXCEPT
Definition stdio.cpp.hpp:66
void stream_floating(sstream &temp, const ScalarArg &a) FL_NO_EXCEPT
Definition stdio.cpp.hpp:76
void emit_padded(sstream &stream, const FormatSpec &spec, const fl::string &result, bool is_numeric) FL_NO_EXCEPT
Definition stdio.cpp.hpp:84
void format_arg(sstream &stream, const FormatSpec &spec, const fl::string &arg) FL_NO_EXCEPT
fl::string apply_width(const fl::string &str, const FormatSpec &spec, bool is_numeric=false) FL_NO_EXCEPT
Definition stdio.h:247
fl::string to_octal(T value) FL_NO_EXCEPT
Definition stdio.h:228
fl::string format_float(float value, int precision) FL_NO_EXCEPT
Definition stdio.h:341
bool float_is_finite(float value) FL_NO_EXCEPT
Definition stdio.cpp.hpp:6
FormatSpec parse_format_spec(const char *&format) FL_NO_EXCEPT
Definition stdio.h:130
void format_floating(sstream &stream, const FormatSpec &spec, const ScalarArg &a) FL_NO_EXCEPT
fl::string format_float_scientific(float value, int precision) FL_NO_EXCEPT
Definition stdio.cpp.hpp:15
void format_integral(sstream &stream, const FormatSpec &spec, const ScalarArg &a) FL_NO_EXCEPT
Definition stdio.cpp.hpp:93
constexpr int type_rank< T >::value
int snprintf(char *buffer, fl::size size, const char *format) FL_NO_EXCEPT
void print(const char *str)
void printf(const char *format) FL_NO_EXCEPT
fl::string to_hex(T value, bool uppercase=false, bool pad_to_width=false) FL_NO_EXCEPT
Convert an integer value to hexadecimal string representation.
Definition string.h:526
expected< T, E > result
Alias for expected (Rust-style naming)
Definition result.h:31
InputGamut g FL_NO_EXCEPT
Definition rgbw.h:121
fl::string format(const char *fmt)
Format with no arguments.
Definition format.h:439
Base definition for an LED controller.
Definition crgb.hpp:179