Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pqexpbuffer.c
4 : *
5 : * PQExpBuffer provides an indefinitely-extensible string data type.
6 : * It can be used to buffer either ordinary C strings (null-terminated text)
7 : * or arbitrary binary data. All storage is allocated with malloc().
8 : *
9 : * This module is essentially the same as the backend's StringInfo data type,
10 : * but it is intended for use in frontend libpq and client applications.
11 : * Thus, it does not rely on palloc() nor elog(), nor psprintf.c which
12 : * will exit() on error.
13 : *
14 : * It does rely on vsnprintf(); if configure finds that libc doesn't provide
15 : * a usable vsnprintf(), then a copy of our own implementation of it will
16 : * be linked into libpq.
17 : *
18 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group
19 : * Portions Copyright (c) 1994, Regents of the University of California
20 : *
21 : * src/interfaces/libpq/pqexpbuffer.c
22 : *
23 : *-------------------------------------------------------------------------
24 : */
25 :
26 : #include "postgres_fe.h"
27 :
28 : #include <limits.h>
29 :
30 : #include "pqexpbuffer.h"
31 :
32 : #ifdef WIN32
33 : #include "win32.h"
34 : #endif
35 :
36 :
37 : /* All "broken" PQExpBuffers point to this string. */
38 : static const char oom_buffer[1] = "";
39 :
40 : /* Need a char * for unconstify() compatibility */
41 : static const char *const oom_buffer_ptr = oom_buffer;
42 :
43 :
44 : /*
45 : * markPQExpBufferBroken
46 : *
47 : * Put a PQExpBuffer in "broken" state if it isn't already.
48 : */
49 : static void
50 0 : markPQExpBufferBroken(PQExpBuffer str)
51 : {
52 0 : if (str->data != oom_buffer)
53 0 : free(str->data);
54 :
55 : /*
56 : * Casting away const here is a bit ugly, but it seems preferable to not
57 : * marking oom_buffer const. We want to do that to encourage the compiler
58 : * to put oom_buffer in read-only storage, so that anyone who tries to
59 : * scribble on a broken PQExpBuffer will get a failure.
60 : */
61 0 : str->data = unconstify(char *, oom_buffer_ptr);
62 0 : str->len = 0;
63 0 : str->maxlen = 0;
64 0 : }
65 :
66 : /*
67 : * createPQExpBuffer
68 : *
69 : * Create an empty 'PQExpBufferData' & return a pointer to it.
70 : */
71 : PQExpBuffer
72 779784 : createPQExpBuffer(void)
73 : {
74 : PQExpBuffer res;
75 :
76 779784 : res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
77 779784 : if (res != NULL)
78 779784 : initPQExpBuffer(res);
79 :
80 779784 : return res;
81 : }
82 :
83 : /*
84 : * initPQExpBuffer
85 : *
86 : * Initialize a PQExpBufferData struct (with previously undefined contents)
87 : * to describe an empty string.
88 : */
89 : void
90 1670194 : initPQExpBuffer(PQExpBuffer str)
91 : {
92 1670194 : str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
93 1670194 : if (str->data == NULL)
94 : {
95 0 : str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
96 0 : str->maxlen = 0;
97 0 : str->len = 0;
98 : }
99 : else
100 : {
101 1670194 : str->maxlen = INITIAL_EXPBUFFER_SIZE;
102 1670194 : str->len = 0;
103 1670194 : str->data[0] = '\0';
104 : }
105 1670194 : }
106 :
107 : /*
108 : * destroyPQExpBuffer(str);
109 : *
110 : * free()s both the data buffer and the PQExpBufferData.
111 : * This is the inverse of createPQExpBuffer().
112 : */
113 : void
114 758972 : destroyPQExpBuffer(PQExpBuffer str)
115 : {
116 758972 : if (str)
117 : {
118 758682 : termPQExpBuffer(str);
119 758682 : free(str);
120 : }
121 758972 : }
122 :
123 : /*
124 : * termPQExpBuffer(str)
125 : * free()s the data buffer but not the PQExpBufferData itself.
126 : * This is the inverse of initPQExpBuffer().
127 : */
128 : void
129 1525990 : termPQExpBuffer(PQExpBuffer str)
130 : {
131 1525990 : if (str->data != oom_buffer)
132 1525990 : free(str->data);
133 : /* just for luck, make the buffer validly empty. */
134 1525990 : str->data = unconstify(char *, oom_buffer_ptr); /* see comment above */
135 1525990 : str->maxlen = 0;
136 1525990 : str->len = 0;
137 1525990 : }
138 :
139 : /*
140 : * resetPQExpBuffer
141 : * Reset a PQExpBuffer to empty
142 : *
143 : * Note: if possible, a "broken" PQExpBuffer is returned to normal.
144 : */
145 : void
146 7167808 : resetPQExpBuffer(PQExpBuffer str)
147 : {
148 7167808 : if (str)
149 : {
150 7166378 : if (str->data != oom_buffer)
151 : {
152 7166378 : str->len = 0;
153 7166378 : str->data[0] = '\0';
154 : }
155 : else
156 : {
157 : /* try to reinitialize to valid state */
158 0 : initPQExpBuffer(str);
159 : }
160 : }
161 7167808 : }
162 :
163 : /*
164 : * enlargePQExpBuffer
165 : * Make sure there is enough space for 'needed' more bytes in the buffer
166 : * ('needed' does not include the terminating null).
167 : *
168 : * Returns 1 if OK, 0 if failed to enlarge buffer. (In the latter case
169 : * the buffer is left in "broken" state.)
170 : */
171 : int
172 16268646 : enlargePQExpBuffer(PQExpBuffer str, size_t needed)
173 : {
174 : size_t newlen;
175 : char *newdata;
176 :
177 16268646 : if (PQExpBufferBroken(str))
178 0 : return 0; /* already failed */
179 :
180 : /*
181 : * Guard against ridiculous "needed" values, which can occur if we're fed
182 : * bogus data. Without this, we can get an overflow or infinite loop in
183 : * the following.
184 : */
185 16268646 : if (needed >= ((size_t) INT_MAX - str->len))
186 : {
187 0 : markPQExpBufferBroken(str);
188 0 : return 0;
189 : }
190 :
191 16268646 : needed += str->len + 1; /* total space required now */
192 :
193 : /* Because of the above test, we now have needed <= INT_MAX */
194 :
195 16268646 : if (needed <= str->maxlen)
196 16094400 : return 1; /* got enough space already */
197 :
198 : /*
199 : * We don't want to allocate just a little more space with each append;
200 : * for efficiency, double the buffer size each time it overflows.
201 : * Actually, we might need to more than double it if 'needed' is big...
202 : */
203 174246 : newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64;
204 181248 : while (needed > newlen)
205 7002 : newlen = 2 * newlen;
206 :
207 : /*
208 : * Clamp to INT_MAX in case we went past it. Note we are assuming here
209 : * that INT_MAX <= UINT_MAX/2, else the above loop could overflow. We
210 : * will still have newlen >= needed.
211 : */
212 174246 : if (newlen > (size_t) INT_MAX)
213 0 : newlen = (size_t) INT_MAX;
214 :
215 174246 : newdata = (char *) realloc(str->data, newlen);
216 174246 : if (newdata != NULL)
217 : {
218 174246 : str->data = newdata;
219 174246 : str->maxlen = newlen;
220 174246 : return 1;
221 : }
222 :
223 0 : markPQExpBufferBroken(str);
224 0 : return 0;
225 : }
226 :
227 : /*
228 : * printfPQExpBuffer
229 : * Format text data under the control of fmt (an sprintf-like format string)
230 : * and insert it into str. More space is allocated to str if necessary.
231 : * This is a convenience routine that does the same thing as
232 : * resetPQExpBuffer() followed by appendPQExpBuffer().
233 : */
234 : void
235 477354 : printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
236 : {
237 477354 : int save_errno = errno;
238 : va_list args;
239 : bool done;
240 :
241 477354 : resetPQExpBuffer(str);
242 :
243 477354 : if (PQExpBufferBroken(str))
244 0 : return; /* already failed */
245 :
246 : /* Loop in case we have to retry after enlarging the buffer. */
247 : do
248 : {
249 482286 : errno = save_errno;
250 482286 : va_start(args, fmt);
251 482286 : done = appendPQExpBufferVA(str, fmt, args);
252 482286 : va_end(args);
253 482286 : } while (!done);
254 : }
255 :
256 : /*
257 : * appendPQExpBuffer
258 : *
259 : * Format text data under the control of fmt (an sprintf-like format string)
260 : * and append it to whatever is already in str. More space is allocated
261 : * to str if necessary. This is sort of like a combination of sprintf and
262 : * strcat.
263 : */
264 : void
265 1159840 : appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
266 : {
267 1159840 : int save_errno = errno;
268 : va_list args;
269 : bool done;
270 :
271 1159840 : if (PQExpBufferBroken(str))
272 0 : return; /* already failed */
273 :
274 : /* Loop in case we have to retry after enlarging the buffer. */
275 : do
276 : {
277 1314620 : errno = save_errno;
278 1314620 : va_start(args, fmt);
279 1314620 : done = appendPQExpBufferVA(str, fmt, args);
280 1314620 : va_end(args);
281 1314620 : } while (!done);
282 : }
283 :
284 : /*
285 : * appendPQExpBufferVA
286 : * Shared guts of printfPQExpBuffer/appendPQExpBuffer.
287 : * Attempt to format data and append it to str. Returns true if done
288 : * (either successful or hard failure), false if need to retry.
289 : *
290 : * Caution: callers must be sure to preserve their entry-time errno
291 : * when looping, in case the fmt contains "%m".
292 : */
293 : bool
294 1797918 : appendPQExpBufferVA(PQExpBuffer str, const char *fmt, va_list args)
295 : {
296 : size_t avail;
297 : size_t needed;
298 : int nprinted;
299 :
300 : /*
301 : * Try to format the given string into the available space; but if there's
302 : * hardly any space, don't bother trying, just enlarge the buffer first.
303 : */
304 1797918 : if (str->maxlen > str->len + 16)
305 : {
306 1793502 : avail = str->maxlen - str->len;
307 :
308 1793502 : nprinted = vsnprintf(str->data + str->len, avail, fmt, args);
309 :
310 : /*
311 : * If vsnprintf reports an error, fail (we assume this means there's
312 : * something wrong with the format string).
313 : */
314 1793502 : if (unlikely(nprinted < 0))
315 : {
316 0 : markPQExpBufferBroken(str);
317 0 : return true;
318 : }
319 :
320 1793502 : if ((size_t) nprinted < avail)
321 : {
322 : /* Success. Note nprinted does not include trailing null. */
323 1638198 : str->len += nprinted;
324 1638198 : return true;
325 : }
326 :
327 : /*
328 : * We assume a C99-compliant vsnprintf, so believe its estimate of the
329 : * required space, and add one for the trailing null. (If it's wrong,
330 : * the logic will still work, but we may loop multiple times.)
331 : *
332 : * Choke if the required space would exceed INT_MAX, since str->maxlen
333 : * can't represent more than that.
334 : */
335 155304 : if (unlikely(nprinted > INT_MAX - 1))
336 : {
337 0 : markPQExpBufferBroken(str);
338 0 : return true;
339 : }
340 155304 : needed = nprinted + 1;
341 : }
342 : else
343 : {
344 : /*
345 : * We have to guess at how much to enlarge, since we're skipping the
346 : * formatting work. Fortunately, because of enlargePQExpBuffer's
347 : * preference for power-of-2 sizes, this number isn't very sensitive;
348 : * the net effect is that we'll double the buffer size before trying
349 : * to run vsnprintf, which seems sensible.
350 : */
351 4416 : needed = 32;
352 : }
353 :
354 : /* Increase the buffer size and try again. */
355 159720 : if (!enlargePQExpBuffer(str, needed))
356 0 : return true; /* oops, out of memory */
357 :
358 159720 : return false;
359 : }
360 :
361 : /*
362 : * appendPQExpBufferStr
363 : * Append the given string to a PQExpBuffer, allocating more space
364 : * if necessary.
365 : */
366 : void
367 2298460 : appendPQExpBufferStr(PQExpBuffer str, const char *data)
368 : {
369 2298460 : appendBinaryPQExpBuffer(str, data, strlen(data));
370 2298460 : }
371 :
372 : /*
373 : * appendPQExpBufferChar
374 : * Append a single byte to str.
375 : * Like appendPQExpBuffer(str, "%c", ch) but much faster.
376 : */
377 : void
378 2395620 : appendPQExpBufferChar(PQExpBuffer str, char ch)
379 : {
380 : /* Make more room if needed */
381 2395620 : if (!enlargePQExpBuffer(str, 1))
382 0 : return;
383 :
384 : /* OK, append the character */
385 2395620 : str->data[str->len] = ch;
386 2395620 : str->len++;
387 2395620 : str->data[str->len] = '\0';
388 : }
389 :
390 : /*
391 : * appendBinaryPQExpBuffer
392 : *
393 : * Append arbitrary binary data to a PQExpBuffer, allocating more space
394 : * if necessary.
395 : */
396 : void
397 13671898 : appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
398 : {
399 : /* Make more room if needed */
400 13671898 : if (!enlargePQExpBuffer(str, datalen))
401 0 : return;
402 :
403 : /* OK, append the data */
404 13671898 : memcpy(str->data + str->len, data, datalen);
405 13671898 : str->len += datalen;
406 :
407 : /*
408 : * Keep a trailing null in place, even though it's probably useless for
409 : * binary data...
410 : */
411 13671898 : str->data[str->len] = '\0';
412 : }
|