#include	<sys/types.h>
#include	"btconf.h"
#include	"btree.h"
#include	"btintern.h"

/*
         (C) Copyright, 1988, 1989 Marcus J. Ranum
                    All rights reserved


          This software, its documentation,  and  supporting
          files  are  copyrighted  material  and may only be
          distributed in accordance with the terms listed in
          the COPYRIGHT document.

	$Log:	btpage2.c,v $
 * Revision 1.1  90/06/08  16:11:50  mjr
 *  
 * 
 * Revision 1.1  90/06/06  15:03:50  mjr
 *  
 * 
 * Revision 1.1  90/06/03  16:23:14  mjr
 *  
 * 
 * Revision 1.1  90/05/18  23:20:40  mjr
 *  
 * 
 * Revision 1.1  90/05/15  13:18:35  mjr
 * Initial revision
 * 
 * Revision 1.1  90/05/05  15:04:37  mjr
 * Initial revision
 * 
 * Revision 1.1  90/03/23  15:03:58  mjr
 * Initial revision
 * 
*/


#ifndef	lint
static char *rcsid = "$Header: /atreus/mjr/hacks/mud/btlib/RCS/btpage2.c,v 1.1 90/06/08 16:11:50 mjr Exp $";
#endif


bt_delpg(at,in,out)
int	at;
bt_chrp	in;
bt_chrp	out;
{
	int	iky;			/* key iterator */
	REGISTER bt_chrp	icp;	/* old key pointer */
	REGISTER bt_chrp	ocp;	/* new key pointer */
	REGISTER int	*iin;		/* old index/length pointer */
	REGISTER int	*iout;		/* new index/length pointer */
	REGISTER off_t	*lin;		/* old child ptr */
	REGISTER off_t	*lout;		/* new child ptr */
	REGISTER int	t;		/* iterator */
	int	dropd = 0;		/* flag AND count of dropped bytes */
	int	shif = 0;		/* length index shift after drop */

	/* delete a key from a page. very similar to insert in page */
	/* except we do it in 2 steps, like a split because we are not */
	/* sure how long the key being dropped is until we get there. */
	/* since we don't know how long it is, we can't set up the */
	/* index and child ptrs, and have to do that in a second pass */

	/* fix key count in new page */
	KEYCNT(out) = KEYCNT(in) - 1;

	/* fix left and right sibs */
	LSIB(out) = LSIB(in);
	RSIB(out) = RSIB(in);

	/* fix high pointer (may change later) */
	HIPT(out) = HIPT(in);

	/* init various pointers */
	ocp = KEYADR(out);
	icp = KEYADR(in);
	iin = (int *)KEYIND(in);

	/* start copy/drop of keys */
	for(iky = 0; iky < KEYCNT(in); iky++) {
		if(at == iky) {
			if(iky == 0)
				for(t = 0; t < *iin; t++)
					icp++;
			else
				for(t = 0; t < (*iin - *(iin - 1)); t++)
					icp++;
			dropd = t;
		} else {
			if(iky == 0)
				for(t = 0; t < *iin; t++)
					*ocp++ = *icp++;
			else
				for(t = 0; t < (*iin - *(iin - 1)); t++)
					*ocp++ = *icp++;
		}
		iin++;
	}

	/* fix key count length new page */
	if(at == KEYCNT(in) && HIPT(in) != BT_NULL) {
		/* if we are dropping last key from an index page, */
		/* drop the last key (effectively) by decrementing */
		/* the length. kind of a kludgey way to do it... */
		KEYLEN(out) = KEYLEN(in) - t;
	} else {
		/* key length of the new page is length of old minus */
		/* the length of the key that we already dropped */
		KEYLEN(out) = KEYLEN(in) - dropd;
	}

	/* reset pointers in prep for ptr/index copy/drop */
	iin = (int *)KEYIND(in);
	iout = (int *)KEYIND(out);
	lin = (off_t *)KEYCLD(in);
	lout = (off_t *)KEYCLD(out);

	/* start copy/drop of ptrs */
	for(iky = 0; iky < KEYCNT(in); iky++) {
		if(at == iky) {
			shif = dropd;
		} else {
			if(iky == KEYCNT(in) - 1 && HIPT(in) != BT_NULL && at == KEYCNT(in)) {
				HIPT(out) = *lin;
			} else {
				*iout++ = *iin - shif;
				*lout++ = *lin;
			}
		}
		iin++;
		lin++;
	}
}


