xref: /csrg-svn/lib/libc/db/hash/hash_bigkey.c (revision 66215)
146364Sbostic /*-
261202Sbostic  * Copyright (c) 1990, 1993
361202Sbostic  *	The Regents of the University of California.  All rights reserved.
446364Sbostic  *
546364Sbostic  * This code is derived from software contributed to Berkeley by
646364Sbostic  * Margo Seltzer.
746364Sbostic  *
846364Sbostic  * %sccs.include.redist.c%
946364Sbostic  */
1046364Sbostic 
1146364Sbostic #if defined(LIBC_SCCS) && !defined(lint)
12*66215Sbostic static char sccsid[] = "@(#)hash_bigkey.c	8.2 (Berkeley) 02/21/94";
1346364Sbostic #endif /* LIBC_SCCS and not lint */
1446364Sbostic 
1550997Sbostic /*
1650997Sbostic  * PACKAGE: hash
1750997Sbostic  * DESCRIPTION:
1850997Sbostic  *	Big key/data handling for the hashing package.
1950997Sbostic  *
2050997Sbostic  * ROUTINES:
2150997Sbostic  * External
2250997Sbostic  *	__big_keydata
2350997Sbostic  *	__big_split
2450997Sbostic  *	__big_insert
2550997Sbostic  *	__big_return
2650997Sbostic  *	__big_delete
2750997Sbostic  *	__find_last_page
2850997Sbostic  * Internal
2950997Sbostic  *	collect_key
3050997Sbostic  *	collect_data
3150997Sbostic  */
3246364Sbostic 
3346644Sbostic #include <sys/param.h>
3457586Sbostic 
3546364Sbostic #include <errno.h>
3646364Sbostic #include <stdio.h>
3746562Sbostic #include <stdlib.h>
3846562Sbostic #include <string.h>
3957586Sbostic 
4050997Sbostic #ifdef DEBUG
4150997Sbostic #include <assert.h>
4250997Sbostic #endif
4357586Sbostic 
4457932Sbostic #include <db.h>
4546364Sbostic #include "hash.h"
4646364Sbostic #include "page.h"
4750997Sbostic #include "extern.h"
4846364Sbostic 
4957586Sbostic static int collect_key __P((HTAB *, BUFHEAD *, int, DBT *, int));
5057586Sbostic static int collect_data __P((HTAB *, BUFHEAD *, int, int));
5146364Sbostic 
5246364Sbostic /*
5350997Sbostic  * Big_insert
5450997Sbostic  *
5550997Sbostic  * You need to do an insert and the key/data pair is too big
5650997Sbostic  *
5750997Sbostic  * Returns:
5850997Sbostic  * 0 ==> OK
5950997Sbostic  *-1 ==> ERROR
6050997Sbostic  */
6146364Sbostic extern int
6257586Sbostic __big_insert(hashp, bufp, key, val)
6357586Sbostic 	HTAB *hashp;
6450997Sbostic 	BUFHEAD *bufp;
6550997Sbostic 	const DBT *key, *val;
6646364Sbostic {
6750997Sbostic 	register u_short *p;
6850997Sbostic 	int key_size, n, val_size;
6950997Sbostic 	u_short space, move_bytes, off;
7050997Sbostic 	char *cp, *key_data, *val_data;
7146364Sbostic 
7250997Sbostic 	cp = bufp->page;		/* Character pointer of p. */
7350997Sbostic 	p = (u_short *)cp;
7446364Sbostic 
7550997Sbostic 	key_data = (char *)key->data;
7650997Sbostic 	key_size = key->size;
7750997Sbostic 	val_data = (char *)val->data;
7850997Sbostic 	val_size = val->size;
7950997Sbostic 
8050997Sbostic 	/* First move the Key */
8150997Sbostic 	for (space = FREESPACE(p) - BIGOVERHEAD; key_size;
8250997Sbostic 	    space = FREESPACE(p) - BIGOVERHEAD) {
8350997Sbostic 		move_bytes = MIN(space, key_size);
8450997Sbostic 		off = OFFSET(p) - move_bytes;
8558016Sbostic 		memmove(cp + off, key_data, move_bytes);
8650997Sbostic 		key_size -= move_bytes;
8750997Sbostic 		key_data += move_bytes;
8850997Sbostic 		n = p[0];
8950997Sbostic 		p[++n] = off;
9050997Sbostic 		p[0] = ++n;
9150997Sbostic 		FREESPACE(p) = off - PAGE_META(n);
9250997Sbostic 		OFFSET(p) = off;
9350997Sbostic 		p[n] = PARTIAL_KEY;
9457586Sbostic 		bufp = __add_ovflpage(hashp, bufp);
9550997Sbostic 		if (!bufp)
9650997Sbostic 			return (-1);
9750997Sbostic 		n = p[0];
9850997Sbostic 		if (!key_size)
9950997Sbostic 			if (FREESPACE(p)) {
10050997Sbostic 				move_bytes = MIN(FREESPACE(p), val_size);
10150997Sbostic 				off = OFFSET(p) - move_bytes;
10250997Sbostic 				p[n] = off;
10358016Sbostic 				memmove(cp + off, val_data, move_bytes);
10450997Sbostic 				val_data += move_bytes;
10550997Sbostic 				val_size -= move_bytes;
10650997Sbostic 				p[n - 2] = FULL_KEY_DATA;
10750997Sbostic 				FREESPACE(p) = FREESPACE(p) - move_bytes;
10850997Sbostic 				OFFSET(p) = off;
10950997Sbostic 			} else
11050997Sbostic 				p[n - 2] = FULL_KEY;
11150997Sbostic 		p = (u_short *)bufp->page;
11250997Sbostic 		cp = bufp->page;
11350997Sbostic 		bufp->flags |= BUF_MOD;
11446364Sbostic 	}
11550997Sbostic 
11650997Sbostic 	/* Now move the data */
11750997Sbostic 	for (space = FREESPACE(p) - BIGOVERHEAD; val_size;
11850997Sbostic 	    space = FREESPACE(p) - BIGOVERHEAD) {
11950997Sbostic 		move_bytes = MIN(space, val_size);
12050997Sbostic 		/*
12150997Sbostic 		 * Here's the hack to make sure that if the data ends on the
12250997Sbostic 		 * same page as the key ends, FREESPACE is at least one.
12350997Sbostic 		 */
12450997Sbostic 		if (space == val_size && val_size == val->size)
12550997Sbostic 			move_bytes--;
12646364Sbostic 		off = OFFSET(p) - move_bytes;
12758016Sbostic 		memmove(cp + off, val_data, move_bytes);
12850997Sbostic 		val_size -= move_bytes;
12946364Sbostic 		val_data += move_bytes;
13050997Sbostic 		n = p[0];
13150997Sbostic 		p[++n] = off;
13250997Sbostic 		p[0] = ++n;
13350997Sbostic 		FREESPACE(p) = off - PAGE_META(n);
13446364Sbostic 		OFFSET(p) = off;
13550997Sbostic 		if (val_size) {
13650997Sbostic 			p[n] = FULL_KEY;
13757586Sbostic 			bufp = __add_ovflpage(hashp, bufp);
13850997Sbostic 			if (!bufp)
13950997Sbostic 				return (-1);
14050997Sbostic 			cp = bufp->page;
14150997Sbostic 			p = (u_short *)cp;
14250997Sbostic 		} else
14350997Sbostic 			p[n] = FULL_KEY_DATA;
14450997Sbostic 		bufp->flags |= BUF_MOD;
14546364Sbostic 	}
14650997Sbostic 	return (0);
14746364Sbostic }
14846364Sbostic 
14946364Sbostic /*
15050997Sbostic  * Called when bufp's page  contains a partial key (index should be 1)
15150997Sbostic  *
15250997Sbostic  * All pages in the big key/data pair except bufp are freed.  We cannot
15350997Sbostic  * free bufp because the page pointing to it is lost and we can't get rid
15450997Sbostic  * of its pointer.
15550997Sbostic  *
15650997Sbostic  * Returns:
15750997Sbostic  * 0 => OK
15850997Sbostic  *-1 => ERROR
15950997Sbostic  */
16046364Sbostic extern int
16157586Sbostic __big_delete(hashp, bufp)
16257586Sbostic 	HTAB *hashp;
16350997Sbostic 	BUFHEAD *bufp;
16446364Sbostic {
16550997Sbostic 	register BUFHEAD *last_bfp, *rbufp;
16650997Sbostic 	u_short *bp, pageno;
16750997Sbostic 	int key_done, n;
16846364Sbostic 
16950997Sbostic 	rbufp = bufp;
17050997Sbostic 	last_bfp = NULL;
17150997Sbostic 	bp = (u_short *)bufp->page;
17250997Sbostic 	pageno = 0;
17350997Sbostic 	key_done = 0;
17450997Sbostic 
17546364Sbostic 	while (!key_done || (bp[2] != FULL_KEY_DATA)) {
17650997Sbostic 		if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA)
17750997Sbostic 			key_done = 1;
17846364Sbostic 
17950997Sbostic 		/*
18050997Sbostic 		 * If there is freespace left on a FULL_KEY_DATA page, then
18150997Sbostic 		 * the data is short and fits entirely on this page, and this
18250997Sbostic 		 * is the last page.
18350997Sbostic 		 */
18450997Sbostic 		if (bp[2] == FULL_KEY_DATA && FREESPACE(bp))
18550997Sbostic 			break;
18650997Sbostic 		pageno = bp[bp[0] - 1];
18750997Sbostic 		rbufp->flags |= BUF_MOD;
18857586Sbostic 		rbufp = __get_buf(hashp, pageno, rbufp, 0);
18950997Sbostic 		if (last_bfp)
19057586Sbostic 			__free_ovflpage(hashp, last_bfp);
19150997Sbostic 		last_bfp = rbufp;
19250997Sbostic 		if (!rbufp)
19350997Sbostic 			return (-1);		/* Error. */
19450997Sbostic 		bp = (u_short *)rbufp->page;
19546364Sbostic 	}
19646364Sbostic 
19750997Sbostic 	/*
19850997Sbostic 	 * If we get here then rbufp points to the last page of the big
19950997Sbostic 	 * key/data pair.  Bufp points to the first one -- it should now be
20050997Sbostic 	 * empty pointing to the next page after this pair.  Can't free it
20150997Sbostic 	 * because we don't have the page pointing to it.
20250997Sbostic 	 */
20346364Sbostic 
20450997Sbostic 	/* This is information from the last page of the pair. */
20546364Sbostic 	n = bp[0];
20650997Sbostic 	pageno = bp[n - 1];
20746364Sbostic 
20850997Sbostic 	/* Now, bp is the first page of the pair. */
20946364Sbostic 	bp = (u_short *)bufp->page;
21050997Sbostic 	if (n > 2) {
21150997Sbostic 		/* There is an overflow page. */
21250997Sbostic 		bp[1] = pageno;
21350997Sbostic 		bp[2] = OVFLPAGE;
21450997Sbostic 		bufp->ovfl = rbufp->ovfl;
21550997Sbostic 	} else
21650997Sbostic 		/* This is the last page. */
21750997Sbostic 		bufp->ovfl = NULL;
21846364Sbostic 	n -= 2;
21946364Sbostic 	bp[0] = n;
22046364Sbostic 	FREESPACE(bp) = hashp->BSIZE - PAGE_META(n);
22146364Sbostic 	OFFSET(bp) = hashp->BSIZE - 1;
22246364Sbostic 
22346364Sbostic 	bufp->flags |= BUF_MOD;
22450997Sbostic 	if (rbufp)
22557586Sbostic 		__free_ovflpage(hashp, rbufp);
22650997Sbostic 	if (last_bfp != rbufp)
22757586Sbostic 		__free_ovflpage(hashp, last_bfp);
22846364Sbostic 
22946364Sbostic 	hashp->NKEYS--;
23050997Sbostic 	return (0);
23146364Sbostic }
23246364Sbostic /*
23350997Sbostic  * Returns:
23450997Sbostic  *  0 = key not found
23550997Sbostic  * -1 = get next overflow page
23650997Sbostic  * -2 means key not found and this is big key/data
23750997Sbostic  * -3 error
23850997Sbostic  */
23946364Sbostic extern int
24057586Sbostic __find_bigpair(hashp, bufp, ndx, key, size)
24157586Sbostic 	HTAB *hashp;
24250997Sbostic 	BUFHEAD *bufp;
24350997Sbostic 	int ndx;
24450997Sbostic 	char *key;
24550997Sbostic 	int size;
24646364Sbostic {
24750997Sbostic 	register u_short *bp;
24850997Sbostic 	register char *p;
24950997Sbostic 	int ksize;
25050997Sbostic 	u_short bytes;
25150997Sbostic 	char *kkey;
25246364Sbostic 
25350997Sbostic 	bp = (u_short *)bufp->page;
25450997Sbostic 	p = bufp->page;
25550997Sbostic 	ksize = size;
25650997Sbostic 	kkey = key;
25746364Sbostic 
25850997Sbostic 	for (bytes = hashp->BSIZE - bp[ndx];
25950997Sbostic 	    bytes <= size && bp[ndx + 1] == PARTIAL_KEY;
26050997Sbostic 	    bytes = hashp->BSIZE - bp[ndx]) {
26158016Sbostic 		if (memcmp(p + bp[ndx], kkey, bytes))
26250997Sbostic 			return (-2);
26350997Sbostic 		kkey += bytes;
26450997Sbostic 		ksize -= bytes;
26557586Sbostic 		bufp = __get_buf(hashp, bp[ndx + 2], bufp, 0);
26650997Sbostic 		if (!bufp)
26750997Sbostic 			return (-3);
26850997Sbostic 		p = bufp->page;
26950997Sbostic 		bp = (u_short *)p;
27050997Sbostic 		ndx = 1;
27146364Sbostic 	}
27246364Sbostic 
27358016Sbostic 	if (bytes != ksize || memcmp(p + bp[ndx], kkey, bytes)) {
27446364Sbostic #ifdef HASH_STATISTICS
27550997Sbostic 		++hash_collisions;
27646364Sbostic #endif
27750997Sbostic 		return (-2);
27850997Sbostic 	} else
27950997Sbostic 		return (ndx);
28046364Sbostic }
28146364Sbostic 
28246364Sbostic /*
28350997Sbostic  * Given the buffer pointer of the first overflow page of a big pair,
28450997Sbostic  * find the end of the big pair
28550997Sbostic  *
28650997Sbostic  * This will set bpp to the buffer header of the last page of the big pair.
28750997Sbostic  * It will return the pageno of the overflow page following the last page
28850997Sbostic  * of the pair; 0 if there isn't any (i.e. big pair is the last key in the
28950997Sbostic  * bucket)
29050997Sbostic  */
29146364Sbostic extern u_short
29257586Sbostic __find_last_page(hashp, bpp)
29357586Sbostic 	HTAB *hashp;
29450997Sbostic 	BUFHEAD **bpp;
29546364Sbostic {
29650997Sbostic 	BUFHEAD *bufp;
29750997Sbostic 	u_short *bp, pageno;
29850997Sbostic 	int n;
29946364Sbostic 
30050997Sbostic 	bufp = *bpp;
30150997Sbostic 	bp = (u_short *)bufp->page;
30250997Sbostic 	for (;;) {
30350997Sbostic 		n = bp[0];
30446364Sbostic 
30550997Sbostic 		/*
30650997Sbostic 		 * This is the last page if: the tag is FULL_KEY_DATA and
30750997Sbostic 		 * either only 2 entries OVFLPAGE marker is explicit there
30850997Sbostic 		 * is freespace on the page.
30950997Sbostic 		 */
31050997Sbostic 		if (bp[2] == FULL_KEY_DATA &&
31150997Sbostic 		    ((n == 2) || (bp[n] == OVFLPAGE) || (FREESPACE(bp))))
31250997Sbostic 			break;
31346364Sbostic 
31450997Sbostic 		pageno = bp[n - 1];
31557586Sbostic 		bufp = __get_buf(hashp, pageno, bufp, 0);
31650997Sbostic 		if (!bufp)
31750997Sbostic 			return (0);	/* Need to indicate an error! */
31850997Sbostic 		bp = (u_short *)bufp->page;
31946364Sbostic 	}
32046364Sbostic 
32146364Sbostic 	*bpp = bufp;
32250997Sbostic 	if (bp[0] > 2)
32350997Sbostic 		return (bp[3]);
32450997Sbostic 	else
32550997Sbostic 		return (0);
32646364Sbostic }
32746364Sbostic 
32846364Sbostic /*
32950997Sbostic  * Return the data for the key/data pair that begins on this page at this
33050997Sbostic  * index (index should always be 1).
33150997Sbostic  */
33246364Sbostic extern int
33357586Sbostic __big_return(hashp, bufp, ndx, val, set_current)
33457586Sbostic 	HTAB *hashp;
33550997Sbostic 	BUFHEAD *bufp;
33650997Sbostic 	int ndx;
33750997Sbostic 	DBT *val;
33850997Sbostic 	int set_current;
33946364Sbostic {
34050997Sbostic 	BUFHEAD *save_p;
34150997Sbostic 	u_short *bp, len, off, save_addr;
34250997Sbostic 	char *tp;
34346364Sbostic 
34446364Sbostic 	bp = (u_short *)bufp->page;
34550997Sbostic 	while (bp[ndx + 1] == PARTIAL_KEY) {
34657586Sbostic 		bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
34750997Sbostic 		if (!bufp)
34850997Sbostic 			return (-1);
34950997Sbostic 		bp = (u_short *)bufp->page;
35050997Sbostic 		ndx = 1;
35150997Sbostic 	}
35246364Sbostic 
35350997Sbostic 	if (bp[ndx + 1] == FULL_KEY) {
35457586Sbostic 		bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
35550997Sbostic 		if (!bufp)
35650997Sbostic 			return (-1);
35750997Sbostic 		bp = (u_short *)bufp->page;
35850997Sbostic 		save_p = bufp;
35950997Sbostic 		save_addr = save_p->addr;
36050997Sbostic 		off = bp[1];
36150997Sbostic 		len = 0;
36250997Sbostic 	} else
36350997Sbostic 		if (!FREESPACE(bp)) {
36450997Sbostic 			/*
36550997Sbostic 			 * This is a hack.  We can't distinguish between
36650997Sbostic 			 * FULL_KEY_DATA that contains complete data or
36750997Sbostic 			 * incomplete data, so we require that if the data
36850997Sbostic 			 * is complete, there is at least 1 byte of free
36950997Sbostic 			 * space left.
37050997Sbostic 			 */
37150997Sbostic 			off = bp[bp[0]];
37250997Sbostic 			len = bp[1] - off;
37350997Sbostic 			save_p = bufp;
37450997Sbostic 			save_addr = bufp->addr;
37557586Sbostic 			bufp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
37650997Sbostic 			if (!bufp)
37750997Sbostic 				return (-1);
37850997Sbostic 			bp = (u_short *)bufp->page;
37950997Sbostic 		} else {
38050997Sbostic 			/* The data is all on one page. */
38150997Sbostic 			tp = (char *)bp;
38250997Sbostic 			off = bp[bp[0]];
38350997Sbostic 			val->data = (u_char *)tp + off;
38450997Sbostic 			val->size = bp[1] - off;
38550997Sbostic 			if (set_current) {
38650997Sbostic 				if (bp[0] == 2) {	/* No more buckets in
38750997Sbostic 							 * chain */
38850997Sbostic 					hashp->cpage = NULL;
38950997Sbostic 					hashp->cbucket++;
39050997Sbostic 					hashp->cndx = 1;
39150997Sbostic 				} else {
39257586Sbostic 					hashp->cpage = __get_buf(hashp,
39357586Sbostic 					    bp[bp[0] - 1], bufp, 0);
39450997Sbostic 					if (!hashp->cpage)
39550997Sbostic 						return (-1);
39650997Sbostic 					hashp->cndx = 1;
39750997Sbostic 					if (!((u_short *)
39850997Sbostic 					    hashp->cpage->page)[0]) {
39950997Sbostic 						hashp->cbucket++;
40050997Sbostic 						hashp->cpage = NULL;
40150997Sbostic 					}
40250997Sbostic 				}
40350997Sbostic 			}
40450997Sbostic 			return (0);
40546364Sbostic 		}
40650997Sbostic 
40757586Sbostic 	val->size = collect_data(hashp, bufp, (int)len, set_current);
40850997Sbostic 	if (val->size == -1)
40950997Sbostic 		return (-1);
41050997Sbostic 	if (save_p->addr != save_addr) {
41150997Sbostic 		/* We are pretty short on buffers. */
41250997Sbostic 		errno = EINVAL;			/* OUT OF BUFFERS */
41350997Sbostic 		return (-1);
41446364Sbostic 	}
41558016Sbostic 	memmove(hashp->tmp_buf, (save_p->page) + off, len);
41650997Sbostic 	val->data = (u_char *)hashp->tmp_buf;
41750997Sbostic 	return (0);
41846364Sbostic }
41946364Sbostic /*
42050997Sbostic  * Count how big the total datasize is by recursing through the pages.  Then
42150997Sbostic  * allocate a buffer and copy the data as you recurse up.
42250997Sbostic  */
42346364Sbostic static int
collect_data(hashp,bufp,len,set)42457586Sbostic collect_data(hashp, bufp, len, set)
42557586Sbostic 	HTAB *hashp;
42650997Sbostic 	BUFHEAD *bufp;
42750997Sbostic 	int len, set;
42846364Sbostic {
42950997Sbostic 	register u_short *bp;
43050997Sbostic 	register char *p;
43150997Sbostic 	BUFHEAD *xbp;
43250997Sbostic 	u_short save_addr;
43350997Sbostic 	int mylen, totlen;
43446364Sbostic 
43550997Sbostic 	p = bufp->page;
43650997Sbostic 	bp = (u_short *)p;
43750997Sbostic 	mylen = hashp->BSIZE - bp[1];
43850997Sbostic 	save_addr = bufp->addr;
43946364Sbostic 
44050997Sbostic 	if (bp[2] == FULL_KEY_DATA) {		/* End of Data */
44150997Sbostic 		totlen = len + mylen;
44250997Sbostic 		if (hashp->tmp_buf)
44350997Sbostic 			free(hashp->tmp_buf);
444*66215Sbostic 		if ((hashp->tmp_buf = (char *)malloc(totlen)) == NULL)
44550997Sbostic 			return (-1);
44650997Sbostic 		if (set) {
44750997Sbostic 			hashp->cndx = 1;
44850997Sbostic 			if (bp[0] == 2) {	/* No more buckets in chain */
44950997Sbostic 				hashp->cpage = NULL;
45050997Sbostic 				hashp->cbucket++;
45150997Sbostic 			} else {
45250997Sbostic 				hashp->cpage =
45357586Sbostic 				    __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
45450997Sbostic 				if (!hashp->cpage)
45550997Sbostic 					return (-1);
45650997Sbostic 				else if (!((u_short *)hashp->cpage->page)[0]) {
45750997Sbostic 					hashp->cbucket++;
45850997Sbostic 					hashp->cpage = NULL;
45950997Sbostic 				}
46050997Sbostic 			}
46146364Sbostic 		}
46250997Sbostic 	} else {
46357586Sbostic 		xbp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
46457586Sbostic 		if (!xbp || ((totlen =
46557586Sbostic 		    collect_data(hashp, xbp, len + mylen, set)) < 1))
46650997Sbostic 			return (-1);
46746364Sbostic 	}
46850997Sbostic 	if (bufp->addr != save_addr) {
46950997Sbostic 		errno = EINVAL;			/* Out of buffers. */
47050997Sbostic 		return (-1);
47146364Sbostic 	}
47258016Sbostic 	memmove(&hashp->tmp_buf[len], (bufp->page) + bp[1], mylen);
47350997Sbostic 	return (totlen);
47446364Sbostic }
47546364Sbostic 
47646364Sbostic /*
47750997Sbostic  * Fill in the key and data for this big pair.
47850997Sbostic  */
47946364Sbostic extern int
48057586Sbostic __big_keydata(hashp, bufp, key, val, set)
48157586Sbostic 	HTAB *hashp;
48250997Sbostic 	BUFHEAD *bufp;
48350997Sbostic 	DBT *key, *val;
48450997Sbostic 	int set;
48546364Sbostic {
48657586Sbostic 	key->size = collect_key(hashp, bufp, 0, val, set);
48750997Sbostic 	if (key->size == -1)
48850997Sbostic 		return (-1);
48950997Sbostic 	key->data = (u_char *)hashp->tmp_key;
49050997Sbostic 	return (0);
49146364Sbostic }
49246364Sbostic 
49346364Sbostic /*
49450997Sbostic  * Count how big the total key size is by recursing through the pages.  Then
49550997Sbostic  * collect the data, allocate a buffer and copy the key as you recurse up.
49650997Sbostic  */
49746364Sbostic static int
collect_key(hashp,bufp,len,val,set)49857586Sbostic collect_key(hashp, bufp, len, val, set)
49957586Sbostic 	HTAB *hashp;
50050997Sbostic 	BUFHEAD *bufp;
50150997Sbostic 	int len;
50250997Sbostic 	DBT *val;
50350997Sbostic 	int set;
50446364Sbostic {
50550997Sbostic 	BUFHEAD *xbp;
50650997Sbostic 	char *p;
50750997Sbostic 	int mylen, totlen;
50850997Sbostic 	u_short *bp, save_addr;
50946364Sbostic 
51050997Sbostic 	p = bufp->page;
51150997Sbostic 	bp = (u_short *)p;
51250997Sbostic 	mylen = hashp->BSIZE - bp[1];
51346364Sbostic 
51450997Sbostic 	save_addr = bufp->addr;
51550997Sbostic 	totlen = len + mylen;
51650997Sbostic 	if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA) {    /* End of Key. */
517*66215Sbostic 		if (hashp->tmp_key != NULL)
51850997Sbostic 			free(hashp->tmp_key);
519*66215Sbostic 		if ((hashp->tmp_key = (char *)malloc(totlen)) == NULL)
52050997Sbostic 			return (-1);
52157586Sbostic 		if (__big_return(hashp, bufp, 1, val, set))
52251046Sbostic 			return (-1);
52350997Sbostic 	} else {
52457586Sbostic 		xbp = __get_buf(hashp, bp[bp[0] - 1], bufp, 0);
52557586Sbostic 		if (!xbp || ((totlen =
52657586Sbostic 		    collect_key(hashp, xbp, totlen, val, set)) < 1))
52750997Sbostic 			return (-1);
52846364Sbostic 	}
52950997Sbostic 	if (bufp->addr != save_addr) {
53050997Sbostic 		errno = EINVAL;		/* MIS -- OUT OF BUFFERS */
53150997Sbostic 		return (-1);
53246364Sbostic 	}
53358016Sbostic 	memmove(&hashp->tmp_key[len], (bufp->page) + bp[1], mylen);
53450997Sbostic 	return (totlen);
53546364Sbostic }
53646364Sbostic 
53746364Sbostic /*
53850997Sbostic  * Returns:
53950997Sbostic  *  0 => OK
54050997Sbostic  * -1 => error
54150997Sbostic  */
54246364Sbostic extern int
54357586Sbostic __big_split(hashp, op, np, big_keyp, addr, obucket, ret)
54457586Sbostic 	HTAB *hashp;
54550997Sbostic 	BUFHEAD *op;	/* Pointer to where to put keys that go in old bucket */
54650997Sbostic 	BUFHEAD *np;	/* Pointer to new bucket page */
54750997Sbostic 			/* Pointer to first page containing the big key/data */
54850997Sbostic 	BUFHEAD *big_keyp;
54950997Sbostic 	int addr;	/* Address of big_keyp */
55050997Sbostic 	u_int   obucket;/* Old Bucket */
55150997Sbostic 	SPLIT_RETURN *ret;
55246364Sbostic {
55350997Sbostic 	register BUFHEAD *tmpp;
55450997Sbostic 	register u_short *tp;
55550997Sbostic 	BUFHEAD *bp;
55650997Sbostic 	DBT key, val;
55750997Sbostic 	u_int change;
55850997Sbostic 	u_short free_space, n, off;
55946364Sbostic 
56050997Sbostic 	bp = big_keyp;
56146364Sbostic 
56250997Sbostic 	/* Now figure out where the big key/data goes */
56357586Sbostic 	if (__big_keydata(hashp, big_keyp, &key, &val, 0))
56450997Sbostic 		return (-1);
56557586Sbostic 	change = (__call_hash(hashp, key.data, key.size) != obucket);
56646364Sbostic 
56757586Sbostic 	if (ret->next_addr = __find_last_page(hashp, &big_keyp)) {
56857586Sbostic 		if (!(ret->nextp =
56957586Sbostic 		    __get_buf(hashp, ret->next_addr, big_keyp, 0)))
57050997Sbostic 			return (-1);;
57150997Sbostic 	} else
57250997Sbostic 		ret->nextp = NULL;
57346364Sbostic 
57450997Sbostic 	/* Now make one of np/op point to the big key/data pair */
57550997Sbostic #ifdef DEBUG
57650997Sbostic 	assert(np->ovfl == NULL);
57750997Sbostic #endif
57850997Sbostic 	if (change)
57950997Sbostic 		tmpp = np;
58050997Sbostic 	else
58150997Sbostic 		tmpp = op;
58246364Sbostic 
58350997Sbostic 	tmpp->flags |= BUF_MOD;
58446364Sbostic #ifdef DEBUG1
58550997Sbostic 	(void)fprintf(stderr,
58650997Sbostic 	    "BIG_SPLIT: %d->ovfl was %d is now %d\n", tmpp->addr,
58750997Sbostic 	    (tmpp->ovfl ? tmpp->ovfl->addr : 0), (bp ? bp->addr : 0));
58846364Sbostic #endif
58950997Sbostic 	tmpp->ovfl = bp;	/* one of op/np point to big_keyp */
59050997Sbostic 	tp = (u_short *)tmpp->page;
59150997Sbostic #ifdef DEBUG
59250997Sbostic 	assert(FREESPACE(tp) >= OVFLSIZE);
59350997Sbostic #endif
59450997Sbostic 	n = tp[0];
59550997Sbostic 	off = OFFSET(tp);
59650997Sbostic 	free_space = FREESPACE(tp);
59750997Sbostic 	tp[++n] = (u_short)addr;
59850997Sbostic 	tp[++n] = OVFLPAGE;
59950997Sbostic 	tp[0] = n;
60050997Sbostic 	OFFSET(tp) = off;
60150997Sbostic 	FREESPACE(tp) = free_space - OVFLSIZE;
60246364Sbostic 
60350997Sbostic 	/*
60450997Sbostic 	 * Finally, set the new and old return values. BIG_KEYP contains a
60550997Sbostic 	 * pointer to the last page of the big key_data pair. Make sure that
60650997Sbostic 	 * big_keyp has no following page (2 elements) or create an empty
60750997Sbostic 	 * following page.
60850997Sbostic 	 */
60946364Sbostic 
61050997Sbostic 	ret->newp = np;
61150997Sbostic 	ret->oldp = op;
61246364Sbostic 
61350997Sbostic 	tp = (u_short *)big_keyp->page;
61450997Sbostic 	big_keyp->flags |= BUF_MOD;
61550997Sbostic 	if (tp[0] > 2) {
61650997Sbostic 		/*
61750997Sbostic 		 * There may be either one or two offsets on this page.  If
61850997Sbostic 		 * there is one, then the overflow page is linked on normally
61950997Sbostic 		 * and tp[4] is OVFLPAGE.  If there are two, tp[4] contains
62050997Sbostic 		 * the second offset and needs to get stuffed in after the
62150997Sbostic 		 * next overflow page is added.
62250997Sbostic 		 */
62350997Sbostic 		n = tp[4];
62450997Sbostic 		free_space = FREESPACE(tp);
62550997Sbostic 		off = OFFSET(tp);
62650997Sbostic 		tp[0] -= 2;
62750997Sbostic 		FREESPACE(tp) = free_space + OVFLSIZE;
62850997Sbostic 		OFFSET(tp) = off;
62957586Sbostic 		tmpp = __add_ovflpage(hashp, big_keyp);
63050997Sbostic 		if (!tmpp)
63150997Sbostic 			return (-1);
63250997Sbostic 		tp[4] = n;
63350997Sbostic 	} else
63450997Sbostic 		tmpp = big_keyp;
63546364Sbostic 
63650997Sbostic 	if (change)
63750997Sbostic 		ret->newp = tmpp;
63850997Sbostic 	else
63950997Sbostic 		ret->oldp = tmpp;
64050997Sbostic 	return (0);
64146364Sbostic }
642