Coverage Report

Created: 2026-10-16 06:48

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
12.4k
{
43
12.4k
  Assert(initsize >= 1 && initsize <= MaxAllocSize);
44
45
12.4k
  str->data = (char *) palloc(initsize);
46
12.4k
  str->maxlen = initsize;
47
12.4k
  resetStringInfo(str);
48
12.4k
}
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
2.05k
{
60
2.05k
  StringInfo  res = palloc_object(StringInfoData);
61
62
2.05k
  initStringInfoInternal(res, initsize);
63
2.05k
  return res;
64
2.05k
}
65
66
/*
67
 * makeStringInfo
68
 *
69
 * Create an empty 'StringInfoData' & return a pointer to it.
70
 */
71
StringInfo
72
makeStringInfo(void)
73
2.05k
{
74
2.05k
  return makeStringInfoInternal(STRINGINFO_DEFAULT_SIZE);
75
2.05k
}
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.4k
{
99
10.4k
  initStringInfoInternal(str, STRINGINFO_DEFAULT_SIZE);
100
10.4k
}
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
17.3k
{
128
  /* don't allow resets of read-only StringInfos */
129
17.3k
  Assert(str->maxlen != 0);
130
131
17.3k
  str->data[0] = '\0';
132
17.3k
  str->len = 0;
133
17.3k
  str->cursor = 0;
134
17.3k
}
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
15.1k
{
147
15.1k
  int     save_errno = errno;
148
149
15.1k
  for (;;)
150
15.1k
  {
151
15.1k
    va_list   args;
152
15.1k
    int     needed;
153
154
    /* Try to format the data. */
155
15.1k
    va_start(args, fmt);
156
15.1k
    needed = appendStringInfoVA(str, fmt, args);
157
15.1k
    va_end(args);
158
159
15.1k
    if (likely(needed == 0))
160
15.1k
      break;        /* success */
161
162
    /* Increase the buffer size and try again. */
163
1
    enlargeStringInfo(str, needed);
164
1
    errno = save_errno;
165
1
  }
166
15.1k
}
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
inline int
187
appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
188
20.8k
{
189
20.8k
  int     avail;
190
20.8k
  int     nprinted;
191
192
  /*
193
   * If there's hardly any space, don't bother trying, just fail to make the
194
   * caller enlarge the buffer first.  We have to guess at how much to
195
   * enlarge, since we're skipping the formatting work.
196
   */
197
20.8k
  avail = str->maxlen - str->len;
198
20.8k
  if (avail < 16)
199
1
    return 32;
200
201
20.8k
  nprinted = vsnprintf(str->data + str->len, (size_t) avail, fmt, args);
202
203
  /* We assume failure means the fmt is bogus, hence hard failure is OK */
204
20.8k
  if (unlikely(nprinted < 0))
205
0
  {
206
0
#ifndef FRONTEND
207
0
    elog(ERROR, "vsnprintf failed: %m with format string \"%s\"", fmt);
208
#else
209
    fprintf(stderr, "vsnprintf failed: %m with format string \"%s\"\n",
210
        fmt);
211
    exit(EXIT_FAILURE);
212
#endif
213
0
  }
214
215
20.8k
  if (likely(nprinted < avail))
216
20.3k
  {
217
    /* Success.  Note nprinted does not include trailing null. */
218
20.3k
    str->len += nprinted;
219
20.3k
    return 0;
220
20.3k
  }
221
222
  /* Restore the trailing null so that str is unmodified. */
223
529
  str->data[str->len] = '\0';
224
225
  /*
226
   * We assume a C99-compliant vsnprintf, so believe its estimate of the
227
   * required space.  (If it's wrong, the logic will still work, but we may
228
   * loop multiple times.)
229
   *
230
   * Unlike pvsnprintf(), we don't check for overrunning MaxAllocSize,
231
   * preferring to leave that to enlargeStringInfo().
232
   */
233
529
  return nprinted;
234
20.8k
}
235
236
/*
237
 * appendStringInfoString
238
 *
239
 * Append a null-terminated string to str.
240
 * Like appendStringInfo(str, "%s", s) but faster.
241
 */
242
void
243
appendStringInfoString(StringInfo str, const char *s)
244
20.6k
{
245
20.6k
  appendBinaryStringInfo(str, s, strlen(s));
246
20.6k
}
247
248
/*
249
 * appendStringInfoChar
250
 *
251
 * Append a single byte to str.
252
 * Like appendStringInfo(str, "%c", ch) but much faster.
253
 */
254
void
255
appendStringInfoChar(StringInfo str, char ch)
256
159k
{
257
  /* Make more room if needed */
258
159k
  if (str->len + 1 >= str->maxlen)
259
2.54k
    enlargeStringInfo(str, 1);
260
261
  /* OK, append the character */
262
159k
  str->data[str->len] = ch;
263
159k
  str->len++;
264
159k
  str->data[str->len] = '\0';
265
159k
}
266
267
/*
268
 * appendStringInfoSpaces
269
 *
270
 * Append the specified number of spaces to a buffer.
271
 */
272
void
273
appendStringInfoSpaces(StringInfo str, int count)
274
0
{
275
0
  if (count > 0)
276
0
  {
277
    /* Make more room if needed */
278
0
    enlargeStringInfo(str, count);
279
280
    /* OK, append the spaces */
281
0
    memset(&str->data[str->len], ' ', count);
282
0
    str->len += count;
283
0
    str->data[str->len] = '\0';
284
0
  }
285
0
}
286
287
/*
288
 * appendBinaryStringInfo
289
 *
290
 * Append arbitrary binary data to a StringInfo, allocating more space
291
 * if necessary. Ensures that a trailing null byte is present.
292
 */
