xref: /csrg-svn/lib/libc/db/hash/hash_bigkey.c (revision 46644)
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*46644Sbostic static char sccsid[] = "@(#)hash_bigkey.c	5.3 (Berkeley) 02/24/91";
1346364Sbostic #endif /* LIBC_SCCS and not lint */
1446364Sbostic 
1546364Sbostic /******************************************************************************
1646364Sbostic 
1746364Sbostic PACKAGE: hash
1846364Sbostic 
1946364Sbostic DESCRIPTION:
2046364Sbostic 	Big key/data handling for the hashing package.
2146364Sbostic 
2246364Sbostic ROUTINES:
2346364Sbostic     External
2446364Sbostic 	__big_keydata
2546364Sbostic 	__big_split
2646364Sbostic 	__big_insert
2746364Sbostic 	__big_return
2846364Sbostic 	__big_delete
2946364Sbostic 	__find_last_page
3046364Sbostic     Internal
3146364Sbostic 	collect_key
3246364Sbostic 	collect_data
3346364Sbostic ******************************************************************************/
3446364Sbostic /* Includes */
35*46644Sbostic #include <sys/param.h>
3646364Sbostic #include <assert.h>
3746364Sbostic #include <errno.h>
3846364Sbostic #include <db.h>
3946364Sbostic #include <stdio.h>
4046562Sbostic #include <stdlib.h>
4146562Sbostic #include <string.h>
4246364Sbostic #include "hash.h"
4346364Sbostic #include "page.h"
4446364Sbostic 
4546364Sbostic /* Externals */
4646364Sbostic /* buf.c */
4746364Sbostic extern BUFHEAD *__get_buf();
4846364Sbostic 
4946364Sbostic /* page.c */
5046364Sbostic extern BUFHEAD *__add_ovflpage();
5146364Sbostic 
5246364Sbostic /* My externals */
5346364Sbostic extern int __big_keydata();
5446364Sbostic extern int __big_split();
5546364Sbostic extern int __big_insert();
5646364Sbostic extern int __big_return();
5746364Sbostic extern int __big_delete();
5846364Sbostic extern u_short __find_last_page();
5946364Sbostic extern int __find_bigpair();
6046364Sbostic 
6146364Sbostic /* My internals */
6246364Sbostic static int collect_key();
6346364Sbostic static int collect_data();
6446364Sbostic 
6546364Sbostic #ifdef HASH_STATISTICS
6646364Sbostic extern long hash_accesses, hash_collisions, hash_expansions, hash_overflows;
6746364Sbostic #endif
6846364Sbostic /*
6946364Sbostic Big_insert
7046364Sbostic 
7146364Sbostic You need to do an insert and the key/data pair is too big
7246364Sbostic 0 ==> OK
7346364Sbostic -1 ==> ERROR
7446364Sbostic */
7546364Sbostic extern int
7646364Sbostic __big_insert ( bufp, key, val )
7746364Sbostic BUFHEAD *bufp;
7846364Sbostic DBT	*key, *val;
7946364Sbostic {
8046364Sbostic     char	*cp = bufp->page;	/* Character pointer of p */
8146364Sbostic     register u_short	*p = (u_short *)cp;
8246364Sbostic     char	*key_data, *val_data;
8346364Sbostic     int		key_size, val_size;
8446364Sbostic     int		n;
8546364Sbostic     u_short	space, move_bytes, off;
8646364Sbostic 
8746364Sbostic     key_data = key->data;
8846364Sbostic     key_size = key->size;
8946364Sbostic     val_data = val->data;
9046364Sbostic     val_size = val->size;
9146364Sbostic 
9246364Sbostic     /* First move the Key */
9346364Sbostic     for ( space = FREESPACE(p) - BIGOVERHEAD;
9446364Sbostic 	  key_size;
9546364Sbostic 	  space = FREESPACE(p) - BIGOVERHEAD ) {
9646364Sbostic 	move_bytes = MIN(space, key_size);
9746364Sbostic 	off = OFFSET(p) - move_bytes;
9846364Sbostic 	bcopy (key_data, cp+off, move_bytes );
9946364Sbostic 	key_size -= move_bytes;
10046364Sbostic 	key_data += move_bytes;
10146364Sbostic 	n = p[0];
10246364Sbostic 	p[++n] = off;
10346364Sbostic 	p[0] = ++n;
10446364Sbostic 	FREESPACE(p) = off - PAGE_META(n);
10546364Sbostic 	OFFSET(p) = off;
10646364Sbostic 	p[n] = PARTIAL_KEY;
10746364Sbostic 	bufp = __add_ovflpage(bufp);
10846364Sbostic 	if ( !bufp ) {
10946364Sbostic 	    return(-1);
11046364Sbostic 	}
11146364Sbostic 	n = p[0];
11246364Sbostic 	if ( !key_size ) {
11346364Sbostic 	    if ( FREESPACE(p) ) {
11446364Sbostic 		move_bytes = MIN (FREESPACE(p), val_size);
11546364Sbostic 		off = OFFSET(p) - move_bytes;
11646364Sbostic 		p[n] = off;
11746364Sbostic 		bcopy ( val_data, cp + off, move_bytes );
11846364Sbostic 		val_data += move_bytes;
11946364Sbostic 		val_size -= move_bytes;
12046364Sbostic 		p[n-2] = FULL_KEY_DATA;
12146364Sbostic 		FREESPACE(p) = FREESPACE(p) - move_bytes;
12246364Sbostic 		OFFSET(p) = off;
12346364Sbostic 	    }
12446364Sbostic 	    else p[n-2] = FULL_KEY;
12546364Sbostic 	}
12646364Sbostic 	p = (u_short *)bufp->page;
12746364Sbostic 	cp = bufp->page;
12846364Sbostic 	bufp->flags |= BUF_MOD;
12946364Sbostic     }
13046364Sbostic 
13146364Sbostic     /* Now move the data */
13246364Sbostic     for ( space = FREESPACE(p) - BIGOVERHEAD;
13346364Sbostic 	  val_size;
13446364Sbostic 	  space = FREESPACE(p) - BIGOVERHEAD ) {
13546364Sbostic 	move_bytes = MIN(space, val_size);
13646364Sbostic 	/*
13746364Sbostic 	    Here's the hack to make sure that if the data ends
13846364Sbostic 	    on the same page as the key ends, FREESPACE is
13946364Sbostic 	    at least one
14046364Sbostic 	*/
14146364Sbostic 	if ( space == val_size && val_size == val->size ) {
14246364Sbostic 	    move_bytes--;
14346364Sbostic 	}
14446364Sbostic 	off = OFFSET(p) - move_bytes;
14546364Sbostic 	bcopy (val_data, cp+off, move_bytes );
14646364Sbostic 	val_size -= move_bytes;
14746364Sbostic 	val_data += move_bytes;
14846364Sbostic 	n = p[0];
14946364Sbostic 	p[++n] = off;
15046364Sbostic 	p[0] = ++n;
15146364Sbostic 	FREESPACE(p) = off - PAGE_META(n);
15246364Sbostic 	OFFSET(p) = off;
15346364Sbostic 	if ( val_size ) {
15446364Sbostic 	    p[n] = FULL_KEY;
15546364Sbostic 	    bufp = __add_ovflpage (bufp);
15646364Sbostic 	    if ( !bufp ) {
15746364Sbostic 		return(-1);
15846364Sbostic 	    }
15946364Sbostic 	    cp = bufp->page;
16046364Sbostic 	    p = (u_short *)cp;
16146364Sbostic 	} else {
16246364Sbostic 	    p[n] = FULL_KEY_DATA;
16346364Sbostic 	}
16446364Sbostic 	bufp->flags |= BUF_MOD;
16546364Sbostic     }
16646364Sbostic     return(0);
16746364Sbostic }
16846364Sbostic 
16946364Sbostic /*
17046364Sbostic     Called when bufp's page  contains a partial key (index should be 1)
17146364Sbostic 
17246364Sbostic     All pages in the big key/data pair except bufp are freed.  We cannot
17346364Sbostic     free bufp because the page pointing to it is lost and we can't
17446364Sbostic     get rid of its pointer.
17546364Sbostic 
17646364Sbostic     Returns 0 => OK
17746364Sbostic 	    -1 => ERROR
17846364Sbostic */
17946364Sbostic extern int
18046364Sbostic __big_delete (bufp, ndx)
18146364Sbostic BUFHEAD	*bufp;
18246364Sbostic int	ndx;
18346364Sbostic {
18446364Sbostic 	register	BUFHEAD		*rbufp = bufp;
18546364Sbostic 	register	BUFHEAD		*last_bfp = NULL;
18646364Sbostic 	char	*cp;
18746364Sbostic 	u_short	*bp = (u_short *)bufp->page;
18846364Sbostic 	u_short	*xbp;
18946364Sbostic 	u_short	pageno = 0;
19046364Sbostic 	u_short	off, free_sp;
19146364Sbostic 	int	key_done = 0;
19246364Sbostic 	int	n;
19346364Sbostic 
19446364Sbostic 	while (!key_done || (bp[2] != FULL_KEY_DATA)) {
19546364Sbostic 	    if ( bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA ) key_done = 1;
19646364Sbostic 
19746364Sbostic 	    /*
19846364Sbostic 		If there is freespace left on a FULL_KEY_DATA page,
19946364Sbostic 		then the data is short and fits entirely on this
20046364Sbostic 		page, and this is the last page.
20146364Sbostic 	    */
20246364Sbostic 	    if ( bp[2] == FULL_KEY_DATA && FREESPACE(bp) ) break;
20346364Sbostic 	    pageno = bp[bp[0]-1];
20446364Sbostic 	    rbufp->flags |= BUF_MOD;
20546364Sbostic 	    rbufp = __get_buf ( pageno, rbufp, 0 );
20646364Sbostic 	    if ( last_bfp ) __free_ovflpage(last_bfp);
20746364Sbostic 	    last_bfp = rbufp;
20846364Sbostic 	    if ( !rbufp ) return(-1);			/* Error */
20946364Sbostic 	    bp = (u_short *)rbufp->page;
21046364Sbostic 	}
21146364Sbostic 
21246364Sbostic 	/*
21346364Sbostic 	    If we get here then rbufp points to the last page of
21446364Sbostic 	    the big key/data pair.  Bufp points to the first
21546364Sbostic 	    one -- it should now be empty pointing to the next
21646364Sbostic 	    page after this pair.  Can't free it because we don't
21746364Sbostic 	    have the page pointing to it.
21846364Sbostic 	*/
21946364Sbostic 
22046364Sbostic 	/* This is information from the last page of the pair */
22146364Sbostic 	n = bp[0];
22246364Sbostic 	pageno = bp[n-1];
22346364Sbostic 
22446364Sbostic 	/* Now, bp is the first page of the pair */
22546364Sbostic 	bp = (u_short *)bufp->page;
22646364Sbostic 	if ( n > 2 ) {
22746364Sbostic 	    /* There is an overflow page */
22846364Sbostic 	    bp[1] = pageno;
22946364Sbostic 	    bp[2] = OVFLPAGE;
23046364Sbostic 	    bufp->ovfl = rbufp->ovfl;
23146364Sbostic 	} else {
23246364Sbostic 	    /* This is the last page */
23346364Sbostic 	    bufp->ovfl = NULL;
23446364Sbostic 	}
23546364Sbostic 	n -= 2;
23646364Sbostic 	bp[0] = n;
23746364Sbostic 	FREESPACE(bp) = hashp->BSIZE - PAGE_META(n);
23846364Sbostic 	OFFSET(bp) = hashp->BSIZE - 1;
23946364Sbostic 
24046364Sbostic 	bufp->flags |= BUF_MOD;
24146364Sbostic 	if ( rbufp ) __free_ovflpage(rbufp);
24246364Sbostic 	if ( last_bfp != rbufp ) __free_ovflpage(last_bfp);
24346364Sbostic 
24446364Sbostic 	hashp->NKEYS--;
24546364Sbostic 	return(0);
24646364Sbostic }
24746364Sbostic 
24846364Sbostic /*
24946364Sbostic     0 = key not found
25046364Sbostic     -1 = get next overflow page
25146364Sbostic     -2 means key not found and this is big key/data
25246364Sbostic     -3 error
25346364Sbostic */
25446364Sbostic extern int
25546364Sbostic __find_bigpair(bufp, ndx, key, size )
25646364Sbostic BUFHEAD	*bufp;
25746364Sbostic int	ndx;
25846364Sbostic char	*key;
25946364Sbostic int	size;
26046364Sbostic {
26146364Sbostic     register	u_short	*bp = (u_short *)bufp->page;
26246364Sbostic     register	char	*p = bufp->page;
26346364Sbostic     int		ksize = size;
26446364Sbostic     char	*kkey = key;
26546364Sbostic     u_short	bytes;
26646364Sbostic 
26746364Sbostic 
26846364Sbostic     for ( bytes = hashp->BSIZE - bp[ndx];
26946364Sbostic 	  bytes <= size && bp[ndx+1] == PARTIAL_KEY;
27046364Sbostic 	  bytes = hashp->BSIZE - bp[ndx] ) {
27146364Sbostic 
27246364Sbostic 	if ( bcmp ( p+bp[ndx], kkey, bytes ))return(-2);
27346364Sbostic 	kkey += bytes;
27446364Sbostic 	ksize -= bytes;
27546364Sbostic 	bufp = __get_buf ( bp[ndx+2], bufp, 0 );
27646364Sbostic 	if ( !bufp ) {
27746364Sbostic 	    return(-3);
27846364Sbostic 	}
27946364Sbostic 	p = bufp->page;
28046364Sbostic 	bp = (u_short *)p;
28146364Sbostic 	ndx = 1;
28246364Sbostic     }
28346364Sbostic 
28446364Sbostic     if ( (bytes != ksize) || bcmp ( p+bp[ndx], kkey, bytes )) {
28546364Sbostic #ifdef HASH_STATISTICS
28646364Sbostic 	hash_collisions++;
28746364Sbostic #endif
28846364Sbostic 	return(-2);
28946364Sbostic     }
29046364Sbostic     else return (ndx);
29146364Sbostic }
29246364Sbostic 
29346364Sbostic 
29446364Sbostic /*
29546364Sbostic     Given the buffer pointer of the first overflow page of a big pair,
29646364Sbostic     find the end of the big pair
29746364Sbostic 
29846364Sbostic     This will set bpp to the buffer header of the last page of the big pair.
29946364Sbostic     It will return the pageno of the overflow page following the last page of
30046364Sbostic     the pair; 0 if there isn't any (i.e. big pair is the last key in the
30146364Sbostic     bucket)
30246364Sbostic */
30346364Sbostic extern u_short
30446364Sbostic __find_last_page ( bpp )
30546364Sbostic BUFHEAD	**bpp;
30646364Sbostic {
30746364Sbostic 	int	n;
30846364Sbostic 	u_short	pageno;
30946364Sbostic 	BUFHEAD	*bufp = *bpp;
31046364Sbostic 	u_short	*bp = (u_short *)bufp->page;
31146364Sbostic 
31246364Sbostic 	while ( 1 ) {
31346364Sbostic 	    n = bp[0];
31446364Sbostic 
31546364Sbostic 	    /*
31646364Sbostic 		This is the last page if:
31746364Sbostic 			the tag is FULL_KEY_DATA and either
31846364Sbostic 				only 2 entries
31946364Sbostic 				OVFLPAGE marker is explicit
32046364Sbostic 				there is freespace on the page
32146364Sbostic 	    */
32246364Sbostic 	    if ( bp[2] == FULL_KEY_DATA &&
32346364Sbostic 		 ((n == 2) ||  (bp[n] == OVFLPAGE) || (FREESPACE(bp)) ) ) break;
32446364Sbostic 
32546364Sbostic 	    pageno = bp[n-1];
32646364Sbostic 	    bufp = __get_buf ( pageno, bufp, 0 );
32746364Sbostic 	    if ( !bufp ) return (0);		/* Need to indicate an error! */
32846364Sbostic 	    bp = (u_short *)bufp->page;
32946364Sbostic 	}
33046364Sbostic 
33146364Sbostic 	*bpp = bufp;
33246364Sbostic 	if ( bp[0] > 2 ) return ( bp[3] );
33346364Sbostic 	else return(0);
33446364Sbostic }
33546364Sbostic 
33646364Sbostic 
33746364Sbostic /*
33846364Sbostic     Return the data for the key/data pair
33946364Sbostic     that begins on this page at this index
34046364Sbostic     (index should always be 1)
34146364Sbostic */
34246364Sbostic extern int
34346364Sbostic __big_return ( bufp, ndx, val, set_current )
34446364Sbostic BUFHEAD	*bufp;
34546364Sbostic int	ndx;
34646364Sbostic DBT	*val;
34746364Sbostic int	set_current;
34846364Sbostic {
34946364Sbostic     BUFHEAD	*save_p;
35046364Sbostic     u_short	save_addr;
35146364Sbostic     u_short	*bp = (u_short *)bufp->page;
35246364Sbostic     u_short	off, len;
35346364Sbostic     char	*cp, *tp;
35446364Sbostic 
35546364Sbostic     while ( bp[ndx+1] == PARTIAL_KEY ) {
35646364Sbostic 	bufp = __get_buf ( bp[bp[0]-1], bufp, 0 );
35746364Sbostic 	if ( !bufp ) return(-1);
35846364Sbostic 	bp = (u_short *)bufp->page;
35946364Sbostic 	ndx = 1;
36046364Sbostic     }
36146364Sbostic 
36246364Sbostic     if ( bp[ndx+1] == FULL_KEY ) {
36346364Sbostic 	bufp = __get_buf ( bp[bp[0]-1], bufp, 0 );
36446364Sbostic 	if ( !bufp ) return(-1);
36546364Sbostic 	bp = (u_short *)bufp->page;
36646364Sbostic 	save_p = bufp;
36746364Sbostic 	save_addr = save_p->addr;
36846364Sbostic 	off = bp[1];
36946364Sbostic 	len = 0;
37046364Sbostic     } else if (!FREESPACE(bp)) {
37146364Sbostic 	/*
37246364Sbostic 	    This is a hack.  We can't distinguish between
37346364Sbostic 	    FULL_KEY_DATA that contains complete data or
37446364Sbostic 	    incomplete data, so we require that if the
37546364Sbostic 	    data  is complete, there is at least 1 byte
37646364Sbostic 	    of free space left.
37746364Sbostic 	*/
37846364Sbostic 	off = bp[bp[0]];
37946364Sbostic 	len = bp[1] - off;
38046364Sbostic 	save_p = bufp;
38146364Sbostic 	save_addr = bufp->addr;
38246364Sbostic 	bufp = __get_buf ( bp[bp[0]-1], bufp, 0 );
38346364Sbostic 	if ( !bufp ) return(-1);
38446364Sbostic 	bp = (u_short *)bufp->page;
38546364Sbostic     } else {
38646364Sbostic 	/* The data is all on one page */
38746364Sbostic 	tp = (char *)bp;
38846364Sbostic 	off = bp[bp[0]];
38946364Sbostic 	val->data = tp + off;
39046364Sbostic 	val->size = bp[1] - off;
39146364Sbostic 	if ( set_current ) {
39246364Sbostic 	    if ( bp[0] == 2 ) {		/* No more buckets in chain */
39346364Sbostic 		hashp->cpage = NULL;
39446364Sbostic 		hashp->cbucket++;
39546364Sbostic 		hashp->cndx=1;
39646364Sbostic 	    } else  {
39746364Sbostic 		hashp->cpage = __get_buf ( bp[bp[0]-1], bufp, 0 );
39846364Sbostic 		if ( !hashp->cpage )return(-1);
39946364Sbostic 		hashp->cndx = 1;
40046364Sbostic 		if ( !((u_short *)hashp->cpage->page)[0] ) {
40146364Sbostic 		    hashp->cbucket++;
40246364Sbostic 		    hashp->cpage = NULL;
40346364Sbostic 		}
40446364Sbostic 	    }
40546364Sbostic 	}
40646364Sbostic 	return(0);
40746364Sbostic     }
40846364Sbostic 
40946364Sbostic     val->size = collect_data ( bufp, len, set_current );
41046364Sbostic     if ( val->size == -1 ) {
41146364Sbostic 	return(-1);
41246364Sbostic     }
41346364Sbostic     if ( save_p->addr != save_addr ) {
41446364Sbostic 	/* We are pretty short on buffers */
41546364Sbostic 	errno = EINVAL;		/* OUT OF BUFFERS */
41646364Sbostic 	return(-1);
41746364Sbostic     }
41846364Sbostic     bcopy ( (save_p->page)+off, hashp->tmp_buf, len );
41946364Sbostic     val->data = hashp->tmp_buf;
42046364Sbostic     return(0);
42146364Sbostic }
42246364Sbostic 
42346364Sbostic /*
42446364Sbostic     Count how big the total datasize is by
42546364Sbostic     recursing through the pages.  Then allocate
42646364Sbostic     a buffer and copy the data as you recurse up.
42746364Sbostic */
42846364Sbostic static int
42946364Sbostic collect_data ( bufp, len, set )
43046364Sbostic BUFHEAD	*bufp;
43146364Sbostic int	len;
43246364Sbostic int	set;
43346364Sbostic {
43446364Sbostic     register	char	*p = bufp->page;
43546364Sbostic     register	u_short	*bp = (u_short *)p;
43646364Sbostic     u_short	save_addr;
43746364Sbostic     int	mylen, totlen;
43846364Sbostic     BUFHEAD	*xbp;
43946364Sbostic 
44046364Sbostic     mylen = hashp->BSIZE - bp[1];
44146364Sbostic     save_addr = bufp->addr;
44246364Sbostic 
44346364Sbostic     if ( bp[2] == FULL_KEY_DATA ) {	/* End of Data */
44446364Sbostic 	totlen = len + mylen;
44546364Sbostic 	if ( hashp->tmp_buf ) free (hashp->tmp_buf);
44646364Sbostic 	hashp->tmp_buf = (char *)malloc ( totlen );
44746364Sbostic 	if ( !hashp->tmp_buf ) {
44846364Sbostic 	    return(-1);
44946364Sbostic 	}
45046364Sbostic 	if ( set ) {
45146364Sbostic 	    hashp->cndx = 1;
45246364Sbostic 	    if ( bp[0] == 2 ) {		/* No more buckets in chain */
45346364Sbostic 		hashp->cpage = NULL;
45446364Sbostic 		hashp->cbucket++;
45546364Sbostic 	    } else  {
45646364Sbostic 		hashp->cpage = __get_buf ( bp[bp[0]-1], bufp, 0 );
45746364Sbostic 		if (!hashp->cpage) {
45846364Sbostic 		    return(-1);
45946364Sbostic 		} else if ( !((u_short *)hashp->cpage->page)[0] ) {
46046364Sbostic 		    hashp->cbucket++;
46146364Sbostic 		    hashp->cpage = NULL;
46246364Sbostic 		}
46346364Sbostic 	    }
46446364Sbostic 	}
46546364Sbostic     } else {
46646364Sbostic 	xbp = __get_buf ( bp[bp[0]-1], bufp, 0 );
46746364Sbostic 	if ( !xbp || ((totlen = collect_data ( xbp, len + mylen, set )) < 1) ) {
46846364Sbostic 	    return(-1);
46946364Sbostic 	}
47046364Sbostic     }
47146364Sbostic     if ( bufp->addr != save_addr ) {
47246364Sbostic 	errno = EINVAL;		/* Out of buffers */
47346364Sbostic 	return(-1);
47446364Sbostic     }
47546364Sbostic     bcopy ( (bufp->page) + bp[1], &hashp->tmp_buf[len], mylen );
47646364Sbostic     return ( totlen );
47746364Sbostic }
47846364Sbostic 
47946364Sbostic /*
48046364Sbostic 	Fill in the key and data
48146364Sbostic 	for this big pair
48246364Sbostic */
48346364Sbostic extern int
48446364Sbostic __big_keydata ( bufp, ndx, key, val, set )
48546364Sbostic BUFHEAD	*bufp;
48646364Sbostic int	ndx;
48746364Sbostic DBT	*key, *val;
48846364Sbostic int	set;
48946364Sbostic {
49046364Sbostic     key->size = collect_key ( bufp, 0, val, set );
49146364Sbostic     if ( key->size == -1 ) {
49246364Sbostic 	return (-1);
49346364Sbostic     }
49446364Sbostic     key->data = hashp->tmp_key;
49546364Sbostic     return(0);
49646364Sbostic }
49746364Sbostic 
49846364Sbostic /*
49946364Sbostic     Count how big the total key size is by
50046364Sbostic     recursing through the pages.  Then collect
50146364Sbostic     the data, allocate a buffer and copy the key as
50246364Sbostic     you recurse up.
50346364Sbostic */
50446364Sbostic static int
50546364Sbostic collect_key ( bufp, len, val, set )
50646364Sbostic BUFHEAD	*bufp;
50746364Sbostic int	len;
50846364Sbostic DBT	*val;
50946364Sbostic int	set;
51046364Sbostic {
51146364Sbostic     char	*p = bufp->page;
51246364Sbostic     u_short	*bp = (u_short *)p;
51346364Sbostic     u_short	save_addr;
51446364Sbostic     int	mylen, totlen;
51546364Sbostic     BUFHEAD	*xbp;
51646364Sbostic 
51746364Sbostic     mylen = hashp->BSIZE - bp[1];
51846364Sbostic 
51946364Sbostic     save_addr = bufp->addr;
52046364Sbostic     totlen = len + mylen;
52146364Sbostic     if ( bp[2] == FULL_KEY || bp[2] == FULL_KEY_DATA ) {/* End of Key */
52246364Sbostic 	if ( hashp->tmp_key ) free (hashp->tmp_key);
52346364Sbostic 	hashp->tmp_key = (char *)malloc ( totlen );
52446364Sbostic 	if ( !hashp->tmp_key ) {
52546364Sbostic 	    return(-1);
52646364Sbostic 	}
52746364Sbostic 	__big_return ( bufp, 1, val, set );
52846364Sbostic     } else {
52946364Sbostic 	xbp = __get_buf (bp[bp[0]-1], bufp, 0);
53046364Sbostic 	if ( !xbp || ((totlen = collect_key (xbp, totlen, val, set)) < 1 ) ) {
53146364Sbostic 	    return(-1);
53246364Sbostic 	}
53346364Sbostic     }
53446364Sbostic     if ( bufp->addr != save_addr ) {
53546364Sbostic 	errno = EINVAL;		/* MIS -- OUT OF BUFFERS */
53646364Sbostic 	return (-1);
53746364Sbostic     }
53846364Sbostic     bcopy ( (bufp->page) + bp[1], &hashp->tmp_key[len], mylen );
53946364Sbostic     return ( totlen );
54046364Sbostic }
54146364Sbostic 
54246364Sbostic 
54346364Sbostic /*
54446364Sbostic     return 0 => OK
54546364Sbostic 	   -1 => error
54646364Sbostic */
54746364Sbostic extern int
54846364Sbostic __big_split ( op, np, big_keyp, addr, obucket, ret )
54946364Sbostic BUFHEAD	*op;		/* Pointer to where to put keys that go in old bucket */
55046364Sbostic BUFHEAD	*np;		/* Pointer to new bucket page */
55146364Sbostic BUFHEAD	*big_keyp;	/* Pointer to first page containing the big key/data */
55246364Sbostic u_short	addr;		/* Address of big_keyp */
55346364Sbostic int	obucket;	/* Old Bucket */
55446364Sbostic SPLIT_RETURN	*ret;
55546364Sbostic {
55646364Sbostic     register	u_short	*prev_pagep;
55746364Sbostic     register	BUFHEAD	*tmpp;
55846364Sbostic     register	u_short 	*tp;
55946364Sbostic     BUFHEAD	*bp = big_keyp;
56046364Sbostic     u_short	off, free_space;
56146364Sbostic     u_short	n;
56246364Sbostic 
56346364Sbostic     DBT		key, val;
56446364Sbostic 
56546364Sbostic     int		change;
56646364Sbostic 
56746364Sbostic     /* Now figure out where the big key/data goes */
56846364Sbostic     if (__big_keydata ( big_keyp, 1, &key, &val, 0 )) {
56946364Sbostic 	return(-1);
57046364Sbostic     }
57146364Sbostic     change = (__call_hash ( key.data, key.size ) != obucket );
57246364Sbostic 
57346364Sbostic     if ( ret->next_addr = __find_last_page ( &big_keyp ) ) {
57446364Sbostic 	if (!(ret->nextp = __get_buf ( ret->next_addr, big_keyp, 0 ))) {
57546364Sbostic 	    return(-1);;
57646364Sbostic 	}
57746364Sbostic     } else {
57846364Sbostic 	ret->nextp = NULL;
57946364Sbostic     }
58046364Sbostic 
58146364Sbostic     /* Now make one of np/op point to the big key/data pair */
58246364Sbostic     assert(np->ovfl == NULL);
58346364Sbostic     if ( change ) tmpp = np;
58446364Sbostic     else tmpp = op;
58546364Sbostic 
58646364Sbostic     tmpp->flags |= BUF_MOD;
58746364Sbostic #ifdef DEBUG1
58846364Sbostic     fprintf ( stderr, "BIG_SPLIT: %d->ovfl was %d is now %d\n", tmpp->addr,
58946364Sbostic 		(tmpp->ovfl?tmpp->ovfl->addr:0),
59046364Sbostic 		(bp?bp->addr:0) );
59146364Sbostic #endif
59246364Sbostic     tmpp->ovfl = bp;		/* one of op/np point to big_keyp */
59346364Sbostic     tp = (u_short *)tmpp->page;
59446364Sbostic     assert ( FREESPACE(tp) >= OVFLSIZE);
59546364Sbostic     n = tp[0];
59646364Sbostic     off = OFFSET(tp);
59746364Sbostic     free_space = FREESPACE(tp);
59846364Sbostic     tp[++n] = addr;
59946364Sbostic     tp[++n] = OVFLPAGE;
60046364Sbostic     tp[0] = n;
60146364Sbostic     OFFSET(tp) = off;
60246364Sbostic     FREESPACE(tp) = free_space - OVFLSIZE;
60346364Sbostic 
60446364Sbostic     /*
60546364Sbostic 	Finally, set the new and old return values.
60646364Sbostic 	BIG_KEYP contains a pointer to the last page of the big key_data pair.
60746364Sbostic 	Make sure that big_keyp has no following page (2 elements) or create
60846364Sbostic 	an empty following page.
60946364Sbostic     */
61046364Sbostic 
61146364Sbostic     ret->newp = np;
61246364Sbostic     ret->oldp = op;
61346364Sbostic 
61446364Sbostic     tp = (u_short *)big_keyp->page;
61546364Sbostic     big_keyp->flags |= BUF_MOD;
61646364Sbostic     if ( tp[0] > 2 ) {
61746364Sbostic 	/*
61846364Sbostic 	    There may be either one or two offsets on this page
61946364Sbostic 	    If there is one, then the overflow page is linked on
62046364Sbostic 	    normally and tp[4] is OVFLPAGE.  If there are two, tp[4]
62146364Sbostic 	    contains the second offset and needs to get stuffed in
62246364Sbostic 	    after the next overflow page is added
62346364Sbostic 	*/
62446364Sbostic 	n = tp[4];
62546364Sbostic 	free_space = FREESPACE(tp);
62646364Sbostic 	off = OFFSET(tp);
62746364Sbostic 	tp[0] -= 2;
62846364Sbostic 	FREESPACE(tp) = free_space + OVFLSIZE;
62946364Sbostic 	OFFSET(tp) = off;
63046364Sbostic 	tmpp = __add_ovflpage ( big_keyp );
63146364Sbostic 	if ( !tmpp ) {
63246364Sbostic 	    return(-1);
63346364Sbostic 	}
63446364Sbostic 	tp[4] = n;
63546364Sbostic     } else {
63646364Sbostic 	tmpp = big_keyp;
63746364Sbostic     }
63846364Sbostic 
63946364Sbostic     if ( change ) ret->newp = tmpp;
64046364Sbostic     else ret->oldp = tmpp;
64146364Sbostic 
64246364Sbostic     return(0);
64346364Sbostic }
644