/****************************************************************
 *								*
 *	Copyright 2001, 2012 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.	*
 *								*
 ****************************************************************/

/***********************************************************************************
mu_split.c:
	Split a block on the boundary of fill_factor.
	Split ancestors's if necessary. Ancestor's split will also honor fill_factor
 ***********************************************************************************/

#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 "gdsblkops.h"
#include "gdskill.h"
#include "gdscc.h"
#include "jnl.h"
#include "copy.h"
#include "muextr.h"
#include "mu_reorg.h"

/* Include prototypes */
#include "t_qread.h"
#include "t_write.h"
#include "t_write_root.h"
#include "t_create.h"
#include "mupip_reorg.h"

GBLREF gv_namehead	*gv_target;
GBLREF sgmnt_data_ptr_t	cs_data;
GBLREF gd_region        *gv_cur_region;
GBLREF char		*update_array, *update_array_ptr;
GBLREF uint4		update_array_size;	/* for the BLK_* macros */
GBLREF cw_set_element	cw_set[];
GBLREF unsigned char	cw_set_depth;
GBLREF unsigned int     t_tries;
GBLREF gv_key           *gv_currkey;
GBLREF gv_key           *gv_currkey_next_reorg;

static int4 const   	zeroes = 0;
enum cdb_sc locate_block_split_point(sm_uc_ptr_t blk_base, int level, int cur_blk_size, int max_fill,
int *last_rec_size, unsigned char last_key[], int *last_keysz, int *top_off);


/***********************************************************************************************
	Input Parameters:
		cur_level: Working block's level
		d_max_fill: Database fill factor
		i_max_fill: Index fill factor
	Output Parameters:
		blks_created: how many new blocks are created
		lvls_increased : How much level is increased
	Input/Output Parameters:
		gv_target: History of working block
	Here it is assumed that i_max_fill or, d_max_fill is strictly less than block size.
	Returns:
		cdb_sc_normal: if successful
		cdb_sc status otherwise
 ************************************************************************************************/
enum cdb_sc mu_split(int cur_level, int i_max_fill, int d_max_fill, int *blks_created, int *lvls_increased)
{
	boolean_t	first_copy, new_rtblk_star_only, create_root = FALSE, split_required, insert_in_left;
	unsigned char	curr_prev_key[MAX_KEY_SZ+1], new_blk1_last_key[MAX_KEY_SZ+1];
	unsigned short  temp_ushort;
	int		rec_size, new_ins_keycmpc, tkeycmpc, new_ances_currkeycmpc, old_ances_currkeycmpc;
	int		tmp_cmpc;
	block_index	left_index, right_index;
	block_offset 	ins_off, ins_off2;
	int		level;
	int		new_ins_keysz, new_ances_currkeysz, new_blk1_last_keysz, newblk2_first_keysz, next_gv_currkeysz;
	int		old_ances_currkeylen, new_ins_keylen, new_ances_currkeylen, tkeylen, newblk2_first_keylen;
	int		old_blk1_last_rec_size, old_blk1_sz, save_blk_piece_len, old_right_piece_len;
	int		delta, max_fill;
	enum cdb_sc	status;
	int		blk_seg_cnt, blk_size, new_leftblk_top_off;
	block_id	allocation_clue;
	sm_uc_ptr_t 	rPtr1, rPtr2, rec_base, key_base, next_gv_currkey,
			bn_ptr1, bn_ptr2, save_blk_piece,
			old_blk_after_currec, ances_currkey,
			old_blk1_base,
			new_blk1_top, new_blk2_top,
			new_blk2_frec_base, new_blk2_rem,
			newblk2_first_key, new_ins_key;
	blk_segment     *bs_ptr1, *bs_ptr2;
	cw_set_element  *cse;
	rec_hdr_ptr_t	star_rec_hdr, new_rec_hdr1a, new_rec_hdr1b, new_rec_hdr2, root_hdr;
	blk_hdr_ptr_t	blk_hdr_ptr;

	blk_size = cs_data->blk_size;
	CHECK_AND_RESET_UPDATE_ARRAY;	/* reset update_array_ptr to update_array */

	BLK_ADDR(star_rec_hdr, SIZEOF(rec_hdr), rec_hdr);
	star_rec_hdr->rsiz = BSTAR_REC_SIZE;
	SET_CMPC(star_rec_hdr, 0);
	level = cur_level;
	max_fill = (0 == level)? d_max_fill : i_max_fill;

