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-2024, 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
9121 tgl@sss.pgh.pa.us 41 :CBC 48868 : makeStringInfo(void)
42 : : {
43 : : StringInfo res;
44 : :
9716 bruce@momjian.us 45 : 48868 : res = (StringInfo) palloc(sizeof(StringInfoData));
46 : :
9121 tgl@sss.pgh.pa.us 47 : 48868 : initStringInfo(res);
48 : :
9357 bruce@momjian.us 49 : 48868 : 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
9121 tgl@sss.pgh.pa.us 59 : 4771173 : initStringInfo(StringInfo str)
60 : : {
5995 bruce@momjian.us 61 : 4771173 : int size = 1024; /* initial default buffer size */
62 : :
8993 tgl@sss.pgh.pa.us 63 : 4771173 : str->data = (char *) palloc(size);
9121 64 : 4771173 : str->maxlen = size;
6252 neilc@samurai.com 65 : 4771173 : resetStringInfo(str);
66 : 4771173 : }
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 : : * Read-only StringInfos as initialized by initReadOnlyStringInfo cannot be
75 : : * reset.
76 : : */
77 : : void
78 : 16292531 : resetStringInfo(StringInfo str)
79 : : {
80 : : /* don't allow resets of read-only StringInfos */
171 drowley@postgresql.o 81 [ - + ]:GNC 16292531 : Assert(str->maxlen != 0);
82 : :
9121 tgl@sss.pgh.pa.us 83 :CBC 16292531 : str->data[0] = '\0';
6252 neilc@samurai.com 84 : 16292531 : str->len = 0;
7666 tgl@sss.pgh.pa.us 85 : 16292531 : str->cursor = 0;
9121 86 : 16292531 : }
87 : :
88 : : /*
89 : : * appendStringInfo
90 : : *
91 : : * Format text data under the control of fmt (an sprintf-style format string)
92 : : * and append it to whatever is already in str. More space is allocated
93 : : * to str if necessary. This is sort of like a combination of sprintf and
94 : : * strcat.
95 : : */
96 : : void
7559 bruce@momjian.us 97 : 12107644 : appendStringInfo(StringInfo str, const char *fmt,...)
98 : : {
2027 tgl@sss.pgh.pa.us 99 : 12107644 : int save_errno = errno;
100 : :
101 : : for (;;)
7661 102 : 18329 : {
103 : : va_list args;
104 : : int needed;
105 : :
106 : : /* Try to format the data. */
2027 107 : 12125973 : errno = save_errno;
7661 108 : 12125973 : va_start(args, fmt);
3825 109 : 12125973 : needed = appendStringInfoVA(str, fmt, args);
7661 110 : 12125973 : va_end(args);
111 : :
3825 112 [ + + ]: 12125973 : if (needed == 0)
113 : 12107644 : break; /* success */
114 : :
115 : : /* Increase the buffer size and try again. */
116 : 18329 : enlargeStringInfo(str, needed);
117 : : }
7661 118 : 12107644 : }
119 : :
120 : : /*
121 : : * appendStringInfoVA
122 : : *
123 : : * Attempt to format text data under the control of fmt (an sprintf-style
124 : : * format string) and append it to whatever is already in str. If successful
125 : : * return zero; if not (because there's not enough space), return an estimate
126 : : * of the space needed, without modifying str. Typically the caller should
127 : : * pass the return value to enlargeStringInfo() before trying again; see
128 : : * appendStringInfo for standard usage pattern.
129 : : *
130 : : * Caution: callers must be sure to preserve their entry-time errno
131 : : * when looping, in case the fmt contains "%m".
132 : : *
133 : : * XXX This API is ugly, but there seems no alternative given the C spec's
134 : : * restrictions on what can portably be done with va_list arguments: you have
135 : : * to redo va_start before you can rescan the argument list, and we can't do
136 : : * that from here.
137 : : */
138 : : int
139 : 12782773 : appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
140 : : {
141 : : int avail;
142 : : size_t nprinted;
143 : :
9121 144 [ - + ]: 12782773 : Assert(str != NULL);
145 : :
146 : : /*
147 : : * If there's hardly any space, don't bother trying, just fail to make the
148 : : * caller enlarge the buffer first. We have to guess at how much to
149 : : * enlarge, since we're skipping the formatting work.
150 : : */
3825 151 : 12782773 : avail = str->maxlen - str->len;
7661 152 [ + + ]: 12782773 : if (avail < 16)
3825 153 : 17293 : return 32;
154 : :
155 : 12765480 : nprinted = pvsnprintf(str->data + str->len, (size_t) avail, fmt, args);
156 : :
157 [ + + ]: 12765480 : if (nprinted < (size_t) avail)
158 : : {
159 : : /* Success. Note nprinted does not include trailing null. */
160 : 12763052 : str->len += (int) nprinted;
161 : 12763052 : return 0;
162 : : }
163 : :
164 : : /* Restore the trailing null so that str is unmodified. */
7661 165 : 2428 : str->data[str->len] = '\0';
166 : :
167 : : /*
168 : : * Return pvsnprintf's estimate of the space needed. (Although this is
169 : : * given as a size_t, we know it will fit in int because it's not more
170 : : * than MaxAllocSize.)
171 : : */
3825 172 : 2428 : return (int) nprinted;
173 : : }
174 : :
175 : : /*
176 : : * appendStringInfoString
177 : : *
178 : : * Append a null-terminated string to str.
179 : : * Like appendStringInfo(str, "%s", s) but faster.
180 : : */
181 : : void
7661 182 : 7972253 : appendStringInfoString(StringInfo str, const char *s)
183 : : {
184 : 7972253 : appendBinaryStringInfo(str, s, strlen(s));
9121 185 : 7972253 : }
186 : :
187 : : /*
188 : : * appendStringInfoChar
189 : : *
190 : : * Append a single byte to str.
191 : : * Like appendStringInfo(str, "%c", ch) but much faster.
192 : : */
193 : : void
194 : 39456990 : appendStringInfoChar(StringInfo str, char ch)
195 : : {
196 : : /* Make more room if needed */
8931 197 [ + + ]: 39456990 : if (str->len + 1 >= str->maxlen)
198 : 11244 : enlargeStringInfo(str, 1);
199 : :
200 : : /* OK, append the character */
9121 201 : 39456990 : str->data[str->len] = ch;
202 : 39456990 : str->len++;
203 : 39456990 : str->data[str->len] = '\0';
204 : 39456990 : }
205 : :
206 : : /*
207 : : * appendStringInfoSpaces
208 : : *
209 : : * Append the specified number of spaces to a buffer.
210 : : */
211 : : void
5378 212 : 86211 : appendStringInfoSpaces(StringInfo str, int count)
213 : : {
214 [ + + ]: 86211 : if (count > 0)
215 : : {
216 : : /* Make more room if needed */
217 : 83547 : enlargeStringInfo(str, count);
218 : :
219 : : /* OK, append the spaces */
450 drowley@postgresql.o 220 : 83547 : memset(&str->data[str->len], ' ', count);
221 : 83547 : str->len += count;
5378 tgl@sss.pgh.pa.us 222 : 83547 : str->data[str->len] = '\0';
223 : : }
224 : 86211 : }
225 : :
226 : : /*
227 : : * appendBinaryStringInfo
228 : : *
229 : : * Append arbitrary binary data to a StringInfo, allocating more space
230 : : * if necessary. Ensures that a trailing null byte is present.
231 : : */
232 : : void
471 peter@eisentraut.org 233 : 16929717 : appendBinaryStringInfo(StringInfo str, const void *data, int datalen)
234 : : {
9121 tgl@sss.pgh.pa.us 235 [ - + ]: 16929717 : Assert(str != NULL);
236 : :
237 : : /* Make more room if needed */
238 : 16929717 : enlargeStringInfo(str, datalen);
239 : :
240 : : /* OK, append the data */
241 : 16929717 : memcpy(str->data + str->len, data, datalen);
242 : 16929717 : str->len += datalen;
243 : :
244 : : /*
245 : : * Keep a trailing null in place, even though it's probably useless for
246 : : * binary data. (Some callers are dealing with text but call this because
247 : : * their input isn't null-terminated.)
248 : : */
249 : 16929717 : str->data[str->len] = '\0';
10141 scrappy@hub.org 250 : 16929717 : }
251 : :
252 : : /*
253 : : * appendBinaryStringInfoNT
254 : : *
255 : : * Append arbitrary binary data to a StringInfo, allocating more space
256 : : * if necessary. Does not ensure a trailing null-byte exists.
257 : : */
258 : : void
471 peter@eisentraut.org 259 : 14988591 : appendBinaryStringInfoNT(StringInfo str, const void *data, int datalen)
260 : : {
2377 andres@anarazel.de 261 [ - + ]: 14988591 : Assert(str != NULL);
262 : :
263 : : /* Make more room if needed */
264 : 14988591 : enlargeStringInfo(str, datalen);
265 : :
266 : : /* OK, append the data */
267 : 14988591 : memcpy(str->data + str->len, data, datalen);
268 : 14988591 : str->len += datalen;
269 : 14988591 : }
270 : :
271 : : /*
272 : : * enlargeStringInfo
273 : : *
274 : : * Make sure there is enough space for 'needed' more bytes
275 : : * ('needed' does not include the terminating null).
276 : : *
277 : : * External callers usually need not concern themselves with this, since
278 : : * all stringinfo.c routines do it automatically. However, if a caller
279 : : * knows that a StringInfo will eventually become X bytes large, it
280 : : * can save some palloc overhead by enlarging the buffer before starting
281 : : * to store data in it.
282 : : *
283 : : * NB: In the backend, because we use repalloc() to enlarge the buffer, the
284 : : * string buffer will remain allocated in the same memory context that was
285 : : * current when initStringInfo was called, even if another context is now
286 : : * current. This is the desired and indeed critical behavior!
287 : : */
288 : : void
7666 tgl@sss.pgh.pa.us 289 : 56210815 : enlargeStringInfo(StringInfo str, int needed)
290 : : {
291 : : int newlen;
292 : :
293 : : /* validate this is not a read-only StringInfo */
171 drowley@postgresql.o 294 [ - + ]:GNC 56210815 : Assert(str->maxlen != 0);
295 : :
296 : : /*
297 : : * Guard against out-of-range "needed" values. Without this, we can get
298 : : * an overflow or infinite loop in the following.
299 : : */
6166 tgl@sss.pgh.pa.us 300 [ - + ]:CBC 56210815 : if (needed < 0) /* should not happen */
301 : : {
302 : : #ifndef FRONTEND
6166 tgl@sss.pgh.pa.us 303 [ # # ]:UBC 0 : elog(ERROR, "invalid string enlargement request size: %d", needed);
304 : : #else
1622 andres@anarazel.de 305 : 0 : fprintf(stderr, "invalid string enlargement request size: %d\n", needed);
306 : 0 : exit(EXIT_FAILURE);
307 : : #endif
308 : : }
2531 alvherre@alvh.no-ip. 309 [ - + ]:CBC 56210815 : if (((Size) needed) >= (MaxAllocSize - (Size) str->len))
310 : : {
311 : : #ifndef FRONTEND
6166 tgl@sss.pgh.pa.us 312 [ # # ]:UBC 0 : ereport(ERROR,
313 : : (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
314 : : errmsg("out of memory"),
315 : : errdetail("Cannot enlarge string buffer containing %d bytes by %d more bytes.",
316 : : str->len, needed)));
317 : : #else
1622 andres@anarazel.de 318 : 0 : fprintf(stderr,
319 : 0 : _("out of memory\n\nCannot enlarge string buffer containing %d bytes by %d more bytes.\n"),
320 : : str->len, needed);
321 : 0 : exit(EXIT_FAILURE);
322 : : #endif
323 : : }
324 : :
7666 tgl@sss.pgh.pa.us 325 :CBC 56210815 : needed += str->len + 1; /* total space required now */
326 : :
327 : : /* Because of the above test, we now have needed <= MaxAllocSize */
328 : :
329 [ + + ]: 56210815 : if (needed <= str->maxlen)
330 : 56117943 : return; /* got enough space already */
331 : :
332 : : /*
333 : : * We don't want to allocate just a little more space with each append;
334 : : * for efficiency, double the buffer size each time it overflows.
335 : : * Actually, we might need to more than double it if 'needed' is big...
336 : : */
2531 alvherre@alvh.no-ip. 337 : 92872 : newlen = 2 * str->maxlen;
338 [ + + ]: 208996 : while (needed > newlen)
7666 tgl@sss.pgh.pa.us 339 : 116124 : newlen = 2 * newlen;
340 : :
341 : : /*
342 : : * Clamp to MaxAllocSize in case we went past it. Note we are assuming
343 : : * here that MaxAllocSize <= INT_MAX/2, else the above loop could
344 : : * overflow. We will still have newlen >= needed.
345 : : */
2531 alvherre@alvh.no-ip. 346 [ - + ]: 92872 : if (newlen > (int) MaxAllocSize)
2531 alvherre@alvh.no-ip. 347 :UBC 0 : newlen = (int) MaxAllocSize;
348 : :
2531 alvherre@alvh.no-ip. 349 :CBC 92872 : str->data = (char *) repalloc(str->data, newlen);
350 : :
7666 tgl@sss.pgh.pa.us 351 : 92872 : str->maxlen = newlen;
352 : : }
353 : :
354 : : /*
355 : : * destroyStringInfo
356 : : *
357 : : * Frees a StringInfo and its buffer (opposite of makeStringInfo()).
358 : : * This must only be called on palloc'd StringInfos.
359 : : */
360 : : void
29 dgustafsson@postgres 361 :GNC 3569 : destroyStringInfo(StringInfo str)
362 : : {
363 : : /* don't allow destroys of read-only StringInfos */
364 [ - + ]: 3569 : Assert(str->maxlen != 0);
365 : :
366 : 3569 : pfree(str->data);
367 : 3569 : pfree(str);
368 : 3569 : }
|