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