mirror of
https://github.com/postgres/postgres.git
synced 2025-04-21 12:05:57 +03:00
Make assorted performance improvements in snprintf.c.
In combination, these changes make our version of snprintf as fast or faster than most platforms' native snprintf, except for cases involving floating-point conversion (which we still delegate to the native sprintf). The speed penalty for a float conversion is down to around 10% though, much better than before. Notable changes: * Rather than always parsing the format twice to see if it contains instances of %n$, do the extra scan only if we actually find a $. This obviously wins for non-localized formats, and even when there is use of %n$, we can avoid scanning text before the first % twice. * Use strchrnul() if available to find the next %, and emit the literal text between % escapes as strings rather than char-by-char. * Create a bespoke function (dopr_outchmulti) for the common case of emitting N copies of the same character, in place of writing loops around dopr_outch. * Simplify construction of the format string for invocations of sprintf for floats. * Const-ify some internal functions, and avoid unnecessary use of pass-by-reference arguments. Patch by me, reviewed by Andres Freund Discussion: https://postgr.es/m/11787.1534530779@sss.pgh.pa.us
This commit is contained in:
parent
9bc9f72b28
commit
abd9ca377d
2
configure
vendored
2
configure
vendored
@ -15100,7 +15100,7 @@ fi
|
|||||||
LIBS_including_readline="$LIBS"
|
LIBS_including_readline="$LIBS"
|
||||||
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
|
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 ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open symlink sync_file_range utime utimes wcstombs_l
|
for ac_func in cbrt clock_gettime fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open strchrnul symlink sync_file_range utime utimes wcstombs_l
|
||||||
do :
|
do :
|
||||||
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
as_ac_var=`$as_echo "ac_cv_func_$ac_func" | $as_tr_sh`
|
||||||
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
|
ac_fn_c_check_func "$LINENO" "$ac_func" "$as_ac_var"
|
||||||
|
@ -1571,7 +1571,7 @@ PGAC_FUNC_WCSTOMBS_L
|
|||||||
LIBS_including_readline="$LIBS"
|
LIBS_including_readline="$LIBS"
|
||||||
LIBS=`echo "$LIBS" | sed -e 's/-ledit//g' -e 's/-lreadline//g'`
|
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 ppoll pstat pthread_is_threaded_np readlink setproctitle setproctitle_fast setsid shm_open symlink sync_file_range utime utimes wcstombs_l])
|
AC_CHECK_FUNCS([cbrt clock_gettime fdatasync getifaddrs getpeerucred getrlimit mbstowcs_l memmove poll posix_fallocate ppoll 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)
|
AC_REPLACE_FUNCS(fseeko)
|
||||||
case $host_os in
|
case $host_os in
|
||||||
|
@ -523,6 +523,9 @@
|
|||||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
#undef HAVE_STDLIB_H
|
#undef HAVE_STDLIB_H
|
||||||
|
|
||||||
|
/* Define to 1 if you have the `strchrnul' function. */
|
||||||
|
#undef HAVE_STRCHRNUL
|
||||||
|
|
||||||
/* Define to 1 if you have the `strerror_r' function. */
|
/* Define to 1 if you have the `strerror_r' function. */
|
||||||
#undef HAVE_STRERROR_R
|
#undef HAVE_STRERROR_R
|
||||||
|
|
||||||
|
@ -394,6 +394,9 @@
|
|||||||
/* Define to 1 if you have the <stdlib.h> header file. */
|
/* Define to 1 if you have the <stdlib.h> header file. */
|
||||||
#define HAVE_STDLIB_H 1
|
#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_r' function. */
|
/* Define to 1 if you have the `strerror_r' function. */
|
||||||
/* #undef HAVE_STRERROR_R */
|
/* #undef HAVE_STRERROR_R */
|
||||||
|
|
||||||
|
@ -314,7 +314,9 @@ flushbuffer(PrintfTarget *target)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void fmtstr(char *value, int leftjust, int minlen, int maxwidth,
|
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);
|
int pointflag, PrintfTarget *target);
|
||||||
static void fmtptr(void *value, PrintfTarget *target);
|
static void fmtptr(void *value, PrintfTarget *target);
|
||||||
static void fmtint(int64 value, char type, int forcesign,
|
static void fmtint(int64 value, char type, int forcesign,
|
||||||
@ -326,11 +328,43 @@ static void fmtfloat(double value, char type, int forcesign,
|
|||||||
PrintfTarget *target);
|
PrintfTarget *target);
|
||||||
static void dostr(const char *str, int slen, PrintfTarget *target);
|
static void dostr(const char *str, int slen, PrintfTarget *target);
|
||||||
static void dopr_outch(int c, 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 adjust_sign(int is_negative, int forcesign, int *signvalue);
|
||||||
static void adjust_padlen(int minlen, int vallen, int leftjust, int *padlen);
|
static int compute_padlen(int minlen, int vallen, int leftjust);
|
||||||
static void leading_pad(int zpad, int *signvalue, int *padlen,
|
static void leading_pad(int zpad, int signvalue, int *padlen,
|
||||||
PrintfTarget *target);
|
PrintfTarget *target);
|
||||||
static void trailing_pad(int *padlen, PrintfTarget *target);
|
static void trailing_pad(int padlen, PrintfTarget *target);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If strchrnul exists (it's a glibc-ism), it's a good bit faster than the
|
||||||
|
* equivalent manual loop. If it doesn't exist, provide a replacement.
|
||||||
|
*
|
||||||
|
* Note: glibc declares this as returning "char *", but that would require
|
||||||
|
* casting away const internally, so we don't follow that detail.
|
||||||
|
*/
|
||||||
|
#ifndef HAVE_STRCHRNUL
|
||||||
|
|
||||||
|
static inline const char *
|
||||||
|
strchrnul(const char *s, int c)
|
||||||
|
{
|
||||||
|
while (*s != '\0' && *s != c)
|
||||||
|
s++;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
/*
|
||||||
|
* glibc's <string.h> declares strchrnul only if _GNU_SOURCE is defined.
|
||||||
|
* While we typically use that on glibc platforms, configure will set
|
||||||
|
* HAVE_STRCHRNUL whether it's used or not. Fill in the missing declaration
|
||||||
|
* so that this file will compile cleanly with or without _GNU_SOURCE.
|
||||||
|
*/
|
||||||
|
#ifndef _GNU_SOURCE
|
||||||
|
extern char *strchrnul(const char *s, int c);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* HAVE_STRCHRNUL */
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -340,10 +374,9 @@ static void
|
|||||||
dopr(PrintfTarget *target, const char *format, va_list args)
|
dopr(PrintfTarget *target, const char *format, va_list args)
|
||||||
{
|
{
|
||||||
int save_errno = errno;
|
int save_errno = errno;
|
||||||
const char *format_start = format;
|
const char *first_pct = NULL;
|
||||||
int ch;
|
int ch;
|
||||||
bool have_dollar;
|
bool have_dollar;
|
||||||
bool have_non_dollar;
|
|
||||||
bool have_star;
|
bool have_star;
|
||||||
bool afterstar;
|
bool afterstar;
|
||||||
int accum;
|
int accum;
|
||||||
@ -355,226 +388,49 @@ dopr(PrintfTarget *target, const char *format, va_list args)
|
|||||||
int precision;
|
int precision;
|
||||||
int zpad;
|
int zpad;
|
||||||
int forcesign;
|
int forcesign;
|
||||||
int last_dollar;
|
|
||||||
int fmtpos;
|
int fmtpos;
|
||||||
int cvalue;
|
int cvalue;
|
||||||
int64 numvalue;
|
int64 numvalue;
|
||||||
double fvalue;
|
double fvalue;
|
||||||
char *strvalue;
|
char *strvalue;
|
||||||
int i;
|
|
||||||
PrintfArgType argtypes[PG_NL_ARGMAX + 1];
|
|
||||||
PrintfArgValue argvalues[PG_NL_ARGMAX + 1];
|
PrintfArgValue argvalues[PG_NL_ARGMAX + 1];
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parse the format string to determine whether there are %n$ format
|
* Initially, we suppose the format string does not use %n$. The first
|
||||||
* specs, and identify the types and order of the format parameters.
|
* 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 = have_non_dollar = false;
|
have_dollar = false;
|
||||||
last_dollar = 0;
|
|
||||||
MemSet(argtypes, 0, sizeof(argtypes));
|
|
||||||
|
|
||||||
while ((ch = *format++) != '\0')
|
while (*format != '\0')
|
||||||
{
|
{
|
||||||
if (ch != '%')
|
/* Locate next conversion specifier */
|
||||||
continue;
|
if (*format != '%')
|
||||||
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 '-':
|
/* Scan to next '%' or end of string */
|
||||||
case '+':
|
const char *next_pct = strchrnul(format + 1, '%');
|
||||||
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 > PG_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)
|
/* Dump literal data we just scanned over */
|
||||||
atype = ATYPE_LONGLONG;
|
dostr(format, next_pct - format, target);
|
||||||
else if (longflag)
|
if (target->failed)
|
||||||
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;
|
break;
|
||||||
case 'c':
|
|
||||||
if (fmtpos)
|
if (*next_pct == '\0')
|
||||||
{
|
|
||||||
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 'm':
|
|
||||||
case '%':
|
|
||||||
break;
|
break;
|
||||||
|
format = next_pct;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* If we finish the spec with afterstar still set, there's a
|
* Remember start of first conversion spec; if we find %n$, then it's
|
||||||
* non-dollar star in there.
|
* sufficient for find_arguments() to start here, without rescanning
|
||||||
|
* earlier literal text.
|
||||||
*/
|
*/
|
||||||
if (afterstar)
|
if (first_pct == NULL)
|
||||||
have_non_dollar = true;
|
first_pct = format;
|
||||||
}
|
|
||||||
|
|
||||||
/* Per spec, you use either all dollar or all not. */
|
/* Process conversion spec starting at *format */
|
||||||
if (have_dollar && have_non_dollar)
|
format++;
|
||||||
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;
|
fieldwidth = precision = zpad = leftjust = forcesign = 0;
|
||||||
longflag = longlongflag = pointflag = 0;
|
longflag = longlongflag = pointflag = 0;
|
||||||
fmtpos = accum = 0;
|
fmtpos = accum = 0;
|
||||||
@ -618,7 +474,11 @@ nextch2:
|
|||||||
case '*':
|
case '*':
|
||||||
if (have_dollar)
|
if (have_dollar)
|
||||||
{
|
{
|
||||||
/* process value after reading n$ */
|
/*
|
||||||
|
* 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;
|
afterstar = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -649,6 +509,14 @@ nextch2:
|
|||||||
accum = 0;
|
accum = 0;
|
||||||
goto nextch2;
|
goto nextch2;
|
||||||
case '$':
|
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)
|
if (afterstar)
|
||||||
{
|
{
|
||||||
/* fetch and process star value */
|
/* fetch and process star value */
|
||||||
@ -836,6 +704,10 @@ nextch2:
|
|||||||
dopr_outch('%', target);
|
dopr_outch('%', target);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Check for failure after each conversion spec */
|
||||||
|
if (target->failed)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -845,8 +717,236 @@ bad_format:
|
|||||||
target->failed = true;
|
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[PG_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 > PG_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 'm':
|
||||||
|
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
|
static void
|
||||||
fmtstr(char *value, int leftjust, int minlen, int maxwidth,
|
fmtstr(const char *value, int leftjust, int minlen, int maxwidth,
|
||||||
int pointflag, PrintfTarget *target)
|
int pointflag, PrintfTarget *target)
|
||||||
{
|
{
|
||||||
int padlen,
|
int padlen,
|
||||||
@ -861,17 +961,17 @@ fmtstr(char *value, int leftjust, int minlen, int maxwidth,
|
|||||||
else
|
else
|
||||||
vallen = strlen(value);
|
vallen = strlen(value);
|
||||||
|
|
||||||
adjust_padlen(minlen, vallen, leftjust, &padlen);
|
padlen = compute_padlen(minlen, vallen, leftjust);
|
||||||
|
|
||||||
while (padlen > 0)
|
if (padlen > 0)
|
||||||
{
|
{
|
||||||
dopr_outch(' ', target);
|
dopr_outchmulti(' ', padlen, target);
|
||||||
--padlen;
|
padlen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
dostr(value, vallen, target);
|
dostr(value, vallen, target);
|
||||||
|
|
||||||
trailing_pad(&padlen, target);
|
trailing_pad(padlen, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -899,7 +999,7 @@ fmtint(int64 value, char type, int forcesign, int leftjust,
|
|||||||
int signvalue = 0;
|
int signvalue = 0;
|
||||||
char convert[64];
|
char convert[64];
|
||||||
int vallen = 0;
|
int vallen = 0;
|
||||||
int padlen = 0; /* amount to pad */
|
int padlen; /* amount to pad */
|
||||||
int zeropad; /* extra leading zeroes */
|
int zeropad; /* extra leading zeroes */
|
||||||
|
|
||||||
switch (type)
|
switch (type)
|
||||||
@ -947,42 +1047,41 @@ fmtint(int64 value, char type, int forcesign, int leftjust,
|
|||||||
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
convert[vallen++] = cvt[uvalue % base];
|
convert[sizeof(convert) - (++vallen)] = cvt[uvalue % base];
|
||||||
uvalue = uvalue / base;
|
uvalue = uvalue / base;
|
||||||
} while (uvalue);
|
} while (uvalue);
|
||||||
}
|
}
|
||||||
|
|
||||||
zeropad = Max(0, precision - vallen);
|
zeropad = Max(0, precision - vallen);
|
||||||
|
|
||||||
adjust_padlen(minlen, vallen + zeropad, leftjust, &padlen);
|
padlen = compute_padlen(minlen, vallen + zeropad, leftjust);
|
||||||
|
|
||||||
leading_pad(zpad, &signvalue, &padlen, target);
|
leading_pad(zpad, signvalue, &padlen, target);
|
||||||
|
|
||||||
while (zeropad-- > 0)
|
if (zeropad > 0)
|
||||||
dopr_outch('0', target);
|
dopr_outchmulti('0', zeropad, target);
|
||||||
|
|
||||||
while (vallen > 0)
|
dostr(convert + sizeof(convert) - vallen, vallen, target);
|
||||||
dopr_outch(convert[--vallen], target);
|
|
||||||
|
|
||||||
trailing_pad(&padlen, target);
|
trailing_pad(padlen, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
|
fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
|
||||||
{
|
{
|
||||||
int padlen = 0; /* amount to pad */
|
int padlen; /* amount to pad */
|
||||||
|
|
||||||
adjust_padlen(minlen, 1, leftjust, &padlen);
|
padlen = compute_padlen(minlen, 1, leftjust);
|
||||||
|
|
||||||
while (padlen > 0)
|
if (padlen > 0)
|
||||||
{
|
{
|
||||||
dopr_outch(' ', target);
|
dopr_outchmulti(' ', padlen, target);
|
||||||
--padlen;
|
padlen = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
dopr_outch(value, target);
|
dopr_outch(value, target);
|
||||||
|
|
||||||
trailing_pad(&padlen, target);
|
trailing_pad(padlen, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -993,10 +1092,14 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
|
|||||||
int signvalue = 0;
|
int signvalue = 0;
|
||||||
int prec;
|
int prec;
|
||||||
int vallen;
|
int vallen;
|
||||||
char fmt[32];
|
char fmt[8];
|
||||||
char convert[1024];
|
char convert[1024];
|
||||||
int zeropadlen = 0; /* amount to pad with zeroes */
|
int zeropadlen = 0; /* amount to pad with zeroes */
|
||||||
int padlen = 0; /* amount to pad with spaces */
|
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,
|
* We rely on the regular C library's sprintf to do the basic conversion,
|
||||||
@ -1018,17 +1121,21 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
|
|||||||
|
|
||||||
if (pointflag)
|
if (pointflag)
|
||||||
{
|
{
|
||||||
if (sprintf(fmt, "%%.%d%c", prec, type) < 0)
|
|
||||||
goto fail;
|
|
||||||
zeropadlen = precision - prec;
|
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);
|
||||||
}
|
}
|
||||||
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)
|
if (vallen < 0)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
@ -1036,9 +1143,9 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
|
|||||||
if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1]))
|
if (zeropadlen > 0 && !isdigit((unsigned char) convert[vallen - 1]))
|
||||||
zeropadlen = 0;
|
zeropadlen = 0;
|
||||||
|
|
||||||
adjust_padlen(minlen, vallen + zeropadlen, leftjust, &padlen);
|
padlen = compute_padlen(minlen, vallen + zeropadlen, leftjust);
|
||||||
|
|
||||||
leading_pad(zpad, &signvalue, &padlen, target);
|
leading_pad(zpad, signvalue, &padlen, target);
|
||||||
|
|
||||||
if (zeropadlen > 0)
|
if (zeropadlen > 0)
|
||||||
{
|
{
|
||||||
@ -1049,18 +1156,18 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
|
|||||||
epos = strrchr(convert, 'E');
|
epos = strrchr(convert, 'E');
|
||||||
if (epos)
|
if (epos)
|
||||||
{
|
{
|
||||||
/* pad after exponent */
|
/* pad before exponent */
|
||||||
dostr(convert, epos - convert, target);
|
dostr(convert, epos - convert, target);
|
||||||
while (zeropadlen-- > 0)
|
if (zeropadlen > 0)
|
||||||
dopr_outch('0', target);
|
dopr_outchmulti('0', zeropadlen, target);
|
||||||
dostr(epos, vallen - (epos - convert), target);
|
dostr(epos, vallen - (epos - convert), target);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
/* no exponent, pad after the digits */
|
/* no exponent, pad after the digits */
|
||||||
dostr(convert, vallen, target);
|
dostr(convert, vallen, target);
|
||||||
while (zeropadlen-- > 0)
|
if (zeropadlen > 0)
|
||||||
dopr_outch('0', target);
|
dopr_outchmulti('0', zeropadlen, target);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -1069,7 +1176,7 @@ fmtfloat(double value, char type, int forcesign, int leftjust,
|
|||||||
dostr(convert, vallen, target);
|
dostr(convert, vallen, target);
|
||||||
}
|
}
|
||||||
|
|
||||||
trailing_pad(&padlen, target);
|
trailing_pad(padlen, target);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
fail:
|
fail:
|
||||||
@ -1079,6 +1186,13 @@ fail:
|
|||||||
static void
|
static void
|
||||||
dostr(const char *str, int slen, PrintfTarget *target)
|
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)
|
while (slen > 0)
|
||||||
{
|
{
|
||||||
int avail;
|
int avail;
|
||||||
@ -1122,6 +1236,42 @@ dopr_outch(int c, PrintfTarget *target)
|
|||||||
*(target->bufptr++) = c;
|
*(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
|
static int
|
||||||
adjust_sign(int is_negative, int forcesign, int *signvalue)
|
adjust_sign(int is_negative, int forcesign, int *signvalue)
|
||||||
@ -1137,42 +1287,48 @@ adjust_sign(int is_negative, int forcesign, int *signvalue)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static int
|
||||||
adjust_padlen(int minlen, int vallen, int leftjust, int *padlen)
|
compute_padlen(int minlen, int vallen, int leftjust)
|
||||||
{
|
{
|
||||||
*padlen = minlen - vallen;
|
int padlen;
|
||||||
if (*padlen < 0)
|
|
||||||
*padlen = 0;
|
padlen = minlen - vallen;
|
||||||
|
if (padlen < 0)
|
||||||
|
padlen = 0;
|
||||||
if (leftjust)
|
if (leftjust)
|
||||||
*padlen = -(*padlen);
|
padlen = -padlen;
|
||||||
|
return padlen;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
leading_pad(int zpad, int *signvalue, int *padlen, PrintfTarget *target)
|
leading_pad(int zpad, int signvalue, int *padlen, PrintfTarget *target)
|
||||||
{
|
{
|
||||||
|
int maxpad;
|
||||||
|
|
||||||
if (*padlen > 0 && zpad)
|
if (*padlen > 0 && zpad)
|
||||||
{
|
{
|
||||||
if (*signvalue)
|
if (signvalue)
|
||||||
{
|
{
|
||||||
dopr_outch(*signvalue, target);
|
dopr_outch(signvalue, target);
|
||||||
--(*padlen);
|
--(*padlen);
|
||||||
*signvalue = 0;
|
signvalue = 0;
|
||||||
}
|
}
|
||||||
while (*padlen > 0)
|
if (*padlen > 0)
|
||||||
{
|
{
|
||||||
dopr_outch(zpad, target);
|
dopr_outchmulti(zpad, *padlen, target);
|
||||||
--(*padlen);
|
*padlen = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (*padlen > (*signvalue != 0))
|
maxpad = (signvalue != 0);
|
||||||
|
if (*padlen > maxpad)
|
||||||
{
|
{
|
||||||
dopr_outch(' ', target);
|
dopr_outchmulti(' ', *padlen - maxpad, target);
|
||||||
--(*padlen);
|
*padlen = maxpad;
|
||||||
}
|
}
|
||||||
if (*signvalue)
|
if (signvalue)
|
||||||
{
|
{
|
||||||
dopr_outch(*signvalue, target);
|
dopr_outch(signvalue, target);
|
||||||
if (*padlen > 0)
|
if (*padlen > 0)
|
||||||
--(*padlen);
|
--(*padlen);
|
||||||
else if (*padlen < 0)
|
else if (*padlen < 0)
|
||||||
@ -1182,11 +1338,8 @@ leading_pad(int zpad, int *signvalue, int *padlen, PrintfTarget *target)
|
|||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
trailing_pad(int *padlen, PrintfTarget *target)
|
trailing_pad(int padlen, PrintfTarget *target)
|
||||||
{
|
{
|
||||||
while (*padlen < 0)
|
if (padlen < 0)
|
||||||
{
|
dopr_outchmulti(' ', -padlen, target);
|
||||||
dopr_outch(' ', target);
|
|
||||||
++(*padlen);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user