Header And Logo

PostgreSQL
| The world's most advanced open source database.

stringinfo.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * stringinfo.c
00004  *
00005  * StringInfo provides an indefinitely-extensible string data type.
00006  * It can be used to buffer either ordinary C strings (null-terminated text)
00007  * or arbitrary binary data.  All storage is allocated with palloc().
00008  *
00009  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00010  * Portions Copyright (c) 1994, Regents of the University of California
00011  *
00012  *    src/backend/lib/stringinfo.c
00013  *
00014  *-------------------------------------------------------------------------
00015  */
00016 #include "postgres.h"
00017 
00018 #include "lib/stringinfo.h"
00019 #include "utils/memutils.h"
00020 
00021 
00022 /*
00023  * makeStringInfo
00024  *
00025  * Create an empty 'StringInfoData' & return a pointer to it.
00026  */
00027 StringInfo
00028 makeStringInfo(void)
00029 {
00030     StringInfo  res;
00031 
00032     res = (StringInfo) palloc(sizeof(StringInfoData));
00033 
00034     initStringInfo(res);
00035 
00036     return res;
00037 }
00038 
00039 /*
00040  * initStringInfo
00041  *
00042  * Initialize a StringInfoData struct (with previously undefined contents)
00043  * to describe an empty string.
00044  */
00045 void
00046 initStringInfo(StringInfo str)
00047 {
00048     int         size = 1024;    /* initial default buffer size */
00049 
00050     str->data = (char *) palloc(size);
00051     str->maxlen = size;
00052     resetStringInfo(str);
00053 }
00054 
00055 /*
00056  * resetStringInfo
00057  *
00058  * Reset the StringInfo: the data buffer remains valid, but its
00059  * previous content, if any, is cleared.
00060  */
00061 void
00062 resetStringInfo(StringInfo str)
00063 {
00064     str->data[0] = '\0';
00065     str->len = 0;
00066     str->cursor = 0;
00067 }
00068 
00069 /*
00070  * appendStringInfo
00071  *
00072  * Format text data under the control of fmt (an sprintf-style format string)
00073  * and append it to whatever is already in str.  More space is allocated
00074  * to str if necessary.  This is sort of like a combination of sprintf and
00075  * strcat.
00076  */
00077 void
00078 appendStringInfo(StringInfo str, const char *fmt,...)
00079 {
00080     for (;;)
00081     {
00082         va_list     args;
00083         bool        success;
00084 
00085         /* Try to format the data. */
00086         va_start(args, fmt);
00087         success = appendStringInfoVA(str, fmt, args);
00088         va_end(args);
00089 
00090         if (success)
00091             break;
00092 
00093         /* Double the buffer size and try again. */
00094         enlargeStringInfo(str, str->maxlen);
00095     }
00096 }
00097 
00098 /*
00099  * appendStringInfoVA
00100  *
00101  * Attempt to format text data under the control of fmt (an sprintf-style
00102  * format string) and append it to whatever is already in str.  If successful
00103  * return true; if not (because there's not enough space), return false
00104  * without modifying str.  Typically the caller would enlarge str and retry
00105  * on false return --- see appendStringInfo for standard usage pattern.
00106  *
00107  * XXX This API is ugly, but there seems no alternative given the C spec's
00108  * restrictions on what can portably be done with va_list arguments: you have
00109  * to redo va_start before you can rescan the argument list, and we can't do
00110  * that from here.
00111  */
00112 bool
00113 appendStringInfoVA(StringInfo str, const char *fmt, va_list args)
00114 {
00115     int         avail,
00116                 nprinted;
00117 
00118     Assert(str != NULL);
00119 
00120     /*
00121      * If there's hardly any space, don't bother trying, just fail to make the
00122      * caller enlarge the buffer first.
00123      */
00124     avail = str->maxlen - str->len - 1;
00125     if (avail < 16)
00126         return false;
00127 
00128     /*
00129      * Assert check here is to catch buggy vsnprintf that overruns the
00130      * specified buffer length.  Solaris 7 in 64-bit mode is an example of a
00131      * platform with such a bug.
00132      */
00133 #ifdef USE_ASSERT_CHECKING
00134     str->data[str->maxlen - 1] = '\0';
00135 #endif
00136 
00137     nprinted = vsnprintf(str->data + str->len, avail, fmt, args);
00138 
00139     Assert(str->data[str->maxlen - 1] == '\0');
00140 
00141     /*
00142      * Note: some versions of vsnprintf return the number of chars actually
00143      * stored, but at least one returns -1 on failure. Be conservative about
00144      * believing whether the print worked.
00145      */
00146     if (nprinted >= 0 && nprinted < avail - 1)
00147     {
00148         /* Success.  Note nprinted does not include trailing null. */
00149         str->len += nprinted;
00150         return true;
00151     }
00152 
00153     /* Restore the trailing null so that str is unmodified. */
00154     str->data[str->len] = '\0';
00155     return false;
00156 }
00157 
00158 /*
00159  * appendStringInfoString
00160  *
00161  * Append a null-terminated string to str.
00162  * Like appendStringInfo(str, "%s", s) but faster.
00163  */
00164 void
00165 appendStringInfoString(StringInfo str, const char *s)
00166 {
00167     appendBinaryStringInfo(str, s, strlen(s));
00168 }
00169 
00170 /*
00171  * appendStringInfoChar
00172  *
00173  * Append a single byte to str.
00174  * Like appendStringInfo(str, "%c", ch) but much faster.
00175  */
00176 void
00177 appendStringInfoChar(StringInfo str, char ch)
00178 {
00179     /* Make more room if needed */
00180     if (str->len + 1 >= str->maxlen)
00181         enlargeStringInfo(str, 1);
00182 
00183     /* OK, append the character */
00184     str->data[str->len] = ch;
00185     str->len++;
00186     str->data[str->len] = '\0';
00187 }
00188 
00189 /*
00190  * appendStringInfoSpaces
00191  *
00192  * Append the specified number of spaces to a buffer.
00193  */
00194 void
00195 appendStringInfoSpaces(StringInfo str, int count)
00196 {
00197     if (count > 0)
00198     {
00199         /* Make more room if needed */
00200         enlargeStringInfo(str, count);
00201 
00202         /* OK, append the spaces */
00203         while (--count >= 0)
00204             str->data[str->len++] = ' ';
00205         str->data[str->len] = '\0';
00206     }
00207 }
00208 
00209 /*
00210  * appendBinaryStringInfo
00211  *
00212  * Append arbitrary binary data to a StringInfo, allocating more space
00213  * if necessary.
00214  */
00215 void
00216 appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
00217 {
00218     Assert(str != NULL);
00219 
00220     /* Make more room if needed */
00221     enlargeStringInfo(str, datalen);
00222 
00223     /* OK, append the data */
00224     memcpy(str->data + str->len, data, datalen);
00225     str->len += datalen;
00226 
00227     /*
00228      * Keep a trailing null in place, even though it's probably useless for
00229      * binary data.  (Some callers are dealing with text but call this because
00230      * their input isn't null-terminated.)
00231      */
00232     str->data[str->len] = '\0';
00233 }
00234 
00235 /*
00236  * enlargeStringInfo
00237  *
00238  * Make sure there is enough space for 'needed' more bytes
00239  * ('needed' does not include the terminating null).
00240  *
00241  * External callers usually need not concern themselves with this, since
00242  * all stringinfo.c routines do it automatically.  However, if a caller
00243  * knows that a StringInfo will eventually become X bytes large, it
00244  * can save some palloc overhead by enlarging the buffer before starting
00245  * to store data in it.
00246  *
00247  * NB: because we use repalloc() to enlarge the buffer, the string buffer
00248  * will remain allocated in the same memory context that was current when
00249  * initStringInfo was called, even if another context is now current.
00250  * This is the desired and indeed critical behavior!
00251  */
00252 void
00253 enlargeStringInfo(StringInfo str, int needed)
00254 {
00255     int         newlen;
00256 
00257     /*
00258      * Guard against out-of-range "needed" values.  Without this, we can get
00259      * an overflow or infinite loop in the following.
00260      */
00261     if (needed < 0)             /* should not happen */
00262         elog(ERROR, "invalid string enlargement request size: %d", needed);
00263     if (((Size) needed) >= (MaxAllocSize - (Size) str->len))
00264         ereport(ERROR,
00265                 (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED),
00266                  errmsg("out of memory"),
00267                  errdetail("Cannot enlarge string buffer containing %d bytes by %d more bytes.",
00268                            str->len, needed)));
00269 
00270     needed += str->len + 1;     /* total space required now */
00271 
00272     /* Because of the above test, we now have needed <= MaxAllocSize */
00273 
00274     if (needed <= str->maxlen)
00275         return;                 /* got enough space already */
00276 
00277     /*
00278      * We don't want to allocate just a little more space with each append;
00279      * for efficiency, double the buffer size each time it overflows.
00280      * Actually, we might need to more than double it if 'needed' is big...
00281      */
00282     newlen = 2 * str->maxlen;
00283     while (needed > newlen)
00284         newlen = 2 * newlen;
00285 
00286     /*
00287      * Clamp to MaxAllocSize in case we went past it.  Note we are assuming
00288      * here that MaxAllocSize <= INT_MAX/2, else the above loop could
00289      * overflow.  We will still have newlen >= needed.
00290      */
00291     if (newlen > (int) MaxAllocSize)
00292         newlen = (int) MaxAllocSize;
00293 
00294     str->data = (char *) repalloc(str->data, newlen);
00295 
00296     str->maxlen = newlen;
00297 }