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