Header And Logo

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

pqexpbuffer.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * pqexpbuffer.c
00004  *
00005  * PQExpBuffer 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 malloc().
00008  *
00009  * This module is essentially the same as the backend's StringInfo data type,
00010  * but it is intended for use in frontend libpq and client applications.
00011  * Thus, it does not rely on palloc() nor elog().
00012  *
00013  * It does rely on vsnprintf(); if configure finds that libc doesn't provide
00014  * a usable vsnprintf(), then a copy of our own implementation of it will
00015  * be linked into libpq.
00016  *
00017  * Portions Copyright (c) 1996-2013, PostgreSQL Global Development Group
00018  * Portions Copyright (c) 1994, Regents of the University of California
00019  *
00020  * src/interfaces/libpq/pqexpbuffer.c
00021  *
00022  *-------------------------------------------------------------------------
00023  */
00024 
00025 #include "postgres_fe.h"
00026 
00027 #include <limits.h>
00028 
00029 #include "pqexpbuffer.h"
00030 
00031 #ifdef WIN32
00032 #include "win32.h"
00033 #endif
00034 
00035 
00036 /* All "broken" PQExpBuffers point to this string. */
00037 static const char oom_buffer[1] = "";
00038 
00039 
00040 /*
00041  * markPQExpBufferBroken
00042  *
00043  * Put a PQExpBuffer in "broken" state if it isn't already.
00044  */
00045 static void
00046 markPQExpBufferBroken(PQExpBuffer str)
00047 {
00048     if (str->data != oom_buffer)
00049         free(str->data);
00050 
00051     /*
00052      * Casting away const here is a bit ugly, but it seems preferable to not
00053      * marking oom_buffer const.  We want to do that to encourage the compiler
00054      * to put oom_buffer in read-only storage, so that anyone who tries to
00055      * scribble on a broken PQExpBuffer will get a failure.
00056      */
00057     str->data = (char *) oom_buffer;
00058     str->len = 0;
00059     str->maxlen = 0;
00060 }
00061 
00062 /*
00063  * createPQExpBuffer
00064  *
00065  * Create an empty 'PQExpBufferData' & return a pointer to it.
00066  */
00067 PQExpBuffer
00068 createPQExpBuffer(void)
00069 {
00070     PQExpBuffer res;
00071 
00072     res = (PQExpBuffer) malloc(sizeof(PQExpBufferData));
00073     if (res != NULL)
00074         initPQExpBuffer(res);
00075 
00076     return res;
00077 }
00078 
00079 /*
00080  * initPQExpBuffer
00081  *
00082  * Initialize a PQExpBufferData struct (with previously undefined contents)
00083  * to describe an empty string.
00084  */
00085 void
00086 initPQExpBuffer(PQExpBuffer str)
00087 {
00088     str->data = (char *) malloc(INITIAL_EXPBUFFER_SIZE);
00089     if (str->data == NULL)
00090     {
00091         str->data = (char *) oom_buffer;        /* see comment above */
00092         str->maxlen = 0;
00093         str->len = 0;
00094     }
00095     else
00096     {
00097         str->maxlen = INITIAL_EXPBUFFER_SIZE;
00098         str->len = 0;
00099         str->data[0] = '\0';
00100     }
00101 }
00102 
00103 /*
00104  * destroyPQExpBuffer(str);
00105  *
00106  *      free()s both the data buffer and the PQExpBufferData.
00107  *      This is the inverse of createPQExpBuffer().
00108  */
00109 void
00110 destroyPQExpBuffer(PQExpBuffer str)
00111 {
00112     if (str)
00113     {
00114         termPQExpBuffer(str);
00115         free(str);
00116     }
00117 }
00118 
00119 /*
00120  * termPQExpBuffer(str)
00121  *      free()s the data buffer but not the PQExpBufferData itself.
00122  *      This is the inverse of initPQExpBuffer().
00123  */
00124 void
00125 termPQExpBuffer(PQExpBuffer str)
00126 {
00127     if (str->data != oom_buffer)
00128         free(str->data);
00129     /* just for luck, make the buffer validly empty. */
00130     str->data = (char *) oom_buffer;    /* see comment above */
00131     str->maxlen = 0;
00132     str->len = 0;
00133 }
00134 
00135 /*
00136  * resetPQExpBuffer
00137  *      Reset a PQExpBuffer to empty
00138  *
00139  * Note: if possible, a "broken" PQExpBuffer is returned to normal.
00140  */
00141 void
00142 resetPQExpBuffer(PQExpBuffer str)
00143 {
00144     if (str)
00145     {
00146         if (str->data != oom_buffer)
00147         {
00148             str->len = 0;
00149             str->data[0] = '\0';
00150         }
00151         else
00152         {
00153             /* try to reinitialize to valid state */
00154             initPQExpBuffer(str);
00155         }
00156     }
00157 }
00158 
00159 /*
00160  * enlargePQExpBuffer
00161  * Make sure there is enough space for 'needed' more bytes in the buffer
00162  * ('needed' does not include the terminating null).
00163  *
00164  * Returns 1 if OK, 0 if failed to enlarge buffer.  (In the latter case
00165  * the buffer is left in "broken" state.)
00166  */
00167 int
00168 enlargePQExpBuffer(PQExpBuffer str, size_t needed)
00169 {
00170     size_t      newlen;
00171     char       *newdata;
00172 
00173     if (PQExpBufferBroken(str))
00174         return 0;               /* already failed */
00175 
00176     /*
00177      * Guard against ridiculous "needed" values, which can occur if we're fed
00178      * bogus data.  Without this, we can get an overflow or infinite loop in
00179      * the following.
00180      */
00181     if (needed >= ((size_t) INT_MAX - str->len))
00182     {
00183         markPQExpBufferBroken(str);
00184         return 0;
00185     }
00186 
00187     needed += str->len + 1;     /* total space required now */
00188 
00189     /* Because of the above test, we now have needed <= INT_MAX */
00190 
00191     if (needed <= str->maxlen)
00192         return 1;               /* got enough space already */
00193 
00194     /*
00195      * We don't want to allocate just a little more space with each append;
00196      * for efficiency, double the buffer size each time it overflows.
00197      * Actually, we might need to more than double it if 'needed' is big...
00198      */
00199     newlen = (str->maxlen > 0) ? (2 * str->maxlen) : 64;
00200     while (needed > newlen)
00201         newlen = 2 * newlen;
00202 
00203     /*
00204      * Clamp to INT_MAX in case we went past it.  Note we are assuming here
00205      * that INT_MAX <= UINT_MAX/2, else the above loop could overflow.  We
00206      * will still have newlen >= needed.
00207      */
00208     if (newlen > (size_t) INT_MAX)
00209         newlen = (size_t) INT_MAX;
00210 
00211     newdata = (char *) realloc(str->data, newlen);
00212     if (newdata != NULL)
00213     {
00214         str->data = newdata;
00215         str->maxlen = newlen;
00216         return 1;
00217     }
00218 
00219     markPQExpBufferBroken(str);
00220     return 0;
00221 }
00222 
00223 /*
00224  * printfPQExpBuffer
00225  * Format text data under the control of fmt (an sprintf-like format string)
00226  * and insert it into str.  More space is allocated to str if necessary.
00227  * This is a convenience routine that does the same thing as
00228  * resetPQExpBuffer() followed by appendPQExpBuffer().
00229  */
00230 void
00231 printfPQExpBuffer(PQExpBuffer str, const char *fmt,...)
00232 {
00233     va_list     args;
00234     size_t      avail;
00235     int         nprinted;
00236 
00237     resetPQExpBuffer(str);
00238 
00239     if (PQExpBufferBroken(str))
00240         return;                 /* already failed */
00241 
00242     for (;;)
00243     {
00244         /*
00245          * Try to format the given string into the available space; but if
00246          * there's hardly any space, don't bother trying, just fall through to
00247          * enlarge the buffer first.
00248          */
00249         if (str->maxlen > str->len + 16)
00250         {
00251             avail = str->maxlen - str->len - 1;
00252             va_start(args, fmt);
00253             nprinted = vsnprintf(str->data + str->len, avail,
00254                                  fmt, args);
00255             va_end(args);
00256 
00257             /*
00258              * Note: some versions of vsnprintf return the number of chars
00259              * actually stored, but at least one returns -1 on failure. Be
00260              * conservative about believing whether the print worked.
00261              */
00262             if (nprinted >= 0 && nprinted < (int) avail - 1)
00263             {
00264                 /* Success.  Note nprinted does not include trailing null. */
00265                 str->len += nprinted;
00266                 break;
00267             }
00268         }
00269         /* Double the buffer size and try again. */
00270         if (!enlargePQExpBuffer(str, str->maxlen))
00271             return;             /* oops, out of memory */
00272     }
00273 }
00274 
00275 /*
00276  * appendPQExpBuffer
00277  *
00278  * Format text data under the control of fmt (an sprintf-like format string)
00279  * and append it to whatever is already in str.  More space is allocated
00280  * to str if necessary.  This is sort of like a combination of sprintf and
00281  * strcat.
00282  */
00283 void
00284 appendPQExpBuffer(PQExpBuffer str, const char *fmt,...)
00285 {
00286     va_list     args;
00287     size_t      avail;
00288     int         nprinted;
00289 
00290     if (PQExpBufferBroken(str))
00291         return;                 /* already failed */
00292 
00293     for (;;)
00294     {
00295         /*
00296          * Try to format the given string into the available space; but if
00297          * there's hardly any space, don't bother trying, just fall through to
00298          * enlarge the buffer first.
00299          */
00300         if (str->maxlen > str->len + 16)
00301         {
00302             avail = str->maxlen - str->len - 1;
00303             va_start(args, fmt);
00304             nprinted = vsnprintf(str->data + str->len, avail,
00305                                  fmt, args);
00306             va_end(args);
00307 
00308             /*
00309              * Note: some versions of vsnprintf return the number of chars
00310              * actually stored, but at least one returns -1 on failure. Be
00311              * conservative about believing whether the print worked.
00312              */
00313             if (nprinted >= 0 && nprinted < (int) avail - 1)
00314             {
00315                 /* Success.  Note nprinted does not include trailing null. */
00316                 str->len += nprinted;
00317                 break;
00318             }
00319         }
00320         /* Double the buffer size and try again. */
00321         if (!enlargePQExpBuffer(str, str->maxlen))
00322             return;             /* oops, out of memory */
00323     }
00324 }
00325 
00326 /*
00327  * appendPQExpBufferStr
00328  * Append the given string to a PQExpBuffer, allocating more space
00329  * if necessary.
00330  */
00331 void
00332 appendPQExpBufferStr(PQExpBuffer str, const char *data)
00333 {
00334     appendBinaryPQExpBuffer(str, data, strlen(data));
00335 }
00336 
00337 /*
00338  * appendPQExpBufferChar
00339  * Append a single byte to str.
00340  * Like appendPQExpBuffer(str, "%c", ch) but much faster.
00341  */
00342 void
00343 appendPQExpBufferChar(PQExpBuffer str, char ch)
00344 {
00345     /* Make more room if needed */
00346     if (!enlargePQExpBuffer(str, 1))
00347         return;
00348 
00349     /* OK, append the character */
00350     str->data[str->len] = ch;
00351     str->len++;
00352     str->data[str->len] = '\0';
00353 }
00354 
00355 /*
00356  * appendBinaryPQExpBuffer
00357  *
00358  * Append arbitrary binary data to a PQExpBuffer, allocating more space
00359  * if necessary.
00360  */
00361 void
00362 appendBinaryPQExpBuffer(PQExpBuffer str, const char *data, size_t datalen)
00363 {
00364     /* Make more room if needed */
00365     if (!enlargePQExpBuffer(str, datalen))
00366         return;
00367 
00368     /* OK, append the data */
00369     memcpy(str->data + str->len, data, datalen);
00370     str->len += datalen;
00371 
00372     /*
00373      * Keep a trailing null in place, even though it's probably useless for
00374      * binary data...
00375      */
00376     str->data[str->len] = '\0';
00377 }