Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
8687 turbocat 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 
34
#include 
35
#include 
36
#include "format_print.h"
37
#include 
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  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 
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 
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
}