FastLED 3.9.15
Loading...
Searching...
No Matches
printf.h
Go to the documentation of this file.
1#pragma once
2
3#include "fl/strstream.h"
4#include "fl/namespace.h"
5#include "fl/type_traits.h"
6#include "fl/str.h"
7#include "fl/int.h"
8#include "fl/io.h" // For fl::print and fl::println
9
10namespace fl {
11
31template<typename... Args>
32void printf(const char* format, const Args&... args);
33
56template<typename... Args>
57int snprintf(char* buffer, fl::size size, const char* format, const Args&... args);
58
74template<fl::size N, typename... Args>
75int sprintf(char (&buffer)[N], const char* format, const Args&... args);
76
77
79
80namespace printf_detail {
81
82// Helper to parse format specifiers and extract precision
83struct FormatSpec {
84 char type = '\0'; // Format character (d, f, s, etc.)
85 int precision = -1; // Precision for floating point
86 bool uppercase = false; // For hex formatting
87
88 FormatSpec() = default;
89 explicit FormatSpec(char t) : type(t) {}
90 FormatSpec(char t, int prec) : type(t), precision(prec) {}
91};
92
93// Parse a format specifier from the format string
94// Returns the format spec and advances the pointer past the specifier
95inline FormatSpec parse_format_spec(const char*& format) {
96 FormatSpec spec;
97
98 if (*format != '%') {
99 return spec;
100 }
101
102 ++format; // Skip the '%'
103
104 // Handle literal '%'
105 if (*format == '%') {
106 spec.type = '%';
107 ++format;
108 return spec;
109 }
110
111 // Parse precision for floating point
112 if (*format == '.') {
113 ++format; // Skip the '.'
114 spec.precision = 0;
115 while (*format >= '0' && *format <= '9') {
116 spec.precision = spec.precision * 10 + (*format - '0');
117 ++format;
118 }
119 }
120
121 // Get the format type
122 spec.type = *format;
123 if (spec.type == 'X') {
124 spec.uppercase = true;
125 spec.type = 'x'; // Normalize to lowercase for processing
126 }
127
128 ++format;
129 return spec;
130}
131
132// Format floating point with specified precision
133inline fl::string format_float(float value, int precision) {
134 if (precision < 0) {
135 // Default precision - use StrStream's default behavior
136 StrStream stream;
137 stream << value;
138 return stream.str();
139 }
140
141 // Simple precision formatting
142 // This is a basic implementation - could be enhanced
143 if (precision == 0) {
144 int int_part = static_cast<int>(value + 0.5f); // Round
145 StrStream stream;
146 stream << int_part;
147 return stream.str();
148 }
149
150 // For non-zero precision, use basic rounding
151 int multiplier = 1;
152 for (int i = 0; i < precision; ++i) {
153 multiplier *= 10;
154 }
155
156 int scaled = static_cast<int>(value * multiplier + 0.5f);
157 int int_part = scaled / multiplier;
158 int frac_part = scaled % multiplier;
159
160 StrStream stream;
161 stream << int_part;
162 stream << ".";
163
164 // Pad fractional part with leading zeros if needed
165 int temp_multiplier = multiplier / 10;
166 while (temp_multiplier > frac_part && temp_multiplier > 1) {
167 stream << "0";
168 temp_multiplier /= 10;
169 }
170 if (frac_part > 0) {
171 stream << frac_part;
172 }
173
174 return stream.str();
175}
176
177// Convert integer to hex string - only for integral types
178template<typename T>
180to_hex(T value, bool uppercase) {
181 if (value == 0) {
182 return fl::string("0");
183 }
184
186 const char* digits = uppercase ? "0123456789ABCDEF" : "0123456789abcdef";
187
188 // Handle negative values for signed types
189 bool negative = false;
190 if (fl::is_signed<T>::value && value < 0) {
191 negative = true;
192 value = -value;
193 }
194
195 while (value > 0) {
196 char ch = digits[value % 16];
197 // Convert char to string since fl::string::append treats char as number
198 char temp_ch_str[2] = {ch, '\0'};
199 fl::string digit_str(temp_ch_str);
200 // Use += since + operator is not defined for fl::string
201 fl::string temp = digit_str;
202 temp += result;
203 result = temp;
204 value /= 16;
205 }
206
207 if (negative) {
208 fl::string minus_str("-");
209 minus_str += result;
210 result = minus_str;
211 }
212
213 return result;
214}
215
216// Non-integral types return error string
217template<typename T>
219to_hex(T, bool) {
220 return fl::string("<not_integral>");
221}
222
223// Format a single argument based on format specifier with better type handling
224template<typename T>
225void format_arg(StrStream& stream, const FormatSpec& spec, const T& arg) {
226 switch (spec.type) {
227 case 'd':
228 case 'i':
230 stream << arg;
231 } else {
232 stream << "<type_error>";
233 }
234 break;
235
236 case 'u':
238 // Convert unsigned manually since StrStream treats all as signed
239 unsigned int val = static_cast<unsigned int>(arg);
240 if (val == 0) {
241 stream << "0";
242 } else {
244 while (val > 0) {
245 char digit = '0' + (val % 10);
246 char temp_str[2] = {digit, '\0'};
247 fl::string digit_str(temp_str);
248 fl::string temp = digit_str;
249 temp += result;
250 result = temp;
251 val /= 10;
252 }
253 stream << result;
254 }
255 } else {
256 stream << "<type_error>";
257 }
258 break;
259
260 case 'f':
262 if (spec.precision >= 0) {
263 stream << format_float(static_cast<float>(arg), spec.precision);
264 } else {
265 stream << arg;
266 }
267 } else {
268 stream << "<type_error>";
269 }
270 break;
271
272 case 'c':
274 char ch = static_cast<char>(arg);
275 // Convert char to string since StrStream treats char as number
276 char temp_str[2] = {ch, '\0'};
277 stream << temp_str;
278 } else {
279 stream << "<type_error>";
280 }
281 break;
282
283 case 'x':
284 stream << to_hex(arg, spec.uppercase);
285 break;
286
287 case 's':
288 stream << arg; // StrStream handles string conversion
289 break;
290
291 default:
292 stream << "<unknown_format>";
293 break;
294 }
295}
296
297// Specialized format_arg for const char* (string literals)
298inline void format_arg(StrStream& stream, const FormatSpec& spec, const char* arg) {
299 switch (spec.type) {
300 case 's':
301 stream << arg;
302 break;
303 case 'x':
304 stream << "<string_not_hex>";
305 break;
306 case 'd':
307 case 'i':
308 case 'u':
309 case 'f':
310 case 'c':
311 stream << "<type_error>";
312 break;
313 default:
314 stream << "<unknown_format>";
315 break;
316 }
317}
318
319// Specialized format_arg for char arrays (string literals like "hello")
320template<fl::size N>
321void format_arg(StrStream& stream, const FormatSpec& spec, const char (&arg)[N]) {
322 format_arg(stream, spec, static_cast<const char*>(arg));
323}
324
325// Base case: no more arguments
326inline void format_impl(StrStream& stream, const char* format) {
327 while (*format) {
328 if (*format == '%') {
329 FormatSpec spec = parse_format_spec(format);
330 if (spec.type == '%') {
331 stream << "%";
332 } else {
333 // No argument for format specifier
334 stream << "<missing_arg>";
335 }
336 } else {
337 // Create a single-character string since StrStream treats char as number
338 char temp_str[2] = {*format, '\0'};
339 stream << temp_str;
340 ++format;
341 }
342 }
343}
344
345// Recursive case: process one argument and continue
346template<typename T, typename... Args>
347void format_impl(StrStream& stream, const char* format, const T& first, const Args&... rest) {
348 while (*format) {
349 if (*format == '%') {
350 FormatSpec spec = parse_format_spec(format);
351 if (spec.type == '%') {
352 stream << "%";
353 } else {
354 // Format the first argument and continue with the rest
355 format_arg(stream, spec, first);
356 format_impl(stream, format, rest...);
357 return;
358 }
359 } else {
360 // Create a single-character string since StrStream treats char as number
361 char temp_str[2] = {*format, '\0'};
362 stream << temp_str;
363 ++format;
364 }
365 }
366
367 // If we get here, there are unused arguments
368 // This is not an error in printf, so we just ignore them
369}
370
371}
372
392template<typename... Args>
393void printf(const char* format, const Args&... args) {
394 StrStream stream;
395 printf_detail::format_impl(stream, format, args...);
396 fl::print(stream.str().c_str());
397}
398
421template<typename... Args>
422int snprintf(char* buffer, fl::size size, const char* format, const Args&... args) {
423 // Handle null buffer or zero size
424 if (!buffer || size == 0) {
425 return 0;
426 }
427
428 // Format to internal string stream
429 StrStream stream;
430 printf_detail::format_impl(stream, format, args...);
431 fl::string result = stream.str();
432
433 // Get the formatted string length
434 fl::size formatted_len = result.size();
435
436 // Copy to buffer, ensuring null termination
437 fl::size copy_len = (formatted_len < size - 1) ? formatted_len : size - 1;
438
439 // Copy characters
440 for (fl::size i = 0; i < copy_len; ++i) {
441 buffer[i] = result[i];
442 }
443
444 // Null terminate
445 buffer[copy_len] = '\0';
446
447 // Return the number of characters actually written (excluding null terminator)
448 // This respects the buffer size limit instead of returning the full formatted length
449 return static_cast<int>(copy_len);
450}
451
467template<fl::size N, typename... Args>
468int sprintf(char (&buffer)[N], const char* format, const Args&... args) {
469 // Use the compile-time known buffer size for safety
470 return snprintf(buffer, N, format, args...);
471}
472
473} // namespace fl
const char * c_str() const
Definition str.h:326
const string & str() const
Definition strstream.h:51
Result type for promise operations.
static uint32_t t
Definition Luminova.h:54
Implements the FastLED namespace macros.
fl::string format_float(float value, int precision)
Definition printf.h:133
void format_impl(StrStream &stream, const char *format)
Definition printf.h:326
FormatSpec parse_format_spec(const char *&format)
Definition printf.h:95
void format_arg(StrStream &stream, const FormatSpec &spec, const T &arg)
Definition printf.h:225
fl::enable_if< fl::is_integral< T >::value, fl::string >::type to_hex(T value, bool uppercase)
Definition printf.h:180
void print(const char *str)
Definition io.cpp:49
int snprintf(char *buffer, fl::size size, const char *format, const Args &... args)
Snprintf-like formatting function that writes to a buffer.
Definition printf.h:422
void printf(const char *format, const Args &... args)
Printf-like formatting function that prints directly to the platform output.
Definition printf.h:393
int sprintf(char(&buffer)[N], const char *format, const Args &... args)
Sprintf-like formatting function that writes to a buffer.
Definition printf.h:468
IMPORTANT!
Definition crgb.h:20
corkscrew_args args
Definition old.h:150
static constexpr bool value
static constexpr bool value
static constexpr bool value
FormatSpec(char t, int prec)
Definition printf.h:90