| /*------------------------------------------------------------------------- |
| * |
| * stringinfo.c |
| * |
| * StringInfo provides an indefinitely-extensible string data type. |
| * It can be used to buffer either ordinary C strings (null-terminated text) |
| * or arbitrary binary data. All storage is allocated with palloc(). |
| * |
| * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * $PostgreSQL: pgsql/src/backend/lib/stringinfo.c,v 1.50 2009/01/01 17:23:42 momjian Exp $ |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #include "postgres.h" |
| |
| #include "lib/stringinfo.h" |
| #include "utils/memutils.h" |
| |
| |
| /* |
| * makeStringInfo |
| * |
| * Create an empty 'StringInfoData' & return a pointer to it. |
| */ |
| StringInfo |
| makeStringInfo(void) |
| { |
| StringInfo res; |
| |
| res = (StringInfo) palloc(sizeof(StringInfoData)); |
| |
| initStringInfo(res); |
| |
| return res; |
| } |
| |
| /* |
| * initStringInfo |
| * |
| * Initialize a StringInfoData struct (with previously undefined contents) |
| * to describe an empty string. |
| */ |
| void |
| initStringInfo(StringInfo str) |
| { |
| int size = 1024; /* initial default buffer size */ |
| |
| str->data = (char *) palloc(size); |
| str->maxlen = size; |
| str->len = 0; |
| str->data[0] = '\0'; |
| str->cursor = 0; |
| } |
| |
| /* |
| * initStringInfoOfSize |
| * |
| * Initialize a StringInfoData struct with data buffer of 'size' bytes |
| */ |
| void |
| initStringInfoOfSize(StringInfo str, int size) |
| { |
| |
| str->data = (char *) palloc(size); |
| str->maxlen = size; |
| str->len = 0; |
| str->data[0] = '\0'; |
| str->cursor = 0; |
| } |
| |
| /* |
| * initStringInfoOfString |
| * |
| * Initialize a StringInfoData struct with c string |
| */ |
| void |
| initStringInfoOfString(StringInfo str, char * buf, int size) |
| { |
| str->data = buf; |
| str->maxlen = size; |
| str->len = size; |
| str->cursor = 0; |
| } |
| |
| /* |
| * resetStringInfo |
| * |
| * Reset the StringInfo: the data buffer remains valid, but its |
| * previous content, if any, is cleared. |
| */ |
| void |
| resetStringInfo(StringInfo str) |
| { |
| str->data[0] = '\0'; |
| str->len = 0; |
| str->cursor = 0; |
| } |
| |
| /* |
| * appendStringInfo |
| * |
| * Format text data under the control of fmt (an sprintf-style format string) |
| * and append it to whatever is already in str. More space is allocated |
| * to str if necessary. This is sort of like a combination of sprintf and |
| * strcat. |
| */ |
| void |
| appendStringInfo(StringInfo str, const char *fmt,...) |
| { |
| for (;;) |
| { |
| va_list args; |
| bool success; |
| |
| /* Try to format the data. */ |
| va_start(args, fmt); |
| success = appendStringInfoVA(str, fmt, args); |
| va_end(args); |
| |
| if (success) |
| break; |
| |
| /* Double the buffer size and try again. */ |
| enlargeStringInfo(str, str->maxlen); |
| } |
| } |
| |
| /* |
| * appendStringInfoVA |
| * |
| * Attempt to format text data under the control of fmt (an sprintf-style |
| * format string) and append it to whatever is already in str. If successful |
| * return true; if not (because there's not enough space), return false |
| * without modifying str. Typically the caller would enlarge str and retry |
| * on false return --- see appendStringInfo for standard usage pattern. |
| * |
| * XXX This API is ugly, but there seems no alternative given the C spec's |
| * restrictions on what can portably be done with va_list arguments: you have |
| * to redo va_start before you can rescan the argument list, and we can't do |
| * that from here. |
| */ |
| bool |
| appendStringInfoVA(StringInfo str, const char *fmt, va_list args) |
| { |
| int avail, |
| nprinted; |
| |
| Assert(str != NULL); |
| |
| /* |
| * If there's hardly any space, don't bother trying, just fail to make the |
| * caller enlarge the buffer first. |
| */ |
| avail = str->maxlen - str->len - 1; |
| if (avail < 16) |
| return false; |
| |
| /* |
| * Assert check here is to catch buggy vsnprintf that overruns the |
| * specified buffer length. Solaris 7 in 64-bit mode is an example of a |
| * platform with such a bug. |
| */ |
| #ifdef USE_ASSERT_CHECKING |
| str->data[str->maxlen - 1] = '\0'; |
| #endif |
| |
| nprinted = vsnprintf(str->data + str->len, avail, fmt, args); |
| |
| Assert(str->data[str->maxlen - 1] == '\0'); |
| |
| /* |
| * Note: some versions of vsnprintf return the number of chars actually |
| * stored, but at least one returns -1 on failure. Be conservative about |
| * believing whether the print worked. |
| */ |
| if (nprinted >= 0 && nprinted < avail - 1) |
| { |
| /* Success. Note nprinted does not include trailing null. */ |
| str->len += nprinted; |
| return true; |
| } |
| |
| /* Restore the trailing null so that str is unmodified. */ |
| str->data[str->len] = '\0'; |
| return false; |
| } |
| |
| /* |
| * appendStringInfoString |
| * |
| * Append a null-terminated string to str. |
| * Like appendStringInfo(str, "%s", s) but faster. |
| */ |
| void |
| appendStringInfoString(StringInfo str, const char *s) |
| { |
| appendBinaryStringInfo(str, s, strlen(s)); |
| } |
| |
| /* |
| * appendStringInfoChar |
| * |
| * Append a single byte to str. |
| * Like appendStringInfo(str, "%c", ch) but much faster. |
| */ |
| void |
| appendStringInfoChar(StringInfo str, char ch) |
| { |
| /* Make more room if needed */ |
| if (str->len + 1 >= str->maxlen) |
| enlargeStringInfo(str, 1); |
| |
| /* OK, append the character */ |
| str->data[str->len] = ch; |
| str->len++; |
| str->data[str->len] = '\0'; |
| } |
| |
| /* |
| * appendStringInfoFill |
| * |
| * Append a single byte, repeated 0 or more times, to str. |
| */ |
| void |
| appendStringInfoFill(StringInfo str, int occurrences, char ch) |
| { |
| /* Length must not overflow. */ |
| if (str->len + occurrences <= str->len) |
| return; |
| |
| /* Make more room if needed */ |
| if (str->len + occurrences >= str->maxlen) |
| enlargeStringInfo(str, occurrences); |
| |
| /* Fill specified number of bytes with the character. */ |
| memset(str->data + str->len, ch, occurrences); |
| str->len += occurrences; |
| str->data[str->len] = '\0'; |
| } |
| |
| /* |
| * appendBinaryStringInfo |
| * |
| * Append arbitrary binary data to a StringInfo, allocating more space |
| * if necessary. |
| */ |
| void |
| appendBinaryStringInfo(StringInfo str, const void *data, int datalen) |
| { |
| Assert(str != NULL); |
| |
| /* Make more room if needed */ |
| enlargeStringInfo(str, datalen); |
| |
| /* OK, append the data */ |
| memcpy(str->data + str->len, data, datalen); |
| str->len += datalen; |
| |
| /* |
| * Keep a trailing null in place, even though it's probably useless for |
| * binary data... |
| */ |
| str->data[str->len] = '\0'; |
| } |
| |
| /* |
| * enlargeStringInfo |
| * |
| * Make sure there is enough space for 'needed' more bytes |
| * ('needed' does not include the terminating null). |
| * |
| * External callers usually need not concern themselves with this, since |
| * all stringinfo.c routines do it automatically. However, if a caller |
| * knows that a StringInfo will eventually become X bytes large, it |
| * can save some palloc overhead by enlarging the buffer before starting |
| * to store data in it. |
| * |
| * NB: because we use repalloc() to enlarge the buffer, the string buffer |
| * will remain allocated in the same memory context that was current when |
| * initStringInfo was called, even if another context is now current. |
| * This is the desired and indeed critical behavior! |
| */ |
| void |
| enlargeStringInfo(StringInfo str, int needed) |
| { |
| int newlen; |
| |
| /* |
| * Guard against out-of-range "needed" values. Without this, we can get |
| * an overflow or infinite loop in the following. |
| */ |
| if (needed < 0) /* should not happen */ |
| elog(ERROR, "invalid string enlargement request size: %d", needed); |
| if (((Size) needed) >= (MaxAllocSize - (Size) str->len)) |
| ereport(ERROR, |
| (errcode(ERRCODE_PROGRAM_LIMIT_EXCEEDED), |
| errmsg("out of memory"), |
| errdetail("Cannot enlarge string buffer containing %d bytes by %d more bytes.", |
| str->len, needed))); |
| |
| needed += str->len + 1; /* total space required now */ |
| |
| /* Because of the above test, we now have needed <= MaxAllocSize */ |
| |
| if (needed <= str->maxlen) |
| return; /* got enough space already */ |
| |
| /* |
| * We don't want to allocate just a little more space with each append; |
| * for efficiency, double the buffer size each time it overflows. |
| * Actually, we might need to more than double it if 'needed' is big... |
| */ |
| newlen = 2 * str->maxlen; |
| while (needed > newlen) |
| newlen = 2 * newlen; |
| |
| /* |
| * Clamp to MaxAllocSize in case we went past it. Note we are assuming |
| * here that MaxAllocSize <= INT_MAX/2, else the above loop could |
| * overflow. We will still have newlen >= needed. |
| */ |
| if (newlen >= (int) MaxAllocSize) |
| { |
| /* |
| * Currently we support allocations only up to MaxAllocSize - 1 |
| * (see AllocSizeIsValid()). |
| */ |
| newlen = (int) MaxAllocSize - 1; |
| } |
| |
| str->data = (char *) repalloc(str->data, newlen); |
| |
| str->maxlen = newlen; |
| } |
| |
| |
| /*------------------------ |
| * truncateStringInfo |
| * Make sure a StringInfo's string is no longer than 'nchars' characters. |
| */ |
| void |
| truncateStringInfo(StringInfo str, int nchars) |
| { |
| if (str && |
| str->len > nchars) |
| { |
| Assert(str->data != NULL && |
| str->len <= str->maxlen); |
| str->len = nchars; |
| str->data[nchars] = '\0'; |
| } |
| } /* truncateStringInfo */ |
| |
| /* |
| * Replace all occurrences of a string in a StringInfo with a different string. |
| */ |
| void |
| replaceStringInfoString(StringInfo str, char *replace, char *replacement) |
| { |
| char *ptr; |
| |
| while ((ptr = strstr(str->data, replace)) != NULL) |
| { |
| char *dup = pstrdup(str->data); |
| |
| resetStringInfo(str); |
| appendBinaryStringInfo(str, dup, ptr - str->data); |
| appendStringInfoString(str, replacement); |
| appendStringInfoString(str, dup + (ptr - str->data) + strlen(replace)); |
| pfree(dup); |
| } |
| } |
| |