/****************************************************************
 *								*
 * Copyright (c) 2013-2015 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.	*
 *								*
 ****************************************************************/

#include "mdef.h"

#include "gtm_string.h"

#include "cdb_sc.h"
#include "gdsroot.h"
#include "gdsblk.h"
#include "gtm_facility.h"
#include "fileinfo.h"
#include "gdsbt.h"
#include "gdsfhead.h"
#include "filestruct.h"
#include "jnl.h"
#include "gdsblkops.h"
#include "gdskill.h"
#include "gdscc.h"
#include "copy.h"
#include "mu_reorg.h"
#include "util.h"
#include "min_max.h"

GBLREF 	sgmnt_data_ptr_t 	cs_data;
GBLREF	unsigned int		t_tries;

#ifdef DEBUG
# define DBG_VERIFY_ACCESS(PTR)				\
{	/* Ensure accessible pointer (no SIG-11) */	\
	unsigned char	c;				\
							\
	c = *(unsigned char *)(PTR);			\
}
#else
# define DBG_VERIFY_ACCESS(PTR)
#endif

/*
 * 	Get length of the global variable name contained in the key starting at KEY_BASE.
 *	NOTE: If keys reside outside a GDS block (e.g. in cs_data), allocated buffer should have capacity at least MAX_KEY_SZ.
 * 	Input:
 * 		BLK_BASE := 	if non-NULL, base of current block
 * 				if NULL, it means we're dealing with a key that is not within a database block
 *		KEY_BASE :=	starting address of key
 * 	Output:
 * 		GBLNAME_LEN := 	length of global variable name
 */
int get_gblname_len(sm_uc_ptr_t blk_base, sm_uc_ptr_t key_base)
{
	sm_uc_ptr_t		rPtr1, blk_end;

	blk_end = (NULL != blk_base) ? (blk_base + cs_data->blk_size) : (key_base + MAX_KEY_SZ);
	DBG_VERIFY_ACCESS(blk_end - 1);
	for (rPtr1 = key_base; ; )
	{
		if (blk_end <= rPtr1)
			break;
		if (KEY_DELIMITER == *rPtr1++)
			break;
	}
	return (int)(rPtr1 - key_base);
}

/*
 * 	Get length of the key starting at KEY_BASE.
 *	NOTE: If keys reside outside a GDS block (e.g. in cs_data), allocated buffer should have capacity at least MAX_KEY_SZ.
 * 	Currently, all get_key_len() calls take a key in shared memory; the only "allocated buffer" is csd->reorg_restart_key.
 *	Input:
 * 		BLK_BASE := 	if non-NULL, base of current block
 * 				if NULL, it means we're dealing with a key that is not within a database block
 *		KEY_BASE :=	starting address of key
 * 	Output:
 * 		KEY_LEN	:= 	length of key, including 2 null bytes at the end
 */
int get_key_len(sm_uc_ptr_t blk_base, sm_uc_ptr_t key_base)
{
	sm_uc_ptr_t		rPtr1, blk_end;

	blk_end = (NULL != blk_base) ? (blk_base + cs_data->blk_size) : (key_base + MAX_KEY_SZ);
	DBG_VERIFY_ACCESS(blk_end - 1);
	for (rPtr1 = key_base; ; )
	{
		if (blk_end <= rPtr1 + 1)
			break;
		if ((KEY_DELIMITER == *rPtr1++) && (KEY_DELIMITER == *rPtr1))
			break;
	}
	return (int)(rPtr1 + 1 - key_base);
}

/*
 *	Get compression count of SECOND_KEY with respect to FIRST_KEY.
 * 	NOTE: Each key should reside in a private buffer with capacity at least MAX_KEY_SZ.
 */
