Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit805889d

Browse files
committed
Make snprintf.c follow the C99 standard for snprintf's result value.
C99 says that the result should be the number of bytes that would havebeen emitted given a large enough buffer, not the number we actuallywere able to put in the buffer. It's time to make our substituteimplementation comply with that. Not doing so results in inefficiencyin buffer-enlargement cases, and also poses a portability hazard forthird-party code that might expect C99-compliant snprintf behaviorwithin Postgres.In passing, remove useless tests for str == NULL; neither C99 norpredecessor standards ever allowed that except when count == 0,so I see no reason to expend cycles on making that a non-crash casefor this implementation. Also, don't waste a byte in pg_vfprintf'slocal I/O buffer; this might have performance benefits by allowingaligned writes during flushbuffer calls.Discussion:https://postgr.es/m/17245.1534289329@sss.pgh.pa.us
1 parent777e6dd commit805889d

File tree

1 file changed

+63
-31
lines changed

1 file changed

+63
-31
lines changed

‎src/port/snprintf.c

Lines changed: 63 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
* Copyright (c) 1983, 1995, 1996 Eric P. Allman
33
* Copyright (c) 1988, 1993
44
*The Regents of the University of California. All rights reserved.
5+
* Portions Copyright (c) 1996-2018, PostgreSQL Global Development Group
56
*
67
* Redistribution and use in source and binary forms, with or without
78
* modification, are permitted provided that the following conditions
@@ -49,8 +50,8 @@
4950
*SNPRINTF, VSNPRINTF and friends
5051
*
5152
* These versions have been grabbed off the net. They have been
52-
* cleaned up to compile properly and support for most of theSingle Unix
53-
*Specification has been added. Remaining unimplemented features are:
53+
* cleaned up to compile properly and support for most of theC99
54+
*specification has been added. Remaining unimplemented features are:
5455
*
5556
* 1. No locale support: the radix character is always '.' and the '
5657
* (single quote) format flag is ignored.
@@ -64,25 +65,24 @@
6465
* 5. Space and '#' flags are not implemented.
6566
*
6667
*
67-
*Theresult values ofthese functions are not the sameacrossdifferent
68-
*platforms.This implementationis compatible withtheSingle Unix Spec:
68+
*Historically theresult values ofsprintf/snprintf variedacrossplatforms.
69+
* This implementationnow followstheC99 standard:
6970
*
70-
* 1. -1 is returned only if processing is abandoned due to an invalid
71-
* parameter, such as incorrect format string. (Although not required by
72-
* the spec, this happens only when no characters have yet been transmitted
73-
* to the destination.)
71+
* 1. -1 is returned if an error is detected in the format string, or if
72+
* a write to the target stream fails (as reported by fwrite). Note that
73+
* overrunning snprintf's target buffer is *not* an error.
7474
*
75-
* 2. Forsnprintf and sprintf, 0 is returned if str == NULL or count == 0;
76-
*no data has been stored.
75+
* 2. Forsuccessful writes to streams, the actual number of bytes written
76+
*to the stream is returned.
7777
*
78-
* 3. Otherwise, the number of bytes actually transmitted to the destination
79-
* is returned (excluding the trailing '\0' for snprintf and sprintf).
78+
* 3. For successful sprintf/snprintf, the number of bytes that would have
79+
* been written to an infinite-size buffer (excluding the trailing '\0')
80+
* is returned. snprintf will truncate its output to fit in the buffer
81+
* (ensuring a trailing '\0' unless count == 0), but this is not reflected
82+
* in the function result.
8083
*
81-
* For snprintf with nonzero count, the result cannot be more than count-1
82-
* (a trailing '\0' is always stored); it is not possible to distinguish
83-
* buffer overrun from exact fit. This is unlike some implementations that
84-
* return the number of bytes that would have been needed for the complete
85-
* result string.
84+
* snprintf buffer overrun can be detected by checking for function result
85+
* greater than or equal to the supplied count.
8686
*/
8787

