/*-------------------------------------------------------------------------
 *
 * 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().
 *
 * Copyright (c) 1994, Regents of the University of California
 *
 *	  $Id: stringinfo.c,v 1.20 1999/07/17 20:16:59 momjian Exp $
 *
 *-------------------------------------------------------------------------
 */


#include "postgres.h"
#include "lib/stringinfo.h"

#ifdef NOT_USED
/*
 * makeStringInfo
 *
 * Create an empty 'StringInfoData' & return a pointer to it.
 */
StringInfo
makeStringInfo(void)
{
	StringInfo	res;

	res = (StringInfo) palloc(sizeof(StringInfoData));
	if (res == NULL)
		elog(ERROR, "makeStringInfo: Out of memory");

	initStringInfo(res);

	return res;
}
#endif

/*
 * initStringInfo
 *
 * Initialize a StringInfoData struct (with previously undefined contents)
 * to describe an empty string.
 */
void
initStringInfo(StringInfo str)
{
	int			size = 256;		/* initial default buffer size */

	str->data = palloc(size);
	if (str->data == NULL)
		elog(ERROR,
			 "initStringInfo: Out of memory (%d bytes requested)", size);
	str->maxlen = size;
	str->len = 0;
	str->data[0] = '\0';
}

/*
 * enlargeStringInfo
 *
 * Internal routine: make sure there is enough space for 'needed' more bytes
 * ('needed' does not include the terminating null).
 */
static void
enlargeStringInfo(StringInfo str, int needed)
{
	int			newlen;
	char	   *newdata;

	needed += str->len + 1;		/* total space required now */
	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;

	newdata = palloc(newlen);
	if (newdata == NULL)
		elog(ERROR,
		"enlargeStringInfo: Out of memory (%d bytes requested)", newlen);

	/* OK, transfer data into new buffer, and release old buffer */
	memcpy(newdata, str->data, str->len + 1);
	pfree(str->data);
	str->data = newdata;
	str->maxlen = newlen;
}

/*
 * appendStringInfo
 *
 * Format text data under the control of fmt (an sprintf-like 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.
 *
 * CAUTION: the current implementation has a 1K limit on the amount of text
 * generated in a single call (not on the total string length).
 */
void
appendStringInfo(StringInfo str, const char *fmt,...)
{
	va_list		args;
	char		buffer[1024];
	int			buflen;

	Assert(str != NULL);

	va_start(args, fmt);
	buflen = vsnprintf(buffer, sizeof(buffer), fmt, args);
	va_end(args);

	/* Make more room if needed */
	enlargeStringInfo(str, buflen);

	/* OK, append the data, including the trailing null */
	memcpy(str->data + str->len, buffer, buflen + 1);
	str->len += buflen;
}

/*------------------------
 * appendStringInfoChar
 * Append a single byte to str.
 * Like appendStringInfo(str, "%c", ch) but much faster.
 */
void
appendStringInfoChar(StringInfo str, char ch)
{
	Assert(str != NULL);

	/* Make more room if needed */
	enlargeStringInfo(str, 1);

	/* OK, append the character */
	str->data[str->len] = ch;
	str->len++;
	str->data[str->len] = '\0';
}

/*
 * 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);

	/* 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';
}
