Coverage Report

Created: 2026-08-14 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/postgres/src/common/stringinfo.c
Line
Count
Source
1
/*-------------------------------------------------------------------------
2
 *
3
 * stringinfo.c
4
 *
5
 * StringInfo provides an extensible string data type (currently limited to a
6
 * length of 1GB).  It can be used to buffer either ordinary C strings
7
 * (null-terminated text) or arbitrary binary data.  All storage is allocated
8
 * with palloc() (falling back to malloc in frontend code).
9
 *
10
 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
11
 * Portions Copyright (c) 1994, Regents of the University of California
12
 *
13
 *    src/common/stringinfo.c
14
 *
15
 *-------------------------------------------------------------------------
16
 */
17
18
#ifndef FRONTEND
19
20
#include "postgres.h"
21
#include "utils/memutils.h"
22
23
#else
24
25
#include "postgres_fe.h"
26
27
#endif
28
29
#include "lib/stringinfo.h"
30
31
32
/*
33
 * initStringInfoInternal
34
 *
35
 * Initialize a StringInfoData struct (with previously undefined contents)
36
 * to describe an empty string.
37
 * The initial memory allocation size is specified by 'initsize'.
38
 * The valid range for 'initsize' is 1 to MaxAllocSize.
39
 */
40
static inline void
41
initStringInfoInternal(StringInfo str, int initsize)
42
11.9k
{
43
11.9k
  Assert(initsize >= 1 && initsize <= MaxAllocSize);
44
45
11.9k
  str->data = (char *) palloc(initsize);
46
11.9k
  str->maxlen = initsize;
47
11.9k
  resetStringInfo(str);
48
11.9k
}
49
50
/*
51
 * makeStringInfoInternal(int initsize)
52
 *
53
 * Create an empty 'StringInfoData' & return a pointer to it.
54
 * The initial memory allocation size is specified by 'initsize'.
55
 * The valid range for 'initsize' is 1 to MaxAllocSize.
56
 */
57
static inline StringInfo
58
makeStringInfoInternal(int initsize)
59
1.97k
{
60
1.97k
  StringInfo  res = palloc_object(StringInfoData);
61
62
1.97k
  initStringInfoInternal(res, initsize);
63
1.97k
  return res;
64
1.97k
}
65
66
/*
67
 * makeStringInfo
68
 *
69
 * Create an empty 'StringInfoData' & return a pointer to it.
70
 */
71
StringInfo
72
makeStringInfo(void)
73
1.97k
{
74
1.97k
  return makeStringInfoInternal(STRINGINFO_DEFAULT_SIZE);
75
1.97k
}
76
77
/*
78
 * makeStringInfoExt(int initsize)
79
 *
80
 * Create an empty 'StringInfoData' & return a pointer to it.
81
 * The initial memory allocation size is specified by 'initsize'.
82
 * The valid range for 'initsize' is 1 to MaxAllocSize.
83
 */
84
StringInfo
85
makeStringInfoExt(int initsize)
86
0
{
87
0
  return makeStringInfoInternal(initsize);
88
0
}
89
90
/*
91
 * initStringInfo
92
 *
93
 * Initialize a StringInfoData struct (with previously undefined contents)
94
 * to describe an empty string.
95
 */
96
void
97
initStringInfo(StringInfo str)
98
10.0k
{
99
10.0k
  initStringInfoInternal(str, STRINGINFO_DEFAULT_SIZE);
100
10.0k
}
101
102
/*
103
 * initStringInfoExt
104
 *
105
 * Initialize a StringInfoData struct (with previously undefined contents)
106
 * to describe an empty string.
107
 * The initial memory allocation size is specified by 'initsize'.
108
 * The valid range for 'initsize' is 1 to MaxAllocSize.
109
 */
110
void
111
initStringInfoExt(StringInfo str, int initsize)
112
2
{
113
2
  initStringInfoInternal(str, initsize);
114
2
}
115
116
/*
117
 * resetStringInfo
118
 *
119
 * Reset the StringInfo: the data buffer remains valid, but its
120
 * previous content, if any, is cleared.
121
 *
122
 * Read-only StringInfos as initialized by initReadOnlyStringInfo cannot be
123
 * reset.
124
 */
125
void
126
resetStringInfo(StringInfo str)
127
15.8k
{
128
  /* don't allow resets of read-only StringInfos */
129
15.8k
  Assert(str->maxlen != 0);
130
131
15.8k
  str->data[0] = '\0';
132
15.8k
  str->len = 0;
133
15.8k
  str->cursor = 0;
134
15.8k
}
135
136
/*
137
 * appendStringInfo
138
 *
139
 * Format text data under the control of fmt (an sprintf-style format string)
140
 * and append it to whatever is already in str.  More space is allocated
141
 * to str if necessary.  This is sort of like a combination of sprintf and
142
 * strcat.
143
 */
