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