8888
/**************************************************************
@@ -101,15 +101,27 @@
101101
#undeffprintf
102102
#undefprintf
103103

104-
/* Info about where the formatted output is going */
104+
/*
105+
* Info about where the formatted output is going.
106+
*
107+
* dopr and subroutines will not write at/past bufend, but snprintf
108+
* reserves one byte, ensuring it may place the trailing '\0' there.
109+
*
110+
* In snprintf, we use nchars to count the number of bytes dropped on the
111+
* floor due to buffer overrun. The correct result of snprintf is thus
112+
* (bufptr - bufstart) + nchars. (This isn't as inconsistent as it might
113+
* seem: nchars is the number of emitted bytes that are not in the buffer now,
114+
* either because we sent them to the stream or because we couldn't fit them
115+
* into the buffer to begin with.)
116+
*/
105117
typedefstruct
106118
{
107119
char*bufptr;/* next buffer output position */
108120
char*bufstart;/* first buffer element */
109-
char*bufend;/* last buffer element, or NULL */
121+
char*bufend;/* last+1 buffer element, or NULL */
110122
/* bufend == NULL is for sprintf, where we assume buf is big enough */
111123
FILE*stream;/* eventual output destination, or NULL */
112-
intnchars;/* # charsalreadysent to stream */
124+
intnchars;/* # chars sent to stream, or dropped */
113125
boolfailed;/* call is a failure; errno is set */
114126
}PrintfTarget;
115127

@@ -147,17 +159,28 @@ int
147159
pg_vsnprintf(char*str,size_tcount,constchar*fmt,va_listargs)
148160
{
149161
PrintfTargettarget;
162+
charonebyte[1];
150163

151-
if (str==NULL||count==0)
152-
return0;
164+
/*
165+
* C99 allows the case str == NULL when count == 0. Rather than
166+
* special-casing this situation further down, we substitute a one-byte
167+
* local buffer. Callers cannot tell, since the function result doesn't
168+
* depend on count.
169+
*/
170+
if (count==0)
171+
{
172+
str=onebyte;
173+
count=1;
174+
}
153175
target.bufstart=target.bufptr=str;
154176
target.bufend=str+count-1;
155177
target.stream=NULL;
156-
/*target.ncharsis unused in this case */
178+
target.nchars=0;
157179
target.failed= false;
158180
dopr(&target,fmt,args);
159181
*(target.bufptr)='\0';
160-
returntarget.failed ?-1 : (target.bufptr-target.bufstart);
182+
returntarget.failed ?-1 : (target.bufptr-target.bufstart
183+
+target.nchars);
161184
}
162185

163186
int
@@ -177,16 +200,15 @@ pg_vsprintf(char *str, const char *fmt, va_list args)
177200
{
178201
PrintfTargettarget;
179202

180-
if (str==NULL)
181-
return0;
182203
target.bufstart=target.bufptr=str;
183204
target.bufend=NULL;
184205
target.stream=NULL;
185-
/*target.ncharsis unused in this case */
206+
target.nchars=0;/* not really used in this case */
186207
target.failed= false;
187208
dopr(&target,fmt,args);
188209
*(target.bufptr)='\0';
189-
returntarget.failed ?-1 : (target.bufptr-target.bufstart);
210+
returntarget.failed ?-1 : (target.bufptr-target.bufstart
211+
+target.nchars);
190212
}
191213

192214
int
@@ -213,7 +235,7 @@ pg_vfprintf(FILE *stream, const char *fmt, va_list args)
213235
return-1;
214236
}
215237
target.bufstart=target.bufptr=buffer;
216-
target.bufend=buffer+sizeof(buffer)-1;
238+
target.bufend=buffer+sizeof(buffer);/* use the whole buffer */
217239
target.stream=stream;
218240
target.nchars=0;
219241
target.failed= false;
@@ -256,6 +278,10 @@ flushbuffer(PrintfTarget *target)
256278
{
257279
size_tnc=target->bufptr-target->bufstart;
258280

281+
/*
282+
* Don't write anything if we already failed; this is to ensure we
283+
* preserve the original failure's errno.
284+
*/
259285
if (!target->failed&&nc>0)
260286
{
261287
size_twritten;
@@ -1035,7 +1061,10 @@ dostr(const char *str, int slen, PrintfTarget *target)
10351061
{
10361062
/* buffer full, can we dump to stream? */
10371063
if (target->stream==NULL)
1038-
return;/* no, lose the data */
1064+
{
1065+
target->nchars+=slen;/* no, lose the data */
1066+
return;
1067+
}
10391068
flushbuffer(target);
10401069
continue;
10411070
}
@@ -1054,7 +1083,10 @@ dopr_outch(int c, PrintfTarget *target)
10541083
{
10551084
/* buffer full, can we dump to stream? */
10561085
if (target->stream==NULL)
1057-
return;/* no, lose the data */
1086+
{
1087+
target->nchars++;/* no, lose the data */
1088+
return;
1089+
}
10581090
flushbuffer(target);
10591091
}
10601092
*(target->bufptr++)=c;

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp