Home | History | Annotate | Download | only in core
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <stdio.h>
     18 #include <printf.h>
     19 #include <cpu/cpuMath.h>
     20 
     21 #define FLAG_ALT            (1 << 0)
     22 #define FLAG_ZERO_EXTEND    (1 << 1)
     23 #define FLAG_IS_SIGNED      (1 << 2)
     24 #define FLAG_NEG_PAD        (1 << 3)
     25 #define FLAG_CAPS           (1 << 4)
     26 
     27 struct PrintfData
     28 {
     29     uint64_t    number;
     30     void       *userData;
     31     uint32_t    fieldWidth;
     32     uint32_t    precision;
     33     uint32_t    flags;
     34     uint8_t     posChar;
     35     uint8_t     base;
     36 };
     37 
     38 static uint32_t StrPrvPrintfEx_number(printf_write_c putc_, struct PrintfData *data, bool *bail)
     39 {
     40     char buf[64];
     41     uint32_t idx = sizeof(buf) - 1;
     42     uint32_t chr, i;
     43     uint32_t numPrinted = 0;
     44 
     45     *bail = false;
     46 
     47 #ifdef USE_PRINTF_FLAG_CHARS
     48     if (data->fieldWidth > sizeof(buf) - 1)
     49         data->fieldWidth = sizeof(buf) - 1;
     50 
     51     if (data->precision > sizeof(buf) - 1)
     52         data->precision = sizeof(buf) - 1;
     53 #endif
     54 
     55     buf[idx--] = 0;    //terminate
     56 
     57     if (data->flags & FLAG_IS_SIGNED) {
     58 
     59         if (((int64_t)data->number) < 0) {
     60 
     61             data->posChar = '-';
     62             data->number = -data->number;
     63         }
     64     }
     65 
     66     do {
     67         if (data->base == 8) {
     68 
     69             chr = (data->number & 0x07) + '0';
     70             data->number >>= 3;
     71         }
     72         else if (data->base == 10) {
     73 
     74             uint64_t t = U64_DIV_BY_CONST_U16(data->number, 10);
     75             chr = (data->number - t * 10) + '0';
     76             data->number = t;
     77         }
     78         else {
     79 
     80             chr = data->number & 0x0F;
     81             data->number >>= 4;
     82             chr = (chr >= 10) ? (chr + (data->flags & FLAG_CAPS ? 'A' : 'a') - 10) : (chr + '0');
     83         }
     84 
     85         buf[idx--] = chr;
     86 
     87         numPrinted++;
     88 
     89     } while (data->number);
     90 
     91 #ifdef USE_PRINTF_FLAG_CHARS
     92     while (data->precision > numPrinted) {
     93 
     94         buf[idx--] = '0';
     95         numPrinted++;
     96     }
     97 
     98     if (data->flags & FLAG_ALT) {
     99 
    100         if (data->base == 8) {
    101 
    102             if (buf[idx+1] != '0') {
    103                 buf[idx--] = '0';
    104                 numPrinted++;
    105             }
    106         }
    107         else if (data->base == 16) {
    108 
    109             buf[idx--] = data->flags & FLAG_CAPS ? 'X' : 'x';
    110             numPrinted++;
    111             buf[idx--] = '0';
    112             numPrinted++;
    113         }
    114     }
    115 
    116 
    117     if (!(data->flags & FLAG_NEG_PAD)) {
    118 
    119         if (data->fieldWidth > 0 && data->posChar != '\0')
    120             data->fieldWidth--;
    121 
    122         while (data->fieldWidth > numPrinted) {
    123 
    124             buf[idx--] = data->flags & FLAG_ZERO_EXTEND ? '0' : ' ';
    125             numPrinted++;
    126         }
    127     }
    128 #endif
    129 
    130     if (data->posChar != '\0') {
    131 
    132         buf[idx--] = data->posChar;
    133         numPrinted++;
    134     }
    135 
    136     idx++;
    137 
    138     for(i = 0; i < numPrinted; i++) {
    139 
    140         if (!putc_(data->userData,(buf + idx)[i])) {
    141 
    142             *bail = true;
    143             break;
    144         }
    145     }
    146 
    147 #ifdef USE_PRINTF_FLAG_CHARS
    148     if (!*bail && data->flags & FLAG_NEG_PAD) {
    149 
    150         for(i = numPrinted; i < data->fieldWidth; i++) {
    151 
    152             if (!putc_(data->userData, ' ')) {
    153 
    154                 *bail = true;
    155                 break;
    156             }
    157         }
    158     }
    159 #endif
    160 
    161     return i;
    162 }
    163 
    164 static uint32_t StrVPrintf_StrLen_withMax(const char* s, uint32_t max)
    165 {
    166     uint32_t len = 0;
    167 
    168     while ((*s++) && (len < max)) len++;
    169 
    170     return len;
    171 }
    172 
    173 static uint32_t StrVPrintf_StrLen(const char* s)
    174 {
    175     uint32_t len = 0;
    176 
    177     while (*s++) len++;
    178 
    179     return len;
    180 }
    181 
    182 static inline char prvGetChar(const char** fmtP)
    183 {
    184 
    185     return *(*fmtP)++;
    186 }
    187 
    188 uint32_t cvprintf(printf_write_c putc_f, uint32_t flags, void* userData, const char* fmtStr, va_list vl)
    189 {
    190 
    191     char c, t;
    192     uint32_t numPrinted = 0;
    193     double dbl;
    194     long double ldbl;
    195     struct PrintfData data;
    196 
    197     data.userData = userData;
    198 
    199 #define putc_(_ud,_c)                \
    200         do {                 \
    201             if (!putc_f(_ud,_c))    \
    202                 goto out;    \
    203         } while(0)
    204 
    205     while ((c = prvGetChar(&fmtStr)) != 0) {
    206 
    207         if (c == '\n') {
    208 
    209             putc_(userData,c);
    210             numPrinted++;
    211         }
    212         else if (c == '%') {
    213             uint32_t len, i;
    214             const char* str;
    215             bool useChar = false, useShort = false, useLong = false, useLongLong = false, useLongDouble =false, useSizeT = false, usePtrdiffT = false;
    216             bool havePrecision = false, bail = false;
    217 
    218             data.fieldWidth = 0;
    219             data.precision = 0;
    220             data.flags = 0;
    221             data.posChar = 0;
    222 
    223 more_fmt:
    224 
    225             c = prvGetChar(&fmtStr);
    226 
    227             switch(c) {
    228 
    229                 case '%':
    230 
    231                     putc_(userData,c);
    232                     numPrinted++;
    233                     break;
    234 
    235                 case 'c':
    236 
    237                     t = va_arg(vl,unsigned int);
    238                     putc_(userData,t);
    239                     numPrinted++;
    240                     break;
    241 
    242                 case 's':
    243 
    244                     str = va_arg(vl,char*);
    245                     if (!str) str = "(null)";
    246 
    247                     if (data.precision)
    248                         len = StrVPrintf_StrLen_withMax(str,data.precision);
    249                     else
    250                         len = StrVPrintf_StrLen(str);
    251 
    252 #ifdef USE_PRINTF_FLAG_CHARS
    253                     if (!(data.flags & FLAG_NEG_PAD)) {
    254                         for(i = len; i < data.fieldWidth; i++) {
    255                             putc_(userData, ' ');
    256                             numPrinted++;
    257                         }
    258                     }
    259 #endif
    260 
    261                     for(i = 0; i < len; i++) {
    262                         putc_(userData,*str++);
    263                         numPrinted++;
    264                     }
    265 
    266 #ifdef USE_PRINTF_FLAG_CHARS
    267                     if (data.flags & FLAG_NEG_PAD) {
    268                         for(i = len; i < data.fieldWidth; i++) {
    269                             putc_(userData, ' ');
    270                             numPrinted++;
    271                         }
    272                     }
    273 #endif
    274 
    275                     break;
    276 
    277                 case '.':
    278 
    279                     havePrecision = true;
    280                     goto more_fmt;
    281 
    282                 case '0':
    283 
    284                     if (!(data.flags & FLAG_ZERO_EXTEND) && !data.fieldWidth && !havePrecision) {
    285 
    286                         data.flags |= FLAG_ZERO_EXTEND;
    287                         goto more_fmt;
    288                     }
    289 
    290                 case '1':
    291                 case '2':
    292                 case '3':
    293                 case '4':
    294                 case '5':
    295                 case '6':
    296                 case '7':
    297                 case '8':
    298                 case '9':
    299 
    300                     if (havePrecision)
    301                         data.precision = (data.precision * 10) + c - '0';
    302                     else
    303                         data.fieldWidth = (data.fieldWidth * 10) + c - '0';
    304                     goto more_fmt;
    305 
    306                 case '#':
    307 
    308                     data.flags |= FLAG_ALT;
    309                     goto more_fmt;
    310 
    311                 case '-':
    312 
    313                     data.flags |= FLAG_NEG_PAD;
    314                     goto more_fmt;
    315 
    316                 case '+':
    317 
    318                     data.posChar = '+';
    319                     goto more_fmt;
    320 
    321                 case ' ':
    322 
    323                     if (data.posChar != '+')
    324                         data.posChar = ' ';
    325                     goto more_fmt;
    326 
    327 #define GET_UVAL64() \
    328         useSizeT ? va_arg(vl, size_t) :                 \
    329         usePtrdiffT ? va_arg(vl, ptrdiff_t) :           \
    330         useLongLong ? va_arg(vl, unsigned long long) :  \
    331         useLong ? va_arg(vl, unsigned long) :           \
    332         useChar ? (unsigned char)va_arg(vl, unsigned int) : \
    333         useShort ? (unsigned short)va_arg(vl, unsigned int) : \
    334         va_arg(vl, unsigned int)
    335 
    336 #define GET_SVAL64() \
    337         useSizeT ? va_arg(vl, size_t) :                 \
    338         usePtrdiffT ? va_arg(vl, ptrdiff_t) :           \
    339         useLongLong ? va_arg(vl, signed long long) :    \
    340         useLong ? va_arg(vl, signed long) :             \
    341         useChar ? (signed char)va_arg(vl, signed int) : \
    342         useShort ? (signed short)va_arg(vl, signed int) : \
    343         va_arg(vl, signed int)
    344 
    345                 case 'u':
    346 
    347                     data.number = GET_UVAL64();
    348                     data.base = 10;
    349                     data.flags &= ~(FLAG_ALT | FLAG_CAPS);
    350                     numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail);
    351                     if (bail)
    352                         goto out;
    353                     break;
    354 
    355                 case 'd':
    356                 case 'i':
    357 
    358                     data.number = GET_SVAL64();
    359                     data.base = 10;
    360                     data.flags &= ~(FLAG_ALT | FLAG_CAPS);
    361                     data.flags |= FLAG_IS_SIGNED;
    362                     numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail);
    363                     if (bail)
    364                         goto out;
    365                     break;
    366 
    367                 case 'o':
    368 
    369                     data.number = GET_UVAL64();
    370                     data.base = 8;
    371                     data.flags &= ~FLAG_CAPS;
    372                     data.posChar = '\0';
    373                     numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail);
    374                     if (bail)
    375                         goto out;
    376                     break;
    377 
    378                 case 'X':
    379 
    380                     data.flags |= FLAG_CAPS;
    381 
    382                 case 'x':
    383 
    384                     data.number = GET_UVAL64();
    385                     data.base = 16;
    386                     data.posChar = '\0';
    387                     numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail);
    388                     if (bail)
    389                         goto out;
    390                     break;
    391 
    392                 case 'p':
    393 
    394                     data.number = (uintptr_t)va_arg(vl, const void*);
    395                     data.base = 16;
    396                     data.flags &= ~FLAG_CAPS;
    397                     data.flags |= FLAG_ALT;
    398                     data.posChar = '\0';
    399                     numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail);
    400                     if (bail)
    401                         goto out;
    402                     break;
    403 
    404 #undef GET_UVAL64
    405 #undef GET_SVAL64
    406 
    407                 case 'F':
    408 
    409                     data.flags |= FLAG_CAPS;
    410 
    411                 case 'f':
    412 
    413                     if (flags & PRINTF_FLAG_CHRE) {
    414                         if (flags & PRINTF_FLAG_SHORT_DOUBLE) {
    415                             if (useLongDouble) {
    416                                 dbl = va_arg(vl, double);
    417                                 data.number = *(uint64_t *)(&dbl);
    418                             } else {
    419                                 // just grab the 32-bits
    420                                 data.number = va_arg(vl, uint32_t);
    421                             }
    422                         } else {
    423                             if (useLongDouble) {
    424                                 ldbl = va_arg(vl, long double);
    425                                 data.number = *(uint64_t *)(&ldbl);
    426                             } else {
    427                                 dbl = va_arg(vl, double);
    428                                 data.number = *(uint64_t *)(&dbl);
    429                             }
    430                         }
    431                         data.base = 16;
    432                         data.flags |= FLAG_ALT;
    433                         data.posChar = '\0';
    434                         numPrinted += StrPrvPrintfEx_number(putc_f, &data, &bail);
    435                     } else {
    436                         bail = true;
    437                     }
    438                     if (bail)
    439                         goto out;
    440                     break;
    441 
    442                 case 'h':
    443 
    444                     if (useShort)
    445                         useChar = true;
    446                     useShort = true;
    447                     goto more_fmt;
    448 
    449                 case 'L':
    450 
    451                     useLongDouble = true;
    452                     goto more_fmt;
    453 
    454                 case 'l':
    455 
    456                     if (useLong)
    457                         useLongLong = true;
    458                     useLong = true;
    459                     goto more_fmt;
    460 
    461                 case 'z':
    462 
    463                     useSizeT = true;
    464                     goto more_fmt;
    465 
    466                 case 't':
    467 
    468                     usePtrdiffT = true;
    469                     goto more_fmt;
    470 
    471                 default:
    472 
    473                     putc_(userData,c);
    474                     numPrinted++;
    475                     break;
    476 
    477             }
    478         }
    479         else {
    480 
    481             putc_(userData,c);
    482             numPrinted++;
    483         }
    484     }
    485 
    486 out:
    487 
    488     return numPrinted;
    489 }
    490