/src/postgres/src/common/stringinfo.c
Line | Count | Source |
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-2026, 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 |
41 | | initStringInfoInternal(StringInfo str, int initsize) |
42 | 12.4k | { |
43 | 12.4k | Assert(initsize >= 1 && initsize <= MaxAllocSize); |
44 | | |
45 | 12.4k | str->data = (char *) palloc(initsize); |
46 | 12.4k | str->maxlen = initsize; |
47 | 12.4k | resetStringInfo(str); |
48 | 12.4k | } |
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 | | makeStringInfoInternal(int initsize) |
59 | 2.05k | { |
60 | 2.05k | StringInfo res = palloc_object(StringInfoData); |
61 | | |
62 | 2.05k | initStringInfoInternal(res, initsize); |
63 | 2.05k | return res; |
64 | 2.05k | } |
65 | | |
66 | | /* |
67 | | * makeStringInfo |
68 | | * |
69 | | * Create an empty 'StringInfoData' & return a pointer to it. |
70 | | */ |
71 | | StringInfo |
72 | | makeStringInfo(void) |
73 | 2.05k | { |
74 | 2.05k | return makeStringInfoInternal(STRINGINFO_DEFAULT_SIZE); |
75 | 2.05k | } |
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 | | makeStringInfoExt(int initsize) |
86 | 0 | { |
87 | 0 | return makeStringInfoInternal(initsize); |
88 | 0 | } |
89 | | |
90 | | /* |
91 | | * initStringInfo |
92 | | * |
93 | | * Initialize a StringInfoData struct (with previously undefined contents) |
94 | | * to describe an empty string. |
95 | | */ |
96 | | void |
97 | | initStringInfo(StringInfo str) |
98 | 10.4k | { |
99 | 10.4k | initStringInfoInternal(str, STRINGINFO_DEFAULT_SIZE); |
100 | 10.4k | } |
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 |
111 | | initStringInfoExt(StringInfo str, int initsize) |
112 | 2 | { |
113 | 2 | initStringInfoInternal(str, initsize); |
114 | 2 | } |
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 |
126 | | resetStringInfo(StringInfo str) |
127 | 17.3k | { |
128 | | /* don't allow resets of read-only StringInfos */ |
129 | 17.3k | Assert(str->maxlen != 0); |
130 | | |
131 | 17.3k | str->data[0] = '\0'; |
132 | 17.3k | str->len = 0; |
133 | 17.3k | str->cursor = 0; |
134 | 17.3k | } |
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 |
145 | | appendStringInfo(StringInfo str, const char *fmt, ...) |
146 | 15.1k | { |
147 | 15.1k | int save_errno = errno; |
148 | | |
149 | 15.1k | for (;;) |
150 | 15.1k | { |
151 | 15.1k | va_list args; |
152 | 15.1k | int needed; |
153 | | |
154 | | /* Try to format the data. */ |
155 | 15.1k | va_start(args, fmt); |
156 | 15.1k | needed = appendStringInfoVA(str, fmt, args); |
157 | 15.1k | va_end(args); |
158 | | |
159 | 15.1k | if (likely(needed == 0)) |
160 | 15.1k | break; /* success */ |
161 | | |
162 | | /* Increase the buffer size and try again. */ |
163 | 1 | enlargeStringInfo(str, needed); |
164 | 1 | errno = save_errno; |
165 | 1 | } |
166 | 15.1k | } |
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 | | inline int |
187 | | appendStringInfoVA(StringInfo str, const char *fmt, va_list args) |
188 | 20.8k | { |
189 | 20.8k | int avail; |
190 | 20.8k | int nprinted; |
191 | | |
192 | | /* |
193 | | * If there's hardly any space, don't bother trying, just fail to make the |
194 | | * caller enlarge the buffer first. We have to guess at how much to |
195 | | * enlarge, since we're skipping the formatting work. |
196 | | */ |
197 | 20.8k | avail = str->maxlen - str->len; |
198 | 20.8k | if (avail < 16) |
199 | 1 | return 32; |
200 | | |
201 | 20.8k | nprinted = vsnprintf(str->data + str->len, (size_t) avail, fmt, args); |
202 | | |
203 | | /* We assume failure means the fmt is bogus, hence hard failure is OK */ |
204 | 20.8k | if (unlikely(nprinted < 0)) |
205 | 0 | { |
206 | 0 | #ifndef FRONTEND |
207 | 0 | elog(ERROR, "vsnprintf failed: %m with format string \"%s\"", fmt); |
208 | | #else |
209 | | fprintf(stderr, "vsnprintf failed: %m with format string \"%s\"\n", |
210 | | fmt); |
211 | | exit(EXIT_FAILURE); |
212 | | #endif |
213 | 0 | } |
214 | | |
215 | 20.8k | if (likely(nprinted < avail)) |
216 | 20.3k | { |
217 | | /* Success. Note nprinted does not include trailing null. */ |
218 | 20.3k | str->len += nprinted; |
219 | 20.3k | return 0; |
220 | 20.3k | } |
221 | | |
222 | | /* Restore the trailing null so that str is unmodified. */ |
223 | 529 | str->data[str->len] = '\0'; |
224 | | |
225 | | /* |
226 | | * We assume a C99-compliant vsnprintf, so believe its estimate of the |
227 | | * required space. (If it's wrong, the logic will still work, but we may |
228 | | * loop multiple times.) |
229 | | * |
230 | | * Unlike pvsnprintf(), we don't check for overrunning MaxAllocSize, |
231 | | * preferring to leave that to enlargeStringInfo(). |
232 | | */ |
233 | 529 | return nprinted; |
234 | 20.8k | } |
235 | | |
236 | | /* |
237 | | * appendStringInfoString |
238 | | * |
239 | | * Append a null-terminated string to str. |
240 | | * Like appendStringInfo(str, "%s", s) but faster. |
241 | | */ |
242 | | void |
243 | | appendStringInfoString(StringInfo str, const char *s) |
244 | 20.6k | { |
245 | 20.6k | appendBinaryStringInfo(str, s, strlen(s)); |
246 | 20.6k | } |
247 | | |
248 | | /* |
249 | | * appendStringInfoChar |
250 | | * |
251 | | * Append a single byte to str. |
252 | | * Like appendStringInfo(str, "%c", ch) but much faster. |
253 | | */ |
254 | | void |
255 | | appendStringInfoChar(StringInfo str, char ch) |
256 | 159k | { |
257 | | /* Make more room if needed */ |
258 | 159k | if (str->len + 1 >= str->maxlen) |
259 | 2.54k | enlargeStringInfo(str, 1); |
260 | | |
261 | | /* OK, append the character */ |
262 | 159k | str->data[str->len] = ch; |
263 | 159k | str->len++; |
264 | 159k | str->data[str->len] = '\0'; |
265 | 159k | } |
266 | | |
267 | | /* |
268 | | * appendStringInfoSpaces |
269 | | * |
270 | | * Append the specified number of spaces to a buffer. |
271 | | */ |
272 | | void |
273 | | appendStringInfoSpaces(StringInfo str, int count) |
274 | 0 | { |
275 | 0 | if (count > 0) |
276 | 0 | { |
277 | | /* Make more room if needed */ |
278 | 0 | enlargeStringInfo(str, count); |
279 | | |
280 | | /* OK, append the spaces */ |
281 | 0 | memset(&str->data[str->len], ' ', count); |
282 | 0 | str->len += count; |
283 | 0 | str->data[str->len] = '\0'; |
284 | 0 | } |
285 | 0 | } |
286 | | |
287 | | /* |
288 | | * appendBinaryStringInfo |
289 | | * |
290 | | * Append arbitrary binary data to a StringInfo, allocating more space |
291 | | * if necessary. Ensures that a trailing null byte is present. |
292 | | */ |
293 | | void |
294 | | appendBinaryStringInfo(StringInfo str, const void *data, int datalen) |
295 | 88.5k | { |
296 | 88.5k | Assert(str != NULL); |
297 | | |
298 | | /* Make more room if needed */ |
299 | 88.5k | enlargeStringInfo(str, datalen); |
300 | | |
301 | | /* OK, append the data */ |
302 | 88.5k | memcpy(str->data + str->len, data, datalen); |
303 | 88.5k | str->len += datalen; |
304 | | |
305 | | /* |
306 | | * Keep a trailing null in place, even though it's probably useless for |
307 | | * binary data. (Some callers are dealing with text but call this because |
308 | | * their input isn't null-terminated.) |
309 | | */ |
310 | 88.5k | str->data[str->len] = '\0'; |
311 | 88.5k | } |
312 | | |
313 | | /* |
314 | | * appendBinaryStringInfoNT |
315 | | * |
316 | | * Append arbitrary binary data to a StringInfo, allocating more space |
317 | | * if necessary. Does not ensure a trailing null-byte exists. |
318 | | */ |
319 | | void |
320 | | appendBinaryStringInfoNT(StringInfo str, const void *data, int datalen) |
321 | 0 | { |
322 | 0 | Assert(str != NULL); |
323 | | |
324 | | /* Make more room if needed */ |
325 | 0 | enlargeStringInfo(str, datalen); |
326 | | |
327 | | /* OK, append the data */ |
328 | 0 | memcpy(str->data + str->len, data, datalen); |
329 | 0 | str->len += datalen; |
330 | 0 | } |
331 | | |
332 | | /* |
333 | | * enlargeStringInfo |
334 | | * |
335 | | * Make sure there is enough space for 'needed' more bytes |
336 | | * ('needed' does not include the terminating null). |
337 | | * |
338 | | * External callers usually need not concern themselves with this, since |
339 | | * all stringinfo.c routines do it automatically. However, if a caller |
340 | | * knows that a StringInfo will eventually become X bytes large, it |
341 | | * can save some palloc overhead by enlarging the buffer before starting |
342 | | * to store data in it. |
343 | | * |
344 | | * NB: In the backend, because we use repalloc() to enlarge the buffer, the |
345 | | * string buffer will remain allocated in the same memory context that was |
346 | | * current when initStringInfo was called, even if another context is now |
347 | | * current. This is the desired and indeed critical behavior! |
348 | | */ |
349 | | void |
350 | | enlargeStringInfo(StringInfo str, int needed) |
351 | 91.6k | { |
352 | 91.6k | int newlen; |
353 | | |
354 | | /* validate this is not a read-only StringInfo */ |
355 | 91.6k | Assert(str->maxlen != 0); |
356 | | |
357 | | /* |
358 | | * Guard against out-of-range "needed" values. Without this, we can get |
359 | | * an overflow or infinite loop in the following. |
360 | | */ |
361 | 91.6k | if (needed < 0) /* should not happen */ |
362 | 0 | { |
363 | 0 | #ifndef FRONTEND |
364 | 0 | elog(ERROR, "invalid string enlargement request size: %d", needed); |
365 | | #else |
366 | | fprintf(stderr, "invalid string enlargement request size: %d\n", needed); |
367 | | exit(EXIT_FAILURE); |
368 | | #endif |
369 | 0 | } |
370 | 91.6k | if (((Size) needed) >= (MaxAllocSize - (Size) str->len)) |
371 | 0 | { |
372 | 0 | #ifndef FRONTEND |
373 | 0 | ereport(ERROR, |
374 | 0 | (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
375 | 0 | errmsg("string buffer exceeds maximum allowed length (%zu bytes)", MaxAllocSize), |
376 | 0 | errdetail("Cannot enlarge string buffer containing %d bytes by %d more bytes.", |
377 | 0 | str->len, needed))); |
378 | | #else |
379 | | fprintf(stderr, |
380 | | _("string buffer exceeds maximum allowed length (%zu bytes)\n\nCannot enlarge string buffer containing %d bytes by %d more bytes.\n"), |
381 | | MaxAllocSize, str->len, needed); |
382 | | exit(EXIT_FAILURE); |
383 | | #endif |
384 | 0 | } |
385 | | |
386 | 91.6k | needed += str->len + 1; /* total space required now */ |
387 | | |
388 | | /* Because of the above test, we now have needed <= MaxAllocSize */ |
389 | | |
390 | 91.6k | if (needed <= str->maxlen) |
391 | 88.4k | return; /* got enough space already */ |
392 | | |
393 | | /* |
394 | | * We don't want to allocate just a little more space with each append; |
395 | | * for efficiency, double the buffer size each time it overflows. |
396 | | * Actually, we might need to more than double it if 'needed' is big... |
397 | | */ |
398 | 3.24k | newlen = 2 * str->maxlen; |
399 | 5.39k | while (needed > newlen) |
400 | 2.15k | newlen = 2 * newlen; |
401 | | |
402 | | /* |
403 | | * Clamp to MaxAllocSize in case we went past it. Note we are assuming |
404 | | * here that MaxAllocSize <= INT_MAX/2, else the above loop could |
405 | | * overflow. We will still have newlen >= needed. |
406 | | */ |
407 | 3.24k | if (newlen > (int) MaxAllocSize) |
408 | 0 | newlen = (int) MaxAllocSize; |
409 | | |
410 | 3.24k | str->data = (char *) repalloc(str->data, newlen); |
411 | | |
412 | 3.24k | str->maxlen = newlen; |
413 | 3.24k | } |
414 | | |
415 | | /* |
416 | | * destroyStringInfo |
417 | | * |
418 | | * Frees a StringInfo and its buffer (opposite of makeStringInfo()). |
419 | | * This must only be called on palloc'd StringInfos. |
420 | | */ |
421 | | void |
422 | | destroyStringInfo(StringInfo str) |
423 | 0 | { |
424 | | /* don't allow destroys of read-only StringInfos */ |
425 | 0 | Assert(str->maxlen != 0); |
426 | |
|
427 | 0 | pfree(str->data); |
428 | 0 | pfree(str); |
429 | 0 | } |