PostgreSQL Source Code  git master
stringinfo.c
Go to the documentation of this file.
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  */
42 {
44 
45  res = (StringInfo) palloc(sizeof(StringInfoData));
46 
48 
49  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
60 {
61  int size = 1024; /* initial default buffer size */
62 
63  str->data = (char *) palloc(size);
64  str->maxlen = size;
66 }
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
79 {
80  /* don't allow resets of read-only StringInfos */
81  Assert(str->maxlen != 0);
82 
83  str->data[0] = '\0';
84  str->len = 0;
85  str->cursor = 0;
86 }
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
98 {
99  int save_errno = errno;
100 
101  for (;;)
102  {
103  va_list args;
104  int needed;
105 
106  /* Try to format the data. */
107  errno = save_errno;
108  va_start(args, fmt);
109  needed = appendStringInfoVA(str, fmt, args);
110  va_end(args);
111 
112  if (needed == 0)
113  break; /* success */
114 
115  /* Increase the buffer size and try again. */
116  enlargeStringInfo(str, needed);
117  }
118 }
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 appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
140 {
141  int avail;
142  size_t nprinted;
143 
144  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  */
151  avail = str->maxlen - str->len;
152  if (avail < 16)
153  return 32;
154 
155  nprinted = pvsnprintf(str->data + str->len, (size_t) avail, fmt, args);
156 
157  if (nprinted < (size_t) avail)
158  {
159  /* Success. Note nprinted does not include trailing null. */
160  str->len += (int) nprinted;
161  return 0;
162  }
163 
164  /* Restore the trailing null so that str is unmodified. */
165  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  */
172  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
183 {
184  appendBinaryStringInfo(str, s, strlen(s));
185 }
186 
187 /*
188  * appendStringInfoChar
189  *
190  * Append a single byte to str.
191  * Like appendStringInfo(str, "%c", ch) but much faster.
192  */
193 void
195 {
196  /* Make more room if needed */
197  if (str->len + 1 >= str->maxlen)
199 
200  /* OK, append the character */
201  str->data[str->len] = ch;
202  str->len++;
203  str->data[str->len] = '\0';
204 }
205 
206 /*
207  * appendStringInfoSpaces
208  *
209  * Append the specified number of spaces to a buffer.
210  */
211 void
213 {
214  if (count > 0)
215  {
216  /* Make more room if needed */
217  enlargeStringInfo(str, count);
218 
219  /* OK, append the spaces */
220  memset(&str->data[str->len], ' ', count);
221  str->len += count;
222  str->data[str->len] = '\0';
223  }
224 }
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
233 appendBinaryStringInfo(StringInfo str, const void *data, int datalen)
234 {
235  Assert(str != NULL);
236 
237  /* Make more room if needed */
238  enlargeStringInfo(str, datalen);
239 
240  /* OK, append the data */
241  memcpy(str->data + str->len, data, datalen);
242  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  str->data[str->len] = '\0';
250 }
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
259 appendBinaryStringInfoNT(StringInfo str, const void *data, int datalen)
260 {
261  Assert(str != NULL);
262 
263  /* Make more room if needed */
264  enlargeStringInfo(str, datalen);
265 
266  /* OK, append the data */
267  memcpy(str->data + str->len, data, datalen);
268  str->len += datalen;
269 }
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
290 {
291  int newlen;
292 
293  /* validate this is not a read-only StringInfo */
294  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  */
300  if (needed < 0) /* should not happen */
301  {
302 #ifndef FRONTEND
303  elog(ERROR, "invalid string enlargement request size: %d", needed);
304 #else
305  fprintf(stderr, "invalid string enlargement request size: %d\n", needed);
307 #endif
308  }
309  if (((Size) needed) >= (MaxAllocSize - (Size) str->len))
310  {
311 #ifndef FRONTEND
312  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
318  fprintf(stderr,
319  _("out of memory\n\nCannot enlarge string buffer containing %d bytes by %d more bytes.\n"),
320  str->len, needed);
322 #endif
323  }
324 
325  needed += str->len + 1; /* total space required now */
326 
327  /* Because of the above test, we now have needed <= MaxAllocSize */
328 
329  if (needed <= str->maxlen)
330  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  */
337  newlen = 2 * str->maxlen;
338  while (needed > newlen)
339  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  */
346  if (newlen > (int) MaxAllocSize)
347  newlen = (int) MaxAllocSize;
348 
349  str->data = (char *) repalloc(str->data, newlen);
350 
351  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
362 {
363  /* don't allow destroys of read-only StringInfos */
364  Assert(str->maxlen != 0);
365 
366  pfree(str->data);
367  pfree(str);
368 }
size_t Size
Definition: c.h:592
int errdetail(const char *fmt,...)
Definition: elog.c:1205
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define _(x)
Definition: elog.c:90
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define ereport(elevel,...)
Definition: elog.h:149
static void const char * fmt
va_end(args)
Assert(fmt[strlen(fmt) - 1] !='\n')
exit(1)
va_start(args, fmt)
void pfree(void *pointer)
Definition: mcxt.c:1508
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1528
void * palloc(Size size)
Definition: mcxt.c:1304
#define MaxAllocSize
Definition: memutils.h:40
const void * data
#define fprintf
Definition: port.h:242
size_t pvsnprintf(char *buf, size_t len, const char *fmt, va_list args)
Definition: psprintf.c:106
#define EXIT_FAILURE
Definition: settings.h:167
static pg_noinline void Size size
Definition: slab.c:607
int appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
Definition: stringinfo.c:139
void destroyStringInfo(StringInfo str)
Definition: stringinfo.c:361
StringInfo makeStringInfo(void)
Definition: stringinfo.c:41
void resetStringInfo(StringInfo str)
Definition: stringinfo.c:78
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:97
void enlargeStringInfo(StringInfo str, int needed)
Definition: stringinfo.c:289
void appendBinaryStringInfoNT(StringInfo str, const void *data, int datalen)
Definition: stringinfo.c:259
void appendBinaryStringInfo(StringInfo str, const void *data, int datalen)
Definition: stringinfo.c:233
void appendStringInfoSpaces(StringInfo str, int count)
Definition: stringinfo.c:212
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:182
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:194
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
StringInfoData * StringInfo
Definition: stringinfo.h:54