1998-09-04 16:34:23 +02:00
|
|
|
/*
|
|
|
|
* Copyright (c) 1983, 1995, 1996 Eric P. Allman
|
|
|
|
* Copyright (c) 1988, 1993
|
1998-08-01 21:30:29 +02:00
|
|
|
* The Regents of the University of California. All rights reserved.
|
2024-01-04 02:49:05 +01:00
|
|
|
* Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
|
1998-08-01 21:30:29 +02:00
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
1998-10-07 19:12:52 +02:00
|
|
|
* notice, this list of conditions and the following disclaimer.
|
1998-08-01 21:30:29 +02:00
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
1998-10-07 19:12:52 +02:00
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
2007-03-26 23:44:11 +02:00
|
|
|
* 3. Neither the name of the University nor the names of its contributors
|
1998-10-07 19:12:52 +02:00
|
|
|
* may be used to endorse or promote products derived from this software
|
|
|
|
* without specific prior written permission.
|
1998-08-01 21:30:29 +02:00
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
1998-10-07 19:12:52 +02:00
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
1998-08-01 21:30:29 +02:00
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
2005-12-05 03:39:38 +01:00
|
|
|
*
|
2010-09-20 22:08:53 +02:00
|
|
|
* src/port/snprintf.c
|
1998-08-01 21:30:29 +02:00
|
|
|
*/
|
1998-09-10 06:11:52 +02:00
|
|
|
|
2005-07-28 06:03:14 +02:00
|
|
|
#include "c.h"
|
2001-02-10 03:31:31 +01:00
|
|
|
|
2015-02-02 16:00:45 +01:00
|
|
|
#include <math.h>
|
1998-09-01 06:40:42 +02:00
|
|
|
|
Set snprintf.c's maximum number of NL arguments to be 31.
Previously, we used the platform's NL_ARGMAX if any, otherwise 16.
The trouble with this is that the platform value is hugely variable,
ranging from the POSIX-minimum 9 to as much as 64K on recent FreeBSD.
Values of more than a dozen or two have no practical use and slow down
the initialization of the argtypes array. Worse, they cause snprintf.c
to consume far more stack space than was the design intention, possibly
resulting in stack-overflow crashes.
Standardize on 31, which is comfortably more than we need (it looks like
no existing translatable message has more than about 10 parameters).
I chose that, not 32, to make the array sizes powers of 2, for some
possible small gain in speed of the memset.
The lack of reported crashes suggests that the set of platforms we
use snprintf.c on (in released branches) may have no overlap with
the set where NL_ARGMAX has unreasonably large values. But that's
not entirely clear, so back-patch to all supported branches.
Per report from Mateusz Guzik (via Thomas Munro).
Discussion: https://postgr.es/m/CAEepm=3VF=PUp2f8gU8fgZB22yPE_KBS0+e1AHAtQ=09schTHg@mail.gmail.com
2018-10-02 18:41:28 +02:00
|
|
|
/*
|
|
|
|
* We used to use the platform's NL_ARGMAX here, but that's a bad idea,
|
|
|
|
* first because the point of this module is to remove platform dependencies
|
|
|
|
* not perpetuate them, and second because some platforms use ridiculously
|
|
|
|
* large values, leading to excessive stack consumption in dopr().
|
|
|
|
*/
|
|
|
|
#define PG_NL_ARGMAX 31
|
2005-12-05 03:39:38 +01:00
|
|
|
|
2005-03-20 04:53:39 +01:00
|
|
|
|
1998-09-04 16:34:23 +02:00
|
|
|
/*
|
2005-12-05 03:39:38 +01:00
|
|
|
* SNPRINTF, VSNPRINTF and friends
|
|
|
|
*
|
|
|
|
* These versions have been grabbed off the net. They have been
|
2018-08-15 19:21:05 +02:00
|
|
|
* cleaned up to compile properly and support for most of the C99
|
|
|
|
* specification has been added. Remaining unimplemented features are:
|
2005-12-05 03:39:38 +01:00
|
|
|
*
|
|
|
|
* 1. No locale support: the radix character is always '.' and the '
|
|
|
|
* (single quote) format flag is ignored.
|
|
|
|
*
|
|
|
|
* 2. No support for the "%n" format specification.
|
|
|
|
*
|
|
|
|
* 3. No support for wide characters ("lc" and "ls" formats).
|
|
|
|
*
|
|
|
|
* 4. No support for "long double" ("Lf" and related formats).
|
|
|
|
*
|
|
|
|
* 5. Space and '#' flags are not implemented.
|
2005-12-05 22:57:00 +01:00
|
|
|
*
|
2018-09-26 19:31:56 +02:00
|
|
|
* In addition, we support some extensions over C99:
|
|
|
|
*
|
|
|
|
* 1. Argument order control through "%n$" and "*n$", as required by POSIX.
|
|
|
|
*
|
|
|
|
* 2. "%m" expands to the value of strerror(errno), where errno is the
|
|
|
|
* value that variable had at the start of the call. This is a glibc
|
|
|
|
* extension, but a very useful one.
|
|
|
|
*
|
2005-12-05 22:57:00 +01:00
|
|
|
*
|
2018-08-15 19:21:05 +02:00
|
|
|
* Historically the result values of sprintf/snprintf varied across platforms.
|
|
|
|
* This implementation now follows the C99 standard:
|
2005-12-05 22:57:00 +01:00
|
|
|
*
|
2018-08-15 19:21:05 +02:00
|
|
|
* 1. -1 is returned if an error is detected in the format string, or if
|
|
|
|
* a write to the target stream fails (as reported by fwrite). Note that
|
|
|
|
* overrunning snprintf's target buffer is *not* an error.
|
2005-12-05 22:57:00 +01:00
|
|
|
*
|
2018-08-15 19:21:05 +02:00
|
|
|
* 2. For successful writes to streams, the actual number of bytes written
|
|
|
|
* to the stream is returned.
|
2005-12-05 22:57:00 +01:00
|
|
|
*
|
2018-08-15 19:21:05 +02:00
|
|
|
* 3. For successful sprintf/snprintf, the number of bytes that would have
|
|
|
|
* been written to an infinite-size buffer (excluding the trailing '\0')
|
|
|
|
* is returned. snprintf will truncate its output to fit in the buffer
|
|
|
|
* (ensuring a trailing '\0' unless count == 0), but this is not reflected
|
|
|
|
* in the function result.
|
2005-12-05 22:57:00 +01:00
|
|
|
*
|
2018-08-15 19:21:05 +02:00
|
|
|
* snprintf buffer overrun can be detected by checking for function result
|
|
|
|
* greater than or equal to the supplied count.
|
2005-12-05 03:39:38 +01:00
|
|
|
*/
|
1998-09-01 06:40:42 +02:00
|
|
|
|
1998-09-04 16:34:23 +02:00
|
|
|
/**************************************************************
|
|
|
|
* Original:
|
|
|
|
* Patrick Powell Tue Apr 11 09:48:21 PDT 1995
|
|
|
|
* A bombproof version of doprnt (dopr) included.
|
1998-10-07 19:12:52 +02:00
|
|
|
* Sigh. This sort of thing is always nasty do deal with. Note that
|
1999-02-06 22:51:03 +01:00
|
|
|
* the version here does not include floating point. (now it does ... tgl)
|
1998-09-04 16:34:23 +02:00
|
|
|
**************************************************************/
|
1998-08-01 21:30:29 +02:00
|
|
|
|
2005-03-11 20:13:43 +01:00
|
|
|
/* Prevent recursion */
|
|
|
|
#undef vsnprintf
|
|
|
|
#undef snprintf
|
2018-10-09 01:15:55 +02:00
|
|
|
#undef vsprintf
|
2005-03-16 22:27:23 +01:00
|
|
|
#undef sprintf
|
2006-11-28 02:12:34 +01:00
|
|
|
#undef vfprintf
|
2005-03-11 20:13:43 +01:00
|
|
|
#undef fprintf
|
2018-10-09 01:15:55 +02:00
|
|
|
#undef vprintf
|
2005-03-11 20:13:43 +01:00
|
|
|
#undef printf
|
|
|
|
|
2018-08-15 19:21:05 +02:00
|
|
|
/*
|
|
|
|
* Info about where the formatted output is going.
|
|
|
|
*
|
|
|
|
* dopr and subroutines will not write at/past bufend, but snprintf
|
|
|
|
* reserves one byte, ensuring it may place the trailing '\0' there.
|
|
|
|
*
|
|
|
|
* In snprintf, we use nchars to count the number of bytes dropped on the
|
|
|
|
* floor due to buffer overrun. The correct result of snprintf is thus
|
|
|
|
* (bufptr - bufstart) + nchars. (This isn't as inconsistent as it might
|
|
|
|
* seem: nchars is the number of emitted bytes that are not in the buffer now,
|
|
|
|
* either because we sent them to the stream or because we couldn't fit them
|
|
|
|
* into the buffer to begin with.)
|
|
|
|
*/
|
2005-12-05 03:39:38 +01:00
|
|
|
typedef struct
|
|
|
|
{
|
|
|
|
char *bufptr; /* next buffer output position */
|
|
|
|
char *bufstart; /* first buffer element */
|
2018-08-15 19:21:05 +02:00
|
|
|
char *bufend; /* last+1 buffer element, or NULL */
|
2005-12-05 03:39:38 +01:00
|
|
|
/* bufend == NULL is for sprintf, where we assume buf is big enough */
|
|
|
|
FILE *stream; /* eventual output destination, or NULL */
|
2018-08-15 19:21:05 +02:00
|
|
|
int nchars; /* # chars sent to stream, or dropped */
|
Add error-throwing wrappers for the printf family of functions.
All known standard library implementations of these functions can fail
with ENOMEM. A caller neglecting to check for failure would experience
missing output, information exposure, or a crash. Check return values
within wrappers and code, currently just snprintf.c, that bypasses the
wrappers. The wrappers do not return after an error, so their callers
need not check. Back-patch to 9.0 (all supported versions).
Popular free software standard library implementations do take pains to
bypass malloc() in simple cases, but they risk ENOMEM for floating point
numbers, positional arguments, large field widths, and large precisions.
No specification demands such caution, so this commit regards every call
to a printf family function as a potential threat.
Injecting the wrappers implicitly is a compromise between patch scope
and design goals. I would prefer to edit each call site to name a
wrapper explicitly. libpq and the ECPG libraries would, ideally, convey
errors to the caller rather than abort(). All that would be painfully
invasive for a back-patched security fix, hence this compromise.
Security: CVE-2015-3166
2015-05-18 16:02:31 +02:00
|
|
|
bool failed; /* call is a failure; errno is set */
|
2005-12-05 03:39:38 +01:00
|
|
|
} PrintfTarget;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Info about the type and value of a formatting parameter. Note that we
|
|
|
|
* don't currently support "long double", "wint_t", or "wchar_t *" data,
|
|
|
|
* nor the '%n' formatting code; else we'd need more types. Also, at this
|
|
|
|
* level we need not worry about signed vs unsigned values.
|
|
|
|
*/
|
|
|
|
typedef enum
|
|
|
|
{
|
|
|
|
ATYPE_NONE = 0,
|
|
|
|
ATYPE_INT,
|
|
|
|
ATYPE_LONG,
|
|
|
|
ATYPE_LONGLONG,
|
|
|
|
ATYPE_DOUBLE,
|
|
|
|
ATYPE_CHARPTR
|
|
|
|
} PrintfArgType;
|
|
|
|
|
|
|
|
typedef union
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
long l;
|
Rationalize snprintf.c's handling of "ll" formats.
Although all known platforms define "long long" as 64 bits, it still feels
a bit shaky to be using "va_arg(args, int64)" to pull out an argument that
the caller thought was declared "long long". The reason it was coded like
this, way back in commit 3311c7669, was to work around the possibility that
the compiler had no type named "long long" --- and, at the time, that it
maybe didn't have 64-bit ints at all. Now that we're requiring compilers
to support C99, those concerns are moot. Let's make the code clearer and
more bulletproof by writing "long long" where we mean "long long".
This does introduce a hazard that we'd inefficiently use 128-bit arithmetic
to convert plain old integers. The way to tackle that would be to provide
two versions of fmtint(), one for "long long" and one for narrower types.
Since, as of today, no platforms require that, we won't bother with the
extra code for now.
Discussion: https://postgr.es/m/1680.1538587115@sss.pgh.pa.us
2018-10-03 20:33:13 +02:00
|
|
|
long long ll;
|
2005-12-05 03:39:38 +01:00
|
|
|
double d;
|
|
|
|
char *cptr;
|
|
|
|
} PrintfArgValue;
|
|
|
|
|
|
|
|
|
|
|
|
static void flushbuffer(PrintfTarget *target);
|
Add error-throwing wrappers for the printf family of functions.
All known standard library implementations of these functions can fail
with ENOMEM. A caller neglecting to check for failure would experience
missing output, information exposure, or a crash. Check return values
within wrappers and code, currently just snprintf.c, that bypasses the
wrappers. The wrappers do not return after an error, so their callers
need not check. Back-patch to 9.0 (all supported versions).
Popular free software standard library implementations do take pains to
bypass malloc() in simple cases, but they risk ENOMEM for floating point
numbers, positional arguments, large field widths, and large precisions.
No specification demands such caution, so this commit regards every call
to a printf family function as a potential threat.
Injecting the wrappers implicitly is a compromise between patch scope
and design goals. I would prefer to edit each call site to name a
wrapper explicitly. libpq and the ECPG libraries would, ideally, convey
errors to the caller rather than abort(). All that would be painfully
invasive for a back-patched security fix, hence this compromise.
Security: CVE-2015-3166
2015-05-18 16:02:31 +02:00
|
|
|
static void dopr(PrintfTarget *target, const char *format, va_list args);
|
2005-12-05 03:39:38 +01:00
|
|
|
|
|
|
|
|
2018-09-26 19:31:56 +02:00
|
|
|
/*
|
|
|
|
* Externally visible entry points.
|
|
|
|
*
|
|
|
|
* All of these are just wrappers around dopr(). Note it's essential that
|
|
|
|
* they not change the value of "errno" before reaching dopr().
|
|
|
|
*/
|
|
|
|
|
2005-02-22 04:56:22 +01:00
|
|
|
int
|
2005-03-11 18:20:35 +01:00
|
|
|
pg_vsnprintf(char *str, size_t count, const char *fmt, va_list args)
|
2005-02-22 04:56:22 +01:00
|
|
|
{
|
2005-12-05 03:39:38 +01:00
|
|
|
PrintfTarget target;
|
2018-08-15 19:21:05 +02:00
|
|
|
char onebyte[1];
|
2005-12-05 03:39:38 +01:00
|
|
|
|
2018-08-15 19:21:05 +02:00
|
|
|
/*
|
|
|
|
* C99 allows the case str == NULL when count == 0. Rather than
|
|
|
|
* special-casing this situation further down, we substitute a one-byte
|
|
|
|
* local buffer. Callers cannot tell, since the function result doesn't
|
|
|
|
* depend on count.
|
|
|
|
*/
|
|
|
|
if (count == 0)
|
|
|
|
{
|
|
|
|
str = onebyte;
|
|
|
|
count = 1;
|
|
|
|
}
|
2005-12-05 03:39:38 +01:00
|
|
|
target.bufstart = target.bufptr = str;
|
|
|
|
target.bufend = str + count - 1;
|
|
|
|
target.stream = NULL;
|
2018-08-15 19:21:05 +02:00
|
|
|
target.nchars = 0;
|
Add error-throwing wrappers for the printf family of functions.
All known standard library implementations of these functions can fail
with ENOMEM. A caller neglecting to check for failure would experience
missing output, information exposure, or a crash. Check return values
within wrappers and code, currently just snprintf.c, that bypasses the
wrappers. The wrappers do not return after an error, so their callers
need not check. Back-patch to 9.0 (all supported versions).
Popular free software standard library implementations do take pains to
bypass malloc() in simple cases, but they risk ENOMEM for floating point
numbers, positional arguments, large field widths, and large precisions.
No specification demands such caution, so this commit regards every call
to a printf family function as a potential threat.
Injecting the wrappers implicitly is a compromise between patch scope
and design goals. I would prefer to edit each call site to name a
wrapper explicitly. libpq and the ECPG libraries would, ideally, convey
errors to the caller rather than abort(). All that would be painfully
invasive for a back-patched security fix, hence this compromise.
Security: CVE-2015-3166
2015-05-18 16:02:31 +02:00
|
|
|
target.failed = false;
|
|
|
|
dopr(&target, fmt, args);
|
2005-12-05 03:39:38 +01:00
|
|
|
*(target.bufptr) = '\0';
|
2018-08-15 19:21:05 +02:00
|
|
|
return target.failed ? -1 : (target.bufptr - target.bufstart
|
|
|
|
+ target.nchars);
|
2005-02-22 04:56:22 +01:00
|
|
|
}
|
|
|
|
|
1998-08-01 21:30:29 +02:00
|
|
|
int
|
2005-03-11 18:20:35 +01:00
|
|
|
pg_snprintf(char *str, size_t count, const char *fmt,...)
|
1998-09-04 16:34:23 +02:00
|
|
|
{
|
1998-10-07 19:12:52 +02:00
|
|
|
int len;
|
1998-12-18 08:03:06 +01:00
|
|
|
va_list args;
|
1998-10-07 19:12:52 +02:00
|
|
|
|
1998-12-18 08:03:06 +01:00
|
|
|
va_start(args, fmt);
|
2005-03-11 18:20:35 +01:00
|
|
|
len = pg_vsnprintf(str, count, fmt, args);
|
1998-12-18 08:03:06 +01:00
|
|
|
va_end(args);
|
1998-09-04 16:34:23 +02:00
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2018-10-09 01:15:55 +02:00
|
|
|
int
|
2005-12-05 03:39:38 +01:00
|
|
|
pg_vsprintf(char *str, const char *fmt, va_list args)
|
|
|
|
{
|
|
|
|
PrintfTarget target;
|
|
|
|
|
|
|
|
target.bufstart = target.bufptr = str;
|
|
|
|
target.bufend = NULL;
|
|
|
|
target.stream = NULL;
|
2018-08-15 19:21:05 +02:00
|
|
|
target.nchars = 0; /* not really used in this case */
|
Add error-throwing wrappers for the printf family of functions.
All known standard library implementations of these functions can fail
with ENOMEM. A caller neglecting to check for failure would experience
missing output, information exposure, or a crash. Check return values
within wrappers and code, currently just snprintf.c, that bypasses the
wrappers. The wrappers do not return after an error, so their callers
need not check. Back-patch to 9.0 (all supported versions).
Popular free software standard library implementations do take pains to
bypass malloc() in simple cases, but they risk ENOMEM for floating point
numbers, positional arguments, large field widths, and large precisions.
No specification demands such caution, so this commit regards every call
to a printf family function as a potential threat.
Injecting the wrappers implicitly is a compromise between patch scope
and design goals. I would prefer to edit each call site to name a
wrapper explicitly. libpq and the ECPG libraries would, ideally, convey
errors to the caller rather than abort(). All that would be painfully
invasive for a back-patched security fix, hence this compromise.
Security: CVE-2015-3166
2015-05-18 16:02:31 +02:00
|
|
|
target.failed = false;
|
|
|
|
dopr(&target, fmt, args);
|
2005-12-05 03:39:38 +01:00
|
|
|
*(target.bufptr) = '\0';
|
2018-08-15 19:21:05 +02:00
|
|
|
return target.failed ? -1 : (target.bufptr - target.bufstart
|
|
|
|
+ target.nchars);
|
2005-12-05 03:39:38 +01:00
|
|
|
}
|
|
|
|
|
2005-03-16 22:27:23 +01:00
|
|
|
int
|
|
|
|
pg_sprintf(char *str, const char *fmt,...)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
2005-12-05 03:39:38 +01:00
|
|
|
len = pg_vsprintf(str, fmt, args);
|
2005-03-16 22:27:23 +01:00
|
|
|
va_end(args);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2006-11-28 02:12:34 +01:00
|
|
|
int
|
2005-12-05 03:39:38 +01:00
|
|
|
pg_vfprintf(FILE *stream, const char *fmt, va_list args)
|
|
|
|
{
|
|
|
|
PrintfTarget target;
|
|
|
|
char buffer[1024]; /* size is arbitrary */
|
|
|
|
|
|
|
|
if (stream == NULL)
|
|
|
|
{
|
|
|
|
errno = EINVAL;
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
target.bufstart = target.bufptr = buffer;
|
2018-08-15 19:21:05 +02:00
|
|
|
target.bufend = buffer + sizeof(buffer); /* use the whole buffer */
|
2005-12-05 03:39:38 +01:00
|
|
|
target.stream = stream;
|
|
|
|
target.nchars = 0;
|
Add error-throwing wrappers for the printf family of functions.
All known standard library implementations of these functions can fail
with ENOMEM. A caller neglecting to check for failure would experience
missing output, information exposure, or a crash. Check return values
within wrappers and code, currently just snprintf.c, that bypasses the
wrappers. The wrappers do not return after an error, so their callers
need not check. Back-patch to 9.0 (all supported versions).
Popular free software standard library implementations do take pains to
bypass malloc() in simple cases, but they risk ENOMEM for floating point
numbers, positional arguments, large field widths, and large precisions.
No specification demands such caution, so this commit regards every call
to a printf family function as a potential threat.
Injecting the wrappers implicitly is a compromise between patch scope
and design goals. I would prefer to edit each call site to name a
wrapper explicitly. libpq and the ECPG libraries would, ideally, convey
errors to the caller rather than abort(). All that would be painfully
invasive for a back-patched security fix, hence this compromise.
Security: CVE-2015-3166
2015-05-18 16:02:31 +02:00
|
|
|
target.failed = false;
|
|
|
|
dopr(&target, fmt, args);
|
2005-12-05 03:39:38 +01:00
|
|
|
/* dump any remaining buffer contents */
|
|
|
|
flushbuffer(&target);
|
Add error-throwing wrappers for the printf family of functions.
All known standard library implementations of these functions can fail
with ENOMEM. A caller neglecting to check for failure would experience
missing output, information exposure, or a crash. Check return values
within wrappers and code, currently just snprintf.c, that bypasses the
wrappers. The wrappers do not return after an error, so their callers
need not check. Back-patch to 9.0 (all supported versions).
Popular free software standard library implementations do take pains to
bypass malloc() in simple cases, but they risk ENOMEM for floating point
numbers, positional arguments, large field widths, and large precisions.
No specification demands such caution, so this commit regards every call
to a printf family function as a potential threat.
Injecting the wrappers implicitly is a compromise between patch scope
and design goals. I would prefer to edit each call site to name a
wrapper explicitly. libpq and the ECPG libraries would, ideally, convey
errors to the caller rather than abort(). All that would be painfully
invasive for a back-patched security fix, hence this compromise.
Security: CVE-2015-3166
2015-05-18 16:02:31 +02:00
|
|
|
return target.failed ? -1 : target.nchars;
|
2005-12-05 03:39:38 +01:00
|
|
|
}
|
|
|
|
|
2005-03-11 20:13:43 +01:00
|
|
|
int
|
|
|
|
pg_fprintf(FILE *stream, const char *fmt,...)
|
|
|
|
{
|
|
|
|
int len;
|
|
|
|
va_list args;
|
|
|
|
|
|
|
|
va_start(args, fmt);
|
2005-12-05 03:39:38 +01:00
|
|
|
len = pg_vfprintf(stream, fmt, args);
|
2005-03-11 20:13:43 +01:00
|
|
|
va_end(args);
|
|
|
|
return len;
|
|
|
|
}
|
|
|
|
|
2018-10-09 01:15:55 +02:00
|
|
|
int
|
|
|
|
pg_vprintf(const char *fmt, va_list args)
|
|
|
|
{
|
|
|
|
return pg_vfprintf(stdout, fmt, args);
|
|
|
|
}
|
|
|
|
|
1998-08-01 21:30:29 +02:00
|
|
|
int
|
2005-03-11 18:20:35 +01:00
|
|
|
pg_printf(const char *fmt,...)
|
1998-09-04 16:34:23 +02:00
|
|
|
{
|
2005-03-02 04:21:52 +01:00
|
|
|
int len;
|
2005-03-11 20:13:43 +01:00
|
|
|
va_list args;
|
2005-03-02 04:21:52 +01:00
|
|
|
|
|
|
|
va_start(args, fmt);
|
2005-12-05 03:39:38 +01:00
|
|
|
len = pg_vfprintf(stdout, fmt, args);
|
2005-03-02 04:21:52 +01:00
|
|
|
va_end(args);
|
|
|
|
return len;
|
1998-09-04 16:34:23 +02:00
|
|
|
}
|
1998-09-01 06:40:42 +02:00
|
|
|
|
Add error-throwing wrappers for the printf family of functions.
All known standard library implementations of these functions can fail
with ENOMEM. A caller neglecting to check for failure would experience
missing output, information exposure, or a crash. Check return values
within wrappers and code, currently just snprintf.c, that bypasses the
wrappers. The wrappers do not return after an error, so their callers
need not check. Back-patch to 9.0 (all supported versions).
Popular free software standard library implementations do take pains to
bypass malloc() in simple cases, but they risk ENOMEM for floating point
numbers, positional arguments, large field widths, and large precisions.
No specification demands such caution, so this commit regards every call
to a printf family function as a potential threat.
Injecting the wrappers implicitly is a compromise between patch scope
and design goals. I would prefer to edit each call site to name a
wrapper explicitly. libpq and the ECPG libraries would, ideally, convey
errors to the caller rather than abort(). All that would be painfully
invasive for a back-patched security fix, hence this compromise.
Security: CVE-2015-3166
2015-05-18 16:02:31 +02:00
|
|
|
/*
|
|
|
|
* Attempt to write the entire buffer to target->stream; discard the entire
|
|
|
|
* buffer in any case. Call this only when target->stream is defined.
|
|
|
|
*/
|
2005-12-05 03:39:38 +01:00
|
|
|
static void
|
|
|
|
flushbuffer(PrintfTarget *target)
|
|
|
|
{
|
|
|
|
size_t nc = target->bufptr - target->bufstart;
|
|
|
|
|
2018-08-15 19:21:05 +02:00
|
|
|
/*
|
|
|
|
* Don't write anything if we already failed; this is to ensure we
|
|
|
|
* preserve the original failure's errno.
|
|
|
|
*/
|
Add error-throwing wrappers for the printf family of functions.
All known standard library implementations of these functions can fail
with ENOMEM. A caller neglecting to check for failure would experience
missing output, information exposure, or a crash. Check return values
within wrappers and code, currently just snprintf.c, that bypasses the
wrappers. The wrappers do not return after an error, so their callers
need not check. Back-patch to 9.0 (all supported versions).
Popular free software standard library implementations do take pains to
bypass malloc() in simple cases, but they risk ENOMEM for floating point
numbers, positional arguments, large field widths, and large precisions.
No specification demands such caution, so this commit regards every call
to a printf family function as a potential threat.
Injecting the wrappers implicitly is a compromise between patch scope
and design goals. I would prefer to edit each call site to name a
wrapper explicitly. libpq and the ECPG libraries would, ideally, convey
errors to the caller rather than abort(). All that would be painfully
invasive for a back-patched security fix, hence this compromise.
Security: CVE-2015-3166
2015-05-18 16:02:31 +02:00
|
|
|
if (!target->failed && nc > 0)
|
|
|
|
{
|
|
|
|
size_t written;
|
|
|
|
|
|
|
|
written = fwrite(target->bufstart, 1, nc, target->stream);
|
|
|
|
target->nchars += written;
|
|
|
|
if (written != nc)
|
|
|
|
target->failed = true;
|
|
|
|
}
|
2005-12-05 03:39:38 +01:00
|
|
|
target->bufptr = target->bufstart;
|
|
|
|
}
|
|
|
|
|
1998-09-04 16:34:23 +02: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
2018-10-03 16:18:15 +02:00
|
|
|
static bool find_arguments(const char *format, va_list args,
|
|
|
|
PrintfArgValue *argvalues);
|
|
|
|
static void fmtstr(const char *value, int leftjust, int minlen, int maxwidth,
|
2005-12-05 03:39:38 +01:00
|
|
|
int pointflag, PrintfTarget *target);
|
Make printf("%s", NULL) print "(null)" instead of crashing.
We previously took a hard-line attitude that callers should never print
a null string pointer, and doing so is worthy of an assertion failure
or crash. However, we've long since flushed out any easy-to-find bugs
of that nature. What remains is a lot of code that perhaps could fail
that way in hard-to-reach corner cases. For example, in something as
simple as
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("constraint \"%s\" for table \"%s\" does not exist",
conname, get_rel_name(relid))));
one must wonder whether it's completely guaranteed that get_rel_name
cannot return NULL in this context. If such a situation did occur,
the existing policy converts what might be a pretty minor bug into
a server crash condition. This is not good for robustness.
Hence, let's follow the lead of glibc and print "(null)" instead
of failing. We should, of course, still consider it a bug if that
behavior is reachable in ordinary use; but crashing seems less
desirable than not crashing.
This fix works across-the-board in v12 and up, where we always use
src/port/snprintf.c. Before that, on most platforms we're at the mercy
of the local libc, but it appears that Solaris 10 is the only supported
platform where we'd still get a crash. Most other platforms such as
*BSD, macOS, and Solaris 11 have adopted glibc's behavior at some
point. (AIX and HPUX just print "" not "(null)", but that's close
enough.) I've not checked what Windows' native printf would do, but
it doesn't matter because we've long used snprintf.c on that platform.
In v12 and up, also const-ify related code so that we're not casting
away const on the constant string. This is just neatnik-ism, since
next to no compilers will warn about that.
Discussion: https://postgr.es/m/17098-b960f3616c861f83@postgresql.org
2021-07-24 19:41:17 +02:00
|
|
|
static void fmtptr(const void *value, PrintfTarget *target);
|
Rationalize snprintf.c's handling of "ll" formats.
Although all known platforms define "long long" as 64 bits, it still feels
a bit shaky to be using "va_arg(args, int64)" to pull out an argument that
the caller thought was declared "long long". The reason it was coded like
this, way back in commit 3311c7669, was to work around the possibility that
the compiler had no type named "long long" --- and, at the time, that it
maybe didn't have 64-bit ints at all. Now that we're requiring compilers
to support C99, those concerns are moot. Let's make the code clearer and
more bulletproof by writing "long long" where we mean "long long".
This does introduce a hazard that we'd inefficiently use 128-bit arithmetic
to convert plain old integers. The way to tackle that would be to provide
two versions of fmtint(), one for "long long" and one for narrower types.
Since, as of today, no platforms require that, we won't bother with the
extra code for now.
Discussion: https://postgr.es/m/1680.1538587115@sss.pgh.pa.us
2018-10-03 20:33:13 +02:00
|
|
|
static void fmtint(long long value, char type, int forcesign,
|
2005-12-05 03:39:38 +01:00
|
|
|
int leftjust, int minlen, int zpad, int precision, int pointflag,
|
|
|
|
PrintfTarget *target);
|
|
|
|
static void fmtchar(int value, int leftjust, int minlen, PrintfTarget *target);
|
2005-03-16 22:27:23 +01:00
|
|
|
static void fmtfloat(double value, char type, int forcesign,
|
2005-12-05 03:39:38 +01:00
|
|
|
int leftjust, int minlen, int zpad, int precision, int pointflag,
|
|
|
|
PrintfTarget *target);
|
|
|
|
static void dostr(const char *str, int slen, PrintfTarget *target);
|
|
|
|
static void dopr_outch(int c, PrintfTarget *target);
|
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
2018-10-03 16:18:15 +02:00
|
|
|
static void dopr_outchmulti(int c, int slen, PrintfTarget *target);
|
2005-12-05 03:39:38 +01:00
|
|
|
static int adjust_sign(int is_negative, int forcesign, int *signvalue);
|
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
2018-10-03 16:18:15 +02:00
|
|
|
static int compute_padlen(int minlen, int vallen, int leftjust);
|
|
|
|
static void leading_pad(int zpad, int signvalue, int *padlen,
|
2005-12-05 03:39:38 +01:00
|
|
|
PrintfTarget *target);
|
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
2018-10-03 16:18:15 +02:00
|
|
|
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 */
|
2005-12-05 03:39:38 +01:00
|
|
|
|
1998-09-04 16:34:23 +02:00
|
|
|
|
2005-03-17 04:18:14 +01:00
|
|
|
/*
|
2018-09-26 19:31:56 +02:00
|
|
|
* dopr(): the guts of *printf for all cases.
|
2005-03-17 04:18:14 +01:00
|
|
|
*/
|
Add error-throwing wrappers for the printf family of functions.
All known standard library implementations of these functions can fail
with ENOMEM. A caller neglecting to check for failure would experience
missing output, information exposure, or a crash. Check return values
within wrappers and code, currently just snprintf.c, that bypasses the
wrappers. The wrappers do not return after an error, so their callers
need not check. Back-patch to 9.0 (all supported versions).
Popular free software standard library implementations do take pains to
bypass malloc() in simple cases, but they risk ENOMEM for floating point
numbers, positional arguments, large field widths, and large precisions.
No specification demands such caution, so this commit regards every call
to a printf family function as a potential threat.
Injecting the wrappers implicitly is a compromise between patch scope
and design goals. I would prefer to edit each call site to name a
wrapper explicitly. libpq and the ECPG libraries would, ideally, convey
errors to the caller rather than abort(). All that would be painfully
invasive for a back-patched security fix, hence this compromise.
Security: CVE-2015-3166
2015-05-18 16:02:31 +02:00
|
|
|
static void
|
2005-12-05 03:39:38 +01:00
|
|
|
dopr(PrintfTarget *target, const char *format, va_list args)
|
1998-08-01 21:30:29 +02:00
|
|
|
{
|
2018-09-26 19:31:56 +02:00
|
|
|
int save_errno = errno;
|
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
2018-10-03 16:18:15 +02:00
|
|
|
const char *first_pct = NULL;
|
1998-10-07 19:12:52 +02:00
|
|
|
int ch;
|
2005-12-05 03:39:38 +01:00
|
|
|
bool have_dollar;
|
|
|
|
bool have_star;
|
|
|
|
bool afterstar;
|
|
|
|
int accum;
|
2005-03-16 22:27:23 +01:00
|
|
|
int longlongflag;
|
|
|
|
int longflag;
|
|
|
|
int pointflag;
|
2005-03-17 04:18:14 +01:00
|
|
|
int leftjust;
|
2005-12-05 03:39:38 +01:00
|
|
|
int fieldwidth;
|
|
|
|
int precision;
|
1998-10-07 19:12:52 +02:00
|
|
|
int zpad;
|
2005-03-16 22:27:23 +01:00
|
|
|
int forcesign;
|
2005-12-05 03:39:38 +01:00
|
|
|
int fmtpos;
|
|
|
|
int cvalue;
|
Rationalize snprintf.c's handling of "ll" formats.
Although all known platforms define "long long" as 64 bits, it still feels
a bit shaky to be using "va_arg(args, int64)" to pull out an argument that
the caller thought was declared "long long". The reason it was coded like
this, way back in commit 3311c7669, was to work around the possibility that
the compiler had no type named "long long" --- and, at the time, that it
maybe didn't have 64-bit ints at all. Now that we're requiring compilers
to support C99, those concerns are moot. Let's make the code clearer and
more bulletproof by writing "long long" where we mean "long long".
This does introduce a hazard that we'd inefficiently use 128-bit arithmetic
to convert plain old integers. The way to tackle that would be to provide
two versions of fmtint(), one for "long long" and one for narrower types.
Since, as of today, no platforms require that, we won't bother with the
extra code for now.
Discussion: https://postgr.es/m/1680.1538587115@sss.pgh.pa.us
2018-10-03 20:33:13 +02:00
|
|
|
long long numvalue;
|
2005-12-05 03:39:38 +01:00
|
|
|
double fvalue;
|
Make printf("%s", NULL) print "(null)" instead of crashing.
We previously took a hard-line attitude that callers should never print
a null string pointer, and doing so is worthy of an assertion failure
or crash. However, we've long since flushed out any easy-to-find bugs
of that nature. What remains is a lot of code that perhaps could fail
that way in hard-to-reach corner cases. For example, in something as
simple as
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("constraint \"%s\" for table \"%s\" does not exist",
conname, get_rel_name(relid))));
one must wonder whether it's completely guaranteed that get_rel_name
cannot return NULL in this context. If such a situation did occur,
the existing policy converts what might be a pretty minor bug into
a server crash condition. This is not good for robustness.
Hence, let's follow the lead of glibc and print "(null)" instead
of failing. We should, of course, still consider it a bug if that
behavior is reachable in ordinary use; but crashing seems less
desirable than not crashing.
This fix works across-the-board in v12 and up, where we always use
src/port/snprintf.c. Before that, on most platforms we're at the mercy
of the local libc, but it appears that Solaris 10 is the only supported
platform where we'd still get a crash. Most other platforms such as
*BSD, macOS, and Solaris 11 have adopted glibc's behavior at some
point. (AIX and HPUX just print "" not "(null)", but that's close
enough.) I've not checked what Windows' native printf would do, but
it doesn't matter because we've long used snprintf.c on that platform.
In v12 and up, also const-ify related code so that we're not casting
away const on the constant string. This is just neatnik-ism, since
next to no compilers will warn about that.
Discussion: https://postgr.es/m/17098-b960f3616c861f83@postgresql.org
2021-07-24 19:41:17 +02:00
|
|
|
const char *strvalue;
|
Set snprintf.c's maximum number of NL arguments to be 31.
Previously, we used the platform's NL_ARGMAX if any, otherwise 16.
The trouble with this is that the platform value is hugely variable,
ranging from the POSIX-minimum 9 to as much as 64K on recent FreeBSD.
Values of more than a dozen or two have no practical use and slow down
the initialization of the argtypes array. Worse, they cause snprintf.c
to consume far more stack space than was the design intention, possibly
resulting in stack-overflow crashes.
Standardize on 31, which is comfortably more than we need (it looks like
no existing translatable message has more than about 10 parameters).
I chose that, not 32, to make the array sizes powers of 2, for some
possible small gain in speed of the memset.
The lack of reported crashes suggests that the set of platforms we
use snprintf.c on (in released branches) may have no overlap with
the set where NL_ARGMAX has unreasonably large values. But that's
not entirely clear, so back-patch to all supported branches.
Per report from Mateusz Guzik (via Thomas Munro).
Discussion: https://postgr.es/m/CAEepm=3VF=PUp2f8gU8fgZB22yPE_KBS0+e1AHAtQ=09schTHg@mail.gmail.com
2018-10-02 18:41:28 +02:00
|
|
|
PrintfArgValue argvalues[PG_NL_ARGMAX + 1];
|
1998-10-07 19:12:52 +02:00
|
|
|
|
2005-04-14 22:53:09 +02: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
2018-10-03 16:18:15 +02:00
|
|
|
* 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.
|
2005-04-14 22:53:09 +02: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
2018-10-03 16:18:15 +02:00
|
|
|
have_dollar = false;
|
2005-03-20 04:53:39 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
while (*format != '\0')
|
2005-03-02 06:22:22 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
/* Locate next conversion specifier */
|
|
|
|
if (*format != '%')
|
2005-12-05 03:39:38 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
/* Scan to next '%' or end of string */
|
|
|
|
const char *next_pct = strchrnul(format + 1, '%');
|
2005-12-05 03:39:38 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
/* Dump literal data we just scanned over */
|
|
|
|
dostr(format, next_pct - format, target);
|
|
|
|
if (target->failed)
|
2005-12-05 03:39:38 +01:00
|
|
|
break;
|
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
2018-10-03 16:18:15 +02:00
|
|
|
|
|
|
|
if (*next_pct == '\0')
|
2005-12-05 03:39:38 +01:00
|
|
|
break;
|
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
2018-10-03 16:18:15 +02:00
|
|
|
format = next_pct;
|
2005-12-05 03:39:38 +01:00
|
|
|
}
|
2006-10-04 02:30:14 +02:00
|
|
|
|
2005-12-05 03:39:38 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
* 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.
|
2005-12-05 03:39:38 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
if (first_pct == NULL)
|
|
|
|
first_pct = format;
|
Add error-throwing wrappers for the printf family of functions.
All known standard library implementations of these functions can fail
with ENOMEM. A caller neglecting to check for failure would experience
missing output, information exposure, or a crash. Check return values
within wrappers and code, currently just snprintf.c, that bypasses the
wrappers. The wrappers do not return after an error, so their callers
need not check. Back-patch to 9.0 (all supported versions).
Popular free software standard library implementations do take pains to
bypass malloc() in simple cases, but they risk ENOMEM for floating point
numbers, positional arguments, large field widths, and large precisions.
No specification demands such caution, so this commit regards every call
to a printf family function as a potential threat.
Injecting the wrappers implicitly is a compromise between patch scope
and design goals. I would prefer to edit each call site to name a
wrapper explicitly. libpq and the ECPG libraries would, ideally, convey
errors to the caller rather than abort(). All that would be painfully
invasive for a back-patched security fix, hence this compromise.
Security: CVE-2015-3166
2015-05-18 16:02:31 +02: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
2018-10-03 16:18:15 +02:00
|
|
|
/* Process conversion spec starting at *format */
|
|
|
|
format++;
|
2018-10-03 19:05:01 +02:00
|
|
|
|
|
|
|
/* Fast path for conversion spec that is exactly %s */
|
|
|
|
if (*format == 's')
|
|
|
|
{
|
|
|
|
format++;
|
|
|
|
strvalue = va_arg(args, char *);
|
Make printf("%s", NULL) print "(null)" instead of crashing.
We previously took a hard-line attitude that callers should never print
a null string pointer, and doing so is worthy of an assertion failure
or crash. However, we've long since flushed out any easy-to-find bugs
of that nature. What remains is a lot of code that perhaps could fail
that way in hard-to-reach corner cases. For example, in something as
simple as
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("constraint \"%s\" for table \"%s\" does not exist",
conname, get_rel_name(relid))));
one must wonder whether it's completely guaranteed that get_rel_name
cannot return NULL in this context. If such a situation did occur,
the existing policy converts what might be a pretty minor bug into
a server crash condition. This is not good for robustness.
Hence, let's follow the lead of glibc and print "(null)" instead
of failing. We should, of course, still consider it a bug if that
behavior is reachable in ordinary use; but crashing seems less
desirable than not crashing.
This fix works across-the-board in v12 and up, where we always use
src/port/snprintf.c. Before that, on most platforms we're at the mercy
of the local libc, but it appears that Solaris 10 is the only supported
platform where we'd still get a crash. Most other platforms such as
*BSD, macOS, and Solaris 11 have adopted glibc's behavior at some
point. (AIX and HPUX just print "" not "(null)", but that's close
enough.) I've not checked what Windows' native printf would do, but
it doesn't matter because we've long used snprintf.c on that platform.
In v12 and up, also const-ify related code so that we're not casting
away const on the constant string. This is just neatnik-ism, since
next to no compilers will warn about that.
Discussion: https://postgr.es/m/17098-b960f3616c861f83@postgresql.org
2021-07-24 19:41:17 +02:00
|
|
|
if (strvalue == NULL)
|
|
|
|
strvalue = "(null)";
|
2018-10-03 19:05:01 +02:00
|
|
|
dostr(strvalue, strlen(strvalue), target);
|
|
|
|
if (target->failed)
|
|
|
|
break;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2005-12-05 03:39:38 +01:00
|
|
|
fieldwidth = precision = zpad = leftjust = forcesign = 0;
|
|
|
|
longflag = longlongflag = pointflag = 0;
|
|
|
|
fmtpos = accum = 0;
|
|
|
|
have_star = afterstar = false;
|
|
|
|
nextch2:
|
|
|
|
ch = *format++;
|
1998-10-07 19:12:52 +02:00
|
|
|
switch (ch)
|
|
|
|
{
|
2005-12-05 03:39:38 +01:00
|
|
|
case '-':
|
|
|
|
leftjust = 1;
|
|
|
|
goto nextch2;
|
|
|
|
case '+':
|
|
|
|
forcesign = 1;
|
|
|
|
goto nextch2;
|
|
|
|
case '0':
|
|
|
|
/* set zero padding if no nonzero digits yet */
|
|
|
|
if (accum == 0 && !pointflag)
|
|
|
|
zpad = '0';
|
2020-05-13 21:31:14 +02:00
|
|
|
/* FALL THRU */
|
2005-12-05 03:39:38 +01:00
|
|
|
case '1':
|
|
|
|
case '2':
|
|
|
|
case '3':
|
|
|
|
case '4':
|
|
|
|
case '5':
|
|
|
|
case '6':
|
|
|
|
case '7':
|
|
|
|
case '8':
|
|
|
|
case '9':
|
|
|
|
accum = accum * 10 + (ch - '0');
|
|
|
|
goto nextch2;
|
|
|
|
case '.':
|
|
|
|
if (have_star)
|
|
|
|
have_star = false;
|
|
|
|
else
|
|
|
|
fieldwidth = accum;
|
|
|
|
pointflag = 1;
|
|
|
|
accum = 0;
|
|
|
|
goto nextch2;
|
|
|
|
case '*':
|
|
|
|
if (have_dollar)
|
1998-10-07 19:12:52 +02: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
2018-10-03 16:18:15 +02:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2005-12-05 03:39:38 +01:00
|
|
|
afterstar = true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* fetch and process value now */
|
|
|
|
int starval = va_arg(args, int);
|
|
|
|
|
|
|
|
if (pointflag)
|
|
|
|
{
|
|
|
|
precision = starval;
|
|
|
|
if (precision < 0)
|
2008-03-18 02:49:44 +01:00
|
|
|
{
|
2005-12-05 03:39:38 +01:00
|
|
|
precision = 0;
|
2008-03-18 02:49:44 +01:00
|
|
|
pointflag = 0;
|
|
|
|
}
|
2005-12-05 03:39:38 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fieldwidth = starval;
|
|
|
|
if (fieldwidth < 0)
|
2005-03-16 07:00:58 +01:00
|
|
|
{
|
2005-12-05 03:39:38 +01:00
|
|
|
leftjust = 1;
|
|
|
|
fieldwidth = -fieldwidth;
|
2005-03-16 07:00:58 +01:00
|
|
|
}
|
2005-12-05 03:39:38 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
have_star = true;
|
|
|
|
accum = 0;
|
|
|
|
goto nextch2;
|
|
|
|
case '$':
|
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
2018-10-03 16:18:15 +02:00
|
|
|
/* 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;
|
|
|
|
}
|
2005-12-05 03:39:38 +01:00
|
|
|
if (afterstar)
|
|
|
|
{
|
|
|
|
/* fetch and process star value */
|
|
|
|
int starval = argvalues[accum].i;
|
|
|
|
|
|
|
|
if (pointflag)
|
|
|
|
{
|
|
|
|
precision = starval;
|
|
|
|
if (precision < 0)
|
2008-03-18 02:49:44 +01:00
|
|
|
{
|
2005-12-05 03:39:38 +01:00
|
|
|
precision = 0;
|
2008-03-18 02:49:44 +01:00
|
|
|
pointflag = 0;
|
|
|
|
}
|
2005-12-05 03:39:38 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fieldwidth = starval;
|
|
|
|
if (fieldwidth < 0)
|
2005-03-02 15:48:22 +01:00
|
|
|
{
|
2005-12-05 03:39:38 +01:00
|
|
|
leftjust = 1;
|
|
|
|
fieldwidth = -fieldwidth;
|
2005-03-02 16:07:09 +01:00
|
|
|
}
|
2005-12-05 03:39:38 +01:00
|
|
|
}
|
|
|
|
afterstar = false;
|
1998-10-07 19:12:52 +02:00
|
|
|
}
|
2005-12-05 03:39:38 +01:00
|
|
|
else
|
|
|
|
fmtpos = accum;
|
|
|
|
accum = 0;
|
|
|
|
goto nextch2;
|
|
|
|
case 'l':
|
|
|
|
if (longflag)
|
|
|
|
longlongflag = 1;
|
|
|
|
else
|
|
|
|
longflag = 1;
|
|
|
|
goto nextch2;
|
2014-01-23 23:18:23 +01:00
|
|
|
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 nextch2;
|
2005-12-05 03:39:38 +01:00
|
|
|
case 'h':
|
|
|
|
case '\'':
|
|
|
|
/* ignore these */
|
|
|
|
goto nextch2;
|
|
|
|
case 'd':
|
|
|
|
case 'i':
|
|
|
|
if (!have_star)
|
|
|
|
{
|
|
|
|
if (pointflag)
|
|
|
|
precision = accum;
|
|
|
|
else
|
|
|
|
fieldwidth = accum;
|
|
|
|
}
|
|
|
|
if (have_dollar)
|
|
|
|
{
|
|
|
|
if (longlongflag)
|
|
|
|
numvalue = argvalues[fmtpos].ll;
|
|
|
|
else if (longflag)
|
|
|
|
numvalue = argvalues[fmtpos].l;
|
|
|
|
else
|
|
|
|
numvalue = argvalues[fmtpos].i;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (longlongflag)
|
Rationalize snprintf.c's handling of "ll" formats.
Although all known platforms define "long long" as 64 bits, it still feels
a bit shaky to be using "va_arg(args, int64)" to pull out an argument that
the caller thought was declared "long long". The reason it was coded like
this, way back in commit 3311c7669, was to work around the possibility that
the compiler had no type named "long long" --- and, at the time, that it
maybe didn't have 64-bit ints at all. Now that we're requiring compilers
to support C99, those concerns are moot. Let's make the code clearer and
more bulletproof by writing "long long" where we mean "long long".
This does introduce a hazard that we'd inefficiently use 128-bit arithmetic
to convert plain old integers. The way to tackle that would be to provide
two versions of fmtint(), one for "long long" and one for narrower types.
Since, as of today, no platforms require that, we won't bother with the
extra code for now.
Discussion: https://postgr.es/m/1680.1538587115@sss.pgh.pa.us
2018-10-03 20:33:13 +02:00
|
|
|
numvalue = va_arg(args, long long);
|
2005-12-05 03:39:38 +01:00
|
|
|
else if (longflag)
|
|
|
|
numvalue = va_arg(args, long);
|
|
|
|
else
|
|
|
|
numvalue = va_arg(args, int);
|
|
|
|
}
|
|
|
|
fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
|
|
|
|
precision, pointflag, target);
|
1998-10-07 19:12:52 +02:00
|
|
|
break;
|
2005-12-05 03:39:38 +01:00
|
|
|
case 'o':
|
|
|
|
case 'u':
|
|
|
|
case 'x':
|
|
|
|
case 'X':
|
|
|
|
if (!have_star)
|
|
|
|
{
|
|
|
|
if (pointflag)
|
|
|
|
precision = accum;
|
|
|
|
else
|
|
|
|
fieldwidth = accum;
|
|
|
|
}
|
|
|
|
if (have_dollar)
|
2005-03-16 16:12:18 +01:00
|
|
|
{
|
2005-12-05 03:39:38 +01:00
|
|
|
if (longlongflag)
|
Rationalize snprintf.c's handling of "ll" formats.
Although all known platforms define "long long" as 64 bits, it still feels
a bit shaky to be using "va_arg(args, int64)" to pull out an argument that
the caller thought was declared "long long". The reason it was coded like
this, way back in commit 3311c7669, was to work around the possibility that
the compiler had no type named "long long" --- and, at the time, that it
maybe didn't have 64-bit ints at all. Now that we're requiring compilers
to support C99, those concerns are moot. Let's make the code clearer and
more bulletproof by writing "long long" where we mean "long long".
This does introduce a hazard that we'd inefficiently use 128-bit arithmetic
to convert plain old integers. The way to tackle that would be to provide
two versions of fmtint(), one for "long long" and one for narrower types.
Since, as of today, no platforms require that, we won't bother with the
extra code for now.
Discussion: https://postgr.es/m/1680.1538587115@sss.pgh.pa.us
2018-10-03 20:33:13 +02:00
|
|
|
numvalue = (unsigned long long) argvalues[fmtpos].ll;
|
2005-12-05 03:39:38 +01:00
|
|
|
else if (longflag)
|
|
|
|
numvalue = (unsigned long) argvalues[fmtpos].l;
|
2005-03-16 16:12:18 +01:00
|
|
|
else
|
2005-12-05 03:39:38 +01:00
|
|
|
numvalue = (unsigned int) argvalues[fmtpos].i;
|
2005-03-16 16:12:18 +01:00
|
|
|
}
|
2005-03-16 07:00:58 +01:00
|
|
|
else
|
2005-12-05 03:39:38 +01:00
|
|
|
{
|
|
|
|
if (longlongflag)
|
Rationalize snprintf.c's handling of "ll" formats.
Although all known platforms define "long long" as 64 bits, it still feels
a bit shaky to be using "va_arg(args, int64)" to pull out an argument that
the caller thought was declared "long long". The reason it was coded like
this, way back in commit 3311c7669, was to work around the possibility that
the compiler had no type named "long long" --- and, at the time, that it
maybe didn't have 64-bit ints at all. Now that we're requiring compilers
to support C99, those concerns are moot. Let's make the code clearer and
more bulletproof by writing "long long" where we mean "long long".
This does introduce a hazard that we'd inefficiently use 128-bit arithmetic
to convert plain old integers. The way to tackle that would be to provide
two versions of fmtint(), one for "long long" and one for narrower types.
Since, as of today, no platforms require that, we won't bother with the
extra code for now.
Discussion: https://postgr.es/m/1680.1538587115@sss.pgh.pa.us
2018-10-03 20:33:13 +02:00
|
|
|
numvalue = (unsigned long long) va_arg(args, long long);
|
2005-12-05 03:39:38 +01:00
|
|
|
else if (longflag)
|
|
|
|
numvalue = (unsigned long) va_arg(args, long);
|
|
|
|
else
|
|
|
|
numvalue = (unsigned int) va_arg(args, int);
|
|
|
|
}
|
|
|
|
fmtint(numvalue, ch, forcesign, leftjust, fieldwidth, zpad,
|
|
|
|
precision, pointflag, target);
|
2005-03-16 16:12:18 +01:00
|
|
|
break;
|
2005-12-05 03:39:38 +01:00
|
|
|
case 'c':
|
|
|
|
if (!have_star)
|
2005-03-16 16:12:18 +01:00
|
|
|
{
|
2005-12-05 03:39:38 +01:00
|
|
|
if (pointflag)
|
|
|
|
precision = accum;
|
2005-03-16 16:12:18 +01:00
|
|
|
else
|
2005-12-05 03:39:38 +01:00
|
|
|
fieldwidth = accum;
|
2005-03-16 16:12:18 +01:00
|
|
|
}
|
2005-12-05 03:39:38 +01:00
|
|
|
if (have_dollar)
|
|
|
|
cvalue = (unsigned char) argvalues[fmtpos].i;
|
2005-03-16 07:00:58 +01:00
|
|
|
else
|
2005-12-05 03:39:38 +01:00
|
|
|
cvalue = (unsigned char) va_arg(args, int);
|
|
|
|
fmtchar(cvalue, leftjust, fieldwidth, target);
|
2005-03-16 16:12:18 +01:00
|
|
|
break;
|
2005-12-05 03:39:38 +01:00
|
|
|
case 's':
|
|
|
|
if (!have_star)
|
|
|
|
{
|
|
|
|
if (pointflag)
|
|
|
|
precision = accum;
|
|
|
|
else
|
|
|
|
fieldwidth = accum;
|
|
|
|
}
|
|
|
|
if (have_dollar)
|
|
|
|
strvalue = argvalues[fmtpos].cptr;
|
|
|
|
else
|
|
|
|
strvalue = va_arg(args, char *);
|
Make printf("%s", NULL) print "(null)" instead of crashing.
We previously took a hard-line attitude that callers should never print
a null string pointer, and doing so is worthy of an assertion failure
or crash. However, we've long since flushed out any easy-to-find bugs
of that nature. What remains is a lot of code that perhaps could fail
that way in hard-to-reach corner cases. For example, in something as
simple as
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("constraint \"%s\" for table \"%s\" does not exist",
conname, get_rel_name(relid))));
one must wonder whether it's completely guaranteed that get_rel_name
cannot return NULL in this context. If such a situation did occur,
the existing policy converts what might be a pretty minor bug into
a server crash condition. This is not good for robustness.
Hence, let's follow the lead of glibc and print "(null)" instead
of failing. We should, of course, still consider it a bug if that
behavior is reachable in ordinary use; but crashing seems less
desirable than not crashing.
This fix works across-the-board in v12 and up, where we always use
src/port/snprintf.c. Before that, on most platforms we're at the mercy
of the local libc, but it appears that Solaris 10 is the only supported
platform where we'd still get a crash. Most other platforms such as
*BSD, macOS, and Solaris 11 have adopted glibc's behavior at some
point. (AIX and HPUX just print "" not "(null)", but that's close
enough.) I've not checked what Windows' native printf would do, but
it doesn't matter because we've long used snprintf.c on that platform.
In v12 and up, also const-ify related code so that we're not casting
away const on the constant string. This is just neatnik-ism, since
next to no compilers will warn about that.
Discussion: https://postgr.es/m/17098-b960f3616c861f83@postgresql.org
2021-07-24 19:41:17 +02:00
|
|
|
/* If string is NULL, silently substitute "(null)" */
|
|
|
|
if (strvalue == NULL)
|
|
|
|
strvalue = "(null)";
|
2005-12-05 03:39:38 +01:00
|
|
|
fmtstr(strvalue, leftjust, fieldwidth, precision, pointflag,
|
|
|
|
target);
|
2005-03-16 16:12:18 +01:00
|
|
|
break;
|
2005-12-05 03:39:38 +01:00
|
|
|
case 'p':
|
|
|
|
/* fieldwidth/leftjust are ignored ... */
|
|
|
|
if (have_dollar)
|
|
|
|
strvalue = argvalues[fmtpos].cptr;
|
|
|
|
else
|
|
|
|
strvalue = va_arg(args, char *);
|
Make printf("%s", NULL) print "(null)" instead of crashing.
We previously took a hard-line attitude that callers should never print
a null string pointer, and doing so is worthy of an assertion failure
or crash. However, we've long since flushed out any easy-to-find bugs
of that nature. What remains is a lot of code that perhaps could fail
that way in hard-to-reach corner cases. For example, in something as
simple as
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("constraint \"%s\" for table \"%s\" does not exist",
conname, get_rel_name(relid))));
one must wonder whether it's completely guaranteed that get_rel_name
cannot return NULL in this context. If such a situation did occur,
the existing policy converts what might be a pretty minor bug into
a server crash condition. This is not good for robustness.
Hence, let's follow the lead of glibc and print "(null)" instead
of failing. We should, of course, still consider it a bug if that
behavior is reachable in ordinary use; but crashing seems less
desirable than not crashing.
This fix works across-the-board in v12 and up, where we always use
src/port/snprintf.c. Before that, on most platforms we're at the mercy
of the local libc, but it appears that Solaris 10 is the only supported
platform where we'd still get a crash. Most other platforms such as
*BSD, macOS, and Solaris 11 have adopted glibc's behavior at some
point. (AIX and HPUX just print "" not "(null)", but that's close
enough.) I've not checked what Windows' native printf would do, but
it doesn't matter because we've long used snprintf.c on that platform.
In v12 and up, also const-ify related code so that we're not casting
away const on the constant string. This is just neatnik-ism, since
next to no compilers will warn about that.
Discussion: https://postgr.es/m/17098-b960f3616c861f83@postgresql.org
2021-07-24 19:41:17 +02:00
|
|
|
fmtptr((const void *) strvalue, target);
|
2005-03-16 16:12:18 +01:00
|
|
|
break;
|
2005-12-05 03:39:38 +01:00
|
|
|
case 'e':
|
|
|
|
case 'E':
|
|
|
|
case 'f':
|
|
|
|
case 'g':
|
|
|
|
case 'G':
|
|
|
|
if (!have_star)
|
2005-03-17 04:18:14 +01:00
|
|
|
{
|
2005-12-05 03:39:38 +01:00
|
|
|
if (pointflag)
|
|
|
|
precision = accum;
|
|
|
|
else
|
|
|
|
fieldwidth = accum;
|
2005-03-17 04:18:14 +01:00
|
|
|
}
|
2005-12-05 03:39:38 +01:00
|
|
|
if (have_dollar)
|
|
|
|
fvalue = argvalues[fmtpos].d;
|
|
|
|
else
|
|
|
|
fvalue = va_arg(args, double);
|
|
|
|
fmtfloat(fvalue, ch, forcesign, leftjust,
|
|
|
|
fieldwidth, zpad,
|
|
|
|
precision, pointflag,
|
|
|
|
target);
|
2005-03-16 16:12:18 +01:00
|
|
|
break;
|
2018-09-26 19:31:56 +02:00
|
|
|
case 'm':
|
|
|
|
{
|
|
|
|
char errbuf[PG_STRERROR_R_BUFLEN];
|
|
|
|
const char *errm = strerror_r(save_errno,
|
|
|
|
errbuf, sizeof(errbuf));
|
|
|
|
|
|
|
|
dostr(errm, strlen(errm), target);
|
|
|
|
}
|
|
|
|
break;
|
2005-12-05 03:39:38 +01:00
|
|
|
case '%':
|
|
|
|
dopr_outch('%', target);
|
2005-03-16 16:12:18 +01:00
|
|
|
break;
|
2018-12-06 21:08:44 +01:00
|
|
|
default:
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Anything else --- in particular, '\0' indicating end of
|
|
|
|
* format string --- is bogus.
|
|
|
|
*/
|
|
|
|
goto bad_format;
|
2005-03-16 07:00:58 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
|
|
|
|
/* Check for failure after each conversion spec */
|
|
|
|
if (target->failed)
|
|
|
|
break;
|
2005-03-16 07:00:58 +01:00
|
|
|
}
|
|
|
|
|
Add error-throwing wrappers for the printf family of functions.
All known standard library implementations of these functions can fail
with ENOMEM. A caller neglecting to check for failure would experience
missing output, information exposure, or a crash. Check return values
within wrappers and code, currently just snprintf.c, that bypasses the
wrappers. The wrappers do not return after an error, so their callers
need not check. Back-patch to 9.0 (all supported versions).
Popular free software standard library implementations do take pains to
bypass malloc() in simple cases, but they risk ENOMEM for floating point
numbers, positional arguments, large field widths, and large precisions.
No specification demands such caution, so this commit regards every call
to a printf family function as a potential threat.
Injecting the wrappers implicitly is a compromise between patch scope
and design goals. I would prefer to edit each call site to name a
wrapper explicitly. libpq and the ECPG libraries would, ideally, convey
errors to the caller rather than abort(). All that would be painfully
invasive for a back-patched security fix, hence this compromise.
Security: CVE-2015-3166
2015-05-18 16:02:31 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
bad_format:
|
|
|
|
errno = EINVAL;
|
|
|
|
target->failed = true;
|
2005-12-05 03:39:38 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
/*
|
|
|
|
* 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;
|
2022-07-16 08:42:15 +02:00
|
|
|
int last_dollar = 0; /* Init to "no dollar arguments known" */
|
|
|
|
PrintfArgType argtypes[PG_NL_ARGMAX + 1] = {0};
|
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
2018-10-03 16:18:15 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* 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++;
|
|
|
|
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;
|
2018-12-06 21:08:44 +01:00
|
|
|
default:
|
|
|
|
return false; /* bogus format string */
|
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
2018-10-03 16:18:15 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* 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:
|
Rationalize snprintf.c's handling of "ll" formats.
Although all known platforms define "long long" as 64 bits, it still feels
a bit shaky to be using "va_arg(args, int64)" to pull out an argument that
the caller thought was declared "long long". The reason it was coded like
this, way back in commit 3311c7669, was to work around the possibility that
the compiler had no type named "long long" --- and, at the time, that it
maybe didn't have 64-bit ints at all. Now that we're requiring compilers
to support C99, those concerns are moot. Let's make the code clearer and
more bulletproof by writing "long long" where we mean "long long".
This does introduce a hazard that we'd inefficiently use 128-bit arithmetic
to convert plain old integers. The way to tackle that would be to provide
two versions of fmtint(), one for "long long" and one for narrower types.
Since, as of today, no platforms require that, we won't bother with the
extra code for now.
Discussion: https://postgr.es/m/1680.1538587115@sss.pgh.pa.us
2018-10-03 20:33:13 +02:00
|
|
|
argvalues[i].ll = va_arg(args, long long);
|
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
2018-10-03 16:18:15 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
1998-09-04 16:34:23 +02:00
|
|
|
static void
|
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
2018-10-03 16:18:15 +02:00
|
|
|
fmtstr(const char *value, int leftjust, int minlen, int maxwidth,
|
2005-12-05 03:39:38 +01:00
|
|
|
int pointflag, PrintfTarget *target)
|
1998-09-04 16:34:23 +02:00
|
|
|
{
|
1998-10-07 19:12:52 +02:00
|
|
|
int padlen,
|
2005-03-16 22:27:23 +01:00
|
|
|
vallen; /* amount to pad */
|
1998-09-04 16:34:23 +02:00
|
|
|
|
2005-12-05 03:39:38 +01:00
|
|
|
/*
|
|
|
|
* If a maxwidth (precision) is specified, we must not fetch more bytes
|
|
|
|
* than that.
|
|
|
|
*/
|
|
|
|
if (pointflag)
|
2017-10-10 23:42:16 +02:00
|
|
|
vallen = strnlen(value, maxwidth);
|
2005-12-05 03:39:38 +01:00
|
|
|
else
|
|
|
|
vallen = strlen(value);
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
padlen = compute_padlen(minlen, vallen, leftjust);
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
if (padlen > 0)
|
1998-10-07 19:12:52 +02: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
2018-10-03 16:18:15 +02:00
|
|
|
dopr_outchmulti(' ', padlen, target);
|
|
|
|
padlen = 0;
|
1998-10-07 19:12:52 +02:00
|
|
|
}
|
2005-03-17 04:18:14 +01:00
|
|
|
|
2005-12-05 03:39:38 +01:00
|
|
|
dostr(value, vallen, target);
|
|
|
|
|
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
2018-10-03 16:18:15 +02:00
|
|
|
trailing_pad(padlen, target);
|
1998-09-04 16:34:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
Make printf("%s", NULL) print "(null)" instead of crashing.
We previously took a hard-line attitude that callers should never print
a null string pointer, and doing so is worthy of an assertion failure
or crash. However, we've long since flushed out any easy-to-find bugs
of that nature. What remains is a lot of code that perhaps could fail
that way in hard-to-reach corner cases. For example, in something as
simple as
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("constraint \"%s\" for table \"%s\" does not exist",
conname, get_rel_name(relid))));
one must wonder whether it's completely guaranteed that get_rel_name
cannot return NULL in this context. If such a situation did occur,
the existing policy converts what might be a pretty minor bug into
a server crash condition. This is not good for robustness.
Hence, let's follow the lead of glibc and print "(null)" instead
of failing. We should, of course, still consider it a bug if that
behavior is reachable in ordinary use; but crashing seems less
desirable than not crashing.
This fix works across-the-board in v12 and up, where we always use
src/port/snprintf.c. Before that, on most platforms we're at the mercy
of the local libc, but it appears that Solaris 10 is the only supported
platform where we'd still get a crash. Most other platforms such as
*BSD, macOS, and Solaris 11 have adopted glibc's behavior at some
point. (AIX and HPUX just print "" not "(null)", but that's close
enough.) I've not checked what Windows' native printf would do, but
it doesn't matter because we've long used snprintf.c on that platform.
In v12 and up, also const-ify related code so that we're not casting
away const on the constant string. This is just neatnik-ism, since
next to no compilers will warn about that.
Discussion: https://postgr.es/m/17098-b960f3616c861f83@postgresql.org
2021-07-24 19:41:17 +02:00
|
|
|
fmtptr(const void *value, PrintfTarget *target)
|
1998-09-04 16:34:23 +02:00
|
|
|
{
|
2005-12-05 03:39:38 +01:00
|
|
|
int vallen;
|
|
|
|
char convert[64];
|
|
|
|
|
Use libc's snprintf, not sprintf, for special cases in snprintf.c.
snprintf.c has always fallen back on libc's *printf implementation
when printing pointers (%p) and floats. When this code originated,
we were still supporting some platforms that lacked native snprintf,
so we used sprintf for that. That's not actually unsafe in our usage,
but nonetheless builds on macOS are starting to complain about sprintf
being unconditionally deprecated; and I wouldn't be surprised if other
platforms follow suit. There seems little reason to believe that any
platform supporting C99 wouldn't have standards-compliant snprintf,
so let's just use that instead to suppress such warnings.
Back-patch to v12, which is where we started to require C99. It's
also where we started to use our snprintf.c everywhere, so this
wouldn't be enough to suppress the warning in older branches anyway
--- that is, in older branches these aren't necessarily all our
usages of libc's sprintf. It is enough in v12+ because any
deprecation annotation attached to libc's sprintf won't apply to
pg_sprintf. (Whether all our usages of pg_sprintf are adequately
safe is not a matter I intend to address here, but perhaps it could
do with some review.)
Per report from Andres Freund and local testing.
Discussion: https://postgr.es/m/20221015211955.q4cwbsfkyk3c4ty3@awork3.anarazel.de
2022-10-16 17:47:44 +02:00
|
|
|
/* we rely on regular C library's snprintf to do the basic conversion */
|
|
|
|
vallen = snprintf(convert, sizeof(convert), "%p", value);
|
Add error-throwing wrappers for the printf family of functions.
All known standard library implementations of these functions can fail
with ENOMEM. A caller neglecting to check for failure would experience
missing output, information exposure, or a crash. Check return values
within wrappers and code, currently just snprintf.c, that bypasses the
wrappers. The wrappers do not return after an error, so their callers
need not check. Back-patch to 9.0 (all supported versions).
Popular free software standard library implementations do take pains to
bypass malloc() in simple cases, but they risk ENOMEM for floating point
numbers, positional arguments, large field widths, and large precisions.
No specification demands such caution, so this commit regards every call
to a printf family function as a potential threat.
Injecting the wrappers implicitly is a compromise between patch scope
and design goals. I would prefer to edit each call site to name a
wrapper explicitly. libpq and the ECPG libraries would, ideally, convey
errors to the caller rather than abort(). All that would be painfully
invasive for a back-patched security fix, hence this compromise.
Security: CVE-2015-3166
2015-05-18 16:02:31 +02:00
|
|
|
if (vallen < 0)
|
|
|
|
target->failed = true;
|
|
|
|
else
|
|
|
|
dostr(convert, vallen, target);
|
2005-12-05 03:39:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
Rationalize snprintf.c's handling of "ll" formats.
Although all known platforms define "long long" as 64 bits, it still feels
a bit shaky to be using "va_arg(args, int64)" to pull out an argument that
the caller thought was declared "long long". The reason it was coded like
this, way back in commit 3311c7669, was to work around the possibility that
the compiler had no type named "long long" --- and, at the time, that it
maybe didn't have 64-bit ints at all. Now that we're requiring compilers
to support C99, those concerns are moot. Let's make the code clearer and
more bulletproof by writing "long long" where we mean "long long".
This does introduce a hazard that we'd inefficiently use 128-bit arithmetic
to convert plain old integers. The way to tackle that would be to provide
two versions of fmtint(), one for "long long" and one for narrower types.
Since, as of today, no platforms require that, we won't bother with the
extra code for now.
Discussion: https://postgr.es/m/1680.1538587115@sss.pgh.pa.us
2018-10-03 20:33:13 +02:00
|
|
|
fmtint(long long value, char type, int forcesign, int leftjust,
|
2005-12-05 03:39:38 +01:00
|
|
|
int minlen, int zpad, int precision, int pointflag,
|
|
|
|
PrintfTarget *target)
|
|
|
|
{
|
2018-10-03 22:02:25 +02:00
|
|
|
unsigned long long uvalue;
|
2021-10-28 19:39:57 +02:00
|
|
|
int base;
|
2005-12-05 03:39:38 +01:00
|
|
|
int dosign;
|
|
|
|
const char *cvt = "0123456789abcdef";
|
1998-10-07 19:12:52 +02:00
|
|
|
int signvalue = 0;
|
1999-02-06 22:51:03 +01:00
|
|
|
char convert[64];
|
2005-03-17 04:18:14 +01:00
|
|
|
int vallen = 0;
|
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
2018-10-03 16:18:15 +02:00
|
|
|
int padlen; /* amount to pad */
|
2005-12-05 03:39:38 +01:00
|
|
|
int zeropad; /* extra leading zeroes */
|
1998-10-07 19:12:52 +02:00
|
|
|
|
2005-12-05 03:39:38 +01:00
|
|
|
switch (type)
|
|
|
|
{
|
|
|
|
case 'd':
|
|
|
|
case 'i':
|
|
|
|
base = 10;
|
|
|
|
dosign = 1;
|
|
|
|
break;
|
|
|
|
case 'o':
|
|
|
|
base = 8;
|
|
|
|
dosign = 0;
|
|
|
|
break;
|
|
|
|
case 'u':
|
|
|
|
base = 10;
|
|
|
|
dosign = 0;
|
|
|
|
break;
|
|
|
|
case 'x':
|
|
|
|
base = 16;
|
|
|
|
dosign = 0;
|
|
|
|
break;
|
|
|
|
case 'X':
|
|
|
|
cvt = "0123456789ABCDEF";
|
|
|
|
base = 16;
|
|
|
|
dosign = 0;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
return; /* keep compiler quiet */
|
|
|
|
}
|
|
|
|
|
2018-11-11 02:20:54 +01:00
|
|
|
/* disable MSVC warning about applying unary minus to an unsigned value */
|
2019-10-19 18:21:58 +02:00
|
|
|
#ifdef _MSC_VER
|
2018-11-11 02:20:54 +01:00
|
|
|
#pragma warning(push)
|
|
|
|
#pragma warning(disable: 4146)
|
|
|
|
#endif
|
2005-12-05 03:39:38 +01:00
|
|
|
/* Handle +/- */
|
2005-03-17 04:18:14 +01:00
|
|
|
if (dosign && adjust_sign((value < 0), forcesign, &signvalue))
|
2018-10-03 22:28:14 +02:00
|
|
|
uvalue = -(unsigned long long) value;
|
2018-10-03 22:02:25 +02:00
|
|
|
else
|
2018-10-03 22:28:14 +02:00
|
|
|
uvalue = (unsigned long long) value;
|
2019-10-19 18:21:58 +02:00
|
|
|
#ifdef _MSC_VER
|
2018-11-11 02:20:54 +01:00
|
|
|
#pragma warning(pop)
|
|
|
|
#endif
|
2005-12-05 03:39:38 +01:00
|
|
|
|
|
|
|
/*
|
|
|
|
* SUS: the result of converting 0 with an explicit precision of 0 is no
|
|
|
|
* characters
|
|
|
|
*/
|
|
|
|
if (value == 0 && pointflag && precision == 0)
|
|
|
|
vallen = 0;
|
|
|
|
else
|
1998-10-07 19:12:52 +02:00
|
|
|
{
|
2021-10-28 19:39:57 +02:00
|
|
|
/*
|
|
|
|
* Convert integer to string. We special-case each of the possible
|
|
|
|
* base values so as to avoid general-purpose divisions. On most
|
|
|
|
* machines, division by a fixed constant can be done much more
|
|
|
|
* cheaply than a general divide.
|
|
|
|
*/
|
|
|
|
if (base == 10)
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 10];
|
|
|
|
uvalue = uvalue / 10;
|
|
|
|
} while (uvalue);
|
|
|
|
}
|
|
|
|
else if (base == 16)
|
2005-12-05 03:39:38 +01:00
|
|
|
{
|
2021-10-28 19:39:57 +02:00
|
|
|
do
|
|
|
|
{
|
|
|
|
convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 16];
|
|
|
|
uvalue = uvalue / 16;
|
|
|
|
} while (uvalue);
|
|
|
|
}
|
|
|
|
else /* base == 8 */
|
|
|
|
{
|
|
|
|
do
|
|
|
|
{
|
|
|
|
convert[sizeof(convert) - (++vallen)] = cvt[uvalue % 8];
|
|
|
|
uvalue = uvalue / 8;
|
|
|
|
} while (uvalue);
|
|
|
|
}
|
1998-10-07 19:12:52 +02:00
|
|
|
}
|
2005-03-17 04:18:14 +01:00
|
|
|
|
2005-12-05 03:39:38 +01:00
|
|
|
zeropad = Max(0, precision - vallen);
|
1999-02-06 22:51:03 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
padlen = compute_padlen(minlen, vallen + zeropad, leftjust);
|
2005-12-05 03:39:38 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
leading_pad(zpad, signvalue, &padlen, target);
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
if (zeropad > 0)
|
|
|
|
dopr_outchmulti('0', zeropad, target);
|
2005-10-15 04:49:52 +02: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
2018-10-03 16:18:15 +02:00
|
|
|
dostr(convert + sizeof(convert) - vallen, vallen, target);
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
trailing_pad(padlen, target);
|
2005-12-05 03:39:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
fmtchar(int value, int leftjust, int minlen, PrintfTarget *target)
|
|
|
|
{
|
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
2018-10-03 16:18:15 +02:00
|
|
|
int padlen; /* amount to pad */
|
2005-12-05 03:39:38 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
padlen = compute_padlen(minlen, 1, leftjust);
|
2005-12-05 03:39:38 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
if (padlen > 0)
|
2005-12-05 03:39:38 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
dopr_outchmulti(' ', padlen, target);
|
|
|
|
padlen = 0;
|
2005-12-05 03:39:38 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
dopr_outch(value, target);
|
|
|
|
|
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
2018-10-03 16:18:15 +02:00
|
|
|
trailing_pad(padlen, target);
|
1998-09-04 16:34:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-03-17 04:18:14 +01:00
|
|
|
fmtfloat(double value, char type, int forcesign, int leftjust,
|
2005-12-05 03:39:38 +01:00
|
|
|
int minlen, int zpad, int precision, int pointflag,
|
|
|
|
PrintfTarget *target)
|
1999-02-06 22:51:03 +01:00
|
|
|
{
|
2005-03-16 22:27:23 +01:00
|
|
|
int signvalue = 0;
|
2015-02-02 16:00:45 +01:00
|
|
|
int prec;
|
2005-03-17 04:18:14 +01:00
|
|
|
int vallen;
|
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
2018-10-03 16:18:15 +02:00
|
|
|
char fmt[8];
|
2015-02-02 16:00:45 +01:00
|
|
|
char convert[1024];
|
|
|
|
int zeropadlen = 0; /* amount to pad with zeroes */
|
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
2018-10-03 16:18:15 +02:00
|
|
|
int padlen; /* amount to pad with spaces */
|
|
|
|
|
2015-02-02 16:00:45 +01:00
|
|
|
/*
|
Use libc's snprintf, not sprintf, for special cases in snprintf.c.
snprintf.c has always fallen back on libc's *printf implementation
when printing pointers (%p) and floats. When this code originated,
we were still supporting some platforms that lacked native snprintf,
so we used sprintf for that. That's not actually unsafe in our usage,
but nonetheless builds on macOS are starting to complain about sprintf
being unconditionally deprecated; and I wouldn't be surprised if other
platforms follow suit. There seems little reason to believe that any
platform supporting C99 wouldn't have standards-compliant snprintf,
so let's just use that instead to suppress such warnings.
Back-patch to v12, which is where we started to require C99. It's
also where we started to use our snprintf.c everywhere, so this
wouldn't be enough to suppress the warning in older branches anyway
--- that is, in older branches these aren't necessarily all our
usages of libc's sprintf. It is enough in v12+ because any
deprecation annotation attached to libc's sprintf won't apply to
pg_sprintf. (Whether all our usages of pg_sprintf are adequately
safe is not a matter I intend to address here, but perhaps it could
do with some review.)
Per report from Andres Freund and local testing.
Discussion: https://postgr.es/m/20221015211955.q4cwbsfkyk3c4ty3@awork3.anarazel.de
2022-10-16 17:47:44 +02:00
|
|
|
* We rely on the regular C library's snprintf to do the basic conversion,
|
2015-02-02 16:00:45 +01:00
|
|
|
* then handle padding considerations here.
|
|
|
|
*
|
|
|
|
* The dynamic range of "double" is about 1E+-308 for IEEE math, and not
|
Use libc's snprintf, not sprintf, for special cases in snprintf.c.
snprintf.c has always fallen back on libc's *printf implementation
when printing pointers (%p) and floats. When this code originated,
we were still supporting some platforms that lacked native snprintf,
so we used sprintf for that. That's not actually unsafe in our usage,
but nonetheless builds on macOS are starting to complain about sprintf
being unconditionally deprecated; and I wouldn't be surprised if other
platforms follow suit. There seems little reason to believe that any
platform supporting C99 wouldn't have standards-compliant snprintf,
so let's just use that instead to suppress such warnings.
Back-patch to v12, which is where we started to require C99. It's
also where we started to use our snprintf.c everywhere, so this
wouldn't be enough to suppress the warning in older branches anyway
--- that is, in older branches these aren't necessarily all our
usages of libc's sprintf. It is enough in v12+ because any
deprecation annotation attached to libc's sprintf won't apply to
pg_sprintf. (Whether all our usages of pg_sprintf are adequately
safe is not a matter I intend to address here, but perhaps it could
do with some review.)
Per report from Andres Freund and local testing.
Discussion: https://postgr.es/m/20221015211955.q4cwbsfkyk3c4ty3@awork3.anarazel.de
2022-10-16 17:47:44 +02:00
|
|
|
* too wildly more than that with other hardware. In "f" format, snprintf
|
2015-02-02 16:00:45 +01:00
|
|
|
* could therefore generate at most 308 characters to the left of the
|
|
|
|
* decimal point; while we need to allow the precision to get as high as
|
|
|
|
* 308+17 to ensure that we don't truncate significant digits from very
|
|
|
|
* small values. To handle both these extremes, we use a buffer of 1024
|
|
|
|
* bytes and limit requested precision to 350 digits; this should prevent
|
|
|
|
* buffer overrun even with non-IEEE math. If the original precision
|
|
|
|
* request was more than 350, separately pad with zeroes.
|
Improve snprintf.c's handling of NaN, Infinity, and minus zero.
Up to now, float4out/float8out handled NaN and Infinity cases explicitly,
and invoked psprintf only for ordinary float values. This was done because
platform implementations of snprintf produce varying representations of
these special cases. But now that we use snprintf.c always, it's better
to give it the responsibility to produce a uniform representation of
these cases, so that we have uniformity across the board not only in
float4out/float8out. Hence, move that work into fmtfloat().
Also, teach fmtfloat() to recognize IEEE minus zero and handle it
correctly. The previous coding worked only accidentally, and would
fail for e.g. "%+f" format (it'd print "+-0.00000"). Now that we're
using snprintf.c everywhere, it's not acceptable for it to do weird
things in corner cases. (This incidentally avoids a portability
problem we've seen on some really ancient platforms, that native
sprintf does the wrong thing with minus zero.)
Also, introduce a new entry point in snprintf.c to allow float[48]out
to bypass the work of interpreting a well-known format spec, as well
as bypassing the overhead of the psprintf layer. I modeled this API
loosely on strfromd(). In my testing, this brings float[48]out back
to approximately the same speed they had when using native snprintf,
fixing one of the main performance issues caused by using snprintf.c.
(There is some talk of more aggressive work to improve the speed of
floating-point output conversion, but these changes seem to provide
a better starting point for such work anyway.)
Getting rid of the previous ad-hoc hack for Infinity/NaN in fmtfloat()
allows removing <ctype.h> from snprintf.c's #includes. I also removed
a few other #includes that I think are historical, though the buildfarm
may expose that as wrong.
Discussion: https://postgr.es/m/13178.1538794717@sss.pgh.pa.us
2018-10-08 18:19:20 +02:00
|
|
|
*
|
|
|
|
* We handle infinities and NaNs specially to ensure platform-independent
|
|
|
|
* output.
|
2015-02-02 16:00:45 +01:00
|
|
|
*/
|
|
|
|
if (precision < 0) /* cover possible overflow of "accum" */
|
|
|
|
precision = 0;
|
|
|
|
prec = Min(precision, 350);
|
1999-02-06 22:51:03 +01:00
|
|
|
|
Improve snprintf.c's handling of NaN, Infinity, and minus zero.
Up to now, float4out/float8out handled NaN and Infinity cases explicitly,
and invoked psprintf only for ordinary float values. This was done because
platform implementations of snprintf produce varying representations of
these special cases. But now that we use snprintf.c always, it's better
to give it the responsibility to produce a uniform representation of
these cases, so that we have uniformity across the board not only in
float4out/float8out. Hence, move that work into fmtfloat().
Also, teach fmtfloat() to recognize IEEE minus zero and handle it
correctly. The previous coding worked only accidentally, and would
fail for e.g. "%+f" format (it'd print "+-0.00000"). Now that we're
using snprintf.c everywhere, it's not acceptable for it to do weird
things in corner cases. (This incidentally avoids a portability
problem we've seen on some really ancient platforms, that native
sprintf does the wrong thing with minus zero.)
Also, introduce a new entry point in snprintf.c to allow float[48]out
to bypass the work of interpreting a well-known format spec, as well
as bypassing the overhead of the psprintf layer. I modeled this API
loosely on strfromd(). In my testing, this brings float[48]out back
to approximately the same speed they had when using native snprintf,
fixing one of the main performance issues caused by using snprintf.c.
(There is some talk of more aggressive work to improve the speed of
floating-point output conversion, but these changes seem to provide
a better starting point for such work anyway.)
Getting rid of the previous ad-hoc hack for Infinity/NaN in fmtfloat()
allows removing <ctype.h> from snprintf.c's #includes. I also removed
a few other #includes that I think are historical, though the buildfarm
may expose that as wrong.
Discussion: https://postgr.es/m/13178.1538794717@sss.pgh.pa.us
2018-10-08 18:19:20 +02:00
|
|
|
if (isnan(value))
|
2015-02-02 16:00:45 +01:00
|
|
|
{
|
Improve snprintf.c's handling of NaN, Infinity, and minus zero.
Up to now, float4out/float8out handled NaN and Infinity cases explicitly,
and invoked psprintf only for ordinary float values. This was done because
platform implementations of snprintf produce varying representations of
these special cases. But now that we use snprintf.c always, it's better
to give it the responsibility to produce a uniform representation of
these cases, so that we have uniformity across the board not only in
float4out/float8out. Hence, move that work into fmtfloat().
Also, teach fmtfloat() to recognize IEEE minus zero and handle it
correctly. The previous coding worked only accidentally, and would
fail for e.g. "%+f" format (it'd print "+-0.00000"). Now that we're
using snprintf.c everywhere, it's not acceptable for it to do weird
things in corner cases. (This incidentally avoids a portability
problem we've seen on some really ancient platforms, that native
sprintf does the wrong thing with minus zero.)
Also, introduce a new entry point in snprintf.c to allow float[48]out
to bypass the work of interpreting a well-known format spec, as well
as bypassing the overhead of the psprintf layer. I modeled this API
loosely on strfromd(). In my testing, this brings float[48]out back
to approximately the same speed they had when using native snprintf,
fixing one of the main performance issues caused by using snprintf.c.
(There is some talk of more aggressive work to improve the speed of
floating-point output conversion, but these changes seem to provide
a better starting point for such work anyway.)
Getting rid of the previous ad-hoc hack for Infinity/NaN in fmtfloat()
allows removing <ctype.h> from snprintf.c's #includes. I also removed
a few other #includes that I think are historical, though the buildfarm
may expose that as wrong.
Discussion: https://postgr.es/m/13178.1538794717@sss.pgh.pa.us
2018-10-08 18:19:20 +02:00
|
|
|
strcpy(convert, "NaN");
|
|
|
|
vallen = 3;
|
|
|
|
/* no zero padding, regardless of precision spec */
|
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
2018-10-03 16:18:15 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
Improve snprintf.c's handling of NaN, Infinity, and minus zero.
Up to now, float4out/float8out handled NaN and Infinity cases explicitly,
and invoked psprintf only for ordinary float values. This was done because
platform implementations of snprintf produce varying representations of
these special cases. But now that we use snprintf.c always, it's better
to give it the responsibility to produce a uniform representation of
these cases, so that we have uniformity across the board not only in
float4out/float8out. Hence, move that work into fmtfloat().
Also, teach fmtfloat() to recognize IEEE minus zero and handle it
correctly. The previous coding worked only accidentally, and would
fail for e.g. "%+f" format (it'd print "+-0.00000"). Now that we're
using snprintf.c everywhere, it's not acceptable for it to do weird
things in corner cases. (This incidentally avoids a portability
problem we've seen on some really ancient platforms, that native
sprintf does the wrong thing with minus zero.)
Also, introduce a new entry point in snprintf.c to allow float[48]out
to bypass the work of interpreting a well-known format spec, as well
as bypassing the overhead of the psprintf layer. I modeled this API
loosely on strfromd(). In my testing, this brings float[48]out back
to approximately the same speed they had when using native snprintf,
fixing one of the main performance issues caused by using snprintf.c.
(There is some talk of more aggressive work to improve the speed of
floating-point output conversion, but these changes seem to provide
a better starting point for such work anyway.)
Getting rid of the previous ad-hoc hack for Infinity/NaN in fmtfloat()
allows removing <ctype.h> from snprintf.c's #includes. I also removed
a few other #includes that I think are historical, though the buildfarm
may expose that as wrong.
Discussion: https://postgr.es/m/13178.1538794717@sss.pgh.pa.us
2018-10-08 18:19:20 +02:00
|
|
|
/*
|
|
|
|
* Handle sign (NaNs have no sign, so we don't do this in the case
|
|
|
|
* above). "value < 0.0" will not be true for IEEE minus zero, so we
|
|
|
|
* detect that by looking for the case where value equals 0.0
|
|
|
|
* according to == but not according to memcmp.
|
|
|
|
*/
|
|
|
|
static const double dzero = 0.0;
|
|
|
|
|
|
|
|
if (adjust_sign((value < 0.0 ||
|
|
|
|
(value == 0.0 &&
|
|
|
|
memcmp(&value, &dzero, sizeof(double)) != 0)),
|
|
|
|
forcesign, &signvalue))
|
|
|
|
value = -value;
|
1999-02-06 22:51:03 +01:00
|
|
|
|
Improve snprintf.c's handling of NaN, Infinity, and minus zero.
Up to now, float4out/float8out handled NaN and Infinity cases explicitly,
and invoked psprintf only for ordinary float values. This was done because
platform implementations of snprintf produce varying representations of
these special cases. But now that we use snprintf.c always, it's better
to give it the responsibility to produce a uniform representation of
these cases, so that we have uniformity across the board not only in
float4out/float8out. Hence, move that work into fmtfloat().
Also, teach fmtfloat() to recognize IEEE minus zero and handle it
correctly. The previous coding worked only accidentally, and would
fail for e.g. "%+f" format (it'd print "+-0.00000"). Now that we're
using snprintf.c everywhere, it's not acceptable for it to do weird
things in corner cases. (This incidentally avoids a portability
problem we've seen on some really ancient platforms, that native
sprintf does the wrong thing with minus zero.)
Also, introduce a new entry point in snprintf.c to allow float[48]out
to bypass the work of interpreting a well-known format spec, as well
as bypassing the overhead of the psprintf layer. I modeled this API
loosely on strfromd(). In my testing, this brings float[48]out back
to approximately the same speed they had when using native snprintf,
fixing one of the main performance issues caused by using snprintf.c.
(There is some talk of more aggressive work to improve the speed of
floating-point output conversion, but these changes seem to provide
a better starting point for such work anyway.)
Getting rid of the previous ad-hoc hack for Infinity/NaN in fmtfloat()
allows removing <ctype.h> from snprintf.c's #includes. I also removed
a few other #includes that I think are historical, though the buildfarm
may expose that as wrong.
Discussion: https://postgr.es/m/13178.1538794717@sss.pgh.pa.us
2018-10-08 18:19:20 +02:00
|
|
|
if (isinf(value))
|
|
|
|
{
|
|
|
|
strcpy(convert, "Infinity");
|
|
|
|
vallen = 8;
|
|
|
|
/* no zero padding, regardless of precision spec */
|
|
|
|
}
|
|
|
|
else if (pointflag)
|
|
|
|
{
|
|
|
|
zeropadlen = precision - prec;
|
|
|
|
fmt[0] = '%';
|
|
|
|
fmt[1] = '.';
|
|
|
|
fmt[2] = '*';
|
|
|
|
fmt[3] = type;
|
|
|
|
fmt[4] = '\0';
|
Use libc's snprintf, not sprintf, for special cases in snprintf.c.
snprintf.c has always fallen back on libc's *printf implementation
when printing pointers (%p) and floats. When this code originated,
we were still supporting some platforms that lacked native snprintf,
so we used sprintf for that. That's not actually unsafe in our usage,
but nonetheless builds on macOS are starting to complain about sprintf
being unconditionally deprecated; and I wouldn't be surprised if other
platforms follow suit. There seems little reason to believe that any
platform supporting C99 wouldn't have standards-compliant snprintf,
so let's just use that instead to suppress such warnings.
Back-patch to v12, which is where we started to require C99. It's
also where we started to use our snprintf.c everywhere, so this
wouldn't be enough to suppress the warning in older branches anyway
--- that is, in older branches these aren't necessarily all our
usages of libc's sprintf. It is enough in v12+ because any
deprecation annotation attached to libc's sprintf won't apply to
pg_sprintf. (Whether all our usages of pg_sprintf are adequately
safe is not a matter I intend to address here, but perhaps it could
do with some review.)
Per report from Andres Freund and local testing.
Discussion: https://postgr.es/m/20221015211955.q4cwbsfkyk3c4ty3@awork3.anarazel.de
2022-10-16 17:47:44 +02:00
|
|
|
vallen = snprintf(convert, sizeof(convert), fmt, prec, value);
|
Improve snprintf.c's handling of NaN, Infinity, and minus zero.
Up to now, float4out/float8out handled NaN and Infinity cases explicitly,
and invoked psprintf only for ordinary float values. This was done because
platform implementations of snprintf produce varying representations of
these special cases. But now that we use snprintf.c always, it's better
to give it the responsibility to produce a uniform representation of
these cases, so that we have uniformity across the board not only in
float4out/float8out. Hence, move that work into fmtfloat().
Also, teach fmtfloat() to recognize IEEE minus zero and handle it
correctly. The previous coding worked only accidentally, and would
fail for e.g. "%+f" format (it'd print "+-0.00000"). Now that we're
using snprintf.c everywhere, it's not acceptable for it to do weird
things in corner cases. (This incidentally avoids a portability
problem we've seen on some really ancient platforms, that native
sprintf does the wrong thing with minus zero.)
Also, introduce a new entry point in snprintf.c to allow float[48]out
to bypass the work of interpreting a well-known format spec, as well
as bypassing the overhead of the psprintf layer. I modeled this API
loosely on strfromd(). In my testing, this brings float[48]out back
to approximately the same speed they had when using native snprintf,
fixing one of the main performance issues caused by using snprintf.c.
(There is some talk of more aggressive work to improve the speed of
floating-point output conversion, but these changes seem to provide
a better starting point for such work anyway.)
Getting rid of the previous ad-hoc hack for Infinity/NaN in fmtfloat()
allows removing <ctype.h> from snprintf.c's #includes. I also removed
a few other #includes that I think are historical, though the buildfarm
may expose that as wrong.
Discussion: https://postgr.es/m/13178.1538794717@sss.pgh.pa.us
2018-10-08 18:19:20 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fmt[0] = '%';
|
|
|
|
fmt[1] = type;
|
|
|
|
fmt[2] = '\0';
|
Use libc's snprintf, not sprintf, for special cases in snprintf.c.
snprintf.c has always fallen back on libc's *printf implementation
when printing pointers (%p) and floats. When this code originated,
we were still supporting some platforms that lacked native snprintf,
so we used sprintf for that. That's not actually unsafe in our usage,
but nonetheless builds on macOS are starting to complain about sprintf
being unconditionally deprecated; and I wouldn't be surprised if other
platforms follow suit. There seems little reason to believe that any
platform supporting C99 wouldn't have standards-compliant snprintf,
so let's just use that instead to suppress such warnings.
Back-patch to v12, which is where we started to require C99. It's
also where we started to use our snprintf.c everywhere, so this
wouldn't be enough to suppress the warning in older branches anyway
--- that is, in older branches these aren't necessarily all our
usages of libc's sprintf. It is enough in v12+ because any
deprecation annotation attached to libc's sprintf won't apply to
pg_sprintf. (Whether all our usages of pg_sprintf are adequately
safe is not a matter I intend to address here, but perhaps it could
do with some review.)
Per report from Andres Freund and local testing.
Discussion: https://postgr.es/m/20221015211955.q4cwbsfkyk3c4ty3@awork3.anarazel.de
2022-10-16 17:47:44 +02:00
|
|
|
vallen = snprintf(convert, sizeof(convert), fmt, value);
|
Improve snprintf.c's handling of NaN, Infinity, and minus zero.
Up to now, float4out/float8out handled NaN and Infinity cases explicitly,
and invoked psprintf only for ordinary float values. This was done because
platform implementations of snprintf produce varying representations of
these special cases. But now that we use snprintf.c always, it's better
to give it the responsibility to produce a uniform representation of
these cases, so that we have uniformity across the board not only in
float4out/float8out. Hence, move that work into fmtfloat().
Also, teach fmtfloat() to recognize IEEE minus zero and handle it
correctly. The previous coding worked only accidentally, and would
fail for e.g. "%+f" format (it'd print "+-0.00000"). Now that we're
using snprintf.c everywhere, it's not acceptable for it to do weird
things in corner cases. (This incidentally avoids a portability
problem we've seen on some really ancient platforms, that native
sprintf does the wrong thing with minus zero.)
Also, introduce a new entry point in snprintf.c to allow float[48]out
to bypass the work of interpreting a well-known format spec, as well
as bypassing the overhead of the psprintf layer. I modeled this API
loosely on strfromd(). In my testing, this brings float[48]out back
to approximately the same speed they had when using native snprintf,
fixing one of the main performance issues caused by using snprintf.c.
(There is some talk of more aggressive work to improve the speed of
floating-point output conversion, but these changes seem to provide
a better starting point for such work anyway.)
Getting rid of the previous ad-hoc hack for Infinity/NaN in fmtfloat()
allows removing <ctype.h> from snprintf.c's #includes. I also removed
a few other #includes that I think are historical, though the buildfarm
may expose that as wrong.
Discussion: https://postgr.es/m/13178.1538794717@sss.pgh.pa.us
2018-10-08 18:19:20 +02:00
|
|
|
}
|
|
|
|
if (vallen < 0)
|
|
|
|
goto fail;
|
2018-10-12 17:14:27 +02:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Windows, alone among our supported platforms, likes to emit
|
|
|
|
* three-digit exponent fields even when two digits would do. Hack
|
|
|
|
* such results to look like the way everyone else does it.
|
|
|
|
*/
|
|
|
|
#ifdef WIN32
|
|
|
|
if (vallen >= 6 &&
|
|
|
|
convert[vallen - 5] == 'e' &&
|
|
|
|
convert[vallen - 3] == '0')
|
|
|
|
{
|
|
|
|
convert[vallen - 3] = convert[vallen - 2];
|
|
|
|
convert[vallen - 2] = convert[vallen - 1];
|
|
|
|
vallen--;
|
|
|
|
}
|
|
|
|
#endif
|
Improve snprintf.c's handling of NaN, Infinity, and minus zero.
Up to now, float4out/float8out handled NaN and Infinity cases explicitly,
and invoked psprintf only for ordinary float values. This was done because
platform implementations of snprintf produce varying representations of
these special cases. But now that we use snprintf.c always, it's better
to give it the responsibility to produce a uniform representation of
these cases, so that we have uniformity across the board not only in
float4out/float8out. Hence, move that work into fmtfloat().
Also, teach fmtfloat() to recognize IEEE minus zero and handle it
correctly. The previous coding worked only accidentally, and would
fail for e.g. "%+f" format (it'd print "+-0.00000"). Now that we're
using snprintf.c everywhere, it's not acceptable for it to do weird
things in corner cases. (This incidentally avoids a portability
problem we've seen on some really ancient platforms, that native
sprintf does the wrong thing with minus zero.)
Also, introduce a new entry point in snprintf.c to allow float[48]out
to bypass the work of interpreting a well-known format spec, as well
as bypassing the overhead of the psprintf layer. I modeled this API
loosely on strfromd(). In my testing, this brings float[48]out back
to approximately the same speed they had when using native snprintf,
fixing one of the main performance issues caused by using snprintf.c.
(There is some talk of more aggressive work to improve the speed of
floating-point output conversion, but these changes seem to provide
a better starting point for such work anyway.)
Getting rid of the previous ad-hoc hack for Infinity/NaN in fmtfloat()
allows removing <ctype.h> from snprintf.c's #includes. I also removed
a few other #includes that I think are historical, though the buildfarm
may expose that as wrong.
Discussion: https://postgr.es/m/13178.1538794717@sss.pgh.pa.us
2018-10-08 18:19:20 +02:00
|
|
|
}
|
2015-02-02 16:00:45 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
padlen = compute_padlen(minlen, vallen + zeropadlen, leftjust);
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
leading_pad(zpad, signvalue, &padlen, target);
|
1999-02-06 22:51:03 +01:00
|
|
|
|
2015-02-02 16:00:45 +01:00
|
|
|
if (zeropadlen > 0)
|
|
|
|
{
|
|
|
|
/* If 'e' or 'E' format, inject zeroes before the exponent */
|
|
|
|
char *epos = strrchr(convert, 'e');
|
|
|
|
|
|
|
|
if (!epos)
|
|
|
|
epos = strrchr(convert, 'E');
|
|
|
|
if (epos)
|
|
|
|
{
|
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
2018-10-03 16:18:15 +02:00
|
|
|
/* pad before exponent */
|
2015-02-02 16:00:45 +01:00
|
|
|
dostr(convert, epos - convert, target);
|
2019-11-24 18:03:16 +01:00
|
|
|
dopr_outchmulti('0', zeropadlen, target);
|
2015-02-02 16:00:45 +01:00
|
|
|
dostr(epos, vallen - (epos - convert), target);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* no exponent, pad after the digits */
|
|
|
|
dostr(convert, vallen, target);
|
2019-11-24 18:03:16 +01:00
|
|
|
dopr_outchmulti('0', zeropadlen, target);
|
2015-02-02 16:00:45 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* no zero padding, just emit the number as-is */
|
|
|
|
dostr(convert, vallen, target);
|
|
|
|
}
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
trailing_pad(padlen, target);
|
Add error-throwing wrappers for the printf family of functions.
All known standard library implementations of these functions can fail
with ENOMEM. A caller neglecting to check for failure would experience
missing output, information exposure, or a crash. Check return values
within wrappers and code, currently just snprintf.c, that bypasses the
wrappers. The wrappers do not return after an error, so their callers
need not check. Back-patch to 9.0 (all supported versions).
Popular free software standard library implementations do take pains to
bypass malloc() in simple cases, but they risk ENOMEM for floating point
numbers, positional arguments, large field widths, and large precisions.
No specification demands such caution, so this commit regards every call
to a printf family function as a potential threat.
Injecting the wrappers implicitly is a compromise between patch scope
and design goals. I would prefer to edit each call site to name a
wrapper explicitly. libpq and the ECPG libraries would, ideally, convey
errors to the caller rather than abort(). All that would be painfully
invasive for a back-patched security fix, hence this compromise.
Security: CVE-2015-3166
2015-05-18 16:02:31 +02:00
|
|
|
return;
|
|
|
|
|
|
|
|
fail:
|
|
|
|
target->failed = true;
|
1999-02-06 22:51:03 +01:00
|
|
|
}
|
|
|
|
|
Improve snprintf.c's handling of NaN, Infinity, and minus zero.
Up to now, float4out/float8out handled NaN and Infinity cases explicitly,
and invoked psprintf only for ordinary float values. This was done because
platform implementations of snprintf produce varying representations of
these special cases. But now that we use snprintf.c always, it's better
to give it the responsibility to produce a uniform representation of
these cases, so that we have uniformity across the board not only in
float4out/float8out. Hence, move that work into fmtfloat().
Also, teach fmtfloat() to recognize IEEE minus zero and handle it
correctly. The previous coding worked only accidentally, and would
fail for e.g. "%+f" format (it'd print "+-0.00000"). Now that we're
using snprintf.c everywhere, it's not acceptable for it to do weird
things in corner cases. (This incidentally avoids a portability
problem we've seen on some really ancient platforms, that native
sprintf does the wrong thing with minus zero.)
Also, introduce a new entry point in snprintf.c to allow float[48]out
to bypass the work of interpreting a well-known format spec, as well
as bypassing the overhead of the psprintf layer. I modeled this API
loosely on strfromd(). In my testing, this brings float[48]out back
to approximately the same speed they had when using native snprintf,
fixing one of the main performance issues caused by using snprintf.c.
(There is some talk of more aggressive work to improve the speed of
floating-point output conversion, but these changes seem to provide
a better starting point for such work anyway.)
Getting rid of the previous ad-hoc hack for Infinity/NaN in fmtfloat()
allows removing <ctype.h> from snprintf.c's #includes. I also removed
a few other #includes that I think are historical, though the buildfarm
may expose that as wrong.
Discussion: https://postgr.es/m/13178.1538794717@sss.pgh.pa.us
2018-10-08 18:19:20 +02:00
|
|
|
/*
|
|
|
|
* Nonstandard entry point to print a double value efficiently.
|
|
|
|
*
|
|
|
|
* This is approximately equivalent to strfromd(), but has an API more
|
|
|
|
* adapted to what float8out() wants. The behavior is like snprintf()
|
|
|
|
* with a format of "%.ng", where n is the specified precision.
|
|
|
|
* However, the target buffer must be nonempty (i.e. count > 0), and
|
|
|
|
* the precision is silently bounded to a sane range.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
pg_strfromd(char *str, size_t count, int precision, double value)
|
|
|
|
{
|
|
|
|
PrintfTarget target;
|
|
|
|
int signvalue = 0;
|
|
|
|
int vallen;
|
|
|
|
char fmt[8];
|
|
|
|
char convert[64];
|
|
|
|
|
|
|
|
/* Set up the target like pg_snprintf, but require nonempty buffer */
|
|
|
|
Assert(count > 0);
|
|
|
|
target.bufstart = target.bufptr = str;
|
|
|
|
target.bufend = str + count - 1;
|
|
|
|
target.stream = NULL;
|
|
|
|
target.nchars = 0;
|
|
|
|
target.failed = false;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* We bound precision to a reasonable range; the combination of this and
|
|
|
|
* the knowledge that we're using "g" format without padding allows the
|
|
|
|
* convert[] buffer to be reasonably small.
|
|
|
|
*/
|
|
|
|
if (precision < 1)
|
|
|
|
precision = 1;
|
|
|
|
else if (precision > 32)
|
|
|
|
precision = 32;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* The rest is just an inlined version of the fmtfloat() logic above,
|
|
|
|
* simplified using the knowledge that no padding is wanted.
|
|
|
|
*/
|
|
|
|
if (isnan(value))
|
|
|
|
{
|
|
|
|
strcpy(convert, "NaN");
|
|
|
|
vallen = 3;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
static const double dzero = 0.0;
|
|
|
|
|
|
|
|
if (value < 0.0 ||
|
|
|
|
(value == 0.0 &&
|
|
|
|
memcmp(&value, &dzero, sizeof(double)) != 0))
|
|
|
|
{
|
|
|
|
signvalue = '-';
|
|
|
|
value = -value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isinf(value))
|
|
|
|
{
|
|
|
|
strcpy(convert, "Infinity");
|
|
|
|
vallen = 8;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fmt[0] = '%';
|
|
|
|
fmt[1] = '.';
|
|
|
|
fmt[2] = '*';
|
|
|
|
fmt[3] = 'g';
|
|
|
|
fmt[4] = '\0';
|
Use libc's snprintf, not sprintf, for special cases in snprintf.c.
snprintf.c has always fallen back on libc's *printf implementation
when printing pointers (%p) and floats. When this code originated,
we were still supporting some platforms that lacked native snprintf,
so we used sprintf for that. That's not actually unsafe in our usage,
but nonetheless builds on macOS are starting to complain about sprintf
being unconditionally deprecated; and I wouldn't be surprised if other
platforms follow suit. There seems little reason to believe that any
platform supporting C99 wouldn't have standards-compliant snprintf,
so let's just use that instead to suppress such warnings.
Back-patch to v12, which is where we started to require C99. It's
also where we started to use our snprintf.c everywhere, so this
wouldn't be enough to suppress the warning in older branches anyway
--- that is, in older branches these aren't necessarily all our
usages of libc's sprintf. It is enough in v12+ because any
deprecation annotation attached to libc's sprintf won't apply to
pg_sprintf. (Whether all our usages of pg_sprintf are adequately
safe is not a matter I intend to address here, but perhaps it could
do with some review.)
Per report from Andres Freund and local testing.
Discussion: https://postgr.es/m/20221015211955.q4cwbsfkyk3c4ty3@awork3.anarazel.de
2022-10-16 17:47:44 +02:00
|
|
|
vallen = snprintf(convert, sizeof(convert), fmt, precision, value);
|
Improve snprintf.c's handling of NaN, Infinity, and minus zero.
Up to now, float4out/float8out handled NaN and Infinity cases explicitly,
and invoked psprintf only for ordinary float values. This was done because
platform implementations of snprintf produce varying representations of
these special cases. But now that we use snprintf.c always, it's better
to give it the responsibility to produce a uniform representation of
these cases, so that we have uniformity across the board not only in
float4out/float8out. Hence, move that work into fmtfloat().
Also, teach fmtfloat() to recognize IEEE minus zero and handle it
correctly. The previous coding worked only accidentally, and would
fail for e.g. "%+f" format (it'd print "+-0.00000"). Now that we're
using snprintf.c everywhere, it's not acceptable for it to do weird
things in corner cases. (This incidentally avoids a portability
problem we've seen on some really ancient platforms, that native
sprintf does the wrong thing with minus zero.)
Also, introduce a new entry point in snprintf.c to allow float[48]out
to bypass the work of interpreting a well-known format spec, as well
as bypassing the overhead of the psprintf layer. I modeled this API
loosely on strfromd(). In my testing, this brings float[48]out back
to approximately the same speed they had when using native snprintf,
fixing one of the main performance issues caused by using snprintf.c.
(There is some talk of more aggressive work to improve the speed of
floating-point output conversion, but these changes seem to provide
a better starting point for such work anyway.)
Getting rid of the previous ad-hoc hack for Infinity/NaN in fmtfloat()
allows removing <ctype.h> from snprintf.c's #includes. I also removed
a few other #includes that I think are historical, though the buildfarm
may expose that as wrong.
Discussion: https://postgr.es/m/13178.1538794717@sss.pgh.pa.us
2018-10-08 18:19:20 +02:00
|
|
|
if (vallen < 0)
|
|
|
|
{
|
|
|
|
target.failed = true;
|
|
|
|
goto fail;
|
|
|
|
}
|
2018-10-12 17:14:27 +02:00
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
if (vallen >= 6 &&
|
|
|
|
convert[vallen - 5] == 'e' &&
|
|
|
|
convert[vallen - 3] == '0')
|
|
|
|
{
|
|
|
|
convert[vallen - 3] = convert[vallen - 2];
|
|
|
|
convert[vallen - 2] = convert[vallen - 1];
|
|
|
|
vallen--;
|
|
|
|
}
|
|
|
|
#endif
|
Improve snprintf.c's handling of NaN, Infinity, and minus zero.
Up to now, float4out/float8out handled NaN and Infinity cases explicitly,
and invoked psprintf only for ordinary float values. This was done because
platform implementations of snprintf produce varying representations of
these special cases. But now that we use snprintf.c always, it's better
to give it the responsibility to produce a uniform representation of
these cases, so that we have uniformity across the board not only in
float4out/float8out. Hence, move that work into fmtfloat().
Also, teach fmtfloat() to recognize IEEE minus zero and handle it
correctly. The previous coding worked only accidentally, and would
fail for e.g. "%+f" format (it'd print "+-0.00000"). Now that we're
using snprintf.c everywhere, it's not acceptable for it to do weird
things in corner cases. (This incidentally avoids a portability
problem we've seen on some really ancient platforms, that native
sprintf does the wrong thing with minus zero.)
Also, introduce a new entry point in snprintf.c to allow float[48]out
to bypass the work of interpreting a well-known format spec, as well
as bypassing the overhead of the psprintf layer. I modeled this API
loosely on strfromd(). In my testing, this brings float[48]out back
to approximately the same speed they had when using native snprintf,
fixing one of the main performance issues caused by using snprintf.c.
(There is some talk of more aggressive work to improve the speed of
floating-point output conversion, but these changes seem to provide
a better starting point for such work anyway.)
Getting rid of the previous ad-hoc hack for Infinity/NaN in fmtfloat()
allows removing <ctype.h> from snprintf.c's #includes. I also removed
a few other #includes that I think are historical, though the buildfarm
may expose that as wrong.
Discussion: https://postgr.es/m/13178.1538794717@sss.pgh.pa.us
2018-10-08 18:19:20 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (signvalue)
|
|
|
|
dopr_outch(signvalue, &target);
|
|
|
|
|
|
|
|
dostr(convert, vallen, &target);
|
|
|
|
|
|
|
|
fail:
|
|
|
|
*(target.bufptr) = '\0';
|
|
|
|
return target.failed ? -1 : (target.bufptr - target.bufstart
|
|
|
|
+ target.nchars);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-02-06 22:51:03 +01:00
|
|
|
static void
|
2005-12-05 03:39:38 +01:00
|
|
|
dostr(const char *str, int slen, PrintfTarget *target)
|
1998-09-04 16:34:23 +02: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
2018-10-03 16:18:15 +02:00
|
|
|
/* fast path for common case of slen == 1 */
|
|
|
|
if (slen == 1)
|
|
|
|
{
|
|
|
|
dopr_outch(*str, target);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2005-12-05 03:39:38 +01:00
|
|
|
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)
|
2018-08-15 19:21:05 +02:00
|
|
|
{
|
|
|
|
target->nchars += slen; /* no, lose the data */
|
|
|
|
return;
|
|
|
|
}
|
2005-12-05 03:39:38 +01:00
|
|
|
flushbuffer(target);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
avail = Min(avail, slen);
|
|
|
|
memmove(target->bufptr, str, avail);
|
|
|
|
target->bufptr += avail;
|
|
|
|
str += avail;
|
|
|
|
slen -= avail;
|
|
|
|
}
|
1998-09-04 16:34:23 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2005-12-05 03:39:38 +01:00
|
|
|
dopr_outch(int c, PrintfTarget *target)
|
1998-09-04 16:34:23 +02:00
|
|
|
{
|
2005-12-05 03:39:38 +01:00
|
|
|
if (target->bufend != NULL && target->bufptr >= target->bufend)
|
1998-10-07 19:12:52 +02:00
|
|
|
{
|
2005-12-05 03:39:38 +01:00
|
|
|
/* buffer full, can we dump to stream? */
|
|
|
|
if (target->stream == NULL)
|
2018-08-15 19:21:05 +02:00
|
|
|
{
|
|
|
|
target->nchars++; /* no, lose the data */
|
|
|
|
return;
|
|
|
|
}
|
2005-12-05 03:39:38 +01:00
|
|
|
flushbuffer(target);
|
1998-10-07 19:12:52 +02:00
|
|
|
}
|
2005-12-05 03:39:38 +01:00
|
|
|
*(target->bufptr++) = c;
|
1998-08-01 21:30:29 +02:00
|
|
|
}
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2005-03-17 04:18:14 +01:00
|
|
|
|
|
|
|
static int
|
|
|
|
adjust_sign(int is_negative, int forcesign, int *signvalue)
|
|
|
|
{
|
|
|
|
if (is_negative)
|
|
|
|
{
|
|
|
|
*signvalue = '-';
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else if (forcesign)
|
|
|
|
*signvalue = '+';
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
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
2018-10-03 16:18:15 +02:00
|
|
|
static int
|
|
|
|
compute_padlen(int minlen, int vallen, int leftjust)
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
int padlen;
|
|
|
|
|
|
|
|
padlen = minlen - vallen;
|
|
|
|
if (padlen < 0)
|
|
|
|
padlen = 0;
|
2005-03-17 04:18:14 +01:00
|
|
|
if (leftjust)
|
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
2018-10-03 16:18:15 +02:00
|
|
|
padlen = -padlen;
|
|
|
|
return padlen;
|
2005-03-17 04:18:14 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
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
2018-10-03 16:18:15 +02:00
|
|
|
leading_pad(int zpad, int signvalue, int *padlen, PrintfTarget *target)
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
int maxpad;
|
|
|
|
|
2005-03-17 04:18:14 +01:00
|
|
|
if (*padlen > 0 && zpad)
|
|
|
|
{
|
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
2018-10-03 16:18:15 +02:00
|
|
|
if (signvalue)
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
dopr_outch(signvalue, target);
|
2005-12-05 03:39:38 +01:00
|
|
|
--(*padlen);
|
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
2018-10-03 16:18:15 +02:00
|
|
|
signvalue = 0;
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
if (*padlen > 0)
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
dopr_outchmulti(zpad, *padlen, target);
|
|
|
|
*padlen = 0;
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
maxpad = (signvalue != 0);
|
|
|
|
if (*padlen > maxpad)
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
dopr_outchmulti(' ', *padlen - maxpad, target);
|
|
|
|
*padlen = maxpad;
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
if (signvalue)
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
dopr_outch(signvalue, target);
|
2005-03-17 04:18:14 +01:00
|
|
|
if (*padlen > 0)
|
2005-12-05 03:39:38 +01:00
|
|
|
--(*padlen);
|
|
|
|
else if (*padlen < 0)
|
|
|
|
++(*padlen);
|
2005-03-17 04:18:14 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static void
|
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
2018-10-03 16:18:15 +02:00
|
|
|
trailing_pad(int padlen, PrintfTarget *target)
|
2005-03-17 04:18:14 +01: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
2018-10-03 16:18:15 +02:00
|
|
|
if (padlen < 0)
|
|
|
|
dopr_outchmulti(' ', -padlen, target);
|
2005-03-17 04:18:14 +01:00
|
|
|
}
|