	/*  -------------------
	 *  Split working block.
	 *  -------------------
	 *  new_blk1_last_key = last key of the new working block after split
	 *  new_blk1_last_keysz = size of new_blk1_last_key
	 *  old_blk1_last_rec_size = last record size of the new working block after split (for old block)
	 *  new_blk2_frec_base = base of first record of right block created after split
	 *  newblk2_first_key = first key of new block created after split
	 *  newblk2_first_keysz = size of newblk2_first_key
	 *  new_blk2_rem = pointer to new block to be created after split exclude 1st record header + key
	 */
	blk_hdr_ptr = (blk_hdr_ptr_t)(gv_target->hist.h[level].buffaddr);
	old_blk1_base = (sm_uc_ptr_t)blk_hdr_ptr;
	old_blk1_sz = blk_hdr_ptr->bsiz;
	new_blk2_top = old_blk1_base + old_blk1_sz;
	if (cdb_sc_normal != (status = locate_block_split_point (old_blk1_base, level, old_blk1_sz, max_fill,
		&old_blk1_last_rec_size, new_blk1_last_key, &new_blk1_last_keysz, &new_leftblk_top_off)))
	{
		assert(t_tries < CDB_STAGNATE);
		return cdb_sc_blkmod;
	}
	if (new_leftblk_top_off + BSTAR_REC_SIZE >= old_blk1_sz)
		/* Avoid split to create a small right sibling. Note this should not happen often when tolerance is high */
		return cdb_sc_oprnotneeded;
	old_right_piece_len = old_blk1_sz - new_leftblk_top_off;
	new_blk2_frec_base = old_blk1_base + new_leftblk_top_off;
	BLK_ADDR(newblk2_first_key, gv_cur_region->max_rec_size + 1, unsigned char);
	READ_RECORD(level, new_blk2_frec_base, tkeycmpc, rec_size, newblk2_first_key, newblk2_first_keylen, status);
	if (cdb_sc_normal != status) /* restart for cdb_sc_starrecord too, because we eliminated the possibility already */
	{
		assert(t_tries < CDB_STAGNATE);
		return cdb_sc_blkmod;
	}
	memcpy(newblk2_first_key, &new_blk1_last_key[0], tkeycmpc); /* copy the compressed key piece */
	new_blk2_rem = new_blk2_frec_base + SIZEOF(rec_hdr) + newblk2_first_keylen;
	newblk2_first_keysz = newblk2_first_keylen + tkeycmpc;

	/* gv_currkey_next_reorg will be saved for next iteration in mu_reorg */
	next_gv_currkey = newblk2_first_key;
	next_gv_currkeysz = newblk2_first_keysz;

	BLK_ADDR(new_rec_hdr1b, SIZEOF(rec_hdr), rec_hdr);
	new_rec_hdr1b->rsiz = rec_size + tkeycmpc;
	SET_CMPC(new_rec_hdr1b, 0);

