/****************************************************************
 *								*
 *	Copyright 2001, 2013 Fidelity Information Services, Inc	*
 *								*
 *	This source code contains the intellectual property	*
 *	of its copyright holder(s), and is made available	*
 *	under a license.  If you do not know the terms of	*
 *	the license, please stop and do not read further.	*
 *								*
 ****************************************************************/

#include "mdef.h"
#include "gtm_string.h"
#include "error.h"
#include "gtm_maxstr.h"

/* GT.M string buffer stack where each entry is allocated geometrically on the need basis */
GBLDEF	mstr	maxstr_buff[MAXSTR_STACK_SIZE];
GBLDEF  int	maxstr_stack_level = -1;

/* This handler is to release the heap storage allocated within a function in case of errors.
 * The suggested protocol is that after a function is done with using the string buffer, the macro
 * MAXSTR_BUFF_FINI used in the function epilog releases the malloc'd storage. But if an error
 * occurs within the function body, the function epilog is not executed so this condition handler
 * should do the same so that the storage is not kept dangling. */
CONDITION_HANDLER(gtm_maxstr_ch)
{
	START_CH(TRUE);
	if (maxstr_buff[maxstr_stack_level].addr)
	{
		free(maxstr_buff[maxstr_stack_level].addr);
		maxstr_buff[maxstr_stack_level].addr = NULL;
	}
	maxstr_buff[maxstr_stack_level].len = 0;
	maxstr_stack_level--;
	NEXTCH;
}

/* The routine checks if the currently available buffer (either 32K automatic or >32K malloc'd)
 * is sufficient for the new space requirement. If not sufficient, it keeps doubling the buffer size
 * and checks until a new buffer size is large enough to accommodate the incoming string. It then
 * allocates the new buffer and releases the previously malloc'd buffer.
 * Note that if strings fit within 32K, the automating buffer is used and no heap storage is used.
 * Parameters:
 *
 * 	space_needed - buffer space needed to accommodate the string that is about to be written
 * 	buff - address of the pointer to the beginning of the buffer. If reallocation occurs, buff will be
 * 		modified to point to the reallocated buffer.
 * 	space_occupied - how full is the buffer?
 *
 * Returns the new allocated buffer size.
 */
int gtm_maxstr_alloc(int space_needed, char** buff, int space_occupied)
{
	int	new_buff_size;

	if (space_needed > (maxstr_buff[maxstr_stack_level].len - space_occupied))
	{ /* Existing buffer is not sufficient. reallocate the buffer with the double the size */
		new_buff_size = maxstr_buff[maxstr_stack_level].len;
		while (space_needed > new_buff_size - space_occupied)
			new_buff_size += new_buff_size;
		if (NULL != maxstr_buff[maxstr_stack_level].addr)
		{
			assert(maxstr_buff[maxstr_stack_level].addr == *buff);
			maxstr_buff[maxstr_stack_level].addr = (char *)malloc(new_buff_size);
			memcpy(maxstr_buff[maxstr_stack_level].addr, *buff, space_occupied);
			free(*buff);
		} else
		{
			maxstr_buff[maxstr_stack_level].addr = (char *)malloc(new_buff_size);
			memcpy(maxstr_buff[maxstr_stack_level].addr, *buff, space_occupied);
		}
		*buff = maxstr_buff[maxstr_stack_level].addr;
		maxstr_buff[maxstr_stack_level].len = new_buff_size;
	}
	return maxstr_buff[maxstr_stack_level].len;
}
