add memset asm code
[ti-x.git] / src / arch / lib / string.c
blob07aa00446229887516fcd14bc624bb37a5548428
1 /* the ti-x embed operation system
2 *
3 * it is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * it is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with it; If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>
16 * Copyright (C) 2009, Ruki All rights reserved.
17 * Home: <a href="http://www.xxx.org">http://www.xxx.org</a>
19 * \author ruki
20 * \date 09.12.06
21 * \file string.c
25 /* ////////////////////////////////////////////////////////////////////////
26 * includes
28 #include "string.h"
29 #include "memory.h"
30 #include "limits.h"
31 #include "utils.h"
33 /* ////////////////////////////////////////////////////////////////////////
34 * types
37 // printf format type
38 typedef enum __ti_printf_type_t
40 TI_PRINTF_TYPE_NONE
41 , TI_PRINTF_TYPE_INT
42 , TI_PRINTF_TYPE_CHAR
43 , TI_PRINTF_TYPE_CHAR_PERCENT
44 , TI_PRINTF_TYPE_FLOAT
45 , TI_PRINTF_TYPE_STRING
46 , TI_PRINTF_TYPE_WIDTH
47 , TI_PRINTF_TYPE_PRECISION
48 , TI_PRINTF_TYPE_INVALID
50 }ti_printf_type_t;
52 // printf format extra info
53 typedef enum __ti_printf_extra_t
55 TI_PRINTF_EXTRA_NONE = 0
56 , TI_PRINTF_EXTRA_SIGNED = 1 // signed integer for %d %i
57 , TI_PRINTF_EXTRA_UPPER = 2 // upper case for %X %B
58 , TI_PRINTF_EXTRA_PERCENT = 4 // percent char: %
59 , TI_PRINTF_EXTRA_EXP = 8 // exponent form: [-]d.ddd e[+/-]ddd
61 }ti_printf_extra_t;
63 // printf length qualifier
64 typedef enum __ti_printf_qual_t
66 TI_PRINTF_QUAL_NONE
67 , TI_PRINTF_QUAL_I
68 , TI_PRINTF_QUAL_I8
69 , TI_PRINTF_QUAL_I16
70 , TI_PRINTF_QUAL_I32
71 , TI_PRINTF_QUAL_I64
73 }ti_printf_qual_t;
75 // printf flag type
76 typedef enum __ti_printf_flag_t
78 TI_PRINTF_FLAG_NONE = 0
79 , TI_PRINTF_FLAG_PLUS = 1 // +: denote the sign '+' or '-' of a number
80 , TI_PRINTF_FLAG_LEFT = 2 // -: left-justified
81 , TI_PRINTF_FLAG_ZERO = 4 // 0: fill 0 instead of spaces
82 , TI_PRINTF_FLAG_PFIX = 8 // #: add prefix
84 }ti_printf_flag_t;
86 // printf entry
87 typedef struct __ti_printf_entry_t
89 // format type
90 ti_printf_type_t type;
92 // extra info
93 ti_printf_extra_t extra;
95 // flag
96 ti_printf_flag_t flags;
98 // field width
99 ti_int_t width;
101 // precision
102 ti_int_t precision;
104 // qualifier
105 ti_printf_qual_t qual;
107 // base: 2 8 10 16
108 ti_int_t base;
111 }ti_printf_entry_t;
113 /* ////////////////////////////////////////////////////////////////////////
114 * internal declaration
117 // string length
118 ti_int_t __strlen(ti_char_t const* s);
119 ti_int_t __strnlen(ti_char_t const* s, ti_int_t n);
121 // string comparision
122 ti_int_t __strcmp(ti_char_t const* lhs, ti_char_t const* rhs);
123 ti_int_t __strncmp(ti_char_t const* lhs, ti_char_t const* rhs, ti_size_t n);
125 // string copy
126 ti_char_t const* __strcpy(ti_char_t* pd, ti_char_t const* ps);
127 ti_char_t const* __strncpy(ti_char_t* pd, ti_char_t const* ps, ti_size_t n);
129 ti_int_t __vsnprintf(ti_char_t* s, ti_size_t n, ti_char_t const* fmt, ti_va_list_t args);
130 ti_int_t __vsprintf(ti_char_t* s, ti_char_t const* fmt, ti_va_list_t args);
132 /* ////////////////////////////////////////////////////////////////////////
133 * details
136 static ti_int_t ti_skip_atoi(ti_char_t const** s)
138 ti_int_t i = 0;
139 while (ti_isdigit(**s) == TI_TRUE)
140 i = i * 10 + *((*s)++) - '0';
141 return i;
143 static ti_char_t* ti_printf_string(ti_char_t* pb, ti_char_t* pe, ti_printf_entry_t e, ti_char_t const* s)
145 ti_int_t n = ti_strnlen(s, e.precision);
147 // fill space at left side, e.g. " abcd"
148 if (!(e.flags & TI_PRINTF_FLAG_LEFT))
150 while (n < e.width--)
151 if (pb < pe) *pb++ = ' ';
154 // copy string
155 ti_int_t i = 0;
156 for (i = 0; i < n; ++i)
157 if (pb < pe) *pb++ = *s++;
159 // fill space at right side, e.g. "abcd "
160 while (n < e.width--)
161 if (pb < pe) *pb++ = ' ';
163 return pb;
165 static ti_char_t* ti_printf_integer(ti_char_t* pb, ti_char_t* pe, ti_printf_entry_t e, ti_uint64_t num)
167 // digits table
168 static ti_char_t const digits_table[16] = "0123456789ABCDEF";
170 // max: 64-bit binary decimal
171 ti_char_t digits[64] = {0};
172 ti_int_t digit_i = 0;
174 // lowercase mask, e.g. 'F' | 0x20 => 'f'
175 ti_int_t lomask = (e.extra & TI_PRINTF_EXTRA_UPPER)? 0x0 : 0x20;
177 // sign: + -
178 ti_char_t sign = 0;
179 if (e.extra & TI_PRINTF_EXTRA_SIGNED)
181 if ((ti_int64_t)num < 0)
183 sign = '-';
184 --e.width;
186 else if (e.flags & TI_PRINTF_FLAG_PLUS)
188 sign = '+';
189 --e.width;
193 // convert num => digits string in reverse order
194 if (num == 0) digits[digit_i++] = '0';
195 else
197 if ((ti_int64_t)num < 0) num = (ti_uint64_t)(-(ti_int64_t)num);
199 #if 0
202 digits[digit_i++] = digits_table[num % e.base] | lomask;
203 num /= e.base;
205 while (num);
206 #else
207 if (e.base != 10)
209 ti_int_t shift_bits = 4;
210 if (e.base == 8) shift_bits--;
211 else if (e.base == 2) shift_bits -= 3;
214 digits[digit_i++] = digits_table[(ti_uint8_t)num & (e.base - 1)] | lomask;
215 num >>= shift_bits;
217 while (num);
219 else
223 digits[digit_i++] = digits_table[num % e.base] | lomask;
224 num /= e.base;
226 while (num);
228 #endif
231 // adjust precision
232 if (digit_i > e.precision)
233 e.precision = digit_i;
235 // fill spaces at left side, e.g. " 0x0"
236 e.width -= e.precision;
237 if (!(e.flags & (TI_PRINTF_FLAG_LEFT + TI_PRINTF_FLAG_ZERO)))
239 while (--e.width >= 0)
240 if (pb < pe) *pb++ = ' ';
243 // append sign: + / -
244 if (sign && (pb < pe)) *pb++ = sign;
246 // append prefix: 0x..., 0X..., 0b..., 0B...
247 if (e.flags & TI_PRINTF_FLAG_PFIX)
249 switch (e.base)
251 case 16:
253 if (pb + 1 < pe)
255 *pb++ = '0';
256 *pb++ = 'X' | lomask;
257 e.width -= 2;
259 break;
261 case 8:
263 if (pb < pe)
265 *pb++ = '0';
266 --e.width;
268 break;
270 case 2:
272 if (pb + 1 < pe)
274 *pb++ = '0';
275 *pb++ = 'B' | lomask;
276 e.width -= 2;
278 break;
280 default:
281 break;
285 // fill 0 or spaces, e.g. "0x ff"
286 if (!(e.flags & TI_PRINTF_FLAG_LEFT))
288 ti_char_t c = (e.flags & TI_PRINTF_FLAG_ZERO)? '0' : ' ';
289 while (--e.width >= 0)
290 if (pb < pe) *pb++ = c;
293 // fill 0 if precision is larger, e.g. "0x000ff"
294 while (digit_i <= --e.precision)
295 if (pb < pe) *pb++ = '0';
297 // append digits
298 while (--digit_i >= 0)
299 if (pb < pe) *pb++ = digits[digit_i];
301 // trailing space padding for left-justified flags, e.g. "0xff "
302 while (--e.width >= 0)
303 if (pb < pe) *pb++ = ' ';
305 return pb;
307 #ifdef TI_ARCH_HAVE_FLOAT
308 // only support double float-point
309 static ti_char_t* ti_printf_float(ti_char_t* pb, ti_char_t* pe, ti_printf_entry_t e, ti_float_t num)
311 // digits
312 ti_char_t ints[64] = {0};
313 ti_char_t decs[64] = {0};
314 ti_int_t ints_i = 0, decs_i = 0;
316 // sign: + -
317 ti_char_t sign = 0;
318 if (e.extra & TI_PRINTF_EXTRA_SIGNED)
320 if (num < 0)
322 sign = '-';
323 --e.width;
325 else if (e.flags & TI_PRINTF_FLAG_PLUS)
327 sign = '+';
328 --e.width;
332 // adjust sign
333 if (num < 0) num = -num;
335 // get integer & decimal
336 ti_int64_t integer = (ti_int64_t)num;
337 ti_float_t decimal = num - integer;
339 // convert integer => digits string in reverse order
340 if (integer == 0) ints[ints_i++] = '0';
341 else
343 if (integer < 0) integer = -integer;
346 ints[ints_i++] = (integer % 10) + '0';
347 integer /= 10;
349 while (integer);
352 // default precision: 6
353 if (e.precision <= 0) e.precision = 6;
355 // convert decimal => digits string in positive order
356 if (decimal == 0) decs[decs_i++] = '0';
357 else
359 ti_int_t d = (ti_int_t)(decimal * 10);
362 decs[decs_i++] = d + '0';
363 decimal = decimal * 10 - d;
364 d = (ti_int_t)(decimal * 10);
366 while (decs_i < e.precision);
369 // adjust precision
370 if (decs_i > e.precision)
371 decs_i = e.precision;
373 // fill spaces at left side, e.g. " 0.31415926"
374 e.width -= ints_i + 1 + e.precision;
375 if (!(e.flags & (TI_PRINTF_FLAG_LEFT + TI_PRINTF_FLAG_ZERO)))
377 while (--e.width >= 0)
378 if (pb < pe) *pb++ = ' ';
381 // append sign: + / -
382 if (sign && (pb < pe)) *pb++ = sign;
384 // fill 0 or spaces, e.g. "00003.1415926"
385 if (!(e.flags & TI_PRINTF_FLAG_LEFT))
387 ti_char_t c = (e.flags & TI_PRINTF_FLAG_ZERO)? '0' : ' ';
388 while (--e.width >= 0)
389 if (pb < pe) *pb++ = c;
392 // append integer
393 while (--ints_i >= 0)
394 if (pb < pe) *pb++ = ints[ints_i];
396 // append .
397 if (pb < pe) *pb++ = '.';
399 // append decimal
400 ti_int_t decs_n = decs_i;
401 while (--decs_i >= 0)
402 if (pb < pe) *pb++ = decs[decs_n - decs_i - 1];
404 // fill 0 if precision is larger, e.g. "0.3140000"
405 while (decs_n <= --e.precision)
406 if (pb < pe) *pb++ = '0';
408 // trailing space padding for left-justified flags, e.g. "0.31415926 "
409 while (--e.width >= 0)
410 if (pb < pe) *pb++ = ' ';
412 return pb;
414 #endif
415 // get a printf format entry
416 static ti_int_t ti_printf_entry(ti_char_t const* fmt, ti_printf_entry_t* e)
418 ti_char_t const* p = fmt;
420 // get field width for *
421 if (e->type == TI_PRINTF_TYPE_WIDTH)
423 if (e->width < 0)
425 e->width = -e->width;
426 e->flags |= TI_PRINTF_FLAG_LEFT;
428 e->type = TI_PRINTF_TYPE_NONE;
429 goto get_precision;
432 // get precision for *
433 if (e->type == TI_PRINTF_TYPE_PRECISION)
435 if (e->precision < 0) e->precision = 0;
436 e->type = TI_PRINTF_TYPE_NONE;
437 goto get_qualifier;
440 // default type
441 e->type = TI_PRINTF_TYPE_NONE;
443 // goto %
444 for (; *p; ++p)
445 if (*p == '%') break;
447 // return non-format string
448 if (p != fmt || !*p)
449 return (p - fmt);
451 // skip %
452 ++p;
454 // get flags
455 e->flags = TI_PRINTF_FLAG_NONE;
456 while (1)
458 ti_bool_t is_found = TI_TRUE;
459 switch (*p)
461 case '+': e->flags |= TI_PRINTF_FLAG_PLUS; break;
462 case '-': e->flags |= TI_PRINTF_FLAG_LEFT; break;
463 case '0': e->flags |= TI_PRINTF_FLAG_ZERO; break;
464 case '#': e->flags |= TI_PRINTF_FLAG_PFIX; break;
465 default: is_found = TI_FALSE; break;
467 if (is_found == TI_FALSE) break;
468 else ++p;
471 // get field width
472 e->width = -1;
473 if (ti_isdigit(*p) == TI_TRUE) e->width = ti_skip_atoi(&p);
474 else if (*p == '*')
476 // it's the next argument
477 e->type = TI_PRINTF_TYPE_WIDTH;
478 return ++p - fmt;
481 get_precision:
482 // get precision
483 e->precision = -1;
484 if (*p == '.')
486 ++p;
487 if (ti_isdigit(*p) == TI_TRUE)
489 e->precision = ti_skip_atoi(&p);
490 if (e->precision < 0) e->precision = 0;
492 else if (*p == '*')
494 // it's the next argument
495 e->type = TI_PRINTF_TYPE_PRECISION;
496 return ++p - fmt;
500 get_qualifier:
501 // get length qualifier
502 e->qual = TI_PRINTF_QUAL_NONE;
503 switch (*p)
505 // short & long => int
506 case 'h':
507 case 'l':
508 ++p;
509 if (*p == 'l')
511 e->qual = TI_PRINTF_QUAL_I64;
512 ++p;
514 else e->qual = TI_PRINTF_QUAL_I;
515 break;
516 case 'I':
518 ++p;
519 ti_int_t n = ti_skip_atoi(&p);
520 switch (n)
522 case 8: e->qual = TI_PRINTF_QUAL_I8; break;
523 case 16: e->qual = TI_PRINTF_QUAL_I16; break;
524 case 32: e->qual = TI_PRINTF_QUAL_I32; break;
525 case 64: e->qual = TI_PRINTF_QUAL_I64; break;
526 default: e->qual = TI_PRINTF_QUAL_I; break;
528 break;
530 default:
531 e->qual = TI_PRINTF_QUAL_NONE;
532 break;
535 // get base & type
536 e->base = -1;
537 e->type = TI_PRINTF_TYPE_INVALID;
538 e->extra = TI_PRINTF_EXTRA_NONE;
539 switch (*p)
541 case 's':
542 e->type = TI_PRINTF_TYPE_STRING;
543 return (++p - fmt);
544 case '%':
545 e->extra |= TI_PRINTF_EXTRA_PERCENT;
546 case 'c':
547 e->type = TI_PRINTF_TYPE_CHAR;
548 return (++p - fmt);
549 case 'd':
550 case 'i':
551 e->extra |= TI_PRINTF_EXTRA_SIGNED;
552 case 'u':
553 e->base = 10;
554 e->type = TI_PRINTF_TYPE_INT;
555 break;
556 case 'X':
557 e->extra |= TI_PRINTF_EXTRA_UPPER;
558 case 'x':
559 e->base = 16;
560 e->type = TI_PRINTF_TYPE_INT;
561 break;
562 case 'o':
563 e->base = 8;
564 e->type = TI_PRINTF_TYPE_INT;
565 break;
566 case 'B':
567 e->extra |= TI_PRINTF_EXTRA_UPPER;
568 case 'b':
569 e->base = 2;
570 e->type = TI_PRINTF_TYPE_INT;
571 break;
572 #ifdef TI_CONFIG_FLOAT
573 case 'F':
574 e->extra |= TI_PRINTF_EXTRA_UPPER;
575 case 'f':
576 e->type = TI_PRINTF_TYPE_FLOAT;
577 e->extra |= TI_PRINTF_EXTRA_SIGNED;
578 break;
579 case 'E':
580 e->extra |= TI_PRINTF_EXTRA_UPPER;
581 case 'e':
582 e->type = TI_PRINTF_TYPE_FLOAT;
583 e->extra |= TI_PRINTF_EXTRA_SIGNED;
584 e->extra |= TI_PRINTF_EXTRA_EXP;
585 break;
586 #endif
587 default:
588 e->type = TI_PRINTF_TYPE_INVALID;
589 return (p - fmt);
592 return (++p - fmt);
595 /* ////////////////////////////////////////////////////////////////////////
596 * interface implemetion
599 ti_int_t ti_strlen(ti_char_t const* s)
601 #ifndef TI_ARCH_SPEC_HAVE_STRLEN
602 ti_char_t const* ps;
603 for (ps = s; *ps != '\0'; ++ps) ;
604 return (ps - s);
605 #else
606 return __strlen(s);
607 #endif
609 ti_int_t ti_strnlen(ti_char_t const* s, ti_int_t n)
611 #ifndef TI_ARCH_SPEC_HAVE_STRNLEN
612 ti_char_t const* ps;
613 for (ps = s; n-- && *ps != '\0'; ++ps) ;
614 return (ps - s);
615 #else
616 return __strnlen(s, n);
617 #endif
619 ti_char_t const* ti_strcpy(ti_char_t* pd, ti_char_t const* ps)
621 #ifndef TI_ARCH_SPEC_HAVE_STRCPY
622 ti_char_t const* p = pd;
623 while ((*pd++ = *ps++) != '\0') ;
624 return p;
625 #else
626 return __strcpy(pd, ps);
627 #endif
629 ti_char_t const* ti_strncpy(ti_char_t* pd, ti_char_t const* ps, ti_size_t n)
631 #ifndef TI_ARCH_SPEC_HAVE_STRNCPY
632 ti_char_t const* p = pd;
633 while (n)
635 if ((*pd = *ps) != 0) ps++;
636 pd++;
637 n--;
639 return p;
640 #else
641 return __strncpy(pd, ps, n);
642 #endif
644 ti_bool_t ti_strcmp(ti_char_t const* lhs, ti_char_t const* rhs)
646 #ifndef TI_ARCH_SPEC_HAVE_STRCMP
647 ti_int_t ret;
648 while (1)
650 if ((ret = *lhs - *rhs++) != 0 || !*lhs++)
651 break;
653 return ((ret == 0)? TI_TRUE : TI_FALSE);
654 #else
655 return ((__strcmp(lhs, rhs) == 0)? TI_TRUE : TI_FALSE);
656 #endif
658 ti_bool_t ti_strncmp(ti_char_t const* lhs, ti_char_t const* rhs, ti_size_t n)
660 #ifndef TI_ARCH_SPEC_HAVE_STRNCMP
661 ti_int_t ret = -1;
662 while (n)
664 if ((ret = *lhs - *rhs++) != 0 || !*lhs++)
665 break;
666 n--;
668 return ((ret == 0)? TI_TRUE : TI_FALSE);
669 #else
670 return ((__strncmp(lhs, rhs, n) == 0)? TI_TRUE : TI_FALSE);
671 #endif
674 /*! format a string and place it in a buffer
676 * \param s: the buffer to place the result into
677 * \param n: the size of the buffer, including the trailing null space
678 * \param fmt: the format string to use
679 * \param args: arguments for the format string
681 * return the number of characters which would be generated for the given input,
682 * excluding the trailing '\0'.
684 * format: %[flags][width][.precision][qualifier]type
686 * - flags:
687 * - default: right-justified, left-pad the output with spaces until the required length of output is attained.
688 * If combined with '0' (see below),
689 * it will cause the sign to become a space when positive,
690 * but the remaining characters will be zero-padded
691 * - -: left-justified, e.g. %-d
692 * - +: denote the sign '+' or '-' of a number
693 * - 0: use 0 instead of spaces to left-fill a fixed-length field
694 * - #: add prefix or suffix
695 * - %#o => add prefix: 0...
696 * - %#x => add prefix: 0x...
697 * - %#X => add prefix: 0X...
698 * - %#b => add prefix: 0b...
699 * - %#B => add prefix: 0B...
700 * - %#f => add prefix: 0f...
701 * - %#F => add prefix: 0F...
703 * - width:
704 * - n: n = 1, 2, 3, ..., fill spaces
705 * - 0n: n = 1, 2, 3, ..., fill 0
706 * - *: causes printf to pad the output until it is n characters wide,
707 * where n is an integer value stored in the a function argument just preceding
708 * that represented by the modified type.
709 * e.g. printf("%*d", 5, 10) will result in "10" being printed with a width of 5.
711 * - .precision:
712 * - .n: for non-integral numeric types, causes the decimal portion of the output to be expressed in at least number digits.
713 * for the string type, causes the output to be truncated at number characters.
714 * if the precision is zero, nothing is printed for the corresponding argument.
715 * - *: same as the above, but uses an integer value in the intaken argument to
716 * determine the number of decimal places or maximum string length.
717 * e.g. printf("%.*s", 3, "abcdef") will result in "abc" being printed.
719 * - qualifier:
720 * - h: short integer or single float-point
721 * - l: long integer or double float-point
722 * - I8: 8-bit integer
723 * - I16: 16-bit integer
724 * - I32: 32-bit integer
725 * - I64/ll: 64-bit integer
727 * \note:
728 * support: h, l, I8, I16, I32, I64, ll
729 * not support:
731 * - type(e.g. %d %x %u %% ...):
732 * - d, i: print an int as a signed decimal number.
733 * '%d' and '%i' are synonymous for output, but are different when used with scanf() for input.
734 * - u: print decimal unsigned int.
735 * - o: print an unsigned int in octal.
736 * - x/X: print an unsigned int as a hexadecimal number. 'x' uses lower-case letters and 'X' uses upper-case.
737 * - b/B: print an unsigned binary interger
738 * - e/E: print a double value in standard form ([-]d.ddd e[+/-]ddd).
739 * An E conversion uses the letter E (rather than e) to introduce the exponent.
740 * The exponent always contains at least two digits; if the value is zero, the exponent is 00.
741 * e.g. 3.141593e+00
742 * - f/F: Print a double in normal (fixed-point) notation.
743 * 'f' and 'F' only differs in how the strings for an infinite number or NaN are printed
744 * ('inf', 'infinity' and 'nan' for 'f', 'INF', 'INFINITY' and 'NAN' for 'F').
745 * - g/G: print a double in either normal or exponential notation, whichever is more appropriate for its magnitude.
746 * 'g' uses lower-case letters, 'G' uses upper-case letters.
747 * This type differs slightly from fixed-point notation in
748 * that insignificant zeroes to the right of the decimal point are not included.
749 * Also, the decimal point is not included on whole numbers.
750 * - c: print a char (character).
751 * - s: print a character string
752 * - p: print a void * (pointer to void) in an implementation-defined format.
753 * - n: print nothing, but write number of characters successfully written so far into an integer pointer parameter.
754 * - %: %
756 * \note:
757 * support: d, i, u, o, u, x/X, b/B, f/F, c, s, %
758 * not support: e/E, g/G, p, n
760 * e.g.
762 * ti_printf("|hello world|\n");
763 ti_printf("|%-10s|%%|%10s|\n", "hello", "world");
764 ti_printf("|%#2c|%2.5c|%*c|\n", 'A', 'B', 5, 'C');
765 ti_printf("|%#2d|%#8.3o|%*.*d|\n", -56, 56, 10, 5, 56);
766 ti_printf("|%#-8.5x|%#2.9X|\n", 0x1f, 0x1f);
767 ti_printf("|%#-8.5b|%#2.9B|\n", 0x1f, 0x1f);
768 ti_printf("|%-6Id|%5I8u|%#I64x|%#llx|\n", 256, 255, (ti_int64_t)0x8fffffffffff, (ti_int64_t)0x8fffffffffff);
769 ti_printf("|%f|\n", -3.1415926535897932384626433832795);
770 ti_printf("|%f|%f|%f|\n", 3.14, 0, -0);
771 ti_printf("|%0.9f|\n", 3.1415926535897932384626433832795);
772 ti_printf("|%16.9f|\n", 3.1415926535897932384626433832795);
773 ti_printf("|%016.9f|\n", 3.14159);
774 ti_printf("|%lf|\n", 1.0 / 6.0);
775 ti_printf("|%f|\n", 0.0003141596);
778 ti_int_t ti_vsnprintf(ti_char_t* s, ti_size_t n, ti_char_t const* fmt, ti_va_list_t args)
780 #ifndef TI_ARCH_SPEC_HAVE_VSNPRINTF
781 if ((ti_int_t)n < 0 || !s || !fmt) return 0;
783 ti_char_t* pb = s;
784 ti_char_t* pe = s + n;
786 // pe must be larger than pb
787 if (pe < pb)
789 pe = ((ti_char_t*)-1);
790 n = (ti_size_t)(pe - pb);
793 // parse format
794 ti_printf_entry_t e = {0};
795 ti_int_t en = 0;
796 while (*fmt)
798 ti_char_t const* ofmt = fmt;
800 // get an entry
801 en = ti_printf_entry(fmt, &e);
802 fmt += en;
804 switch (e.type)
806 // copy it if none type
807 case TI_PRINTF_TYPE_NONE:
809 ti_int_t copy_n = en;
810 if (pb < pe)
812 if (copy_n > pe - pb) copy_n = pe - pb;
813 ti_memcpy((ti_byte_t*)pb, (ti_byte_t const*)ofmt, copy_n);
814 pb += copy_n;
816 break;
818 // get a character for %c
819 case TI_PRINTF_TYPE_CHAR:
821 // char: %
822 if (e.extra & TI_PRINTF_EXTRA_PERCENT)
824 if (pb < pe) *pb++ = '%';
826 // char: %c
827 else
829 // fill space at left side, e.g. " a"
830 if (!(e.flags & TI_PRINTF_FLAG_LEFT))
832 while (--e.width > 0)
834 if (pb < pe) *pb++ = ' ';
838 if (pb < pe) *pb++ = (ti_char_t)TI_VA_ARG(args, ti_int_t);
840 // fill space at right side, e.g. "a "
841 while (--e.width > 0)
843 if (pb < pe) *pb++ = ' ';
846 break;
848 // get field width for *
849 case TI_PRINTF_TYPE_WIDTH:
850 e.width = TI_VA_ARG(args, ti_int_t);
851 break;
852 // get precision for *
853 case TI_PRINTF_TYPE_PRECISION:
854 e.precision = TI_VA_ARG(args, ti_int_t);
855 break;
856 // get string for %s
857 case TI_PRINTF_TYPE_STRING:
859 pb = ti_printf_string(pb, pe, e, TI_VA_ARG(args, ti_char_t const*));
860 break;
862 // get an integer for %d %u %x ...
863 case TI_PRINTF_TYPE_INT:
865 ti_uint64_t num = 0;
866 if (e.extra & TI_PRINTF_EXTRA_SIGNED)
868 switch (e.qual)
870 case TI_PRINTF_QUAL_I: num = TI_VA_ARG(args, ti_int_t); break;
871 case TI_PRINTF_QUAL_I8: num = (ti_int8_t)TI_VA_ARG(args, ti_int_t); break;
872 case TI_PRINTF_QUAL_I16: num = (ti_int16_t)TI_VA_ARG(args, ti_int_t); break;
873 case TI_PRINTF_QUAL_I32: num = TI_VA_ARG(args, ti_int32_t); break;
874 case TI_PRINTF_QUAL_I64: num = TI_VA_ARG(args, ti_int64_t); break;
875 default: num = TI_VA_ARG(args, ti_int_t); break;
878 else
880 switch (e.qual)
882 case TI_PRINTF_QUAL_I: num = TI_VA_ARG(args, ti_uint_t); break;
883 case TI_PRINTF_QUAL_I8: num = (ti_uint8_t)TI_VA_ARG(args, ti_uint_t); break;
884 case TI_PRINTF_QUAL_I16: num = (ti_uint16_t)TI_VA_ARG(args, ti_uint_t); break;
885 case TI_PRINTF_QUAL_I32: num = TI_VA_ARG(args, ti_uint32_t); break;
886 case TI_PRINTF_QUAL_I64: num = TI_VA_ARG(args, ti_uint64_t); break;
887 default: num = TI_VA_ARG(args, ti_uint_t); break;
890 pb = ti_printf_integer(pb, pe, e, num);
891 break;
893 #ifdef TI_CONFIG_FLOAT
894 case TI_PRINTF_TYPE_FLOAT:
896 ti_float_t num = TI_VA_ARG(args, ti_float_t);
897 pb = ti_printf_float(pb, pe, e, num);
898 break;
900 #endif
901 case TI_PRINTF_TYPE_INVALID:
903 if (pb < pe) *pb++ = '%';
904 break;
906 default:
907 break;
911 // != 0, the string is null-truncated
912 if (n > 0)
914 if (pb < pe) *pb = '\0';
915 else pe[-1] = '\0';
918 // the trailing null byte doesn't count towards the total
919 return (pb - s);
920 #else
921 return __vsnprintf(s, n, fmt, args);
922 #endif
924 ti_int_t ti_vsprintf(ti_char_t* s, ti_char_t const* fmt, ti_va_list_t args)
926 #ifndef TI_ARCH_SPEC_HAVE_VSPRINTF
927 return ti_vsnprintf(s, TI_INT_MAX, fmt, args);
928 #else
929 return __vsprintf(s, fmt, args);
930 #endif
932 ti_int_t ti_snprintf(ti_char_t* s, ti_size_t n, ti_char_t const* fmt, ...)
934 ti_va_list_t args;
935 ti_int_t i;
937 TI_VA_START(args, fmt);
938 i = ti_vsnprintf(s, n, fmt, args);
939 TI_VA_END(args);
941 return i;
943 ti_int_t ti_sprintf(ti_char_t* s, ti_char_t const* fmt, ...)
945 ti_va_list_t args;
946 ti_int_t i;
948 TI_VA_START(args, fmt);
949 i = ti_vsnprintf(s, TI_INT_MAX, fmt, args);
950 TI_VA_END(args);
952 return i;