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