os-workshop

same materials and sample source for RV32 OS projects
git clone http://frotz.net/git/os-workshop.git
Log | Files | Refs

printf.c (18488B)


      1 /*
      2  * Copyright (c) 2008-2014 Travis Geiselbrecht
      3  *
      4  * Use of this source code is governed by a MIT-style
      5  * license that can be found in the LICENSE file or at
      6  * https://opensource.org/licenses/MIT
      7  */
      8 #include <assert.h>
      9 #include <limits.h>
     10 #include <stdarg.h>
     11 #include <stdbool.h>
     12 #include <stdint.h>
     13 #include <stdio.h>
     14 #include <string.h>
     15 
     16 #include "printf-engine.h"
     17 
     18 #define __NO_INLINE __attribute((noinline))
     19 
     20 #ifndef WITH_PRINTF_FP
     21 #define WITH_PRINTF_FP 0
     22 #endif
     23 #ifndef WITH_PRINTF_LL
     24 #define WITH_PRINTF_LL 0
     25 #endif
     26 
     27 int sprintf(char *str, const char *fmt, ...) {
     28     int err;
     29 
     30     va_list ap;
     31     va_start(ap, fmt);
     32     err = vsprintf(str, fmt, ap);
     33     va_end(ap);
     34 
     35     return err;
     36 }
     37 
     38 int snprintf(char *str, size_t len, const char *fmt, ...) {
     39     int err;
     40 
     41     va_list ap;
     42     va_start(ap, fmt);
     43     err = vsnprintf(str, len, fmt, ap);
     44     va_end(ap);
     45 
     46     return err;
     47 }
     48 
     49 int vsprintf(char *str, const char *fmt, va_list ap) {
     50     return vsnprintf(str, INT_MAX, fmt, ap);
     51 }
     52 
     53 struct _output_args {
     54     char *outstr;
     55     size_t len;
     56     size_t pos;
     57 };
     58 
     59 static int _vsnprintf_output(const char *str, size_t len, void *state) {
     60     struct _output_args *args = state;
     61 
     62     size_t count = 0;
     63     while (count < len) {
     64         if (args->pos < args->len) {
     65             args->outstr[args->pos++] = *str;
     66         }
     67 
     68         str++;
     69         count++;
     70     }
     71 
     72     return count;
     73 }
     74 
     75 int vsnprintf(char *str, size_t len, const char *fmt, va_list ap) {
     76     struct _output_args args;
     77     int wlen;
     78 
     79     args.outstr = str;
     80     args.len = len;
     81     args.pos = 0;
     82 
     83     wlen = _printf_engine(&_vsnprintf_output, (void *)&args, fmt, ap);
     84     if (args.pos >= len)
     85         str[len-1] = '\0';
     86     else
     87         str[wlen] = '\0';
     88     return wlen;
     89 }
     90 
     91 #define LONGFLAG       0x00000001
     92 #define LONGLONGFLAG   0x00000002
     93 #define HALFFLAG       0x00000004
     94 #define HALFHALFFLAG   0x00000008
     95 #define SIZETFLAG      0x00000010
     96 #define INTMAXFLAG     0x00000020
     97 #define PTRDIFFFLAG    0x00000040
     98 #define ALTFLAG        0x00000080
     99 #define CAPSFLAG       0x00000100
    100 #define SHOWSIGNFLAG   0x00000200
    101 #define SIGNEDFLAG     0x00000400
    102 #define LEFTFORMATFLAG 0x00000800
    103 #define LEADZEROFLAG   0x00001000
    104 #define BLANKPOSFLAG   0x00002000
    105 
    106 #if WITH_PRINTF_LL
    107 typedef unsigned long long x_ull_t;
    108 typedef long long x_ll_t;
    109 #else
    110 typedef unsigned long x_ull_t;
    111 typedef long x_ll_t;
    112 #endif
    113 
    114 __NO_INLINE static char *longlong_to_string(char *buf, x_ull_t n, size_t len, unsigned flag, char *signchar) {
    115     size_t pos = len;
    116     int negative = 0;
    117 
    118     if ((flag & SIGNEDFLAG) && (x_ll_t)n < 0) {
    119         negative = 1;
    120         n = -n;
    121     }
    122 
    123     buf[--pos] = 0;
    124 
    125     /* only do the math if the number is >= 10 */
    126     while (n >= 10) {
    127         int digit = n % 10;
    128 
    129         n /= 10;
    130 
    131         buf[--pos] = digit + '0';
    132     }
    133     buf[--pos] = n + '0';
    134 
    135     if (negative)
    136         *signchar = '-';
    137     else if ((flag & SHOWSIGNFLAG))
    138         *signchar = '+';
    139     else if ((flag & BLANKPOSFLAG))
    140         *signchar = ' ';
    141     else
    142         *signchar = '\0';
    143 
    144     return &buf[pos];
    145 }
    146 
    147 static const char hextable[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
    148 static const char hextable_caps[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
    149 
    150 __NO_INLINE static char *longlong_to_hexstring(char *buf, x_ull_t u, size_t len, unsigned flag) {
    151     size_t pos = len;
    152     const char *table = (flag & CAPSFLAG) ? hextable_caps : hextable;
    153 
    154     buf[--pos] = 0;
    155     do {
    156         unsigned int digit = u % 16;
    157         u /= 16;
    158 
    159         buf[--pos] = table[digit];
    160     } while (u != 0);
    161 
    162     return &buf[pos];
    163 }
    164 
    165 #if WITH_PRINTF_FP
    166 union double_int {
    167     double d;
    168     uint64_t i;
    169 };
    170 
    171 #define OUT(c) buf[pos++] = (c)
    172 #define OUTSTR(str) do { for (size_t i = 0; (str)[i] != 0; i++) OUT((str)[i]); } while (0)
    173 
    174 /* print up to a 4 digit exponent as string, with sign */
    175 __NO_INLINE static size_t exponent_to_string(char *buf, int32_t exponent) {
    176     size_t pos = 0;
    177 
    178     /* handle sign */
    179     if (exponent < 0) {
    180         OUT('-');
    181         exponent = -exponent;
    182     } else {
    183         OUT('+');
    184     }
    185 
    186     /* see how far we need to bump into the string to print from the right */
    187     if (exponent >= 1000) pos += 4;
    188     else if (exponent >= 100) pos += 3;
    189     else if (exponent >= 10) pos += 2;
    190     else pos++;
    191 
    192     /* print decimal string, from the right */
    193     unsigned i = pos;
    194     do {
    195         unsigned digit = (uint32_t)exponent % 10;
    196 
    197         buf[--i] = digit + '0';
    198 
    199         exponent /= 10;
    200     } while (exponent != 0);
    201 
    202     /* return number of characters printed */
    203     return pos;
    204 }
    205 
    206 __NO_INLINE static char *double_to_string(char *buf, size_t len, double d, unsigned flag) {
    207     size_t pos = 0;
    208     union double_int u = { d };
    209 
    210     uint32_t exponent = (u.i >> 52) & 0x7ff;
    211     uint64_t fraction = (u.i & ((1ULL << 52) - 1));
    212     bool neg = !!(u.i & (1ULL << 63));
    213 
    214     /* start constructing the string */
    215     if (neg) {
    216         OUT('-');
    217         d = -d;
    218     }
    219 
    220     /* longest:
    221      * 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.000000o
    222      */
    223 
    224     /* look for special cases */
    225     if (exponent == 0x7ff) {
    226         if (fraction == 0) {
    227             /* infinity */
    228             if (flag & CAPSFLAG) OUTSTR("INF");
    229             else OUTSTR("inf");
    230         } else {
    231             /* NaN */
    232             if (flag & CAPSFLAG) OUTSTR("NAN");
    233             else OUTSTR("nan");
    234         }
    235     } else if (exponent == 0) {
    236         if (fraction == 0) {
    237             /* zero */
    238             OUTSTR("0.000000");
    239         } else {
    240             /* denormalized */
    241             /* XXX does not handle */
    242             if (flag & CAPSFLAG) OUTSTR("DEN");
    243             else OUTSTR("den");
    244         }
    245     } else {
    246         /* see if it's in the range of floats we can easily print */
    247         int exponent_signed = exponent - 1023;
    248         if (exponent_signed < -52 || exponent_signed > 52) {
    249             OUTSTR("<range>");
    250         } else {
    251             /* start by walking backwards through the string */
    252 #define OUTREV(c) do { if (&buf[pos] == buf) goto done; else buf[--pos] = (c); } while (0)
    253             pos = len;
    254             OUTREV(0);
    255 
    256             /* reserve space for the fractional component first */
    257             for (int i = 0; i <= 6; i++)
    258                 OUTREV('0');
    259             size_t decimal_spot = pos;
    260 
    261             /* print the integer portion */
    262             uint64_t u;
    263             if (exponent_signed >= 0) {
    264                 u = fraction;
    265                 u |= (1ULL<<52);
    266                 u >>= (52 - exponent_signed);
    267 
    268                 char *s = longlong_to_string(buf, u, pos + 1, flag, &(char) {0});
    269 
    270                 pos = s - buf;
    271             } else {
    272                 /* exponent is negative */
    273                 u = 0;
    274                 OUTREV('0');
    275             }
    276 
    277             buf[decimal_spot] = '.';
    278 
    279             /* handle the fractional part */
    280             uint32_t frac = ((d - u) * 1000000) + .5;
    281 
    282             unsigned i = decimal_spot + 6 + 1;
    283             while (frac != 0) {
    284                 unsigned digit = frac % 10;
    285 
    286                 buf[--i] = digit + '0';
    287 
    288                 frac /= 10;
    289             }
    290 
    291             if (neg)
    292                 OUTREV('-');
    293 
    294 done:
    295             /* separate return path, since we've been walking backwards through the string */
    296             return &buf[pos];
    297         }
    298 #undef OUTREV
    299     }
    300 
    301     buf[pos] = 0;
    302     return buf;
    303 }
    304 
    305 __NO_INLINE static char *double_to_hexstring(char *buf, size_t len, double d, unsigned flag) {
    306     size_t pos = 0;
    307     union double_int u = { d };
    308 
    309     uint32_t exponent = (u.i >> 52) & 0x7ff;
    310     uint64_t fraction = (u.i & ((1ULL << 52) - 1));
    311     bool neg = !!(u.i & (1ULL << 63));
    312 
    313     /* start constructing the string */
    314     if (neg) {
    315         OUT('-');
    316     }
    317 
    318     /* look for special cases */
    319     if (exponent == 0x7ff) {
    320         if (fraction == 0) {
    321             /* infinity */
    322             if (flag & CAPSFLAG) OUTSTR("INF");
    323             else OUTSTR("inf");
    324         } else {
    325             /* NaN */
    326             if (flag & CAPSFLAG) OUTSTR("NAN");
    327             else OUTSTR("nan");
    328         }
    329     } else if (exponent == 0) {
    330         if (fraction == 0) {
    331             /* zero */
    332             if (flag & CAPSFLAG) OUTSTR("0X0P+0");
    333             else OUTSTR("0x0p+0");
    334         } else {
    335             /* denormalized */
    336             /* XXX does not handle */
    337             if (flag & CAPSFLAG) OUTSTR("DEN");
    338             else OUTSTR("den");
    339         }
    340     } else {
    341         /* regular normalized numbers:
    342          * 0x1p+1
    343          * 0x1.0000000000001p+1
    344          * 0X1.FFFFFFFFFFFFFP+1023
    345          * 0x1.FFFFFFFFFFFFFP+1023
    346          */
    347         int exponent_signed = exponent - 1023;
    348 
    349         /* implicit 1. */
    350         if (flag & CAPSFLAG) OUTSTR("0X1");
    351         else OUTSTR("0x1");
    352 
    353         /* select the appropriate hex case table */
    354         const char *table = (flag & CAPSFLAG) ? hextable_caps : hextable;
    355 
    356         int zero_count = 0;
    357         bool output_dot = false;
    358         for (int i = 52 - 4; i >= 0; i -= 4) {
    359             unsigned digit = (fraction >> i) & 0xf;
    360 
    361             if (digit == 0) {
    362                 zero_count++;
    363             } else {
    364                 /* output a . the first time we output a char */
    365                 if (!output_dot) {
    366                     OUT('.');
    367                     output_dot = true;
    368                 }
    369                 /* if we have a non zero digit, see if we need to output a string of zeros */
    370                 while (zero_count > 0) {
    371                     OUT('0');
    372                     zero_count--;
    373                 }
    374                 buf[pos++] = table[digit];
    375             }
    376         }
    377 
    378         /* handle the exponent */
    379         buf[pos++] = (flag & CAPSFLAG) ? 'P' : 'p';
    380         pos += exponent_to_string(&buf[pos], exponent_signed);
    381     }
    382 
    383     buf[pos] = 0;
    384     return buf;
    385 }
    386 
    387 #undef OUT
    388 #undef OUTSTR
    389 
    390 #endif
    391 
    392 int _printf_engine(_printf_engine_output_func out, void *state, const char *fmt, va_list ap) {
    393     int err = 0;
    394     char c;
    395     unsigned char uc;
    396     const char *s;
    397     size_t string_len;
    398     x_ull_t n;
    399     void *ptr;
    400     int flags;
    401     unsigned int format_num;
    402     char signchar;
    403     size_t chars_written = 0;
    404     char num_buffer[32];
    405 
    406 #define OUTPUT_STRING(str, len) do { err = out(str, len, state); if (err < 0) { goto exit; } else { chars_written += err; } } while(0)
    407 #define OUTPUT_CHAR(c) do { char __temp[1] = { c }; OUTPUT_STRING(__temp, 1); } while (0)
    408 
    409     for (;;) {
    410         /* reset the format state */
    411         flags = 0;
    412         format_num = 0;
    413         signchar = '\0';
    414 
    415         /* handle regular chars that aren't format related */
    416         s = fmt;
    417         string_len = 0;
    418         while ((c = *fmt++) != 0) {
    419             if (c == '%')
    420                 break; /* we saw a '%', break and start parsing format */
    421             string_len++;
    422         }
    423 
    424         /* output the string we've accumulated */
    425         OUTPUT_STRING(s, string_len);
    426 
    427         /* make sure we haven't just hit the end of the string */
    428         if (c == 0)
    429             break;
    430 
    431 next_format:
    432         /* grab the next format character */
    433         c = *fmt++;
    434         if (c == 0)
    435             break;
    436 
    437         switch (c) {
    438             case '0'...'9':
    439                 if (c == '0' && format_num == 0)
    440                     flags |= LEADZEROFLAG;
    441                 format_num *= 10;
    442                 format_num += c - '0';
    443                 goto next_format;
    444             case '.':
    445                 /* XXX for now eat numeric formatting */
    446                 goto next_format;
    447             case '%':
    448                 OUTPUT_CHAR('%');
    449                 break;
    450             case 'c':
    451                 uc = va_arg(ap, unsigned int);
    452                 OUTPUT_CHAR(uc);
    453                 break;
    454             case 's':
    455                 s = va_arg(ap, const char *);
    456                 if (s == 0)
    457                     s = "<null>";
    458                 flags &= ~LEADZEROFLAG; /* doesn't make sense for strings */
    459                 goto _output_string;
    460             case '-':
    461                 flags |= LEFTFORMATFLAG;
    462                 goto next_format;
    463             case '+':
    464                 flags |= SHOWSIGNFLAG;
    465                 goto next_format;
    466             case ' ':
    467                 flags |= BLANKPOSFLAG;
    468                 goto next_format;
    469             case '#':
    470                 flags |= ALTFLAG;
    471                 goto next_format;
    472             case 'l':
    473                 if (flags & LONGFLAG)
    474                     flags |= LONGLONGFLAG;
    475                 flags |= LONGFLAG;
    476                 goto next_format;
    477             case 'h':
    478                 if (flags & HALFFLAG)
    479                     flags |= HALFHALFFLAG;
    480                 flags |= HALFFLAG;
    481                 goto next_format;
    482             case 'z':
    483                 flags |= SIZETFLAG;
    484                 goto next_format;
    485             case 'j':
    486                 flags |= INTMAXFLAG;
    487                 goto next_format;
    488             case 't':
    489                 flags |= PTRDIFFFLAG;
    490                 goto next_format;
    491             case 'i':
    492             case 'd':
    493                 n = (flags & LONGLONGFLAG) ? va_arg(ap, long long) :
    494                     (flags & LONGFLAG) ? va_arg(ap, long) :
    495                     (flags & HALFHALFFLAG) ? (signed char)va_arg(ap, int) :
    496                     (flags & HALFFLAG) ? (short)va_arg(ap, int) :
    497                     //(flags & SIZETFLAG) ? va_arg(ap, ssize_t) :  // no ssize_t available
    498                     (flags & INTMAXFLAG) ? va_arg(ap, intmax_t) :
    499                     (flags & PTRDIFFFLAG) ? va_arg(ap, ptrdiff_t) :
    500                     va_arg(ap, int);
    501                 flags |= SIGNEDFLAG;
    502                 s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags, &signchar);
    503                 goto _output_string;
    504             case 'u':
    505                 n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) :
    506                     (flags & LONGFLAG) ? va_arg(ap, unsigned long) :
    507                     (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) :
    508                     (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) :
    509                     (flags & SIZETFLAG) ? va_arg(ap, size_t) :
    510                     (flags & INTMAXFLAG) ? va_arg(ap, uintmax_t) :
    511                     (flags & PTRDIFFFLAG) ? (uintptr_t)va_arg(ap, ptrdiff_t) :
    512                     va_arg(ap, unsigned int);
    513                 s = longlong_to_string(num_buffer, n, sizeof(num_buffer), flags, &signchar);
    514                 goto _output_string;
    515             case 'p':
    516                 flags |= LONGFLAG | ALTFLAG;
    517                 goto hex;
    518             case 'X':
    519                 flags |= CAPSFLAG;
    520                 /* fallthrough */
    521 hex:
    522             case 'x':
    523                 n = (flags & LONGLONGFLAG) ? va_arg(ap, unsigned long long) :
    524                     (flags & LONGFLAG) ? va_arg(ap, unsigned long) :
    525                     (flags & HALFHALFFLAG) ? (unsigned char)va_arg(ap, unsigned int) :
    526                     (flags & HALFFLAG) ? (unsigned short)va_arg(ap, unsigned int) :
    527                     (flags & SIZETFLAG) ? va_arg(ap, size_t) :
    528                     (flags & INTMAXFLAG) ? va_arg(ap, uintmax_t) :
    529                     (flags & PTRDIFFFLAG) ? (uintptr_t)va_arg(ap, ptrdiff_t) :
    530                     va_arg(ap, unsigned int);
    531                 s = longlong_to_hexstring(num_buffer, n, sizeof(num_buffer), flags);
    532                 if (flags & ALTFLAG) {
    533                     OUTPUT_CHAR('0');
    534                     OUTPUT_CHAR((flags & CAPSFLAG) ? 'X': 'x');
    535                 }
    536                 goto _output_string;
    537             case 'n':
    538                 ptr = va_arg(ap, void *);
    539                 if (flags & LONGLONGFLAG)
    540                     *(long long *)ptr = chars_written;
    541                 else if (flags & LONGFLAG)
    542                     *(long *)ptr = chars_written;
    543                 else if (flags & HALFHALFFLAG)
    544                     *(signed char *)ptr = chars_written;
    545                 else if (flags & HALFFLAG)
    546                     *(short *)ptr = chars_written;
    547                 else if (flags & SIZETFLAG)
    548                     *(size_t *)ptr = chars_written;
    549                 else
    550                     *(int *)ptr = chars_written;
    551                 break;
    552 #if WITH_PRINTF_FP
    553             case 'F':
    554                 flags |= CAPSFLAG;
    555             /* fallthrough */
    556             case 'f': {
    557                 double d = va_arg(ap, double);
    558                 s = double_to_string(num_buffer, sizeof(num_buffer), d, flags);
    559                 goto _output_string;
    560             }
    561             case 'A':
    562                 flags |= CAPSFLAG;
    563             /* fallthrough */
    564             case 'a': {
    565                 double d = va_arg(ap, double);
    566                 s = double_to_hexstring(num_buffer, sizeof(num_buffer), d, flags);
    567                 goto _output_string;
    568             }
    569 #endif
    570             default:
    571                 OUTPUT_CHAR('%');
    572                 OUTPUT_CHAR(c);
    573                 break;
    574         }
    575 
    576         /* move on to the next field */
    577         continue;
    578 
    579         /* shared output code */
    580 _output_string:
    581         string_len = strlen(s);
    582 
    583         if (flags & LEFTFORMATFLAG) {
    584             /* left justify the text */
    585             OUTPUT_STRING(s, string_len);
    586             unsigned written = err;
    587 
    588             /* pad to the right (if necessary) */
    589             for (; format_num > written; format_num--)
    590                 OUTPUT_CHAR(' ');
    591         } else {
    592             /* right justify the text (digits) */
    593 
    594             /* if we're going to print a sign digit,
    595                it'll chew up one byte of the format size */
    596             if (signchar != '\0' && format_num > 0)
    597                 format_num--;
    598 
    599             /* output the sign char before the leading zeros */
    600             if (flags & LEADZEROFLAG && signchar != '\0')
    601                 OUTPUT_CHAR(signchar);
    602 
    603             /* pad according to the format string */
    604             for (; format_num > string_len; format_num--)
    605                 OUTPUT_CHAR(flags & LEADZEROFLAG ? '0' : ' ');
    606 
    607             /* if not leading zeros, output the sign char just before the number */
    608             if (!(flags & LEADZEROFLAG) && signchar != '\0')
    609                 OUTPUT_CHAR(signchar);
    610 
    611             /* output the string */
    612             OUTPUT_STRING(s, string_len);
    613         }
    614         continue;
    615     }
    616 
    617 #undef OUTPUT_STRING
    618 #undef OUTPUT_CHAR
    619 
    620 exit:
    621     return (err < 0) ? err : (int)chars_written;
    622 }