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