Alexander Kuzmenkov <a.kuzmen...@postgrespro.ru> writes: > I benchmarked this, using your testbed and comparing to libc sprintf > (Ubuntu GLIBC 2.27-0ubuntu3) and another implementation I know [1], all > compiled with gcc 5.
Thanks for reviewing! The cfbot noticed that the recent dlopen patch conflicted with this in configure.in, so here's a rebased version. The code itself didn't change. regards, tom lane
diff --git a/configure b/configure index dd77742..5fa9396 100755 *** a/configure --- b/configure *************** fi *** 15060,15066 **** LIBS_including_readline="$LIBS" LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'` ! for ac_func in cbrt clock_gettime fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open symlink sync_file_range utime utimes wcstombs_l do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" --- 15060,15066 ---- LIBS_including_readline="$LIBS" LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'` ! for ac_func in cbrt clock_gettime fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul symlink sync_file_range utime utimes wcstombs_l do : as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh` ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var" diff --git a/configure.in b/configure.in index 3ada48b..93e8556 100644 *** a/configure.in --- b/configure.in *************** PGAC_FUNC_WCSTOMBS_L *** 1544,1550 **** LIBS_including_readline="$LIBS" LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'` ! AC_CHECK_FUNCS([cbrt clock_gettime fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open symlink sync_file_range utime utimes wcstombs_l]) AC_REPLACE_FUNCS(fseeko) case $host_os in --- 1544,1550 ---- LIBS_including_readline="$LIBS" LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'` ! AC_CHECK_FUNCS([cbrt clock_gettime fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul symlink sync_file_range utime utimes wcstombs_l]) AC_REPLACE_FUNCS(fseeko) case $host_os in diff --git a/src/include/pg_config.h.in b/src/include/pg_config.h.in index 4094e22..752a547 100644 *** a/src/include/pg_config.h.in --- b/src/include/pg_config.h.in *************** *** 531,536 **** --- 531,539 ---- /* Define to 1 if you have the <stdlib.h> header file. */ #undef HAVE_STDLIB_H + /* Define to 1 if you have the `strchrnul' function. */ + #undef HAVE_STRCHRNUL + /* Define to 1 if you have the `strerror' function. */ #undef HAVE_STRERROR diff --git a/src/include/pg_config.h.win32 b/src/include/pg_config.h.win32 index 6618b43..ea72c44 100644 *** a/src/include/pg_config.h.win32 --- b/src/include/pg_config.h.win32 *************** *** 402,407 **** --- 402,410 ---- /* Define to 1 if you have the <stdlib.h> header file. */ #define HAVE_STDLIB_H 1 + /* Define to 1 if you have the `strchrnul' function. */ + /* #undef HAVE_STRCHRNUL */ + /* Define to 1 if you have the `strerror' function. */ #ifndef HAVE_STRERROR #define HAVE_STRERROR 1 diff --git a/src/port/snprintf.c b/src/port/snprintf.c index 851e2ae..66151c2 100644 *** a/src/port/snprintf.c --- b/src/port/snprintf.c *************** flushbuffer(PrintfTarget *target) *** 295,301 **** } ! static void fmtstr(char *value, int leftjust, int minlen, int maxwidth, int pointflag, PrintfTarget *target); static void fmtptr(void *value, PrintfTarget *target); static void fmtint(int64 value, char type, int forcesign, --- 295,303 ---- } ! static bool find_arguments(const char *format, va_list args, ! PrintfArgValue *argvalues); ! static void fmtstr(const char *value, int leftjust, int minlen, int maxwidth, int pointflag, PrintfTarget *target); static void fmtptr(void *value, PrintfTarget *target); static void fmtint(int64 value, char type, int forcesign, *************** static void fmtfloat(double value, char *** 307,317 **** PrintfTarget *target); static void dostr(const char *str, int slen, PrintfTarget *target); static void dopr_outch(int c, PrintfTarget *target); static int adjust_sign(int is_negative, int forcesign, int *signvalue); ! static void adjust_padlen(int minlen, int vallen, int leftjust, int *padlen); ! static void leading_pad(int zpad, int *signvalue, int *padlen, PrintfTarget *target); ! static void trailing_pad(int *padlen, PrintfTarget *target); /* --- 309,320 ---- PrintfTarget *target); static void dostr(const char *str, int slen, PrintfTarget *target); static void dopr_outch(int c, PrintfTarget *target); + static void dopr_outchmulti(int c, int slen, PrintfTarget *target); static int adjust_sign(int is_negative, int forcesign, int *signvalue); ! static int compute_padlen(int minlen, int vallen, int leftjust); ! static void leading_pad(int zpad, int signvalue, int *padlen, PrintfTarget *target); ! static void trailing_pad(int padlen, PrintfTarget *target); /* *************** static void trailing_pad(int *padlen, Pr *** 320,329 **** static void dopr(PrintfTarget *target, const char *format, va_list args) { ! const char *format_start = format; int ch; bool have_dollar; - bool have_non_dollar; bool have_star; bool afterstar; int accum; --- 323,331 ---- static void dopr(PrintfTarget *target, const char *format, va_list args) { ! const char *first_pct = NULL; int ch; bool have_dollar; bool have_star; bool afterstar; int accum; *************** dopr(PrintfTarget *target, const char *f *** 335,559 **** int precision; int zpad; int forcesign; - int last_dollar; int fmtpos; int cvalue; int64 numvalue; double fvalue; char *strvalue; - int i; - PrintfArgType argtypes[NL_ARGMAX + 1]; PrintfArgValue argvalues[NL_ARGMAX + 1]; /* ! * Parse the format string to determine whether there are %n$ format ! * specs, and identify the types and order of the format parameters. */ ! have_dollar = have_non_dollar = false; ! last_dollar = 0; ! MemSet(argtypes, 0, sizeof(argtypes)); ! while ((ch = *format++) != '\0') { ! if (ch != '%') ! continue; ! longflag = longlongflag = pointflag = 0; ! fmtpos = accum = 0; ! afterstar = false; ! nextch1: ! ch = *format++; ! if (ch == '\0') ! break; /* illegal, but we don't complain */ ! switch (ch) { ! case '-': ! case '+': ! goto nextch1; ! case '0': ! case '1': ! case '2': ! case '3': ! case '4': ! case '5': ! case '6': ! case '7': ! case '8': ! case '9': ! accum = accum * 10 + (ch - '0'); ! goto nextch1; ! case '.': ! pointflag = 1; ! accum = 0; ! goto nextch1; ! case '*': ! if (afterstar) ! have_non_dollar = true; /* multiple stars */ ! afterstar = true; ! accum = 0; ! goto nextch1; ! case '$': ! have_dollar = true; ! if (accum <= 0 || accum > NL_ARGMAX) ! goto bad_format; ! if (afterstar) ! { ! if (argtypes[accum] && ! argtypes[accum] != ATYPE_INT) ! goto bad_format; ! argtypes[accum] = ATYPE_INT; ! last_dollar = Max(last_dollar, accum); ! afterstar = false; ! } ! else ! fmtpos = accum; ! accum = 0; ! goto nextch1; ! case 'l': ! if (longflag) ! longlongflag = 1; ! else ! longflag = 1; ! goto nextch1; ! case 'z': ! #if SIZEOF_SIZE_T == 8 ! #ifdef HAVE_LONG_INT_64 ! longflag = 1; ! #elif defined(HAVE_LONG_LONG_INT_64) ! longlongflag = 1; ! #else ! #error "Don't know how to print 64bit integers" ! #endif #else ! /* assume size_t is same size as int */ #endif - goto nextch1; - case 'h': - case '\'': - /* ignore these */ - goto nextch1; - case 'd': - case 'i': - case 'o': - case 'u': - case 'x': - case 'X': - if (fmtpos) - { - PrintfArgType atype; ! if (longlongflag) ! atype = ATYPE_LONGLONG; ! else if (longflag) ! atype = ATYPE_LONG; ! else ! atype = ATYPE_INT; ! if (argtypes[fmtpos] && ! argtypes[fmtpos] != atype) ! goto bad_format; ! argtypes[fmtpos] = atype; ! last_dollar = Max(last_dollar, fmtpos); ! } ! else ! have_non_dollar = true; ! break; ! case 'c': ! if (fmtpos) ! { ! if (argtypes[fmtpos] && ! argtypes[fmtpos] != ATYPE_INT) ! goto bad_format; ! argtypes[fmtpos] = ATYPE_INT; ! last_dollar = Max(last_dollar, fmtpos); ! } ! else ! have_non_dollar = true; ! break; ! case 's': ! case 'p': ! if (fmtpos) ! { ! if (argtypes[fmtpos] && ! argtypes[fmtpos] != ATYPE_CHARPTR) ! goto bad_format; ! argtypes[fmtpos] = ATYPE_CHARPTR; ! last_dollar = Max(last_dollar, fmtpos); ! } ! else ! have_non_dollar = true; ! break; ! case 'e': ! case 'E': ! case 'f': ! case 'g': ! case 'G': ! if (fmtpos) ! { ! if (argtypes[fmtpos] && ! argtypes[fmtpos] != ATYPE_DOUBLE) ! goto bad_format; ! argtypes[fmtpos] = ATYPE_DOUBLE; ! last_dollar = Max(last_dollar, fmtpos); ! } ! else ! have_non_dollar = true; break; ! case '%': break; } /* ! * If we finish the spec with afterstar still set, there's a ! * non-dollar star in there. */ ! if (afterstar) ! have_non_dollar = true; ! } ! ! /* Per spec, you use either all dollar or all not. */ ! if (have_dollar && have_non_dollar) ! goto bad_format; ! ! /* ! * In dollar mode, collect the arguments in physical order. ! */ ! for (i = 1; i <= last_dollar; i++) ! { ! switch (argtypes[i]) ! { ! case ATYPE_NONE: ! goto bad_format; ! case ATYPE_INT: ! argvalues[i].i = va_arg(args, int); ! break; ! case ATYPE_LONG: ! argvalues[i].l = va_arg(args, long); ! break; ! case ATYPE_LONGLONG: ! argvalues[i].ll = va_arg(args, int64); ! break; ! case ATYPE_DOUBLE: ! argvalues[i].d = va_arg(args, double); ! break; ! case ATYPE_CHARPTR: ! argvalues[i].cptr = va_arg(args, char *); ! break; ! } ! } ! ! /* ! * At last we can parse the format for real. ! */ ! format = format_start; ! while ((ch = *format++) != '\0') ! { ! if (target->failed) ! break; ! if (ch != '%') ! { ! dopr_outch(ch, target); ! continue; ! } fieldwidth = precision = zpad = leftjust = forcesign = 0; longflag = longlongflag = pointflag = 0; fmtpos = accum = 0; --- 337,397 ---- int precision; int zpad; int forcesign; int fmtpos; int cvalue; int64 numvalue; double fvalue; char *strvalue; PrintfArgValue argvalues[NL_ARGMAX + 1]; /* ! * Initially, we suppose the format string does not use %n$. The first ! * time we come to a conversion spec that has that, we'll call ! * find_arguments() to check for consistent use of %n$ and fill the ! * argvalues array with the argument values in the correct order. */ ! have_dollar = false; ! while (*format != '\0') { ! /* Locate next conversion specifier */ ! if (*format != '%') { ! const char *next_pct = format + 1; ! ! /* ! * If strchrnul exists (it's a glibc-ism), it's a good bit faster ! * than the equivalent manual loop. Note: this doesn't compile ! * cleanly without -D_GNU_SOURCE, but we normally use that on ! * glibc platforms. ! */ ! #ifdef HAVE_STRCHRNUL ! next_pct = strchrnul(next_pct, '%'); #else ! while (*next_pct != '\0' && *next_pct != '%') ! next_pct++; #endif ! /* Dump literal data we just scanned over */ ! dostr(format, next_pct - format, target); ! if (target->failed) break; ! ! if (*next_pct == '\0') break; + format = next_pct; } /* ! * Remember start of first conversion spec; if we find %n$, then it's ! * sufficient for find_arguments() to start here, without rescanning ! * earlier literal text. */ ! if (first_pct == NULL) ! first_pct = format; ! /* Process conversion spec starting at *format */ ! format++; fieldwidth = precision = zpad = leftjust = forcesign = 0; longflag = longlongflag = pointflag = 0; fmtpos = accum = 0; *************** nextch2: *** 597,603 **** case '*': if (have_dollar) { ! /* process value after reading n$ */ afterstar = true; } else --- 435,445 ---- case '*': if (have_dollar) { ! /* ! * We'll process value after reading n$. Note it's OK to ! * assume have_dollar is set correctly, because in a valid ! * format string the initial % must have had n$ if * does. ! */ afterstar = true; } else *************** nextch2: *** 628,633 **** --- 470,483 ---- accum = 0; goto nextch2; case '$': + /* First dollar sign? */ + if (!have_dollar) + { + /* Yup, so examine all conversion specs in format */ + if (!find_arguments(first_pct, args, argvalues)) + goto bad_format; + have_dollar = true; + } if (afterstar) { /* fetch and process star value */ *************** nextch2: *** 806,811 **** --- 656,665 ---- dopr_outch('%', target); break; } + + /* Check for failure after each conversion spec */ + if (target->failed) + break; } return; *************** bad_format: *** 815,822 **** target->failed = true; } static void ! fmtstr(char *value, int leftjust, int minlen, int maxwidth, int pointflag, PrintfTarget *target) { int padlen, --- 669,903 ---- target->failed = true; } + /* + * find_arguments(): sort out the arguments for a format spec with %n$ + * + * If format is valid, return true and fill argvalues[i] with the value + * for the conversion spec that has %i$ or *i$. Else return false. + */ + static bool + find_arguments(const char *format, va_list args, + PrintfArgValue *argvalues) + { + int ch; + bool afterstar; + int accum; + int longlongflag; + int longflag; + int fmtpos; + int i; + int last_dollar; + PrintfArgType argtypes[NL_ARGMAX + 1]; + + /* Initialize to "no dollar arguments known" */ + last_dollar = 0; + MemSet(argtypes, 0, sizeof(argtypes)); + + /* + * This loop must accept the same format strings as the one in dopr(). + * However, we don't need to analyze them to the same level of detail. + * + * Since we're only called if there's a dollar-type spec somewhere, we can + * fail immediately if we find a non-dollar spec. Per the C99 standard, + * all argument references in the format string must be one or the other. + */ + while (*format != '\0') + { + /* Locate next conversion specifier */ + if (*format != '%') + { + /* Unlike dopr, we can just quit if there's no more specifiers */ + format = strchr(format + 1, '%'); + if (format == NULL) + break; + } + + /* Process conversion spec starting at *format */ + format++; + longflag = longlongflag = 0; + fmtpos = accum = 0; + afterstar = false; + nextch1: + ch = *format++; + if (ch == '\0') + break; /* illegal, but we don't complain */ + switch (ch) + { + case '-': + case '+': + goto nextch1; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + accum = accum * 10 + (ch - '0'); + goto nextch1; + case '.': + accum = 0; + goto nextch1; + case '*': + if (afterstar) + return false; /* previous star missing dollar */ + afterstar = true; + accum = 0; + goto nextch1; + case '$': + if (accum <= 0 || accum > NL_ARGMAX) + return false; + if (afterstar) + { + if (argtypes[accum] && + argtypes[accum] != ATYPE_INT) + return false; + argtypes[accum] = ATYPE_INT; + last_dollar = Max(last_dollar, accum); + afterstar = false; + } + else + fmtpos = accum; + accum = 0; + goto nextch1; + case 'l': + if (longflag) + longlongflag = 1; + else + longflag = 1; + goto nextch1; + case 'z': + #if SIZEOF_SIZE_T == 8 + #ifdef HAVE_LONG_INT_64 + longflag = 1; + #elif defined(HAVE_LONG_LONG_INT_64) + longlongflag = 1; + #else + #error "Don't know how to print 64bit integers" + #endif + #else + /* assume size_t is same size as int */ + #endif + goto nextch1; + case 'h': + case '\'': + /* ignore these */ + goto nextch1; + case 'd': + case 'i': + case 'o': + case 'u': + case 'x': + case 'X': + if (fmtpos) + { + PrintfArgType atype; + + if (longlongflag) + atype = ATYPE_LONGLONG; + else if (longflag) + atype = ATYPE_LONG; + else + atype = ATYPE_INT; + if (argtypes[fmtpos] && + argtypes[fmtpos] != atype) + return false; + argtypes[fmtpos] = atype; + last_dollar = Max(last_dollar, fmtpos); + } + else + return false; /* non-dollar conversion spec */ + break; + case 'c': + if (fmtpos) + { + if (argtypes[fmtpos] && + argtypes[fmtpos] != ATYPE_INT) + return false; + argtypes[fmtpos] = ATYPE_INT; + last_dollar = Max(last_dollar, fmtpos); + } + else + return false; /* non-dollar conversion spec */ + break; + case 's': + case 'p': + if (fmtpos) + { + if (argtypes[fmtpos] && + argtypes[fmtpos] != ATYPE_CHARPTR) + return false; + argtypes[fmtpos] = ATYPE_CHARPTR; + last_dollar = Max(last_dollar, fmtpos); + } + else + return false; /* non-dollar conversion spec */ + break; + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': + if (fmtpos) + { + if (argtypes[fmtpos] && + argtypes[fmtpos] != ATYPE_DOUBLE) + return false; + argtypes[fmtpos] = ATYPE_DOUBLE; + last_dollar = Max(last_dollar, fmtpos); + } + else + return false; /* non-dollar conversion spec */ + break; + case '%': + break; + } + + /* + * If we finish the spec with afterstar still set, there's a + * non-dollar star in there. + */ + if (afterstar) + return false; /* non-dollar conversion spec */ + } + + /* + * Format appears valid so far, so collect the arguments in physical + * order. (Since we rejected any non-dollar specs that would have + * collected arguments, we know that dopr() hasn't collected any yet.) + */ + for (i = 1; i <= last_dollar; i++) + { + switch (argtypes[i]) + { + case ATYPE_NONE: + return false; + case ATYPE_INT: + argvalues[i].i = va_arg(args, int); + break; + case ATYPE_LONG: + argvalues[i].l = va_arg(args, long); + break; + case ATYPE_LONGLONG: + argvalues[i].ll = va_arg(args, int64); + break; + case ATYPE_DOUBLE: + argvalues[i].d = va_arg(args, double); + break; + case ATYPE_CHARPTR: + argvalues[i].cptr = va_arg(args, char *); + break; + } + } + + return true; + } + static void ! fmtstr(const char *value, int leftjust, int minlen, int maxwidth, int pointflag, PrintfTarget *target) { int padlen, *************** fmtstr(char *value, int leftjust, int mi *** 831,847 **** else vallen = strlen(value); ! adjust_padlen(minlen, vallen, leftjust, &padlen); ! while (padlen > 0) { ! dopr_outch(' ', target); ! --padlen; } dostr(value, vallen, target); ! trailing_pad(&padlen, target); } static void --- 912,928 ---- else vallen = strlen(value); ! padlen = compute_padlen(minlen, vallen, leftjust); ! if (padlen > 0) { ! dopr_outchmulti(' ', padlen, target); ! padlen = 0; } dostr(value, vallen, target); ! trailing_pad(padlen, target); } static void *************** fmtint(int64 value, char type, int force *** 869,875 **** int signvalue = 0; char convert[64]; int vallen = 0; ! int padlen = 0; /* amount to pad */ int zeropad; /* extra leading zeroes */ switch (type) --- 950,956 ---- int signvalue = 0; char convert[64]; int vallen = 0; ! int padlen; /* amount to pad */ int zeropad; /* extra leading zeroes */ switch (type) *************** fmtint(int64 value, char type, int force *** 917,958 **** do { ! convert[vallen++] = cvt[uvalue % base]; uvalue = uvalue / base; } while (uvalue); } zeropad = Max(0, precision - vallen); ! adjust_padlen(minlen, vallen + zeropad, leftjust, &padlen); ! leading_pad(zpad, &signvalue, &padlen, target); ! while (zeropad-- > 0) ! dopr_outch('0', target); ! while (vallen > 0) ! dopr_outch(convert[--vallen], target); ! trailing_pad(&padlen, target); } static void fmtchar(int value, int leftjust, int minlen, PrintfTarget *target) { ! int padlen = 0; /* amount to pad */ ! adjust_padlen(minlen, 1, leftjust, &padlen); ! while (padlen > 0) { ! dopr_outch(' ', target); ! --padlen; } dopr_outch(value, target); ! trailing_pad(&padlen, target); } static void --- 998,1038 ---- do { ! convert[sizeof(convert) - (++vallen)] = cvt[uvalue % base]; uvalue = uvalue / base; } while (uvalue); } zeropad = Max(0, precision - vallen); ! padlen = compute_padlen(minlen, vallen + zeropad, leftjust); ! leading_pad(zpad, signvalue, &padlen, target); ! if (zeropad > 0) ! dopr_outchmulti('0', zeropad, target); ! dostr(convert + sizeof(convert) - vallen, vallen, target); ! trailing_pad(padlen, target); } static void fmtchar(int value, int leftjust, int minlen, PrintfTarget *target) { ! int padlen; /* amount to pad */ ! padlen = compute_padlen(minlen, 1, leftjust); ! if (padlen > 0) { ! dopr_outchmulti(' ', padlen, target); ! padlen = 0; } dopr_outch(value, target); ! trailing_pad(padlen, target); } static void *************** fmtfloat(double value, char type, int fo *** 963,972 **** int signvalue = 0; int prec; int vallen; ! char fmt[32]; char convert[1024]; int zeropadlen = 0; /* amount to pad with zeroes */ ! int padlen = 0; /* amount to pad with spaces */ /* * We rely on the regular C library's sprintf to do the basic conversion, --- 1043,1056 ---- int signvalue = 0; int prec; int vallen; ! char fmt[8]; char convert[1024]; int zeropadlen = 0; /* amount to pad with zeroes */ ! int padlen; /* amount to pad with spaces */ ! ! /* Handle sign (NaNs have no sign) */ ! if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue)) ! value = -value; /* * We rely on the regular C library's sprintf to do the basic conversion, *************** fmtfloat(double value, char type, int fo *** 988,1004 **** if (pointflag) { - if (sprintf(fmt, "%%.%d%c", prec, type) < 0) - goto fail; zeropadlen = precision - prec; } - else if (sprintf(fmt, "%%%c", type) < 0) - goto fail; - - if (!isnan(value) && adjust_sign((value < 0), forcesign, &signvalue)) - value = -value; - - vallen = sprintf(convert, fmt, value); if (vallen < 0) goto fail; --- 1072,1092 ---- if (pointflag) { zeropadlen = precision - prec; + fmt[0] = '%'; + fmt[1] = '.'; + fmt[2] = '*'; + fmt[3] = type; + fmt[4] = '\0'; + vallen = sprintf(convert, fmt, prec, value); + } + else + { + fmt[0] = '%'; + fmt[1] = type; + fmt[2] = '\0'; + vallen = sprintf(convert, fmt, value); } if (vallen < 0) goto fail; *************** fmtfloat(double value, char type, int fo *** 1006,1014 **** if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1])) zeropadlen = 0; ! adjust_padlen(minlen, vallen + zeropadlen, leftjust, &padlen); ! leading_pad(zpad, &signvalue, &padlen, target); if (zeropadlen > 0) { --- 1094,1102 ---- if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1])) zeropadlen = 0; ! padlen = compute_padlen(minlen, vallen + zeropadlen, leftjust); ! leading_pad(zpad, signvalue, &padlen, target); if (zeropadlen > 0) { *************** fmtfloat(double value, char type, int fo *** 1019,1036 **** epos = strrchr(convert, 'E'); if (epos) { ! /* pad after exponent */ dostr(convert, epos - convert, target); ! while (zeropadlen-- > 0) ! dopr_outch('0', target); dostr(epos, vallen - (epos - convert), target); } else { /* no exponent, pad after the digits */ dostr(convert, vallen, target); ! while (zeropadlen-- > 0) ! dopr_outch('0', target); } } else --- 1107,1124 ---- epos = strrchr(convert, 'E'); if (epos) { ! /* pad before exponent */ dostr(convert, epos - convert, target); ! if (zeropadlen > 0) ! dopr_outchmulti('0', zeropadlen, target); dostr(epos, vallen - (epos - convert), target); } else { /* no exponent, pad after the digits */ dostr(convert, vallen, target); ! if (zeropadlen > 0) ! dopr_outchmulti('0', zeropadlen, target); } } else *************** fmtfloat(double value, char type, int fo *** 1039,1045 **** dostr(convert, vallen, target); } ! trailing_pad(&padlen, target); return; fail: --- 1127,1133 ---- dostr(convert, vallen, target); } ! trailing_pad(padlen, target); return; fail: *************** fail: *** 1049,1054 **** --- 1137,1149 ---- static void dostr(const char *str, int slen, PrintfTarget *target) { + /* fast path for common case of slen == 1 */ + if (slen == 1) + { + dopr_outch(*str, target); + return; + } + while (slen > 0) { int avail; *************** dopr_outch(int c, PrintfTarget *target) *** 1092,1097 **** --- 1187,1228 ---- *(target->bufptr++) = c; } + static void + dopr_outchmulti(int c, int slen, PrintfTarget *target) + { + /* fast path for common case of slen == 1 */ + if (slen == 1) + { + dopr_outch(c, target); + return; + } + + while (slen > 0) + { + int avail; + + if (target->bufend != NULL) + avail = target->bufend - target->bufptr; + else + avail = slen; + if (avail <= 0) + { + /* buffer full, can we dump to stream? */ + if (target->stream == NULL) + { + target->nchars += slen; /* no, lose the data */ + return; + } + flushbuffer(target); + continue; + } + avail = Min(avail, slen); + memset(target->bufptr, c, avail); + target->bufptr += avail; + slen -= avail; + } + } + static int adjust_sign(int is_negative, int forcesign, int *signvalue) *************** adjust_sign(int is_negative, int forcesi *** 1107,1148 **** } ! static void ! adjust_padlen(int minlen, int vallen, int leftjust, int *padlen) { ! *padlen = minlen - vallen; ! if (*padlen < 0) ! *padlen = 0; if (leftjust) ! *padlen = -(*padlen); } static void ! leading_pad(int zpad, int *signvalue, int *padlen, PrintfTarget *target) { if (*padlen > 0 && zpad) { ! if (*signvalue) { ! dopr_outch(*signvalue, target); --(*padlen); ! *signvalue = 0; } ! while (*padlen > 0) { ! dopr_outch(zpad, target); ! --(*padlen); } } ! while (*padlen > (*signvalue != 0)) { ! dopr_outch(' ', target); ! --(*padlen); } ! if (*signvalue) { ! dopr_outch(*signvalue, target); if (*padlen > 0) --(*padlen); else if (*padlen < 0) --- 1238,1285 ---- } ! static int ! compute_padlen(int minlen, int vallen, int leftjust) { ! int padlen; ! ! padlen = minlen - vallen; ! if (padlen < 0) ! padlen = 0; if (leftjust) ! padlen = -padlen; ! return padlen; } static void ! leading_pad(int zpad, int signvalue, int *padlen, PrintfTarget *target) { + int maxpad; + if (*padlen > 0 && zpad) { ! if (signvalue) { ! dopr_outch(signvalue, target); --(*padlen); ! signvalue = 0; } ! if (*padlen > 0) { ! dopr_outchmulti(zpad, *padlen, target); ! *padlen = 0; } } ! maxpad = (signvalue != 0); ! if (*padlen > maxpad) { ! dopr_outchmulti(' ', *padlen - maxpad, target); ! *padlen = maxpad; } ! if (signvalue) { ! dopr_outch(signvalue, target); if (*padlen > 0) --(*padlen); else if (*padlen < 0) *************** leading_pad(int zpad, int *signvalue, in *** 1152,1162 **** static void ! trailing_pad(int *padlen, PrintfTarget *target) { ! while (*padlen < 0) ! { ! dopr_outch(' ', target); ! ++(*padlen); ! } } --- 1289,1296 ---- static void ! trailing_pad(int padlen, PrintfTarget *target) { ! if (padlen < 0) ! dopr_outchmulti(' ', -padlen, target); }