/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 | 11.9k | { |
43 | 11.9k | Assert(initsize >= 1 && initsize <= MaxAllocSize); |
44 | | |
45 | 11.9k | str->data = (char *) palloc(initsize); |
46 | 11.9k | str->maxlen = initsize; |
47 | 11.9k | resetStringInfo(str); |
48 | 11.9k | } |
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 | 1.97k | { |
60 | 1.97k | StringInfo res = palloc_object(StringInfoData); |
61 | | |
62 | 1.97k | initStringInfoInternal(res, initsize); |
63 | 1.97k | return res; |
64 | 1.97k | } |
65 | | |
66 | | /* |
67 | | * makeStringInfo |
68 | | * |
69 | | * Create an empty 'StringInfoData' & return a pointer to it. |
70 | | */ |
71 | | StringInfo |
72 | | makeStringInfo(void) |
73 | 1.97k | { |
74 | 1.97k | return makeStringInfoInternal(STRINGINFO_DEFAULT_SIZE); |
75 | 1.97k | } |
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.0k | { |
99 | 10.0k | initStringInfoInternal(str, STRINGINFO_DEFAULT_SIZE); |
100 | 10.0k | } |
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 | 15.8k | { |
128 | | /* don't allow resets of read-only StringInfos */ |
129 | 15.8k | Assert(str->maxlen != 0); |
130 | | |
131 | 15.8k | str->data[0] = '\0'; |
132 | 15.8k | str->len = 0; |
133 | 15.8k | str->cursor = 0; |
134 | 15.8k | } |
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 | 14.4k | { |
147 | 14.4k | int save_errno = errno; |
148 | | |
149 | 14.4k | for (;;) |
150 | 14.4k | { |
151 | 14.4k | va_list args; |
152 | 14.4k | int needed; |
153 | | |
154 | | /* Try to format the data. */ |
155 | 14.4k | errno = save_errno; |
156 | 14.4k | va_start(args, fmt); |
157 | 14.4k | needed = appendStringInfoVA(str, fmt, args); |
158 | 14.4k | va_end(args); |
159 | | |
160 | 14.4k | if (needed == 0) |
161 | 14.4k | break; /* success */ |
162 | | |
163 | | /* Increase the buffer size and try again. */ |
164 | 0 | enlargeStringInfo(str, needed); |
165 | 0 | } |
166 | 14.4k | } |
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 | | appendStringInfoVA(StringInfo str, const char *fmt, va_list args) |
188 | 19.9k | { |
189 | 19.9k | int avail; |
190 | 19.9k | size_t nprinted; |
191 | | |
192 | 19.9k | 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 | | */ |
199 | 19.9k | avail = str->maxlen - str->len; |
200 | 19.9k | if (avail < 16) |
201 | 0 | return 32; |
202 | | |
203 | 19.9k | nprinted = pvsnprintf(str->data + str->len, (size_t) avail, fmt, args); |
204 | | |
205 | 19.9k | if (nprinted < (size_t) avail) |
206 | 19.5k | { |
207 | | /* Success. Note nprinted does not include trailing null. */ |
208 | 19.5k | str->len += (int) nprinted; |
209 | 19.5k | return 0; |
210 | 19.5k | } |
211 | | |
212 | | /* Restore the trailing null so that str is unmodified. */ |
213 | 455 | 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 | | */ |
220 | 455 | return (int) nprinted; |
221 | 19.9k | } |
222 | | |
223 | | /* |
224 | | * appendStringInfoString |
225 | | * |
226 | | * Append a null-terminated string to str. |
227 | | * Like appendStringInfo(str, "%s", s) but faster. |
228 | | */ |
229 | | void |
230 | | appendStringInfoString(StringInfo str, const char *s) |
231 | 22.9k | { |
232 | 22.9k | appendBinaryStringInfo(str, s, strlen(s)); |
233 | 22.9k | } |
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 | | appendStringInfoChar(StringInfo str, char ch) |
243 | 152k | { |
244 | | /* Make more room if needed */ |
245 | 152k | if (str->len + 1 >= str->maxlen) |
246 | 2.24k | enlargeStringInfo(str, 1); |
247 | | |
248 | | /* OK, append the character */ |
249 | 152k | str->data[str->len] = ch; |
250 | 152k | str->len++; |
251 | 152k | str->data[str->len] = '\0'; |
252 | 152k | } |
253 | | |
254 | | /* |
255 | | * appendStringInfoSpaces |
256 | | * |
257 | | * Append the specified number of spaces to a buffer. |
258 | | */ |
259 | | void |
260 | | appendStringInfoSpaces(StringInfo str, int count) |
261 | 0 | { |
262 | 0 | if (count > 0) |
263 | 0 | { |
264 | | /* Make more room if needed */ |
265 | 0 | enlargeStringInfo(str, count); |
266 | | |
267 | | /* OK, append the spaces */ |
268 | 0 | memset(&str->data[str->len], ' ', count); |
269 | 0 | str->len += count; |
270 | 0 | str->data[str->len] = '\0'; |
271 | 0 | } |
272 | 0 | } |
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 |
281 | | appendBinaryStringInfo(StringInfo str, const void *data, int datalen) |
282 | 92.9k | { |
283 | 92.9k | Assert(str != NULL); |
284 | | |
285 | | /* Make more room if needed */ |
286 | 92.9k | enlargeStringInfo(str, datalen); |
287 | | |
288 | | /* OK, append the data */ |
289 | 92.9k | memcpy(str->data + str->len, data, datalen); |
290 | 92.9k | 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 | 92.9k | str->data[str->len] = '\0'; |
298 | 92.9k | } |
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 |
307 | | appendBinaryStringInfoNT(StringInfo str, const void *data, int datalen) |
308 | 0 | { |
309 | 0 | Assert(str != NULL); |
310 | | |
311 | | /* Make more room if needed */ |
312 | 0 | enlargeStringInfo(str, datalen); |
313 | | |
314 | | /* OK, append the data */ |
315 | 0 | memcpy(str->data + str->len, data, datalen); |
316 | 0 | str->len += datalen; |
317 | 0 | } |
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 |
337 | | enlargeStringInfo(StringInfo str, int needed) |
338 | 95.6k | { |
339 | 95.6k | int newlen; |
340 | | |
341 | | /* validate this is not a read-only StringInfo */ |
342 | 95.6k | 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 | | */ |
348 | 95.6k | if (needed < 0) /* should not happen */ |
349 | 0 | { |
350 | 0 | #ifndef FRONTEND |
351 | 0 | elog(ERROR, "invalid string enlargement request size: %d", needed); |
352 | | #else |
353 | | fprintf(stderr, "invalid string enlargement request size: %d\n", needed); |
354 | | exit(EXIT_FAILURE); |
355 | | #endif |
356 | 0 | } |
357 | 95.6k | if (((Size) needed) >= (MaxAllocSize - (Size) str->len)) |
358 | 0 | { |
359 | 0 | #ifndef FRONTEND |
360 | 0 | ereport(ERROR, |
361 | 0 | (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
362 | 0 | errmsg("string buffer exceeds maximum allowed length (%zu bytes)", MaxAllocSize), |
363 | 0 | errdetail("Cannot enlarge string buffer containing %d bytes by %d more bytes.", |
364 | 0 | str->len, needed))); |
365 | | #else |
366 | | fprintf(stderr, |
367 | | _("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); |
369 | | exit(EXIT_FAILURE); |
370 | | #endif |
371 | 0 | } |
372 | | |
373 | 95.6k | needed += str->len + 1; /* total space required now */ |
374 | | |
375 | | /* Because of the above test, we now have needed <= MaxAllocSize */ |
376 | | |
377 | 95.6k | if (needed <= str->maxlen) |
378 | 92.7k | 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 | | */ |
385 | 2.89k | newlen = 2 * str->maxlen; |
386 | 4.80k | while (needed > newlen) |
387 | 1.91k | 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 | | */ |
394 | 2.89k | if (newlen > (int) MaxAllocSize) |
395 | 0 | newlen = (int) MaxAllocSize; |
396 | | |
397 | 2.89k | str->data = (char *) repalloc(str->data, newlen); |
398 | | |
399 | 2.89k | str->maxlen = newlen; |
400 | 2.89k | } |
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 |
409 | | destroyStringInfo(StringInfo str) |
410 | 0 | { |
411 | | /* don't allow destroys of read-only StringInfos */ |
412 | 0 | Assert(str->maxlen != 0); |
413 | |
|
414 | 0 | pfree(str->data); |
415 | 0 | pfree(str); |
416 | 0 | } |