* Code mostly borrowed from PostgreSQL's stringinfo.c
* palloc replaced to malloc, etc.
*/
#include "postgres_fe.h"
#include "postgres.h"
#include <lib/stringinfo.h>
constexpr size_t MAX_ALLOC_SIZE = 0x3fffffff;
constexpr int BUFFER_GROWTH_FACTOR = 2;
* StringInfoData holds information about an extensible string.
* data is the current buffer for the string.
* len is the current string length. There is guaranteed to be
* a terminating '\0' at data[len], although this is not very
* useful when the string holds binary data rather than text.
* maxlen is the allocated size in bytes of 'data', i.e. the maximum
* string size (including the terminating '\0' char) that we can
* currently store in 'data' without having to reallocate
* more space. We must always have maxlen > len.
* cursor is initialized to zero by makeStringInfo or initStringInfo,
* but is not otherwise touched by the stringinfo.c routines.
* Some routines use it to scan through a StringInfo.
*-------------------------
*/
* initStringInfo
*
* Initialize a StringInfoData struct (with previously undefined contents)
* to describe an empty string.
*/
void initStringInfo(StringInfo str)
{
int size = 1024;
str->data = (char *)malloc(size);
str->maxlen = size;
resetStringInfo(str);
}
* appendStringInfoString
*
* Append a null-terminated string to str.
*/
void appendStringInfoString(StringInfo str, const char *s)
{
appendBinaryStringInfo(str, s, strlen(s));
}
* appendBinaryStringInfo
*
* Append arbitrary binary data to a StringInfo, allocating more space
* if necessary.
*/
void appendBinaryStringInfo(StringInfo str, const char *data, int datalen)
{
Assert(str != NULL);
enlargeStringInfo(str, datalen);
errno_t rc = memcpy_s(str->data + str->len, (size_t)(str->maxlen - str->len), data, (size_t)datalen);
securec_check(rc, "\0", "\0");
str->len += datalen;
* Keep a trailing null in place, even though it's probably useless for
* binary data. (Some callers are dealing with text but call this because
* their input isn't null-terminated.)
*/
str->data[str->len] = '\0';
}
* enlargeBuffer
*
* Make sure there is enough space for 'needed' more bytes
* ('needed' does not include the terminating null).
*
* 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 enlargeBuffer(int needed,
int len,
int *maxlen,
char **data)
{
int newlen;
* Guard against out-of-range "needed" values. Without this, we can get
* an overflow or infinite loop in the following.
*/
if (unlikely(needed < 0)) {
printf("Error: invalid string enlargement request size: %d\n", needed);
exit(1);
}
if (unlikely(len > INT_MAX - needed - 1)) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("requested buffer size would overflow: %d", needed)));
}
needed += len + 1;
if (likely(needed <= static_cast<int>(*maxlen))) {
return;
}
if (unlikely(((Size)len > MAX_ALLOC_SIZE) || ((Size)(needed - 1)) >= MAX_ALLOC_SIZE)) {
printf("out of memory\n");
printf("Cannot enlarge buffer containing %d bytes by %d more bytes.\n", len, needed);
exit(1);
}
* 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 = BUFFER_GROWTH_FACTOR * *maxlen;
while (needed > newlen) {
newlen = BUFFER_GROWTH_FACTOR * newlen;
}
* Clamp to MAX_ALLOC_SIZE in case we went past it. Note we are assuming
* here that MAX_ALLOC_SIZE <= INT_MAX/2, else the above loop could
* overflow. We will still have newlen >= needed.
*/
if (newlen > (int)MAX_ALLOC_SIZE) {
newlen = (int)MAX_ALLOC_SIZE;
}
void* tmp = realloc(*data, newlen);
if (tmp == nullptr) {
return;
}
*data = static_cast<char*>(tmp);
*maxlen = newlen;
}