kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

printf.c (13678B)


      1 //===-- printf.c - kit freestanding printf routines ---------------------===//
      2 //
      3 // Based on the mpaland/printf MIT-licensed formatter, imported for kit's
      4 // freestanding runtime and reduced to the top-level <stdio.h> declarations.
      5 // Floating-point formats are intentionally omitted until kit's backends can
      6 // compile the original mpaland conversion path portably.
      7 //
      8 //===----------------------------------------------------------------------===//
      9 
     10 #include <stdarg.h>
     11 #include <stddef.h>
     12 #include <stdint.h>
     13 
     14 typedef void (*KitPrintfCallback)(char ch, void* arg);
     15 
     16 typedef struct {
     17   char* buf;
     18   size_t cap;
     19   size_t len;
     20   KitPrintfCallback fct;
     21   void* arg;
     22 } KitPrintfOut;
     23 
     24 static void kit_out_ch(KitPrintfOut* out, char ch) {
     25   if (out->fct) {
     26     out->fct(ch, out->arg);
     27   } else if (out->cap && out->len + 1U < out->cap) {
     28     out->buf[out->len] = ch;
     29   }
     30   out->len++;
     31 }
     32 
     33 static void kit_out_repeat(KitPrintfOut* out, char ch, unsigned count) {
     34   while (count-- > 0) kit_out_ch(out, ch);
     35 }
     36 
     37 static unsigned kit_strlen_prec(const char* s, int has_prec, unsigned prec) {
     38   unsigned n = 0;
     39   if (!s) s = "(null)";
     40   while (s[n] != '\0' && (!has_prec || n < prec)) n++;
     41   return n;
     42 }
     43 
     44 static void kit_out_string(KitPrintfOut* out, const char* s, int left,
     45                            unsigned width, int has_prec, unsigned prec) {
     46   unsigned len = kit_strlen_prec(s, has_prec, prec);
     47   if (!s) s = "(null)";
     48   if (!left && width > len) kit_out_repeat(out, ' ', width - len);
     49   for (unsigned i = 0; i < len; i++) kit_out_ch(out, s[i]);
     50   if (left && width > len) kit_out_repeat(out, ' ', width - len);
     51 }
     52 
     53 static void kit_out_uint(KitPrintfOut* out, unsigned long long value,
     54                          unsigned base, int upper, int neg, int left, int zero,
     55                          unsigned width) {
     56   char tmp[64];
     57   unsigned len = 0;
     58   unsigned digits_len;
     59   const char* digits = upper ? "0123456789ABCDEF" : "0123456789abcdef";
     60   unsigned prefix = neg ? 1U : 0U;
     61   char pad = (zero && !left) ? '0' : ' ';
     62 
     63   do {
     64     tmp[len++] = digits[value % base];
     65     value /= base;
     66   } while (value != 0);
     67   digits_len = len;
     68 
     69   if (!left && pad == ' ' && width > len + prefix) {
     70     kit_out_repeat(out, ' ', width - len - prefix);
     71   }
     72   if (neg) kit_out_ch(out, '-');
     73   if (!left && pad == '0' && width > len + prefix) {
     74     kit_out_repeat(out, '0', width - len - prefix);
     75   }
     76   while (len > 0) kit_out_ch(out, tmp[--len]);
     77   if (left && width > digits_len + prefix) {
     78     kit_out_repeat(out, ' ', width - digits_len - prefix);
     79   }
     80 }
     81 
     82 static void kit_out_padded_string(KitPrintfOut* out, const char* s,
     83                                   unsigned len, int neg, int plus, int space,
     84                                   int left, int zero, unsigned width) {
     85   unsigned prefix = (neg || plus || space) ? 1U : 0U;
     86   char sign = neg ? '-' : (plus ? '+' : ' ');
     87   char pad = (zero && !left) ? '0' : ' ';
     88 
     89   if (!left && pad == ' ' && width > len + prefix) {
     90     kit_out_repeat(out, ' ', width - len - prefix);
     91   }
     92   if (prefix) kit_out_ch(out, sign);
     93   if (!left && pad == '0' && width > len + prefix) {
     94     kit_out_repeat(out, '0', width - len - prefix);
     95   }
     96   for (unsigned i = 0; i < len; i++) kit_out_ch(out, s[i]);
     97   if (left && width > len + prefix) {
     98     kit_out_repeat(out, ' ', width - len - prefix);
     99   }
    100 }
    101 
    102 static int kit_double_negative(double v) {
    103   union {
    104     double d;
    105     uint64_t u;
    106   } bits;
    107   bits.d = v;
    108   return (int)(bits.u >> 63);
    109 }
    110 
    111 static double kit_double_abs(double v) { return v < 0.0 ? -v : v; }
    112 
    113 static double kit_pow10_u(unsigned n) {
    114   double value = 1.0;
    115   while (n-- > 0) value *= 10.0;
    116   return value;
    117 }
    118 
    119 static void kit_format_fixed_abs(KitPrintfOut* out, double value, unsigned prec,
    120                                  int alt) {
    121   double rounded;
    122   double pow10 = 1.0;
    123   int emitted = 0;
    124 
    125   if (prec > 18U) prec = 18U;
    126   rounded = value + 0.5 / kit_pow10_u(prec);
    127 
    128   if (rounded >= 1.0) {
    129     for (;;) {
    130       double next = pow10 * 10.0;
    131       if (!(next > pow10) || next > rounded) break;
    132       pow10 = next;
    133     }
    134   }
    135 
    136   while (pow10 >= 1.0) {
    137     int digit = (int)(rounded / pow10);
    138     if (digit < 0) digit = 0;
    139     if (digit > 9) digit = 9;
    140     kit_out_ch(out, (char)('0' + digit));
    141     rounded -= (double)digit * pow10;
    142     pow10 /= 10.0;
    143     emitted = 1;
    144   }
    145   if (!emitted) kit_out_ch(out, '0');
    146 
    147   if (prec != 0 || alt) kit_out_ch(out, '.');
    148   for (unsigned i = 0; i < prec; i++) {
    149     int digit;
    150     rounded *= 10.0;
    151     digit = (int)rounded;
    152     if (digit < 0) digit = 0;
    153     if (digit > 9) digit = 9;
    154     kit_out_ch(out, (char)('0' + digit));
    155     rounded -= (double)digit;
    156   }
    157 }
    158 
    159 static int kit_decimal_exp(double value) {
    160   int exp = 0;
    161   if (value == 0.0) return 0;
    162   while (value >= 10.0) {
    163     value /= 10.0;
    164     exp++;
    165   }
    166   while (value < 1.0) {
    167     value *= 10.0;
    168     exp--;
    169   }
    170   return exp;
    171 }
    172 
    173 static double kit_normalize_decimal(double value, int* exp) {
    174   *exp = 0;
    175   if (value == 0.0) return 0.0;
    176   while (value >= 10.0) {
    177     value /= 10.0;
    178     (*exp)++;
    179   }
    180   while (value < 1.0) {
    181     value *= 10.0;
    182     (*exp)--;
    183   }
    184   return value;
    185 }
    186 
    187 static void kit_format_exp_abs(KitPrintfOut* out, double value, unsigned prec,
    188                                int upper, int alt) {
    189   int exp;
    190   unsigned e;
    191   char digits[12];
    192   unsigned len = 0;
    193   double norm;
    194 
    195   if (prec > 18U) prec = 18U;
    196   norm = kit_normalize_decimal(value, &exp);
    197   if (norm + 0.5 / kit_pow10_u(prec) >= 10.0) {
    198     norm /= 10.0;
    199     exp++;
    200   }
    201 
    202   kit_format_fixed_abs(out, norm, prec, alt);
    203   kit_out_ch(out, upper ? 'E' : 'e');
    204   if (exp < 0) {
    205     kit_out_ch(out, '-');
    206     e = (unsigned)-exp;
    207   } else {
    208     kit_out_ch(out, '+');
    209     e = (unsigned)exp;
    210   }
    211 
    212   do {
    213     digits[len++] = (char)('0' + (e % 10U));
    214     e /= 10U;
    215   } while (e != 0U);
    216   while (len < 2U) digits[len++] = '0';
    217   while (len > 0) kit_out_ch(out, digits[--len]);
    218 }
    219 
    220 static void kit_trim_float(char* s, unsigned* len, int alt) {
    221   unsigned end = *len;
    222   unsigned exp_start = end;
    223   unsigned trim_end;
    224   unsigned dot_pos = end;
    225   unsigned i;
    226 
    227   if (alt) return;
    228   for (i = 0; i < end; i++) {
    229     if (s[i] == 'e' || s[i] == 'E') {
    230       exp_start = i;
    231       break;
    232     }
    233   }
    234   trim_end = exp_start;
    235   for (i = 0; i < exp_start; i++) {
    236     if (s[i] == '.') {
    237       dot_pos = i;
    238       break;
    239     }
    240   }
    241   if (dot_pos == end) return;
    242 
    243   while (trim_end > dot_pos + 1U && s[trim_end - 1U] == '0') trim_end--;
    244   if (trim_end > dot_pos && s[trim_end - 1U] == '.') trim_end--;
    245 
    246   if (exp_start < end) {
    247     unsigned src = exp_start;
    248     while (src < end) s[trim_end++] = s[src++];
    249   }
    250   *len = trim_end;
    251   s[*len] = '\0';
    252 }
    253 
    254 static void kit_format_float(KitPrintfOut* out, double value, char spec,
    255                              int left, int zero, int plus, int space, int alt,
    256                              unsigned width, int has_prec, unsigned prec) {
    257   char tmp[384];
    258   KitPrintfOut tmp_out;
    259   unsigned len;
    260   int neg = kit_double_negative(value);
    261   double mag = kit_double_abs(value);
    262 
    263   if (!has_prec) prec = 6U;
    264   tmp_out.buf = tmp;
    265   tmp_out.cap = sizeof(tmp);
    266   tmp_out.len = 0U;
    267   tmp_out.fct = NULL;
    268   tmp_out.arg = NULL;
    269 
    270   if (value != value) {
    271     kit_out_string(&tmp_out, (spec >= 'A' && spec <= 'Z') ? "NAN" : "nan", 0, 0,
    272                    0, 0);
    273     neg = 0;
    274   } else if (mag > 1.7976931348623157e308) {
    275     kit_out_string(&tmp_out, (spec >= 'A' && spec <= 'Z') ? "INF" : "inf", 0, 0,
    276                    0, 0);
    277   } else if (spec == 'e' || spec == 'E') {
    278     kit_format_exp_abs(&tmp_out, mag, prec, spec == 'E', alt);
    279   } else if (spec == 'g' || spec == 'G') {
    280     int exp = kit_decimal_exp(mag);
    281     if (prec == 0U) prec = 1U;
    282     if (exp < -4 || exp >= (int)prec) {
    283       kit_format_exp_abs(&tmp_out, mag, prec - 1U, spec == 'G', alt);
    284     } else {
    285       unsigned fprec =
    286           exp >= 0 ? prec - (unsigned)exp - 1U : prec + (unsigned)(-exp) - 1U;
    287       kit_format_fixed_abs(&tmp_out, mag, fprec, alt);
    288     }
    289   } else {
    290     kit_format_fixed_abs(&tmp_out, mag, prec, alt);
    291   }
    292 
    293   len = tmp_out.len < sizeof(tmp) ? (unsigned)tmp_out.len
    294                                   : (unsigned)sizeof(tmp) - 1U;
    295   tmp[len] = '\0';
    296   if (spec == 'g' || spec == 'G') kit_trim_float(tmp, &len, alt);
    297   if (value != value) neg = 0;
    298   kit_out_padded_string(out, tmp, len, neg, plus && !neg, space && !neg, left,
    299                         zero, width);
    300 }
    301 
    302 static int kit_is_digit(char ch) { return ch >= '0' && ch <= '9'; }
    303 
    304 static unsigned kit_parse_uint(const char** p) {
    305   unsigned value = 0;
    306   while (kit_is_digit(**p)) {
    307     value = value * 10U + (unsigned)(*(*p)++ - '0');
    308   }
    309   return value;
    310 }
    311 
    312 static int kit_vprintf_impl(KitPrintfOut* out, const char* fmt, va_list ap) {
    313   while (*fmt) {
    314     int left = 0;
    315     int zero = 0;
    316     int plus = 0;
    317     int space = 0;
    318     int alt = 0;
    319     int long_mod = 0;
    320     int long_long_mod = 0;
    321     int has_prec = 0;
    322     unsigned width = 0;
    323     unsigned prec = 0;
    324 
    325     if (*fmt != '%') {
    326       kit_out_ch(out, *fmt++);
    327       continue;
    328     }
    329     fmt++;
    330     if (*fmt == '%') {
    331       kit_out_ch(out, *fmt++);
    332       continue;
    333     }
    334 
    335     for (;;) {
    336       if (*fmt == '-') {
    337         left = 1;
    338         fmt++;
    339       } else if (*fmt == '0') {
    340         zero = 1;
    341         fmt++;
    342       } else if (*fmt == '+') {
    343         plus = 1;
    344         fmt++;
    345       } else if (*fmt == ' ') {
    346         space = 1;
    347         fmt++;
    348       } else if (*fmt == '#') {
    349         alt = 1;
    350         fmt++;
    351       } else {
    352         break;
    353       }
    354     }
    355     if (*fmt == '*') {
    356       /* Width from an int arg; a negative value means left-justify (C11
    357        * 7.21.6.1p5: taken as a '-' flag plus a positive width). */
    358       int w = va_arg(ap, int);
    359       fmt++;
    360       if (w < 0) {
    361         left = 1;
    362         width = (unsigned)(-w);
    363       } else {
    364         width = (unsigned)w;
    365       }
    366     } else if (kit_is_digit(*fmt)) {
    367       width = kit_parse_uint(&fmt);
    368     }
    369     if (*fmt == '.') {
    370       fmt++;
    371       has_prec = 1;
    372       if (*fmt == '*') {
    373         /* Precision from an int arg; a negative value is taken as if the
    374          * precision were omitted (C11 7.21.6.1p5). */
    375         int p = va_arg(ap, int);
    376         fmt++;
    377         if (p < 0) {
    378           has_prec = 0;
    379         } else {
    380           prec = (unsigned)p;
    381         }
    382       } else {
    383         prec = kit_parse_uint(&fmt);
    384       }
    385     }
    386     if (*fmt == 'l') {
    387       fmt++;
    388       if (*fmt == 'l') {
    389         fmt++;
    390         long_long_mod = 1;
    391       } else {
    392         long_mod = 1;
    393       }
    394     } else if (*fmt == 'h') {
    395       fmt++;
    396       if (*fmt == 'h') fmt++;
    397     }
    398 
    399     switch (*fmt) {
    400       case 'd':
    401       case 'i': {
    402         long long v;
    403         unsigned long long mag;
    404         if (long_long_mod) {
    405           v = va_arg(ap, long long);
    406         } else if (long_mod) {
    407           v = va_arg(ap, long);
    408         } else {
    409           v = va_arg(ap, int);
    410         }
    411         mag = v < 0 ? 0ULL - (unsigned long long)v : (unsigned long long)v;
    412         kit_out_uint(out, mag, 10U, 0, v < 0, left, zero, width);
    413         break;
    414       }
    415       case 'u':
    416       case 'x':
    417       case 'X':
    418       case 'o': {
    419         unsigned long long v;
    420         unsigned base = *fmt == 'o' ? 8U : ((*fmt == 'u') ? 10U : 16U);
    421         if (long_long_mod) {
    422           v = va_arg(ap, unsigned long long);
    423         } else if (long_mod) {
    424           v = va_arg(ap, unsigned long);
    425         } else {
    426           v = va_arg(ap, unsigned int);
    427         }
    428         kit_out_uint(out, v, base, *fmt == 'X', 0, left, zero, width);
    429         break;
    430       }
    431       case 'p': {
    432         uintptr_t v = (uintptr_t)va_arg(ap, void*);
    433         kit_out_string(out, "0x", 0, 0, 0, 0);
    434         kit_out_uint(out, (unsigned long long)v, 16U, 0, 0, 0, 0, 0);
    435         break;
    436       }
    437       case 'c':
    438         kit_out_ch(out, (char)va_arg(ap, int));
    439         break;
    440       case 's':
    441         kit_out_string(out, va_arg(ap, const char*), left, width, has_prec,
    442                        prec);
    443         break;
    444       case 'f':
    445       case 'F':
    446       case 'e':
    447       case 'E':
    448       case 'g':
    449       case 'G':
    450         kit_format_float(out, va_arg(ap, double), *fmt, left, zero, plus, space,
    451                          alt, width, has_prec, prec);
    452         break;
    453       default:
    454         if (*fmt) kit_out_ch(out, *fmt);
    455         break;
    456     }
    457     if (*fmt) fmt++;
    458   }
    459 
    460   return (int)out->len;
    461 }
    462 
    463 static int kit_vsnprintf_impl(char* s, size_t n, const char* fmt, va_list ap) {
    464   KitPrintfOut out;
    465   int rc;
    466   out.buf = s;
    467   out.cap = s ? n : 0U;
    468   out.len = 0U;
    469   out.fct = NULL;
    470   out.arg = NULL;
    471 
    472   rc = kit_vprintf_impl(&out, fmt, ap);
    473   if (out.cap) {
    474     size_t pos = out.len < out.cap ? out.len : out.cap - 1U;
    475     out.buf[pos] = '\0';
    476   }
    477   return rc;
    478 }
    479 
    480 __attribute__((weak)) int vsnprintf(char* s, size_t n, const char* fmt,
    481                                     va_list ap) {
    482   va_list copy;
    483   int rc;
    484   va_copy(copy, ap);
    485   rc = kit_vsnprintf_impl(s, n, fmt, copy);
    486   va_end(copy);
    487   return rc;
    488 }
    489 
    490 __attribute__((weak)) int snprintf(char* s, size_t n, const char* fmt, ...) {
    491   va_list ap;
    492   int rc;
    493   va_start(ap, fmt);
    494   rc = vsnprintf(s, n, fmt, ap);
    495   va_end(ap);
    496   return rc;
    497 }
    498 
    499 __attribute__((weak)) int vsprintf(char* s, const char* fmt, va_list ap) {
    500   return vsnprintf(s, (size_t)-1, fmt, ap);
    501 }
    502 
    503 __attribute__((weak)) int sprintf(char* s, const char* fmt, ...) {
    504   va_list ap;
    505   int rc;
    506   va_start(ap, fmt);
    507   rc = vsprintf(s, fmt, ap);
    508   va_end(ap);
    509   return rc;
    510 }
    511 
    512 __attribute__((weak)) int fctprintf(void (*out_fct)(char ch, void* arg),
    513                                     void* arg, const char* fmt, ...) {
    514   KitPrintfOut out;
    515   va_list ap;
    516   int rc;
    517   out.buf = NULL;
    518   out.cap = 0U;
    519   out.len = 0U;
    520   out.fct = out_fct;
    521   out.arg = arg;
    522   va_start(ap, fmt);
    523   rc = kit_vprintf_impl(&out, fmt, ap);
    524   va_end(ap);
    525   return rc;
    526 }