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-2016, 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
makeStringInfo(void)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
initStringInfo(StringInfo str)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
resetStringInfo(StringInfo str)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
appendStringInfo(StringInfo str,const char * fmt,...)78 appendStringInfo(StringInfo str, const char *fmt,...)
79 {
80 for (;;)
81 {
82 va_list args;
83 int needed;
84
85 /* Try to format the data. */
86 va_start(args, fmt);
87 needed = appendStringInfoVA(str, fmt, args);
88 va_end(args);
89
90 if (needed == 0)
91 break; /* success */
92
93 /* Increase the buffer size and try again. */
94 enlargeStringInfo(str, needed);
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 zero; if not (because there's not enough space), return an estimate
104 * of the space needed, without modifying str. Typically the caller should
105 * pass the return value to enlargeStringInfo() before trying again; see
106 * appendStringInfo for standard usage pattern.
107 *
108 * XXX This API is ugly, but there seems no alternative given the C spec's
109 * restrictions on what can portably be done with va_list arguments: you have
110 * to redo va_start before you can rescan the argument list, and we can't do
111 * that from here.
112 */
113 int
appendStringInfoVA(StringInfo str,const char * fmt,va_list args)114 appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
115 {
116 int avail;
117 size_t nprinted;
118
119 Assert(str != NULL);
120
121 /*
122 * If there's hardly any space, don't bother trying, just fail to make the
123 * caller enlarge the buffer first. We have to guess at how much to
124 * enlarge, since we're skipping the formatting work.
125 */
126 avail = str->maxlen - str->len;
127 if (avail < 16)
128 return 32;
129
130 nprinted = pvsnprintf(str->data + str->len, (size_t) avail, fmt, args);
131
132 if (nprinted < (size_t) avail)
133 {
134 /* Success. Note nprinted does not include trailing null. */
135 str->len += (int) nprinted;
136 return 0;
137 }
138
139 /* Restore the trailing null so that str is unmodified. */
140 str->data[str->len] = '\0';
141
142 /*
143 * Return pvsnprintf's estimate of the space needed. (Although this is
144 * given as a size_t, we know it will fit in int because it's not more
145 * than MaxAllocSize.)
146 */
147 return (int) nprinted;
148 }
149
150 /*
151 * appendStringInfoString
152 *
153 * Append a null-terminated string to str.
154 * Like appendStringInfo(str, "%s", s) but faster.
155 */
156 void
appendStringInfoString(StringInfo str,const char * s)157 appendStringInfoString(StringInfo str, const char *s)
158 {
159 appendBinaryStringInfo(str, s, strlen(s));
160 }
161
162 /*
163 * appendStringInfoChar
164 *
165 * Append a single byte to str.
166 * Like appendStringInfo(str, "%c", ch) but much faster.
167 */
168 void
appendStringInfoChar(StringInfo str,char ch)169 appendStringInfoChar(StringInfo str, char ch)
170 {
171 /* Make more room if needed */
172 if (str->len + 1 >= str->maxlen)
173 enlargeStringInfo(str, 1);
174
175 /* OK, append the character */
176 str->data[str->len] = ch;
177 str->len++;
178 str->data[str->len] = '\0';
179 }
180
181 /*
182 * appendStringInfoSpaces
183 *
184 * Append the specified number of spaces to a buffer.
185 */
186 void
appendStringInfoSpaces(StringInfo str,int count)187 appendStringInfoSpaces(StringInfo str, int count)
188 {
189 if (count > 0)
190 {
191 /* Make more room if needed */
192 enlargeStringInfo(str, count);
193
194 /* OK, append the spaces */
195 while (--count >= 0)
196 str->data[str->len++] = ' ';
197 str->data[str->len] = '\0';
198 }
199 }
200
201 /*
202 * appendBinaryStringInfo
203 *
204 * Append arbitrary binary data to a StringInfo, allocating more space
205 * if necessary.
206 */
207 void
appendBinaryStringInfo(StringInfo str,const char * data,int datalen)208 appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
209 {
210 Assert(str != NULL);
211
212 /* Make more room if needed */
213 enlargeStringInfo(str, datalen);
214
215 /* OK, append the data */
216 memcpy(str->data + str->len, data, datalen);
217 str->len += datalen;
218
219 /*
220 * Keep a trailing null in place, even though it's probably useless for
221 * binary data. (Some callers are dealing with text but call this because
222 * their input isn't null-terminated.)
223 */
224 str->data[str->len] = '\0';
225 }
226
227 /*
228 * enlargeStringInfo
229 *
230 * Make sure there is enough space for 'needed' more bytes
231 * ('needed' does not include the terminating null).
232 *
233 * External callers usually need not concern themselves with this, since
234 * all stringinfo.c routines do it automatically. However, if a caller
235 * knows that a StringInfo will eventually become X bytes large, it
236 * can save some palloc overhead by enlarging the buffer before starting
237 * to store data in it.
238 *
239 * NB: because we use repalloc() to enlarge the buffer, the string buffer
240 * will remain allocated in the same memory context that was current when
241 * initStringInfo was called, even if another context is now current.
242 * This is the desired and indeed critical behavior!
243 */
244 void
enlargeStringInfo(StringInfo str,int needed)245 enlargeStringInfo(StringInfo str, int needed)
246 {
247 int newlen;
248
249 /*
250 * Guard against out-of-range "needed" values. Without this, we can get
251 * an overflow or infinite loop in the following.
252 */
253 if (needed < 0) /* should not happen */
254 elog(ERROR, "invalid string enlargement request size: %d", needed);
255 if (((Size) needed) >= (MaxAllocSize - (Size) str->len))
256 ereport(ERROR,
257 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
258 errmsg("out of memory"),
259 errdetail("Cannot enlarge string buffer containing %d bytes by %d more bytes.",
260 str->len, needed)));
261
262 needed += str->len + 1; /* total space required now */
263
264 /* Because of the above test, we now have needed <= MaxAllocSize */
265
266 if (needed <= str->maxlen)
267 return; /* got enough space already */
268
269 /*
270 * We don't want to allocate just a little more space with each append;
271 * for efficiency, double the buffer size each time it overflows.
272 * Actually, we might need to more than double it if 'needed' is big...
273 */
274 newlen = 2 * str->maxlen;
275 while (needed > newlen)
276 newlen = 2 * newlen;
277
278 /*
279 * Clamp to MaxAllocSize in case we went past it. Note we are assuming
280 * here that MaxAllocSize <= INT_MAX/2, else the above loop could
281 * overflow. We will still have newlen >= needed.
282 */
283 if (newlen > (int) MaxAllocSize)
284 newlen = (int) MaxAllocSize;
285
286 str->data = (char *) repalloc(str->data, newlen);
287
288 str->maxlen = newlen;
289 }
290