Subversion Repositories Kolibri OS

Rev

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