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-2016, 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  */
27 StringInfo
makeStringInfo(void)28 makeStringInfo(void)
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
initStringInfo(StringInfo str)46 initStringInfo(StringInfo str)
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
resetStringInfo(StringInfo str)62 resetStringInfo(StringInfo str)
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
appendStringInfo(StringInfo str,const char * fmt,...)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
appendStringInfoVA(StringInfo str,const char * fmt,va_list args)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
appendStringInfoString(StringInfo str,const char * s)157 appendStringInfoString(StringInfo str, const char *s)
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
appendStringInfoChar(StringInfo str,char ch)169 appendStringInfoChar(StringInfo str, char ch)
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
appendStringInfoSpaces(StringInfo str,int count)187 appendStringInfoSpaces(StringInfo str, int count)
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.
206  */
207 void
appendBinaryStringInfo(StringInfo str,const char * data,int datalen)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  * enlargeStringInfo
229  *
230  * Make sure there is enough space for 'needed' more bytes
231  * ('needed' does not include the terminating null).
232  *
233  * External callers usually need not concern themselves with this, since
234  * all stringinfo.c routines do it automatically.  However, if a caller
235  * knows that a StringInfo will eventually become X bytes large, it
236  * can save some palloc overhead by enlarging the buffer before starting
237  * to store data in it.
238  *
239  * NB: because we use repalloc() to enlarge the buffer, the string buffer
240  * will remain allocated in the same memory context that was current when
241  * initStringInfo was called, even if another context is now current.
242  * This is the desired and indeed critical behavior!
243  */
244 void
enlargeStringInfo(StringInfo str,int needed)245 enlargeStringInfo(StringInfo str, int needed)
246 {
247 	int			newlen;
248 
249 	/*
250 	 * Guard against out-of-range "needed" values.  Without this, we can get
251 	 * an overflow or infinite loop in the following.
252 	 */
253 	if (needed < 0)				/* should not happen */
254 		elog(ERROR, "invalid string enlargement request size: %d", needed);
255 	if (((Size) needed) >= (MaxAllocSize - (Size) str->len))
256 		ereport(ERROR,
257 				(errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
258 				 errmsg("out of memory"),
259 				 errdetail("Cannot enlarge string buffer containing %d bytes by %d more bytes.",
260 						   str->len, needed)));
261 
262 	needed += str->len + 1;		/* total space required now */
263 
264 	/* Because of the above test, we now have needed <= MaxAllocSize */
265 
266 	if (needed <= str->maxlen)
267 		return;					/* got enough space already */
268 
269 	/*
270 	 * We don't want to allocate just a little more space with each append;
271 	 * for efficiency, double the buffer size each time it overflows.
272 	 * Actually, we might need to more than double it if 'needed' is big...
273 	 */
274 	newlen = 2 * str->maxlen;
275 	while (needed > newlen)
276 		newlen = 2 * newlen;
277 
278 	/*
279 	 * Clamp to MaxAllocSize in case we went past it.  Note we are assuming
280 	 * here that MaxAllocSize <= INT_MAX/2, else the above loop could
281 	 * overflow.  We will still have newlen >= needed.
282 	 */
283 	if (newlen > (int) MaxAllocSize)
284 		newlen = (int) MaxAllocSize;
285 
286 	str->data = (char *) repalloc(str->data, newlen);
287 
288 	str->maxlen = newlen;
289 }
290