Age Owner Branch data TLA Line data Source code
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-2025, 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 : : #endif
28 : :
29 : : #include "lib/stringinfo.h"
30 : :
31 : :
32 : : /*
33 : : * initStringInfoInternal
34 : : *
35 : : * Initialize a StringInfoData struct (with previously undefined contents)
36 : : * to describe an empty string.
37 : : * The initial memory allocation size is specified by 'initsize'.
38 : : * The valid range for 'initsize' is 1 to MaxAllocSize.
39 : : */
40 : : static inline void
238 ishii@postgresql.org 41 :CBC 4345285 : initStringInfoInternal(StringInfo str, int initsize)
42 : : {
43 [ + - + + ]: 4345285 : Assert(initsize >= 1 && initsize <= MaxAllocSize);
44 : :
45 : 4345285 : str->data = (char *) palloc(initsize);
46 : 4345285 : str->maxlen = initsize;
47 : 4345285 : resetStringInfo(str);
48 : 4345285 : }
49 : :
50 : : /*
51 : : * makeStringInfoInternal(int initsize)
52 : : *
53 : : * Create an empty 'StringInfoData' & return a pointer to it.
54 : : * The initial memory allocation size is specified by 'initsize'.
55 : : * The valid range for 'initsize' is 1 to MaxAllocSize.
56 : : */
57 : : static inline StringInfo
58 : 53898 : makeStringInfoInternal(int initsize)
59 : : {
60 : 53898 : StringInfo res = (StringInfo) palloc(sizeof(StringInfoData));
61 : :
62 : 53898 : initStringInfoInternal(res, initsize);
63 : 53898 : return res;
64 : : }
65 : :
66 : : /*
67 : : * makeStringInfo
68 : : *
69 : : * Create an empty 'StringInfoData' & return a pointer to it.
70 : : */
71 : : StringInfo
9631 tgl@sss.pgh.pa.us 72 : 53846 : makeStringInfo(void)
73 : : {
238 ishii@postgresql.org 74 : 53846 : return makeStringInfoInternal(STRINGINFO_DEFAULT_SIZE);
75 : : }
76 : :
77 : : /*
78 : : * makeStringInfoExt(int initsize)
79 : : *
80 : : * Create an empty 'StringInfoData' & return a pointer to it.
81 : : * The initial memory allocation size is specified by 'initsize'.
82 : : * The valid range for 'initsize' is 1 to MaxAllocSize.
83 : : */
84 : : StringInfo
85 : 52 : makeStringInfoExt(int initsize)
86 : : {
87 : 52 : return makeStringInfoInternal(initsize);
88 : : }
89 : :
90 : : /*
91 : : * initStringInfo
92 : : *
93 : : * Initialize a StringInfoData struct (with previously undefined contents)
94 : : * to describe an empty string.
95 : : */
96 : : void
9631 tgl@sss.pgh.pa.us 97 : 4291387 : initStringInfo(StringInfo str)
98 : : {
238 ishii@postgresql.org 99 : 4291387 : initStringInfoInternal(str, STRINGINFO_DEFAULT_SIZE);
100 : 4291387 : }
101 : :
102 : : /*
103 : : * initStringInfoExt
104 : : *
105 : : * Initialize a StringInfoData struct (with previously undefined contents)
106 : : * to describe an empty string.
107 : : * The initial memory allocation size is specified by 'initsize'.
108 : : * The valid range for 'initsize' is 1 to MaxAllocSize.
109 : : */
110 : : void
238 ishii@postgresql.org 111 :UBC 0 : initStringInfoExt(StringInfo str, int initsize)
112 : : {
113 : 0 : initStringInfoInternal(str, initsize);
6762 neilc@samurai.com 114 : 0 : }
115 : :
116 : : /*
117 : : * resetStringInfo
118 : : *
119 : : * Reset the StringInfo: the data buffer remains valid, but its
120 : : * previous content, if any, is cleared.
121 : : *
122 : : * Read-only StringInfos as initialized by initReadOnlyStringInfo cannot be
123 : : * reset.
124 : : */
125 : : void
6762 neilc@samurai.com 126 :CBC 17941070 : resetStringInfo(StringInfo str)
127 : : {
128 : : /* don't allow resets of read-only StringInfos */
681 drowley@postgresql.o 129 [ - + ]: 17941070 : Assert(str->maxlen != 0);
130 : :
9631 tgl@sss.pgh.pa.us 131 : 17941070 : str->data[0] = '\0';
6762 neilc@samurai.com 132 : 17941070 : str->len = 0;
8176 tgl@sss.pgh.pa.us 133 : 17941070 : str->cursor = 0;
9631 134 : 17941070 : }
135 : :
136 : : /*
137 : : * appendStringInfo
138 : : *
139 : : * Format text data under the control of fmt (an sprintf-style format string)
140 : : * and append it to whatever is already in str. More space is allocated
141 : : * to str if necessary. This is sort of like a combination of sprintf and
142 : : * strcat.
143 : : */
144 : : void
8069 bruce@momjian.us 145 : 13932815 : appendStringInfo(StringInfo str, const char *fmt,...)
146 : : {
2537 tgl@sss.pgh.pa.us 147 : 13932815 : int save_errno = errno;
148 : :
149 : : for (;;)
8171 150 : 23773 : {
151 : : va_list args;
152 : : int needed;
153 : :
154 : : /* Try to format the data. */
2537 155 : 13956588 : errno = save_errno;
8171 156 : 13956588 : va_start(args, fmt);
4335 157 : 13956588 : needed = appendStringInfoVA(str, fmt, args);
8171 158 : 13956588 : va_end(args);
159 : :
4335 160 [ + + ]: 13956588 : if (needed == 0)
161 : 13932815 : break; /* success */
162 : :
163 : : /* Increase the buffer size and try again. */
164 : 23773 : enlargeStringInfo(str, needed);
165 : : }
8171 166 : 13932815 : }
167 : :
168 : : /*
169 : : * appendStringInfoVA
170 : : *
171 : : * Attempt to format text data under the control of fmt (an sprintf-style
172 : : * format string) and append it to whatever is already in str. If successful
173 : : * return zero; if not (because there's not enough space), return an estimate
174 : : * of the space needed, without modifying str. Typically the caller should
175 : : * pass the return value to enlargeStringInfo() before trying again; see
176 : : * appendStringInfo for standard usage pattern.
177 : : *
178 : : * Caution: callers must be sure to preserve their entry-time errno
179 : : * when looping, in case the fmt contains "%m".
180 : : *
181 : : * XXX This API is ugly, but there seems no alternative given the C spec's
182 : : * restrictions on what can portably be done with va_list arguments: you have
183 : : * to redo va_start before you can rescan the argument list, and we can't do
184 : : * that from here.
185 : : */
186 : : int
187 : 14216967 : appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
188 : : {
189 : : int avail;
190 : : size_t nprinted;
191 : :
9631 192 [ - + ]: 14216967 : Assert(str != NULL);
193 : :
194 : : /*
195 : : * If there's hardly any space, don't bother trying, just fail to make the
196 : : * caller enlarge the buffer first. We have to guess at how much to
197 : : * enlarge, since we're skipping the formatting work.
198 : : */
4335 199 : 14216967 : avail = str->maxlen - str->len;
8171 200 [ + + ]: 14216967 : if (avail < 16)
4335 201 : 22322 : return 32;
202 : :
203 : 14194645 : nprinted = pvsnprintf(str->data + str->len, (size_t) avail, fmt, args);
204 : :
205 [ + + ]: 14194645 : if (nprinted < (size_t) avail)
206 : : {
207 : : /* Success. Note nprinted does not include trailing null. */
208 : 14192134 : str->len += (int) nprinted;
209 : 14192134 : return 0;
210 : : }
211 : :
212 : : /* Restore the trailing null so that str is unmodified. */
8171 213 : 2511 : str->data[str->len] = '\0';
214 : :
215 : : /*
216 : : * Return pvsnprintf's estimate of the space needed. (Although this is
217 : : * given as a size_t, we know it will fit in int because it's not more
218 : : * than MaxAllocSize.)
219 : : */
4335 220 : 2511 : return (int) nprinted;
221 : : }
222 : :
223 : : /*
224 : : * appendStringInfoString
225 : : *
226 : : * Append a null-terminated string to str.
227 : : * Like appendStringInfo(str, "%s", s) but faster.
228 : : */
229 : : void
8171 230 : 8855220 : appendStringInfoString(StringInfo str, const char *s)
231 : : {
232 : 8855220 : appendBinaryStringInfo(str, s, strlen(s));
9631 233 : 8855220 : }
234 : :
235 : : /*
236 : : * appendStringInfoChar
237 : : *
238 : : * Append a single byte to str.
239 : : * Like appendStringInfo(str, "%c", ch) but much faster.
240 : : */
241 : : void
242 : 45251551 : appendStringInfoChar(StringInfo str, char ch)
243 : : {
244 : : /* Make more room if needed */
9441 245 [ + + ]: 45251551 : if (str->len + 1 >= str->maxlen)
246 : 13036 : enlargeStringInfo(str, 1);
247 : :
248 : : /* OK, append the character */
9631 249 : 45251551 : str->data[str->len] = ch;
250 : 45251551 : str->len++;
251 : 45251551 : str->data[str->len] = '\0';
252 : 45251551 : }
253 : :
254 : : /*
255 : : * appendStringInfoSpaces
256 : : *
257 : : * Append the specified number of spaces to a buffer.
258 : : */
259 : : void
5888 260 : 105490 : appendStringInfoSpaces(StringInfo str, int count)
261 : : {
262 [ + + ]: 105490 : if (count > 0)
263 : : {
264 : : /* Make more room if needed */
265 : 102430 : enlargeStringInfo(str, count);
266 : :
267 : : /* OK, append the spaces */
960 drowley@postgresql.o 268 : 102430 : memset(&str->data[str->len], ' ', count);
269 : 102430 : str->len += count;
5888 tgl@sss.pgh.pa.us 270 : 102430 : str->data[str->len] = '\0';
271 : : }
272 : 105490 : }
273 : :
274 : : /*
275 : : * appendBinaryStringInfo
276 : : *
277 : : * Append arbitrary binary data to a StringInfo, allocating more space
278 : : * if necessary. Ensures that a trailing null byte is present.
279 : : */
280 : : void
981 peter@eisentraut.org 281 : 20204358 : appendBinaryStringInfo(StringInfo str, const void *data, int datalen)
282 : : {
9631 tgl@sss.pgh.pa.us 283 [ - + ]: 20204358 : Assert(str != NULL);
284 : :
285 : : /* Make more room if needed */
286 : 20204358 : enlargeStringInfo(str, datalen);
287 : :
288 : : /* OK, append the data */
289 : 20204358 : memcpy(str->data + str->len, data, datalen);
290 : 20204358 : str->len += datalen;
291 : :
292 : : /*
293 : : * Keep a trailing null in place, even though it's probably useless for
294 : : * binary data. (Some callers are dealing with text but call this because
295 : : * their input isn't null-terminated.)
296 : : */
297 : 20204358 : str->data[str->len] = '\0';
10651 scrappy@hub.org 298 : 20204358 : }
299 : :
300 : : /*
301 : : * appendBinaryStringInfoNT
302 : : *
303 : : * Append arbitrary binary data to a StringInfo, allocating more space
304 : : * if necessary. Does not ensure a trailing null-byte exists.
305 : : */
306 : : void
981 peter@eisentraut.org 307 : 18090301 : appendBinaryStringInfoNT(StringInfo str, const void *data, int datalen)
308 : : {
2887 andres@anarazel.de 309 [ - + ]: 18090301 : Assert(str != NULL);
310 : :
311 : : /* Make more room if needed */
312 : 18090301 : enlargeStringInfo(str, datalen);
313 : :
314 : : /* OK, append the data */
315 : 18090301 : memcpy(str->data + str->len, data, datalen);
316 : 18090301 : str->len += datalen;
317 : 18090301 : }
318 : :
319 : : /*
320 : : * enlargeStringInfo
321 : : *
322 : : * Make sure there is enough space for 'needed' more bytes
323 : : * ('needed' does not include the terminating null).
324 : : *
325 : : * External callers usually need not concern themselves with this, since
326 : : * all stringinfo.c routines do it automatically. However, if a caller
327 : : * knows that a StringInfo will eventually become X bytes large, it
328 : : * can save some palloc overhead by enlarging the buffer before starting
329 : : * to store data in it.
330 : : *
331 : : * NB: In the backend, because we use repalloc() to enlarge the buffer, the
332 : : * string buffer will remain allocated in the same memory context that was
333 : : * current when initStringInfo was called, even if another context is now
334 : : * current. This is the desired and indeed critical behavior!
335 : : */
336 : : void
8176 tgl@sss.pgh.pa.us 337 : 67453290 : enlargeStringInfo(StringInfo str, int needed)
338 : : {
339 : : int newlen;
340 : :
341 : : /* validate this is not a read-only StringInfo */
681 drowley@postgresql.o 342 [ - + ]: 67453290 : Assert(str->maxlen != 0);
343 : :
344 : : /*
345 : : * Guard against out-of-range "needed" values. Without this, we can get
346 : : * an overflow or infinite loop in the following.
347 : : */
6676 tgl@sss.pgh.pa.us 348 [ - + ]: 67453290 : if (needed < 0) /* should not happen */
349 : : {
350 : : #ifndef FRONTEND
6676 tgl@sss.pgh.pa.us 351 [ # # ]:UBC 0 : elog(ERROR, "invalid string enlargement request size: %d", needed);
352 : : #else
2132 andres@anarazel.de 353 : 0 : fprintf(stderr, "invalid string enlargement request size: %d\n", needed);
354 : 0 : exit(EXIT_FAILURE);
355 : : #endif
356 : : }
3041 alvherre@alvh.no-ip. 357 [ - + ]:CBC 67453290 : if (((Size) needed) >= (MaxAllocSize - (Size) str->len))
358 : : {
359 : : #ifndef FRONTEND
6676 tgl@sss.pgh.pa.us 360 [ # # ]:UBC 0 : ereport(ERROR,
361 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
362 : : errmsg("string buffer exceeds maximum allowed length (%zu bytes)", MaxAllocSize),
363 : : errdetail("Cannot enlarge string buffer containing %d bytes by %d more bytes.",
364 : : str->len, needed)));
365 : : #else
2132 andres@anarazel.de 366 : 0 : fprintf(stderr,
432 drowley@postgresql.o 367 : 0 : _("string buffer exceeds maximum allowed length (%zu bytes)\n\nCannot enlarge string buffer containing %d bytes by %d more bytes.\n"),
368 : : MaxAllocSize, str->len, needed);
2132 andres@anarazel.de 369 : 0 : exit(EXIT_FAILURE);
370 : : #endif
371 : : }
372 : :
8176 tgl@sss.pgh.pa.us 373 :CBC 67453290 : needed += str->len + 1; /* total space required now */
374 : :
375 : : /* Because of the above test, we now have needed <= MaxAllocSize */
376 : :
377 [ + + ]: 67453290 : if (needed <= str->maxlen)
378 : 67339822 : return; /* got enough space already */
379 : :
380 : : /*
381 : : * We don't want to allocate just a little more space with each append;
382 : : * for efficiency, double the buffer size each time it overflows.
383 : : * Actually, we might need to more than double it if 'needed' is big...
384 : : */
3041 alvherre@alvh.no-ip. 385 : 113468 : newlen = 2 * str->maxlen;
386 [ + + ]: 235874 : while (needed > newlen)
8176 tgl@sss.pgh.pa.us 387 : 122406 : newlen = 2 * newlen;
388 : :
389 : : /*
390 : : * Clamp to MaxAllocSize in case we went past it. Note we are assuming
391 : : * here that MaxAllocSize <= INT_MAX/2, else the above loop could
392 : : * overflow. We will still have newlen >= needed.
393 : : */
3041 alvherre@alvh.no-ip. 394 [ - + ]: 113468 : if (newlen > (int) MaxAllocSize)
3041 alvherre@alvh.no-ip. 395 :UBC 0 : newlen = (int) MaxAllocSize;
396 : :
3041 alvherre@alvh.no-ip. 397 :CBC 113468 : str->data = (char *) repalloc(str->data, newlen);
398 : :
8176 tgl@sss.pgh.pa.us 399 : 113468 : str->maxlen = newlen;
400 : : }
401 : :
402 : : /*
403 : : * destroyStringInfo
404 : : *
405 : : * Frees a StringInfo and its buffer (opposite of makeStringInfo()).
406 : : * This must only be called on palloc'd StringInfos.
407 : : */
408 : : void
539 dgustafsson@postgres 409 : 4496 : destroyStringInfo(StringInfo str)
410 : : {
411 : : /* don't allow destroys of read-only StringInfos */
412 [ - + ]: 4496 : Assert(str->maxlen != 0);
413 : :
414 : 4496 : pfree(str->data);
415 : 4496 : pfree(str);
416 : 4496 : }
|