	/* Create new split piece, we already know that this will not be *-rec only.
	 * Note that this has to be done BEFORE modifying working block as building this buffer relies on the
	 * working block to be pinned which is possible only if this cw-set-element is created ahead of that
	 * of the working block (since order in which blocks are built is the order in which cses are created).
	 */
	BLK_INIT(bs_ptr2, bs_ptr1);
	BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_rec_hdr1b, SIZEOF(rec_hdr));
	BLK_SEG(bs_ptr2, newblk2_first_key, newblk2_first_keysz);
	BLK_SEG(bs_ptr2, new_blk2_rem, new_blk2_top - new_blk2_rem);
	if (!BLK_FINI(bs_ptr2, bs_ptr1))
	{
		assert(t_tries < CDB_STAGNATE);
		return cdb_sc_blkmod;
	}
        allocation_clue = ALLOCATION_CLUE(cs_data->trans_hist.total_blks);
	right_index = t_create(allocation_clue++, (unsigned char *)bs_ptr1, 0, 0, level);
	(*blks_created)++;

	/* Modify working block removing split piece */
	BLK_INIT(bs_ptr2, bs_ptr1);
	if (0 == level)
	{
		BLK_SEG(bs_ptr2, old_blk1_base + SIZEOF(blk_hdr), new_leftblk_top_off - SIZEOF(blk_hdr));
	}
	else
	{
		BLK_SEG(bs_ptr2, old_blk1_base + SIZEOF(blk_hdr),
			new_leftblk_top_off - SIZEOF(blk_hdr) - old_blk1_last_rec_size);
		BLK_SEG(bs_ptr2, (sm_uc_ptr_t)star_rec_hdr, SIZEOF(rec_hdr) );
		BLK_ADDR(bn_ptr1, SIZEOF(block_id), unsigned char);
		memcpy(bn_ptr1, old_blk1_base + new_leftblk_top_off - SIZEOF(block_id), SIZEOF(block_id));
		BLK_SEG(bs_ptr2, bn_ptr1, SIZEOF(block_id));
	}
	if ( !BLK_FINI(bs_ptr2, bs_ptr1))
	{
		assert(t_tries < CDB_STAGNATE);
		return cdb_sc_blkmod;
	}
	t_write(&gv_target->hist.h[level], (unsigned char *)bs_ptr1, 0, 0, level, FALSE, TRUE, GDS_WRITE_KILLTN);

	/*
	----------------------------------------------------------------------------
	Modify ancestor block for the split in current level.
	new_ins_key = new key to be inserted in parent because of split in child
	new_ins_key will be inserted after gv_target->hist.h[level].prev_rec and
	                            before gv_target->hist.h[level].curr_rec
	    new_ins_keysz = size of new_ins_key
	    Note: A restriction of the algorithm is to have current key and new_ins_key
		in the same block, either left or, new right block
	----------------------------------------------------------------------------
	*/
	BLK_ADDR(new_ins_key, new_blk1_last_keysz, unsigned char);
	memcpy(new_ins_key, &new_blk1_last_key[0], new_blk1_last_keysz);
	new_ins_keysz = new_blk1_last_keysz;
	for(;;) 	/* ========== loop through ancestors as necessary ======= */
	{
		level ++;
		max_fill = i_max_fill;
		/*
		old_blk_after_currec = remaining of current block after currec
		ances_currkey = old real value of currkey in ancestor block
		*/
		blk_hdr_ptr = (blk_hdr_ptr_t)(gv_target->hist.h[level].buffaddr);
		old_blk1_base = (sm_uc_ptr_t)blk_hdr_ptr;
		old_blk1_sz = blk_hdr_ptr->bsiz;
		new_blk2_top = old_blk1_base + old_blk1_sz;
		rec_base = old_blk1_base + gv_target->hist.h[level].curr_rec.offset;
		GET_RSIZ(rec_size, rec_base);
		old_blk_after_currec = rec_base + rec_size;
		old_ances_currkeycmpc = EVAL_CMPC((rec_hdr_ptr_t)rec_base);
		old_ances_currkeylen = rec_size - BSTAR_REC_SIZE;
		if (INVALID_RECORD(level, rec_size,  old_ances_currkeylen, old_ances_currkeycmpc))
		{
			assert(t_tries < CDB_STAGNATE);
			return cdb_sc_blkmod;
		}
		if (0 == old_ances_currkeylen)
		{
			if (0 != old_ances_currkeycmpc)
			{
				assert(t_tries < CDB_STAGNATE);
				return cdb_sc_blkmod;
			}
			new_ances_currkeycmpc = new_ances_currkeylen = 0;
		}
		else
		{
			BLK_ADDR(ances_currkey, gv_cur_region->max_rec_size + 1, unsigned char);
			key_base = rec_base +  SIZEOF(rec_hdr);
		}
		new_ances_currkeysz = old_ances_currkeycmpc + old_ances_currkeylen;
		if (SIZEOF(blk_hdr) != gv_target->hist.h[level].curr_rec.offset) /* cur_rec is not first key */
		{
			if (cdb_sc_normal != (status = gvcst_expand_any_key(old_blk1_base,
				old_blk1_base + gv_target->hist.h[level].curr_rec.offset,
				&curr_prev_key[0], &rec_size, &tkeylen, &tkeycmpc, NULL)))
			{
				assert(t_tries < CDB_STAGNATE);
				return cdb_sc_blkmod;
			}
			if (old_ances_currkeycmpc)
				memcpy(ances_currkey, &curr_prev_key[0], old_ances_currkeycmpc);
		}
		if (old_ances_currkeylen)
		{
			memcpy(ances_currkey + old_ances_currkeycmpc, key_base, old_ances_currkeylen);
			GET_CMPC(new_ances_currkeycmpc, new_ins_key, ances_currkey);
			new_ances_currkeylen = new_ances_currkeysz - new_ances_currkeycmpc;
		}
		if (SIZEOF(blk_hdr) != gv_target->hist.h[level].curr_rec.offset)
		{
			/* new_ins_key will be inseted after curr_prev_key */
			GET_CMPC(new_ins_keycmpc, &curr_prev_key[0], new_ins_key);
		}
		else
			new_ins_keycmpc = 0; /* new_ins_key will be the 1st key */
		new_ins_keylen = new_ins_keysz - new_ins_keycmpc ;

		delta = BSTAR_REC_SIZE + new_ins_keylen - old_ances_currkeylen + new_ances_currkeylen;
		if (old_blk1_sz + delta > blk_size - cs_data->reserved_bytes) /* split required */
		{
			split_required = TRUE;
			if (level == gv_target->hist.depth)
			{
				create_root = TRUE;
				if (MAX_BT_DEPTH - 1 <= level)  /* maximum level reached */
					return cdb_sc_maxlvl;
			}
			if (max_fill + BSTAR_REC_SIZE > old_blk1_sz)
			{
				if (SIZEOF(blk_hdr) + BSTAR_REC_SIZE == old_blk1_sz)
					return cdb_sc_oprnotneeded; /* Improve code to avoid this */
				max_fill = old_blk1_sz - BSTAR_REC_SIZE;
			}
			status = locate_block_split_point(old_blk1_base, level, old_blk1_sz, max_fill,
				&old_blk1_last_rec_size, new_blk1_last_key, &new_blk1_last_keysz, &new_leftblk_top_off);
			if (cdb_sc_normal != status || new_leftblk_top_off >= old_blk1_sz
				|| 0 == new_blk1_last_keysz)
			{
				assert(t_tries < CDB_STAGNATE);
				return cdb_sc_blkmod;
			}
			assert(BSTAR_REC_SIZE != old_blk1_last_rec_size);
			old_right_piece_len = old_blk1_sz - new_leftblk_top_off;
			new_blk2_frec_base = new_blk1_top = old_blk1_base + new_leftblk_top_off;
			if (BSTAR_REC_SIZE == old_right_piece_len)
				new_rtblk_star_only = TRUE;
			else
				new_rtblk_star_only = FALSE;
			if (new_leftblk_top_off == gv_target->hist.h[level].curr_rec.offset)
			{
				/* inserted key will be the first record of new right block */
				new_ins_keylen = new_ins_keysz;
				new_ins_keycmpc = 0;
			}
			else
				/* process 1st record of new right block */
			{
				BLK_ADDR(newblk2_first_key, gv_cur_region->max_rec_size + 1, unsigned char);
				READ_RECORD(level, new_blk2_frec_base, tkeycmpc, rec_size,
						newblk2_first_key, newblk2_first_keylen, status);
				if (cdb_sc_normal == status)
				{
					memcpy(newblk2_first_key, &new_blk1_last_key[0], tkeycmpc); /* compressed piece */
					new_blk2_rem =  new_blk2_frec_base + SIZEOF(rec_hdr) + newblk2_first_keylen;
					newblk2_first_keysz = newblk2_first_keylen + tkeycmpc;
					BLK_ADDR(new_rec_hdr2, SIZEOF(rec_hdr), rec_hdr);
					new_rec_hdr2->rsiz = newblk2_first_keysz + BSTAR_REC_SIZE;
					SET_CMPC(new_rec_hdr2, 0);
				}
				else if (cdb_sc_starrecord != status || !new_rtblk_star_only)
				{
					assert(t_tries < CDB_STAGNATE);
					return cdb_sc_blkmod;
				}
			}
			/* else gv_target->hist.h[level].curr_rec will be newblk2_first_key */

			if (new_leftblk_top_off >  gv_target->hist.h[level].curr_rec.offset +
				old_ances_currkeylen + BSTAR_REC_SIZE)
			{
				/* in this case prev_rec (if exists), new key and curr_rec should go into left block */
				if (new_leftblk_top_off + delta - old_blk1_last_rec_size + BSTAR_REC_SIZE
					<= blk_size - cs_data->reserved_bytes)
					insert_in_left = TRUE;
				else
				{
					/* cannot handle it now */
					return cdb_sc_oprnotneeded;
				}
			}
			else if (new_leftblk_top_off <  gv_target->hist.h[level].curr_rec.offset +
				old_ances_currkeylen + BSTAR_REC_SIZE)
			{
				/* if gv_target->hist.h[level].curr_rec is the first key in old_blk1
				   then in new right block,
				   	new_ins_key will be the 1st record key and
					curr_rec will be 2nd record and
					there will be no prev_rec in right block.
				   Else (if curr_rec is not first key)
					there will be some records before new_ins_key, at least prev_rec */
				delta = (int)(BSTAR_REC_SIZE + new_ins_keylen
					- old_ances_currkeylen + new_ances_currkeylen
					+ ((0 == new_ins_keycmpc) ? 0 : (EVAL_CMPC((rec_hdr_ptr_t)new_blk2_frec_base))));
				if (SIZEOF(blk_hdr) + old_right_piece_len + delta <= blk_size - cs_data->reserved_bytes)
				{
					insert_in_left = FALSE;
					if (new_leftblk_top_off + BSTAR_REC_SIZE >= old_blk1_sz)
					{
						/* cannot handle it now */
						return cdb_sc_oprnotneeded;
					}
				}
				else
				{
					/* cannot handle it now */
					return cdb_sc_oprnotneeded;
				}
			}
			else
			{
				/* in this case prev_rec (if exists), new key and curr_rec should go into left block
					and curr_rec will be the last record (*-key) of left new block */
				delta = BSTAR_REC_SIZE + new_ins_keylen;
				if (new_leftblk_top_off + delta <= blk_size - cs_data->reserved_bytes)
					insert_in_left = TRUE;
				else
				{
					/* cannot handle it now */
					return cdb_sc_oprnotneeded;
				}
			}
		} /* end if split required */
		else
			split_required = FALSE;
		BLK_ADDR(new_rec_hdr1a, SIZEOF(rec_hdr), rec_hdr);
		new_rec_hdr1a->rsiz = BSTAR_REC_SIZE + new_ins_keylen;
		SET_CMPC(new_rec_hdr1a, new_ins_keycmpc);
		BLK_ADDR(new_rec_hdr1b, SIZEOF(rec_hdr), rec_hdr);
		new_rec_hdr1b->rsiz = BSTAR_REC_SIZE + new_ances_currkeylen;
		SET_CMPC(new_rec_hdr1b, new_ances_currkeycmpc);
		BLK_ADDR(bn_ptr1, SIZEOF(block_id), unsigned char);
		/* child pointer of ances_currkey */
		memcpy(bn_ptr1, old_blk1_base + gv_target->hist.h[level].curr_rec.offset +
			SIZEOF(rec_hdr) + old_ances_currkeylen, SIZEOF(block_id));
		if (!split_required)
		{
			/* LEFT part of old BLOCK */
			BLK_INIT(bs_ptr2, bs_ptr1);
			if (SIZEOF(blk_hdr) < gv_target->hist.h[level].curr_rec.offset)
			{
				BLK_SEG(bs_ptr2, old_blk1_base + SIZEOF(blk_hdr),
					gv_target->hist.h[level].curr_rec.offset - SIZEOF(blk_hdr));
				first_copy = FALSE;
			} else
				first_copy = TRUE;
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_rec_hdr1a, SIZEOF(rec_hdr));
			BLK_SEG(bs_ptr2, new_ins_key + new_ins_keycmpc, new_ins_keylen);
			BLK_SEG(bs_ptr2, bn_ptr1, SIZEOF(block_id));
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_rec_hdr1b, SIZEOF(rec_hdr));
			if (0 < new_ances_currkeylen)
				BLK_SEG(bs_ptr2, ances_currkey + new_ances_currkeycmpc, new_ances_currkeylen);
			ins_off = blk_seg_cnt;
			BLK_SEG(bs_ptr2, (unsigned char *)&zeroes, SIZEOF(block_id));
			if (0 < old_blk1_base + old_blk1_sz - old_blk_after_currec)
				BLK_SEG(bs_ptr2, old_blk_after_currec,  old_blk1_base + old_blk1_sz - old_blk_after_currec);
			if (!BLK_FINI(bs_ptr2, bs_ptr1))
			{
				assert(t_tries < CDB_STAGNATE);
				return cdb_sc_blkmod;
			}
			t_write(&gv_target->hist.h[level], (unsigned char *)bs_ptr1, ins_off, right_index,
				level, first_copy, FALSE, GDS_WRITE_KILLTN);
			break;
		}
		/* if SPLIT REQUIRED */
		if (insert_in_left) /* new_ins_key will go to left block */
		{
			/* LEFT BLOCK */
			BLK_INIT(bs_ptr2, bs_ptr1);
			if (SIZEOF(blk_hdr) < gv_target->hist.h[level].curr_rec.offset)
			{
				BLK_SEG(bs_ptr2, old_blk1_base + SIZEOF(blk_hdr),
					gv_target->hist.h[level].curr_rec.offset - SIZEOF(blk_hdr));
				first_copy = FALSE;
			} else
				first_copy = TRUE;
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_rec_hdr1a, SIZEOF(rec_hdr));
			BLK_SEG(bs_ptr2, new_ins_key + new_ins_keycmpc, new_ins_keylen);
			BLK_SEG(bs_ptr2, bn_ptr1, SIZEOF(block_id));
			if (old_blk_after_currec < new_blk1_top) /* curr_rec is not the last record of new left block */
			{
				BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_rec_hdr1b, SIZEOF(rec_hdr));
				BLK_SEG(bs_ptr2, ances_currkey + new_ances_currkeycmpc, new_ances_currkeylen);
				ins_off = blk_seg_cnt;
				BLK_SEG(bs_ptr2, (unsigned char *)&zeroes, SIZEOF(block_id));
				save_blk_piece_len = (int)(new_blk1_top - old_blk1_last_rec_size - old_blk_after_currec);
				if (0 < save_blk_piece_len )
				{
 					if (old_blk_after_currec + save_blk_piece_len >= new_blk2_top)
					{
						assert(t_tries < CDB_STAGNATE);
						return cdb_sc_blkmod;
					}
					BLK_ADDR(save_blk_piece, save_blk_piece_len, unsigned char);
					memcpy(save_blk_piece, old_blk_after_currec, save_blk_piece_len);
					BLK_SEG(bs_ptr2, save_blk_piece, save_blk_piece_len);
				}
				BLK_SEG(bs_ptr2, (sm_uc_ptr_t)star_rec_hdr, SIZEOF(rec_hdr) );
				BLK_ADDR(bn_ptr2, SIZEOF(block_id), unsigned char);
				memcpy(bn_ptr2, new_blk1_top - SIZEOF(block_id), SIZEOF(block_id));
				BLK_SEG(bs_ptr2, bn_ptr2, SIZEOF(block_id));
			} else
			{
				assert (old_blk_after_currec == new_blk1_top);
				BLK_SEG(bs_ptr2, (sm_uc_ptr_t)star_rec_hdr, SIZEOF(rec_hdr) );
                                ins_off = blk_seg_cnt;
                                BLK_SEG(bs_ptr2, (unsigned char *)&zeroes, SIZEOF(block_id));

			}
			if (!BLK_FINI(bs_ptr2, bs_ptr1))
			{
				assert(t_tries < CDB_STAGNATE);
				return cdb_sc_blkmod;
			}
			if (create_root)
				left_index = t_create(allocation_clue++, (unsigned char *)bs_ptr1, ins_off, right_index, level);
			else
				t_write(&gv_target->hist.h[level], (unsigned char *)bs_ptr1, ins_off, right_index,
					level, first_copy, FALSE, GDS_WRITE_KILLTN);
			/* RIGHT BLOCK */
			BLK_INIT(bs_ptr2, bs_ptr1);
			if (new_rtblk_star_only)
			{
				BLK_SEG(bs_ptr2, (sm_uc_ptr_t)star_rec_hdr, SIZEOF(rec_hdr) );
				BLK_ADDR(bn_ptr2, SIZEOF(block_id), unsigned char);
				memcpy(bn_ptr2, new_blk2_top - SIZEOF(block_id), SIZEOF(block_id));
				BLK_SEG(bs_ptr2, bn_ptr2, SIZEOF(block_id));
			} else
			{
				BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_rec_hdr2, SIZEOF(rec_hdr));
				BLK_SEG(bs_ptr2, newblk2_first_key, newblk2_first_keysz);
				save_blk_piece_len = (int)(new_blk2_top - new_blk2_rem);
				if (0 > save_blk_piece_len)
				{
					assert(t_tries < CDB_STAGNATE);
					return cdb_sc_blkmod;
				}
				BLK_ADDR(save_blk_piece, save_blk_piece_len, unsigned char);
				memcpy(save_blk_piece, new_blk2_rem, save_blk_piece_len);
				BLK_SEG(bs_ptr2, save_blk_piece, new_blk2_top - new_blk2_rem );
			}
			if (!BLK_FINI(bs_ptr2, bs_ptr1))
			{
				assert(t_tries < CDB_STAGNATE);
				return cdb_sc_blkmod;
			}
			right_index = t_create(allocation_clue++, (unsigned char *)bs_ptr1, 0, 0, level);
			(*blks_created)++;
		} /* end if insert_in_left */
		else
		{	/* new_ins_key to be inserted in right block */
			/* LEFT BLOCK */
			BLK_INIT(bs_ptr2, bs_ptr1);
			save_blk_piece_len = (int)(new_leftblk_top_off - SIZEOF(blk_hdr) - old_blk1_last_rec_size);
			if ((old_blk1_base + SIZEOF(blk_hdr) + save_blk_piece_len >= new_blk2_top) || (0 > save_blk_piece_len))
			{
				assert(t_tries < CDB_STAGNATE);
				return cdb_sc_blkmod;
			}
			BLK_ADDR(save_blk_piece, save_blk_piece_len, unsigned char);
			memcpy(save_blk_piece, old_blk1_base + SIZEOF(blk_hdr), save_blk_piece_len);
			BLK_SEG(bs_ptr2, save_blk_piece, save_blk_piece_len);
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)star_rec_hdr, SIZEOF(rec_hdr) );
			BLK_ADDR(bn_ptr2, SIZEOF(block_id), unsigned char);
			memcpy(bn_ptr2, old_blk1_base + new_leftblk_top_off - SIZEOF(block_id), SIZEOF(block_id));
			BLK_SEG(bs_ptr2, bn_ptr2, SIZEOF(block_id));
			if ( !BLK_FINI(bs_ptr2, bs_ptr1))
			{
				assert(t_tries < CDB_STAGNATE);
				return cdb_sc_blkmod;
			}
			if (create_root)
				left_index = t_create(allocation_clue++, (unsigned char *)bs_ptr1, 0, 0, level);
			else
				t_write(&gv_target->hist.h[level], (unsigned char *)bs_ptr1, 0, 0,
					level, TRUE, TRUE, GDS_WRITE_KILLTN);
			/* RIGHT BLOCK */
			BLK_INIT(bs_ptr2, bs_ptr1);
			if (new_leftblk_top_off < gv_target->hist.h[level].curr_rec.offset)
			{	/* anything before curr_rec */
				BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_rec_hdr2, SIZEOF(rec_hdr));
				BLK_SEG(bs_ptr2, newblk2_first_key, newblk2_first_keysz);
				save_blk_piece_len = (int)(gv_target->hist.h[level].curr_rec.offset -
					new_leftblk_top_off  - (new_blk2_rem - new_blk2_frec_base));
				if ((new_blk2_rem + save_blk_piece_len >= new_blk2_top) || (0 > save_blk_piece_len))
				{
					assert(t_tries < CDB_STAGNATE);
					return cdb_sc_blkmod;
				}
				BLK_ADDR(save_blk_piece, save_blk_piece_len, unsigned char);
				memcpy(save_blk_piece, new_blk2_rem, save_blk_piece_len);
				BLK_SEG(bs_ptr2, save_blk_piece, save_blk_piece_len);
			}
			/* Following else if may not be necessary. But I wanted it to be safe:Layek:10/3/2000 */
			else if (new_leftblk_top_off > gv_target->hist.h[level].curr_rec.offset)
			{
				assert(t_tries < CDB_STAGNATE);
				return cdb_sc_blkmod;
			}
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_rec_hdr1a, SIZEOF(rec_hdr));
			BLK_SEG(bs_ptr2, new_ins_key + new_ins_keycmpc, new_ins_keylen);
			BLK_SEG(bs_ptr2, bn_ptr1, SIZEOF(block_id));
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)new_rec_hdr1b, SIZEOF(rec_hdr));
			BLK_SEG(bs_ptr2, ances_currkey + new_ances_currkeycmpc, new_ances_currkeylen);
			ins_off = blk_seg_cnt;
			BLK_SEG(bs_ptr2, (unsigned char *)&zeroes, SIZEOF(block_id));
			save_blk_piece_len = (int)(new_blk2_top - old_blk_after_currec);
			if (0 < save_blk_piece_len)
			{
				BLK_ADDR(save_blk_piece, save_blk_piece_len, unsigned char);
				memcpy(save_blk_piece, old_blk_after_currec, save_blk_piece_len);
				BLK_SEG(bs_ptr2, save_blk_piece, save_blk_piece_len);
			}
			if (!BLK_FINI(bs_ptr2, bs_ptr1))
			{
				assert(t_tries < CDB_STAGNATE);
				return cdb_sc_blkmod;
			}
			right_index = t_create(allocation_clue++, (unsigned char *)bs_ptr1, ins_off, right_index, level);
			(*blks_created)++;
		} /* endif new_ins_key insered in right block */
		BLK_ADDR(new_ins_key, new_blk1_last_keysz, unsigned char);
		memcpy(new_ins_key, &new_blk1_last_key[0], new_blk1_last_keysz);
		new_ins_keysz = new_blk1_last_keysz;
		if (create_root)
		{
			BLK_ADDR(root_hdr, SIZEOF(rec_hdr), rec_hdr);
			root_hdr->rsiz = BSTAR_REC_SIZE + new_ins_keysz;
			SET_CMPC(root_hdr, 0);
			BLK_INIT(bs_ptr2, bs_ptr1);
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)root_hdr, SIZEOF(rec_hdr));
			BLK_SEG(bs_ptr2, new_ins_key, new_ins_keysz);
			ins_off = blk_seg_cnt;
			BLK_SEG(bs_ptr2, (unsigned char *)&zeroes, SIZEOF(block_id));
			BLK_SEG(bs_ptr2, (sm_uc_ptr_t)star_rec_hdr, SIZEOF(rec_hdr) );
			ins_off2 = blk_seg_cnt;
			BLK_SEG(bs_ptr2, (unsigned char *)&zeroes, SIZEOF(block_id));
			if (!BLK_FINI(bs_ptr2, bs_ptr1))
			{
				assert(t_tries < CDB_STAGNATE);
				return cdb_sc_blkmod;
			}
			cse = t_write(&gv_target->hist.h[level], (unsigned char *)bs_ptr1, ins_off, left_index,
				level + 1, TRUE, FALSE, GDS_WRITE_KILLTN);
			t_write_root(ins_off2, right_index);	/* create a sibling cw-set-element to store ins_off2/right_index */
			(*lvls_increased)++;
			break;
		}

	} /* ========== End loop through ancestors as necessary ======= */

	/* gv_currkey_next_reorg for next iteration in mu_reorg */
	memcpy(&gv_currkey_next_reorg->base[0], next_gv_currkey, next_gv_currkeysz);
	gv_currkey_next_reorg->end = next_gv_currkeysz - 1;

	return cdb_sc_normal;

}
/* end mu_split() */