int get_cmpc(sm_uc_ptr_t first_key, sm_uc_ptr_t second_key)
{
	sm_uc_ptr_t		rPtr1, rPtr2;
	int			cmpc;

	DBG_VERIFY_ACCESS(first_key + MAX_KEY_SZ - 1);
	DBG_VERIFY_ACCESS(second_key + MAX_KEY_SZ - 1);
	/* We don't expect the inputs to be equal, hence the assert. It shouldn't matter, though. If the keys' contents are equal,
	 * we return an indeterminate value between the key length and MAX_KEY_SZ. The value depends on the garbage bytes past the
	 * terminating null bytes. But we don't care because we don't compress a key off an identical key in the final retry.
	 */
	assert(first_key != second_key);
	for (rPtr1 = first_key, rPtr2 = second_key, cmpc = 0; cmpc < MAX_KEY_SZ; cmpc++)
	{
		if (*rPtr1++ != *rPtr2++)
			break;
	}
	return cmpc;
}

/*
 * 	Copy record info (record size and key) out of a block.
 *	Input:
 *		LEVEL :=	level of current block
 *		BLK_BASE := 	base of current block
 *		REC_BASE :=	starting address of record in current block
 * 		KEY := 		previous key; first KEY_CMPC bytes are retained in output KEY
 *	Output:
 *		STATUS := 	status of read
 *		REC_SIZE :=	record size
 *		KEY_CMPC := 	key compression count
 *		KEY_LEN := 	key length
 *		KEY :=		local copy of key copied out of record
 */
enum cdb_sc read_record(int *rec_size_ptr, int *key_cmpc_ptr, int *key_len_ptr, sm_uc_ptr_t key,
		int level, srch_blk_status *blk_stat, sm_uc_ptr_t rec_base)
{
	sm_uc_ptr_t	rPtr1, rPtr2, blk_end, rPtr1_end, rPtr2_end;
	unsigned short	temp_ushort;
	int		key_cmpc, rec_size, key_len;
	boolean_t	invalid;
	sm_uc_ptr_t blk_base;

	blk_base = blk_stat->buffaddr;
	blk_end = blk_base + cs_data->blk_size;
	DBG_VERIFY_ACCESS(blk_end - 1);
	if (blk_end <= (rec_base + SIZEOF(rec_hdr)))
	{
		assert(CDB_STAGNATE > t_tries);
		NONTP_TRACE_HIST_MOD(blk_stat, t_blkmod_reorg_funcs);
		return cdb_sc_blkmod;
	}
	GET_USHORT(temp_ushort, &(((rec_hdr_ptr_t)rec_base)->rsiz));
	rec_size = temp_ushort;
	key_cmpc = EVAL_CMPC((rec_hdr_ptr_t)rec_base);
	if ((0 != level) && (BSTAR_REC_SIZE == rec_size))
	{
		key_len = 0;
		*key_cmpc_ptr = key_cmpc;
		*rec_size_ptr = rec_size;
		*key_len_ptr = key_len;
		return cdb_sc_starrecord;
	}
	rPtr1_end = key + MAX_KEY_SZ - 1;
	DBG_VERIFY_ACCESS(rPtr1_end);
	rPtr2_end = MIN(blk_end - 1, rec_base + SIZEOF(rec_hdr) + MAX_KEY_SZ - 1);
	for (rPtr1 = key + key_cmpc, rPtr2 = rec_base + SIZEOF(rec_hdr); (rPtr1 < rPtr1_end) && (rPtr2 < rPtr2_end); )
	{
		if ((KEY_DELIMITER == (*rPtr1++ = *rPtr2++)) && (KEY_DELIMITER == *rPtr2))	/* note assignment */
			break;
	}
	*rPtr1++ = *rPtr2++;
	key_len = (int)(rPtr2 - rec_base - SIZEOF(rec_hdr));
	invalid = INVALID_RECORD(level, rec_size, key_len, key_cmpc);
	if (invalid || ((KEY_DELIMITER != *(rPtr1 - 1)) || (KEY_DELIMITER != *(rPtr1 - 2))))
	{
		assert(CDB_STAGNATE > t_tries);
		NONTP_TRACE_HIST_MOD(blk_stat, t_blkmod_reorg_funcs);
		return cdb_sc_blkmod;
	}
	*key_cmpc_ptr = key_cmpc;
	*rec_size_ptr = rec_size;
	*key_len_ptr = key_len;
	return cdb_sc_normal;
}
