xref: /csrg-svn/lib/libc/db/hash/hash_bigkey.c (revision 51046)
146364Sbostic /*-
246364Sbostic  * Copyright (c) 1990 The Regents of the University of California.
346364Sbostic  * 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*51046Sbostic static char sccsid[] = "@(#)hash_bigkey.c	5.7 (Berkeley) 09/08/91";
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>
3446364Sbostic #include <errno.h>
3546364Sbostic #include <db.h>
3646364Sbostic #include <stdio.h>
3746562Sbostic #include <stdlib.h>
3846562Sbostic #include <string.h>
3950997Sbostic #ifdef DEBUG
4050997Sbostic #include <assert.h>
4150997Sbostic #endif
4246364Sbostic #include "hash.h"
4346364Sbostic #include "page.h"
4450997Sbostic #include "extern.h"
4546364Sbostic 
4650997Sbostic static int collect_key __P((BUFHEAD *, int, DBT *, int));
4750997Sbostic static int collect_data __P((BUFHEAD *, int, int));
4846364Sbostic 
4946364Sbostic /*
5050997Sbostic  * Big_insert
5150997Sbostic  *
5250997Sbostic  * You need to do an insert and the key/data pair is too big
5350997Sbostic  *
5450997Sbostic  * Returns:
5550997Sbostic  * 0 ==> OK
5650997Sbostic  *-1 ==> ERROR
5750997Sbostic  */
5846364Sbostic extern int
5950997Sbostic __big_insert(bufp, key, val)
6050997Sbostic 	BUFHEAD *bufp;
6150997Sbostic 	const DBT *key, *val;
6246364Sbostic {
6350997Sbostic 	register u_short *p;
6450997Sbostic 	int key_size, n, val_size;
6550997Sbostic 	u_short space, move_bytes, off;
6650997Sbostic 	char *cp, *key_data, *val_data;
6746364Sbostic 
6850997Sbostic 	cp = bufp->page;		/* Character pointer of p. */
6950997Sbostic 	p = (u_short *)cp;
7046364Sbostic 
7150997Sbostic 	key_data = (char *)key->data;
7250997Sbostic 	key_size = key->size;
7350997Sbostic 	val_data = (char *)val->data;
7450997Sbostic 	val_size = val->size;
7550997Sbostic 
7650997Sbostic 	/* First move the Key */
7750997Sbostic 	for (space = FREESPACE(p) - BIGOVERHEAD; key_size;
7850997Sbostic 	    space = FREESPACE(p) - BIGOVERHEAD) {
7950997Sbostic 		move_bytes = MIN(space, key_size);
8050997Sbostic 		off = OFFSET(p) - move_bytes;
8150997Sbostic 		bcopy(key_data, cp + off, move_bytes);
8250997Sbostic 		key_size -= move_bytes;
8350997Sbostic 		key_data += move_bytes;
8450997Sbostic 		n = p[0];
8550997Sbostic 		p[++n] = off;
8650997Sbostic 		p[0] = ++n;
8750997Sbostic 		FREESPACE(p) = off - PAGE_META(n);
8850997Sbostic 		OFFSET(p) = off;
8950997Sbostic 		p[n] = PARTIAL_KEY;
9050997Sbostic 		bufp = __add_ovflpage(bufp);
9150997Sbostic 		if (!bufp)
9250997Sbostic 			return (-1);
9350997Sbostic 		n = p[0];
9450997Sbostic 		if (!key_size)
9550997Sbostic 			if (FREESPACE(p)) {
9650997Sbostic 				move_bytes = MIN(FREESPACE(p), val_size);
9750997Sbostic 				off = OFFSET(p) - move_bytes;
9850997Sbostic 				p[n] = off;
9950997Sbostic 				bcopy(val_data, cp + off, move_bytes);
10050997Sbostic 				val_data += move_bytes;
10150997Sbostic 				val_size -= move_bytes;
10250997Sbostic 				p[n - 2] = FULL_KEY_DATA;
10350997Sbostic 				FREESPACE(p) = FREESPACE(p) - move_bytes;
10450997Sbostic 				OFFSET(p) = off;
10550997Sbostic 			} else
10650997Sbostic 				p[n - 2] = FULL_KEY;
10750997Sbostic 		p = (u_short *)bufp->page;
10850997Sbostic 		cp = bufp->page;
10950997Sbostic 		bufp->flags |= BUF_MOD;
11046364Sbostic 	}
11150997Sbostic 
11250997Sbostic 	/* Now move the data */
11350997Sbostic 	for (space = FREESPACE(p) - BIGOVERHEAD; val_size;
11450997Sbostic 	    space = FREESPACE(p) - BIGOVERHEAD) {
11550997Sbostic 		move_bytes = MIN(space, val_size);
11650997Sbostic 		/*
11750997Sbostic 		 * Here's the hack to make sure that if the data ends on the
11850997Sbostic 		 * same page as the key ends, FREESPACE is at least one.
11950997Sbostic 		 */
12050997Sbostic 		if (space == val_size && val_size == val->size)
12150997Sbostic 			move_bytes--;
12246364Sbostic 		off = OFFSET(p) - move_bytes;
12350997Sbostic 		bcopy(val_data, cp + off, move_bytes);
12450997Sbostic 		val_size -= move_bytes;
12546364Sbostic 		val_data += move_bytes;
12650997Sbostic 		n = p[0];
12750997Sbostic 		p[++n] = off;
12850997Sbostic 		p[0] = ++n;
12950997Sbostic 		FREESPACE(p) = off - PAGE_META(n);
13046364Sbostic 		OFFSET(p) = off;
13150997Sbostic 		if (val_size) {
13250997Sbostic 			p[n] = FULL_KEY;
13350997Sbostic 			bufp = __add_ovflpage(bufp);
13450997Sbostic 			if (!bufp)
13550997Sbostic 				return (-1);
13650997Sbostic 			cp = bufp->page;
13750997Sbostic 			p = (u_short *)cp;
13850997Sbostic 		} else
13950997Sbostic 			p[n] = FULL_KEY_DATA;
14050997Sbostic 		bufp->flags |= BUF_MOD;
14146364Sbostic 	}
14250997Sbostic 	return (0);
14346364Sbostic }
14446364Sbostic 
14546364Sbostic /*
14650997Sbostic  * Called when bufp's page  contains a partial key (index should be 1)
14750997Sbostic  *
14850997Sbostic  * All pages in the big key/data pair except bufp are freed.  We cannot
14950997Sbostic  * free bufp because the page pointing to it is lost and we can't get rid
15050997Sbostic  * of its pointer.
15150997Sbostic  *
15250997Sbostic  * Returns:
15350997Sbostic  * 0 => OK
15450997Sbostic  *-1 => ERROR
15550997Sbostic  */
15646364Sbostic extern int
157*51046Sbostic __big_delete(bufp)
15850997Sbostic 	BUFHEAD *bufp;
15946364Sbostic {
16050997Sbostic 	register BUFHEAD *last_bfp, *rbufp;
16150997Sbostic 	u_short *bp, pageno;
16250997Sbostic 	int key_done, n;
16346364Sbostic 
16450997Sbostic 	rbufp = bufp;
16550997Sbostic 	last_bfp = NULL;
16650997Sbostic 	bp = (u_short *)bufp->page;
16750997Sbostic 	pageno = 0;
16850997Sbostic 	key_done = 0;
16950997Sbostic 
17046364Sbostic 	while (!key_done || (bp[2] != FULL_KEY_DATA)) {
17150997Sbostic 		if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA)
17250997Sbostic 			key_done = 1;
17346364Sbostic 
17450997Sbostic 		/*
17550997Sbostic 		 * If there is freespace left on a FULL_KEY_DATA page, then
17650997Sbostic 		 * the data is short and fits entirely on this page, and this
17750997Sbostic 		 * is the last page.
17850997Sbostic 		 */
17950997Sbostic 		if (bp[2] == FULL_KEY_DATA && FREESPACE(bp))
18050997Sbostic 			break;
18150997Sbostic 		pageno = bp[bp[0] - 1];
18250997Sbostic 		rbufp->flags |= BUF_MOD;
18350997Sbostic 		rbufp = __get_buf(pageno, rbufp, 0);
18450997Sbostic 		if (last_bfp)
18550997Sbostic 			__free_ovflpage(last_bfp);
18650997Sbostic 		last_bfp = rbufp;
18750997Sbostic 		if (!rbufp)
18850997Sbostic 			return (-1);		/* Error. */
18950997Sbostic 		bp = (u_short *)rbufp->page;
19046364Sbostic 	}
19146364Sbostic 
19250997Sbostic 	/*
19350997Sbostic 	 * If we get here then rbufp points to the last page of the big
19450997Sbostic 	 * key/data pair.  Bufp points to the first one -- it should now be
19550997Sbostic 	 * empty pointing to the next page after this pair.  Can't free it
19650997Sbostic 	 * because we don't have the page pointing to it.
19750997Sbostic 	 */
19846364Sbostic 
19950997Sbostic 	/* This is information from the last page of the pair. */
20046364Sbostic 	n = bp[0];
20150997Sbostic 	pageno = bp[n - 1];
20246364Sbostic 
20350997Sbostic 	/* Now, bp is the first page of the pair. */
20446364Sbostic 	bp = (u_short *)bufp->page;
20550997Sbostic 	if (n > 2) {
20650997Sbostic 		/* There is an overflow page. */
20750997Sbostic 		bp[1] = pageno;
20850997Sbostic 		bp[2] = OVFLPAGE;
20950997Sbostic 		bufp->ovfl = rbufp->ovfl;
21050997Sbostic 	} else
21150997Sbostic 		/* This is the last page. */
21250997Sbostic 		bufp->ovfl = NULL;
21346364Sbostic 	n -= 2;
21446364Sbostic 	bp[0] = n;
21546364Sbostic 	FREESPACE(bp) = hashp->BSIZE - PAGE_META(n);
21646364Sbostic 	OFFSET(bp) = hashp->BSIZE - 1;
21746364Sbostic 
21846364Sbostic 	bufp->flags |= BUF_MOD;
21950997Sbostic 	if (rbufp)
22050997Sbostic 		__free_ovflpage(rbufp);
22150997Sbostic 	if (last_bfp != rbufp)
22250997Sbostic 		__free_ovflpage(last_bfp);
22346364Sbostic 
22446364Sbostic 	hashp->NKEYS--;
22550997Sbostic 	return (0);
22646364Sbostic }
22746364Sbostic /*
22850997Sbostic  * Returns:
22950997Sbostic  *  0 = key not found
23050997Sbostic  * -1 = get next overflow page
23150997Sbostic  * -2 means key not found and this is big key/data
23250997Sbostic  * -3 error
23350997Sbostic  */
23446364Sbostic extern int
23550997Sbostic __find_bigpair(bufp, ndx, key, size)
23650997Sbostic 	BUFHEAD *bufp;
23750997Sbostic 	int ndx;
23850997Sbostic 	char *key;
23950997Sbostic 	int size;
24046364Sbostic {
24150997Sbostic 	register u_short *bp;
24250997Sbostic 	register char *p;
24350997Sbostic 	int ksize;
24450997Sbostic 	u_short bytes;
24550997Sbostic 	char *kkey;
24646364Sbostic 
24750997Sbostic 	bp = (u_short *)bufp->page;
24850997Sbostic 	p = bufp->page;
24950997Sbostic 	ksize = size;
25050997Sbostic 	kkey = key;
25146364Sbostic 
25250997Sbostic 	for (bytes = hashp->BSIZE - bp[ndx];
25350997Sbostic 	    bytes <= size && bp[ndx + 1] == PARTIAL_KEY;
25450997Sbostic 	    bytes = hashp->BSIZE - bp[ndx]) {
25550997Sbostic 		if (bcmp(p + bp[ndx], kkey, bytes))
25650997Sbostic 			return (-2);
25750997Sbostic 		kkey += bytes;
25850997Sbostic 		ksize -= bytes;
25950997Sbostic 		bufp = __get_buf(bp[ndx + 2], bufp, 0);
26050997Sbostic 		if (!bufp)
26150997Sbostic 			return (-3);
26250997Sbostic 		p = bufp->page;
26350997Sbostic 		bp = (u_short *)p;
26450997Sbostic 		ndx = 1;
26546364Sbostic 	}
26646364Sbostic 
26750997Sbostic 	if (bytes != ksize || bcmp(p + bp[ndx], kkey, bytes)) {
26846364Sbostic #ifdef HASH_STATISTICS
26950997Sbostic 		++hash_collisions;
27046364Sbostic #endif
27150997Sbostic 		return (-2);
27250997Sbostic 	} else
27350997Sbostic 		return (ndx);
27446364Sbostic }
27546364Sbostic 
27646364Sbostic /*
27750997Sbostic  * Given the buffer pointer of the first overflow page of a big pair,
27850997Sbostic  * find the end of the big pair
27950997Sbostic  *
28050997Sbostic  * This will set bpp to the buffer header of the last page of the big pair.
28150997Sbostic  * It will return the pageno of the overflow page following the last page
28250997Sbostic  * of the pair; 0 if there isn't any (i.e. big pair is the last key in the
28350997Sbostic  * bucket)
28450997Sbostic  */
28546364Sbostic extern u_short
28650997Sbostic __find_last_page(bpp)
28750997Sbostic 	BUFHEAD **bpp;
28846364Sbostic {
28950997Sbostic 	BUFHEAD *bufp;
29050997Sbostic 	u_short *bp, pageno;
29150997Sbostic 	int n;
29246364Sbostic 
29350997Sbostic 	bufp = *bpp;
29450997Sbostic 	bp = (u_short *)bufp->page;
29550997Sbostic 	for (;;) {
29650997Sbostic 		n = bp[0];
29746364Sbostic 
29850997Sbostic 		/*
29950997Sbostic 		 * This is the last page if: the tag is FULL_KEY_DATA and
30050997Sbostic 		 * either only 2 entries OVFLPAGE marker is explicit there
30150997Sbostic 		 * is freespace on the page.
30250997Sbostic 		 */
30350997Sbostic 		if (bp[2] == FULL_KEY_DATA &&
30450997Sbostic 		    ((n == 2) || (bp[n] == OVFLPAGE) || (FREESPACE(bp))))
30550997Sbostic 			break;
30646364Sbostic 
30750997Sbostic 		pageno = bp[n - 1];
30850997Sbostic 		bufp = __get_buf(pageno, bufp, 0);
30950997Sbostic 		if (!bufp)
31050997Sbostic 			return (0);	/* Need to indicate an error! */
31150997Sbostic 		bp = (u_short *)bufp->page;
31246364Sbostic 	}
31346364Sbostic 
31446364Sbostic 	*bpp = bufp;
31550997Sbostic 	if (bp[0] > 2)
31650997Sbostic 		return (bp[3]);
31750997Sbostic 	else
31850997Sbostic 		return (0);
31946364Sbostic }
32046364Sbostic 
32146364Sbostic /*
32250997Sbostic  * Return the data for the key/data pair that begins on this page at this
32350997Sbostic  * index (index should always be 1).
32450997Sbostic  */
32546364Sbostic extern int
32650997Sbostic __big_return(bufp, ndx, val, set_current)
32750997Sbostic 	BUFHEAD *bufp;
32850997Sbostic 	int ndx;
32950997Sbostic 	DBT *val;
33050997Sbostic 	int set_current;
33146364Sbostic {
33250997Sbostic 	BUFHEAD *save_p;
33350997Sbostic 	u_short *bp, len, off, save_addr;
33450997Sbostic 	char *tp;
33546364Sbostic 
33646364Sbostic 	bp = (u_short *)bufp->page;
33750997Sbostic 	while (bp[ndx + 1] == PARTIAL_KEY) {
33850997Sbostic 		bufp = __get_buf(bp[bp[0] - 1], bufp, 0);
33950997Sbostic 		if (!bufp)
34050997Sbostic 			return (-1);
34150997Sbostic 		bp = (u_short *)bufp->page;
34250997Sbostic 		ndx = 1;
34350997Sbostic 	}
34446364Sbostic 
34550997Sbostic 	if (bp[ndx + 1] == FULL_KEY) {
34650997Sbostic 		bufp = __get_buf(bp[bp[0] - 1], bufp, 0);
34750997Sbostic 		if (!bufp)
34850997Sbostic 			return (-1);
34950997Sbostic 		bp = (u_short *)bufp->page;
35050997Sbostic 		save_p = bufp;
35150997Sbostic 		save_addr = save_p->addr;
35250997Sbostic 		off = bp[1];
35350997Sbostic 		len = 0;
35450997Sbostic 	} else
35550997Sbostic 		if (!FREESPACE(bp)) {
35650997Sbostic 			/*
35750997Sbostic 			 * This is a hack.  We can't distinguish between
35850997Sbostic 			 * FULL_KEY_DATA that contains complete data or
35950997Sbostic 			 * incomplete data, so we require that if the data
36050997Sbostic 			 * is complete, there is at least 1 byte of free
36150997Sbostic 			 * space left.
36250997Sbostic 			 */
36350997Sbostic 			off = bp[bp[0]];
36450997Sbostic 			len = bp[1] - off;
36550997Sbostic 			save_p = bufp;
36650997Sbostic 			save_addr = bufp->addr;
36750997Sbostic 			bufp = __get_buf(bp[bp[0] - 1], bufp, 0);
36850997Sbostic 			if (!bufp)
36950997Sbostic 				return (-1);
37050997Sbostic 			bp = (u_short *)bufp->page;
37150997Sbostic 		} else {
37250997Sbostic 			/* The data is all on one page. */
37350997Sbostic 			tp = (char *)bp;
37450997Sbostic 			off = bp[bp[0]];
37550997Sbostic 			val->data = (u_char *)tp + off;
37650997Sbostic 			val->size = bp[1] - off;
37750997Sbostic 			if (set_current) {
37850997Sbostic 				if (bp[0] == 2) {	/* No more buckets in
37950997Sbostic 							 * chain */
38050997Sbostic 					hashp->cpage = NULL;
38150997Sbostic 					hashp->cbucket++;
38250997Sbostic 					hashp->cndx = 1;
38350997Sbostic 				} else {
38450997Sbostic 					hashp->cpage =
38550997Sbostic 					    __get_buf(bp[bp[0] - 1], bufp, 0);
38650997Sbostic 					if (!hashp->cpage)
38750997Sbostic 						return (-1);
38850997Sbostic 					hashp->cndx = 1;
38950997Sbostic 					if (!((u_short *)
39050997Sbostic 					    hashp->cpage->page)[0]) {
39150997Sbostic 						hashp->cbucket++;
39250997Sbostic 						hashp->cpage = NULL;
39350997Sbostic 					}
39450997Sbostic 				}
39550997Sbostic 			}
39650997Sbostic 			return (0);
39746364Sbostic 		}
39850997Sbostic 
399*51046Sbostic 	val->size = collect_data(bufp, (int)len, set_current);
40050997Sbostic 	if (val->size == -1)
40150997Sbostic 		return (-1);
40250997Sbostic 	if (save_p->addr != save_addr) {
40350997Sbostic 		/* We are pretty short on buffers. */
40450997Sbostic 		errno = EINVAL;			/* OUT OF BUFFERS */
40550997Sbostic 		return (-1);
40646364Sbostic 	}
40750997Sbostic 	bcopy((save_p->page) + off, hashp->tmp_buf, len);
40850997Sbostic 	val->data = (u_char *)hashp->tmp_buf;
40950997Sbostic 	return (0);
41046364Sbostic }
41146364Sbostic /*
41250997Sbostic  * Count how big the total datasize is by recursing through the pages.  Then
41350997Sbostic  * allocate a buffer and copy the data as you recurse up.
41450997Sbostic  */
41546364Sbostic static int
41650997Sbostic collect_data(bufp, len, set)
41750997Sbostic 	BUFHEAD *bufp;
41850997Sbostic 	int len, set;
41946364Sbostic {
42050997Sbostic 	register u_short *bp;
42150997Sbostic 	register char *p;
42250997Sbostic 	BUFHEAD *xbp;
42350997Sbostic 	u_short save_addr;
42450997Sbostic 	int mylen, totlen;
42546364Sbostic 
42650997Sbostic 	p = bufp->page;
42750997Sbostic 	bp = (u_short *)p;
42850997Sbostic 	mylen = hashp->BSIZE - bp[1];
42950997Sbostic 	save_addr = bufp->addr;
43046364Sbostic 
43150997Sbostic 	if (bp[2] == FULL_KEY_DATA) {		/* End of Data */
43250997Sbostic 		totlen = len + mylen;
43350997Sbostic 		if (hashp->tmp_buf)
43450997Sbostic 			free(hashp->tmp_buf);
43550997Sbostic 		hashp->tmp_buf = malloc(totlen);
43650997Sbostic 		if (!hashp->tmp_buf)
43750997Sbostic 			return (-1);
43850997Sbostic 		if (set) {
43950997Sbostic 			hashp->cndx = 1;
44050997Sbostic 			if (bp[0] == 2) {	/* No more buckets in chain */
44150997Sbostic 				hashp->cpage = NULL;
44250997Sbostic 				hashp->cbucket++;
44350997Sbostic 			} else {
44450997Sbostic 				hashp->cpage =
44550997Sbostic 				    __get_buf(bp[bp[0] - 1], bufp, 0);
44650997Sbostic 				if (!hashp->cpage)
44750997Sbostic 					return (-1);
44850997Sbostic 				else if (!((u_short *)hashp->cpage->page)[0]) {
44950997Sbostic 					hashp->cbucket++;
45050997Sbostic 					hashp->cpage = NULL;
45150997Sbostic 				}
45250997Sbostic 			}
45346364Sbostic 		}
45450997Sbostic 	} else {
45550997Sbostic 		xbp = __get_buf(bp[bp[0] - 1], bufp, 0);
45650997Sbostic 		if (!xbp ||
45750997Sbostic 		    ((totlen = collect_data(xbp, len + mylen, set)) < 1))
45850997Sbostic 			return (-1);
45946364Sbostic 	}
46050997Sbostic 	if (bufp->addr != save_addr) {
46150997Sbostic 		errno = EINVAL;			/* Out of buffers. */
46250997Sbostic 		return (-1);
46346364Sbostic 	}
46450997Sbostic 	bcopy((bufp->page) + bp[1], &hashp->tmp_buf[len], mylen);
46550997Sbostic 	return (totlen);
46646364Sbostic }
46746364Sbostic 
46846364Sbostic /*
46950997Sbostic  * Fill in the key and data for this big pair.
47050997Sbostic  */
47146364Sbostic extern int
472*51046Sbostic __big_keydata(bufp, key, val, set)
47350997Sbostic 	BUFHEAD *bufp;
47450997Sbostic 	DBT *key, *val;
47550997Sbostic 	int set;
47646364Sbostic {
47750997Sbostic 	key->size = collect_key(bufp, 0, val, set);
47850997Sbostic 	if (key->size == -1)
47950997Sbostic 		return (-1);
48050997Sbostic 	key->data = (u_char *)hashp->tmp_key;
48150997Sbostic 	return (0);
48246364Sbostic }
48346364Sbostic 
48446364Sbostic /*
48550997Sbostic  * Count how big the total key size is by recursing through the pages.  Then
48650997Sbostic  * collect the data, allocate a buffer and copy the key as you recurse up.
48750997Sbostic  */
48846364Sbostic static int
48950997Sbostic collect_key(bufp, len, val, set)
49050997Sbostic 	BUFHEAD *bufp;
49150997Sbostic 	int len;
49250997Sbostic 	DBT *val;
49350997Sbostic 	int set;
49446364Sbostic {
49550997Sbostic 	BUFHEAD *xbp;
49650997Sbostic 	char *p;
49750997Sbostic 	int mylen, totlen;
49850997Sbostic 	u_short *bp, save_addr;
49946364Sbostic 
50050997Sbostic 	p = bufp->page;
50150997Sbostic 	bp = (u_short *)p;
50250997Sbostic 	mylen = hashp->BSIZE - bp[1];
50346364Sbostic 
50450997Sbostic 	save_addr = bufp->addr;
50550997Sbostic 	totlen = len + mylen;
50650997Sbostic 	if (bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA) {    /* End of Key. */
50750997Sbostic 		if (hashp->tmp_key)
50850997Sbostic 			free(hashp->tmp_key);
50950997Sbostic 		hashp->tmp_key = malloc(totlen);
51050997Sbostic 		if (!hashp->tmp_key)
51150997Sbostic 			return (-1);
512*51046Sbostic 		if (__big_return(bufp, 1, val, set))
513*51046Sbostic 			return (-1);
51450997Sbostic 	} else {
51550997Sbostic 		xbp = __get_buf(bp[bp[0] - 1], bufp, 0);
51650997Sbostic 		if (!xbp ||
51750997Sbostic 		    ((totlen = collect_key(xbp, totlen, val, set)) < 1))
51850997Sbostic 			return (-1);
51946364Sbostic 	}
52050997Sbostic 	if (bufp->addr != save_addr) {
52150997Sbostic 		errno = EINVAL;		/* MIS -- OUT OF BUFFERS */
52250997Sbostic 		return (-1);
52346364Sbostic 	}
52450997Sbostic 	bcopy((bufp->page) + bp[1], &hashp->tmp_key[len], mylen);
52550997Sbostic 	return (totlen);
52646364Sbostic }
52746364Sbostic 
52846364Sbostic /*
52950997Sbostic  * Returns:
53050997Sbostic  *  0 => OK
53150997Sbostic  * -1 => error
53250997Sbostic  */
53346364Sbostic extern int
53450997Sbostic __big_split(op, np, big_keyp, addr, obucket, ret)
53550997Sbostic 	BUFHEAD *op;	/* Pointer to where to put keys that go in old bucket */
53650997Sbostic 	BUFHEAD *np;	/* Pointer to new bucket page */
53750997Sbostic 			/* Pointer to first page containing the big key/data */
53850997Sbostic 	BUFHEAD *big_keyp;
53950997Sbostic 	int addr;	/* Address of big_keyp */
54050997Sbostic 	u_int   obucket;/* Old Bucket */
54150997Sbostic 	SPLIT_RETURN *ret;
54246364Sbostic {
54350997Sbostic 	register BUFHEAD *tmpp;
54450997Sbostic 	register u_short *tp;
54550997Sbostic 	BUFHEAD *bp;
54650997Sbostic 	DBT key, val;
54750997Sbostic 	u_int change;
54850997Sbostic 	u_short free_space, n, off;
54946364Sbostic 
55050997Sbostic 	bp = big_keyp;
55146364Sbostic 
55250997Sbostic 	/* Now figure out where the big key/data goes */
553*51046Sbostic 	if (__big_keydata(big_keyp, &key, &val, 0))
55450997Sbostic 		return (-1);
55550997Sbostic 	change = (__call_hash(key.data, key.size) != obucket);
55646364Sbostic 
55750997Sbostic 	if (ret->next_addr = __find_last_page(&big_keyp)) {
55850997Sbostic 		if (!(ret->nextp = __get_buf(ret->next_addr, big_keyp, 0)))
55950997Sbostic 			return (-1);;
56050997Sbostic 	} else
56150997Sbostic 		ret->nextp = NULL;
56246364Sbostic 
56350997Sbostic 	/* Now make one of np/op point to the big key/data pair */
56450997Sbostic #ifdef DEBUG
56550997Sbostic 	assert(np->ovfl == NULL);
56650997Sbostic #endif
56750997Sbostic 	if (change)
56850997Sbostic 		tmpp = np;
56950997Sbostic 	else
57050997Sbostic 		tmpp = op;
57146364Sbostic 
57250997Sbostic 	tmpp->flags |= BUF_MOD;
57346364Sbostic #ifdef DEBUG1
57450997Sbostic 	(void)fprintf(stderr,
57550997Sbostic 	    "BIG_SPLIT: %d->ovfl was %d is now %d\n", tmpp->addr,
57650997Sbostic 	    (tmpp->ovfl ? tmpp->ovfl->addr : 0), (bp ? bp->addr : 0));
57746364Sbostic #endif
57850997Sbostic 	tmpp->ovfl = bp;	/* one of op/np point to big_keyp */
57950997Sbostic 	tp = (u_short *)tmpp->page;
58050997Sbostic #ifdef DEBUG
58150997Sbostic 	assert(FREESPACE(tp) >= OVFLSIZE);
58250997Sbostic #endif
58350997Sbostic 	n = tp[0];
58450997Sbostic 	off = OFFSET(tp);
58550997Sbostic 	free_space = FREESPACE(tp);
58650997Sbostic 	tp[++n] = (u_short)addr;
58750997Sbostic 	tp[++n] = OVFLPAGE;
58850997Sbostic 	tp[0] = n;
58950997Sbostic 	OFFSET(tp) = off;
59050997Sbostic 	FREESPACE(tp) = free_space - OVFLSIZE;
59146364Sbostic 
59250997Sbostic 	/*
59350997Sbostic 	 * Finally, set the new and old return values. BIG_KEYP contains a
59450997Sbostic 	 * pointer to the last page of the big key_data pair. Make sure that
59550997Sbostic 	 * big_keyp has no following page (2 elements) or create an empty
59650997Sbostic 	 * following page.
59750997Sbostic 	 */
59846364Sbostic 
59950997Sbostic 	ret->newp = np;
60050997Sbostic 	ret->oldp = op;
60146364Sbostic 
60250997Sbostic 	tp = (u_short *)big_keyp->page;
60350997Sbostic 	big_keyp->flags |= BUF_MOD;
60450997Sbostic 	if (tp[0] > 2) {
60550997Sbostic 		/*
60650997Sbostic 		 * There may be either one or two offsets on this page.  If
60750997Sbostic 		 * there is one, then the overflow page is linked on normally
60850997Sbostic 		 * and tp[4] is OVFLPAGE.  If there are two, tp[4] contains
60950997Sbostic 		 * the second offset and needs to get stuffed in after the
61050997Sbostic 		 * next overflow page is added.
61150997Sbostic 		 */
61250997Sbostic 		n = tp[4];
61350997Sbostic 		free_space = FREESPACE(tp);
61450997Sbostic 		off = OFFSET(tp);
61550997Sbostic 		tp[0] -= 2;
61650997Sbostic 		FREESPACE(tp) = free_space + OVFLSIZE;
61750997Sbostic 		OFFSET(tp) = off;
61850997Sbostic 		tmpp = __add_ovflpage(big_keyp);
61950997Sbostic 		if (!tmpp)
62050997Sbostic 			return (-1);
62150997Sbostic 		tp[4] = n;
62250997Sbostic 	} else
62350997Sbostic 		tmpp = big_keyp;
62446364Sbostic 
62550997Sbostic 	if (change)
62650997Sbostic 		ret->newp = tmpp;
62750997Sbostic 	else
62850997Sbostic 		ret->oldp = tmpp;
62950997Sbostic 	return (0);
63046364Sbostic }
631