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