Sapphire SoC DS Sapphire SoC UG Sapphire HP SoC DS Sapphire HP SoC UG RISC-V Embedded IDE UG Board Support Package
Loading...
Searching...
No Matches
print_full.h
Go to the documentation of this file.
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// Github: https://github.com/mpaland
33
34#ifndef _PRINT_FULL_H_
35#define _PRINT_FULL_H_
36
37#include <stdarg.h>
38#include <stddef.h>
39#include <stdbool.h>
40#include <stdint.h>
41#include "bsp.h"
42#include "soc.h"
43
44#if (ENABLE_BSP_PRINTF_FULL || ENABLE_SEMIHOSTING_PRINT == 1)
45#ifdef __cplusplus
46extern "C" {
47#endif
48
49#if (!ENABLE_BSP_PRINTF)
50 static void _putchar(char character){
51 #if (ENABLE_SEMIHOSTING_PRINT == 1)
52 sh_writec(character);
53 #else
54 bsp_putChar(character);
55 #endif // (ENABLE_SEMIHOSTING_PRINT == 1)
56 }
57
58 static void _putchar_s(char *p)
59 {
60 #if (ENABLE_SEMIHOSTING_PRINT == 1)
61 sh_write0(p);
62 #else
63 while (*p)
64 _putchar(*(p++));
65 #endif // (ENABLE_SEMIHOSTING_PRINT == 1)
66 }
67
68 static void bsp_printHex(uint32_t val)
69 {
70 uint32_t digits;
71 digits =8;
72
73 for (int i = (4*digits)-4; i >= 0; i -= 4) {
74 _putchar("0123456789ABCDEF"[(val >> i) % 16]);
75 }
76 }
77
78 static void bsp_printf_c(int c)
79 {
80 _putchar(c);
81 }
82
83 static void bsp_printf_s(char *p)
84 {
85 _putchar_s(p);
86 }
87
88#endif
89
90//void _putchar(char character);
99#define bsp_printf_full printf_
100//int printf_(const char* format, ...);
101
102
110#define bsp_sprintf_full sprintf_
111//int sprintf_(char* buffer, const char* format, ...);
112
113
124#define bsp_snprintf_full snprintf_
125#define bsp_efx_vsnprintf_full vsnprintf_
126//int snprintf_(char* buffer, size_t count, const char* format, ...);
127//int vsnprintf_(char* buffer, size_t count, const char* format, va_list va);
128
129
136#define bsp_vprintf_full vprintf_
137//int vprintf_(const char* format, va_list va);
138
139
148#define bsp_fctprintf_full fctprintf
149//int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...);
150
151
152// define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
153// printf_config.h header file
154// default: undefined
155#ifdef PRINTF_INCLUDE_CONFIG_H
156#include "printf_config.h"
157#endif
158
159
160// 'ntoa' conversion buffer size, this must be big enough to hold one converted
161// numeric number including padded zeros (dynamically created on stack)
162// default: 32 byte
163#ifndef PRINTF_NTOA_BUFFER_SIZE
164#define PRINTF_NTOA_BUFFER_SIZE 32U
165#endif
166
167// 'ftoa' conversion buffer size, this must be big enough to hold one converted
168// float number including padded zeros (dynamically created on stack)
169// default: 32 byte
170#ifndef PRINTF_FTOA_BUFFER_SIZE
171#define PRINTF_FTOA_BUFFER_SIZE 32U
172#endif
173
174// support for the floating point type (%f)
175// default: activated
176#ifndef PRINTF_DISABLE_SUPPORT_FLOAT
177#define PRINTF_SUPPORT_FLOAT
178#endif
179
180// support for exponential floating point notation (%e/%g)
181// default: activated
182#ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
183#define PRINTF_SUPPORT_EXPONENTIAL
184#endif
185
186// define the default floating point precision
187// default: 4 digits
188#ifndef PRINTF_DEFAULT_FLOAT_PRECISION
189#define PRINTF_DEFAULT_FLOAT_PRECISION 4U
190#endif
191
192// define the largest float suitable to print with %f
193// default: 1e9
194#ifndef PRINTF_MAX_FLOAT
195#define PRINTF_MAX_FLOAT 1e9
196#endif
197
198// support for the long long types (%llu or %p)
199// default: activated
200#ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
201#define PRINTF_SUPPORT_LONG_LONG
202#endif
203
204// support for the ptrdiff_t type (%t)
205// ptrdiff_t is normally defined in <stddef.h> as long or long long type
206// default: activated
207#ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
208#define PRINTF_SUPPORT_PTRDIFF_T
209#endif
210
211// Define the max string buffer size can be allocate when using the semihosting printing
212#ifndef MAX_STRING_BUFFER_SIZE
213#define MAX_STRING_BUFFER_SIZE 100
214#endif
215
217
218// internal flag definitions
219#define FLAGS_ZEROPAD (1U << 0U)
220#define FLAGS_LEFT (1U << 1U)
221#define FLAGS_PLUS (1U << 2U)
222#define FLAGS_SPACE (1U << 3U)
223#define FLAGS_HASH (1U << 4U)
224#define FLAGS_UPPERCASE (1U << 5U)
225#define FLAGS_CHAR (1U << 6U)
226#define FLAGS_SHORT (1U << 7U)
227#define FLAGS_LONG (1U << 8U)
228#define FLAGS_LONG_LONG (1U << 9U)
229#define FLAGS_PRECISION (1U << 10U)
230#define FLAGS_ADAPT_EXP (1U << 11U)
231
232
233// import float.h for DBL_MAX
234#if defined(PRINTF_SUPPORT_FLOAT)
235#include <float.h>
236#endif
237
238// output function type
239typedef void (*out_fct_type)(char character, void* buffer, size_t idx, size_t maxlen);
240
241
242// wrapper (used as buffer) for output function type
243typedef struct {
244 void (*fct)(char character, void* arg);
245 void* arg;
247
248
249// internal buffer output
250static inline void _out_buffer(char character, void* buffer, size_t idx, size_t maxlen)
251{
252 if (idx < maxlen) {
253 ((char*)buffer)[idx] = character;
254 }
255}
256
257
258// internal null output
259static inline void _out_null(char character, void* buffer, size_t idx, size_t maxlen)
260{
261 (void)character; (void)buffer; (void)idx; (void)maxlen;
262}
263
264
265// internal _putchar wrapper
266static inline void _out_char(char character, void* buffer, size_t idx, size_t maxlen)
267{
268 (void)buffer; (void)idx; (void)maxlen;
269 if (character) {
270 _putchar(character);
271 }
272}
273
274
275
276// internal output function wrapper
277static inline void _out_fct(char character, void* buffer, size_t idx, size_t maxlen)
278{
279 (void)idx; (void)maxlen;
280 if (character) {
281 // buffer is the output fct pointer
282 ((out_fct_wrap_type*)buffer)->fct(character, ((out_fct_wrap_type*)buffer)->arg);
283 }
284}
285
286// internal secure strlen
287// \return The length of the string (excluding the terminating 0) limited by 'maxsize'
288static inline unsigned int _strnlen_s(const char* str, size_t maxsize)
289{
290 const char* s;
291 for (s = str; *s && maxsize--; ++s);
292 return (unsigned int)(s - str);
293}
294
295
296// internal test if char is a digit (0-9)
297// \return true if char is a digit
298static inline bool _is_digit(char ch)
299{
300 return (ch >= '0') && (ch <= '9');
301}
302
303
304// internal ASCII string to unsigned int conversion
305static unsigned int _atoi(const char** str)
306{
307 unsigned int i = 0U;
308 while (_is_digit(**str)) {
309 i = i * 10U + (unsigned int)(*((*str)++) - '0');
310 }
311 return i;
312}
313
314// output the specified string in reverse, taking care of any zero-padding
315static size_t _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)
316{
317 const size_t start_idx = idx;
318
319 // pad spaces up to given width
320 if (!(flags & FLAGS_LEFT) && !(flags & FLAGS_ZEROPAD)) {
321 for (size_t i = len; i < width; i++) {
322 out(' ', buffer, idx++, maxlen);
323 }
324 }
325
326 // reverse string
327 while (len) {
328 out(buf[--len], buffer, idx++, maxlen);
329 }
330
331 // append pad spaces up to given width
332 if (flags & FLAGS_LEFT) {
333 while (idx - start_idx < width) {
334 out(' ', buffer, idx++, maxlen);
335 }
336 }
337
338 return idx;
339}
340
341
342// internal itoa format
343static size_t _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)
344{
345 // pad leading zeros
346 if (!(flags & FLAGS_LEFT)) {
347 if (width && (flags & FLAGS_ZEROPAD) && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
348 width--;
349 }
350 while ((len < prec) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
351 buf[len++] = '0';
352 }
353 while ((flags & FLAGS_ZEROPAD) && (len < width) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
354 buf[len++] = '0';
355 }
356 }
357
358 // handle hash
359 if (flags & FLAGS_HASH) {
360 if (!(flags & FLAGS_PRECISION) && len && ((len == prec) || (len == width))) {
361 len--;
362 if (len && (base == 16U)) {
363 len--;
364 }
365 }
366 if ((base == 16U) && !(flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
367 buf[len++] = 'x';
368 }
369 else if ((base == 16U) && (flags & FLAGS_UPPERCASE) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
370 buf[len++] = 'X';
371 }
372 else if ((base == 2U) && (len < PRINTF_NTOA_BUFFER_SIZE)) {
373 buf[len++] = 'b';
374 }
375 if (len < PRINTF_NTOA_BUFFER_SIZE) {
376 buf[len++] = '0';
377 }
378 }
379
380 if (len < PRINTF_NTOA_BUFFER_SIZE) {
381 if (negative) {
382 buf[len++] = '-';
383 }
384 else if (flags & FLAGS_PLUS) {
385 buf[len++] = '+'; // ignore the space if the '+' exists
386 }
387 else if (flags & FLAGS_SPACE) {
388 buf[len++] = ' ';
389 }
390 }
391
392 return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
393}
394
395
396// internal itoa for 'long' type
397static size_t _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)
398{
399 char buf[PRINTF_NTOA_BUFFER_SIZE];
400 size_t len = 0U;
401
402 // no hash for 0 values
403 if (!value) {
404 flags &= ~FLAGS_HASH;
405 }
406
407 // write if precision != 0 and value is != 0
408 if (!(flags & FLAGS_PRECISION) || value) {
409 do {
410 const char digit = (char)(value % base);
411 buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
412 value /= base;
413 } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
414 }
415
416 return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
417}
418
419
420// internal itoa for 'long long' type
421#if defined(PRINTF_SUPPORT_LONG_LONG)
422static size_t _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)
423{
424 char buf[PRINTF_NTOA_BUFFER_SIZE];
425 size_t len = 0U;
426
427 // no hash for 0 values
428 if (!value) {
429 flags &= ~FLAGS_HASH;
430 }
431
432 // write if precision != 0 and value is != 0
433 if (!(flags & FLAGS_PRECISION) || value) {
434 do {
435 const char digit = (char)(value % base);
436 buf[len++] = digit < 10 ? '0' + digit : (flags & FLAGS_UPPERCASE ? 'A' : 'a') + digit - 10;
437 value /= base;
438 } while (value && (len < PRINTF_NTOA_BUFFER_SIZE));
439 }
440
441 return _ntoa_format(out, buffer, idx, maxlen, buf, len, negative, (unsigned int)base, prec, width, flags);
442}
443#endif // PRINTF_SUPPORT_LONG_LONG
444
445
446#if defined(PRINTF_SUPPORT_FLOAT)
447
448#if defined(PRINTF_SUPPORT_EXPONENTIAL)
449// forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
450static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags);
451#endif
452
453
454// internal ftoa for fixed decimal floating point
455static size_t _ftoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
456{
457 char buf[PRINTF_FTOA_BUFFER_SIZE];
458 size_t len = 0U;
459 double diff = 0.0;
460
461 // powers of 10
462 static const double pow10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
463
464 // test for special values
465 if (value != value)
466 return _out_rev(out, buffer, idx, maxlen, "nan", 3, width, flags);
467 if (value < -DBL_MAX)
468 return _out_rev(out, buffer, idx, maxlen, "fni-", 4, width, flags);
469 if (value > DBL_MAX)
470 return _out_rev(out, buffer, idx, maxlen, (flags & FLAGS_PLUS) ? "fni+" : "fni", (flags & FLAGS_PLUS) ? 4U : 3U, width, flags);
471
472 // test for very large values
473 // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
474 if ((value > PRINTF_MAX_FLOAT) || (value < -PRINTF_MAX_FLOAT)) {
475#if defined(PRINTF_SUPPORT_EXPONENTIAL)
476 return _etoa(out, buffer, idx, maxlen, value, prec, width, flags);
477#else
478 return 0U;
479#endif
480 }
481
482 // test for negative
483 bool negative = false;
484 if (value < 0) {
485 negative = true;
486 value = 0 - value;
487 }
488
489 // set default precision, if not set explicitly
490 if (!(flags & FLAGS_PRECISION)) {
491 prec = PRINTF_DEFAULT_FLOAT_PRECISION;
492 }
493 // limit precision to 9, cause a prec >= 10 can lead to overflow errors
494 while ((len < PRINTF_FTOA_BUFFER_SIZE) && (prec > 9U)) {
495 buf[len++] = '0';
496 prec--;
497 }
498
499 int whole = (int)value;
500 double tmp = (value - whole) * pow10[prec];
501 unsigned long frac = (unsigned long)tmp;
502 diff = tmp - frac;
503
504 if (diff > 0.5) {
505 ++frac;
506 // handle rollover, e.g. case 0.99 with prec 1 is 1.0
507 if (frac >= pow10[prec]) {
508 frac = 0;
509 ++whole;
510 }
511 }
512 else if (diff < 0.5) {
513 }
514 else if ((frac == 0U) || (frac & 1U)) {
515 // if halfway, round up if odd OR if last digit is 0
516 ++frac;
517 }
518
519 if (prec == 0U) {
520 diff = value - (double)whole;
521 if ((!(diff < 0.5) || (diff > 0.5)) && (whole & 1)) {
522 // exactly 0.5 and ODD, then round up
523 // 1.5 -> 2, but 2.5 -> 2
524 ++whole;
525 }
526 }
527 else {
528 unsigned int count = prec;
529 // now do fractional part, as an unsigned number
530 while (len < PRINTF_FTOA_BUFFER_SIZE) {
531 --count;
532 buf[len++] = (char)(48U + (frac % 10U));
533 if (!(frac /= 10U)) {
534 break;
535 }
536 }
537 // add extra 0s
538 while ((len < PRINTF_FTOA_BUFFER_SIZE) && (count-- > 0U)) {
539 buf[len++] = '0';
540 }
541 if (len < PRINTF_FTOA_BUFFER_SIZE) {
542 // add decimal
543 buf[len++] = '.';
544 }
545 }
546
547 // do whole part, number is reversed
548 while (len < PRINTF_FTOA_BUFFER_SIZE) {
549 buf[len++] = (char)(48 + (whole % 10));
550 if (!(whole /= 10)) {
551 break;
552 }
553 }
554
555 // pad leading zeros
556 if (!(flags & FLAGS_LEFT) && (flags & FLAGS_ZEROPAD)) {
557 if (width && (negative || (flags & (FLAGS_PLUS | FLAGS_SPACE)))) {
558 width--;
559 }
560 while ((len < width) && (len < PRINTF_FTOA_BUFFER_SIZE)) {
561 buf[len++] = '0';
562 }
563 }
564
565 if (len < PRINTF_FTOA_BUFFER_SIZE) {
566 if (negative) {
567 buf[len++] = '-';
568 }
569 else if (flags & FLAGS_PLUS) {
570 buf[len++] = '+'; // ignore the space if the '+' exists
571 }
572 else if (flags & FLAGS_SPACE) {
573 buf[len++] = ' ';
574 }
575 }
576
577 return _out_rev(out, buffer, idx, maxlen, buf, len, width, flags);
578}
579
580
581#if defined(PRINTF_SUPPORT_EXPONENTIAL)
582// internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
583static size_t _etoa(out_fct_type out, char* buffer, size_t idx, size_t maxlen, double value, unsigned int prec, unsigned int width, unsigned int flags)
584{
585 // check for NaN and special values
586 if ((value != value) || (value > DBL_MAX) || (value < -DBL_MAX)) {
587 return _ftoa(out, buffer, idx, maxlen, value, prec, width, flags);
588 }
589
590 // determine the sign
591 const bool negative = value < 0;
592 if (negative) {
593 value = -value;
594 }
595
596 // default precision
597 if (!(flags & FLAGS_PRECISION)) {
598 prec = PRINTF_DEFAULT_FLOAT_PRECISION;
599 }
600
601 // determine the decimal exponent
602 // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
603 union {
604 uint64_t U;
605 double F;
606 } conv;
607
608 conv.F = value;
609 int exp2 = (int)((conv.U >> 52U) & 0x07FFU) - 1023; // effectively log2
610 conv.U = (conv.U & ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
611 // now approximate log10 from the log2 integer part and an expansion of ln around 1.5
612 int expval = (int)(0.1760912590558 + exp2 * 0.301029995663981 + (conv.F - 1.5) * 0.289529654602168);
613 // now we want to compute 10^expval but we want to be sure it won't overflow
614 exp2 = (int)(expval * 3.321928094887362 + 0.5);
615 const double z = expval * 2.302585092994046 - exp2 * 0.6931471805599453;
616 const double z2 = z * z;
617 conv.U = (uint64_t)(exp2 + 1023) << 52U;
618 // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
619 conv.F *= 1 + 2 * z / (2 - z + (z2 / (6 + (z2 / (10 + z2 / 14)))));
620 // correct for rounding errors
621 if (value < conv.F) {
622 expval--;
623 conv.F /= 10;
624 }
625
626 // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
627 unsigned int minwidth = ((expval < 100) && (expval > -100)) ? 4U : 5U;
628
629 // in "%g" mode, "prec" is the number of *significant figures* not decimals
630 if (flags & FLAGS_ADAPT_EXP) {
631 // do we want to fall-back to "%f" mode?
632 if ((value >= 1e-4) && (value < 1e6)) {
633 if ((int)prec > expval) {
634 prec = (unsigned)((int)prec - expval - 1);
635 }
636 else {
637 prec = 0;
638 }
639 flags |= FLAGS_PRECISION; // make sure _ftoa respects precision
640 // no characters in exponent
641 minwidth = 0U;
642 expval = 0;
643 }
644 else {
645 // we use one sigfig for the whole part
646 if ((prec > 0) && (flags & FLAGS_PRECISION)) {
647 --prec;
648 }
649 }
650 }
651
652 // will everything fit?
653 unsigned int fwidth = width;
654 if (width > minwidth) {
655 // we didn't fall-back so subtract the characters required for the exponent
656 fwidth -= minwidth;
657 } else {
658 // not enough characters, so go back to default sizing
659 fwidth = 0U;
660 }
661 if ((flags & FLAGS_LEFT) && minwidth) {
662 // if we're padding on the right, DON'T pad the floating part
663 fwidth = 0U;
664 }
665
666 // rescale the float value
667 if (expval) {
668 value /= conv.F;
669 }
670
671 // output the floating part
672 const size_t start_idx = idx;
673 idx = _ftoa(out, buffer, idx, maxlen, negative ? -value : value, prec, fwidth, flags & ~FLAGS_ADAPT_EXP);
674
675 // output the exponent part
676 if (minwidth) {
677 // output the exponential symbol
678 out((flags & FLAGS_UPPERCASE) ? 'E' : 'e', buffer, idx++, maxlen);
679 // output the exponent value
680 idx = _ntoa_long(out, buffer, idx, maxlen, (expval < 0) ? -expval : expval, expval < 0, 10, 0, minwidth-1, FLAGS_ZEROPAD | FLAGS_PLUS);
681 // might need to right-pad spaces
682 if (flags & FLAGS_LEFT) {
683 while (idx - start_idx < width) out(' ', buffer, idx++, maxlen);
684 }
685 }
686 return idx;
687}
688#endif // PRINTF_SUPPORT_EXPONENTIAL
689#endif // PRINTF_SUPPORT_FLOAT
690
691
692// internal vsnprintf
693static int _vsnprintf(out_fct_type out, char* buffer, const size_t maxlen, const char* format, va_list va)
694{
695 unsigned int flags, width, precision, n;
696 size_t idx = 0U;
697
698 if (!buffer) {
699 // use null output function
700 out = _out_null;
701 }
702
703 while (*format)
704 {
705 // format specifier? %[flags][width][.precision][length]
706 if (*format != '%') {
707 // no
708 out(*format, buffer, idx++, maxlen);
709 format++;
710 continue;
711 }
712 else {
713 // yes, evaluate it
714 format++;
715 }
716
717 // evaluate flags
718 flags = 0U;
719 do {
720 switch (*format) {
721 case '0': flags |= FLAGS_ZEROPAD; format++; n = 1U; break;
722 case '-': flags |= FLAGS_LEFT; format++; n = 1U; break;
723 case '+': flags |= FLAGS_PLUS; format++; n = 1U; break;
724 case ' ': flags |= FLAGS_SPACE; format++; n = 1U; break;
725 case '#': flags |= FLAGS_HASH; format++; n = 1U; break;
726 default : n = 0U; break;
727 }
728 } while (n);
729
730 // evaluate width field
731 width = 0U;
732 if (_is_digit(*format)) {
733 width = _atoi(&format);
734 }
735 else if (*format == '*') {
736 const int w = va_arg(va, int);
737 if (w < 0) {
738 flags |= FLAGS_LEFT; // reverse padding
739 width = (unsigned int)-w;
740 }
741 else {
742 width = (unsigned int)w;
743 }
744 format++;
745 }
746
747 // evaluate precision field
748 precision = 0U;
749 if (*format == '.') {
750 flags |= FLAGS_PRECISION;
751 format++;
752 if (_is_digit(*format)) {
753 precision = _atoi(&format);
754 }
755 else if (*format == '*') {
756 const int prec = (int)va_arg(va, int);
757 precision = prec > 0 ? (unsigned int)prec : 0U;
758 format++;
759 }
760 }
761
762 // evaluate length field
763 switch (*format) {
764 case 'l' :
765 flags |= FLAGS_LONG;
766 format++;
767 if (*format == 'l') {
768 flags |= FLAGS_LONG_LONG;
769 format++;
770 }
771 break;
772 case 'h' :
773 flags |= FLAGS_SHORT;
774 format++;
775 if (*format == 'h') {
776 flags |= FLAGS_CHAR;
777 format++;
778 }
779 break;
780#if defined(PRINTF_SUPPORT_PTRDIFF_T)
781 case 't' :
782 flags |= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
783 format++;
784 break;
785#endif
786 case 'j' :
787 flags |= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
788 format++;
789 break;
790 case 'z' :
791 flags |= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG : FLAGS_LONG_LONG);
792 format++;
793 break;
794 default :
795 break;
796 }
797
798 // evaluate specifier
799 switch (*format) {
800 case 'd' :
801 case 'i' :
802 case 'u' :
803 case 'x' :
804 case 'X' :
805 case 'o' :
806 case 'b' : {
807 // set the base
808 unsigned int base;
809 if (*format == 'x' || *format == 'X') {
810 base = 16U;
811 }
812 else if (*format == 'o') {
813 base = 8U;
814 }
815 else if (*format == 'b') {
816 base = 2U;
817 }
818 else {
819 base = 10U;
820 flags &= ~FLAGS_HASH; // no hash for dec format
821 }
822 // uppercase
823 if (*format == 'X') {
824 flags |= FLAGS_UPPERCASE;
825 }
826
827 // no plus or space flag for u, x, X, o, b
828 if ((*format != 'i') && (*format != 'd')) {
829 flags &= ~(FLAGS_PLUS | FLAGS_SPACE);
830 }
831
832 // ignore '0' flag when precision is given
833 if (flags & FLAGS_PRECISION) {
834 flags &= ~FLAGS_ZEROPAD;
835 }
836
837 // convert the integer
838 if ((*format == 'i') || (*format == 'd')) {
839 // signed
840 if (flags & FLAGS_LONG_LONG) {
841#if defined(PRINTF_SUPPORT_LONG_LONG)
842 const long long value = va_arg(va, long long);
843 idx = _ntoa_long_long(out, buffer, idx, maxlen, (unsigned long long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
844#endif
845 }
846 else if (flags & FLAGS_LONG) {
847 const long value = va_arg(va, long);
848 idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
849 }
850 else {
851 const int value = (flags & FLAGS_CHAR) ? (char)va_arg(va, int) : (flags & FLAGS_SHORT) ? (short int)va_arg(va, int) : va_arg(va, int);
852 idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned int)(value > 0 ? value : 0 - value), value < 0, base, precision, width, flags);
853 }
854 }
855 else {
856 // unsigned
857 if (flags & FLAGS_LONG_LONG) {
858#if defined(PRINTF_SUPPORT_LONG_LONG)
859 idx = _ntoa_long_long(out, buffer, idx, maxlen, va_arg(va, unsigned long long), false, base, precision, width, flags);
860#endif
861 }
862 else if (flags & FLAGS_LONG) {
863 idx = _ntoa_long(out, buffer, idx, maxlen, va_arg(va, unsigned long), false, base, precision, width, flags);
864 }
865 else {
866 const unsigned int value = (flags & FLAGS_CHAR) ? (unsigned char)va_arg(va, unsigned int) : (flags & FLAGS_SHORT) ? (unsigned short int)va_arg(va, unsigned int) : va_arg(va, unsigned int);
867 idx = _ntoa_long(out, buffer, idx, maxlen, value, false, base, precision, width, flags);
868 }
869 }
870 format++;
871 break;
872 }
873#if defined(PRINTF_SUPPORT_FLOAT)
874 case 'f' :
875 case 'F' :
876 if (*format == 'F') flags |= FLAGS_UPPERCASE;
877 idx = _ftoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
878 format++;
879 break;
880#if defined(PRINTF_SUPPORT_EXPONENTIAL)
881 case 'e':
882 case 'E':
883 case 'g':
884 case 'G':
885 if ((*format == 'g')||(*format == 'G')) flags |= FLAGS_ADAPT_EXP;
886 if ((*format == 'E')||(*format == 'G')) flags |= FLAGS_UPPERCASE;
887 idx = _etoa(out, buffer, idx, maxlen, va_arg(va, double), precision, width, flags);
888 format++;
889 break;
890#elif (ENABLE_PRINTF_WARNING)
891 case 'e':
892 case 'E':
893 case 'g':
894 case 'G': {
895 const char* p = "<Warning, unsupported specifier used, please enable floating point support in bsp.h %";
896 unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
897 // pre padding
898 if (flags & FLAGS_PRECISION) {
899 l = (l < precision ? l : precision);
900 }
901 if (!(flags & FLAGS_LEFT)) {
902 while (l++ < width) {
903 out(' ', buffer, idx++, maxlen);
904 }
905 }
906 // string output
907 while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
908 out(*(p++), buffer, idx++, maxlen);
909 }
910 out(*format, buffer, idx++, maxlen);
911 out('>', buffer, idx++, maxlen);
912 format++;
913 break;
914 }
915#endif // PRINTF_SUPPORT_EXPONENTIAL
916#elif (ENABLE_PRINTF_WARNING)
917 case 'f' :
918 case 'F' : {
919 const char* p = "<Warning, unsupported specifier used, please enable floating point support in bsp.h %";
920 unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
921 // pre padding
922 if (flags & FLAGS_PRECISION) {
923 l = (l < precision ? l : precision);
924 }
925 if (!(flags & FLAGS_LEFT)) {
926 while (l++ < width) {
927 out(' ', buffer, idx++, maxlen);
928 }
929 }
930 // string output
931 while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
932 out(*(p++), buffer, idx++, maxlen);
933 }
934 out(*format, buffer, idx++, maxlen);
935 out('>', buffer, idx++, maxlen);
936 format++;
937 break;
938 }
939#endif // PRINTF_SUPPORT_FLOAT
940 case 'c' : {
941 unsigned int l = 1U;
942 // pre padding
943 if (!(flags & FLAGS_LEFT)) {
944 while (l++ < width) {
945 out(' ', buffer, idx++, maxlen);
946 }
947 }
948 // char output
949 out((char)va_arg(va, int), buffer, idx++, maxlen);
950 // post padding
951 if (flags & FLAGS_LEFT) {
952 while (l++ < width) {
953 out(' ', buffer, idx++, maxlen);
954 }
955 }
956 format++;
957 break;
958 }
959
960 case 's' : {
961 const char* p = va_arg(va, char*);
962 unsigned int l = _strnlen_s(p, precision ? precision : (size_t)-1);
963 // pre padding
964 if (flags & FLAGS_PRECISION) {
965 l = (l < precision ? l : precision);
966 }
967 if (!(flags & FLAGS_LEFT)) {
968 while (l++ < width) {
969 out(' ', buffer, idx++, maxlen);
970 }
971 }
972 // string output
973 while ((*p != 0) && (!(flags & FLAGS_PRECISION) || precision--)) {
974 out(*(p++), buffer, idx++, maxlen);
975 }
976 // post padding
977 if (flags & FLAGS_LEFT) {
978 while (l++ < width) {
979 out(' ', buffer, idx++, maxlen);
980 }
981 }
982 format++;
983 break;
984 }
985
986 case 'p' : {
987 width = sizeof(void*) * 2U;
989#if defined(PRINTF_SUPPORT_LONG_LONG)
990 const bool is_ll = sizeof(uintptr_t) == sizeof(long long);
991 if (is_ll) {
992 idx = _ntoa_long_long(out, buffer, idx, maxlen, (uintptr_t)va_arg(va, void*), false, 16U, precision, width, flags);
993 }
994 else {
995#endif
996 idx = _ntoa_long(out, buffer, idx, maxlen, (unsigned long)((uintptr_t)va_arg(va, void*)), false, 16U, precision, width, flags);
997#if defined(PRINTF_SUPPORT_LONG_LONG)
998 }
999#endif
1000 format++;
1001 break;
1002 }
1003
1004 case '%' :
1005 out('%', buffer, idx++, maxlen);
1006 format++;
1007 break;
1008
1009 default :
1010 out(*format, buffer, idx++, maxlen);
1011 format++;
1012 break;
1013 }
1014 }
1015
1016 // termination
1017 out((char)0, buffer, idx < maxlen ? idx : maxlen - 1U, maxlen);
1018
1019 // return written chars without terminating \0
1020 return (int)idx;
1021}
1022
1023
1025
1026static int printf_(const char* format, ...)
1027{
1028 va_list va;
1029 va_start(va, format);
1030#if (ENABLE_SEMIHOSTING_PRINT == 1) // if semihosting is enabled. This is to speed up the printing process by printing string instead of char
1031 char buffer[MAX_STRING_BUFFER_SIZE];
1032 const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);
1033 _putchar_s(buffer);
1034#else
1035 char buffer[1];
1036 const int ret = _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
1037
1038#endif
1039
1040 va_end(va);
1041 return ret;
1042}
1043
1044
1045static int sprintf_(char* buffer, const char* format, ...)
1046{
1047 va_list va;
1048 va_start(va, format);
1049 const int ret = _vsnprintf(_out_buffer, buffer, (size_t)-1, format, va);
1050 va_end(va);
1051 return ret;
1052}
1053
1054
1055static int snprintf_(char* buffer, size_t count, const char* format, ...)
1056{
1057 va_list va;
1058 va_start(va, format);
1059 const int ret = _vsnprintf(_out_buffer, buffer, count, format, va);
1060 va_end(va);
1061 return ret;
1062}
1063
1064
1065static int vprintf_(const char* format, va_list va)
1066{
1067 char buffer[1];
1068 return _vsnprintf(_out_char, buffer, (size_t)-1, format, va);
1069}
1070
1071
1072static int vsnprintf_(char* buffer, size_t count, const char* format, va_list va)
1073{
1074 return _vsnprintf(_out_buffer, buffer, count, format, va);
1075}
1076
1077
1078static int fctprintf(void (*out)(char character, void* arg), void* arg, const char* format, ...)
1079{
1080 va_list va;
1081 va_start(va, format);
1082 const out_fct_wrap_type out_fct_wrap = { out, arg };
1083 const int ret = _vsnprintf(_out_fct, (char*)(uintptr_t)&out_fct_wrap, (size_t)-1, format, va);
1084 va_end(va);
1085 return ret;
1086}
1087
1088
1089#ifdef __cplusplus
1090}
1091#endif
1092
1093#endif //#if (ENABLE_BSP_PRINTF_FULL)
1094
1095#endif //_PRINT_FULL_H_
1096
#define bsp_putChar(c)
Definition bsp.h:42
#define FLAGS_LONG
Definition print_full.h:227
#define FLAGS_SPACE
Definition print_full.h:222
#define FLAGS_PLUS
Definition print_full.h:221
#define FLAGS_LONG_LONG
Definition print_full.h:228
#define FLAGS_SHORT
Definition print_full.h:226
#define FLAGS_HASH
Definition print_full.h:223
#define FLAGS_CHAR
Definition print_full.h:225
#define FLAGS_UPPERCASE
Definition print_full.h:224
#define FLAGS_PRECISION
Definition print_full.h:229
void(* out_fct_type)(char character, void *buffer, size_t idx, size_t maxlen)
Definition print_full.h:239
#define FLAGS_ADAPT_EXP
Definition print_full.h:230
#define FLAGS_LEFT
Definition print_full.h:220
#define FLAGS_ZEROPAD
Definition print_full.h:219
void(* fct)(char character, void *arg)
Definition print_full.h:244