1 ///////////////////////////////////////////////////////////////////////////////
2 // \author (c) Marco Paland (info@paland.com)
3 // 2014-2019, PALANDesign Hannover, Germany
4 //
5 // \license The MIT License (MIT)
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining a copy
8 // of this software and associated documentation files (the "Software"), to deal
9 // in the Software without restriction, including without limitation the rights
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 // copies of the Software, and to permit persons to whom the Software is
12 // furnished to do so, subject to the following conditions:
13 //
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
16 //
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23 // THE SOFTWARE.
24 //
25 // \brief Tiny printf, sprintf and (v)snprintf implementation, optimized for speed on
26 // embedded systems with a very limited resources. These routines are thread
27 // safe and reentrant!
28 // Use this instead of the bloated standard/newlib printf cause these use
29 // malloc for printf (and may not be thread safe).
30 //
31 ///////////////////////////////////////////////////////////////////////////////
32
33 #include <stdbool.h>
34 #include <stdint.h>
35 #include <stdio.h>
36
37 #include "pico.h"
38 #include "pico/printf.h"
39
40 // PICO_CONFIG: PICO_PRINTF_NTOA_BUFFER_SIZE, Define printf ntoa buffer size, min=0, max=128, default=32, group=pico_printf
41 // 'ntoa' conversion buffer size, this must be big enough to hold one converted
42 // numeric number including padded zeros (dynamically created on stack)
43 #ifndef PICO_PRINTF_NTOA_BUFFER_SIZE
44 #define PICO_PRINTF_NTOA_BUFFER_SIZE 32U
45 #endif
46
47 // PICO_CONFIG: PICO_PRINTF_FTOA_BUFFER_SIZE, Define printf ftoa buffer size, min=0, max=128, default=32, group=pico_printf
48 // 'ftoa' conversion buffer size, this must be big enough to hold one converted
49 // float number including padded zeros (dynamically created on stack)
50 #ifndef PICO_PRINTF_FTOA_BUFFER_SIZE
51 #define PICO_PRINTF_FTOA_BUFFER_SIZE 32U
52 #endif
53
54 // PICO_CONFIG: PICO_PRINTF_SUPPORT_FLOAT, Enable floating point printing, type=bool, default=1, group=pico_printf
55 // support for the floating point type (%f)
56 #ifndef PICO_PRINTF_SUPPORT_FLOAT
57 #define PICO_PRINTF_SUPPORT_FLOAT 1
58 #endif
59
60 // PICO_CONFIG: PICO_PRINTF_SUPPORT_EXPONENTIAL, Enable exponential floating point printing, type=bool, default=1, group=pico_printf
61 // support for exponential floating point notation (%e/%g)
62 #ifndef PICO_PRINTF_SUPPORT_EXPONENTIAL
63 #define PICO_PRINTF_SUPPORT_EXPONENTIAL 1
64 #endif
65
66 // PICO_CONFIG: PICO_PRINTF_DEFAULT_FLOAT_PRECISION, Define default floating point precision, min=1, max=16, default=6, group=pico_printf
67 #ifndef PICO_PRINTF_DEFAULT_FLOAT_PRECISION
68 #define PICO_PRINTF_DEFAULT_FLOAT_PRECISION 6U
69 #endif
70
71 // PICO_CONFIG: PICO_PRINTF_MAX_FLOAT, Define the largest float suitable to print with %f, min=1, max=1e9, default=1e9, group=pico_printf
72 #ifndef PICO_PRINTF_MAX_FLOAT
73 #define PICO_PRINTF_MAX_FLOAT 1e9
74 #endif
75
76 // PICO_CONFIG: PICO_PRINTF_SUPPORT_LONG_LONG, Enable support for long long types (%llu or %p), type=bool, default=1, group=pico_printf
77 #ifndef PICO_PRINTF_SUPPORT_LONG_LONG
78 #define PICO_PRINTF_SUPPORT_LONG_LONG 1
79 #endif
80
81 // PICO_CONFIG: PICO_PRINTF_SUPPORT_PTRDIFF_T, Enable support for the ptrdiff_t type (%t), type=bool, default=1, group=pico_printf
82 // ptrdiff_t is normally defined in <stddef.h> as long or long long type
83 #ifndef PICO_PRINTF_SUPPORT_PTRDIFF_T
84 #define PICO_PRINTF_SUPPORT_PTRDIFF_T 1
85 #endif
86
87 ///////////////////////////////////////////////////////////////////////////////
88
89 // internal flag definitions
90 #define FLAGS_ZEROPAD (1U << 0U)
91 #define FLAGS_LEFT (1U << 1U)
92 #define FLAGS_PLUS (1U << 2U)
93 #define FLAGS_SPACE (1U << 3U)
94 #define FLAGS_HASH (1U << 4U)
95 #define FLAGS_UPPERCASE (1U << 5U)
96 #define FLAGS_CHAR (1U << 6U)
97 #define FLAGS_SHORT (1U << 7U)
98 #define FLAGS_LONG (1U << 8U)
99 #define FLAGS_LONG_LONG (1U << 9U)
100 #define FLAGS_PRECISION (1U << 10U)
101 #define FLAGS_ADAPT_EXP (1U << 11U)
102
103 // import float.h for DBL_MAX
104 #if PICO_PRINTF_SUPPORT_FLOAT
105
106 #include <float.h>
107
108 #endif
109
110 // output function type
111 typedef void (*out_fct_type)(char character, void *buffer, size_t idx, size_t maxlen);
112
113 #if !PICO_PRINTF_ALWAYS_INCLUDED
114 // we don't have a way to specify a truly weak symbol reference (the linker will always include targets in a single link step,
115 // so we make a function pointer that is initialized on the first printf called... if printf is not included in the binary
116 // (or has never been called - we can't tell) then this will be null. the assumption is that if you are using printf
117 // you are likely to have printed something.
118 static int (*lazy_vsnprintf)(out_fct_type out, char *buffer, const size_t maxlen, const char *format, va_list va);
119 #endif
120
121 // wrapper (used as buffer) for output function type
122 typedef struct {
123 void (*fct)(char character, void *arg);
124 void *arg;
125 } out_fct_wrap_type;
126
127 // internal buffer output
_out_buffer(char character,void * buffer,size_t idx,size_t maxlen)128 static inline void _out_buffer(char character, void *buffer, size_t idx, size_t maxlen) {
129 if (idx < maxlen) {
130 ((char *) buffer)[idx] = character;
131 }
132 }
133
134 // internal null output
_out_null(char character,void * buffer,size_t idx,size_t maxlen)135 static inline void _out_null(char character, void *buffer, size_t idx, size_t maxlen) {
136 (void) character;
137 (void) buffer;
138 (void) idx;
139 (void) maxlen;
140 }
141
142 // internal output function wrapper
_out_fct(char character,void * buffer,size_t idx,size_t maxlen)143 static inline void _out_fct(char character, void *buffer, size_t idx, size_t maxlen) {
144 (void) idx;
145 (void) maxlen;
146 if (character) {
147 // buffer is the output fct pointer
148 ((out_fct_wrap_type *) buffer)->fct(character, ((out_fct_wrap_type *) buffer)->arg);
149 }
150 }
151
152
153 // internal secure strlen
154 // \return The length of the string (excluding the terminating 0) limited by 'maxsize'
_strnlen_s(const char * str,size_t maxsize)155 static inline unsigned int _strnlen_s(const char *str, size_t maxsize) {
156 const char *s;
157 for (s = str; *s && maxsize--; ++s);
158 return (unsigned int) (s - str);
159 }
160
161
162 // internal test if char is a digit (0-9)
163 // \return true if char is a digit
_is_digit(char ch)164 static inline bool _is_digit(char ch) {
165 return (ch >= '0') && (ch <= '9');
166 }
167
168
169 // internal ASCII string to unsigned int conversion
_atoi(const char ** str)170 static unsigned int _atoi(const char **str) {
171 unsigned int i = 0U;
172 while (_is_digit(**str)) {
173 i = i * 10U + (unsigned int) (*((*str)++) - '0');
174 }
175 return i;
176 }
177
178
179 // output the specified string in reverse, taking care of any zero-padding
_out_rev(out_fct_type out,char * buffer,size_t idx,size_t maxlen,const char * buf,size_t len,unsigned int width,unsigned int flags)180 static size_t _out_rev(out_fct_type out, char *buffer, size_t idx, size_t maxlen, const char *buf, size_t len,
181 unsigned int width, unsigned int flags) {
182 const size_t start_idx = idx;
183
184 // pad spaces up to given width
185 if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
186 for (size_t i = len; i < width; i++) {
187 out(' ', buffer, idx++, maxlen);
188 }
189 }
190
191 // reverse string
192 while (len) {
193 out(buf[--len], buffer, idx++, maxlen);
194 }
195
196 // append pad spaces up to given width
197 if (flags & FLAGS_LEFT) {
198 while (idx - start_idx < width) {
199 out(' ', buffer, idx++, maxlen);
200 }
201 }
202
203 return idx;
204 }
205
206
207 // internal itoa format
_ntoa_format(out_fct_type out,char * buffer,size_t idx,size_t maxlen,char * buf,size_t len,bool negative,unsigned int base,unsigned int prec,unsigned int width,unsigned int flags)208 static size_t _ntoa_format(out_fct_type out, char *buffer, size_t idx, size_t maxlen, char *buf, size_t len,
209 bool negative, unsigned int base, unsigned int prec, unsigned int width,
210 unsigned int flags) {
211 // pad leading zeros
212 if (!(flags & FLAGS_LEFT)) {
213 if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
214 width--;
215 }
216 while ((len < prec) && (len < PICO_PRINTF_NTOA_BUFFER_SIZE)) {
217 buf[len++] = '0';
218 }
219 while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PICO_PRINTF_NTOA_BUFFER_SIZE)) {
220 buf[len++] = '0';
221 }
222 }
223
224 // handle hash
225 if (flags & FLAGS_HASH) {
226 if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
227 len--;
228 if (len && (base == 16U)) {
229 len--;
230 }
231 }
232 if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PICO_PRINTF_NTOA_BUFFER_SIZE)) {
233 buf[len++] = 'x';
234 } else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PICO_PRINTF_NTOA_BUFFER_SIZE)) {
235 buf[len++] = 'X';
236 } else if ((base == 2U) && (len < PICO_PRINTF_NTOA_BUFFER_SIZE)) {
237 buf[len++] = 'b';
238 }
239 if (len < PICO_PRINTF_NTOA_BUFFER_SIZE) {
240 buf[len++] = '0';
241 }
242 }
243
244 if (len < PICO_PRINTF_NTOA_BUFFER_SIZE) {
245 if (negative) {
246 buf[len++] = '-';
247 } else if (flags & FLAGS_PLUS) {
248 buf[len++] = '+'; // ignore the space if the '+' exists
249 } else if (flags & FLAGS_SPACE) {
250 buf[len++] = ' ';
251 }
252 }
253
254 return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
255 }
256
257
258 // internal itoa for 'long' type
_ntoa_long(out_fct_type out,char * buffer,size_t idx,size_t maxlen,unsigned long value,bool negative,unsigned long base,unsigned int prec,unsigned int width,unsigned int flags)259 static size_t _ntoa_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long value, bool negative,
260 unsigned long base, unsigned int prec, unsigned int width, unsigned int flags) {
261 char buf[PICO_PRINTF_NTOA_BUFFER_SIZE];
262 size_t len = 0U;
263
264 // no hash for 0 values
265 if (!value) {
266 flags &= ~FLAGS_HASH;
267 }
268
269 // write if precision != 0 and value is != 0
270 if (!(flags & FLAGS_PRECISION) || value) {
271 do {
272 const char digit = (char) (value % base);
273 buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10);
274 value /= base;
275 } while (value && (len < PICO_PRINTF_NTOA_BUFFER_SIZE));
276 }
277
278 return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int) base, prec, width, flags);
279 }
280
281
282 // internal itoa for 'long long' type
283 #if PICO_PRINTF_SUPPORT_LONG_LONG
284
_ntoa_long_long(out_fct_type out,char * buffer,size_t idx,size_t maxlen,unsigned long long value,bool negative,unsigned long long base,unsigned int prec,unsigned int width,unsigned int flags)285 static size_t _ntoa_long_long(out_fct_type out, char *buffer, size_t idx, size_t maxlen, unsigned long long value,
286 bool negative, unsigned long long base, unsigned int prec, unsigned int width,
287 unsigned int flags) {
288 char buf[PICO_PRINTF_NTOA_BUFFER_SIZE];
289 size_t len = 0U;
290
291 // no hash for 0 values
292 if (!value) {
293 flags &= ~FLAGS_HASH;
294 }
295
296 // write if precision != 0 and value is != 0
297 if (!(flags & FLAGS_PRECISION) || value) {
298 do {
299 const char digit = (char) (value % base);
300 buf[len++] = (char)(digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10);
301 value /= base;
302 } while (value && (len < PICO_PRINTF_NTOA_BUFFER_SIZE));
303 }
304
305 return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int) base, prec, width, flags);
306 }
307
308 #endif // PICO_PRINTF_SUPPORT_LONG_LONG
309
310
311 #if PICO_PRINTF_SUPPORT_FLOAT
312
313 #if PICO_PRINTF_SUPPORT_EXPONENTIAL
314 // forward declaration so that _ftoa can switch to exp notation for values > PICO_PRINTF_MAX_FLOAT
315 static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec,
316 unsigned int width, unsigned int flags);
317 #endif
318
319 #define is_nan __builtin_isnan
320
321 // internal ftoa for fixed decimal floating point
_ftoa(out_fct_type out,char * buffer,size_t idx,size_t maxlen,double value,unsigned int prec,unsigned int width,unsigned int flags)322 static size_t _ftoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec,
323 unsigned int width, unsigned int flags) {
324 char buf[PICO_PRINTF_FTOA_BUFFER_SIZE];
325 size_t len = 0U;
326 double diff = 0.0;
327
328 // powers of 10
329 static const double pow10[] = {1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000};
330
331 // test for special values
332 if (is_nan(value))
333 return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags);
334 if (value < -DBL_MAX)
335 return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags);
336 if (value > DBL_MAX)
337 return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U,
338 width, flags);
339
340 // test for very large values
341 // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
342 if ((value > PICO_PRINTF_MAX_FLOAT) || (value < -PICO_PRINTF_MAX_FLOAT)) {
343 #if PICO_PRINTF_SUPPORT_EXPONENTIAL
344 return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
345 #else
346 return 0U;
347 #endif
348 }
349
350 // test for negative
351 bool negative = false;
352 if (value < 0) {
353 negative = true;
354 value = 0 - value;
355 }
356
357 // set default precision, if not set explicitly
358 if (!(flags & FLAGS_PRECISION)) {
359 prec = PICO_PRINTF_DEFAULT_FLOAT_PRECISION;
360 }
361 // limit precision to 9, cause a prec >= 10 can lead to overflow errors
362 while ((len < PICO_PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
363 buf[len++] = '0';
364 prec--;
365 }
366
367 int whole = (int) value;
368 double tmp = (value - whole) * pow10[prec];
369 unsigned long frac = (unsigned long) tmp;
370 diff = tmp - frac;
371
372 if (diff > 0.5) {
373 ++frac;
374 // handle rollover, e.g. case 0.99 with prec 1 is 1.0
375 if (frac >= pow10[prec]) {
376 frac = 0;
377 ++whole;
378 }
379 } else if (diff < 0.5) {
380 } else if ((frac == 0U) || (frac & 1U)) {
381 // if halfway, round up if odd OR if last digit is 0
382 ++frac;
383 }
384
385 if (prec == 0U) {
386 diff = value - (double) whole;
387 if (!((diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
388 // exactly 0.5 and ODD, then round up
389 // 1.5 -> 2, but 2.5 -> 2
390 ++whole;
391 }
392 } else {
393 unsigned int count = prec;
394 // now do fractional part, as an unsigned number
395 while (len < PICO_PRINTF_FTOA_BUFFER_SIZE) {
396 --count;
397 buf[len++] = (char) (48U + (frac % 10U));
398 if (!(frac /= 10U)) {
399 break;
400 }
401 }
402 // add extra 0s
403 while ((len < PICO_PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
404 buf[len++] = '0';
405 }
406 if (len < PICO_PRINTF_FTOA_BUFFER_SIZE) {
407 // add decimal
408 buf[len++] = '.';
409 }
410 }
411
412 // do whole part, number is reversed
413 while (len < PICO_PRINTF_FTOA_BUFFER_SIZE) {
414 buf[len++] = (char) (48 + (whole % 10));
415 if (!(whole /= 10)) {
416 break;
417 }
418 }
419
420 // pad leading zeros
421 if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
422 if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
423 width--;
424 }
425 while ((len < width) && (len < PICO_PRINTF_FTOA_BUFFER_SIZE)) {
426 buf[len++] = '0';
427 }
428 }
429
430 if (len < PICO_PRINTF_FTOA_BUFFER_SIZE) {
431 if (negative) {
432 buf[len++] = '-';
433 } else if (flags & FLAGS_PLUS) {
434 buf[len++] = '+'; // ignore the space if the '+' exists
435 } else if (flags & FLAGS_SPACE) {
436 buf[len++] = ' ';
437 }
438 }
439
440 return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
441 }
442
443
444 #if PICO_PRINTF_SUPPORT_EXPONENTIAL
445
446 // internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
_etoa(out_fct_type out,char * buffer,size_t idx,size_t maxlen,double value,unsigned int prec,unsigned int width,unsigned int flags)447 static size_t _etoa(out_fct_type out, char *buffer, size_t idx, size_t maxlen, double value, unsigned int prec,
448 unsigned int width, unsigned int flags) {
449 // check for NaN and special values
450 if (is_nan(value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
451 return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);
452 }
453
454 // determine the sign
455 const bool negative = value < 0;
456 if (negative) {
457 value = -value;
458 }
459
460 // default precision
461 if (!(flags & FLAGS_PRECISION)) {
462 prec = PICO_PRINTF_DEFAULT_FLOAT_PRECISION;
463 }
464
465 // determine the decimal exponent
466 // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
467 union {
468 uint64_t U;
469 double F;
470 } conv;
471
472 conv.F = value;
473 int expval;
474 if (conv.U) {
475 int exp2 = (int) ((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2
476 conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
477 // now approximate log10 from the log2 integer part and an expansion of ln around 1.5
478 expval = (int) (0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
479 // now we want to compute 10^expval but we want to be sure it won't overflow
480 exp2 = (int) (expval * 3.321928094887362 + 0.5);
481 const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
482 const double z2 = z * z;
483 conv.U = (uint64_t) (exp2 + 1023) << 52U;
484 // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
485 conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
486 // correct for rounding errors
487 if (value < conv.F) {
488 expval--;
489 conv.F /= 10;
490 }
491 } else {
492 expval = 0;
493 }
494
495 // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
496 unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
497
498 // in "%g" mode, "prec" is the number of *significant figures* not decimals
499 if (flags & FLAGS_ADAPT_EXP) {
500 // do we want to fall-back to "%f" mode?
501 if ((conv.U == 0) || ((value >= 1e-4) && (value < 1e6))) {
502 if ((int) prec > expval) {
503 prec = (unsigned) ((int) prec - expval - 1);
504 } else {
505 prec = 0;
506 }
507 flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
508 // no characters in exponent
509 minwidth = 0U;
510 expval = 0;
511 } else {
512 // we use one sigfig for the whole part
513 if ((prec > 0) && (flags & FLAGS_PRECISION)) {
514 --prec;
515 }
516 }
517 }
518
519 // will everything fit?
520 unsigned int fwidth = width;
521 if (width > minwidth) {
522 // we didn't fall-back so subtract the characters required for the exponent
523 fwidth -= minwidth;
524 } else {
525 // not enough characters, so go back to default sizing
526 fwidth = 0U;
527 }
528 if ((flags & FLAGS_LEFT) && minwidth) {
529 // if we're padding on the right, DON'T pad the floating part
530 fwidth = 0U;
531 }
532
533 // rescale the float value
534 if (expval) {
535 value /= conv.F;
536 }
537
538 // output the floating part
539 const size_t start_idx = idx;
540 idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
541
542 // output the exponent part
543 if (minwidth) {
544 // output the exponential symbol
545 out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);
546 // output the exponent value
547 idx = _ntoa_long(out, buffer, idx, maxlen, (uint)((expval < 0) ? -expval : expval), expval < 0, 10, 0, minwidth - 1,
548 FLAGS_ZEROPAD | FLAGS_PLUS);
549 // might need to right-pad spaces
550 if (flags & FLAGS_LEFT) {
551 while (idx - start_idx < width) out(' ', buffer, idx++, maxlen);
552 }
553 }
554 return idx;
555 }
556
557 #endif // PICO_PRINTF_SUPPORT_EXPONENTIAL
558 #endif // PICO_PRINTF_SUPPORT_FLOAT
559
560 // internal vsnprintf
_vsnprintf(out_fct_type out,char * buffer,const size_t maxlen,const char * format,va_list va)561 static int _vsnprintf(out_fct_type out, char *buffer, const size_t maxlen, const char *format, va_list va) {
562 #if !PICO_PRINTF_ALWAYS_INCLUDED
563 lazy_vsnprintf = _vsnprintf;
564 #endif
565 unsigned int flags, width, precision, n;
566 size_t idx = 0U;
567
568 if (!buffer) {
569 // use null output function
570 out = _out_null;
571 }
572
573 while (*format) {
574 // format specifier? %[flags][width][.precision][length]
575 if (*format != '%') {
576 // no
577 out(*format, buffer, idx++, maxlen);
578 format++;
579 continue;
580 } else {
581 // yes, evaluate it
582 format++;
583 }
584
585 // evaluate flags
586 flags = 0U;
587 do {
588 switch (*format) {
589 case '0':
590 flags |= FLAGS_ZEROPAD;
591 format++;
592 n = 1U;
593 break;
594 case '-':
595 flags |= FLAGS_LEFT;
596 format++;
597 n = 1U;
598 break;
599 case '+':
600 flags |= FLAGS_PLUS;
601 format++;
602 n = 1U;
603 break;
604 case ' ':
605 flags |= FLAGS_SPACE;
606 format++;
607 n = 1U;
608 break;
609 case '#':
610 flags |= FLAGS_HASH;
611 format++;
612 n = 1U;
613 break;
614 default :
615 n = 0U;
616 break;
617 }
618 } while (n);
619
620 // evaluate width field
621 width = 0U;
622 if (_is_digit(*format)) {
623 width = _atoi(&format);
624 } else if (*format == '*') {
625 const int w = va_arg(va, int);
626 if (w < 0) {
627 flags |= FLAGS_LEFT; // reverse padding
628 width = (unsigned int) -w;
629 } else {
630 width = (unsigned int) w;
631 }
632 format++;
633 }
634
635 // evaluate precision field
636 precision = 0U;
637 if (*format == '.') {
638 flags |= FLAGS_PRECISION;
639 format++;
640 if (_is_digit(*format)) {
641 precision = _atoi(&format);
642 } else if (*format == '*') {
643 const int prec = (int) va_arg(va, int);
644 precision = prec > 0 ? (unsigned int) prec : 0U;
645 format++;
646 }
647 }
648
649 // evaluate length field
650 switch (*format) {
651 case 'l' :
652 flags |= FLAGS_LONG;
653 format++;
654 if (*format == 'l') {
655 flags |= FLAGS_LONG_LONG;
656 format++;
657 }
658 break;
659 case 'h' :
660 flags |= FLAGS_SHORT;
661 format++;
662 if (*format == 'h') {
663 flags |= FLAGS_CHAR;
664 format++;
665 }
666 break;
667 #if PICO_PRINTF_SUPPORT_PTRDIFF_T
668 case 't' :
669 flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
670 format++;
671 break;
672 #endif
673 case 'j' :
674 flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
675 format++;
676 break;
677 case 'z' :
678 flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
679 format++;
680 break;
681 default :
682 break;
683 }
684
685 // evaluate specifier
686 switch (*format) {
687 case 'd' :
688 case 'i' :
689 case 'u' :
690 case 'x' :
691 case 'X' :
692 case 'o' :
693 case 'b' : {
694 // set the base
695 unsigned int base;
696 if (*format == 'x' || *format == 'X') {
697 base = 16U;
698 } else if (*format == 'o') {
699 base = 8U;
700 } else if (*format == 'b') {
701 base = 2U;
702 } else {
703 base = 10U;
704 flags &= ~FLAGS_HASH; // no hash for dec format
705 }
706 // uppercase
707 if (*format == 'X') {
708 flags |= FLAGS_UPPERCASE;
709 }
710
711 // no plus or space flag for u, x, X, o, b
712 if ((*format != 'i') && (*format != 'd')) {
713 flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
714 }
715
716 // ignore '0' flag when precision is given
717 if (flags & FLAGS_PRECISION) {
718 flags &= ~FLAGS_ZEROPAD;
719 }
720
721 // convert the integer
722 if ((*format == 'i') || (*format == 'd')) {
723 // signed
724 if (flags & FLAGS_LONG_LONG) {
725 #if PICO_PRINTF_SUPPORT_LONG_LONG
726 const long long value = va_arg(va, long long);
727 idx = _ntoa_long_long(out, buffer, idx, maxlen,
728 (unsigned long long) (value > 0 ? value : 0 - value), value < 0, base,
729 precision, width, flags);
730 #endif
731 } else if (flags & FLAGS_LONG) {
732 const long value = va_arg(va, long);
733 idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long) (value > 0 ? value : 0 - value),
734 value < 0, base, precision, width, flags);
735 } else {
736 const int value = (flags & FLAGS_CHAR) ? (char) va_arg(va, int) : (flags & FLAGS_SHORT)
737 ? (short int) va_arg(va, int)
738 : va_arg(va, int);
739 idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int) (value > 0 ? value : 0 - value),
740 value < 0, base, precision, width, flags);
741 }
742 } else {
743 // unsigned
744 if (flags & FLAGS_LONG_LONG) {
745 #if PICO_PRINTF_SUPPORT_LONG_LONG
746 idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base,
747 precision, width, flags);
748 #endif
749 } else if (flags & FLAGS_LONG) {
750 idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision,
751 width, flags);
752 } else {
753 const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char) va_arg(va, unsigned int)
754 : (flags & FLAGS_SHORT)
755 ? (unsigned short int) va_arg(va,
756 unsigned int)
757 : va_arg(va, unsigned int);
758 idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
759 }
760 }
761 format++;
762 break;
763 }
764 case 'f' :
765 case 'F' :
766 #if PICO_PRINTF_SUPPORT_FLOAT
767 if (*format == 'F') flags |= FLAGS_UPPERCASE;
768 idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
769 #else
770 for(int i=0;i<2;i++) out('?', buffer, idx++, maxlen);
771 va_arg(va, double);
772 #endif
773 format++;
774 break;
775 case 'e':
776 case 'E':
777 case 'g':
778 case 'G':
779 #if PICO_PRINTF_SUPPORT_FLOAT && PICO_PRINTF_SUPPORT_EXPONENTIAL
780 if ((*format == 'g') || (*format == 'G')) flags |= FLAGS_ADAPT_EXP;
781 if ((*format == 'E') || (*format == 'G')) flags |= FLAGS_UPPERCASE;
782 idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
783 #else
784 for(int i=0;i<2;i++) out('?', buffer, idx++, maxlen);
785 va_arg(va, double);
786 #endif
787 format++;
788 break;
789 case 'c' : {
790 unsigned int l = 1U;
791 // pre padding
792 if (!(flags & FLAGS_LEFT)) {
793 while (l++ < width) {
794 out(' ', buffer, idx++, maxlen);
795 }
796 }
797 // char output
798 out((char) va_arg(va, int), buffer, idx++, maxlen);
799 // post padding
800 if (flags & FLAGS_LEFT) {
801 while (l++ < width) {
802 out(' ', buffer, idx++, maxlen);
803 }
804 }
805 format++;
806 break;
807 }
808
809 case 's' : {
810 const char *p = va_arg(va, char*);
811 unsigned int l = _strnlen_s(p, precision ? precision : (size_t) -1);
812 // pre padding
813 if (flags & FLAGS_PRECISION) {
814 l = (l < precision ? l : precision);
815 }
816 if (!(flags & FLAGS_LEFT)) {
817 while (l++ < width) {
818 out(' ', buffer, idx++, maxlen);
819 }
820 }
821 // string output
822 while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
823 out(*(p++), buffer, idx++, maxlen);
824 }
825 // post padding
826 if (flags & FLAGS_LEFT) {
827 while (l++ < width) {
828 out(' ', buffer, idx++, maxlen);
829 }
830 }
831 format++;
832 break;
833 }
834
835 case 'p' : {
836 width = sizeof(void *) * 2U;
837 flags |= FLAGS_ZEROPAD | FLAGS_UPPERCASE;
838 #if PICO_PRINTF_SUPPORT_LONG_LONG
839 const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
840 if (is_ll) {
841 idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t) va_arg(va, void*), false, 16U,
842 precision, width, flags);
843 } else {
844 #endif
845 idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long) ((uintptr_t) va_arg(va, void*)), false,
846 16U, precision, width, flags);
847 #if PICO_PRINTF_SUPPORT_LONG_LONG
848 }
849 #endif
850 format++;
851 break;
852 }
853
854 case '%' :
855 out('%', buffer, idx++, maxlen);
856 format++;
857 break;
858
859 default :
860 out(*format, buffer, idx++, maxlen);
861 format++;
862 break;
863 }
864 }
865
866 // termination
867 out((char) 0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
868
869 // return written chars without terminating \0
870 return (int) idx;
871 }
872
873
874 ///////////////////////////////////////////////////////////////////////////////
875
WRAPPER_FUNC(sprintf)876 int WRAPPER_FUNC(sprintf)(char *buffer, const char *format, ...) {
877 va_list va;
878 va_start(va, format);
879 const int ret = _vsnprintf(_out_buffer, buffer, (size_t) -1, format, va);
880 va_end(va);
881 return ret;
882 }
883
WRAPPER_FUNC(snprintf)884 int WRAPPER_FUNC(snprintf)(char *buffer, size_t count, const char *format, ...) {
885 va_list va;
886 va_start(va, format);
887 const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
888 va_end(va);
889 return ret;
890 }
891
WRAPPER_FUNC(vsnprintf)892 int WRAPPER_FUNC(vsnprintf)(char *buffer, size_t count, const char *format, va_list va) {
893 return _vsnprintf(_out_buffer, buffer, count, format, va);
894 }
895
vfctprintf(void (* out)(char character,void * arg),void * arg,const char * format,va_list va)896 int vfctprintf(void (*out)(char character, void *arg), void *arg, const char *format, va_list va) {
897 const out_fct_wrap_type out_fct_wrap = {out, arg};
898 return _vsnprintf(_out_fct, (char *) (uintptr_t) &out_fct_wrap, (size_t) -1, format, va);
899 }
900
901 #if LIB_PICO_PRINTF_PICO
902 #if !PICO_PRINTF_ALWAYS_INCLUDED
903 /**
904 * Output a character to a custom device like UART, used by the printf() function
905 * This function is declared here only. You have to write your custom implementation somewhere
906 * \param character Character to output
907 */
_putchar(char character)908 static void _putchar(char character) {
909 putchar(character);
910 }
911
912 // internal _putchar wrapper
_out_char(char character,void * buffer,size_t idx,size_t maxlen)913 static inline void _out_char(char character, void *buffer, size_t idx, size_t maxlen) {
914 (void) buffer;
915 (void) idx;
916 (void) maxlen;
917 if (character) {
918 _putchar(character);
919 }
920 }
921
weak_raw_printf(const char * fmt,...)922 bool weak_raw_printf(const char *fmt, ...) {
923 va_list va;
924 va_start(va, fmt);
925 bool rc = weak_raw_vprintf(fmt, va);
926 va_end(va);
927 return rc;
928 }
929
weak_raw_vprintf(const char * fmt,va_list args)930 bool weak_raw_vprintf(const char *fmt, va_list args) {
931 if (lazy_vsnprintf) {
932 char buffer[1];
933 lazy_vsnprintf(_out_char, buffer, (size_t) -1, fmt, args);
934 return true;
935 } else {
936 puts(fmt);
937 return false;
938 }
939 }
940 #endif
941 #endif
942