/****************************************************************
 *								*
 * Copyright (c) 2012-2017 Fidelity National Information	*
 * Services, Inc. and/or its subsidiaries. All rights reserved.	*
 *								*
 *	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.	*
 *								*
 ****************************************************************/

#ifndef PARM_POOL_H_INCLUDED
#define PARM_POOL_H_INCLUDED

#ifdef DEBUG
#  define PARM_POOL_INIT_CAP		2				/* Initial debug capacity */
#  define MAX_SET_COUNT			3				/* Max parameter sets stored at one time */
#  define MAX_TOTAL_SLOTS		129	/* CAPACITY_ROUND_UP2(PARM_POOL_INIT_CAP, (MAX_SET_COUNT * MAX_ACTUALS) + 1) */
#else
#  define PARM_POOL_INIT_CAP		8				/* Initial pro capacity */
#endif

#ifdef GTM64
#  define LV_VALS_PER_SLOT		1				/* 64: number of lv_vals that one slot fits */
#  define SLOTS_NEEDED_FOR_SET(parms)	(parms + 2)			/* 64: slots needed for params, frame, and mask_and_cnt */
#else
#  define LV_VALS_PER_SLOT		2				/* 32: number of lv_vals that one slot fits */
#  define SLOTS_NEEDED_FOR_SET(parms)	(parms / 2 + 2)			/* 32: slots needed for params, frame, and mask_and_cnt.
									 * Note that parms / 2 is used instead of (parms + 1) / 2
									 * because on 32-bit platforms if the number of params is
									 * even, then we need two additional slots for frame and
									 * mask_and_cnt; if odd, then the parameter not counted due
									 * to integer division will share the same slot with frame
									 * field, making this macro still correct */
#endif

/* The first vacant slot */
#define PARM_CURR_SLOT			((TREF(parm_pool_ptr))->parms + (TREF(parm_pool_ptr))->start_idx)

/* Frame field of the previous set */
#ifdef GTM64
#  define PARM_ACT_FRAME(curr_slot, count)	(*(curr_slot - 2)).frame
#else
#  define PARM_ACT_FRAME(curr_slot, count)	(*((&(*(curr_slot - 2)).frame) + ((count % 2) ? 1 : 0)))
#endif

/* This value has to be even, so that when count is increased by this value, it retains its divisibility by two. */
#define SAFE_TO_OVWRT                   (((MAX_ACTUALS + 1) % 2 == 0) ? (MAX_ACTUALS + 1) : (MAX_ACTUALS + 2))

/* round capacity up to the next power of 2 above or equal to min */
#define CAPACITY_ROUND_UP2(cap, min)	\
{					\
	while (cap < min)		\
		cap <<= 1;		\
}

/* Reclaim the space used by the parameter set stored last, because the corresponding frame is being rewound. */
#define PARM_ACT_UNSTACK_IF_NEEDED									\
{													\
	int 		count;										\
	parm_slot	*curr_slot;									\
	if (TREF(parm_pool_ptr) && (0 < (TREF(parm_pool_ptr))->start_idx))				\
	{												\
		curr_slot = PARM_CURR_SLOT;								\
		count = (*(curr_slot - 1)).mask_and_cnt.actualcnt; 					\
		if (PARM_ACT_FRAME(curr_slot, count) == frame_pointer)					\
			(TREF(parm_pool_ptr))->start_idx 						\
				-= (SLOTS_NEEDED_FOR_SET(((MAX_ACTUALS < count) 			\
				? (count - SAFE_TO_OVWRT) : count)));					\
	}												\
}

typedef union
{
	struct
	{
		uint4			mask;		/* Mask for the param list (alias/non-alias) of up to 32 params */
		unsigned int		actualcnt;	/* Number of parameters in the parameter set */
	} mask_and_cnt;
	struct stack_frame_struct 	*frame;		/* Pointer to the frame that the parameter set corresponds to */
	lv_val				*actuallist;	/* On 32-bit platforms this field is 4 bytes, so when the number of
							 * parameters is odd, a frame value is stored in the second half of
							 * this union; otherwise, frame is stored in a separate slot, thus
							 * leaving 4 bytes unused. For details, refer to parm_pool.c.
							 */
} parm_slot;

typedef struct
{
	uint4		capacity;
	uint4		start_idx;
	parm_slot	parms[1];
} parm_pool;

STATICFNDCL void parm_pool_init(unsigned int init_capacity);
STATICFNDCL void parm_pool_expand(int slots_needed, int slots_copied);

#endif	/* PARM_POOL_H_INCLUDED */
