Subversion Repositories Kolibri OS

Rev

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

Rev Author Line No. Line
8622 Boppan 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
 
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  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 
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 
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
}