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