144
void
145
appendStringInfo(StringInfo str, const char *fmt, ...)
146
14.4k
{
147
14.4k
  int     save_errno = errno;
148
149
14.4k
  for (;;)
150
14.4k
  {
151
14.4k
    va_list   args;
152
14.4k
    int     needed;
153
154
    /* Try to format the data. */
155
14.4k
    errno = save_errno;
156
14.4k
    va_start(args, fmt);
157
14.4k
    needed = appendStringInfoVA(str, fmt, args);
158
14.4k
    va_end(args);
159
160
14.4k
    if (needed == 0)
161
14.4k
      break;        /* success */
162
163
    /* Increase the buffer size and try again. */
164
0
    enlargeStringInfo(str, needed);
165
0
  }
166
14.4k
}
167
168
/*
169
 * appendStringInfoVA
170
 *
171
 * Attempt to format text data under the control of fmt (an sprintf-style
172
 * format string) and append it to whatever is already in str.  If successful
173
 * return zero; if not (because there's not enough space), return an estimate
174
 * of the space needed, without modifying str.  Typically the caller should
175
 * pass the return value to enlargeStringInfo() before trying again; see
176
 * appendStringInfo for standard usage pattern.
177
 *
178
 * Caution: callers must be sure to preserve their entry-time errno
179
 * when looping, in case the fmt contains "%m".
180
 *
181
 * XXX This API is ugly, but there seems no alternative given the C spec's
182
 * restrictions on what can portably be done with va_list arguments: you have
183
 * to redo va_start before you can rescan the argument list, and we can't do
184
 * that from here.
185
 */
186
int
187
appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
188
19.9k
{
189
19.9k
  int     avail;
190
19.9k
  size_t    nprinted;
191
192
19.9k
  Assert(str != NULL);
193
194
  /*
195
   * If there's hardly any space, don't bother trying, just fail to make the
196
   * caller enlarge the buffer first.  We have to guess at how much to
197
   * enlarge, since we're skipping the formatting work.
198
   */
199
19.9k
  avail = str->maxlen - str->len;
200
19.9k
  if (avail < 16)
201
0
    return 32;
202
203
19.9k
  nprinted = pvsnprintf(str->data + str->len, (size_t) avail, fmt, args);
204
205
19.9k
  if (nprinted < (size_t) avail)
206
19.5k
  {
207
    /* Success.  Note nprinted does not include trailing null. */
208
19.5k
    str->len += (int) nprinted;
209
19.5k
    return 0;
210
19.5k
  }
211
212
  /* Restore the trailing null so that str is unmodified. */
213
455
  str->data[str->len] = '\0';
214
215
  /*
216
   * Return pvsnprintf's estimate of the space needed.  (Although this is
217
   * given as a size_t, we know it will fit in int because it's not more
218
   * than MaxAllocSize.)
219
   */
220
455
  return (int) nprinted;
221
19.9k
}
222
223
/*
224
 * appendStringInfoString
225
 *
226
 * Append a null-terminated string to str.
227
 * Like appendStringInfo(str, "%s", s) but faster.
228
 */
229
void
230
appendStringInfoString(StringInfo str, const char *s)
231
22.9k
{
232
22.9k
  appendBinaryStringInfo(str, s, strlen(s));
233
22.9k
}
234
235
/*
236
 * appendStringInfoChar
237
 *
238
 * Append a single byte to str.
239
 * Like appendStringInfo(str, "%c", ch) but much faster.
240
 */
241
void
242
appendStringInfoChar(StringInfo str, char ch)
243
152k
{
244
  /* Make more room if needed */
245
152k
  if (str->len + 1 >= str->maxlen)
246
2.24k
    enlargeStringInfo(str, 1);
247
248
  /* OK, append the character */
249
152k
  str->data[str->len] = ch;
250
152k
  str->len++;
251
152k
  str->data[str->len] = '\0';
252
152k
}
253
254
/*
255
 * appendStringInfoSpaces
256
 *
257
 * Append the specified number of spaces to a buffer.
258
 */
259
void
260
appendStringInfoSpaces(StringInfo str, int count)
261
0
{
262
0
  if (count > 0)
263
0
  {
264
    /* Make more room if needed */
265
0
    enlargeStringInfo(str, count);
266
267
    /* OK, append the spaces */
268
0
    memset(&str->data[str->len], ' ', count);
269
0
    str->len += count;
270
0
    str->data[str->len] = '\0';
271
0
  }
272
0
}
273
274
/*
275
 * appendBinaryStringInfo
276
 *
277
 * Append arbitrary binary data to a StringInfo, allocating more space
278
 * if necessary. Ensures that a trailing null byte is present.
279
 */
