Subversion Repositories Kolibri OS

Rev

Go to most recent revision | Blame | Last modification | View Log | RSS feed

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