293
void
294
appendBinaryStringInfo(StringInfo str, const void *data, int datalen)
295
88.5k
{
296
88.5k
  Assert(str != NULL);
297
298
  /* Make more room if needed */
299
88.5k
  enlargeStringInfo(str, datalen);
300
301
  /* OK, append the data */
302
88.5k
  memcpy(str->data + str->len, data, datalen);
303
88.5k
  str->len += datalen;
304
305
  /*
306
   * Keep a trailing null in place, even though it's probably useless for
307
   * binary data.  (Some callers are dealing with text but call this because
308
   * their input isn't null-terminated.)
309
   */
310
88.5k
  str->data[str->len] = '\0';
311
88.5k
}
312
313
/*
314
 * appendBinaryStringInfoNT
315
 *
316
 * Append arbitrary binary data to a StringInfo, allocating more space
317
 * if necessary. Does not ensure a trailing null-byte exists.
318
 */
319
void
320
appendBinaryStringInfoNT(StringInfo str, const void *data, int datalen)
321
0
{
322
0
  Assert(str != NULL);
323
324
  /* Make more room if needed */
325
0
  enlargeStringInfo(str, datalen);
326
327
  /* OK, append the data */
328
0
  memcpy(str->data + str->len, data, datalen);
329
0
  str->len += datalen;
330
0
}
331
332
/*
333
 * enlargeStringInfo
334
 *
335
 * Make sure there is enough space for 'needed' more bytes
336
 * ('needed' does not include the terminating null).
337
 *
338
 * External callers usually need not concern themselves with this, since
339
 * all stringinfo.c routines do it automatically.  However, if a caller
340
 * knows that a StringInfo will eventually become X bytes large, it
341
 * can save some palloc overhead by enlarging the buffer before starting
342
 * to store data in it.
343
 *
344
 * NB: In the backend, because we use repalloc() to enlarge the buffer, the
345
 * string buffer will remain allocated in the same memory context that was
346
 * current when initStringInfo was called, even if another context is now
347
 * current.  This is the desired and indeed critical behavior!
348
 */
349
void
350
enlargeStringInfo(StringInfo str, int needed)
351
91.6k
{
352
91.6k
  int     newlen;
353
354
  /* validate this is not a read-only StringInfo */
355
91.6k
  Assert(str->maxlen != 0);
356
357
  /*
358
   * Guard against out-of-range "needed" values.  Without this, we can get
359
   * an overflow or infinite loop in the following.
360
   */
361
91.6k
  if (needed < 0)        /* should not happen */
362
0
  {
363
0
#ifndef FRONTEND
364
0
    elog(ERROR, "invalid string enlargement request size: %d", needed);
365
#else
366
    fprintf(stderr, "invalid string enlargement request size: %d\n", needed);
367
    exit(EXIT_FAILURE);
368
#endif
369
0
  }
370
91.6k
  if (((Size) needed) >= (MaxAllocSize - (Size) str->len))
371
0
  {
372
0
#ifndef FRONTEND
373
0
    ereport(ERROR,
374
0
        (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
375
0
         errmsg("string buffer exceeds maximum allowed length (%zu bytes)", MaxAllocSize),
376
0
         errdetail("Cannot enlarge string buffer containing %d bytes by %d more bytes.",
377
0
               str->len, needed)));
378
#else
379
    fprintf(stderr,
380
        _("string buffer exceeds maximum allowed length (%zu bytes)\n\nCannot enlarge string buffer containing %d bytes by %d more bytes.\n"),
381
        MaxAllocSize, str->len, needed);
382
    exit(EXIT_FAILURE);
383
#endif
384
0
  }
385
386
91.6k
  needed += str->len + 1;   /* total space required now */
387
388
  /* Because of the above test, we now have needed <= MaxAllocSize */
389
390
91.6k
  if (needed <= str->maxlen)
391
88.4k
    return;          /* got enough space already */
392
393
  /*
394
   * We don't want to allocate just a little more space with each append;
395
   * for efficiency, double the buffer size each time it overflows.
396
   * Actually, we might need to more than double it if 'needed' is big...
397
   */
398
3.24k
  newlen = 2 * str->maxlen;
399
5.39k
  while (needed > newlen)
400
2.15k
    newlen = 2 * newlen;
401
402
  /*
403
   * Clamp to MaxAllocSize in case we went past it.  Note we are assuming
404
   * here that MaxAllocSize <= INT_MAX/2, else the above loop could
405
   * overflow.  We will still have newlen >= needed.
406
   */
407
3.24k
  if (newlen > (int) MaxAllocSize)
408
0
    newlen = (int) MaxAllocSize;
409
410
3.24k
  str->data = (char *) repalloc(str->data, newlen);
411
412
3.24k
  str->maxlen = newlen;
413
3.24k
}
414
415
/*
416
 * destroyStringInfo
417
 *
418
 * Frees a StringInfo and its buffer (opposite of makeStringInfo()).
419
 * This must only be called on palloc'd StringInfos.
420
 */
421
void
422
destroyStringInfo(StringInfo str)
423
0
{
424
  /* don't allow destroys of read-only StringInfos */
425
0
  Assert(str->maxlen != 0);
426
427
0
  pfree(str->data);
428
0
  pfree(str);
429
0
}