]> granicus.if.org Git - postgresql/blob - src/backend/lib/stringinfo.c
Fix recently-understood problems with handling of XID freezing, particularly
[postgresql] / src / backend / lib / stringinfo.c
1 /*-------------------------------------------------------------------------
2  *
3  * stringinfo.c
4  *
5  * StringInfo provides an indefinitely-extensible string data type.
6  * It can be used to buffer either ordinary C strings (null-terminated text)
7  * or arbitrary binary data.  All storage is allocated with palloc().
8  *
9  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
10  * Portions Copyright (c) 1994, Regents of the University of California
11  *
12  *        $PostgreSQL: pgsql/src/backend/lib/stringinfo.c,v 1.43 2006/03/05 15:58:27 momjian Exp $
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres.h"
17
18 #include "lib/stringinfo.h"
19 #include "utils/memutils.h"
20
21
22 /*
23  * makeStringInfo
24  *
25  * Create an empty 'StringInfoData' & return a pointer to it.
26  */
27 StringInfo
28 makeStringInfo(void)
29 {
30         StringInfo      res;
31
32         res = (StringInfo) palloc(sizeof(StringInfoData));
33
34         initStringInfo(res);
35
36         return res;
37 }
38
39 /*
40  * initStringInfo
41  *
42  * Initialize a StringInfoData struct (with previously undefined contents)
43  * to describe an empty string.
44  */
45 void
46 initStringInfo(StringInfo str)
47 {
48         int                     size = 256;             /* initial default buffer size */
49
50         str->data = (char *) palloc(size);
51         str->maxlen = size;
52         str->len = 0;
53         str->data[0] = '\0';
54         str->cursor = 0;
55 }
56
57 /*
58  * appendStringInfo
59  *
60  * Format text data under the control of fmt (an sprintf-style format string)
61  * and append it to whatever is already in str.  More space is allocated
62  * to str if necessary.  This is sort of like a combination of sprintf and
63  * strcat.
64  */
65 void
66 appendStringInfo(StringInfo str, const char *fmt,...)
67 {
68         for (;;)
69         {
70                 va_list         args;
71                 bool            success;
72
73                 /* Try to format the data. */
74                 va_start(args, fmt);
75                 success = appendStringInfoVA(str, fmt, args);
76                 va_end(args);
77
78                 if (success)
79                         break;
80
81                 /* Double the buffer size and try again. */
82                 enlargeStringInfo(str, str->maxlen);
83         }
84 }
85
86 /*
87  * appendStringInfoVA
88  *
89  * Attempt to format text data under the control of fmt (an sprintf-style
90  * format string) and append it to whatever is already in str.  If successful
91  * return true; if not (because there's not enough space), return false
92  * without modifying str.  Typically the caller would enlarge str and retry
93  * on false return --- see appendStringInfo for standard usage pattern.
94  *
95  * XXX This API is ugly, but there seems no alternative given the C spec's
96  * restrictions on what can portably be done with va_list arguments: you have
97  * to redo va_start before you can rescan the argument list, and we can't do
98  * that from here.
99  */
100 bool
101 appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
102 {
103         int                     avail,
104                                 nprinted;
105
106         Assert(str != NULL);
107
108         /*
109          * If there's hardly any space, don't bother trying, just fail to make the
110          * caller enlarge the buffer first.
111          */
112         avail = str->maxlen - str->len - 1;
113         if (avail < 16)
114                 return false;
115
116         /*
117          * Assert check here is to catch buggy vsnprintf that overruns the
118          * specified buffer length.  Solaris 7 in 64-bit mode is an example of a
119          * platform with such a bug.
120          */
121 #ifdef USE_ASSERT_CHECKING
122         str->data[str->maxlen - 1] = '\0';
123 #endif
124
125         nprinted = vsnprintf(str->data + str->len, avail, fmt, args);
126
127         Assert(str->data[str->maxlen - 1] == '\0');
128
129         /*
130          * Note: some versions of vsnprintf return the number of chars actually
131          * stored, but at least one returns -1 on failure. Be conservative about
132          * believing whether the print worked.
133          */
134         if (nprinted >= 0 && nprinted < avail - 1)
135         {
136                 /* Success.  Note nprinted does not include trailing null. */
137                 str->len += nprinted;
138                 return true;
139         }
140
141         /* Restore the trailing null so that str is unmodified. */
142         str->data[str->len] = '\0';
143         return false;
144 }
145
146 /*
147  * appendStringInfoString
148  *
149  * Append a null-terminated string to str.
150  * Like appendStringInfo(str, "%s", s) but faster.
151  */
152 void
153 appendStringInfoString(StringInfo str, const char *s)
154 {
155         appendBinaryStringInfo(str, s, strlen(s));
156 }
157
158 /*
159  * appendStringInfoChar
160  *
161  * Append a single byte to str.
162  * Like appendStringInfo(str, "%c", ch) but much faster.
163  */
164 void
165 appendStringInfoChar(StringInfo str, char ch)
166 {
167         /* Make more room if needed */
168         if (str->len + 1 >= str->maxlen)
169                 enlargeStringInfo(str, 1);
170
171         /* OK, append the character */
172         str->data[str->len] = ch;
173         str->len++;
174         str->data[str->len] = '\0';
175 }
176
177 /*
178  * appendBinaryStringInfo
179  *
180  * Append arbitrary binary data to a StringInfo, allocating more space
181  * if necessary.
182  */
183 void
184 appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
185 {
186         Assert(str != NULL);
187
188         /* Make more room if needed */
189         enlargeStringInfo(str, datalen);
190
191         /* OK, append the data */
192         memcpy(str->data + str->len, data, datalen);
193         str->len += datalen;
194
195         /*
196          * Keep a trailing null in place, even though it's probably useless for
197          * binary data...
198          */
199         str->data[str->len] = '\0';
200 }
201
202 /*
203  * enlargeStringInfo
204  *
205  * Make sure there is enough space for 'needed' more bytes
206  * ('needed' does not include the terminating null).
207  *
208  * External callers usually need not concern themselves with this, since
209  * all stringinfo.c routines do it automatically.  However, if a caller
210  * knows that a StringInfo will eventually become X bytes large, it
211  * can save some palloc overhead by enlarging the buffer before starting
212  * to store data in it.
213  *
214  * NB: because we use repalloc() to enlarge the buffer, the string buffer
215  * will remain allocated in the same memory context that was current when
216  * initStringInfo was called, even if another context is now current.
217  * This is the desired and indeed critical behavior!
218  */
219 void
220 enlargeStringInfo(StringInfo str, int needed)
221 {
222         int                     newlen;
223
224         /*
225          * Guard against ridiculous "needed" values, which can occur if we're fed
226          * bogus data.  Without this, we can get an overflow or infinite loop in
227          * the following.
228          */
229         if (needed < 0 ||
230                 ((Size) needed) >= (MaxAllocSize - (Size) str->len))
231                 elog(ERROR, "invalid string enlargement request size %d",
232                          needed);
233
234         needed += str->len + 1;         /* total space required now */
235
236         /* Because of the above test, we now have needed <= MaxAllocSize */
237
238         if (needed <= str->maxlen)
239                 return;                                 /* got enough space already */
240
241         /*
242          * We don't want to allocate just a little more space with each append;
243          * for efficiency, double the buffer size each time it overflows.
244          * Actually, we might need to more than double it if 'needed' is big...
245          */
246         newlen = 2 * str->maxlen;
247         while (needed > newlen)
248                 newlen = 2 * newlen;
249
250         /*
251          * Clamp to MaxAllocSize in case we went past it.  Note we are assuming
252          * here that MaxAllocSize <= INT_MAX/2, else the above loop could
253          * overflow.  We will still have newlen >= needed.
254          */
255         if (newlen > (int) MaxAllocSize)
256                 newlen = (int) MaxAllocSize;
257
258         str->data = (char *) repalloc(str->data, newlen);
259
260         str->maxlen = newlen;
261 }