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