bt_keyof(n,p,dbuf,dlen,dptr,max)
int	n;
bt_chrp	p;
bt_chrp	dbuf;
int	*dlen;
off_t	*dptr;
int	max;
{

	/* clip the numbered key out of the page and return it in */
	/* kbuf. the key length and rrv are also returned as well */
	int	*ip;
	bt_chrp	cp;

	if(KEYCNT(p) == 0) {
		*dlen = 0;
		*dptr = BT_NULL;
		return(BT_OK);
	}

	cp = KEYADR(p);
	ip = (int *)KEYIND(p);

	if(n == 0) {
		*dlen = *ip;
	} else {
		*dlen = *(ip + n) - *(ip + (n - 1));
		cp += *(ip + (n - 1));
	}

	if(*dlen > max)
		return(BT_ERR);

	*dptr = *((off_t *)KEYCLD(p) + n);
#ifdef	USE_BCOPY
	(void)bcopy((char *)cp,(char *)dbuf,*dlen);
#endif
#ifdef	USE_MEMCPY
	(void)memcpy((char *)dbuf,(char *)cp,*dlen);
#endif
#ifdef	USE_MOVEMEM
	(void)movemem((char *)cp,(char *)dbuf,*dlen);
#endif
	return(BT_OK);
}

void
bt_splpg(key,len,ptr,at,siz,old,lo,hi,new,nlen)
bt_chrp	key;
int	len;
off_t	*ptr;
int	at;
int	siz;
bt_chrp	old;
bt_chrp	lo;
bt_chrp	hi;
bt_chrp	new;
int	*nlen;
{
	int	oky;			/* key iterator */
	int	iky;			/* key iterator */
	int	byt = 0;		/* key byte cnt for low page */
	REGISTER bt_chrp	icp;	/* old key pointer */
	REGISTER bt_chrp	ocp;	/* new key pointer */
	REGISTER int	*iin;		/* old index/length pointer */
	REGISTER int	*iout;		/* new index/length pointer */
	REGISTER off_t	*lin;		/* old child ptr */
	REGISTER off_t	*lout;		/* new child ptr */
	REGISTER int	t;		/* iterator */
	char	copied = 0;		/* flag */
	int	shif = 0;		/* length index shift after insert */
	int	dropbyt = 0;		/* bytes dropped in index split */
	int	splitat;		/* key # where we did the split */
	
	/* this is somewhat different from a simple page insert, since */
	/* we don't yet know HOW long each key block will be, and as a */
	/* result we can't copy the pointers until the keys have been */
	/* done. first we copy the keys, fix up the length and count */
	/* values, which allows us to correctly get the addresses of */
	/* the various pointer offsets, and then we copy the keys/ptrs */
	/* there are, as you would expect, tons of special cases and */
	/* so on, in this function. The MAIN special cases are: the */
	/* last key in the low page is the one that needs to be sent */
	/* up to the calling function for insertion in the parent page */
	/* after the split. This is done with a little kludgery stuck */
	/* in the logic where we switch pages during the key copy. The */
	/* second special case is when we are splitting an index page, */
	/* the low child ptr has to become "meaningful" so what needs */
	/* to happen is that the lowest key in the high page needs to */
	/* get dropped onto the floor, rather than copied. *THEN* the */
	/* pointers need to be adjusted during the pointer copy. Ick ! */

	/* set pointers to keys prior to copy. low key first. */
	icp = KEYADR(old);
	ocp = KEYADR(lo);


	/* set the child ptr pointer here - it is used to tell */
	/* if this is an index page when we do the key copy */
	lin = (off_t *)KEYCLD(old);

	/* warning! used as a temp var to tell if we have switched pages. */
	KEYLEN(lo) = 0;

	/* set pointer to old index - output indices are unknown still */
	iin = (int *)KEYIND(old);

	/* copy one more key than was in the old page */
	for(iky = oky = 0; oky < KEYCNT(old) + 1; oky++) {

		/* if we are at insertion point, insert key here */
		if(at == iky && copied == 0) {
			for(t = 0; t < len; t++)
				*ocp++ = *key++;
			byt += len;
			copied++;
		} else {
			/* the usual nonsense with key #0 length */
			if(iky == 0) {
				for(t = 0; t < *iin; t++)
					*ocp++ = *icp++;
				byt += *iin;
			} else {
				for(t = 0; t < *iin - *(iin - 1); t++)
					*ocp++ = *icp++;
				byt += *iin - *(iin - 1);
			}
			iky++;
			iin++;
		}

		/* when the first page is full, start on the second */
		if(KEYLEN(lo) == 0 && (PTRUSE + byt + (oky * (sizeof(int) + sizeof(off_t)))) > siz) {

			/* set length and count for low page */
			KEYLEN(lo) = byt;
			KEYCNT(lo) = oky + 1;

			/* remember the # of the key where we split */
			splitat = oky;

			/* set high pointer for low page */
			/* if this is an interior page, drop the */
			/* last key and turn the last ptr into */
			/* the high pointer (done below) */
			if(HIPT(old) != BT_NULL) {
				KEYCNT(lo) = oky;

				/* keep track of how many bytes we just */
				/* lost so we can fix key lengths later */
				dropbyt = t;

				/* adjust length of low key */
				KEYLEN(lo) = byt - dropbyt;
			}

			/* return the dropped or otherwise high key */
			/* in new, and set nlen, if applicable */
 			if(new != (bt_chrp)0) {
				bt_chrp	tmpp;

 				*nlen = t;
 				tmpp = new + (t - 1);
 				/* do the copy */
 				while(t--)
 					*tmpp-- = *(--ocp);
			}

			/* now set the out pointer to point to next page */
			ocp = KEYADR(hi);

		}
	}

	/* set key counts - if we split an index, we dropped one */
	if(HIPT(old) == BT_NULL) {
		KEYCNT(hi) = oky - KEYCNT(lo);
	} else {
		KEYCNT(hi) = (oky - KEYCNT(lo)) - 1;
	}

	/* set key lengths - if we split an index, adjust high value */
	KEYLEN(hi) = byt - KEYLEN(lo) - dropbyt;

	/* set the locations of the ptrs */
	iout = (int *)KEYIND(lo);
	iin = (int *)KEYIND(old);
	lout = (off_t *)KEYCLD(lo);

	/* copy ptrs and insert new ptr at specified location */
	copied = 0;

	for(iky = oky = 0; oky < KEYCNT(old) + 1; oky++) {

		if(at == iky && copied == 0) {
			copied++;
			if(oky == splitat && HIPT(old) != BT_NULL) {
				HIPT(lo) = *ptr;

				/* since we did this, dropped bytes */
				/* dont count. ignore them */
				dropbyt = 0; 
			} else {
				*lout = *ptr;

				/* set up length/index ptrs */
				/* do not do this if the ptr was */
				/* inserted at a dropped location */
				if(iky == 0 || oky == splitat + 1)
					*iout = len;
				else 
					*iout = *(iout - 1) + len;

				/* length values now shift due to insert */
				shif += len;
			}
		} else {
			if(oky == splitat && HIPT(old) != BT_NULL) {
				/* normal ptr insert at boundary */
				HIPT(lo) = *lin++;
				iin++;
			} else {
				*lout = *lin++;

				*iout = *iin + shif;
				iin++;
			}

			iky++;
		}
		iout++;
		lout++;

		if(oky == splitat) {

			lout = (off_t *)KEYCLD(hi);
			iout = (int *)KEYIND(hi);

			/* index/length values now shift due to split */
			shif -= (KEYLEN(lo) + dropbyt);

			if(HIPT(old) == BT_NULL)
				HIPT(lo) = BT_NULL;
		}

	}

	/* the high pointer of the high page will be that of the */
	/* original page. the low pages high pointer should have */
	/* already been fixed up properly somewhere in code above */
	HIPT(hi) = HIPT(old);
}