1 ///////////////////////////////////////////////////////////////////////////////
2 // \author (c) Marco Paland (info@paland.com)
3 // 2014-2019, PALANDesign Hannover, Germany
5 // \license The MIT License (MIT)
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:
14 // The above copyright notice and this permission notice shall be included in
15 // all copies or substantial portions of the Software.
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
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).
31 ///////////////////////////////////////////////////////////////////////////////
36 // define this globally (e.g. gcc -DPRINTF_INCLUDE_CONFIG_H ...) to include the
37 // printf_config.h header file
39 #ifdef PRINTF_INCLUDE_CONFIG_H
40 #include "printf_config.h"
44 // 'ntoa' conversion buffer size, this must be big enough to hold one converted
45 // numeric number including padded zeros (dynamically created on stack)
47 #ifndef PRINTF_NTOA_BUFFER_SIZE
48 #define PRINTF_NTOA_BUFFER_SIZE 32U
51 // 'ftoa' conversion buffer size, this must be big enough to hold one converted
52 // float number including padded zeros (dynamically created on stack)
54 #ifndef PRINTF_FTOA_BUFFER_SIZE
55 #define PRINTF_FTOA_BUFFER_SIZE 32U
58 // support for the floating point type (%f)
60 #ifndef PRINTF_DISABLE_SUPPORT_FLOAT
61 #define PRINTF_SUPPORT_FLOAT
64 // support for exponential floating point notation (%e/%g)
66 #ifndef PRINTF_DISABLE_SUPPORT_EXPONENTIAL
67 #define PRINTF_SUPPORT_EXPONENTIAL
70 // define the default floating point precision
72 #ifndef PRINTF_DEFAULT_FLOAT_PRECISION
73 #define PRINTF_DEFAULT_FLOAT_PRECISION 6U
76 // define the largest float suitable to print with %f
78 #ifndef PRINTF_MAX_FLOAT
79 #define PRINTF_MAX_FLOAT 1e9
82 // support for the long long types (%llu or %p)
84 #ifndef PRINTF_DISABLE_SUPPORT_LONG_LONG
85 #define PRINTF_SUPPORT_LONG_LONG
88 // support for the ptrdiff_t type (%t)
89 // ptrdiff_t is normally defined in <stddef.h> as long or long long type
91 #ifndef PRINTF_DISABLE_SUPPORT_PTRDIFF_T
92 #define PRINTF_SUPPORT_PTRDIFF_T
95 ///////////////////////////////////////////////////////////////////////////////
97 // internal flag definitions
98 #define FLAGS_ZEROPAD (1U << 0U)
99 #define FLAGS_LEFT (1U << 1U)
100 #define FLAGS_PLUS (1U << 2U)
101 #define FLAGS_SPACE (1U << 3U)
102 #define FLAGS_HASH (1U << 4U)
103 #define FLAGS_UPPERCASE (1U << 5U)
104 #define FLAGS_CHAR (1U << 6U)
105 #define FLAGS_SHORT (1U << 7U)
106 #define FLAGS_LONG (1U << 8U)
107 #define FLAGS_LONG_LONG (1U << 9U)
108 #define FLAGS_PRECISION (1U << 10U)
109 #define FLAGS_ADAPT_EXP (1U << 11U)
112 // import float.h for DBL_MAX
113 #if defined(PRINTF_SUPPORT_FLOAT)
118 // output function type
119 typedef void (*out_fct_type
)(char character
, void *buffer
, size_t idx
, size_t maxlen
);
122 // wrapper (used as buffer) for output function type
124 void (*fct
)(char character
, void *arg
);
129 // internal buffer output
130 static void _out_buffer(char character
, void *buffer
, size_t idx
, size_t maxlen
) {
132 ((char *)buffer
)[idx
] = character
;
137 // internal null output
138 static void _out_null(char character
, void *buffer
, size_t idx
, size_t maxlen
) {
146 // internal _putchar wrapper
147 static void _out_char(char character
, void *buffer
, size_t idx
, size_t maxlen
) {
157 // internal output function wrapper
158 static void _out_fct(char character
, void *buffer
, size_t idx
, size_t maxlen
) {
162 // buffer is the output fct pointer
163 ((out_fct_wrap_type
*)buffer
)->fct(character
, ((out_fct_wrap_type
*)buffer
)->arg
);
168 // internal secure strlen
169 // \return The length of the string (excluding the terminating 0) limited by 'maxsize'
170 static unsigned int _strnlen_s(const char *str
, size_t maxsize
) {
172 for (s
= str
; *s
&& maxsize
--; ++s
);
173 return (unsigned int)(s
- str
);
177 // internal test if char is a digit (0-9)
178 // \return true if char is a digit
179 static bool _is_digit(char ch
) {
180 return (ch
>= '0') && (ch
<= '9');
184 // internal ASCII string to unsigned int conversion
185 static unsigned int _atoi(const char **str
) {
187 while (_is_digit(**str
)) {
188 i
= i
* 10U + (unsigned int)(*((*str
)++) - '0');
194 // output the specified string in reverse, taking care of any zero-padding
195 static 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
) {
196 const size_t start_idx
= idx
;
198 // pad spaces up to given width
199 if (!(flags
& FLAGS_LEFT
) && !(flags
& FLAGS_ZEROPAD
)) {
200 for (size_t i
= len
; i
< width
; i
++) {
201 out(' ', buffer
, idx
++, maxlen
);
207 out(buf
[--len
], buffer
, idx
++, maxlen
);
210 // append pad spaces up to given width
211 if (flags
& FLAGS_LEFT
) {
212 while (idx
- start_idx
< width
) {
213 out(' ', buffer
, idx
++, maxlen
);
221 // internal itoa format
222 static 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
) {
224 if (!(flags
& FLAGS_LEFT
)) {
225 if (width
&& (flags
& FLAGS_ZEROPAD
) && (negative
|| (flags
& (FLAGS_PLUS
| FLAGS_SPACE
)))) {
228 while ((len
< prec
) && (len
< PRINTF_NTOA_BUFFER_SIZE
)) {
231 while ((flags
& FLAGS_ZEROPAD
) && (len
< width
) && (len
< PRINTF_NTOA_BUFFER_SIZE
)) {
237 if (flags
& FLAGS_HASH
) {
238 if (!(flags
& FLAGS_PRECISION
) && len
&& ((len
== prec
) || (len
== width
))) {
240 if (len
&& (base
== 16U)) {
244 if ((base
== 16U) && !(flags
& FLAGS_UPPERCASE
) && (len
< PRINTF_NTOA_BUFFER_SIZE
)) {
246 } else if ((base
== 16U) && (flags
& FLAGS_UPPERCASE
) && (len
< PRINTF_NTOA_BUFFER_SIZE
)) {
248 } else if ((base
== 2U) && (len
< PRINTF_NTOA_BUFFER_SIZE
)) {
251 if (len
< PRINTF_NTOA_BUFFER_SIZE
) {
256 if (len
< PRINTF_NTOA_BUFFER_SIZE
) {
259 } else if (flags
& FLAGS_PLUS
) {
260 buf
[len
++] = '+'; // ignore the space if the '+' exists
261 } else if (flags
& FLAGS_SPACE
) {
266 return _out_rev(out
, buffer
, idx
, maxlen
, buf
, len
, width
, flags
);
270 // internal itoa for 'long' type
271 static 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
) {
272 char buf
[PRINTF_NTOA_BUFFER_SIZE
];
275 // no hash for 0 values
277 flags
&= ~FLAGS_HASH
;
280 // write if precision != 0 and value is != 0
281 if (!(flags
& FLAGS_PRECISION
) || value
) {
283 const char digit
= (char)(value
% base
);
284 buf
[len
++] = digit
< 10 ? '0' + digit
: ((flags
& FLAGS_UPPERCASE
) ? 'A' : 'a') + digit
- 10;
286 } while (value
&& (len
< PRINTF_NTOA_BUFFER_SIZE
));
289 return _ntoa_format(out
, buffer
, idx
, maxlen
, buf
, len
, negative
, (unsigned int)base
, prec
, width
, flags
);
293 // internal itoa for 'long long' type
294 #if defined(PRINTF_SUPPORT_LONG_LONG)
295 static 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
) {
296 char buf
[PRINTF_NTOA_BUFFER_SIZE
];
299 // no hash for 0 values
301 flags
&= ~FLAGS_HASH
;
304 // write if precision != 0 and value is != 0
305 if (!(flags
& FLAGS_PRECISION
) || value
) {
307 const char digit
= (char)(value
% base
);
308 buf
[len
++] = digit
< 10 ? '0' + digit
: ((flags
& FLAGS_UPPERCASE
) ? 'A' : 'a') + digit
- 10;
310 } while (value
&& (len
< PRINTF_NTOA_BUFFER_SIZE
));
313 return _ntoa_format(out
, buffer
, idx
, maxlen
, buf
, len
, negative
, (unsigned int)base
, prec
, width
, flags
);
315 #endif // PRINTF_SUPPORT_LONG_LONG
318 #if defined(PRINTF_SUPPORT_FLOAT)
320 #if defined(PRINTF_SUPPORT_EXPONENTIAL)
321 // forward declaration so that _ftoa can switch to exp notation for values > PRINTF_MAX_FLOAT
322 static 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
);
326 // internal ftoa for fixed decimal floating point
327 static 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
) {
328 char buf
[PRINTF_FTOA_BUFFER_SIZE
];
333 static const double pow10
[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 };
335 // test for special values
337 return _out_rev(out
, buffer
, idx
, maxlen
, "nan", 3, width
, flags
);
338 if (value
< -DBL_MAX
)
339 return _out_rev(out
, buffer
, idx
, maxlen
, "fni-", 4, width
, flags
);
341 return _out_rev(out
, buffer
, idx
, maxlen
, (flags
& FLAGS_PLUS
) ? "fni+" : "fni", (flags
& FLAGS_PLUS
) ? 4U : 3U, width
, flags
);
343 // test for very large values
344 // standard printf behavior is to print EVERY whole number digit -- which could be 100s of characters overflowing your buffers == bad
345 if ((value
> PRINTF_MAX_FLOAT
) || (value
< -PRINTF_MAX_FLOAT
)) {
346 #if defined(PRINTF_SUPPORT_EXPONENTIAL)
347 return _etoa(out
, buffer
, idx
, maxlen
, value
, prec
, width
, flags
);
354 bool negative
= false;
360 // set default precision, if not set explicitly
361 if (!(flags
& FLAGS_PRECISION
)) {
362 prec
= PRINTF_DEFAULT_FLOAT_PRECISION
;
364 // limit precision to 9, cause a prec >= 10 can lead to overflow errors
365 while ((len
< PRINTF_FTOA_BUFFER_SIZE
) && (prec
> 9U)) {
370 int whole
= (int)value
;
371 double tmp
= (value
- whole
) * pow10
[prec
];
372 unsigned long frac
= (unsigned long)tmp
;
377 // handle rollover, e.g. case 0.99 with prec 1 is 1.0
378 if (frac
>= pow10
[prec
]) {
382 } else if (diff
< 0.5) {
383 } else if ((frac
== 0U) || (frac
& 1U)) {
384 // if halfway, round up if odd OR if last digit is 0
389 diff
= value
- (double)whole
;
390 if ((!(diff
< 0.5) || (diff
> 0.5)) && (whole
& 1)) {
391 // exactly 0.5 and ODD, then round up
392 // 1.5 -> 2, but 2.5 -> 2
396 unsigned int count
= prec
;
397 // now do fractional part, as an unsigned number
398 while (len
< PRINTF_FTOA_BUFFER_SIZE
) {
400 buf
[len
++] = (char)(48U + (frac
% 10U));
401 if (!(frac
/= 10U)) {
406 while ((len
< PRINTF_FTOA_BUFFER_SIZE
) && (count
-- > 0U)) {
409 if (len
< PRINTF_FTOA_BUFFER_SIZE
) {
415 // do whole part, number is reversed
416 while (len
< PRINTF_FTOA_BUFFER_SIZE
) {
417 buf
[len
++] = (char)(48 + (whole
% 10));
418 if (!(whole
/= 10)) {
424 if (!(flags
& FLAGS_LEFT
) && (flags
& FLAGS_ZEROPAD
)) {
425 if (width
&& (negative
|| (flags
& (FLAGS_PLUS
| FLAGS_SPACE
)))) {
428 while ((len
< width
) && (len
< PRINTF_FTOA_BUFFER_SIZE
)) {
433 if (len
< PRINTF_FTOA_BUFFER_SIZE
) {
436 } else if (flags
& FLAGS_PLUS
) {
437 buf
[len
++] = '+'; // ignore the space if the '+' exists
438 } else if (flags
& FLAGS_SPACE
) {
443 return _out_rev(out
, buffer
, idx
, maxlen
, buf
, len
, width
, flags
);
447 #if defined(PRINTF_SUPPORT_EXPONENTIAL)
448 // internal ftoa variant for exponential floating-point type, contributed by Martijn Jasperse <m.jasperse@gmail.com>
449 static 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
) {
450 // check for NaN and special values
451 if ((value
!= value
) || (value
> DBL_MAX
) || (value
< -DBL_MAX
)) {
452 return _ftoa(out
, buffer
, idx
, maxlen
, value
, prec
, width
, flags
);
455 // determine the sign
456 const bool negative
= value
< 0;
462 if (!(flags
& FLAGS_PRECISION
)) {
463 prec
= PRINTF_DEFAULT_FLOAT_PRECISION
;
466 // determine the decimal exponent
467 // based on the algorithm by David Gay (https://www.ampl.com/netlib/fp/dtoa.c)
474 int exp2
= (int)((conv
.U
>> 52U) & 0x07FFU
) - 1023; // effectively log2
475 conv
.U
= (conv
.U
& ((1ULL << 52U) - 1U)) | (1023ULL << 52U); // drop the exponent so conv.F is now in [1,2)
476 // now approximate log10 from the log2 integer part and an expansion of ln around 1.5
477 int expval
= (int)(0.1760912590558 + exp2
* 0.301029995663981 + (conv
.F
- 1.5) * 0.289529654602168);
478 // now we want to compute 10^expval but we want to be sure it won't overflow
479 exp2
= (int)(expval
* 3.321928094887362 + 0.5);
480 const double z
= expval
* 2.302585092994046 - exp2
* 0.6931471805599453;
481 const double z2
= z
* z
;
482 conv
.U
= (uint64_t)(exp2
+ 1023) << 52U;
483 // compute exp(z) using continued fractions, see https://en.wikipedia.org/wiki/Exponential_function#Continued_fractions_for_ex
484 conv
.F
*= 1 + 2 * z
/ (2 - z
+ (z2
/ (6 + (z2
/ (10 + z2
/ 14)))));
485 // correct for rounding errors
486 if (value
< conv
.F
) {
491 // the exponent format is "%+03d" and largest value is "307", so set aside 4-5 characters
492 unsigned int minwidth
= ((expval
< 100) && (expval
> -100)) ? 4U : 5U;
494 // in "%g" mode, "prec" is the number of *significant figures* not decimals
495 if (flags
& FLAGS_ADAPT_EXP
) {
496 // do we want to fall-back to "%f" mode?
497 if ((value
>= 1e-4) && (value
< 1e6
)) {
498 if ((int)prec
> expval
) {
499 prec
= (unsigned)((int)prec
- expval
- 1);
503 flags
|= FLAGS_PRECISION
; // make sure _ftoa respects precision
504 // no characters in exponent
508 // we use one sigfig for the whole part
509 if ((prec
> 0) && (flags
& FLAGS_PRECISION
)) {
515 // will everything fit?
516 unsigned int fwidth
= width
;
517 if (width
> minwidth
) {
518 // we didn't fall-back so subtract the characters required for the exponent
521 // not enough characters, so go back to default sizing
524 if ((flags
& FLAGS_LEFT
) && minwidth
) {
525 // if we're padding on the right, DON'T pad the floating part
529 // rescale the float value
534 // output the floating part
535 const size_t start_idx
= idx
;
536 idx
= _ftoa(out
, buffer
, idx
, maxlen
, negative
? -value
: value
, prec
, fwidth
, flags
& ~FLAGS_ADAPT_EXP
);
538 // output the exponent part
540 // output the exponential symbol
541 out((flags
& FLAGS_UPPERCASE
) ? 'E' : 'e', buffer
, idx
++, maxlen
);
542 // output the exponent value
543 idx
= _ntoa_long(out
, buffer
, idx
, maxlen
, (expval
< 0) ? -expval
: expval
, expval
< 0, 10, 0, minwidth
- 1, FLAGS_ZEROPAD
| FLAGS_PLUS
);
544 // might need to right-pad spaces
545 if (flags
& FLAGS_LEFT
) {
546 while (idx
- start_idx
< width
) out(' ', buffer
, idx
++, maxlen
);
551 #endif // PRINTF_SUPPORT_EXPONENTIAL
552 #endif // PRINTF_SUPPORT_FLOAT
555 // internal vsnprintf
556 static int _vsnprintf(out_fct_type out
, char *buffer
, const size_t maxlen
, const char *format
, va_list va
) {
557 unsigned int flags
, width
, precision
, n
;
561 // use null output function
566 // format specifier? %[flags][width][.precision][length]
567 if (*format
!= '%') {
569 out(*format
, buffer
, idx
++, maxlen
);
582 flags
|= FLAGS_ZEROPAD
;
597 flags
|= FLAGS_SPACE
;
612 // evaluate width field
614 if (_is_digit(*format
)) {
615 width
= _atoi(&format
);
616 } else if (*format
== '*') {
617 const int w
= va_arg(va
, int);
619 flags
|= FLAGS_LEFT
; // reverse padding
620 width
= (unsigned int) - w
;
622 width
= (unsigned int)w
;
627 // evaluate precision field
629 if (*format
== '.') {
630 flags
|= FLAGS_PRECISION
;
632 if (_is_digit(*format
)) {
633 precision
= _atoi(&format
);
634 } else if (*format
== '*') {
635 const int prec
= (int)va_arg(va
, int);
636 precision
= prec
> 0 ? (unsigned int)prec
: 0U;
641 // evaluate length field
646 if (*format
== 'l') {
647 flags
|= FLAGS_LONG_LONG
;
652 flags
|= FLAGS_SHORT
;
654 if (*format
== 'h') {
659 #if defined(PRINTF_SUPPORT_PTRDIFF_T)
661 flags
|= (sizeof(ptrdiff_t) == sizeof(long) ? FLAGS_LONG
: FLAGS_LONG_LONG
);
666 flags
|= (sizeof(intmax_t) == sizeof(long) ? FLAGS_LONG
: FLAGS_LONG_LONG
);
670 flags
|= (sizeof(size_t) == sizeof(long) ? FLAGS_LONG
: FLAGS_LONG_LONG
);
677 // evaluate specifier
688 if (*format
== 'x' || *format
== 'X') {
690 } else if (*format
== 'o') {
692 } else if (*format
== 'b') {
696 flags
&= ~FLAGS_HASH
; // no hash for dec format
699 if (*format
== 'X') {
700 flags
|= FLAGS_UPPERCASE
;
703 // no plus or space flag for u, x, X, o, b
704 if ((*format
!= 'i') && (*format
!= 'd')) {
705 flags
&= ~(FLAGS_PLUS
| FLAGS_SPACE
);
708 // ignore '0' flag when precision is given
709 if (flags
& FLAGS_PRECISION
) {
710 flags
&= ~FLAGS_ZEROPAD
;
713 // convert the integer
714 if ((*format
== 'i') || (*format
== 'd')) {
716 if (flags
& FLAGS_LONG_LONG
) {
717 #if defined(PRINTF_SUPPORT_LONG_LONG)
718 const long long value
= va_arg(va
, long long);
719 idx
= _ntoa_long_long(out
, buffer
, idx
, maxlen
, (unsigned long long)(value
> 0 ? value
: 0 - value
), value
< 0, base
, precision
, width
, flags
);
721 } else if (flags
& FLAGS_LONG
) {
722 const long value
= va_arg(va
, long);
723 idx
= _ntoa_long(out
, buffer
, idx
, maxlen
, (unsigned long)(value
> 0 ? value
: 0 - value
), value
< 0, base
, precision
, width
, flags
);
725 const int value
= (flags
& FLAGS_CHAR
) ? (char)va_arg(va
, int) : (flags
& FLAGS_SHORT
) ? (short int)va_arg(va
, int) : va_arg(va
, int);
726 idx
= _ntoa_long(out
, buffer
, idx
, maxlen
, (unsigned int)(value
> 0 ? value
: 0 - value
), value
< 0, base
, precision
, width
, flags
);
730 if (flags
& FLAGS_LONG_LONG
) {
731 #if defined(PRINTF_SUPPORT_LONG_LONG)
732 idx
= _ntoa_long_long(out
, buffer
, idx
, maxlen
, va_arg(va
, unsigned long long), false, base
, precision
, width
, flags
);
734 } else if (flags
& FLAGS_LONG
) {
735 idx
= _ntoa_long(out
, buffer
, idx
, maxlen
, va_arg(va
, unsigned long), false, base
, precision
, width
, flags
);
737 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);
738 idx
= _ntoa_long(out
, buffer
, idx
, maxlen
, value
, false, base
, precision
, width
, flags
);
744 #if defined(PRINTF_SUPPORT_FLOAT)
747 if (*format
== 'F') flags
|= FLAGS_UPPERCASE
;
748 idx
= _ftoa(out
, buffer
, idx
, maxlen
, va_arg(va
, double), precision
, width
, flags
);
751 #if defined(PRINTF_SUPPORT_EXPONENTIAL)
756 if ((*format
== 'g') || (*format
== 'G')) flags
|= FLAGS_ADAPT_EXP
;
757 if ((*format
== 'E') || (*format
== 'G')) flags
|= FLAGS_UPPERCASE
;
758 idx
= _etoa(out
, buffer
, idx
, maxlen
, va_arg(va
, double), precision
, width
, flags
);
761 #endif // PRINTF_SUPPORT_EXPONENTIAL
762 #endif // PRINTF_SUPPORT_FLOAT
766 if (!(flags
& FLAGS_LEFT
)) {
767 while (l
++ < width
) {
768 out(' ', buffer
, idx
++, maxlen
);
772 out((char)va_arg(va
, int), buffer
, idx
++, maxlen
);
774 if (flags
& FLAGS_LEFT
) {
775 while (l
++ < width
) {
776 out(' ', buffer
, idx
++, maxlen
);
784 const char *p
= va_arg(va
, char *);
785 unsigned int l
= _strnlen_s(p
, precision
? precision
: (size_t) - 1);
787 if (flags
& FLAGS_PRECISION
) {
788 l
= (l
< precision
? l
: precision
);
790 if (!(flags
& FLAGS_LEFT
)) {
791 while (l
++ < width
) {
792 out(' ', buffer
, idx
++, maxlen
);
796 while ((*p
!= 0) && (!(flags
& FLAGS_PRECISION
) || precision
--)) {
797 out(*(p
++), buffer
, idx
++, maxlen
);
800 if (flags
& FLAGS_LEFT
) {
801 while (l
++ < width
) {
802 out(' ', buffer
, idx
++, maxlen
);
810 width
= sizeof(void *) * 2U;
811 flags
|= FLAGS_ZEROPAD
| FLAGS_UPPERCASE
;
812 #if defined(PRINTF_SUPPORT_LONG_LONG)
813 const bool is_ll
= sizeof(uintptr_t) == sizeof(long long);
815 idx
= _ntoa_long_long(out
, buffer
, idx
, maxlen
, (uintptr_t)va_arg(va
, void *), false, 16U, precision
, width
, flags
);
818 idx
= _ntoa_long(out
, buffer
, idx
, maxlen
, (unsigned long)((uintptr_t)va_arg(va
, void *)), false, 16U, precision
, width
, flags
);
819 #if defined(PRINTF_SUPPORT_LONG_LONG)
827 out('%', buffer
, idx
++, maxlen
);
832 out(*format
, buffer
, idx
++, maxlen
);
839 out((char)0, buffer
, idx
< maxlen
? idx
: maxlen
- 1U, maxlen
);
841 // return written chars without terminating \0
846 ///////////////////////////////////////////////////////////////////////////////
848 int printf_(const char *format
, ...) {
850 va_start(va
, format
);
852 const int ret
= _vsnprintf(_out_char
, buffer
, (size_t) - 1, format
, va
);
858 int sprintf_(char *buffer
, const char *format
, ...) {
860 va_start(va
, format
);
861 const int ret
= _vsnprintf(_out_buffer
, buffer
, (size_t) - 1, format
, va
);
867 int snprintf_(char *buffer
, size_t count
, const char *format
, ...) {
869 va_start(va
, format
);
870 const int ret
= _vsnprintf(_out_buffer
, buffer
, count
, format
, va
);
876 int vprintf_(const char *format
, va_list va
) {
878 return _vsnprintf(_out_char
, buffer
, (size_t) - 1, format
, va
);
882 int vsnprintf_(char *buffer
, size_t count
, const char *format
, va_list va
) {
883 return _vsnprintf(_out_buffer
, buffer
, count
, format
, va
);
887 int fctprintf(void (*out
)(char character
, void *arg
), void *arg
, const char *format
, ...) {
889 va_start(va
, format
);
890 const out_fct_wrap_type out_fct_wrap
= { out
, arg
};
891 const int ret
= _vsnprintf(_out_fct
, (char *)(uintptr_t)&out_fct_wrap
, (size_t) - 1, format
, va
);