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