]> granicus.if.org Git - postgresql/blob - src/backend/lib/stringinfo.c
Update copyrights for 2013
[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-2013, PostgreSQL Global Development Group
10  * Portions Copyright (c) 1994, Regents of the University of California
11  *
12  *        src/backend/lib/stringinfo.c
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 = 1024;    /* initial default buffer size */
49
50         str->data = (char *) palloc(size);
51         str->maxlen = size;
52         resetStringInfo(str);
53 }
54
55 /*
56  * resetStringInfo
57  *
58  * Reset the StringInfo: the data buffer remains valid, but its
59  * previous content, if any, is cleared.
60  */
61 void
62 resetStringInfo(StringInfo str)
63 {
64         str->data[0] = '\0';
65         str->len = 0;
66         str->cursor = 0;
67 }
68
69 /*
70  * appendStringInfo
71  *
72  * Format text data under the control of fmt (an sprintf-style format string)
73  * and append it to whatever is already in str.  More space is allocated
74  * to str if necessary.  This is sort of like a combination of sprintf and
75  * strcat.
76  */
77 void
78 appendStringInfo(StringInfo str, const char *fmt,...)
79 {
80         for (;;)
81         {
82                 va_list         args;
83                 bool            success;
84
85                 /* Try to format the data. */
86                 va_start(args, fmt);
87                 success = appendStringInfoVA(str, fmt, args);
88                 va_end(args);
89
90                 if (success)
91                         break;
92
93                 /* Double the buffer size and try again. */
94                 enlargeStringInfo(str, str->maxlen);
95         }
96 }
97
98 /*
99  * appendStringInfoVA
100  *
101  * Attempt to format text data under the control of fmt (an sprintf-style
102  * format string) and append it to whatever is already in str.  If successful
103  * return true; if not (because there's not enough space), return false
104  * without modifying str.  Typically the caller would enlarge str and retry
105  * on false return --- see appendStringInfo for standard usage pattern.
106  *
107  * XXX This API is ugly, but there seems no alternative given the C spec's
108  * restrictions on what can portably be done with va_list arguments: you have
109  * to redo va_start before you can rescan the argument list, and we can't do
110  * that from here.
111  */
112 bool
113 appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
114 {
115         int                     avail,
116                                 nprinted;
117
118         Assert(str != NULL);
119
120         /*
121          * If there's hardly any space, don't bother trying, just fail to make the
122          * caller enlarge the buffer first.
123          */
124         avail = str->maxlen - str->len - 1;
125         if (avail < 16)
126                 return false;
127
128         /*
129          * Assert check here is to catch buggy vsnprintf that overruns the
130          * specified buffer length.  Solaris 7 in 64-bit mode is an example of a
131          * platform with such a bug.
132          */
133 #ifdef USE_ASSERT_CHECKING
134         str->data[str->maxlen - 1] = '\0';
135 #endif
136
137         nprinted = vsnprintf(str->data + str->len, avail, fmt, args);
138
139         Assert(str->data[str->maxlen - 1] == '\0');
140
141         /*
142          * Note: some versions of vsnprintf return the number of chars actually
143          * stored, but at least one returns -1 on failure. Be conservative about
144          * believing whether the print worked.
145          */
146         if (nprinted >= 0 && nprinted < avail - 1)
147         {
148                 /* Success.  Note nprinted does not include trailing null. */
149                 str->len += nprinted;
150                 return true;
151         }
152
153         /* Restore the trailing null so that str is unmodified. */
154         str->data[str->len] = '\0';
155         return false;
156 }
157
158 /*
159  * appendStringInfoString
160  *
161  * Append a null-terminated string to str.
162  * Like appendStringInfo(str, "%s", s) but faster.
163  */
164 void
165 appendStringInfoString(StringInfo str, const char *s)
166 {
167         appendBinaryStringInfo(str, s, strlen(s));
168 }
169
170 /*
171  * appendStringInfoChar
172  *
173  * Append a single byte to str.
174  * Like appendStringInfo(str, "%c", ch) but much faster.
175  */
176 void
177 appendStringInfoChar(StringInfo str, char ch)
178 {
179         /* Make more room if needed */
180         if (str->len + 1 >= str->maxlen)
181                 enlargeStringInfo(str, 1);
182
183         /* OK, append the character */
184         str->data[str->len] = ch;
185         str->len++;
186         str->data[str->len] = '\0';
187 }
188
189 /*
190  * appendStringInfoSpaces
191  *
192  * Append the specified number of spaces to a buffer.
193  */
194 void
195 appendStringInfoSpaces(StringInfo str, int count)
196 {
197         if (count > 0)
198         {
199                 /* Make more room if needed */
200                 enlargeStringInfo(str, count);
201
202                 /* OK, append the spaces */
203                 while (--count >= 0)
204                         str->data[str->len++] = ' ';
205                 str->data[str->len] = '\0';
206         }
207 }
208
209 /*
210  * appendBinaryStringInfo
211  *
212  * Append arbitrary binary data to a StringInfo, allocating more space
213  * if necessary.
214  */
215 void
216 appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
217 {
218         Assert(str != NULL);
219
220         /* Make more room if needed */
221         enlargeStringInfo(str, datalen);
222
223         /* OK, append the data */
224         memcpy(str->data + str->len, data, datalen);
225         str->len += datalen;
226
227         /*
228          * Keep a trailing null in place, even though it's probably useless for
229          * binary data.  (Some callers are dealing with text but call this because
230          * their input isn't null-terminated.)
231          */
232         str->data[str->len] = '\0';
233 }
234
235 /*
236  * enlargeStringInfo
237  *
238  * Make sure there is enough space for 'needed' more bytes
239  * ('needed' does not include the terminating null).
240  *
241  * External callers usually need not concern themselves with this, since
242  * all stringinfo.c routines do it automatically.  However, if a caller
243  * knows that a StringInfo will eventually become X bytes large, it
244  * can save some palloc overhead by enlarging the buffer before starting
245  * to store data in it.
246  *
247  * NB: because we use repalloc() to enlarge the buffer, the string buffer
248  * will remain allocated in the same memory context that was current when
249  * initStringInfo was called, even if another context is now current.
250  * This is the desired and indeed critical behavior!
251  */
252 void
253 enlargeStringInfo(StringInfo str, int needed)
254 {
255         int                     newlen;
256
257         /*
258          * Guard against out-of-range "needed" values.  Without this, we can get
259          * an overflow or infinite loop in the following.
260          */
261         if (needed < 0)                         /* should not happen */
262                 elog(ERROR, "invalid string enlargement request size: %d", needed);
263         if (((Size) needed) >= (MaxAllocSize - (Size) str->len))
264                 ereport(ERROR,
265                                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
266                                  errmsg("out of memory"),
267                                  errdetail("Cannot enlarge string buffer containing %d bytes by %d more bytes.",
268                                                    str->len, needed)));
269
270         needed += str->len + 1;         /* total space required now */
271
272         /* Because of the above test, we now have needed <= MaxAllocSize */
273
274         if (needed <= str->maxlen)
275                 return;                                 /* got enough space already */
276
277         /*
278          * We don't want to allocate just a little more space with each append;
279          * for efficiency, double the buffer size each time it overflows.
280          * Actually, we might need to more than double it if 'needed' is big...
281          */
282         newlen = 2 * str->maxlen;
283         while (needed > newlen)
284                 newlen = 2 * newlen;
285
286         /*
287          * Clamp to MaxAllocSize in case we went past it.  Note we are assuming
288          * here that MaxAllocSize <= INT_MAX/2, else the above loop could
289          * overflow.  We will still have newlen >= needed.
290          */
291         if (newlen > (int) MaxAllocSize)
292                 newlen = (int) MaxAllocSize;
293
294         str->data = (char *) repalloc(str->data, newlen);
295
296         str->maxlen = newlen;
297 }