Line data Source code
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 : */
27 : StringInfo
28 4446 : makeStringInfo(void)
29 : {
30 : StringInfo res;
31 :
32 4446 : res = (StringInfo) palloc(sizeof(StringInfoData));
33 :
34 4446 : initStringInfo(res);
35 :
36 4446 : 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
46 204156 : initStringInfo(StringInfo str)
47 : {
48 204156 : int size = 1024; /* initial default buffer size */
49 :
50 204156 : str->data = (char *) palloc(size);
51 204156 : str->maxlen = size;
52 204156 : resetStringInfo(str);
53 204156 : }
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
62 466202 : resetStringInfo(StringInfo str)
63 : {
64 466202 : str->data[0] = '\0';
65 466202 : str->len = 0;
66 466202 : str->cursor = 0;
67 466202 : }
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 264870 : 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 264870 : va_start(args, fmt);
87 264870 : needed = appendStringInfoVA(str, fmt, args);
88 264870 : va_end(args);
89 :
90 264870 : if (needed == 0)
91 263995 : break; /* success */
92 :
93 : /* Increase the buffer size and try again. */
94 875 : enlargeStringInfo(str, needed);
95 875 : }
96 263995 : }
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 276092 : appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
115 : {
116 : int avail;
117 : size_t nprinted;
118 :
119 276092 : 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 276092 : avail = str->maxlen - str->len;
127 276092 : if (avail < 16)
128 829 : return 32;
129 :
130 275263 : nprinted = pvsnprintf(str->data + str->len, (size_t) avail, fmt, args);
131 :
132 275263 : if (nprinted < (size_t) avail)
133 : {
134 : /* Success. Note nprinted does not include trailing null. */
135 275206 : str->len += (int) nprinted;
136 275206 : return 0;
137 : }
138 :
139 : /* Restore the trailing null so that str is unmodified. */
140 57 : 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 57 : 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
157 192892 : appendStringInfoString(StringInfo str, const char *s)
158 : {
159 192892 : appendBinaryStringInfo(str, s, strlen(s));
160 192892 : }
161 :
162 : /*
163 : * appendStringInfoChar
164 : *
165 : * Append a single byte to str.
166 : * Like appendStringInfo(str, "%c", ch) but much faster.
167 : */
168 : void
169 818343 : appendStringInfoChar(StringInfo str, char ch)
170 : {
171 : /* Make more room if needed */
172 818343 : if (str->len + 1 >= str->maxlen)
173 587 : enlargeStringInfo(str, 1);
174 :
175 : /* OK, append the character */
176 818343 : str->data[str->len] = ch;
177 818343 : str->len++;
178 818343 : str->data[str->len] = '\0';
179 818343 : }
180 :
181 : /*
182 : * appendStringInfoSpaces
183 : *
184 : * Append the specified number of spaces to a buffer.
185 : */
186 : void
187 6672 : appendStringInfoSpaces(StringInfo str, int count)
188 : {
189 6672 : if (count > 0)
190 : {
191 : /* Make more room if needed */
192 6621 : enlargeStringInfo(str, count);
193 :
194 : /* OK, append the spaces */
195 60410 : while (--count >= 0)
196 47168 : str->data[str->len++] = ' ';
197 6621 : str->data[str->len] = '\0';
198 : }
199 6672 : }
200 :
201 : /*
202 : * appendBinaryStringInfo
203 : *
204 : * Append arbitrary binary data to a StringInfo, allocating more space
205 : * if necessary.
206 : */
207 : void
208 869840 : appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
209 : {
210 869840 : Assert(str != NULL);
211 :
212 : /* Make more room if needed */
213 869840 : enlargeStringInfo(str, datalen);
214 :
215 : /* OK, append the data */
216 869840 : memcpy(str->data + str->len, data, datalen);
217 869840 : 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 869840 : str->data[str->len] = '\0';
225 869840 : }
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
245 981872 : 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 981872 : if (needed < 0) /* should not happen */
254 0 : elog(ERROR, "invalid string enlargement request size: %d", needed);
255 981872 : if (((Size) needed) >= (MaxAllocSize - (Size) str->len))
256 0 : 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 981872 : needed += str->len + 1; /* total space required now */
263 :
264 : /* Because of the above test, we now have needed <= MaxAllocSize */
265 :
266 981872 : if (needed <= str->maxlen)
267 1959186 : 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 4558 : newlen = 2 * str->maxlen;
275 21192 : while (needed > newlen)
276 12076 : 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 4558 : if (newlen > (int) MaxAllocSize)
284 0 : newlen = (int) MaxAllocSize;
285 :
286 4558 : str->data = (char *) repalloc(str->data, newlen);
287 :
288 4558 : str->maxlen = newlen;
289 : }
|