280
void
281
appendBinaryStringInfo(StringInfo str, const void *data, int datalen)
282
92.9k
{
283
92.9k
  Assert(str != NULL);
284
285
  /* Make more room if needed */
286
92.9k
  enlargeStringInfo(str, datalen);
287
288
  /* OK, append the data */
289
92.9k
  memcpy(str->data + str->len, data, datalen);
290
92.9k
  str->len += datalen;
291
292
  /*
293
   * Keep a trailing null in place, even though it's probably useless for
294
   * binary data.  (Some callers are dealing with text but call this because
295
   * their input isn't null-terminated.)
296
   */
297
92.9k
  str->data[str->len] = '\0';
298
92.9k
}
299
300
/*
301
 * appendBinaryStringInfoNT
302
 *
303
 * Append arbitrary binary data to a StringInfo, allocating more space
304
 * if necessary. Does not ensure a trailing null-byte exists.
305
 */
306
void
307
appendBinaryStringInfoNT(StringInfo str, const void *data, int datalen)
308
0
{
309
0
  Assert(str != NULL);
310
311
  /* Make more room if needed */
312
0
  enlargeStringInfo(str, datalen);
313
314
  /* OK, append the data */
315
0
  memcpy(str->data + str->len, data, datalen);
316
0
  str->len += datalen;
317
0
}
318
319
/*
320
 * enlargeStringInfo
321
 *
322
 * Make sure there is enough space for 'needed' more bytes
323
 * ('needed' does not include the terminating null).
324
 *
325
 * External callers usually need not concern themselves with this, since
326
 * all stringinfo.c routines do it automatically.  However, if a caller
327
 * knows that a StringInfo will eventually become X bytes large, it
328
 * can save some palloc overhead by enlarging the buffer before starting
329
 * to store data in it.
330
 *
331
 * NB: In the backend, because we use repalloc() to enlarge the buffer, the
332
 * string buffer will remain allocated in the same memory context that was
333
 * current when initStringInfo was called, even if another context is now
334
 * current.  This is the desired and indeed critical behavior!
335
 */
336
void
337
enlargeStringInfo(StringInfo str, int needed)
338
95.6k
{
339
95.6k
  int     newlen;
340
341
  /* validate this is not a read-only StringInfo */
342
95.6k
  Assert(str->maxlen != 0);
343
344
  /*
345
   * Guard against out-of-range "needed" values.  Without this, we can get
346
   * an overflow or infinite loop in the following.
347
   */
348
95.6k
  if (needed < 0)        /* should not happen */
349
0
  {
350
0
#ifndef FRONTEND
351
0
    elog(ERROR, "invalid string enlargement request size: %d", needed);
352
#else
353
    fprintf(stderr, "invalid string enlargement request size: %d\n", needed);
354
    exit(EXIT_FAILURE);
355
#endif
356
0
  }
357
95.6k
  if (((Size) needed) >= (MaxAllocSize - (Size) str->len))
358
0
  {
359
0
#ifndef FRONTEND
360
0
    ereport(ERROR,
361
0
        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
362
0
         errmsg("string buffer exceeds maximum allowed length (%zu bytes)", MaxAllocSize),
363
0
         errdetail("Cannot enlarge string buffer containing %d bytes by %d more bytes.",
364
0
               str->len, needed)));
365
#else
366
    fprintf(stderr,
367
        _("string buffer exceeds maximum allowed length (%zu bytes)\n\nCannot enlarge string buffer containing %d bytes by %d more bytes.\n"),
368
        MaxAllocSize, str->len, needed);
369
    exit(EXIT_FAILURE);
370
#endif
371
0
  }
372
373
95.6k
  needed += str->len + 1;   /* total space required now */
374
375
  /* Because of the above test, we now have needed <= MaxAllocSize */
376
377
95.6k
  if (needed <= str->maxlen)
378
92.7k
    return;          /* got enough space already */
379
380
  /*
381
   * We don't want to allocate just a little more space with each append;
382
   * for efficiency, double the buffer size each time it overflows.
383
   * Actually, we might need to more than double it if 'needed' is big...
384
   */
385
2.89k
  newlen = 2 * str->maxlen;
386
4.80k
  while (needed > newlen)
387
1.91k
    newlen = 2 * newlen;
388
389
  /*
390
   * Clamp to MaxAllocSize in case we went past it.  Note we are assuming
391
   * here that MaxAllocSize <= INT_MAX/2, else the above loop could
392
   * overflow.  We will still have newlen >= needed.
393
   */
394
2.89k
  if (newlen > (int) MaxAllocSize)
395
0
    newlen = (int) MaxAllocSize;
396
397
2.89k
  str->data = (char *) repalloc(str->data, newlen);
398
399
2.89k
  str->maxlen = newlen;
400
2.89k
}
401
402
/*
403
 * destroyStringInfo
404
 *
405
 * Frees a StringInfo and its buffer (opposite of makeStringInfo()).
406
 * This must only be called on palloc'd StringInfos.
407
 */
408
void
409
destroyStringInfo(StringInfo str)
410
0
{
411
  /* don't allow destroys of read-only StringInfos */
412
0
  Assert(str->maxlen != 0);
413
414
0
  pfree(str->data);
415
0
  pfree(str);
416
0
}