PostgreSQL Source Code  git master
stringinfo.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * stringinfo.c
4  *
5  * StringInfo 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 palloc().
8  *
9  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
10  * Portions Copyright (c) 1994, Regents of the University of California
11  *
12  * src/backend/lib/stringinfo.c
13  *
14  *-------------------------------------------------------------------------
15  */
16 #include "postgres.h"
17 
18 #include "lib/stringinfo.h"
19 #include "utils/memutils.h"
20 
21 
22 /*
23  * makeStringInfo
24  *
25  * Create an empty 'StringInfoData' & return a pointer to it.
26  */
29 {
30  StringInfo res;
31 
32  res = (StringInfo) palloc(sizeof(StringInfoData));
33 
34  initStringInfo(res);
35 
36  return res;
37 }
38 
39 /*
40  * initStringInfo
41  *
42  * Initialize a StringInfoData struct (with previously undefined contents)
43  * to describe an empty string.
44  */
45 void
47 {
48  int size = 1024; /* initial default buffer size */
49 
50  str->data = (char *) palloc(size);
51  str->maxlen = size;
52  resetStringInfo(str);
53 }
54 
55 /*
56  * resetStringInfo
57  *
58  * Reset the StringInfo: the data buffer remains valid, but its
59  * previous content, if any, is cleared.
60  */
61 void
63 {
64  str->data[0] = '\0';
65  str->len = 0;
66  str->cursor = 0;
67 }
68 
69 /*
70  * appendStringInfo
71  *
72  * Format text data under the control of fmt (an sprintf-style format string)
73  * and append it to whatever is already in str. More space is allocated
74  * to str if necessary. This is sort of like a combination of sprintf and
75  * strcat.
76  */
77 void
78 appendStringInfo(StringInfo str, const char *fmt,...)
79 {
80  int save_errno = errno;
81 
82  for (;;)
83  {
84  va_list args;
85  int needed;
86 
87  /* Try to format the data. */
88  errno = save_errno;
89  va_start(args, fmt);
90  needed = appendStringInfoVA(str, fmt, args);
91  va_end(args);
92 
93  if (needed == 0)
94  break; /* success */
95 
96  /* Increase the buffer size and try again. */
97  enlargeStringInfo(str, needed);
98  }
99 }
100 
101 /*
102  * appendStringInfoVA
103  *
104  * Attempt to format text data under the control of fmt (an sprintf-style
105  * format string) and append it to whatever is already in str. If successful
106  * return zero; if not (because there's not enough space), return an estimate
107  * of the space needed, without modifying str. Typically the caller should
108  * pass the return value to enlargeStringInfo() before trying again; see
109  * appendStringInfo for standard usage pattern.
110  *
111  * Caution: callers must be sure to preserve their entry-time errno
112  * when looping, in case the fmt contains "%m".
113  *
114  * XXX This API is ugly, but there seems no alternative given the C spec's
115  * restrictions on what can portably be done with va_list arguments: you have
116  * to redo va_start before you can rescan the argument list, and we can't do
117  * that from here.
118  */
119 int
120 appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
121 {
122  int avail;
123  size_t nprinted;
124 
125  Assert(str != NULL);
126 
127  /*
128  * If there's hardly any space, don't bother trying, just fail to make the
129  * caller enlarge the buffer first. We have to guess at how much to
130  * enlarge, since we're skipping the formatting work.
131  */
132  avail = str->maxlen - str->len;
133  if (avail < 16)
134  return 32;
135 
136  nprinted = pvsnprintf(str->data + str->len, (size_t) avail, fmt, args);
137 
138  if (nprinted < (size_t) avail)
139  {
140  /* Success. Note nprinted does not include trailing null. */
141  str->len += (int) nprinted;
142  return 0;
143  }
144 
145  /* Restore the trailing null so that str is unmodified. */
146  str->data[str->len] = '\0';
147 
148  /*
149  * Return pvsnprintf's estimate of the space needed. (Although this is
150  * given as a size_t, we know it will fit in int because it's not more
151  * than MaxAllocSize.)
152  */
153  return (int) nprinted;
154 }
155 
156 /*
157  * appendStringInfoString
158  *
159  * Append a null-terminated string to str.
160  * Like appendStringInfo(str, "%s", s) but faster.
161  */
162 void
164 {
165  appendBinaryStringInfo(str, s, strlen(s));
166 }
167 
168 /*
169  * appendStringInfoChar
170  *
171  * Append a single byte to str.
172  * Like appendStringInfo(str, "%c", ch) but much faster.
173  */
174 void
176 {
177  /* Make more room if needed */
178  if (str->len + 1 >= str->maxlen)
179  enlargeStringInfo(str, 1);
180 
181  /* OK, append the character */
182  str->data[str->len] = ch;
183  str->len++;
184  str->data[str->len] = '\0';
185 }
186 
187 /*
188  * appendStringInfoSpaces
189  *
190  * Append the specified number of spaces to a buffer.
191  */
192 void
194 {
195  if (count > 0)
196  {
197  /* Make more room if needed */
198  enlargeStringInfo(str, count);
199 
200  /* OK, append the spaces */
201  while (--count >= 0)
202  str->data[str->len++] = ' ';
203  str->data[str->len] = '\0';
204  }
205 }
206 
207 /*
208  * appendBinaryStringInfo
209  *
210  * Append arbitrary binary data to a StringInfo, allocating more space
211  * if necessary. Ensures that a trailing null byte is present.
212  */
213 void
214 appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
215 {
216  Assert(str != NULL);
217 
218  /* Make more room if needed */
219  enlargeStringInfo(str, datalen);
220 
221  /* OK, append the data */
222  memcpy(str->data + str->len, data, datalen);
223  str->len += datalen;
224 
225  /*
226  * Keep a trailing null in place, even though it's probably useless for
227  * binary data. (Some callers are dealing with text but call this because
228  * their input isn't null-terminated.)
229  */
230  str->data[str->len] = '\0';
231 }
232 
233 /*
234  * appendBinaryStringInfoNT
235  *
236  * Append arbitrary binary data to a StringInfo, allocating more space
237  * if necessary. Does not ensure a trailing null-byte exists.
238  */
239 void
240 appendBinaryStringInfoNT(StringInfo str, const char *data, int datalen)
241 {
242  Assert(str != NULL);
243 
244  /* Make more room if needed */
245  enlargeStringInfo(str, datalen);
246 
247  /* OK, append the data */
248  memcpy(str->data + str->len, data, datalen);
249  str->len += datalen;
250 }
251 
252 /*
253  * enlargeStringInfo
254  *
255  * Make sure there is enough space for 'needed' more bytes
256  * ('needed' does not include the terminating null).
257  *
258  * External callers usually need not concern themselves with this, since
259  * all stringinfo.c routines do it automatically. However, if a caller
260  * knows that a StringInfo will eventually become X bytes large, it
261  * can save some palloc overhead by enlarging the buffer before starting
262  * to store data in it.
263  *
264  * NB: because we use repalloc() to enlarge the buffer, the string buffer
265  * will remain allocated in the same memory context that was current when
266  * initStringInfo was called, even if another context is now current.
267  * This is the desired and indeed critical behavior!
268  */
269 void
271 {
272  int newlen;
273 
274  /*
275  * Guard against out-of-range "needed" values. Without this, we can get
276  * an overflow or infinite loop in the following.
277  */
278  if (needed < 0) /* should not happen */
279  elog(ERROR, "invalid string enlargement request size: %d", needed);
280  if (((Size) needed) >= (MaxAllocSize - (Size) str->len))
281  ereport(ERROR,
282  (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
283  errmsg("out of memory"),
284  errdetail("Cannot enlarge string buffer containing %d bytes by %d more bytes.",
285  str->len, needed)));
286 
287  needed += str->len + 1; /* total space required now */
288 
289  /* Because of the above test, we now have needed <= MaxAllocSize */
290 
291  if (needed <= str->maxlen)
292  return; /* got enough space already */
293 
294  /*
295  * We don't want to allocate just a little more space with each append;
296  * for efficiency, double the buffer size each time it overflows.
297  * Actually, we might need to more than double it if 'needed' is big...
298  */
299  newlen = 2 * str->maxlen;
300  while (needed > newlen)
301  newlen = 2 * newlen;
302 
303  /*
304  * Clamp to MaxAllocSize in case we went past it. Note we are assuming
305  * here that MaxAllocSize <= INT_MAX/2, else the above loop could
306  * overflow. We will still have newlen >= needed.
307  */
308  if (newlen > (int) MaxAllocSize)
309  newlen = (int) MaxAllocSize;
310 
311  str->data = (char *) repalloc(str->data, newlen);
312 
313  str->maxlen = newlen;
314 }
StringInfo makeStringInfo(void)
Definition: stringinfo.c:28
StringInfoData * StringInfo
Definition: stringinfo.h:43
int errcode(int sqlerrcode)
Definition: elog.c:570
void appendStringInfo(StringInfo str, const char *fmt,...)
Definition: stringinfo.c:78
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:240
void appendStringInfoString(StringInfo str, const char *s)
Definition: stringinfo.c:163
int errdetail(const char *fmt,...)
Definition: elog.c:860
void enlargeStringInfo(StringInfo str, int needed)
Definition: stringinfo.c:270
void resetStringInfo(StringInfo str)
Definition: stringinfo.c:62
#define ereport(elevel, rest)
Definition: elog.h:141
#define MaxAllocSize
Definition: memutils.h:40
void appendStringInfoChar(StringInfo str, char ch)
Definition: stringinfo.c:175
void initStringInfo(StringInfo str)
Definition: stringinfo.c:46
void appendStringInfoSpaces(StringInfo str, int count)
Definition: stringinfo.c:193
#define Assert(condition)
Definition: c.h:732
size_t Size
Definition: c.h:466
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1044
void * palloc(Size size)
Definition: mcxt.c:924
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define elog(elevel,...)
Definition: elog.h:226
int appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
Definition: stringinfo.c:120
void appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
Definition: stringinfo.c:214