/*
-------------------------------------------------------------------------
locate_block_split_point ():
	This will split a block at a point given by fill factor
Input Parameter:
	blk_base = base of the block
	level = level of the block
	cur_blk_size = size of the block
	max_fill = maximum fill allowed for the block (max_fill < cur_blk_size)
Output Parameter:
	last_rec_size = last record size of first piece
	last_key = actual value of last key of the first block
	last_keysz = size of  actual value of last key of the first block
	top_off = offset of left piece's top
Return :
	cdb_sc_blkmod : If block is already modified
	cdb_sc_normal : Otherwise (not necessary block is fine)
	Note:	After split
			*top_off >=  max_fill,
			max_fill <= cur_blk_size
			max_fill > SIZEOF(blk_hdr)
		At least one record will be in left block after split
-------------------------------------------------------------------------
*/
enum cdb_sc locate_block_split_point(sm_uc_ptr_t blk_base, int level, int cur_blk_size, int max_fill,
int *last_rec_size, unsigned char last_key[], int *last_keysz, int *top_off)
{
	unsigned short	temp_ushort;
	int		tkeycmpc;
	int		rec_size;
	enum cdb_sc	status;
	sm_uc_ptr_t 	rPtr1, rPtr2, rec_base;

	*last_keysz = 0;
	*top_off = SIZEOF(blk_hdr);
	*last_rec_size = 0;
	rec_base = blk_base + SIZEOF(blk_hdr);
	while (*top_off < max_fill)
	{
		READ_RECORD(level, rec_base, tkeycmpc, rec_size, &last_key[0], *last_keysz, status);
		*top_off += rec_size;
		*last_keysz += tkeycmpc;
		rec_base += rec_size;
		*last_rec_size = rec_size;
		if (cdb_sc_starrecord == status &&  *top_off == cur_blk_size)
			return cdb_sc_normal;
		else if (cdb_sc_normal != status)
		{
			assert(t_tries < CDB_STAGNATE);
			return cdb_sc_blkmod; /* block became invalid */
		}
	}/* end of "while" loop */
	if (*top_off > cur_blk_size || ((blk_hdr_ptr_t)blk_base)->levl != level  ||
		((blk_hdr_ptr_t)blk_base)->bsiz != cur_blk_size)
	{
		assert(t_tries < CDB_STAGNATE);
		return cdb_sc_blkmod; /* block became invalid */
	}
	return cdb_sc_normal;
}
